diff --git a/00.org/Explanations/CHANGELOG/index.html b/00.org/Explanations/CHANGELOG/index.html new file mode 100644 index 0000000000..3694773081 --- /dev/null +++ b/00.org/Explanations/CHANGELOG/index.html @@ -0,0 +1,1833 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Changelog - MontiCore + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + + + +

Release Notes

+

MontiCore 7.5.0

+

released: 02.05.2023

+

Additions

+
    +
  • type dispatcher for type-safe instance checks and casting
  • +
  • pretty-printer generator
  • +
  • parser generator produces code for the new rule "replcaekeyword"
  • +
  • new coco NoForbiddenProdName
  • +
  • TOP mechanism for generated ANTLr parser classes
  • +
  • add class StreamType which can be used to add Stream symbol with corresponding functions to the global scope
  • +
  • Additions to AccessModifiers
  • +
  • add class StaticAccessModifier to filter whether a symbol is considered as static or not.
  • +
  • add class WriteableAccessModifier to filter whether a symbol is considered as writable or not.
  • +
  • add class CompoundAccessModifier to compose multiple access modifier, e.g. public + static
  • +
  • Additions to the TypeCheck
  • +
  • add new class TypeRelations to provide typecheck methods in a non static fashion. The TypeCheck class now delegates to the implementation
  • +
  • add new class SymTypeOfUnion to store the type of a union of types
  • +
  • add new class SymTypeOfIntersection to store the type of an intersection of types
  • +
  • add new interface ISymTypeVisitor to traverse SymTypeExpressions
  • +
  • add new class SymTypeDeepCloneVisitor to clone SymTypeExpressions
  • +
  • add new class SymTypeBoxingVisitor to box SymTypeExpressions. This implemenation fixes issues over the methods within the SymTypeExpression classes
  • +
  • add new class SymTypeUnboxingVisitor to unbox SymTypeExpressions. This implemenation fixes issues over the methods within the SymTypeExpression classes
  • +
  • add new class SymTypeNormalizeVisitor to normalize SymTypeExpressions. This is required to check for compatibility between SymTypeExpressions, especially regarding union and intersection types
  • +
+

Changes

+
    +
  • Gradle projects containing multiple MCTasks can now use parallel builds
  • +
  • The MontiCore Gradle Plugin ensures that MCTasks are not run in parallel, other tasks, like compile, are run in parallel
  • +
+

Fixes

+
    +
  • parser generation for optional keywords with usage name
  • +
  • Overriding/Imlementing use of lexical productions
  • +
  • in OOScopes, accessmodifier can be used to filter symbols
  • +
+

MontiCore 7.4.0

+

released: 14.11.2022

+

Additions

+
    +
  • new grammar rule replacekeyword
  • +
  • add attribute derived to FieldSymbol
  • +
  • generate XParserInfo
  • +
  • Used by generated language servers
  • +
  • Can be queried for additional info about specific parser states of the generated ANTLR parser
  • +
  • Contains methods stateHasUsageNameY to check the usage name of the nonterminal associated with a parser state
  • +
  • Contains methods stateReferencesZSymbol to check the referenced symbol kind of Name nonterminal associated with a parser state
  • +
  • Additions to the TypeCheck
  • +
  • add new abstract classes AbstractDerive and AbstractSynthesize with basic functionality for FullDerivers and FullSynthesizers
  • +
  • add new class TypeCalculator that can be used to derive SymTypeExpressions from Expressions and synthesize them from types
  • +
  • add SymTypeObscure: new SymTypeExpression that is used when the SymTypeExpression for an expression could not be derived
  • +
  • add SymTypeFunction to store the type of a function
  • +
  • add the functionality for Function chaining, allowing to call functions returned by other functions
  • +
  • add varargs support for CallExpressions
  • +
  • add deriver for LambdaExpressions
  • +
  • Add grammar MCFunctionTypes to write the type of a function in a model
  • +
  • Add ExpressionStatementIsValid CoCo
  • +
  • Add grammars LambdaExpressions and StreamExpressions
  • +
+

Changes

+
    +
  • Java 11
  • +
  • Gradle 7 compatibility
  • +
  • delete deprecated method deepClone in ASTNodes
  • +
  • use CDGenerator (cdanalysis)
  • +
  • Visitor Pattern: Introduce state-based traversal of symbol table
  • +
  • Allows for combined AST and symbol table traversal from global and artifact scopes
  • +
  • Comes with integrated stand-alone symbol table traversal
  • +
  • TypeCheck Refactoring
  • +
  • rename currentResult to result in TypeCheckResult
  • +
  • split TypeCheck facade into TypeCalculator (used to derive SymTypeExpressions from Expressions and synthesize them from types) and TypeCheck (static functions that are useful when checking for type compatibility or for comparing SymTypeExpressions)
  • +
  • rename SymTypeConstant to SymTypePrimitive
  • +
  • Deriver now evaluate all subexpressions of an expression and do not stop at the first error
  • +
  • do not log multiple errors for the same error: If an error occurs in the derivation of a subexpression and this error leads to another error in the derivation of the expression itself, do not log another error
  • +
  • remove the name of CallExpression
  • +
  • rework the calculation of CallExpression, NameExpression and FieldAccessExpression
  • +
  • rework TypeCheck error messages to make them more clear
  • +
+

Fixes

+
    +
  • close all jars used to load models via MCPath
  • +
  • gradle clean should no longer fail because of dangling opened grammar jars
  • +
  • TypeCheck
  • +
  • fix an error in the WildCardTypeArgument where ? super and ? extends were swapped
  • +
  • fix TypeCheck not logging an error for NameExpression and FieldAccessExpression in isolation
  • +
  • make short compatible to byte
  • +
+

MontiCore 7.3.0

+

released: 04.04.2022

+

Additions

+
    +
  • add cocos for lexical mode
  • +
  • add coco for Expression
  • +
  • add cocos for JavaLight
  • +
  • new methods putSymbolDeSer, putXYSymbolDeSer and loadFileForModelNamed (GlobalScope Interface)
  • +
  • new method getToken (MCParser)
  • +
  • use CD4C in 02experiment.configTemplate
  • +
+

Changes

+
    +
  • rename generated classes XYCLI -> XYTool
  • +
+

MontiCore 7.2.0

+

released: 13.09.2021

+

Changes

+
    +
  • Several modes can now be specified in a grammar (for further +explanations see ANTLR). For the grammars, one +file is now generated for lexer rules and one for the parser rules.
  • +
  • delete deprecated classes: + ModelPath, IterablePath, ModelCoordinate, ModelCoordinateImpl, ModelCoordinates, and FileFinder
  • +
  • The symbol table now stores for productions whether a + production is left-recursive.
  • +
  • In the log class the dependency to ch.qos.logback:logback-core was removed
  • +
+

MontiCore 7.1.0

+

released: 05.07.2021

+

Additions

+
    +
  • introduced language-specific CLI generation
  • +
  • New class MCPath that manages a set of path entries. The class is used, e.g., for realizing symbol paths, model paths, handcoded paths, and template paths. MCPath replaces the classes IterablePath and ModelPath.
  • +
  • Different modes can now be defined for lexical tokens in grammar. + The corresponding generator will only be available in the next version.
  • +
+

Changes

+
    +
  • the methods serialize and deserialize of the class XDeSer were moved to the class XSymbols2Json
  • +
  • The following classes are marked as deprecated and will be removed in the near future: +ModelPath, IterablePath, ModelCoordinate, ModelCoordinateImpl, ModelCoordinates, and FileFinder
  • +
+

Fixes

+

MontiCore 7.0.0

+

released: 08.04.2021

+

Additions

+
    +
  • resolveXSubKinds(..) resolves for local symbols of all subkinds of a symbol kind X. This method is used + by the implementation of the resolveXLocally(..) method. It enables proper handling of symbol kind hierarchies + during symbol resolution beyond the borders of a language.
  • +
  • new annotation @NonConservative for productions
  • +
  • add configTemplate (-cf) mechanism to add a freemarker template for customizing the generation processed
  • +
  • add two predefined groovy hook points (-gh1 and -gh2) in the monticore_standard.groovy for injecting + custom groovy scripts into the workflow
  • +
+

Changes

+
    +
  • move grammars OCLExpressions and SetExpressions into OCL-project for further development
  • +
  • DefsTypeBasic was moved to test. There are now only methods for creating symbols. + Use the BasicSymbolsMill to create the basic data types like int, ...
  • +
  • deserialize(String) method of scope DeSer classes is realized as default implementation in IDeSer interface
  • +
  • deserialize(String) method of symbol DeSer classes is realized as default implementation in ISymbolDeSer interface
  • +
  • deserializeAddons() and serializeAddons() methods of scopes are realized as empty default implementation in IDeSer interface
  • +
  • If deserialization encounters a symbol kind for which no DeSer is contained in the symbol Deser map in global scopes, a warning is produced instead of an error
  • +
  • Boolean isShadowing property of scopes is only serialized if its value is "true". Deserialization assumes a default value of "false" if the property is not contained in a serialized scope
  • +
  • deserialize(String) method of symbol DeSers do not produce errors if the serialized kind deviates from the symbol kind that the DeSer is originally engineered for
  • +
  • The TypeCheck was reworked
  • +
  • The interface ITypesCalculator was renamed to IDerive and can now be used similar to the ISynthesize interface
  • +
  • no SymbolSurrogates are created anymore by the TypeCheck. The Synthesize-Classes will now log an error if a type cannot be resolved
  • +
  • SymTypeExpressions now have the method printFullName to print their full name
  • +
  • The class TypeCheck now needs one IDerive and one ISynthesize for its constructor instead of only one of them
  • +
  • The class DeriveSymTypeOfBSCommonExpressions, which does not pay attention to modifiers like static or private, can now be used as an alternative for the class DeriveSymTypeOfCommonExpressions
  • +
+

Fixes

+
    +
  • Symbols with hierarchical symbol kinds are not serialized multiple times anymore.
  • +
+

MontiCore 6.7.0

+

released: 26.01.2021

+

Additions

+
    +
  • Add new CLI for the MontiCore generator engine
  • +
+

Changes

+
    +
  • The context conditions use the new traverser infrastructure. This leads to small changes in the api. + The return value of the method addCoCo is void.
  • +
  • Attribute fileExt in GlobalScopes now refers to a regular expression for file extensions of + symbol table files. The default value of the attribute is "*sym", which usually includes symbol + files of all MontiCore languages. Attention: If your language used the "setFileExt" method in + previous versions of MontiCore to set the file extension of the model file (e.g., to "aut"), this + will cause problems now as the symbol files of the language have differen file extensions + (e.g., "autsym). To fix this, it is sufficient to remove all invocations of "setFileExt" from the + handwritten source code.
  • +
  • For scopes, artifact scopes, and global scopes: Moved abstract methods that do not have a language- + specific name or (argument, return) type from language-specific interface to MontiCore-runtime interfaces
  • +
  • new experiment "strules" demonstrating the use of symbolrules and scoperules
  • +
  • deserialize methods in SymTypeExpressionDeSers do not have an enclosingScope argument anymore. + Internally, it uses the singleton global scope instead.
  • +
  • renamed serializeAdditionalSSymbolAttributes in Symbols2Json class to serializeAddons and moved + to scope and symbol DeSers.
  • +
  • XScopeDeSer is renamed to XDeSer
  • +
  • In Symbols2Json classes:
  • +
  • now implementss Visitor2
  • +
  • new attribute "XTraverser traverser" with getter and setter
  • +
  • Removed attribute "realThis" with getter and setter
  • +
  • New constructor with two arguments XTraverser and JsonPrinter
  • +
  • New zero args constructor
  • +
  • Removed constructor with single JsonPrinter argument
  • +
  • New attributes of all known symbol DeSers and current scope DeSers
  • +
  • New method "protected void init()", initializing the DeSer attributes with the GlobalScope + and the traverser with symbols2json of inherited languages
  • +
  • adjusted store method to use traverser
  • +
  • visit methods for symbols delegate to serialize method of the symbol DeSer
  • +
  • visit and endVisit methods for scope interface and artifact scope interface print object stub + and delegate serialization to scope DeSers
  • +
  • DeSers do not have an attribute of Symbols2Json class anymore, instead it is passed as argument + in the serialize methods
  • +
  • Default values of built-in types that occur in attributes of symbolrules or scoperules are + omitted during serialization and deserialization. The defaults are as follows:
  • +
  • Boolean : false
  • +
  • String : ""
  • +
  • Numeric types: 0 (and 0L and 0.0 and 0.0f)
  • +
  • For symbolrule and scoperule attributes with non-built-in data type, no Log.error is thrown + at execution time of the serialize method call anymore. Instead, these methods (and then, their + classes as well) are generated abstract to yield compilation errors instead.
  • +
  • New interface IDeSer that all symbol and scope DeSers implement.
  • +
  • GlobalScopes manage a map with all relevant DeSers. The map maps the serialized (symbol or scope) + kind to the DeSer that (de)serialized this kind. This mechanism can be used to exchange the DeSer + for a specific kind of symbol or scope.
  • +
  • Scope DeSers have new serialize methods without Symbols2Json argment that can be used for + for serializing (artifact) scopes for, e.g., unit tests
  • +
  • removed the generation of XPhasedSymbolTableCreatorDelegator classes
  • +
  • Experiments now use ScopesGenitor-infrastructure instead of SymbolTableCreator-infrastructure
  • +
+

Fixes

+
    +
  • +

    The initMe and reset methods of the mill now initialize and reset all attributes properly

    +
  • +
  • +

    The CD4Analysis keywords ordered, composition, association, targetimport and classdiagram + can be used in grammars again

    +
  • +
+

MontiCore 6.6.0

+

released: 03.12.2020

+

Additions

+
    +
  • The mill of a language now provides a method parser() to get the parser of the language
      +
    • mill initialization allows to reconfigure the mill to provide a parser for a sublanguage
    • +
    • parser delegator XForYParser are generated that extend a parser of a super language and delegate to the parser of the current language
    • +
    • Due to multiple inheritance, delegation and subclasses are used in combination
    • +
    +
  • +
  • experiments now showcase the use of traversers
  • +
  • add coco (checks if additional attributes are declared twice)
  • +
  • added built-in primitive types to the mills of grammars that extend the grammar BasicSymbols. Add to Mill by executing BasicSymbolsMill.initializePrimitives()
  • +
+

Changes

+
    +
  • The generated parser uses the builder instead of the factory. This means that in grammars the variable _aNode is no longer available. Use instead _builder.
  • +
  • Multiple renamings and signature changes regarding the deser infrastructure
  • +
  • renamed XSymbolTablePrinter to XSymbols2Json
  • +
  • moved load and store methods form XScopeDeSer to XSymbols2Json
  • +
  • removed enclosing scope as method argument of symbol deser methods, as global scope shall be used instead
  • +
  • renamed deserializeAdditionalSSymbolAttributes to deserializeAddons
  • +
  • renamed deserializeAdditionalXScopeAttributes and deserializeAdditionalXScopeAttributes to deserializeAddons
  • +
  • added the JSON printer as a parameter to the methods of XScopeDeSer, SSymbolDeSer und XSymbols2Json
  • +
  • XScopeDeSer, SSymbolDeSer und XSymbols2Json are no longer available via the mill. The constructors can be used instead.
  • +
  • Scope builder have been removed as they did not support multiple inheritance, scope creation methods of the mill should be used instead
  • +
  • Shortened the name of the scope creation methods in the mill from xScope, xGlobalScope and xArtifactScope to scope, globalScope and artifactScope
  • +
  • Shortened the name of the modelFileExtension attribute in the XGlobalScope class to fileExt
  • +
  • renamed XScopeSkeletonCreator and XScopeSkeletonCreatorDelegator to XScopesGenitor and XScopesGenitorDelegator
  • +
  • Deprecated the XPhasedSymbolTableCreatorDelegator, will be removed without replacement in a future release
  • +
  • PrettyPrinters and other visitors in monticore-grammar now use the new Traverser infrastructure instead of the old Visitor infrastructure
  • +
  • generated XScopeGenitor and XScopeGenitorDelegator now use the new Traverser infrastructure instead of the old Visitor infrastructure
  • +
  • Changes to resolving
  • +
  • if name of a topLevelSymbol in ArtifactScope = name of ArtifactScope: qualify symbols in spanned scopes of the topLevelSymbol like before with <topLevelSymbolName>.<symbolName>
  • +
  • if name of a topLevelSymbol in ArtifactScope != name of ArtifactScope: qualify symbols in spanned scope of the topLevelSymbol with <ArtifactScopeName>.<topLevelSymbolName>.<symbolName>
  • +
  • Traverser now support lists of Visitor2 interfaces instead of only one instance
  • +
  • Rename accessor of Traverser from addXVisitor to add4X
  • +
  • Methods returning referenced symbols save the symbols instead of the surroogates
  • +
+

Fixes

+
    +
  • Traverser now properly delegate to handlers as intended
  • +
  • ScopeSkeletonCreator now properly use the mill to create scope instances to ensure substitution via the mill pattern
  • +
  • Fixed a bug where the SymbolSurrogates wrongly qualified their fullName
  • +
  • The clear method of the GlobalScope now deletes all symbols stored in the GlobalScope
  • +
  • Serializing symbolrule attributes of Strings now works properly
  • +
+

MontiCore 6.6.0

+

released: 11.11.2020

+

Additions

+
    +
  • added an experiment hwDeSers showcasing serialization and deserialization
  • +
  • added an experiment hooks showcasing hook point usage
  • +
  • IncCheck provided by the MontiCore Gradle Plugin now considers local super grammar changes to trigger new generation
  • +
  • Added new Traverser generation to replace the visitor infrastructure in a future release
      +
    • XTraverser
    • +
    • XTraverserImplementation
    • +
    • XVisitor2
    • +
    • XHandler
    • +
    +
  • +
  • Added new ScopeSkeletonCreator generation to replace the SymbolTableCreator in a future release and to enable a phased symboltable creation
      +
    • XScopeSkeletonCreator
    • +
    • XScopeSkeletonCreatorDelegator
    • +
    • XPhasedSymbolTableCreatorDelegator
    • +
    +
  • +
  • Added methods to directly obtain instances of the following classes in the mill (instead of their builders)
      +
    • XSymbolTableCreator
    • +
    • XSymbolTableCreatorDelegator
    • +
    • XScopeSkeletonCreator
    • +
    • XScopeSkeletonCreatorDelegator
    • +
    • XPhasedSymbolTableCreatorDelegator
    • +
    • XScopeDeSer
    • +
    • XSymbolDeSer
    • +
    • XSymbolTablePrinter
    • +
    • IXScope
    • +
    • IXArtifactScope
    • +
    +
  • +
+

Changes

+
    +
  • MontiCore now uses Gradle as build tool
  • +
  • some tasks have been introduced for the comfortable control of frequent activities, e.g., buildMC, assembleMC that can be found in the build.gradle
  • +
  • relocated the EMF related subprojects:
      +
    • monticore-emf-grammar to monticore-grammar-emf
    • +
    • monticore-emf-runtime to monticore-runtime-emf
    • +
    +
  • +
  • relocated integration tests and experiments:
      +
    • monticore-generator/it to monticore-test/it
    • +
    • monticore-generator/it/experiments to monticore-test/01.experiments
    • +
    • monticore-generator/it/02.experiments to monticore-test/02.experiments
    • +
    • monticore-grammar/monticore-grammar-it to monticore-test/monticore-grammar-it
    • +
    +
  • +
  • Remove the generation of XModelloader. Languages should now use XScopeDeSer to load symbol tables instead.
  • +
  • Removed the generation of the following builder classes (also from the Mill; alternative solutions described below)
      +
    • XSymbolTableCreatorBuilder
    • +
    • XSymbolTableCreatorDelegatorBuilder
    • +
    • XScopeDeSerBuilder
    • +
    • XSymbolDeSerBuilder
    • +
    • XSymbolTablePrinterBuilder
    • +
    +
  • +
  • renamed IXResolvingDelegate to IXResolver
  • +
  • outsourced Type expressions for arrays to a separate grammar
  • +
  • was FullGenericTypes, is now MCArrayTypes
  • +
  • outsourced initialization for arrays to a separate grammar
  • +
  • was MCVarDeclarationStatements, is now MCArrayStatements
  • +
  • In a composed language, mills of super languages now provide scope instances (scope, global scope and artifact scope) for the composed language
  • +
  • non-existing template paths now result in an error instead of a warning
  • +
  • Set current visitor infrastructure to deprecated
  • +
  • Integrate new visitor infrastructure (i.e., traverser) into XMill to enable re-usability of visitors via language inheritance
  • +
  • Set SymbolTableCreator, SymbolTableCreatorDelegator and their builder to deprecated
  • +
  • Integrate new ScopeSkeletonCreator, ScopeSkeletonCreatorDelegator and PhasedSymbolTableCreatorDelegator into Mill
  • +
  • Added a method clear to the GlobalScope that clears its cache and its resolvers and empties its ModelPath
  • +
+

Fixes

+
    +
  • Fixed that global variable changes in child templates were not changed in parents
  • +
  • Fixed handling of optional names of symbols in symbol table creator
  • +
  • Fixed an issue where surrogates hide symbol attributes
  • +
+

MontiCore 6.4.0

+

released: 12.10.2020

+

Additions

+
    +
  • extended the generated incCheck files to contain information about local super grammars
      +
    • the sh-file is now able to trigger generation if local super grammars are changed
    • +
    • the incCheck method provided by the plugin will support this behavior as well
    • +
    • will only be available in the next release
    • +
    +
  • +
  • extended the mill to manage the global scope instance centrally
  • +
  • added comfort methods for creating modifiers to the ModifierBuilder
      +
    • ModifierBuilder().PUBLIC() short for ModifierBuilder().setPublic(true)
    • +
    +
  • +
  • added MCShadowingJavaBlock to MCCommonStatements
      +
    • standard MCJavaBlock is no longer shadowing
    • +
    +
  • +
  • added a class diagram to the reports that represents the generated data structure for the given grammar + (ast, symbol table visitors, etc.)
  • +
  • added simple BreakStatement to MCCommonStatements
  • +
  • added an include2 alias for the template controller method for including templates in conjunction with templates arguments
  • +
+

Changes

+
    +
  • CLI does no longer check whether a generation is needed (this should be handled by the build tool)
  • +
  • rephrased messages for non-conservative extension (added super grammar name)
  • +
  • added a context condition to prevent list of names in nonterminal production marked as symbols
  • +
  • might be supported in a future version of MontiCore
  • +
  • moved XForYMills to a subpackage to reduce noise (subpackage: _auxiliary)
  • +
  • deprecated the generated enum für constants
      +
    • will be removed without replacement in a future release
    • +
    +
  • +
  • moved EnhancedForControl production from JavaLight to MCCommonStatements as it is commonly used
  • +
  • standard MCJavaBlock is no longer shadowing
  • +
  • renamed BreakStatement in MCLowLevelStatements to LabelledBreakStatement
  • +
  • ForStatement now spans a non-exporting, ordered scope
  • +
  • shortened generated error codes to have 5 digits only
  • +
  • renamed MethOrConstr to JavaMethod in JavaLight
  • +
  • MontiCore Gradle plugin is no longer shipped as a fat jar
  • +
+

Fixes

+
    +
  • Fixed error code calculation for generated error messages to no longer be random
  • +
  • Fixed the report for involved files to contain handwritten files that were considered
      +
    • will only be available in the next release
    • +
    +
  • +
  • Fixed an issue where reports did not contain meaningful names for elements such as class diagram classes or interfaces
  • +
+

MontiCore 6.3.0

+

released: 16.09.2020

+

Additions

+
    +
  • added @Override annotation for nonterminal production to state that this production overrides a super grammars' production
  • +
  • overriding without annotation leads to a warning
  • +
  • using the annotation for a production that does not override an existing nonterminal results in an error
  • +
  • added a context condition to ensure that external production do not have ast rules
  • +
  • added DiagramSymbol in BasicSymbols
  • +
  • introduced generated interfaces for GlobalScope and ArtifactScope
  • +
+

Changes

+
    +
  • serialization of symtype expression now serializes full name of symtype instead of simple name
  • +
  • class ASTNodes is now deprecated and its usages in the generator are removed
  • +
  • visitors no longer provide visit methods for concrete scope classes but their interfaces instead
  • +
  • SymTypeExpression no longer use surrogates but TypeSymbols instead
  • +
  • reverted changes to appended s for list attributes made in previous release
  • +
  • moved initialization of symbols to the endVisit method of the SymbolTableCreator
  • +
+

Fixes

+
    +
  • Fixed missing sourcecode position for overriding warning
  • +
  • Fixed an issue where the inheritance hierarchy was no considered correctly when overriding a nonterminal
  • +
+

MontiCore 6.2.0

+

released: 21.07.2020

+

Additions

+
    +
  • added isFinal to OOType in OOSymbols
  • +
  • extended the mill such that builder for DeSer related classes are provided by the mill
  • +
  • added support for symbol usages in NonterminalSeperator
      +
    • example: Bar = (bla:Name@Foo || "," )+;
    • +
    +
  • +
  • added reports for the symbol table structure of the processed grammar
  • +
  • added isReadOnly to Variable in BasicSymbols
  • +
  • added isElliptic to Method in TypeSymbols
  • +
  • added a context condition to warn if keywords consist of numbers only
      +
    • these numbers will be tokenized as keywords instead of numbers
    • +
    +
  • +
  • added splittoken to express that the listed tokens should be split and not handled as a single token
      +
    • example: splittoken ":::"; results in three token :
    • +
    +
  • +
  • added nokeyword to express that the listed keywords should not be handled as tokens
  • +
  • example: nokeyword "automaton", "state"; means that automaton and state should not be handled as keywords
  • +
  • introduced symbol inheritance
  • +
+

Changes

+
    +
  • renamed de.monticore.type.TypeSymbols to de.monticore.symbols.OOSymbols
  • +
  • renamed de.monticore.type.BasicTypeSymbols to de.monticore.symbols.BasicSymbols
  • +
  • reworked appended s for list attributes
  • +
  • renamed SymbolLoader to SymbolSurrogate
  • +
  • Surrogates are now subclasses of their corresponding symbols
  • +
  • MCJavaBlock in MCCommonStatements now spans a shadowing, non-exporting, ordered scope
  • +
  • MethodDeclaration and ConstructorDeclaration in JavaLight use MCJavaBlock instead of MCBlockStatement
  • +
  • Label in MCLowLevelStatement now is a symbol
  • +
  • VarDecl in MCVarDeclarationStatements no longer exists
      +
    • DeclaratorId now produces FieldSymbols
    • +
    +
  • +
  • removed isParameter and isVariable from Field in TypeSymbols
  • +
  • the language class is no longer generated
  • +
  • moved creator expressions to JavaClassExpression
  • +
  • moved PlusExpression and MinusExpression from AssignmentExpressions to CommonExpressions
  • +
+

Fixes

+

Fixed an issue where super and subtype comparison was wrong in type check +Fixed handling of capital letters in grammar package + * using capital letters now produces a warning +* Fixed an issue were setAbsent methods in the generated SymbolBuilder where not properly overridden +* Fixed that non-shadowing scopes where not handled as intended

+

MontiCore 6.1.0

+

released: 07.05.2020

+

MontiCore 6.0.0

+
    +
  • Uses CD4Analysis 1.5.0
  • +
  • replace get*opt methods with get*
  • +
  • bugfixing
  • +
+

MontiCore 5.4.0.1

+
    +
  • Uses CD4Analysis 1.4.0
  • +
  • add generation of serializers for grammars
  • +
  • add SymbolLoader
  • +
  • remove SymbolReferences
  • +
  • add DeSers for TypeSymbols
  • +
  • improved TypeCheck
  • +
  • replace getName methods with printType methods
  • +
+

MontiCore 5.3.0

+
    +
  • Uses CD4Analysis 1.3.20.2
  • +
  • new Generator based on Decorator-Pattern
  • +
  • add Translation classes
  • +
  • add grammar it-tests
  • +
  • move TypesCalculator to TypeCheck, create derive classes and synthesize classes
  • +
  • add TypeSymbols and SymTypeExpression structure
  • +
  • added DeSers for SymTypeExpressions
  • +
  • added keyword "key" for KeyTerminals
  • +
+

MontiCore 5.2.0

+
    +
  • add "List"-Suffix to attribute name
  • +
+

MontiCore 5.1.0

+
    +
  • Remove the dependency to JavaDSL, add JavaLight
  • +
  • Uses CD4Analysis 1.3.19
  • +
  • added grammar TypeSymbols
  • +
  • renamed SymbolDelegateList to SymbolResolvingDelegateList
  • +
  • add methods for scoperule-attributes in interfaces
  • +
  • add MCTypeVisitor to transform ASTTypes to TypeExpressions
  • +
  • add Groovy Plugin
  • +
  • add MontiCore Statements at de.monticore.statements
  • +
+

MontiCore 5.0.6

+
    +
  • The IncGen-reports are stored in the source code directory
  • +
  • Removed MutableScope
  • +
  • IncGen-Reports are stored
  • +
  • Removed deprecated keyword ast (use astrule) in *.mc4
  • +
  • Add visitors for symbol table
  • +
  • Enable TOP mechanism for visitors
  • +
  • add SymbolRules and ScopeRules
  • +
  • renamed MCBasicLiterals to MCCommonLiterals, add MCLiteralsBasis
  • +
  • move literals to package de.monticore.literals
  • +
  • renamed ShiftExpressions to BitExpressions
  • +
+

MontiCore 5.0.3

+
    +
  • Use the following emf coordinates (MB):
  • +
  • group: org.eclipse.emf
  • +
  • version: 2.15.0
  • +
  • artifact: org.eclipse.emf.ecore | org.eclipse.emf.ecore.xmi | org.eclipse.emf.common
  • +
  • The runtime environment may need the following dependency (group: org.eclipse.platform; artifacitId: org.eclipse.equinox.common; version: 3.10.0)
  • +
  • splitted Types.mc4 in MCBasicTypes, MCCollectionTypes, MCSimpleGenericTypes and MCFullGenericTypes
  • +
  • moved expressions to de.monticore.expressions and added expressions
  • +
+

MontiCore 5.0.2

+
    +
  • Generated by the MontiCore version 5.0.1
  • +
  • Uses JavaDSL 4.3.13, Cd4Analysis 1.3.16, se-commons 1.7.9
  • +
  • Introduce deprecated annotation in grammars (#2215)
  • +
  • Serialization of symobls
  • +
  • Add reporter IncGenCheckReporter
  • +
  • Configuration of the report path
  • +
  • Specific resolving methods in generated scope classes
  • +
  • Bugfixes
  • +
+

MontiCore 5.0.1

+
    +
  • Generated by the MontiCore version 5.0.0
  • +
  • Uses JavaDSL 4.3.12, Cd4Analysis 1.3.13, se-commons 1.7.9
  • +
  • Bugfixes
  • +
  • New methods defineHookPointWithDefault in GlobalExtensionManagement (MB)
  • +
  • new method cmpToken in MCParser (MB)
  • +
  • every (non-)terminal defined in an interface must be present in the implementing production (including Name and Usage Name) (CoCo) (BS)
  • +
  • to ensure that any terminal (with a specific name) has to be implemented, use an empty string, e.g. interface Expression = operator="";
  • +
  • new methods are generated for the referenced symbol and definition and the definition is saved in an attribute (generated wenn you write sth. like "Name@Symbol") (NP)
  • +
  • coco that gives a warning if you do not extend conservative (NP)
  • +
  • coco that attributes with the same usage Name have to reference the same symbol (NP)
  • +
  • SpannedScope and Symbol Methods in ASTNode set to deprecated (NP)
  • +
+

MontiCore 5.0.0

+
    +
  • Generated by the MontiCore version 4.5.5.1
  • +
  • Uses JavaDSL 4.3.11, Cd4Analysis 1.3.13, se-commons 1.7.8
  • +
  • Changed name building for list attributes in grammars (x:Name* -> getXList)
  • +
  • Changed api for GlobalExtensionMangament and TemplateController (see reference manual)
  • +
  • New api for AST nodes (constructor, getter and setter for lists and optional attributes, ...)
  • +
  • Builder classes for AST nodes are external now. Signatures are similar to those in the corresponding AST node, except those methods which set or add something, these return the Builder itself (which allows method chaining)
  • +
  • Changed default script to noemf for the generation of MontiCore. If you want to use emf you can generate monticore-grammar and Java-DSL with the profile "emf". This profile also creates the emf jars. You are also able to test the integration-tests with the profile “emf-it-tests”, which contains extra tests for the generation with emf.
  • +
  • Parsed grammars are not stored as class diagram anymore. CD is only stored as report.
  • +
  • Removed deprecated method filter(ResolvingInfo resolvingInfo, List symbols) use filter(ResolvingInfo, Collection) instead
  • +
  • Removed deprecated method filter(ResolvingInfo resolvingInfo, String name, List symbols) use filter(ResolvingInfo, String, Map) instead
  • +
  • Removed deprecated method create(Class symbolClass, SymbolKind symbolKind) use create(SymbolKind) instead
  • +
  • Removed deprecated method getSymbols use getLocalSymbols instead
  • +
  • Removed deprecated method resolve(SymbolPredicate predicate) use resolveMany(String, SymbolKind, Predicate) instead
  • +
  • Removed deprecated method define use add instead
  • +
  • Removed deprecated method resolve(ResolvingInfo resolvingInfo, String name, SymbolKind kind, AccessModifier modifier)
  • +
  • Removed deprecated method checkIfContinueWithEnclosing use checkIfContinueWithEnclosingScope instead
  • +
  • Removed deprecated method addResolver use addFilter(String, ResolvingFilter) instead
  • +
  • Removed deprecated method addTopScopeResolver use addDefaultFilter instead
  • +
  • Removed deprecated method addTopScopeResolvers use addDefaultFilters instead
  • +
  • Removed deprecated method getTopScopeResolvingFilters use getDefaultFilters instead
  • +
  • Removed deprecated constructer CommonResolvingFilter(Class symbolClass, SymbolKind targetKind) use CommonResolvingFilter(SymbolKind) instead
  • +
  • Removed deprecated method continueWithScope and continueWithEnclosingScope
  • +
  • Removed class FaildLoadingSymbol
  • +
  • Removed deprecated method putInScopeAndLinkWithAst use addToScopeAndLinkWithNode instead
  • +
  • Removed deprecated constructer CommonModelingLanguage(String, String, SymbolKind) use CommonModelingLanguage(String, String) instead
  • +
  • Removed deprecated method addResolver use addResolvingFilter instead
  • +
  • Removed deprecated method addResolver use addResolvingFilter instead
  • +
  • Removed deprecated method getResolvers use getResolvingFilters instead
  • +
  • Removed deprecated method loadAmbiguousModelAndCreateSymbolTable use loadModelsIntoScope instead
  • +
  • Removed deprecated method loadAmbiguousModels use loadModels instead
  • +
  • Removed deprecated method defineHookPoint(String) use glex.defineHookPoint instead
  • +
  • Removed deprecated enum ParserExecution
  • +
  • Removed deprecated method getParserTarget
  • +
  • Removed deprecated method setParserTarget
  • +
+

Further Information

+ + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/00.org/Explanations/FAQ/index.html b/00.org/Explanations/FAQ/index.html new file mode 100644 index 0000000000..d6b927ee19 --- /dev/null +++ b/00.org/Explanations/FAQ/index.html @@ -0,0 +1,755 @@ + + + + + + + + + + + + + + + + + + + + + FAQ - MontiCore + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + + +

FAQ for MontiCore

+

Using Maven

+
    +
  1. Eclipse shows me an error stating that my project configuration is not up-to-date.
  2. +
  3. I get an error saying something about Lifecycle Mappings.
  4. +
  5. Maven build fails because of a missing JDK path.
  6. +
  7. Maven build for de.monticore.parent project fails in eclipse
  8. +
  9. I have a very weird problem, seriously, very weird ...
  10. +
  11. My .m2 folder does not exist.
  12. +
  13. I get a strange error telling me that something is wrong with the UTF8 encoding.
  14. +
  15. Changes I made on one module are not reflected in another module.
  16. +
+
+
    +
  1. +

    Eclipse shows me an error stating that my project configuration is not up-to-date. +Right-click that project and select Maven -> Update Project Configuration.

    +
  2. +
  3. +

    I get an error saying something about Lifecycle Mappings. +Install the m2e extensions mentioned in the developer tutorial.

    +
  4. +
  5. +

    Maven build fails because of a missing JDK path. +Change the installed runtime JREs to the installed JDK. Go to Window -> Preferences -> Installed JREs. Add the JDK path and select it to be the default one.

    +
  6. +
  7. +

    Maven build for de.monticore.parent project fails in eclipse. +Go to Window -> Preferences -> General -> Workspace. Disable "Build automatically" preference.

    +
  8. +
  9. +

    I have a very weird problem, seriously, very weird ... +Right-click that project and select Maven -> Update Project Configuration.

    +
  10. +
  11. +

    My .m2 folder does not exist. +Folders in Windows with a leading "." can only be created using the command line. Start the command line and type in "mkdir .m2" in your home folder.

    +
  12. +
  13. +

    I get a strange error telling me that something is wrong with the UTF8 encoding. +Change the UTF8 encoding by clicking on Window -> Preferences. Then, select the item as shown below and change the values accordingly.

    +
  14. +
  15. +

    Changes I made on one module are not reflected in another module. +Remember that all Maven modules are independent units. By default, they are not directly imported into each other. Instead, Maven resolves dependencies between projects by selecting packages (e.g.jar files) produced by these modules from your local Maven dependency repository. To make the latest version of a module available through this repository, you have to explicitly install it. If you execute an install on an aggregating POM-project, all child modules will be built with the current state of their depending projects as Maven always builds a hierarchy of modules in order of their mutual dependencies. However, if you are working in Eclipse, the workbench can import modules live. This feature is called "Workspace resolution" and is enabled by default for Eclipse automatic project builders. Nevertheless, if you build a module using a Run Configuration you have to explicitly activate "Resolve Workspaces artifacts".

    +
  16. +
+

Further Information

+ + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/00.org/Explanations/StatusOfGrammars/index.html b/00.org/Explanations/StatusOfGrammars/index.html new file mode 100644 index 0000000000..3831e78c75 --- /dev/null +++ b/00.org/Explanations/StatusOfGrammars/index.html @@ -0,0 +1,768 @@ + + + + + + + + + + + + + + + + + + + + + StatusOfGrammars - MontiCore + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + + + +

MontiCore Grammar Status Plans - an Overview

+

MontiCore uses grammars as primary mechanism +to describe DSLs and DSL components. The extended +grammar format allows to compose language components by +(1) inheriting, (2) extending, (3) embedding +and (4) aggregating grammars (see the reference manual for details). +From the grammars a lot of infrastructructure is generated, that is as well +composable, can be extended with handwrittten code and most imprtandly, these +extensions and the grammar composition are compatible, which +leads to optimal forms of reuse.

+

To improve understanding, what will happen with a grammar, we define the +following set of stati and mention the status of each grammar, +both in the explanation and in the grammar itself:

+

Status of a Grammar

+
    +
  1. +

    MontiCore stable: +Such a grammar is meant to be stable in the further development of +MontiCore. The grammar is tested and assumed to be of high quality. +It may rarely happen that smaller extensions are made in a conservative +form, which means that (1) composition with any other grammars, +(2) extensions and adaptations and (3) handwritten extensions will +still work.

    +
  2. +
  3. +

    Beta: In Stabilization: +Such a grammar is in the process of becoming stable. One might already +include the grammar, but some changes may still appear. +(See task list for potential changes.)

    +
  4. +
  5. +

    Alpha: Intention to become stable: +Such a grammar is relatively fresh, but intended to become stable +and useful. Changes may occur, e.g. when restructuring or bug fixing. +Or it may be taken out of the process and become one of the following:

    +
  6. +
  7. +

    Example: +The grammar serves as working example, but will not have high priority on +keeping the grammar up to date. One might use it as inspiration for their +own developments.

    +
  8. +
  9. +

    Deprecated: +The grammar should not be used anymore, it is deprecated, and only +there for compatibility. Normally a newer version of the content +exists in other, often decomposed grammars, allowing to configure +which part of the grammar to be used. +Deprecated grammars are not listed in any overview.

    +
  10. +
+

Marking the Status of Grammars

+

A comment of the following form within the grammar defines this status:

+
    +
  1. /* This is a MontiCore stable grammar. + * Adaptations -- if any -- are conservative. */
  2. +
  3. /* Beta-version: This is intended to become a MontiCore stable grammar. */
  4. +
  5. /* Alpha-version: This is intended to become a MontiCore stable grammar. */ + (but sometimes also omitted)
  6. +
+

Further Information

+ + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/00.org/Licenses/LICENSE-BSD3CLAUSE/index.html b/00.org/Licenses/LICENSE-BSD3CLAUSE/index.html new file mode 100644 index 0000000000..3a3663cad7 --- /dev/null +++ b/00.org/Licenses/LICENSE-BSD3CLAUSE/index.html @@ -0,0 +1,725 @@ + + + + + + + + + + + + + + + + + + + + + LICENSE BSD3CLAUSE - MontiCore + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + +

LICENSE BSD3CLAUSE

+ + +

Copyright (c) MontiCore*, All rights reserved.

+

Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met:

+
    +
  1. +

    Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer.

    +
  2. +
  3. +

    Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution.

    +
  4. +
  5. +

    Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software +without specific prior written permission.

    +
  6. +
+

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE.

+

* belongs to RWTH and RIDT

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/00.org/Licenses/LICENSE-LGPL/index.html b/00.org/Licenses/LICENSE-LGPL/index.html new file mode 100644 index 0000000000..a8334969de --- /dev/null +++ b/00.org/Licenses/LICENSE-LGPL/index.html @@ -0,0 +1,841 @@ + + + + + + + + + + + + + + + + + + + + + LICENSE LGPL - MontiCore + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + + +

GNU LESSER GENERAL PUBLIC LICENSE

+

Version 3, 29 June 2007

+

Copyright (C) 2007 Free Software Foundation, Inc. http://fsf.org/ +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed.

+

This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below.

+
    +
  1. Additional Definitions.
  2. +
+

As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License.

+

"The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below.

+

An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library.

+

A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version".

+

The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version.

+

The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work.

+
    +
  1. Exception to Section 3 of the GNU GPL.
  2. +
+

You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL.

+
    +
  1. Conveying Modified Versions.
  2. +
+

If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version:

+

a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or

+

b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy.

+
    +
  1. Object Code Incorporating Material from Library Header Files.
  2. +
+

The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following:

+

a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License.

+

b) Accompany the object code with a copy of the GNU GPL and this license + document.

+
    +
  1. Combined Works.
  2. +
+

You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following:

+

a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License.

+

b) Accompany the Combined Work with a copy of the GNU GPL and this license + document.

+

c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document.

+

d) Do one of the following:

+
   0. Convey the Minimal Corresponding Source under the terms of this
+   License, and the Corresponding Application Code in a form
+   suitable for, and under terms that permit, the user to
+   recombine or relink the Application with a modified version of
+   the Linked Version to produce a modified Combined Work, in the
+   manner specified by section 6 of the GNU GPL for conveying
+   Corresponding Source.
+
+   1. Use a suitable shared library mechanism for linking with the
+   Library.  A suitable mechanism is one that (a) uses at run time
+   a copy of the Library already present on the user's computer
+   system, and (b) will operate properly with a modified version
+   of the Library that is interface-compatible with the Linked
+   Version.
+
+

e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 5d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 5d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.)

+
    +
  1. Combined Libraries.
  2. +
+

You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following:

+

a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License.

+

b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work.

+
    +
  1. Revised Versions of the GNU Lesser General Public License.
  2. +
+

The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns.

+

Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation.

+

If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library.

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/00.org/Licenses/LICENSE-MONTICORE-3-LEVEL/index.html b/00.org/Licenses/LICENSE-MONTICORE-3-LEVEL/index.html new file mode 100644 index 0000000000..7b53055c03 --- /dev/null +++ b/00.org/Licenses/LICENSE-MONTICORE-3-LEVEL/index.html @@ -0,0 +1,913 @@ + + + + + + + + + + + + + + + + + + + + + + + + + License - MontiCore + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + + + +

MontiCore 3-Level License Model

+

MontiCore is a language workbench for an efficient +development of domain-specific languages (DSLs). All the code available +in these GitHub MontiCore projects
+is published under three levels of licenses as discussed below.

+

For a full use of generated code in commercial and any other forms of +projects, the finally generated code is, completely freely available, +even though the main workbench itself has restrictions.

+

License Overview

+

The MontiCore Language Workbench deals with three levels of code:

+
    +
  • +

    (Level 3) MontiCore: the main library constituting the LWB,

    +
  • +
  • +

    (Level 2) tool derivates that are to a large extent generated by the MontiCore + LWB, and

    +
  • +
  • +

    (Level 1) product code that is finally generated by tool derivates. + This also includes analytical results, thus as results of consistency + checks, code smells, test infrastructures, etc.

    +
  • +
+

Each level has its own and more relaxing license:

+
    +
  • +

    (Level 1) Product code: the generated product code is absolutely +free for each form of use +including commercial use without any mentioning and thus without any restriction +from MontiCore.

    +
  • +
  • +

    (Level 2) Tool derivate: when a tool is derived using the MontiCore +language workbench, then the result falls under the pretty liberal +BSD 3 Clause license +(see BSD-3-Clause).

    +
  • +
  • +

    (Level 3) MontiCore: the main LWB components are published in +GitHub under the +LGPL license (see +LGPL V3.0).

    +
  • +
+

As a consequence using MontiCore during development is rather liberal +and the final products do not have any restriction.

+

Please note that this license level model holds for the MontiCore +LWB and all related projects published in GitHub. For artefacts available +from other sources, different licenses may apply. +E.g. developers of tools may impose their own form of +restrictions i.e. licenses on their tools respectively the results +generated by these tools. +Artefacts directly made available from RWTH Aachen and not published in +GitHub are for the concretely granted purpose only and are +not do be made public at all.

+

As usual in software development: For statistics, scientific reasons, +quality and performance improvement, the tools occasionally send the +fully anonymous statistics report (see file) to the developers.

+

MontiCore 3-Level License on Files

+

This repository for the MontiCore language workbench contains three +kinds of artifacts:

+
    +
  • +

    Java-files that are executed in the MontiCore LWB. They are under +LGPL licence.

    +
  • +
  • +

    Java-files that belong to the runtime environment (RTE) and are thus +copied to the generated code. They are under BSD 3 Clause license.

    +
  • +
  • +

    Templates executed during generation of tool code. +They are also only under BSD 3 Clause license, +because parts of them are copied to the generated code.

    +
  • +
+

MontiCore 3-Level License on Tool and Language Repositories

+

Other MontiCore repositories contain complete or incomplete tools as well as +MontiCore language components. They contain again three +kinds of artifacts:

+
    +
  • +

    Grammars that are used to define language components in the MontiCore LWB. +They are under LGPL licence, but these can be extended by own grammars freely.

    +
  • +
  • +

    Java-files that are executed in the tool (belonging to the tool RTE). +They are under BSD 3 Clause license.

    +
  • +
  • +

    Java-files that belong to the product runtime environment (RTE) are +completely free without restriction.

    +
  • +
  • +

    Templates executed by the tool during generation of product code. +They also are completely free without restriction, +because parts of them are copied to the generated code.

    +
  • +
+

As a result, a tool derivate (level 2) does not contain any LGPL code, +but only BSD 3 Clause code. Executing the tool derivate then +produces completely free code (level 1).

+

If questions appear e.g. on using MontiCore itself in a product +or building an interpreter, please contact monticore@se-rwth.de.

+

Underlying Licenses

+

The MontiCore 3 Level license is built on:

+ +

Please also note the general disclaimer from the BSD 3 Clause license on +liability, etc.

+

Included Software

+

This product includes the following software, both having their own licenses, +compatible with MontiCores licenses:

+ +

Further Information

+ + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/404.html b/404.html new file mode 100644 index 0000000000..6ca140d141 --- /dev/null +++ b/404.html @@ -0,0 +1,682 @@ + + + + + + + + + + + + + + + + + + + MontiCore + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ +

404 - Not found

+ +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/MontiCoreHandbook2021.gif b/MontiCoreHandbook2021.gif new file mode 100644 index 0000000000..a608cbfe8d Binary files /dev/null and b/MontiCoreHandbook2021.gif differ diff --git a/assets/images/favicon.png b/assets/images/favicon.png new file mode 100644 index 0000000000..1cf13b9f9d Binary files /dev/null and b/assets/images/favicon.png differ diff --git a/assets/javascripts/bundle.220ee61c.min.js b/assets/javascripts/bundle.220ee61c.min.js new file mode 100644 index 0000000000..116072a11e --- /dev/null +++ b/assets/javascripts/bundle.220ee61c.min.js @@ -0,0 +1,29 @@ +"use strict";(()=>{var Ci=Object.create;var gr=Object.defineProperty;var Ri=Object.getOwnPropertyDescriptor;var ki=Object.getOwnPropertyNames,Ht=Object.getOwnPropertySymbols,Hi=Object.getPrototypeOf,yr=Object.prototype.hasOwnProperty,nn=Object.prototype.propertyIsEnumerable;var rn=(e,t,r)=>t in e?gr(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,P=(e,t)=>{for(var r in t||(t={}))yr.call(t,r)&&rn(e,r,t[r]);if(Ht)for(var r of Ht(t))nn.call(t,r)&&rn(e,r,t[r]);return e};var on=(e,t)=>{var r={};for(var n in e)yr.call(e,n)&&t.indexOf(n)<0&&(r[n]=e[n]);if(e!=null&&Ht)for(var n of Ht(e))t.indexOf(n)<0&&nn.call(e,n)&&(r[n]=e[n]);return r};var Pt=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var Pi=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of ki(t))!yr.call(e,o)&&o!==r&&gr(e,o,{get:()=>t[o],enumerable:!(n=Ri(t,o))||n.enumerable});return e};var yt=(e,t,r)=>(r=e!=null?Ci(Hi(e)):{},Pi(t||!e||!e.__esModule?gr(r,"default",{value:e,enumerable:!0}):r,e));var sn=Pt((xr,an)=>{(function(e,t){typeof xr=="object"&&typeof an!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(xr,function(){"use strict";function e(r){var n=!0,o=!1,i=null,s={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function a(O){return!!(O&&O!==document&&O.nodeName!=="HTML"&&O.nodeName!=="BODY"&&"classList"in O&&"contains"in O.classList)}function f(O){var Qe=O.type,De=O.tagName;return!!(De==="INPUT"&&s[Qe]&&!O.readOnly||De==="TEXTAREA"&&!O.readOnly||O.isContentEditable)}function c(O){O.classList.contains("focus-visible")||(O.classList.add("focus-visible"),O.setAttribute("data-focus-visible-added",""))}function u(O){O.hasAttribute("data-focus-visible-added")&&(O.classList.remove("focus-visible"),O.removeAttribute("data-focus-visible-added"))}function p(O){O.metaKey||O.altKey||O.ctrlKey||(a(r.activeElement)&&c(r.activeElement),n=!0)}function m(O){n=!1}function d(O){a(O.target)&&(n||f(O.target))&&c(O.target)}function h(O){a(O.target)&&(O.target.classList.contains("focus-visible")||O.target.hasAttribute("data-focus-visible-added"))&&(o=!0,window.clearTimeout(i),i=window.setTimeout(function(){o=!1},100),u(O.target))}function v(O){document.visibilityState==="hidden"&&(o&&(n=!0),Y())}function Y(){document.addEventListener("mousemove",N),document.addEventListener("mousedown",N),document.addEventListener("mouseup",N),document.addEventListener("pointermove",N),document.addEventListener("pointerdown",N),document.addEventListener("pointerup",N),document.addEventListener("touchmove",N),document.addEventListener("touchstart",N),document.addEventListener("touchend",N)}function B(){document.removeEventListener("mousemove",N),document.removeEventListener("mousedown",N),document.removeEventListener("mouseup",N),document.removeEventListener("pointermove",N),document.removeEventListener("pointerdown",N),document.removeEventListener("pointerup",N),document.removeEventListener("touchmove",N),document.removeEventListener("touchstart",N),document.removeEventListener("touchend",N)}function N(O){O.target.nodeName&&O.target.nodeName.toLowerCase()==="html"||(n=!1,B())}document.addEventListener("keydown",p,!0),document.addEventListener("mousedown",m,!0),document.addEventListener("pointerdown",m,!0),document.addEventListener("touchstart",m,!0),document.addEventListener("visibilitychange",v,!0),Y(),r.addEventListener("focus",d,!0),r.addEventListener("blur",h,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)})});var cn=Pt(Er=>{(function(e){var t=function(){try{return!!Symbol.iterator}catch(c){return!1}},r=t(),n=function(c){var u={next:function(){var p=c.shift();return{done:p===void 0,value:p}}};return r&&(u[Symbol.iterator]=function(){return u}),u},o=function(c){return encodeURIComponent(c).replace(/%20/g,"+")},i=function(c){return decodeURIComponent(String(c).replace(/\+/g," "))},s=function(){var c=function(p){Object.defineProperty(this,"_entries",{writable:!0,value:{}});var m=typeof p;if(m!=="undefined")if(m==="string")p!==""&&this._fromString(p);else if(p instanceof c){var d=this;p.forEach(function(B,N){d.append(N,B)})}else if(p!==null&&m==="object")if(Object.prototype.toString.call(p)==="[object Array]")for(var h=0;hd[0]?1:0}),c._entries&&(c._entries={});for(var p=0;p1?i(d[1]):"")}})})(typeof global!="undefined"?global:typeof window!="undefined"?window:typeof self!="undefined"?self:Er);(function(e){var t=function(){try{var o=new e.URL("b","http://a");return o.pathname="c d",o.href==="http://a/c%20d"&&o.searchParams}catch(i){return!1}},r=function(){var o=e.URL,i=function(f,c){typeof f!="string"&&(f=String(f)),c&&typeof c!="string"&&(c=String(c));var u=document,p;if(c&&(e.location===void 0||c!==e.location.href)){c=c.toLowerCase(),u=document.implementation.createHTMLDocument(""),p=u.createElement("base"),p.href=c,u.head.appendChild(p);try{if(p.href.indexOf(c)!==0)throw new Error(p.href)}catch(O){throw new Error("URL unable to set base "+c+" due to "+O)}}var m=u.createElement("a");m.href=f,p&&(u.body.appendChild(m),m.href=m.href);var d=u.createElement("input");if(d.type="url",d.value=f,m.protocol===":"||!/:/.test(m.href)||!d.checkValidity()&&!c)throw new TypeError("Invalid URL");Object.defineProperty(this,"_anchorElement",{value:m});var h=new e.URLSearchParams(this.search),v=!0,Y=!0,B=this;["append","delete","set"].forEach(function(O){var Qe=h[O];h[O]=function(){Qe.apply(h,arguments),v&&(Y=!1,B.search=h.toString(),Y=!0)}}),Object.defineProperty(this,"searchParams",{value:h,enumerable:!0});var N=void 0;Object.defineProperty(this,"_updateSearchParams",{enumerable:!1,configurable:!1,writable:!1,value:function(){this.search!==N&&(N=this.search,Y&&(v=!1,this.searchParams._fromString(this.search),v=!0))}})},s=i.prototype,a=function(f){Object.defineProperty(s,f,{get:function(){return this._anchorElement[f]},set:function(c){this._anchorElement[f]=c},enumerable:!0})};["hash","host","hostname","port","protocol"].forEach(function(f){a(f)}),Object.defineProperty(s,"search",{get:function(){return this._anchorElement.search},set:function(f){this._anchorElement.search=f,this._updateSearchParams()},enumerable:!0}),Object.defineProperties(s,{toString:{get:function(){var f=this;return function(){return f.href}}},href:{get:function(){return this._anchorElement.href.replace(/\?$/,"")},set:function(f){this._anchorElement.href=f,this._updateSearchParams()},enumerable:!0},pathname:{get:function(){return this._anchorElement.pathname.replace(/(^\/?)/,"/")},set:function(f){this._anchorElement.pathname=f},enumerable:!0},origin:{get:function(){var f={"http:":80,"https:":443,"ftp:":21}[this._anchorElement.protocol],c=this._anchorElement.port!=f&&this._anchorElement.port!=="";return this._anchorElement.protocol+"//"+this._anchorElement.hostname+(c?":"+this._anchorElement.port:"")},enumerable:!0},password:{get:function(){return""},set:function(f){},enumerable:!0},username:{get:function(){return""},set:function(f){},enumerable:!0}}),i.createObjectURL=function(f){return o.createObjectURL.apply(o,arguments)},i.revokeObjectURL=function(f){return o.revokeObjectURL.apply(o,arguments)},e.URL=i};if(t()||r(),e.location!==void 0&&!("origin"in e.location)){var n=function(){return e.location.protocol+"//"+e.location.hostname+(e.location.port?":"+e.location.port:"")};try{Object.defineProperty(e.location,"origin",{get:n,enumerable:!0})}catch(o){setInterval(function(){e.location.origin=n()},100)}}})(typeof global!="undefined"?global:typeof window!="undefined"?window:typeof self!="undefined"?self:Er)});var qr=Pt((Mt,Nr)=>{/*! + * clipboard.js v2.0.11 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */(function(t,r){typeof Mt=="object"&&typeof Nr=="object"?Nr.exports=r():typeof define=="function"&&define.amd?define([],r):typeof Mt=="object"?Mt.ClipboardJS=r():t.ClipboardJS=r()})(Mt,function(){return function(){var e={686:function(n,o,i){"use strict";i.d(o,{default:function(){return Ai}});var s=i(279),a=i.n(s),f=i(370),c=i.n(f),u=i(817),p=i.n(u);function m(j){try{return document.execCommand(j)}catch(T){return!1}}var d=function(T){var E=p()(T);return m("cut"),E},h=d;function v(j){var T=document.documentElement.getAttribute("dir")==="rtl",E=document.createElement("textarea");E.style.fontSize="12pt",E.style.border="0",E.style.padding="0",E.style.margin="0",E.style.position="absolute",E.style[T?"right":"left"]="-9999px";var H=window.pageYOffset||document.documentElement.scrollTop;return E.style.top="".concat(H,"px"),E.setAttribute("readonly",""),E.value=j,E}var Y=function(T,E){var H=v(T);E.container.appendChild(H);var I=p()(H);return m("copy"),H.remove(),I},B=function(T){var E=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},H="";return typeof T=="string"?H=Y(T,E):T instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(T==null?void 0:T.type)?H=Y(T.value,E):(H=p()(T),m("copy")),H},N=B;function O(j){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?O=function(E){return typeof E}:O=function(E){return E&&typeof Symbol=="function"&&E.constructor===Symbol&&E!==Symbol.prototype?"symbol":typeof E},O(j)}var Qe=function(){var T=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},E=T.action,H=E===void 0?"copy":E,I=T.container,q=T.target,Me=T.text;if(H!=="copy"&&H!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(q!==void 0)if(q&&O(q)==="object"&&q.nodeType===1){if(H==="copy"&&q.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(H==="cut"&&(q.hasAttribute("readonly")||q.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if(Me)return N(Me,{container:I});if(q)return H==="cut"?h(q):N(q,{container:I})},De=Qe;function $e(j){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?$e=function(E){return typeof E}:$e=function(E){return E&&typeof Symbol=="function"&&E.constructor===Symbol&&E!==Symbol.prototype?"symbol":typeof E},$e(j)}function Ei(j,T){if(!(j instanceof T))throw new TypeError("Cannot call a class as a function")}function tn(j,T){for(var E=0;E0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof I.action=="function"?I.action:this.defaultAction,this.target=typeof I.target=="function"?I.target:this.defaultTarget,this.text=typeof I.text=="function"?I.text:this.defaultText,this.container=$e(I.container)==="object"?I.container:document.body}},{key:"listenClick",value:function(I){var q=this;this.listener=c()(I,"click",function(Me){return q.onClick(Me)})}},{key:"onClick",value:function(I){var q=I.delegateTarget||I.currentTarget,Me=this.action(q)||"copy",kt=De({action:Me,container:this.container,target:this.target(q),text:this.text(q)});this.emit(kt?"success":"error",{action:Me,text:kt,trigger:q,clearSelection:function(){q&&q.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(I){return vr("action",I)}},{key:"defaultTarget",value:function(I){var q=vr("target",I);if(q)return document.querySelector(q)}},{key:"defaultText",value:function(I){return vr("text",I)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(I){var q=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return N(I,q)}},{key:"cut",value:function(I){return h(I)}},{key:"isSupported",value:function(){var I=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],q=typeof I=="string"?[I]:I,Me=!!document.queryCommandSupported;return q.forEach(function(kt){Me=Me&&!!document.queryCommandSupported(kt)}),Me}}]),E}(a()),Ai=Li},828:function(n){var o=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function s(a,f){for(;a&&a.nodeType!==o;){if(typeof a.matches=="function"&&a.matches(f))return a;a=a.parentNode}}n.exports=s},438:function(n,o,i){var s=i(828);function a(u,p,m,d,h){var v=c.apply(this,arguments);return u.addEventListener(m,v,h),{destroy:function(){u.removeEventListener(m,v,h)}}}function f(u,p,m,d,h){return typeof u.addEventListener=="function"?a.apply(null,arguments):typeof m=="function"?a.bind(null,document).apply(null,arguments):(typeof u=="string"&&(u=document.querySelectorAll(u)),Array.prototype.map.call(u,function(v){return a(v,p,m,d,h)}))}function c(u,p,m,d){return function(h){h.delegateTarget=s(h.target,p),h.delegateTarget&&d.call(u,h)}}n.exports=f},879:function(n,o){o.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},o.nodeList=function(i){var s=Object.prototype.toString.call(i);return i!==void 0&&(s==="[object NodeList]"||s==="[object HTMLCollection]")&&"length"in i&&(i.length===0||o.node(i[0]))},o.string=function(i){return typeof i=="string"||i instanceof String},o.fn=function(i){var s=Object.prototype.toString.call(i);return s==="[object Function]"}},370:function(n,o,i){var s=i(879),a=i(438);function f(m,d,h){if(!m&&!d&&!h)throw new Error("Missing required arguments");if(!s.string(d))throw new TypeError("Second argument must be a String");if(!s.fn(h))throw new TypeError("Third argument must be a Function");if(s.node(m))return c(m,d,h);if(s.nodeList(m))return u(m,d,h);if(s.string(m))return p(m,d,h);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function c(m,d,h){return m.addEventListener(d,h),{destroy:function(){m.removeEventListener(d,h)}}}function u(m,d,h){return Array.prototype.forEach.call(m,function(v){v.addEventListener(d,h)}),{destroy:function(){Array.prototype.forEach.call(m,function(v){v.removeEventListener(d,h)})}}}function p(m,d,h){return a(document.body,m,d,h)}n.exports=f},817:function(n){function o(i){var s;if(i.nodeName==="SELECT")i.focus(),s=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var a=i.hasAttribute("readonly");a||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),a||i.removeAttribute("readonly"),s=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var f=window.getSelection(),c=document.createRange();c.selectNodeContents(i),f.removeAllRanges(),f.addRange(c),s=f.toString()}return s}n.exports=o},279:function(n){function o(){}o.prototype={on:function(i,s,a){var f=this.e||(this.e={});return(f[i]||(f[i]=[])).push({fn:s,ctx:a}),this},once:function(i,s,a){var f=this;function c(){f.off(i,c),s.apply(a,arguments)}return c._=s,this.on(i,c,a)},emit:function(i){var s=[].slice.call(arguments,1),a=((this.e||(this.e={}))[i]||[]).slice(),f=0,c=a.length;for(f;f{"use strict";/*! + * escape-html + * Copyright(c) 2012-2013 TJ Holowaychuk + * Copyright(c) 2015 Andreas Lubbe + * Copyright(c) 2015 Tiancheng "Timothy" Gu + * MIT Licensed + */var rs=/["'&<>]/;Yo.exports=ns;function ns(e){var t=""+e,r=rs.exec(t);if(!r)return t;var n,o="",i=0,s=0;for(i=r.index;i0&&i[i.length-1])&&(c[0]===6||c[0]===2)){r=0;continue}if(c[0]===3&&(!i||c[1]>i[0]&&c[1]=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function W(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var n=r.call(e),o,i=[],s;try{for(;(t===void 0||t-- >0)&&!(o=n.next()).done;)i.push(o.value)}catch(a){s={error:a}}finally{try{o&&!o.done&&(r=n.return)&&r.call(n)}finally{if(s)throw s.error}}return i}function D(e,t,r){if(r||arguments.length===2)for(var n=0,o=t.length,i;n1||a(m,d)})})}function a(m,d){try{f(n[m](d))}catch(h){p(i[0][3],h)}}function f(m){m.value instanceof et?Promise.resolve(m.value.v).then(c,u):p(i[0][2],m)}function c(m){a("next",m)}function u(m){a("throw",m)}function p(m,d){m(d),i.shift(),i.length&&a(i[0][0],i[0][1])}}function pn(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof Ee=="function"?Ee(e):e[Symbol.iterator](),r={},n("next"),n("throw"),n("return"),r[Symbol.asyncIterator]=function(){return this},r);function n(i){r[i]=e[i]&&function(s){return new Promise(function(a,f){s=e[i](s),o(a,f,s.done,s.value)})}}function o(i,s,a,f){Promise.resolve(f).then(function(c){i({value:c,done:a})},s)}}function C(e){return typeof e=="function"}function at(e){var t=function(n){Error.call(n),n.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var It=at(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: +`+r.map(function(n,o){return o+1+") "+n.toString()}).join(` + `):"",this.name="UnsubscriptionError",this.errors=r}});function Ve(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var Ie=function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,n,o,i;if(!this.closed){this.closed=!0;var s=this._parentage;if(s)if(this._parentage=null,Array.isArray(s))try{for(var a=Ee(s),f=a.next();!f.done;f=a.next()){var c=f.value;c.remove(this)}}catch(v){t={error:v}}finally{try{f&&!f.done&&(r=a.return)&&r.call(a)}finally{if(t)throw t.error}}else s.remove(this);var u=this.initialTeardown;if(C(u))try{u()}catch(v){i=v instanceof It?v.errors:[v]}var p=this._finalizers;if(p){this._finalizers=null;try{for(var m=Ee(p),d=m.next();!d.done;d=m.next()){var h=d.value;try{ln(h)}catch(v){i=i!=null?i:[],v instanceof It?i=D(D([],W(i)),W(v.errors)):i.push(v)}}}catch(v){n={error:v}}finally{try{d&&!d.done&&(o=m.return)&&o.call(m)}finally{if(n)throw n.error}}}if(i)throw new It(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)ln(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&Ve(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&Ve(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=function(){var t=new e;return t.closed=!0,t}(),e}();var Sr=Ie.EMPTY;function jt(e){return e instanceof Ie||e&&"closed"in e&&C(e.remove)&&C(e.add)&&C(e.unsubscribe)}function ln(e){C(e)?e():e.unsubscribe()}var Le={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var st={setTimeout:function(e,t){for(var r=[],n=2;n0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var n=this,o=this,i=o.hasError,s=o.isStopped,a=o.observers;return i||s?Sr:(this.currentObservers=null,a.push(r),new Ie(function(){n.currentObservers=null,Ve(a,r)}))},t.prototype._checkFinalizedStatuses=function(r){var n=this,o=n.hasError,i=n.thrownError,s=n.isStopped;o?r.error(i):s&&r.complete()},t.prototype.asObservable=function(){var r=new F;return r.source=this,r},t.create=function(r,n){return new xn(r,n)},t}(F);var xn=function(e){ie(t,e);function t(r,n){var o=e.call(this)||this;return o.destination=r,o.source=n,o}return t.prototype.next=function(r){var n,o;(o=(n=this.destination)===null||n===void 0?void 0:n.next)===null||o===void 0||o.call(n,r)},t.prototype.error=function(r){var n,o;(o=(n=this.destination)===null||n===void 0?void 0:n.error)===null||o===void 0||o.call(n,r)},t.prototype.complete=function(){var r,n;(n=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||n===void 0||n.call(r)},t.prototype._subscribe=function(r){var n,o;return(o=(n=this.source)===null||n===void 0?void 0:n.subscribe(r))!==null&&o!==void 0?o:Sr},t}(x);var Et={now:function(){return(Et.delegate||Date).now()},delegate:void 0};var wt=function(e){ie(t,e);function t(r,n,o){r===void 0&&(r=1/0),n===void 0&&(n=1/0),o===void 0&&(o=Et);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=n,i._timestampProvider=o,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=n===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,n),i}return t.prototype.next=function(r){var n=this,o=n.isStopped,i=n._buffer,s=n._infiniteTimeWindow,a=n._timestampProvider,f=n._windowTime;o||(i.push(r),!s&&i.push(a.now()+f)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var n=this._innerSubscribe(r),o=this,i=o._infiniteTimeWindow,s=o._buffer,a=s.slice(),f=0;f0?e.prototype.requestAsyncId.call(this,r,n,o):(r.actions.push(this),r._scheduled||(r._scheduled=ut.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,n,o){var i;if(o===void 0&&(o=0),o!=null?o>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,n,o);var s=r.actions;n!=null&&((i=s[s.length-1])===null||i===void 0?void 0:i.id)!==n&&(ut.cancelAnimationFrame(n),r._scheduled=void 0)},t}(Wt);var Sn=function(e){ie(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var n=this._scheduled;this._scheduled=void 0;var o=this.actions,i;r=r||o.shift();do if(i=r.execute(r.state,r.delay))break;while((r=o[0])&&r.id===n&&o.shift());if(this._active=!1,i){for(;(r=o[0])&&r.id===n&&o.shift();)r.unsubscribe();throw i}},t}(Dt);var Oe=new Sn(wn);var M=new F(function(e){return e.complete()});function Vt(e){return e&&C(e.schedule)}function Cr(e){return e[e.length-1]}function Ye(e){return C(Cr(e))?e.pop():void 0}function Te(e){return Vt(Cr(e))?e.pop():void 0}function zt(e,t){return typeof Cr(e)=="number"?e.pop():t}var pt=function(e){return e&&typeof e.length=="number"&&typeof e!="function"};function Nt(e){return C(e==null?void 0:e.then)}function qt(e){return C(e[ft])}function Kt(e){return Symbol.asyncIterator&&C(e==null?void 0:e[Symbol.asyncIterator])}function Qt(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function zi(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var Yt=zi();function Gt(e){return C(e==null?void 0:e[Yt])}function Bt(e){return un(this,arguments,function(){var r,n,o,i;return $t(this,function(s){switch(s.label){case 0:r=e.getReader(),s.label=1;case 1:s.trys.push([1,,9,10]),s.label=2;case 2:return[4,et(r.read())];case 3:return n=s.sent(),o=n.value,i=n.done,i?[4,et(void 0)]:[3,5];case 4:return[2,s.sent()];case 5:return[4,et(o)];case 6:return[4,s.sent()];case 7:return s.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function Jt(e){return C(e==null?void 0:e.getReader)}function U(e){if(e instanceof F)return e;if(e!=null){if(qt(e))return Ni(e);if(pt(e))return qi(e);if(Nt(e))return Ki(e);if(Kt(e))return On(e);if(Gt(e))return Qi(e);if(Jt(e))return Yi(e)}throw Qt(e)}function Ni(e){return new F(function(t){var r=e[ft]();if(C(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function qi(e){return new F(function(t){for(var r=0;r=2;return function(n){return n.pipe(e?A(function(o,i){return e(o,i,n)}):de,ge(1),r?He(t):Dn(function(){return new Zt}))}}function Vn(){for(var e=[],t=0;t=2,!0))}function pe(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new x}:t,n=e.resetOnError,o=n===void 0?!0:n,i=e.resetOnComplete,s=i===void 0?!0:i,a=e.resetOnRefCountZero,f=a===void 0?!0:a;return function(c){var u,p,m,d=0,h=!1,v=!1,Y=function(){p==null||p.unsubscribe(),p=void 0},B=function(){Y(),u=m=void 0,h=v=!1},N=function(){var O=u;B(),O==null||O.unsubscribe()};return y(function(O,Qe){d++,!v&&!h&&Y();var De=m=m!=null?m:r();Qe.add(function(){d--,d===0&&!v&&!h&&(p=$r(N,f))}),De.subscribe(Qe),!u&&d>0&&(u=new rt({next:function($e){return De.next($e)},error:function($e){v=!0,Y(),p=$r(B,o,$e),De.error($e)},complete:function(){h=!0,Y(),p=$r(B,s),De.complete()}}),U(O).subscribe(u))})(c)}}function $r(e,t){for(var r=[],n=2;ne.next(document)),e}function K(e,t=document){return Array.from(t.querySelectorAll(e))}function z(e,t=document){let r=ce(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function ce(e,t=document){return t.querySelector(e)||void 0}function _e(){return document.activeElement instanceof HTMLElement&&document.activeElement||void 0}function tr(e){return L(b(document.body,"focusin"),b(document.body,"focusout")).pipe(ke(1),l(()=>{let t=_e();return typeof t!="undefined"?e.contains(t):!1}),V(e===_e()),J())}function Xe(e){return{x:e.offsetLeft,y:e.offsetTop}}function Kn(e){return L(b(window,"load"),b(window,"resize")).pipe(Ce(0,Oe),l(()=>Xe(e)),V(Xe(e)))}function rr(e){return{x:e.scrollLeft,y:e.scrollTop}}function dt(e){return L(b(e,"scroll"),b(window,"resize")).pipe(Ce(0,Oe),l(()=>rr(e)),V(rr(e)))}var Yn=function(){if(typeof Map!="undefined")return Map;function e(t,r){var n=-1;return t.some(function(o,i){return o[0]===r?(n=i,!0):!1}),n}return function(){function t(){this.__entries__=[]}return Object.defineProperty(t.prototype,"size",{get:function(){return this.__entries__.length},enumerable:!0,configurable:!0}),t.prototype.get=function(r){var n=e(this.__entries__,r),o=this.__entries__[n];return o&&o[1]},t.prototype.set=function(r,n){var o=e(this.__entries__,r);~o?this.__entries__[o][1]=n:this.__entries__.push([r,n])},t.prototype.delete=function(r){var n=this.__entries__,o=e(n,r);~o&&n.splice(o,1)},t.prototype.has=function(r){return!!~e(this.__entries__,r)},t.prototype.clear=function(){this.__entries__.splice(0)},t.prototype.forEach=function(r,n){n===void 0&&(n=null);for(var o=0,i=this.__entries__;o0},e.prototype.connect_=function(){!Wr||this.connected_||(document.addEventListener("transitionend",this.onTransitionEnd_),window.addEventListener("resize",this.refresh),va?(this.mutationsObserver_=new MutationObserver(this.refresh),this.mutationsObserver_.observe(document,{attributes:!0,childList:!0,characterData:!0,subtree:!0})):(document.addEventListener("DOMSubtreeModified",this.refresh),this.mutationEventsAdded_=!0),this.connected_=!0)},e.prototype.disconnect_=function(){!Wr||!this.connected_||(document.removeEventListener("transitionend",this.onTransitionEnd_),window.removeEventListener("resize",this.refresh),this.mutationsObserver_&&this.mutationsObserver_.disconnect(),this.mutationEventsAdded_&&document.removeEventListener("DOMSubtreeModified",this.refresh),this.mutationsObserver_=null,this.mutationEventsAdded_=!1,this.connected_=!1)},e.prototype.onTransitionEnd_=function(t){var r=t.propertyName,n=r===void 0?"":r,o=ba.some(function(i){return!!~n.indexOf(i)});o&&this.refresh()},e.getInstance=function(){return this.instance_||(this.instance_=new e),this.instance_},e.instance_=null,e}(),Gn=function(e,t){for(var r=0,n=Object.keys(t);r0},e}(),Jn=typeof WeakMap!="undefined"?new WeakMap:new Yn,Xn=function(){function e(t){if(!(this instanceof e))throw new TypeError("Cannot call a class as a function.");if(!arguments.length)throw new TypeError("1 argument required, but only 0 present.");var r=ga.getInstance(),n=new La(t,r,this);Jn.set(this,n)}return e}();["observe","unobserve","disconnect"].forEach(function(e){Xn.prototype[e]=function(){var t;return(t=Jn.get(this))[e].apply(t,arguments)}});var Aa=function(){return typeof nr.ResizeObserver!="undefined"?nr.ResizeObserver:Xn}(),Zn=Aa;var eo=new x,Ca=$(()=>k(new Zn(e=>{for(let t of e)eo.next(t)}))).pipe(g(e=>L(ze,k(e)).pipe(R(()=>e.disconnect()))),X(1));function he(e){return{width:e.offsetWidth,height:e.offsetHeight}}function ye(e){return Ca.pipe(S(t=>t.observe(e)),g(t=>eo.pipe(A(({target:r})=>r===e),R(()=>t.unobserve(e)),l(()=>he(e)))),V(he(e)))}function bt(e){return{width:e.scrollWidth,height:e.scrollHeight}}function ar(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}var to=new x,Ra=$(()=>k(new IntersectionObserver(e=>{for(let t of e)to.next(t)},{threshold:0}))).pipe(g(e=>L(ze,k(e)).pipe(R(()=>e.disconnect()))),X(1));function sr(e){return Ra.pipe(S(t=>t.observe(e)),g(t=>to.pipe(A(({target:r})=>r===e),R(()=>t.unobserve(e)),l(({isIntersecting:r})=>r))))}function ro(e,t=16){return dt(e).pipe(l(({y:r})=>{let n=he(e),o=bt(e);return r>=o.height-n.height-t}),J())}var cr={drawer:z("[data-md-toggle=drawer]"),search:z("[data-md-toggle=search]")};function no(e){return cr[e].checked}function Ke(e,t){cr[e].checked!==t&&cr[e].click()}function Ue(e){let t=cr[e];return b(t,"change").pipe(l(()=>t.checked),V(t.checked))}function ka(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function Ha(){return L(b(window,"compositionstart").pipe(l(()=>!0)),b(window,"compositionend").pipe(l(()=>!1))).pipe(V(!1))}function oo(){let e=b(window,"keydown").pipe(A(t=>!(t.metaKey||t.ctrlKey)),l(t=>({mode:no("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),A(({mode:t,type:r})=>{if(t==="global"){let n=_e();if(typeof n!="undefined")return!ka(n,r)}return!0}),pe());return Ha().pipe(g(t=>t?M:e))}function le(){return new URL(location.href)}function ot(e){location.href=e.href}function io(){return new x}function ao(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)ao(e,r)}function _(e,t,...r){let n=document.createElement(e);if(t)for(let o of Object.keys(t))typeof t[o]!="undefined"&&(typeof t[o]!="boolean"?n.setAttribute(o,t[o]):n.setAttribute(o,""));for(let o of r)ao(n,o);return n}function fr(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function so(){return location.hash.substring(1)}function Dr(e){let t=_("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function Pa(e){return L(b(window,"hashchange"),e).pipe(l(so),V(so()),A(t=>t.length>0),X(1))}function co(e){return Pa(e).pipe(l(t=>ce(`[id="${t}"]`)),A(t=>typeof t!="undefined"))}function Vr(e){let t=matchMedia(e);return er(r=>t.addListener(()=>r(t.matches))).pipe(V(t.matches))}function fo(){let e=matchMedia("print");return L(b(window,"beforeprint").pipe(l(()=>!0)),b(window,"afterprint").pipe(l(()=>!1))).pipe(V(e.matches))}function zr(e,t){return e.pipe(g(r=>r?t():M))}function ur(e,t={credentials:"same-origin"}){return ue(fetch(`${e}`,t)).pipe(fe(()=>M),g(r=>r.status!==200?Ot(()=>new Error(r.statusText)):k(r)))}function We(e,t){return ur(e,t).pipe(g(r=>r.json()),X(1))}function uo(e,t){let r=new DOMParser;return ur(e,t).pipe(g(n=>n.text()),l(n=>r.parseFromString(n,"text/xml")),X(1))}function pr(e){let t=_("script",{src:e});return $(()=>(document.head.appendChild(t),L(b(t,"load"),b(t,"error").pipe(g(()=>Ot(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(l(()=>{}),R(()=>document.head.removeChild(t)),ge(1))))}function po(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function lo(){return L(b(window,"scroll",{passive:!0}),b(window,"resize",{passive:!0})).pipe(l(po),V(po()))}function mo(){return{width:innerWidth,height:innerHeight}}function ho(){return b(window,"resize",{passive:!0}).pipe(l(mo),V(mo()))}function bo(){return G([lo(),ho()]).pipe(l(([e,t])=>({offset:e,size:t})),X(1))}function lr(e,{viewport$:t,header$:r}){let n=t.pipe(ee("size")),o=G([n,r]).pipe(l(()=>Xe(e)));return G([r,t,o]).pipe(l(([{height:i},{offset:s,size:a},{x:f,y:c}])=>({offset:{x:s.x-f,y:s.y-c+i},size:a})))}(()=>{function e(n,o){parent.postMessage(n,o||"*")}function t(...n){return n.reduce((o,i)=>o.then(()=>new Promise(s=>{let a=document.createElement("script");a.src=i,a.onload=s,document.body.appendChild(a)})),Promise.resolve())}var r=class extends EventTarget{constructor(n){super(),this.url=n,this.m=i=>{i.source===this.w&&(this.dispatchEvent(new MessageEvent("message",{data:i.data})),this.onmessage&&this.onmessage(i))},this.e=(i,s,a,f,c)=>{if(s===`${this.url}`){let u=new ErrorEvent("error",{message:i,filename:s,lineno:a,colno:f,error:c});this.dispatchEvent(u),this.onerror&&this.onerror(u)}};let o=document.createElement("iframe");o.hidden=!0,document.body.appendChild(this.iframe=o),this.w.document.open(),this.w.document.write(` + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + + + +

MontiCore Best Practices - Designing Tools for Command Line Interfaces

+

Some DSLs require a tool to enable general accessibility via the command line interface (CLI). +When designing a tool, we recommend some standard guidelines.

+

Designing a Tool

+

The tool provides a general interface for the functionalities developed for a language. +This includes all features such as parsing of models, saving and loading of symbol +tables, pretty printing, reporting, or export as object diagram.

+

Default Options

+

The available options are of course language-specific. +However, we suggest some default arguments for standardized access.

+
-h,--help                    Prints this help dialog
+-i,--input <file>            Reads the (mandatory) source file resp. the
+                             contents of the model
+-path <dirlist>              Sets the artifact path for imported symbols, space separated
+-modelpath <dirlist>         Sets the artifact path for imported models, space separated
+-pp,--prettyprint <file>     Prints the AST to stdout or the specified output 
+                             file (optional)
+-s, --symboltable <file>     Serializes and prints the symbol table to stdout 
+                             or the specified output file (optional) 
+-r,--report <dir>            Prints reports of the parsed artifact to the
+                             specified directory (optional). Available reports
+                             are language-specific
+-o,--output <dir>            Path of generated files (optional)
+-so,--syntaxobjects <file>   Prints an object diagram of the AST to stdout or
+                             the specified file (optional)
+-sc,--script <file>          Advanced configuration 2: through a groovy script 
+                             that allows to adapt and extend the tool workflow (optional) 
+                             (only some tools provide groovy scripting)
+-ct, --configtemplate        Advanced configuration 1: through a Freemarker template
+                             that allows to adapt the generation process (optional)
+                             (only some tools provide a template configuration call)
+
+

An example of a complete yet relatively small tool example can be found in the +JSON project.

+

Some explanation to the arguments: +* The tool is meant for handling one individual model (-i) and store the + results appropriately in files. +* Typical results are + * (1) generated files (-o) that are used in the next step of + the build process (e.g. for compilation). + * (2) the symboltable (-s) that is then used by other tools to import symbols + * (3) reports (-r) and internal information (-so), like the AST of the + parsed model usable for developers to understand what happened + * (4) and potentially also internal information on used input and generated + output files + that allows the calling build script to understand whether a redo is + needed (as part of a + larger incremental and efficient development process). +* Directories in -path are separated via spaces, i.e. each path is an argument on its own. + Example: -path a/b x/y. +* Directories in the above options -path, -o describe the root + structure that is further refined by packages (like in Java). + That means with -path a/b x/y + the actual symboltable for a Statechart de.mine.Door is found in + a/b/de/mine/Door.scsym or x/y/de/mine/Door.scsym (in that order) +* Languages typically only load other symbols rather than other models. Therefore, the argument + -path that identifies only paths containing symbols should be implemented by most languages, whereas + the argument -modelpath for identifying paths containing models is typically not required. +* Groovy-scripting (-sc, --script): A Groovy Script is meant to describe the tool internal + workflow. It controls parsing, symbol construction, reporting, code generation etc. + This kind of scripting should only become necessary when various alternative + configurations are possible. Thus, not every tool provides Groovy scripting. +* Template-scripting (-ct, --configtemplate): + It is possible to add a custom template script right before + the full generation process starts. This template is useful to customize the + generation process e.g. by defining hook points and thus injection more templates + or switching verbosity on/off.

+

Usage of the Tool-JAR

+

A note to the tool usage: +Tools do not organize the correct order of their calls. If embedded in a larger +build process, an appropriate gradle (preferred) or make it is useful for +incremental efficiency.

+

This organisation is above the tool, due to the efficiency of the +(grade or make) buildscript itself, which must be able to decide, whether a redo +is needed. If the tool was called to decide that, too much time was already wasted.

+

For a build script to decide whether to call the tool or not, a tool call should +(and actually MontiCore does) provide among others a list of files it used for input.

+

Automatically Generating a Tool-JAR

+

Note to the tool development: +To automatically derive an executable JAR from the Gradle build process for the +corresponding tool, the following template can be used.

+

// all in one tool-jar
+shadowJar {
+    manifest {
+        attributes "Main-Class": "de.monticore.${archiveBaseName.get().capitalize()}Tool"
+    }
+    archiveFileName = "MC${archiveBaseName.get()}.${archiveExtension.get()}"
+    minimize()
+    archiveClassifier = "mc-tool"
+}
+
+jar.dependsOn shadowJar
+
+This blueprint can be used in the build.gradle script to derive a JAR for the tool +class and its provided command line functionalities. +The packed JAR already contains all the necessary dependencies. +The template defines the main class and name of the JAR. +To foster automated reuse, the template has already been configured to generate +a suitable JAR for each language project without manual adjustments. +However, this requires adhering to the following conventions: +* The name of the main class is equal to the language project name (usually defined + in the settings.gradle) with the suffix Tool. + Furthermore, the first letter of the main class is always capitalized to adhere + to the Java code conventions +* The package of the main class is de.monticore
+* The generated JAR can be found in 'target/libs'

+

Example:
+For a language project MyLang we have to implement the MyLangTool.java located +in the package de.monticore. +This automatically generates the executable JAR MCMyLang.jar

+

In general, the template can be customized by specifying the corresponding main +class and JAR name definitions. +However, we recommend to use the predefined automatic approach.

+

Functional Approach

+

When implementing the tool, we recommend a functional paradigm to provide the +desired functionalities, as the too class is not about data structures but only +exists to make functions available. +In this case it would be counterproductive to store the arguments of the available +functions as attributes. +Instead, it makes more sense to pass these arguments as parameters when calling +the respective methods. +This yields several advantages:

+
    +
  • Values that have not yet been set do not have to be displayed with Optionals
  • +
  • As a result. tedious unwrapping of Optionals with corresponding error messages + is no longer necessary
  • +
  • get/set methods for attributes are not required
  • +
  • Facilitates reusability of modular functions
  • +
+

Of course, there are always trade-offs, but a more explicit functional way of +thinking should be considered more intensively, especially when it is not about +data structures but about the functions. +For instance, if intermediate results are stored for efficiency reasons, this +might a good argument to do it differently.

+

Further Information

+ + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/docs/BestPractices-Errors/index.html b/docs/BestPractices-Errors/index.html new file mode 100644 index 0000000000..ad9382b162 --- /dev/null +++ b/docs/BestPractices-Errors/index.html @@ -0,0 +1,735 @@ + + + + + + + + + + + + + + + + + + + + + BestPractices Errors - MontiCore + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + + + +

MontiCore Best Practices - Understanding Errors, Defining Errors

+

Errors happen. +Some happen because of faults in the code (we call that internal errors), +some happen because we haven't explained well how to use MontiCore and +how to use the generated code.

+

Here we try to add information how to handle occurring errors. +We use the error code for an easier identification. Error codes start with +0xand use 5(!) hex characters and thus should be at the same time +memorizable (because not completely unknown, but still not so common that +they could be taken for something else).

+

Handling Errors 0x.....

+

How to use Expressions (0xA0129)

+
    +
  • Expression is a predefined nonterminal in the MontiCore basic grammars. + Because of the infix notation of some operators and similar challenges, + it is usually not possible to use a subset of the expressions only. + For example use of ConditionalExpression may lead to a parser generation + error (i.e. 0xA0129).
  • +
  • Solutions:
  • +
  • Use nonterminal Expression and forbid all unwanted alternatives through + context conditions.
  • +
  • Think of allowing more general expressions?
  • +
  • If especially the syntax of if . then . else . shall be reused, + why not define this in a new nonterminal and ignore that the same + syntactic constructs were already available in another production.
  • +
  • Defined by: CKi, BR.
  • +
+

Further Information

+ + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/docs/BestPractices-Language-Design/index.html b/docs/BestPractices-Language-Design/index.html new file mode 100644 index 0000000000..f6b87c0153 --- /dev/null +++ b/docs/BestPractices-Language-Design/index.html @@ -0,0 +1,928 @@ + + + + + + + + + + + + + + + + + + + + + BestPractices Language Design - MontiCore + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + + + +

MontiCore Best Practices - Designing Languages

+

MontiCore provides a number of options to design +languages, access and modify the abstract syntax tree, and produce output files.

+

Some general questions on how to design a complete languages are addressed here.

+

Designing A Language

+

Correct language vs. superset?

+
    +
  • When you know that the incoming model will be correct, because they are generated + by algorithm, you can decide to pass a (slight) superset
  • +
  • This may simplify the development process for two reasons: + (a) you may derive a simpler grammar and (b) you may omit definitions of + context conditions.
  • +
  • But beware: (a) situations may change and manually changed models might come in + or (b) the is adapted by an ill-behaving pre-processor or (c) the model + may come in a wrong version.
  • +
  • This applies mainly for unreadable languages, such as JSON or XML.
  • +
  • Defined by: BR
  • +
+

Versioning an evolving language?

+
    +
  • When languages evolve, models may become invalid, because + certain (now obligatory) parts are missing, or old keywords are used.
  • +
  • We generally believe that a language that is made for long-lasting + models should not embody its version in the models (i.e. like Java, C++ and + other GPLs and unlike XML dialects).
  • +
  • When evolving a language, you should only evolve it in conservative form, i.e.
  • +
  • All new elements are optional by .?, .* or offer new alternatives (old | new)
  • +
  • Old elements or keywords are not simply removed, but + forbidden by coco warnings, marking them as deprecated for a while.
  • +
  • Downward compatibility of newer models, however, is not useful. + We can safely enforce developers should normally use the newest + versions of their tools.
  • +
  • Defined by: BR
  • +
+

Language Design in the Large

+

Making Transitively Inherited Grammars Explicit?

+
    +
  • When the grammar inclusion hierarchy becomes larger, there will be redundancy. + In: +
      grammar A { .. } ;
    +  grammar B extends A { .. } ;
    +  grammar C extends A,B { .. } ;
    +  grammar D extends B { .. } ;
    +
    + Grammars C and D actually include the same nonterminals.
  • +
  • If A is made explicit, you have more information right at hand, but also + larger grammars. It is a matter of taste.
  • +
  • A recommendation: when you use nonterminals from A explicitly, then also + make the extension explicit. However, be consistent.
  • +
+

How to Achieve Modularity (in the Sense of Decoupling)

+
    +
  • Modularity in general is an important design principle. + In the case of model-based code generation, modularity involves the following + dimensions:
      +
    1. Modelling languages
    2. +
    3. Models
    4. +
    5. Generator
    6. +
    7. Generated code
    8. +
    9. Runtime-Environment (RTE) including imported standard libraries
    10. +
    11. Software architecture (of the overall system), software stack
    12. +
    +
  • +
  • These dimensions are not orthogonal, but also not completely interrelated. + The actual organisation will depend on the form of project.
  • +
  • A weak form of modularity would be to organize things in + well understood substructures such as packages. + A deeper form of modularity deals with possibility for individual reuse + and thus an explicit decoupling of individual components. We aim for + decoupling (even if developed in the same git project).
  • +
  • Modularity also deals with extensibility and adaptation.
  • +
  • A principle for adaptation for the generator, + the generated code, and the RTE is to design each of them + like a framework with explicit extension points. + Extension points may be (empty) hook methods to be filled, Java interfaces + to be implemented and their objects injected to the code e.g., via + factories, builders or simply method parameters.
  • +
  • A principle for modularity for the generator, + the generated code, and the RTE is to design parts of them as + independent library functions (or larger: components) that can be used if needed.
  • +
  • We recommend to modularize whenever complexity overwhelms or extensibility and + adaptability are important:
      +
    1. MontiCore has powerful techniques for adaptation, extension and + composition of modelling languages (through their grammars). See the + handbook.
    2. +
    3. MontiCore has powerful techniques for the aggregation of models -- + using the same principles as programming languages, namely allowing to keep + the models independent (and thus storable, versionable, reusable) artifacts, + while they are semantically and through the generator technology well integrated. + The appropriate approach is based on using foreign models, e.g., through + import statements and sharing symbol infrastructures as described in the + handbook.
    4. +
    5. The generator provides (a) many Java classes and methods that can be overridden + (b) Freemarker templates hook points to extend and replace templates, and (c) + can be customized using a groovy script. + The generator itself is often structured along the software architecture / stack, + e.g., in frontend, application backend, database, transport layer, etc.
    6. +
    7. The generated code must be designed appropriately by the generator designer, + by generating builders, mills, etc. for each form of product - quite similar + to MontiCore itself. + The generated code is usually structured along the components or sub-systems + that the software architecture defines.
    8. +
    9. The RTE is probably well-designed if it is usable in a normal framework.
    10. +
    +
  • +
  • Please note: it is not easy to design modularity and extensibility from beginning. + Framework design has shown that this is an iterative optimizing process. + It must be avoided to design too many extension elements into the system + from the beginning, because this adds a lot of complexity.
  • +
  • Defined by: BR
  • +
+

Realizing Embedding through an Interface Nonterminal Extension Point

+

Consider the following scenario: +A language Host defines an extension point through an interface nonterminal.

+
grammar Host { A = I*; interface I; }
+
+

Another language Embedded, that has no connection to the Host language, +defines a class nonterminal E.

+
grammar Embedded { E = "something"; }
+
+

MontiCore provides alternative solutions to embed the language Embedded +into the language Host at the extension point I. All solutions presented here +require to implement a new grammar G that extends the grammars Embedded and Host +reuses the start nonterminal of the Host grammar:

+
grammar G extends Host, Embedded { start A; }
+
+

The connection between extension point and extension is performed by an additional +grammar rule in the grammar G. This can be realized in one of the following ways each one +of which has its own advantages and disadvantages:

+
    +
  1. Embedding through overriding of extension rule and implementing extension point:
      +
    • E implements I;
    • +
    • Advantage: simple embedding rule
    • +
    • Disadvantage: does not work in combination with inheritance of extension rule
    • +
    • Should therefore only be used, if E is not used anywhere else (= in not other language that is potentially used in combination with this language)
    • +
    +
  2. +
  3. Embedding through extending extension rule and implementing extension point rule:
      +
    • IE extends E implements I = "something";
    • +
    • Advantage: does work in combination with inheritance of extension rule
    • +
    • Disadvantage: cloning of RHS of the extension rule can produce inconsistencies if E is changed
    • +
    • Can be used if it is assured that this rule is adjusted whenever E is changed, e.g., by assuming that E is not modified at all
    • +
    +
  4. +
  5. Embedding through implementing extension point rule and providing extension on right-hand side:
      +
    • IE implements I = E;
    • +
    • Advantage: does work in combination with inheritance of extension rule
    • +
    • Disadvantage: introduces new level of indirection in the AST that invalidates the check whether the required abstract syntax (RHS of interface nonterminal) is present
    • +
    • Should therefore not be used, if the interface has a right-hand side
    • +
    +
  6. +
  7. Defined by: AB
  8. +
+

Recurring Language Components

+

The import statements

+
    +
  • Many models depend on other models from which they receive symbols they can rely on. + To define this kind of dependencies using import statements is convenient and well + known (e.g., from Java). We thus suggest to use the import statement in the spirit of Java.
  • +
  • import aName at the first sight means that a specific class with the qualified + name aName is used. In reality, however, Java has a very convenient convention + that class aName is always defined in the artifact (i.e. file) with the same name + aName.java and the needed symbol table is part of aName.class. So an import + statement actually locates an artifact.
  • +
  • As a consequence, we suggest:
      +
    • import aModelName refers to an artifact with name aModelName -- regardless + which kind of model is defined there.
    • +
    • All the symbols exported by the artifact aModelName are imported when using + the import statement import aModelName.
    • +
    • The imported artifact provides the desired symbols, typically stored through + an earlier tool execution in a symbol file aModelName.sym.
    • +
    • The symbol file may have specific extensions, such as autsymor cdsym.
    • +
    • Selective import (known from Java), such as import aName.innerClass + should be possible, but currently no such showcase has been made yet (beyond Java).
    • +
    • The import statement is only used to make symbols available in their simple form. + It is usually + not intended to explicate a single dependency, e.g., a configuration model + that depends on exactly one base model. Like in Java, where you import an + artifact and then still explicitly extend the contained class.
    • +
    +
  • +
  • It is methodically of interest to store at most one artifact with the same + qualified name (although it is not per se forbidden to have more). + Java then also uses the first occurring class in its classpath only.
  • +
  • In a heterogeneous language setting, it may be necessary to map symbols + from a source to a target form (e.g., state symbols to Java enum constants or state + pattern classes). There are three main options for this task:
      +
    1. Store in the desired target symbol form upon creating the symbol file. + Has some problems: (1) increases dependencies between tools, + (2) potentially several files need to be stored.
    2. +
    3. Adapt the imported symbols upon loading (recommended).
    4. +
    5. Use an explicit transformation tool between the two model processing tools + to map the initially stored symbol file to the desired format.
    6. +
    +
  • +
+

Version number in language variants

+
    +
  • As an important rule:
      +
    • Do not include version numbers in the DSL explicitly.
    • +
    +
  • +
  • The reason is that whenever you do a tooling update, all the models that have + been defined before are suddenly not valid anymore and have to be adapted. + Java has very carefully ensured that updates in the language are extensions only + and thus all old Java files are still validated with new Java compilers + (with the one exception: new keyword assert).
  • +
  • If your language is still very volatile against disruptive changes, + it may be an option at the beginning, but should be avoided with the first real release.
  • +
  • It is a burden to manage version numbers and downward compatibility through + all the versioning, especially if language components evolve with their own + versioning.
  • +
  • MontiCore provides a theory of conservative extension to avoid + explicit version controlling within the language.
  • +
  • And if needed: MontiCore and their tools provide extensive checks of + wellformedness (i.e. + context conditions), on each update a fully automated consistency check + of all existing models + should be easily establishable.
  • +
+

Further Information

+ + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/docs/BestPractices-Symbols-Scopes/index.html b/docs/BestPractices-Symbols-Scopes/index.html new file mode 100644 index 0000000000..a6d449d5ca --- /dev/null +++ b/docs/BestPractices-Symbols-Scopes/index.html @@ -0,0 +1,818 @@ + + + + + + + + + + + + + + + + + + + + + BestPractices Symbols Scopes - MontiCore + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + + + +

MontiCore Best Practices - Symbols, Scopes, Symboltables

+

MontiCore provides a number of options to design +languages, access and modify the abstract syntax tree, and produce output files.

+

The newest MontiCore release gives powerful capabilities to define and +use symbols. Symbols, scopes, and symboltables are somewhat complex +to design, but powerful in their use.

+

Designing Symbols, Scopes and SymbolTables

+

How to define a Symbol Usage without a given Symbol Definition

+
grammar E { 
+  A = Name@S; 
+  symbol S = Name; 
+}
+
+
    +
  • If you want to use a special form of symbol that shall neither be defined + inside the grammar of a language, nor shall it be imported.
  • +
  • We can define symbols of kind S in the grammar in a grammar rule that + is never reached by the parser from the start production. + Through this, MontiCore generates:
  • +
  • symbol table infrastructure for handling S symbols
  • +
  • symbol table infrastructure for resolving these in E scopes, and
  • +
  • integration of S symbols with the AST of A.
  • +
  • However, S symbols are not automatically instantiated. + This has to be described manually, e.g., by extending the symbol table + creator or via providing an adapter translating a foreign symbol into an S symbol.
  • +
  • This can be used, e.g., in these scenarios:
  • +
  • A name of a certain kind is introduced automatically the first time it occurs + in a model. If it occurs more than once, all other occurrences of the name + do not introduce new symbols. (e.g., this happens with features in FDs, + and works because features do not have a body.)
  • +
  • A name in a language E refers to a named element of another language, + but the language shall be decoupled from E. + Therefore, E introduces a symbol S and an adapter maps other, foreign + symbols to S symbols.
  • +
  • Defined by: AB, BR
  • +
+

Symbol Definition prepared for Reuse

+

grammar E { 
+  symbol Bla = "bla" Name AnotherNT; 
+}
+
+* has the effect that three things are defined: (a) concrete syntax, + abstract syntax with (b) AST element ASTBla + and (c) a symbol BlaSymbol. +* Reuse of the symbol BlaSymbol currently only works together with a reuse + of the syntax too, i.e.

+

grammar F extends E { 
+  Blubb extends Bla = "blubb" Name; 
+}
+
+ would for example be illegal, because the conservative extension paradigm + enforces AnotherNT to be included in Blubb as well. +* To allow individual reuse of symbol BlaSymbol we recommend to + restructure its definition into an interface that does not preclude + create syntax and only a minimal constraint on the abstract syntax:

+
grammar E { 
+  symbol interface Bla = Name; 
+  Bla2 implements Bla = "bla" Name AnotherNT; 
+}
+grammar F extends E { 
+  Blubb implements Bla = "blubb" Name; 
+}
+
+
    +
  • Please note that MontiCore allows that a nonterminal implements + multiple interfaces. However, only one of them may carry the symbol + keyboard property, because the newly defined symbol then is also + a subclass of the inherited symbol (in Java).
  • +
+

Loading (DeSerializing) Symbols of Unknown Symbol Kinds

+

Specific languages (e.g., CD) may provide specific symbols, of specific kinds. +A symbol import of these symbols into another language L1 has to cope with +potentially unknown kinds of symbols, even though the super kind could be known. +E.g., TypeSymbol is extended by CDTypeSymbol providing e.g., additional +visibility information. +Upon loading an CD-symboltable into an L1-tool +it may be that neither AST-class CDTypeSymbol nor superclass information about +it is available. +But, the symbols of the unknown kind should (and can) be loaded as symbols of a more abstract kind.

+

Loading the symbols of the unknown kind as symbols of the specific known kind is possible in multiple ways. +Options would be + 1. adapt the L1-tool to know about the new symbols, or + 2. the L1-tool has been written in such a way that new classes can be added + through appropriate class loading, or + 3. the L1-tool is configurable in handling unknown symbol kinds as explained below.

+

Loading Symbols as Symbols of Another Kind

+

Symbols of an unknown source kind (e.g., CDTypeSymbol) may easily be loaded as +symbols of a known kind (e.g., TypeSymbol) when the source kind provides +all mandatory attributes (i.e. those without defaults) of the symbol class. +This is especially the case if the source kind is a subclass of the known +kind.

+

This behavior can be configured in the global scope by calling the method putSymbolDeser(String, ISymbolDeser), +where the unknown source kind is encoded as string (here: CDTypeSymbol) and is mapped to +an appropriate DeSer (here for TypeSymbol). +For instance the call would be +putSymbolDeSer("de.monticore.cdbasis._symboltable.CDTypeSymbol", new TypeSymbolDeSer()).

+

Because the global scope is a singleton, this configuration can be e.g., called in or shortly +after constructing the global scope. However, this would still encode the name of the unknown +symbol kind in the L1-tool, although it prevents any actual dependency to the imported tools.

+

The method can also be called from a CLI to dynamically configure the deserialization, +e.g., the information be fed to the L1-tool via parameters, e.g., like +

  java L2Tool --typeSymbol=de.monticore.cdbasis._symboltable.CDTypeSymbol
+              --functionSymbol=de.monticore.cdbasis._symboltable.CDMethodSymbol
+

+

Converting Stored Symbol Tables

+

If the unknown symbol kinds do have different attributes or some extra information +needs to be calculated in the new symbols, then either the L1-tool needs to be adapted or +the serialized symbol table can be transformed to another +serialized symbol table where the kind information is transformed as required as an +intermediate step between the tools providing and reading the symbol tables.

+

Further Information

+ + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/docs/BestPractices-Syntax-Design/index.html b/docs/BestPractices-Syntax-Design/index.html new file mode 100644 index 0000000000..b9f3dcecbd --- /dev/null +++ b/docs/BestPractices-Syntax-Design/index.html @@ -0,0 +1,1021 @@ + + + + + + + + + + + + + + + + + + + + + BestPractices Syntax Design - MontiCore + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + + + +

MontiCore Best Practices - Concrete and Abstract Syntax

+

MontiCore provides a number of options to design +languages, access and modify the abstract syntax tree, and produce output files.

+

This (currently unsorted and evolving) list of practices discusses solutions +that we identified and applied as well as alternatives and their specific +advantages and drawbacks. The list also mentions where the solutions have been +found and where they have been applied first.

+

This file is partially temporary and also contains compact (incomplete) solutions. +More detailed descriptions of best practices can be found in the +MontiCore handbook. +Some of the best practices here will also be incorporated in the next version +of the reference manual.

+

Designing Concrete and Abstract Syntax

+

Specific keywords that shall be used as normal names elsewhere

+
    +
  • A = "foo" B introduces foo as a keyword that cannot be used as an ordinary + (variable) name anymore. To prevent that we may use:
  • +
  • A = key("foo") B instead, which introduces foo only at that specific point.
  • +
  • In general, we use all Java keywords as permanent, but abstain from other + permanent keywords, especially if they are only used for a specific purpose in a composable + sublanguage, like in in the OCL.
  • +
  • Defined by: BR
  • +
+

Complex Token clashing with other uses of sub-tokens

+
    +
  • For example <- is supposed to be used as arrow, but in an expression + 3<-10 is also syntactically allowed.
  • +
  • The problem: as soon as "<-" is defined as a token in any part of the + current or any extended grammars, the expression 3<-10 would not be + parsed as 3 < -10 anymore.
  • +
  • Solutions:
      +
    1. We might decompose the token to "<" "-" which in its consequence + means that we put more burden to the context-free parser and less to the + regular scanner. ("scannerless parsing")
        +
      • Drawback: spaces would now be allowed inbetween.
      • +
      +
    2. +
    3. Decompose the token to {noSpace(2)}? "<" "-". This (slightly + hacking approach) prevents spaces between two tokens.
    4. +
    +
  • +
  • The challenge: when designing a language component, we don't know yet + what further uses will bring. This may include sub-tokens to come up + with new interactions. This would require an (already defined) grammar + with the complex token to be adapted afterwards (and thus conflict + with the library idea).
  • +
  • Remark: A forthcoming enhancement will provide an improved solution, + keeping parsing efficiency and compositionality of grammars.
  • +
  • Defined by: BR
  • +
+

Extension forms in a component grammar

+

A component grammar is meant for extension. MontiCore therefore provides five(!) +mechanisms that can be used when a sub-grammar shall extend a super-grammar. +The solutions are briefly discussed here:

+

1. Interface in the super-grammar

+
    +
  • Introduce an interface and allow building of sub-nonterminals in sub-grammars.
    +
    component grammar A {  
    +  interface X;
    +  N = "bla" X "blubb";
    +}
    +grammar B extends A {
    +  Y implements X = "specific" "thing"
    +}
    +
  • +
  • Advantage: Multiple extensions are possible at the same time. + An NT Y can also implement multiple interfaces (like in Java).
  • +
  • Disadvantage: the designer of A explicitly has to design the hole + (extension point) X and add it into the production.
  • +
+

2. Overriding (empty) nonterminal from the super-grammar

+
    +
  • Use a normal nonterminal X and override it in a sub-grammar.
    +
    component grammar A {  
    +  X = "";
    +  N = "bla" X "blubb";
    +}
    +grammar B extends A {
    +  @Override
    +  X = "my" "thing";
    +}
    +
  • +
  • Advantage: Default implementation "" exists, no explicit filling needed.
  • +
  • Disadvantage:
      +
    1. The designer of A explicitly has to design the hole (extension point) X + and inject it into other places.
    2. +
    3. Only one overriding alternative possible (i.e. multiple overriding in + subgrammars are allowed, but only the most specific resides).
    4. +
    +
  • +
+

3. Extending nonterminal from the super-grammar.

+
    +
  • Use an empty normal nonterminal X and extend it in a sub-grammar. +
    component grammar A {  
    +  X = ;
    +  N = "bla" X "blubb";
    +}
    +grammar B extends A {
    +  Y extends X = "this";
    +}
    +
  • +
  • Advantage: Default implementation "" exists, no explicit filling needed.
  • +
  • Disadvantage: + The designer of A explicitly has to design the hole (extension point) X + and inject it into other places.
  • +
  • Care: Extension still allows the (empty) alternative X.
  • +
+

4. Using external nonterminals in the super-grammar.

+
    +
  • +

    Mark nonterminal X as external. +

    component grammar A {  
    +  external X;
    +  N = "bla" X "blubb";
    +}
    +grammar B extends A {
    +  X = "your";
    +}
    +

    +
  • +
  • +

    Advantage: Explicitely marks a nonterminal as hole (extension point) in the grammar.

    +
      +
    • Please observe that interface terminals may or not may be meant to be + extended in sub-grammars. external is clearer here.
    • +
    +
  • +
  • Disadvantage:
      +
    1. Leads to more objects in the AST. Both classes a.X and b.X are + instantiated and a.X only links to b.X.
    2. +
    3. Only one filling of the hole is possible.
    4. +
    +
  • +
+

5. Overriding the whole production.

+
    +
  • If you don't want to add a hole at any possible place of extension: +
    component grammar A {  
    +  N = "bla" "blubb";
    +}
    +grammar B extends A {
    +  @Override
    +  N = "bla" "my" "blubb" "now";
    +}
    +
  • +
  • Advantage: Compact definition. No "framework thinking" needed (no need + to forecast all potential extension points)
  • +
  • Disadvantage:
      +
    1. The entire production is overriden (some redundancy).
    2. +
    3. Only one overriding alternative possible.
    4. +
    +
  • +
  • Combinations are possible. Dependent on the anticipated forms of + adaptations option 1, 2, 3 and 5 are in use.
  • +
  • Defined by: BR
  • +
+

Avoid empty nonterminals (if body is known)

+
    +
  • +

    From the two variants: +

    A = "bla" B? C*;
    +B = "B's body" ;
    +C = "C's body" ;
    +
    + and +
    A = "bla" B C;
    +B = ("B's body")? ;
    +C = ("C's body")* ;
    +
    + we generally prefer the first one, i.e. add multiplicities when + using a nonterminal.

    +
  • +
  • +

    This is a matter of taste, but useful to keep this consistent.

    +
  • +
  • Sometimes exceptions are useful.
  • +
  • Defined by: SVa, BR
  • +
+

Avoid complex tokens (1)

+
    +
  • The token definitions can only define regular expressions. + Furthermore, the token parser (i.e. the lexer) does not consider backtracking.
  • +
  • If combinations of characters may be split into several token sequences + this leads to problems. E.g. in 3-2 and (-2) the - has different roles. + Unfortunately these problems also occur when composing languages + that make excessive use of (conflicting) token definitions.
  • +
  • Solution: instead of defining a complex token like +
      token NegativeNat = "-" Digits;
    +
    + we split the token and allow individual parsing into nonterminals: +
      NegativeNat = negative:["-"] Digits {noSpace()}? 
    +
    + (where we assume Digits is a given token).
  • +
  • +

    As a workaround, we use the semantic predicate {noSpace()}? that ensures + that between the two last processed token there is no space inbetween. + If one of the tokens is optional we have to split the alternatives: +

    SignedNatLiteral = 
    +        (negative:["-"]) Digits {noSpace()}? |
    +                         Digits;  
    +

    +
  • +
  • +

    Adding a handcoded function like getValue() via astrule or the + TOP-mechanism allows to use SignedNatLiteral like a token.

    +
  • +
  • Scannerless parsing is a principle where the tokens are reduced to simple + characters (or character classes, such as [a-z]). Scannerless parsing + generally avoids this kinds of problems, but is way slower. + This kind of solution tries to mediate between the two extremes benefitting + from both approaches.
  • +
  • Defined by: MB, in: MCCommonLiterals.mc4 and other literals grammars.
  • +
+

Avoid complex tokens (2)

+
    +
  • Same general problem. In language composition conflicting tokens may lead to issues.
  • +
  • For example Java allows 42. as a literal of type float. + UML allows to define cardinalities like [42..44]. Composition clashes.
  • +
  • +

    Solution: In a Java sublanguage we split the token: +

    SignedBasicFloatLiteral =
    +   ... 
    +   | Digits "." {noSpace()}? ... ;
    +

    +
  • +
  • +

    This will ensure that [42..44] will be parsed like [ 42 .. 44 ] + in a language composition as well.

    +
  • +
  • It generally seems that overly complex composed tokens may lead to issues + especially if the language allows compact models. Suboptimal tokens may be e.g. + "[[" (vs. nested lists), or + "<-" (vs. 3 < -2).
  • +
  • Defined by: MC team.
  • +
+

How to define keyword enumerations

+
    +
  • A finite set of keyword-based alternatives can be defined in several forms:
  • +
  • Standard three keywords act as alternative: +
    N = (["public"] | ["protected"] | ["private"]) ;
    +
  • +
  • Effects:
      +
    1. not extensible without overriding and repetition
    2. +
    3. introduces boolean flags, where only one can be true at a time
    4. +
    +
  • +
  • Use an enumeration nonterminal +
    enumeration E = "public" | "protected" | "private" ;
    +N = E ;
    +
  • +
  • Effects:
      +
    1. not extensible
    2. +
    +
  • +
  • Use an interface and subclasses with almost empty body: +
    interface E ;
    +P1 implements E = "public"    ;
    +P2 implements E = "protected" ;
    +P3 implements E = "private"   ;
    +N = E ;
    +
  • +
  • Effects:
      +
    1. very extensible in various ways (even beyond mere keywords)
    2. +
    3. visitor can easily address the keywords (i.e. by visit(P1) ...)
    4. +
    5. Disadvantage: Clumsy notation and visitors are always needed.
    6. +
    +
  • +
  • Defined by: SVa, BR.
  • +
+

Common AST-Access to Syntactically Similar Nonterminals

+
    +
  • +

    Sometimes the following occurs (e.g. in associations of CDs or + interactions of SD):
    +

    A = X Y Z;
    +B = Z Y X;
    +

    +
  • +
  • +

    The concrete syntax differs (in order), but the syntactic concepts are + the same.

    +
  • +
  • +

    To allow common access, a common interface nonterminal is introduced + that is not used in the grammar directly. + This doesn't change the concrete syntax but allows common AST access: +

    interface F = X Y Z;      // order is irrelevant
    +A implements F = X Y Z;
    +B implements F = Z Y X;
    +

    +
  • +
  • +

    Defined by: BR.

    +
  • +
+

How and when to use Names for Nonterminals

+
    +
  • Normally names like expr:Exprcan be avoided which makes a grammar easier to read + and more concise, i.e. Expr alone has the same effect.
  • +
  • There may be two reasons to use a name:
      +
    1. Nonterminal X occurs several times and we want to distinguish: + left:Expr "*" right:Expr
    2. +
    3. We can also use the name to describe the purpose of the nonterminal, i.e. +
       MyVariable implements Variable = Name "=" initial:Expression;
      +
      +vs: +
       MyParameter implements Variable = Name "=" default:Expression;
      +
    4. +
    +
  • +
  • Defined by: BR.
  • +
+

Further Information

+ + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/docs/BestPractices/index.html b/docs/BestPractices/index.html new file mode 100644 index 0000000000..e14d3d5156 --- /dev/null +++ b/docs/BestPractices/index.html @@ -0,0 +1,789 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Best Practices - MontiCore + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + + + +

MontiCore Best Practices - A Guide For Small Solutions

+

MontiCore provides a number of options to design +languages, access and modify the abstract syntax tree, and produce output files.

+

This (currently unsorted and evolving) list of practices discusses solutions +that we identified and applied as well as alternatives and their specific +advantages and drawbacks. The list also mentions where the solutions have been +found and where they have been applied first.

+

The list is subdivided into several MD files tackling various language design areas.

+

The list is partially temporary and also contains compact (incomplete) solutions. +More detailed descriptions of best practices can be found in the +MontiCore handbook. +Some of the best practices here will also be incorporated in the next version +of the handbook.

+ +

includes: Language Design in the Large

+ +

Further Information

+ + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/docs/Bild6.png b/docs/Bild6.png new file mode 100644 index 0000000000..c2d2b0b07c Binary files /dev/null and b/docs/Bild6.png differ diff --git a/docs/BuildMontiCore/index.html b/docs/BuildMontiCore/index.html new file mode 100644 index 0000000000..289515adc9 --- /dev/null +++ b/docs/BuildMontiCore/index.html @@ -0,0 +1,777 @@ + + + + + + + + + + + + + + + + + + + + + BuildMontiCore - MontiCore + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + + +

+
+

+

+

MontiCore - Language Workbench And Development Tool Framework

+ +

General disclaimer

+

(Repeated from the BSD 3 Clause license):

+

This software is provided by the copyright holders and contributors +"as is" and any expressed or implied warranties, including, but not limited +to, the implied warranties of merchantability and fitness for a particular +purpose are disclaimed. In no event shall the copyright holder or +contributors be liable for any direct, indirect, incidental, special, +exemplary, or consequential damages (including, but not limited to, +procurement of substitute goods or services, loss of use, data, or +profits, or business interruption) however caused and on any theory of +liability, whether in contract, strict liability, or tort (including +negligence or otherwise) arising in any way out of the use of this +software, even if advised of the possibility of such damage.

+

Included Software

+

This product includes the following software: +* AntLR +* FreeMarker

+

Contribution

+

When you want to contribute: Please make sure that your complete workspace only +uses UNIX line endings (LF) and all files are UTF-8 without BOM. On Windows, you should +configure git to not automatically replace LF with CRLF during checkout +by executing the following configuration:

+
git config --global core.autocrlf input
+
+

Build MontiCore

+

MontiCore is currently built using Gradle.

+

Please note that from the top level build script, not everything is built and +all tests executed. It is a deliberate decision, to exclude some of the longer +lasting tasks.

+
    +
  • build the productive code (including the unit tests, ~8 min)
  • +
  • gradle buildMC
  • +
  • skipping the unit tests: gradle assembleMC
  • +
  • run integration tests (which are not included in the unit tests, ~30 min)
  • +
  • all integration tests
      +
    • gradle testIT
    • +
    +
  • +
  • Integration tests of the generator:
      +
    • gradle -p monticore-test/it build
    • +
    +
  • +
  • EMF Integration tests of the generator (only test collection not included in testIt):
      +
    • gradle -p monticore-test/it build -PbuildProfile=emf
    • +
    +
  • +
  • Experiments (from the Handbook) as integration tests:
      +
    • gradle -p monticore-test/01.experiments build and
    • +
    • gradle -p monticore-test/02.experiments build
    • +
    +
  • +
  • Grammar integration tests:
      +
    • gradle -p monticore-test/monticore-grammar-it build
    • +
    +
  • +
  • clean:
  • +
  • call gradle clean
  • +
  • cleaning integration tests:
      +
    • using gradle gradle clean within the corresponding subproject (see above)
    • +
    +
  • +
+

Further Information

+ + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/docs/DevelopedLanguages/index.html b/docs/DevelopedLanguages/index.html new file mode 100644 index 0000000000..367af035c1 --- /dev/null +++ b/docs/DevelopedLanguages/index.html @@ -0,0 +1,962 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Languages and Language Components - MontiCore + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + + +

Languages and Language Components Developed with MontiCore

+

The MontiCore language workbench has been under development for a while +already and of course has been used by our group to develop +many languages. Not all of those languages are publicly +available and some of these languages are equipped with tools based +on MontiCore 5.

+

Many of these languages are composed of sublanguages and +thus potentially build on each +other. The available languages can be used as is, but +also be adapted, extended and further composed.

+

Please also have a look at our literature references for further +information on many of the languages.

+

Information about Languages and Language Components

+
    +
  • +

    MontiCore Handbook. + The handbook describes how to use MontiCore as an out-of-the-box + language workbench), but also as a grey box tooling framework. + It thus also gives an overview over a number of core mechanisms of MontiCore.

    +
  • +
  • +

    List of MontiCore core Language Components. + MontiCore concentrates on reuse. It therefore offers a set of + predefined language components where the main artifact is usually a + component grammar. Reusing these language components allows + language developers to define their own language as a + composition of reusable assets efficiently. Reusable assets describe among others + several sets of literals, expressions and types, which are relatively + freely composable.

    +
  • +
  • +

    List of languages. + This is a another list of newer MontiCore + languages that can be used out of the box or also composed. + Many of them already are rather stable, but some of them also undergo a + lively development and enhancement. + These complete languages are usually composed of a number of language + components.

    +
  • +
  • +

    MontiCore topic list + Describes various research topics which MontiCore builds on or + where MontiCore has been used as foundation.

    +
  • +
+

Github Available Languages

+ +

Further Languages (e.g. used in scientific and industrial projects)

+
    +
  • +

    MontiArc ADL is an architectural definition language for + component and connector models with enhanced connection facilities, + hierarchical decomposition etc. and provides a simulator + [HRR12,BHH+17,Wor16,Hab16].

    +
  • +
  • +

    MontiArcAutomaton ADL is an extension of the + architectural definition language MontiArc using automata to describe + behavior. + Some applications e.g. are of robotics, production, or InternetOfThings. + [BKRW17a,HKR+16,BRW16a,Wor16].

    +
  • +
  • +

    UML/P is a derivation from UML, especially suited for agile +development. See language definition and usage method in +[Rum17,Rum16,Sch12].

    +
      +
    • UML/P Class Diagrams for data structures
    • +
    • UML/P Object Diagrams for exemplaric situations: usable for + constructive development as well as testing
    • +
    • OCL/P as Java-variant of the OCL with a nice logic, + set-comprehension etc.
    • +
    • UML/P Statecharts for behavior
    • +
    • UML/P Sequence Diagrams for interaction
    • +
    • Activity Diagrams for workflows and requirements + (an extension to the books)
    • +
    +
  • +
  • +

    Delta-MontiArc [HRRS12,HKR+11,HRRS11] + is a DSL for expressing deltas on MontiArc component definitions + which allows to model software product lines in a bottom up way.

    +
  • +
  • +

    MontiArcHV [HRR+11] + allows specifying component variability fully integrated within the + component hierarchy located at variation points in component definitions.

    +
  • +
  • +

    Java as full language as well as source for Java expressions, + statements, attribute or method definitions.

    +
  • +
  • +

    FeatureDSL is a DSL for feature diagrams in software product line + approaches.

    +
  • +
  • +

    DeltaCD is a DSL for expressing deltas on class diagrams + which allows to model software product lines in a bottom up way

    +
  • +
  • +

    Aerospace Constraint Specification Language is a DSL used to + specify critical situations in an airspace including airplanes, + weather, flight conditions and much more. [ZPK+11]

    +
  • +
  • +

    clArc DSL Family: [PR13]

    +
      +
    • Cloud Architecture Description Language: used to model of + architectures of cloud-based systems; based on MontiArc.
    • +
    • Target Description Language: used to model the infrastructure + architecture of cloud-based systems.
    • +
    • Mapping Description Language: used to model deployments + between software and infrastructure architectures.
    • +
    • Architecture Scenario Description Language: used to model + scenario-based test cases for software architectures.
    • +
    +
  • +
  • +

    I/O-TestDSL
    + for the definition of stream-based, input-output + related black-box tests for architecture definition languages + like MontiArc.

    +
  • +
  • +

    LightRocks, a modelling language for robotic assembly processes.

    +
  • +
  • +

    cdViews is a DSL used to model partial views on class diagrams

    +
  • +
  • +

    RBAC for Role-Based Access Control in enterprise information systems.

    +
  • +
  • +

    MontiWis [[RR13,Rei16]] + is a family of DSLs for the model-based, generative + development of web information systems among others based on + class diagrams, activity diagrams and views.

    +
  • +
  • +

    HQL: Hibernate Query Language that maps to hibernate based + executions.

    +
  • +
  • +

    SQL the well known DB query language; used for embedding + e.g. into other languages.

    +
  • +
  • +

    XML the basic infrastructure for all XML dialects

    +
  • +
  • +

    CarOLO DSLs for autonomic driving. This among others + contains a DSL for defining road scenarios with moving vehicles + as well a obstacles suited for laser, lidar, radar and camera + sensors. This languages are part of the Darpa Urban Challenge 2007. + [BR12b,BR12,Ber10,BR09].

    +
  • +
  • +

    ProcEd a Web-based Editing Solution for Domain-Specific + Process-Engineering + [BGR09].

    +
  • +
  • +

    MontiWeb a modular development approach for + Web Information Systems (which was later succeeded by MontiWIS) + [DRRS09].

    +
  • +
  • +

    C++ and its sublanguages for expressions, statements and + definitions (but no generic types, no defines).

    +
  • +
  • +

    MontiCore itself uses a family of DSLs for the definition of + DSLs, i.e., their grammars. [HR17]

    +
  • +
+

Further Information

+ + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/docs/Download/index.html b/docs/Download/index.html new file mode 100644 index 0000000000..20568c24f1 --- /dev/null +++ b/docs/Download/index.html @@ -0,0 +1,869 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Downloads - MontiCore + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + +

Downloads

+ + + +

The following tools for MontiCore can be used from the command line and thus e.g. well be embedded in scripting. Their languages as well as related tooling are currently available for download:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ArtifactDescriptionDownload
MontiCore ToolTool for processing grammars.Link
MontiCore RuntimeMontiCore's runtime library.Link
Automaton Example ProjectExample project that can be used with the MontiCore CLI tool.Link
Automaton Example Project in GradleExample project that can be used with Gradle.Link
CD ToolTool for a Class Diagram language.Link
FACT ToolTool for a Feature Diagram language.Link
FeatureConfiguration ToolTool for a Feature Diagram language.Link
FeatureConfigurationPartial ToolTool for a Feature Diagram language.Link
FeatureDiagram ToolTool for a Feature Diagram language.Link
MLC ToolTool for grouping MontiCore language components.Link
OCL ToolTool for an Object Constraint Language.Link
OD4Data ToolTool for an Object Diagram language.Link
OD4Report ToolTool for an Object Diagram language.Link
SD4Development ToolTool for a Sequence Diagram language.Link
Statecharts ToolTool for a Statechart language.Link
JSON ToolTool for a JSON language.Link
XML ToolTool for an XML language.Link
+

Please note the MontiCore 3-Level License of these tools.

+

Further Information

+ + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/docs/Eclipse.png b/docs/Eclipse.png new file mode 100644 index 0000000000..e3fe32ce2d Binary files /dev/null and b/docs/Eclipse.png differ diff --git a/docs/GenAutomataAST.png b/docs/GenAutomataAST.png new file mode 100644 index 0000000000..3ca12b4c68 Binary files /dev/null and b/docs/GenAutomataAST.png differ diff --git a/docs/GenAutomataParser.png b/docs/GenAutomataParser.png new file mode 100644 index 0000000000..679d00a0fc Binary files /dev/null and b/docs/GenAutomataParser.png differ diff --git a/docs/GenAutomataScopes.png b/docs/GenAutomataScopes.png new file mode 100644 index 0000000000..fd596dd400 Binary files /dev/null and b/docs/GenAutomataScopes.png differ diff --git a/docs/GenAutomataSymbols.png b/docs/GenAutomataSymbols.png new file mode 100644 index 0000000000..c0f1b77749 Binary files /dev/null and b/docs/GenAutomataSymbols.png differ diff --git a/docs/GenAutomataVisitors.png b/docs/GenAutomataVisitors.png new file mode 100644 index 0000000000..40e3789ad1 Binary files /dev/null and b/docs/GenAutomataVisitors.png differ diff --git a/docs/GettingStarted/index.html b/docs/GettingStarted/index.html new file mode 100644 index 0000000000..21545ab8c7 --- /dev/null +++ b/docs/GettingStarted/index.html @@ -0,0 +1,2183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Getting Started - MontiCore + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + + +

Getting Started with MontiCore

+

This page describes the technical installation and usage of MontiCore for +language developers. This page further inspects a simple example grammar and the +Java classes and other artifacts generated from this grammar. After installing +MontiCore as described on this page, it can be used to develop new modeling +languages and generators as described in subsequent chapters.

+

MontiCore provides a command line interface (CLI) tool and can easily be used +with Gradle. The Gradle integration enables developers to easily employ +MontiCore in commonly used integrated development environments (IDEs), such as +Eclipse and IntelliJ IDEA. We strongly recommend to work through the section +about the CLI tool first. The CLI section contains information about an example +MontiCore project and the files generated by MontiCore. It also shortly explains +some key features of MontiCore.

+

Detailed information about all configuration options that can be used in +the MontiCore CLI tool and in MontiCore Gradle projects are explained in +Chapter 16 of the handbook. More +information about the example Automata language are available in +Chapter 21 of the handbook.

+

Prerequisites: Installing the Java Development Kit

+

We start with the JDK: Please perform the following steps to install the +Java Development Kit (JDK) and validate that the installation was successful:

+
    +
  • Install a JDK with at least version 11 provided by Oracle or OpenJDK.
  • +
  • Make sure the environment variable JAVA_HOME points to the installed JDK, and + not to the JRE, e.g., the following would be good:
      +
    • /user/lib/jvm/java-11-openjdk on UNIX or
    • +
    • C:\Program Files\Java\jdk-11.* on Windows. +You will need this in order to run the Java compiler for compiling +the generated Java source files.
    • +
    +
  • +
  • Also make sure that the system variable is set such that the Java + compiler can be used from any directory. JDK installations on UNIX + systems do this automatically. On Windows systems, the bin + directory of the JDK installation needs to be appended to the PATH + variable, e.g. %PATH%;%JAVA_HOME%.
  • +
  • Test whether the setup was successful. Open a command line shell in + any directory. Execute the command javac -version. If this command + is recognized and the shell displays the version of the installed + JDK (e.g., javac 11.0.5), then the setup was successful.
  • +
+

Now we have the prerequisites to run MontiCore from the command line. +The JDK installation is also required for using MontiCore with Gradle.

+

Install and Use the MontiCore Command Line Interface

+

This section describes instructions to perform the following first steps +to use MontiCore as an CLI tool:

+
    +
  • Installation of the MontiCore distribution file.
  • +
  • Grammar inspection
  • +
  • Running the MontiCore generator
  • +
  • Compiling the product
  • +
  • Running the product, i.e. the Automata tool with an example model + example/PingPong.aut.
  • +
+

Installation

+

For installing MontiCore, perform the following steps:

+
    +
  • Download the example Automata MontiCore project: +
    // MontiCore zip distribution source
    +https://www.monticore.de/download/monticore.tar.gz
    +
  • +
  • Unzip the archive. The unzipped files include a directory called + mc-workspace containing the executable MontiCore tool + monticore.jar along with a directory src containing + handwritten Automata DSL infrastructure, a directory hwc + containing handwritten code that is incorporated into the generated + code, and a directory example containing an example model of the + Automata DSL. +
    // MontiCore zip distribution content in directory mc-workspace
    +Automata.mc4
    +monticore.jar
    +monticore-rt.jar
    +src/automata/AutomataTool.java
    +src/automata/visitors/CountStates.java
    +src/automata/prettyprint/PrettyPrinter.java
    +src/automata/cocos/AtLeastOneInitialAndFinalState.java
    +src/automata/cocos/StateNameStartsWithCapitalLetter.java
    +src/automata/cocos/TransitionSourceExists.java
    +hwc/automata/_ast/ASTState.java
    +hwc/automata/_symboltable/AutomatonSymbol.java
    +hwc/automata/_symboltable/AutomataSymbols2Json.java
    +hwc/automata/_symboltable/AutomatonSymbolDeser.java
    +hwc/automata/_symboltable/AutomataGlobalScope.java
    +example/PingPong.aut
    +
  • +
+

Inspect the Example Grammar

+

MontiCore is a language workbench. It supports developers in developing +modular modelling languages. The core of MontiCore is its grammar +modelling language (cf. Chapter 4 of the MontiCore handbook), +which is used by developers for modelling +context-free grammars. A MontiCore grammar defines (parts of) the +abstract and concrete syntax of a language. Each grammar contains +nonterminals, production rules, and may extend other grammars. At most +one rule is marked as the start rule.

+

It is a key feature of MontiCore that it allows a grammar to reuse and extend other grammars. +In an extension all the nonterminals +defined in the extended grammars can be reused or even overridden. This +form of extension allows to achieve several effects:

+
    +
  • Language (i.e. grammar) components can be reused and integrated in + larger languages, composed of several components.
  • +
  • Individual nonterminals can be reused (like classes) from a library.
  • +
  • A given language can be extended, allowing to add additional + alternatives inside a language.
  • +
+

Component grammars and grammar extensions are detailedly discussed in +Chapter 4 of the MontiCore handbook.

+
grammar Automata extends de.monticore.MCBasics {
+
+  symbol scope Automaton =
+    "automaton" Name "{" (State | Transition)* "}" ;
+
+  symbol State =
+    "state" Name
+    (("<<" ["initial"] ">>" ) | ("<<" ["final"] ">>" ))*
+    ( ("{" (State | Transition)* "}") | ";") ;
+
+  Transition =
+    from:Name "-" input:Name ">" to:Name ";" ;
+}
+
+
Listing 2.2: The Automata grammar.
+ +

In the following, we inspect the MontiCore grammar of the Automata +language. Navigate your file explorer to the unzipped mc-workspace +directory. The directory contains the file Automata.mc4. This file +contains the MontiCore grammar depicted in Listing 2.2. MontiCore grammars end with +.mc4.

+

The definition of a MontiCore grammar starts with the keyword grammar, +followed by the grammar's name. In this example, the grammar is +called Automata. The grammar's name is optionally followed by the +keyword extends and a list of grammars that are extended by the +grammar. In this example, the Automata grammar extends the grammar +de.monticore.MCBasics.

+
+
+ +Tip 2.3 MontiCore Key Feature: Composition +
+

The MontiCore language workbench allows to compose language components +by composing grammars and also to reuse all infrastructure, such as +context conditions, symbol table infrastructure, generator parts and +handwritten extensions.

+ +

In the example the Automata grammar extends the grammar +de.monticore.MCBasics and thus reuses its functionality.

+ +

MontiCore comes with an extensive library of predefined language +components.

+
+ +

Grammars can also have a package and import other grammars. If a grammar +has a package, then the package declaration must be the first statement +in the grammar and is of the form package QualifiedName where +package is a keyword and QualifiedName is an arbitrary qualified +name (e.g. de.monticore). The optional grammar imports follow the +package definition. Every import is of the form import +QualifiedName. The Automata example grammar file does neither +contain a package declaration nor imports. The grammar extended by the +Automata grammar is specified by its fully qualified name.

+

As usual in context-free grammars, production rules have a left-hand +side and a right-hand side. The left-hand side contains the possibly +annotated name of a nonterminal. The left-hand side is followed by the +terminal = and the right-hand side. Nonterminal names start with an +upper-case letter. For instance, the Automata grammar contains the +nonterminals Automaton, State, and Transition. A single +nonterminal can be provided with the start keyword. Then, the +nonterminal is the starting symbol of the grammar. If no nonterminal is +marked with start, then the first nonterminal of the grammar +becomes the starting symbol by default. In the Automata grammar, the +Automaton nonterminal is the starting symbol.

+

The other possible keywords for nonterminals influence the generated +classes for the abstract syntax tree as well as the generated symbol +table infrastructure. Details can be found in +Chapter 4 and Chapter 9 of the MontiCore handbook. +For example, the +Automaton nonterminal is marked with symbol and scope. The +keyword symbol makes the MontiCore generator generate a symbol +class for the nonterminal. Intuitively stated, the keyword scope +instructs the MontiCore generator to construct a symbol table +infrastructure that +opens a scope when the production is processed. The following sections +explain the effects of specifying the Automaton nonterminal with the +keywords symbol and scope in more detail. Terminals are surrounded +by quotation marks. The Automata grammar, for example, inter alia +contains the terminals automaton, state, {, }, and ;.

+

The right-hand sides of grammar productions consist of nonterminals, +terminals, and semantic predicates, may use cardinalities (*, +, +?), and introduce alternatives via the terminal | as known from +regular expressions. Details can be found in +Chapter 4 of the MontiCore handbook. +The right-hand side of +the production defining the nonterminal Automaton, for example, uses +the terminal automaton and the nonterminals Name, State, and +Transition. The nonterminal Name is not defined in the grammar +Automata. Thus, it must be defined in one of the extended grammars. In +this case, Name is defined in the grammar MCBasics and is reused by +the grammar Automata. For distinguishing different usages of +nonterminals on right-hand sides, they can be named. For example, the +right-hand side of the production defining the nonterminal Transition +uses the Name nonterminal twice. The first usage is named input and +the second usage is named to. MontiCore also supports interface and +external nonterminals for introducing extension points as detailedly +described in Chapter 4 of the MontiCore handbook. +However, the example grammar does not use these concepts.

+
automaton PingPong {
+  state NoGame <<initial>>;
+  state Ping;
+  state Pong <<final>>;
+
+  NoGame - startGame > Ping;
+
+  Ping - stopGame > NoGame;
+  Pong - stopGame > NoGame;
+
+  Ping - returnBall > Pong;
+  Pong - returnBall > Ping;
+}
+
+
Listing 2.4: A model conforming to the Automata grammar.
+ +

Listing 2.4 depicts an example model conforming to the Automata grammar in its +concrete syntax. +It depicts a simple game of Ping Pong. The automaton consists of three states: +the initial state NoGame, such as the states Ping and Pong, for +identifying on which side the ball is located. +Initially, the automaton starts in the state NoGame. +The game starts at the corresponding event. +During a run, the automaton switches states by returning the ball from one +side to the other. +Additionally, it can be stopped at each stage of the game, resulting in the +initial configuration. +You can find the model in the file PingPong.aut +contained in the example directory of the unzipped mc-workspace +directory.

+

Run MontiCore

+

The MontiCore generator takes a MontiCore grammar as input and generates +an infrastructure for processing models conforming to the grammar. When +a grammar E extends another grammar G, then all the +infrastructure generated for the grammar G is reused and only the +extending part from E is generated.

+
+
+ +Tip 2.5 Infrastructure Generated by MontiCore +
+MontiCore itself as well as the infrastructure generated by the +MontiCore generator are implemented in Java. This infrastructure +includes: + +
    +
  • a parser for parsing models conforming to the grammar and + transforming textual models into abstract syntax tree instances + abstracting from the concrete syntax.
  • +
  • a symbol table infrastructure to handle the symbols introduced or + used by models conforming to the grammar. The symbol table + infrastructure is used for resolving dependencies between model + elements that are possibly defined in different files.
  • +
  • a context-condition checking framework for checking + well-formedness rules that cannot be captured by context-free + languages.
  • +
  • a visitor infrastructure for traversing models respectively their + abstract syntax instances. The abstract syntax of a model consists + of its internal representation as an abstract syntax tree + abstracting from the concrete syntax of the model (the instance of + the data structure obtained from parsing) and the symbol table of + the model.
  • +
  • a mill infrastructure for retrieving objects for language + processing, such as parsers, builders for abstract syntax trees, + visitors and objects for the symbol tables of the language. + A mill serves as a dynamic factory, adapting to the current + modeling language. The + possibility to configure the mills is crucial for reusing the + functionality implemented for a sublanguage (cf. Section 5.9, + Section 5.10.2, and Section 11.5 for details).
  • +
  • a code generating framework that extends the FreeMarker template + engine [Fre21] by various modularity enhancing features.
  • +
+ +
+ +

For executing MontiCore using the Automata grammar as input, perform +the following steps:

+
    +
  1. Open a command line shell and change the working directory to the + unzipped directory (mc-workspace).
  2. +
  3. Execute the following command in order to generate the language + infrastructure of the Automata DSL: +
    java -jar monticore.jar -g Automata.mc4 -hcp hwc/ src/ -mp monticore-rt.jar
    +
    +The only required argument Automata.mc4 denotes the input grammar +that shall be processed by MontiCore. The processing includes the +generation of the language infrastructure. Using the option -hcp +enables specifying the path to a directory containing the +handwritten code that is to be incorporated into the generated +infrastructure. In this case, passing the argument hwc/ to the +option -hcp makes MontiCore consider the handwritten code located +in the directory hwc/. Providing handwritten code enables to +easily incorporate additional functionality into the generated code. +For example, this enables developers to extend generated abstract +syntax classes as detailedly described in +(cf. Section 5.10 of the MontiCore handbook). +Passing the argument -mp enables specifying the paths to directories +or archives +containing paths to grammars and Java classes that are imported by the +processed grammar and the related tooling. In this case, the archive +monticore-rt.jar contains the grammars and handwritten extensions +of the monticore standard library. +More information about the standard library can be found in +Chapters 17- 20 of the handbook.
  4. +
+

Executing the command launches MontiCore, which results in the +executing of the following steps:

+
    +
  1. The specified grammar is parsed and processed by MontiCore.
  2. +
  3. Java source files for the corresponding DSL infrastructure are + generated into the default output directory out. This + infrastructure consists of the directories
      +
    • out/automata/ containing the mill (cf. Section 5.9, Section 5.10.2, Section 11.5).
    • +
    • out/automata/_ast containing the abstract syntax tree data + structure (cf. Chapter 5 of the MontiCore handbook).
    • +
    • out/automata/_auxiliary containing adapted mills of sublanguages, + which are required for configuring the mills of sublanguages + (cf. Chapter 11 of the MontiCore handbook).
    • +
    • out/automata/_cocos containing the infrastructure for context + conditions (cf. Chapter 10 of the MontiCore handbook).
    • +
    • out/automata/_od containing the infrastructure for printing + object diagrams for reports produced during processing the + models.
    • +
    • out/automata/_parser containing the generated parsers, which are + based on ANTLR (cf. Chapter 6 of the MontiCore handbook).
    • +
    • out/automata/_symboltable containing the infrastructure for the symbol + table (cf. Chapter 6 of the MontiCore handbook).
    • +
    • out/automata/_visitor containing the infrastructure for visitors + (cf. Chapter 9 of the MontiCore handbook).
    • +
    • out/reports/automata containing reports created during the + processing of the grammar.
    • +
    +
  4. +
  5. The output directory also contains a log file of the executed + generation process with the generation time in its name.
  6. +
+

In the following, we review the classes and interfaces generated from +the Automata grammar that are relevant for language engineers in more +detail. We do not review the classes and interfaces that are only +internally relevant for MontiCore and are usually not intended to be +used by language engineers.

+

Abstract Syntax Tree Data Structure

+

The tree data structure is generated into the directory out/automata/_ast. +Details about the generation of AST classes can be found in +(cf. Chapter 5 of the MontiCore handbook). For +each nonterminal contained in the grammar, the MontiCore generator +produces AST and corresponding builder classes. The AST classes +implement the abstract syntax tree data structure.

+

The builder classes implement the builder pattern for constructing +instances of the respective AST classes as usual. For example, the class +ASTAutomaton is the AST class generated for the Automaton +nonterminal (cf. Listing 2.2) and the class ASTAutomatonBuilder is the +corresponding generated builder class.

+

Parts of the AST data structure generated for the Automata grammar.

+

+
Figure 2.6: Parts of the AST data structure generated for the Automata grammar.
+ +

The contents of the AST and builder classes are generated systematically +from the grammar. The attributes of each AST class resemble the +right-hand side of the corresponding production rule. In the following, +we mainly speak of attributes, but please be aware that all attributes +come fully equipped with access and modification methods, which should +normally be used.

+

For instance, Figure 2.6 depicts parts of the generated AST infrastructure for +the Automata grammar. The class ASTAutomaton contains the attributes +name, states, and transitions. The AST class does not contain an +attribute for the terminal automaton as it is part of every word +conforming to the production of the Automaton nonterminal. The type of +the attribute name is String whereas the attributes states and +transitions are lists of the types of the AST classes corresponding +to the used nonterminals. This is the case because exactly one Name is +parsed with the right-hand side of the production of the nonterminal +Automaton, whereas multiple states and transitions can be parsed.

+

The ASTAutomaton class further contains the attributes symbol, +spannedScope, and enclosingScope. These attributes are specific to +the symbol table of Automata models and are used for linking the +symbol table of a model with its abstract syntax tree. Details can be +found in Chapter 9 of the MontiCore handbook.

+
+
+ +Tip 2.7 Generated Symbols and Scopes in the AST +
+

Each AST class contains access to the enclosingScope.

+ +

When a production contains the keyword symbol, the generated AST class +contains the attribute symbol +(see Chapter 9 of the MontiCore handbook).

+ +

Keyword scope indicates that a nonterminal also defines a new local +scope, stored in attribute spannedScope.

+ +

The parser builds the abstract syntax tree of a model and the available +scope genitor creates the symbol table of the model, consisting of +symbols and scopes.

+
+ +

The ASTAutomaton class further contains several straight-forward +methods for checking different instances for equality and accessing the +attributes. Similar to the ASTAutomaton class, the +ASTAutomatonBuilder class contains attributes resembling the +right-hand side of the corresponding production. It further contains +methods for changing the values of the attributes (e.g., addState), +checking whether the AST instance that would be constructed from the +current builder state is valid (cf. isValid), and for building the +AST instance corresponding to the builder's state (cf. build). The +contents of the other AST and Builder classes are constructed +analogously.

+
+
+ +Tip 2.8 Handwritten AST Class Extensions +
+

If the generator detects that an AST class for a nonterminal is +already implemented in the handwritten code, then it produces a +corresponding TOP AST class instead.

+ +

This TOP mechanism allows developers to add handwritten extensions to +any generated class, while reusing the generated TOP class via +extension.

+ +

This gives a very close integration between handwritten and generated +code that even adapts builders accordingly, while preventing the very +bad habit of performing manual changes to the generated code.

+ +

Option -hcp tells the generator where to look for handwritten +integrations.

+
+ +

The following section presents the methods of the classes for parsing +textual models (possibly stored in files) into AST class instances at +runtime. For now, it suffices for you to understand that (1) MontiCore +generates an extensible AST data structure that resembles the +nonterminals and productions of the grammar in a straight-forward way +and (2) that all models of a grammar have an AST data structure +representation for internal processing.

+

Parser

+

The infrastructure is generated into the directory out/automata/_parser. +Details about the generated parsers and their uses are described in +Chapter 6 of the MontiCore handbook.

+

Parts of the class AutomataParser generated from the Automata +grammar.

+

+
Figure 2.9: Parts of the class AutomataParser generated from the Automata grammar.
+ +

Parts of the generated class AutomataParser are depicted in Figure 2.9. The +class implements the generated parser for the Automata grammar. +Usually, developers are solely concerned with the methods +parse(String) and parse_String(String). For now, it suffices if you +remember that parsing textual Automata models stored in files is +possible by calling the method parse(String) of an AutomataParser +object with the fully qualified name of the file as input.

+
+
+ +Tip 2.10 Methods for Parsing +
+

The class AutomataParser contains the methods

+ +
    +
  • parse(Reader r),
  • +
  • parse(String filename), and
  • +
  • parse_String(String content).
  • +
+ +

All of the methods return an object of type Optional<ASTAutomaton>, +where absence means failure of parsing and errors have been issued.

+ +

For each nonterminal in the grammar, the class further contains methods +for parsing a sub-model described by this nonterminal.

+
+ +

Symbol Table

+

The infrastructure is generated into the directory out/automata/_symboltable. +Details about the generated symbol table infrastructure and its use are +described in Chapter 9 of the MontiCore handbook. +The symbol table infrastructure is used for resolving +cross-references concerning information defined in different model +elements that are potentially defined in different models stored in +different files.

+

+
Figure 2.11: The scope classes generated from the `Automata` grammar.
+ +
+
+ +Tip 2.12 Scope Classes +
+

For the Automata grammar, the generator produces the classes

+ +
    +
  • AutomataScope,
  • +
  • AutomataArtifactScope, and
  • +
  • AutomataGlobalScope
  • +
+ +

as well as respective interfaces. The relationships between these +classes and interfaces are depicted in Figure 2.11.

+ +

The singleton AutomataGlobalScope contains all +AutomataArtifactScopes of all loaded Automata artifacts. +AutomataScopes represent scopes spanned inside of models.

+
+ +

+
Figure 2.13: Parts of the symbol classes generated from the Automata grammar.
+ +

Figure 2.13 depicts parts of the symbol classes generated for the Automata +grammar. As the nonterminal State is annotated with symbol in the +Automata grammar, the generator produces the class StateSymbol. The +StateSymbol class, inter alia, contains the attributes name, +enclosingScope, and spannedScope. The attribute name stores the +name of the symbol. The attributes enclosingScope and spannedScope +store the enclosing and spanned scopes of the symbol. The class further +contains methods for accessing and setting the attributes. For all +symbol classes, the MontiCore generator also produces builder classes +(e.g., AutomataArtifactScopeBuilder and StateSymbolBuilder).

+
+
+ +Tip 2.14 Extending Symbol Classes +
+

It is possible to add further methods and attributes in two ways:

+ + +
+ +

The generated class AutomataScopesGenitor is responsible for creating +the scope structure of Automata artifacts and linking the scope +structure with the corresponding AST nodes. For this task, it provides +the method createFromAST that takes an ASTAutomaton instance as +input and returns an IAutomataArtifactScope instance. The returned +IAutomataArtifactScope instance can be added as a subscope to the +(during runtime unique and administrated by the mill) +AutomataGlobalScope instance.

+

Developers can create visitors for complementing the symbol table +(creating symbols and filling the extensions introduced via symbol rules +or the TOP mechanism) of an Automata artifact. After creating the +scope structure, the visitor should be used to traverse the AST +instance of the artifact for complementing the symbols and scopes. The +following sections explain the generated visitor infrastructure in more +detail.

+
Optional<AutomatonSymbol> resolveAutomaton(String name)
+List<AutomatonSymbol> resolveAutomatonMany(String name)
+Optional<StateSymbol> resolveState(String name)
+List<StateSymbol> resolveStateMany(String name)
+
+
Listing 2.15: Different resolve methods.
+ +

For each nonterminal annotated with symbol in the grammar Automata, +the scope interfaces contain a symbol-specific resolve method taking a +string as input. The method can be called to resolve symbol instances by +their names. The name given as input to a resolve method should be as +qualified as needed to find the symbol. For instance, Listing 2.15 lists the +signatures of four of the resolve methods provided by the interface +IAutomataScope.

+

For now, it suffices for you to understand that (1) MontiCore generates +an extensible symbol table data structure that resembles the scope and +symbol structure as specified in the grammar in a straight-forward way +and (2) that all models of a grammar have a symbol table data structure +representation for internal processing and (3) that symbols can be +resolved from scopes via calling the resolve methods.

+

(De)Serialization of Symbol Tables

+

MontiCore also supports the serialization and deserialization of symbol tables. The +(de)serialization is crucial for incremental code generation and +efficient language composition via aggregation. Details about this are +explained in Chapter 7 and Chapter 9 of the MontiCore handbook.

+

For the (de)serialization, the generator produces the class +AutomataSymbol2Json. It provides the public methods store and +load. The former can be used to serialize IAutomataScope instances +into their string representations encoded in JSON and persisting these +to a file at a location that is passed as method argument. The latter +can be used to load a stored IAutomataScope into its objects +representation. For now, it suffices that you understand which methods +to call for the (de)serialization.

+

Visitor

+

+
Figure 2.16: Parts of the visitor infrastructure generated from the Automata grammar
+ +

The infrastructure is generated into the directory out/automata/_visitor. +Details about the generated visitor infrastructure are described in +Chapter 8 of the MontiCore handbook. +For each grammar, the generator systematically produces several classes +and interfaces implementing the visitor infrastructure. For the +Automata grammar, for example, the generator produces the interfaces +AutomataTraverser, AutomataVisitor2, and AutomataHandler and the +class AutomataTraverserImplementation. The relationships between these +interfaces and classes are depicted in Figure 2.16.

+

The interfaces Traverser, Visitor2 and Handler together realize +the Visitor pattern. Conceptually, the traverser is the entry point for +traversing. The traverser manages visitors for the different +sublanguages and realizes the default traversing strategy. Whenever an +AST node is traversed, the traverser delegates the visit to the +corresponding visitor implementation. If a special traversal is to be +implemented that differs from the default, it is possible to add +handlers to the traverser that realize the alternative traversal. For a +more detailed explanation consider reading +Chapter 8 of the MontiCore handbook.

+
+
+ +Tip 2.17 Visitors +
+

MontiCore provides the visitor pattern in a detangled and thus flexible +variant.

+ +

AutomataTraverser is traversing the AST. AutomataVisitor2 contain +the actual functionalities, added through subclassing. Many visitors can +be added to the traverser for parallel execution via the method +add4Automata.

+
+ +

The visitors are compositional, allowing to maximize reuse of visitors +from sublanguages, and they can be adapted through the TOP mechanism.

+

For example, the handwritten class PrettyPrinter, which can be found +in the directory mc-workspace/src/automata/prettyprint, implements +functionality for pretty printing an Automata model, which is given by +its abstract syntax tree. Listing 2.18 depicts the attributes and the constructor of +the class. The PrettyPrinter class implements the AutomataHandler +interface. Its constructor instantiates a printer (a helper for printing +indented strings) and retrieves an AutomataTraverser object from the +mill (which is explained later on). It sets the handler of the traverser +to itself. This ensures that the pretty printer becomes the handler of +the traverser. We will execute it in a following section.

+
public class PrettyPrinter implements AutomataHandler {
+  private final IndentPrinter printer;
+  private AutomataTraverser traverser;
+
+  public PrettyPrinter() {
+    this.printer = new IndentPrinter();
+    this.traverser = AutomataMill.traverser();
+    traverser.setAutomataHandler(this);
+  }
+// further methods
+}
+
+
Listing 2.18: Attributes and constructor of the PrettyPrinter for the Automata language.
+ +

For now, you should understand that (1) for implementing visitors it is +often sufficient to implement the visitor interfaces and to add them to +a traverser and (2) custom traversals can be realized by implementing +handlers and adding those to the traverser.

+

Context Conditions

+

The infrastructure is generated into the directory out/automata/_cocos. +Details about the generated context condition infrastructure are +described in Chapter 10 of the MontiCore handbook.

+

For each nonterminal of a grammar, the generator produces a context +condition interface for implementing context conditions for this +nonterminal. For the Automata grammar, for example, the generator +produced the interface AutomataASTStateCoCo. The interface solely +contains the method check(ASTState). Each class implementing the +interface should represent a predicate over subtrees of abstract syntax +trees starting at a node with the type corresponding to the nonterminal.

+

The check method should be implemented such that it reports an error or +a warning if the input node does not satisfy the predicate. Thus, +context conditions implement well-formedness rules that cannot be +captured by context-free grammars (or that are intentionally not +captured by the grammar to achieve a specific AST data structure). For +producing the error or warning, the static methods error and warning +of the MontiCore runtime class Log should be used.

+

For the Automata grammar, the generator also produced the class +AutomataCocoChecker. For each nonterminal of the grammar, the class +contains a method for adding context condition instances to an +AutomataCocoChecker instance. For checking whether an AST node +satisfies all registered context conditions, the method checkAll can +be called with the AST node as input. Calling the method makes the +checker traverse the abstract syntax tree and check whether each node +satisfies the context conditions registered for the node. Thus, +AutomataCocoChecker instances represent sets of context conditions +that are required to be satisfied by abstract syntax tree instances.

+

For now, you should understand that (1) implementing context conditions +is possible via implementing the generated CoCo interfaces and (2) +context conditions can be checked via instantiating the Checker class, +adding the CoCos, and calling the checkAll method.

+

Mill as Factory for Builders

+

The for the Automata language is generated into the directory +out/automata/. Details about the generated mill and the mill pattern +in general are described in Section 11.5. The generated mill class AutomataMill is +responsible for providing ready to use and correct parser, scope +genitor, scope, and builder instances. The mill of each language is a +singleton.

+
+
+ +Tip 2.19 Mill Use and Automatic Initialization +
+

A mill is a factory for builders and other commonly used functions, such +as parsers or visitors. The mill was introduced to ensure +compositionality of languages, while retaining reusability of functions +developed for sublanguages.

+ +

Only one mill instance exists, even though in composed languages it is +available under several static signatures. Let language G2 extend +another language G1. Then G2Mill initializes the G1Mill +appropriately, such that all the code of the sublanguage G1 can be +reused in the tools developed for the language G2, even when creating +new AST nodes, symbols, etc.

+ +

Cool mechanism and the developers don't have to bother.

+
+ +
public static IAutomataGlobalScope globalScope()
+public static IAutomataArtifactScope artifactScope()
+public static IAutomataScope scope()
+public static AutomataScopesGenitor scopesGenitor ()
+public static AutomataScopesGenitorDelegator scopesGenitorDelegator()
+public static ASTAutomatonBuilder automatonBuilder()
+public static AutomatonSymbolBuilder automatonSymbolBuilder()
+public static AutomataParser parser()
+public static AutomataTraverser traverser ()
+
+
Listing 2.20: Some method of the AutomataMill.
+ +

Developers should retrieve all instances of the classes and interfaces +provided by the mill by using the mill. Instances of the classes and +interfaces that are provided by the mill should never be instantiated +manually. Otherwise, it may be the case that not all of the code +implemented for the language can be reused as expected in other +languages extending the language. Listing 2.20 shows some signatures of the methods +of the AutomataMill.

+
+
+ +Tip 2.12 Mill Methods +
+

A mill provides public static methods for retrieving the instances of +the parsers, scope genitors, scopes, and builders. For that is acts like +a factory. Because a mill is realized using the static delegator pattern +(cf. Section 11.1), it still can be adapted at will.

+ +

This combines the advantage of general availability with the advantage +of being able to override the functions.

+
+ +

For now, you should understand that (1) the methods of the mill should +be used for creating ready to use and correct parser, scope genitor, +scope, and builder instances and (2) how to call these methods.

+

Compile the Target

+

Section 2.2.3 of the MontiCore handbook +describes how to generate the desired Java code +from a MontiCore grammar. For these Java classes, generated for the +Automata DSL, execute the following command in the mc-workspace:

+

With Powershell on Windows +

javac -cp monticore-rt.jar -sourcepath "src/;out/;hwc/" `
+                                  src/automata/AutomataTool.java
+
+With Bash on Unix +
javac -cp monticore-rt.jar -sourcepath "src/:out/:hwc/" \
+                                  src/automata/AutomataTool.java
+
+With cmd on Windows +
javac -cp monticore-rt.jar -sourcepath "src/;out/;hwc/" ^
+                                  src/automata/AutomataTool.java
+

+

Please note, on Unix systems paths are separated using ":" (colon) +instead of semicolons.

+

Providing the option -cp with the argument monticore-cli.jar makes +the Java compiler consider the compiled MontiCore runtime classes +contained in the file monticore-cli.jar.

+

The option -sourcepath enables to specify paths to directories +containing the source files that should be considered during the +compilation.

+

In this case, executing the command makes the Java compiler consider all +generated classes located in and all handwritten classes located in src and hwc. +The last argument instructs the Java compiler to compile the class +src/automata/AutomataTool.java.

+

Please note that the structure of the handwritten classes follows the +package layout of the generated code, i.e. there are the following sub +directories (Java packages):

+
    +
  • src/automata contains the top-level language realization for using + the generated DSL infrastructure. In this case the class + src/automata/AutomataTool.java constitutes a main class executable + for processing automata models with the automata DSL.
  • +
  • src/automata/cocos contains infrastructure for context condition + of the automata DSL.
  • +
  • src/automata/prettyprint contains an exemplary use of the + generated visitor infrastructure for processing parsed models for + pretty printing.
  • +
  • src/automata/visitors contains an exemplary analysis using the + visitor infrastructure. The exemplary analysis counts the states + contained in the parsed automata model.
  • +
  • hwc/automata/_ast contains an exemplary usage of the handwritten + code integration mechanism for modifying the AST for the automata + DSL. Details about the integration mechanism are described in Section 5.10.
  • +
  • hwc/automata/_symboltable contains handwritten extensions of the + generated symbol table infrastructure. Details about implementing + handwritten symbol table infrastructure extensions are described in + Chapter 9 of the MontiCore handbook.
  • +
+

Please, also do not mix the code for the Automata tool vs. the code +for the final product, generated from that tool, although both have a +similar package structure.

+

We already described the contents of the directories hwc/automata/_ast +and hwc/automata/_symboltable in the previous section. They contain +handwritten extensions of the abstract syntax of the Automata +language.

+
public class CountStates implements AutomataVisitor2 {
+  private int count = 0;
+
+  @Override
+  public void visit(ASTState node) {
+    count++;
+  }
+
+  public int getCount() {
+    return count;
+  }
+}
+
+
Listing 2.22: The CountStates visitor implementation
+ +

The directory src/automata/visitors contains the file +CountStates.java. The class is depicted in Listing 2.22. It implements a simple +visitor for counting the number of states contained in an Automata +model. To this effect, it implements the AutomataVisitor2 interface. +It has an attribute count of type int for storing the current number +of counted nodes. It overrides the visit method for ASTState to +increase the counter whenever a state is visited.

+

The directory src/automata/cocos contains the context-condition +implementations for the Automata language.

+
public class AtLeastOneInitialAndFinalState
+       implements AutomataASTAutomatonCoCo {
+@Override
+  public void check(ASTAutomaton automaton) {
+    boolean initialState = false;
+    boolean finalState = false;
+
+    for (ASTState state : automaton.getStateList()) {
+      if (state.isInitial()) {
+        initialState = true;
+      }
+      if (state.isFinal()) {
+        finalState = true;
+      }
+    }
+
+    if (!initialState || !finalState) {
+      // Issue error...
+      Log.error("0xA0116 An automaton must have at least one initial 
+                 and one final state.",
+          automaton.get_SourcePositionStart());
+    }
+  }
+}
+
+
Listing 2.23: Context condition implementation for checking that +there exist at least one initial and at least one +final state.
+ +

Listing 2.23 depicts the class AtLeastOneInitialAndFinalState. The class implements +a context condition for checking whether an Automata model contains at +least one initial and at least one final state. To this effect, the +class implements the interface AutomataASTAutomatonCoCo. The class +StateNameStartsWithCapitalLetter is implemented similarly.

+
public class TransitionSourceExists
+                 implements AutomataASTTransitionCoCo {
+
+  @Override
+  public void check(ASTTransition node) {
+
+    IAutomataScope enclosingScope = node.getEnclosingScope();
+    Optional<StateSymbol> sourceState =
+        enclosingScope.resolveState(node.getFrom());
+
+    if (!sourceState.isPresent()) {
+      // Issue error...
+      Log.error(
+        "0xADD03 Source state of transition missing.",
+         node.get_SourcePositionStart());
+    }
+  }
+}
+
+
Listing 2.24: Context condition implementation for checking that +states used in transitions exist.
+ +

Listing 2.24 presents the implementation of the class TransitionSourceExists. The +class implements a context condition for checking whether the source +states used in transitions are defined. To this effect, the class uses +the resolving mechanisms of the symbol table. For each transition, the +context conditions tries to resolve the state symbol corresponding to +the source state of the transition. If the resolving fails for the +state, then the context condition logs an error.

+

The class AutomataTool is the main class of the Automata language. +It is defined in the file AutomataTool.java contained in the directory +src/automata.

+
public class AutomataTool extends AutomataToolTOP {
+  // main method missing in this listing
+
+  public ASTAutomaton parse(String model) {
+    try {
+      AutomataParser parser = new AutomataParser() ;
+      Optional<ASTAutomaton> optAutomaton = parser.parse(model);
+
+      if (!parser.hasErrors() && optAutomaton.isPresent()) {
+        return optAutomaton.get();
+      }
+      Log.error("0xEE840 Model could not be parsed.");
+    }
+    catch (RecognitionException | IOException e) {
+      Log.error("0xEE640 Failed to parse " + model, e);
+    }
+    System.exit(1);
+    return null;
+  }
+}
+
+
Listing 2.25: Methods for parsing and creating symbol tables.
+ +

Listing 2.25 presents the implementation of the method parse of the AutomataTool +class which can be used for parsing Automata models. The class extends the +generated abstract superclass AutomataToolTOP that provides methods to be used in +the run method. One example is the method createSymbolTable that uses the global scope +and genitors available in the mill to create a symbol table for Automata.

+
public static void main(String[] args) {
+  // delegate main to instantiatable method for better integration,
+  // reuse, etc.
+  new AutomataTool().run(args);
+}
+
+public void run(String[] args) {
+
+  // use normal logging (no DEBUG, TRACE)
+  AutomataMill.init();
+  Log.ensureInitalization();
+
+  Options options = initOptions();
+  try {
+    //create CLI Parser and parse input options from commandline
+    CommandLineParser cliparser = new org.apache.commons.cli.DefaultParser();
+    CommandLine cmd = cliparser.parse(options, args);
+
+    //help: when --help
+    if (cmd.hasOption("h")) {
+      printHelp(options);
+      //do not continue, when help is printed.
+      return;
+    }
+    //version: when --version
+    else if (cmd.hasOption("v")) {
+      printVersion();
+      //do not continue when help is printed
+      return;
+    }
+
+    Log.info("Automata DSL Tool", "AutomataTool");
+
+    if (cmd.hasOption("i")) {
+      String model = cmd.getOptionValue("i");
+      final ASTAutomaton ast = parse(model);
+      Log.info(model + " parsed successfully!", "AutomataTool");
+
+      AutomataMill.globalScope().setFileExt("aut");
+      IAutomataArtifactScope modelTopScope = createSymbolTable(ast);
+      // can be used for resolving things in the model
+      Optional<StateSymbol> aSymbol = modelTopScope.resolveState("Ping");
+      if (aSymbol.isPresent()) {
+        Log.info("Resolved state symbol \"Ping\"; FQN = "
+            + aSymbol.get().toString(),
+          "AutomataTool");
+      } else {
+        Log.info("This automaton does not contain a state called \"Ping\";",
+          "AutomataTool");
+        }
+      runDefaultCoCos(ast);
+
+      if(cmd.hasOption("s")){
+        String storeLocation = cmd.getOptionValue("s");
+        storeSymbols(modelTopScope, storeLocation);
+      }
+
+      // analyze the model with a visitor
+      CountStates cs = new CountStates();
+      AutomataTraverser traverser = AutomataMill.traverser();
+      traverser.add4Automata(cs);
+      ast.accept(traverser);
+      Log.info("Automaton has " + cs.getCount() + " states.", "AutomataTool");
+      prettyPrint(ast,"");
+    }else{
+      printHelp(options);
+    }
+  } catch (ParseException e) {
+    // e.getMessage displays the incorrect input-parameters
+    Log.error("0xEE752 Could not process AutomataTool parameters: " + e.getMessage());
+  }
+}
+
+
Listing 2.26: Main method of the AutomataTool class
+ +

The AutomataTool provides a main method, which can be called from +the command line. The implementation of the method is depicted in Listing 2.26. It +expects two inputs. The first is the name of a file containing an +Automata model. The second input is the name of the file in which the +tool should store the symbol table of the model given as first input.

+

The method

+
    +
  • parses the input model,
  • +
  • creates the symbol table,
  • +
  • resolves a state,
  • +
  • executes context conditions,
  • +
  • stores the symbol table by using the serialization,
  • +
  • executes the visitor for counting the states, and
  • +
  • pretty prints the model to the standard output.
  • +
+

Inspect the main method and try to understand the implementation for the +executed tasks. Read the above descriptions again if necessary.

+

Run the Tool

+

The previous command compiles the handwritten and generated code +including the Automata tool class AutomataTool. For running the +Automata DSL tool, execute the following command:

+

With Powershell on Windows +

java -cp "src/;out/;hwc/;monticore-rt.jar" `
+                    automata.AutomataTool -i example/PingPong.aut `
+                    -s st/PingPong.autsym
+
+With Bash on Unix +
java -cp "src/:out/:hwc/:monticore-rt.jar" \
+                    automata.AutomataTool -i example/PingPong.aut \
+                    -s st/PingPong.autsym
+
+With cmd on Windows +
java -cp "src/;out/;hwc/;monticore-rt.jar" ^
+                    automata.AutomataTool -i example/PingPong.aut ^
+                    -s st/PingPong.autsym
+

+

Please note again, on Unix systems paths are separated using ":" +(colon) instead of semicolons. Executing the command runs the Automata +DSL tool.

+

Using the option -cp makes the Java interpreter consider the compiled +classes contained in the paths specified by the argument.

+

The argument automata.AutomataTool makes the Java interpreter execute +the main method of the class automata.AutomataTool contained in the +directory src.

+

The argument example/PingPong.aut is passed to the main method of the +Automata DSL tool class as input. Inspect the output on the command +line, which displays log messages concerning the processing of the +example Automata model.

+

The last argument st/PingPong.autsym is also passed to the main +method. It makes the tool store the serialized symbol table of the input +model into the file example/PingPong.aut.

+

The shipped example Automata DSL (all sources contained in +mc-workspace/src and mc-workspace/hwc) can be used as a starting +point for creating your own language. It can easily be altered to +specify your own DSL by adjusting the grammar and the handwritten Java +sources and rerunning MontiCore as described above.

+

Using MontiCore via Gradle From the Command Line

+

It is possible to execute MontiCore via the MontiCore plugin. A detailed +description about using the MontiCore Gradle plugin is given in +Chapter 16 of the MontiCore handbook. This +section describes the execution of MontiCore via a Gradle plugin from +the command line shell by example. Before you start, +install gradle +and make sure that you can use it from the command line.

+

The shipped example Automata DSL can be used as a starting point and +can be downloaded here:

+
https://www.monticore.de/download/Automaton.zip
+
+

The build script (file build.gradle) can easily be adapted for creating build +scripts for other languages. For executing MontiCore via the Gradle +plugin from the command line shell by example of the Automata DSL, +perform the following steps:

+
    +
  1. Download the Automata example.
  2. +
  3. Unzip the downloaded zip file into an arbitrary directory.
  4. +
  5. Open a shell and change your working directory to the directory in + which you unzipped the downloaded file (the directory containing the + file build.gradle).
  6. +
  7. Execute Gradle in the shell:
      +
    • If you are using a Windows shell, execute the command + gradle build.
    • +
    • If you are using a Unix shell, execute the command + ./gradle build.
    • +
    +
  8. +
+

When executing the above commands, MontiCore launches, which results in +the execution of the following steps:

+
    +
  1. The grammars specified in the build.gradle are incrementally parsed + and processed by MontiCore.
  2. +
  3. Java source files for the corresponding DSL infrastructure are + generated into the default output directory + ../target/generated-sources/monticore/sourcecode. The contents of + this generated directory are equal to the contents of the generated + directory out as described in .
  4. +
+

Using MontiCore in Eclipse

+

The MontiCore plugin can be used in Eclipse. Section 2.4.1 describes the process of setting +up Eclipse. Section 2.4.2 presents how to import the example project in Eclipse. +Finally, Section 2.4.3 explains how the MontiCore Gradle plugin can be executed in +Eclipse.

+

Setting up Eclipse

+

Before you import the example project and run MontiCore as a Gradle +plugin, please make sure that a current version of the Gradle plugin is +installed in Eclipse. When installing a new version of Eclipse, the +Gradle plugin is installed by default. If the Gradle plugin is not yet +integrated into your Eclipse installation, download the latest Eclipse +version or perform the following steps to install the Eclipse plugin:

+
    +
  1. Download and install Eclipse (or use an existing one).
  2. +
  3. Open Eclipse.
  4. +
  5. Install the needed Plugins.
      +
    • Help > Eclipse Marketplace...
    • +
    • Type 'gradle' in the search box and click Enter.
    • +
    • Install the 'Buildship Gradle Integration' plugin.
    • +
    +
  6. +
  7. Make sure to configure Eclipse to use an JDK instead of an JRE.
      +
    • Window > Preferences > Java > Installed JREs.
    • +
    +
  8. +
+

Importing the Example

+

The shipped example Automata DSL can be used as a starting point. Once +imported into Eclipse, it can easily be altered to specify your own DSL +by adjusting the grammar and the handwritten Java sources and rerunning +MontiCore as described in Section 2.4.3. To import the example, perform the following +steps:

+
    +
  1. Download and unzip the Automata example: +
    http://www.monticore.de/download/Automaton.zip
    +
  2. +
  3. Open Eclipse and select
      +
    • File > Import > Gradle (if you are required to choose a Gradle + version, then choose version 7.4.1) > Existing Gradle + Projects > Next.
    • +
    • Click on the Browse.. button and import the directory that + contains the file build.gradle from the Automata example.
    • +
    +
  4. +
+

Running MontiCore

+

To execute the MontiCore Gradle plugin, perform the following steps:

+
    +
  • Select the Gradle Task menu (at the top or bottom, depending on + your installed Eclipse version).
  • +
  • There select automaton > build > build (double click).
  • +
+

This makes Eclipse execute the MontiCore Gradle plugin as described in +Section 2.3 of the MontiCore handbook . +After installing and executing MontiCore in Eclipse, your workspace +should look similar to Figure 2.27.

+

+
Figure 2.27: Eclipse after importing the example project and executing MontiCore
+ +

Using MontiCore in IntelliJ IDEA

+

The MontiCore plugin can be used in IDEA. Section 2.5.1 describes the process of +setting up IntelliJ IDEA. Afterwards, Section 2.5.2 presents how to import the example +project in Eclipse. Finally, Section 2.5.3 explains how the MontiCore Gradle plugin +can be executed in IntelliJ IDEA.

+

Setting up IntelliJ IDEA

+

For setting up IntelliJ IDEA, perform the following steps:

+
    +
  1. Download and install IntelliJ IDEA (or use your existing installation).
      +
    • Hint for Students: You get the Ultimate version of IntelliJ IDEA + for free.
    • +
    +
  2. +
  3. Open IntelliJ IDEA.
  4. +
+

Importing the Example

+

The shipped example Automata DSL can be used as a starting point. Once +imported into IntelliJ IDEA, it can easily be altered to specify your +own DSL by adjusting the grammar and the handwritten Java sources and +rerunning MontiCore as described in Section 2.5.3. For importing the example, perform +the following steps:

+
    +
  1. Download and unzip the Automata example: +
    http://www.monticore.de/download/Automaton.zip
    +
  2. +
  3. In the IDE select: File > Open.
  4. +
  5. Select the directory containing the build.gradle (if you are + required to choose a Gradle version, then choose version 6.7.1).
  6. +
+

Running MontiCore

+

To execute the MontiCore Gradle plugin, perform the following steps:

+
    +
  • Select the Gradle Projects menu on the right.
  • +
  • From there select automaton > Tasks> build > build (double click).
  • +
+

This makes IntelliJ IDEA execute the Gradle plugin as described in Section 2.3. If +you do not see the Gradle Projects menu yet, right-click on the +build.gradle file and select 'Import Gradle Project'. Now the Gradle +Projects menu should occur on the right side and you can follow the +above mentioned steps for the execution. After installing and executing +MontiCore in IntelliJ IDEA, your workspace should look similar to Figure 2.28.

+

+
Figure 2.28: IntelliJ IDEA after importing +the example project and executing MontiCore
+ + + + + + +
Fre21FreeMarker website. https://freemarker.apache.org/, 2021.
+ +

Using MontiCore with GitPod

+

Installing all the prerequisites and an IDE can take some time. +Alternatively to this, you can use Gitpod, an open-source +Kubernetes application for ready-to-code developer environments. It already +has all the prerequisites and an operational Web IDE similar to Microsoft's +Visual Studio Code installed. You need to login with an existing GitHub account +to use it.

+

This link can be used +to access the Gitpod project for the Automata language. +First, an environment for the project with the proper Java and Gradle version +will be prepared and initialized automatically. +After that, you will be directed to the Web IDE. The project will be built +with Gradle first, and after that it is ready-to-use. The Web IDE also has a +built-in terminal which can be used to build the project via gradle build +or execute other tasks.

+

The Web IDE can be used to change existing project files, such as the Automata +grammar or the handwritten classes for the language. Simply navigate to the grammars +or classes in the file explorer on the left-hand side of the IDE and edit the files. +This makes experimenting with MontiCore possible. The changes will be compiled by the +IDE immediately and compilation errors will be marked with red color. To run the project, +execute the command gradle build in the terminal.

+

You will notice that the link to the Gitpod project is generated and always +has the same pattern. +An example for a link is https://indigo-ostrich-8psdfoer.ws-eu18.gitpod.io. +After 30 minutes of non-use, Gitpod will "freeze" the environment. +It can be reactivated by using the same link to access it. The environment is +reactivated, and you do not even need to rebuild the project with Gradle to use +the project again.

+

Further Information

+ + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/docs/IntelliJ-IDEA.png b/docs/IntelliJ-IDEA.png new file mode 100644 index 0000000000..2abfe31a2f Binary files /dev/null and b/docs/IntelliJ-IDEA.png differ diff --git a/docs/Languages/index.html b/docs/Languages/index.html new file mode 100644 index 0000000000..151d3e60f2 --- /dev/null +++ b/docs/Languages/index.html @@ -0,0 +1,1512 @@ + + + + + + + + + + + + + + + + + + + + + + + + + List of Languages - MontiCore + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + + + +

MontiCore Languages of Level II - an Overview

+

MontiCore is a language workbench +with an explicit notion of language components. It uses +grammars to describe textual DSLs. +MontiCore uses an extended grammar format that allows to compose +language components via inheritance, embedding and aggregation (see the +handbook +for details).

+

A language component is mainly represented through +(1) the grammar describing concrete and abstract syntax of the language, +(2) Java-classes implementing specific functionalities, and +(3) Freemarker-Templates helping to print a model to text. +However, language components are often identified with their main +component grammar.

+

Language components are currently organized in two levels: +In this list you mainly find grammars for +complete (but also reusable and adaptable) languages (Level II). +A list of +grammar components +with individual reusable nonterminals is also available in +the MontiCore core project +(development status) (Level I).

+

The following list contains the language grammars found in the +MontiCore projects, such as cd4analysis/cd4analysis. +They are usually contained in project folders like src/main/grammars/ +and organized in packages like de.monticore.cd. +Publicly available MontiCore projects are hosted at +https://github.com/MontiCore.

+

List of Languages

+

Class Diagram For Analysis (CD4A) (MontiCore stable)

+
    +
  • CD4A is the textual representation to describe UML class diagrams + (it uses the UML/P variant).
  • +
  • CD4A covers classes, interfaces, inheritance, attributes with types, + visibilities, + and all kinds of associations and composition, including qualified + and ordered associations. Classes can be placed in different packages. + An example: +
    classdiagram MyLife { 
    +  abstract class Person {
    +    int age;
    +    Date birthday;
    +    List<String> nickNames;
    +  }
    +  package com.universityLib {
    +    <<myStereotype>> class Student extends Person {
    +      StudentStatus status;
    +    }
    +    enum StudentStatus { ENROLLED, FINISHED; }
    +  }
    +
    +  composition Person -> Address [*]  {ordered};
    +  association [0..2] Person (parent) <-> (child) Person [*];
    +  association phonebook Person [String] -> PhoneNumber ;
    +}
    +
  • +
  • CD4A focuses on the analysis phase in typical data-driven development + projects and is therefore mainly for data modelling. + Consequently, it omits method signatures and complex generics. + The primary use of the CD4A language is therefore data modelling. The + CD4A language opens various possibilities for the development of data + structures, database tables as well as data transport infrastructures in + cloud and distributed systems.
  • +
  • Main grammar de.monticore.cd.CD4Analysis + and + detailed description
  • +
+

Class Diagram for Code (CD4Code) (MontiCore stable)

+
    +
  • CD4Code describes UML class diagrams.
  • +
  • CD4Code is a conservative extension of CD4A, + which includes method signatures. An example: +
    classdiagram MyLife2 {
    +  // like CD4A but also allows:
    +  class Person {
    +    protected List<Person> closestFriends(int n);
    +    void addFriend(Person friends...);
    +    <<myStereotype>> void relocate();
    +  }
    +}
    +
  • +
  • CD4Code is often used as tool-internal AST that allows to + map any kind of source models to a class/attribute/method/association based + intermediate structure, before it is printed e.g. as Java code. + For example a transformation sequence could be:
  • +
  • MontiCoreCLI: + Grammar -> + Grammar AST encoded in CD4Code -> + Decoration for custom behavior -> + Java code
  • +
  • Statechart -> State pattern encoded in CD4Code + -> Decoration by monitoring methods -> Java code.
  • +
  • Main grammar de.monticore.cd.CD4Code + and + detailed description + (see Section CD4Code)
  • +
+

Feature Diagrams (MontiCore stable)

+
    +
  • Language for feature models and feature configurations.
  • +
  • Feature diagrams are used to model (software) product lines and their variants.
  • +
  • Feature configurations select a subset of features of a feature model + to describe a product of the product line. An example: +
    featurediagram MyPhones {
    +  Phone -> Memory & OS & Camera? & Screen;
    +  Memory -> Internal & External?;
    +  Internal -> [1..2] of {Small, Medium, Large};
    +  OS -> iOS ^ Android;
    +  Screen -> Flexible | FullHD;
    +
    +  Camera requires (iOS && External) || Android ;
    +}
    +
    + Rules F -> ... have a parent feature (left-hand side) + and its child features (right-hand side). + Operators are: optional feature ?, and &, or |, xor ^, + and subset cardinality constraints, like [1..2] of .... + Further, a feature model may define cross-tree constraints using logic + operators and &&, or ||, implication requires, etc.
  • +
  • Main grammar FeatureDiagram + and + detailed description
  • +
+ +
    +
  • Language for textual definition of Graphical User Interfaces of Web +Applications
  • +
  • GUI DSL covers GUI elements and relevant configuration, which include +layout elements, widgets, their style definition and references to +data sources.
  • +
  • Language is mainly used to describe GUI of Web Applications. The models of +the language represents graphical views or their parts, omitting smaller details +of style definition and simplifying connection between graphical elements and +data sources.
  • +
  • Currently, new version of the GUIDSL is being developed:
  • +
  • Basis grammar GUIBasis +includes constructs for general visualization component definitions, control +statements and components for layout description.
  • +
  • Example models +can be found in the same repository.
  • +
  • Main grammar GUIDSL +includes basic concepts and more specific implementation of component +configuration.
  • +
  • In projects legacy version is currently used:
  • +
  • Examples: MaCoCo, + Ford
  • +
  • Main grammar GUIDSL +includes definitions of MontiGem visualisation components, which are based on +abstract concepts, described in +core grammar GUIDSLCore. +Detailed description +and +documentation.
  • +
+

MontiCore Grammar (MontiCore Stable)

+
    +
  • Language for MontiCore Grammars itself. It can be understood as + meta language, but also used as ordinary language.
  • +
  • Its main use currently: A MontiCore grammar defines the + concrete syntax and the abstract syntax of a textual language. + Examples: All languages on this page are defined using MontiCore grammars + and thus conform to this Grammar.
  • +
  • Main features: Define nonterminals and their productions in EBNF, + lexical token as regular expressions.
  • +
  • Most important extensions to standard grammars:
  • +
  • Abstract, interface and external productions allow to + define extensible component grammars (object-oriented grammar style).
  • +
  • Inherited productions can be redefined (overwritten) as well + as conservatively extended.
  • +
  • Symbol and scope infrastructure is defined by simple keywords.
  • +
  • Symbols definition places can be introduced and + symbol referencing places defined, such that for standard cases + automatically symbol tables can be added.
  • +
  • Additional attributes and methods can be added to the abstract syntax only.
  • +
  • Various elements, such as semantic predicates and actions + can be defined in the same style as the underlying ANTLR.
  • +
  • MontiCore grammars can be left recursive and even allow mutual recursion. + This is e.g. useful for expression hierarchies.
  • +
  • Additional elements, such as enum productions and comfortable + operations for grammar definitions exist.
  • +
  • Main grammars + de.monticore.grammar.Grammar + defines the language with some open parameters and + de.monticore.grammar.Grammar_WithConcepts + binds the external, imported expressions, method bodies, etc.
  • +
  • Detailed description + in the MontiCore Handbook.
  • +
+

JSON (MontiCore Stable)

+
    +
  • The MontiCore language for parsing JSON artifacts. An example: +
    { "Alice": {
    +    "fullname": "Alice Anderson",
    +    "address": {
    +      "postal_code": 10459, 
    +      "street": "Beck Street",
    +      "number": 56              }  },
    +  "Bob": { ... },
    +  "Caroll": { ... }, ...
    +}
    +
  • +
  • The JSON grammar adheres to the common JSON standard and allows parsing + arbitrary JSON artifacts for further processing.
  • +
  • Actually the grammar represents a slight superset to the official JSON standard. + It is intended for parsing JSON-compliant artifacts. Further well-formedness + checks are not included, because we assume to parse correctly produced JSON + documents only.
  • +
  • Please note that JSON (like XML or ASCII) is primarily a carrier language. + The concrete JSON dialect and the question, how to recreate the + real objects / data structures, etc. behind the JSON tree structure + is beyond this grammar, but can be applied to the AST defined here.
  • +
  • Main grammar + de.monticore.lang.JSON + and + detailed description
  • +
+

MontiArc (MontiCore Stable)

+
    +
  • MontiArc is an architecture and behavior modeling language and framework + that provides a platform independent structure and behavior + modeling language with an extensible code generation framework.
  • +
  • MontiArc covers components their ports, connectors between + components and
    + embedded statecharts for component behavior description.
  • +
  • Statecharts define states and transitions with conditions on + the incoming messages as well as transition actions. + An example: +
    component InteriorLight {                           // MontiArc language
    +  port in Boolean lightSignal,          // ports
    +       in Boolean doorSignal
    +       out OnOff status;
    +  ORGate or;                            // used subcomponents
    +  lightSignal -> or.a;                  // connectors
    +  doorSignal -> or.b;
    +  or.c -> cntr.signal;
    +  component LightController cntr {      // freshly defined subcomponent 
    +    port in OnOff signal,
    +         out OnOff status;
    +    statechart {                        // with behavior by a Statechart
    +      initial state Off / {status = OFF};
    +      state On;
    +      Off -> On [ signal == true ] / {status = ON}
    +      On -> Off [ signal == false ] / {status = OFF}
    +    }
    +  }
    +  cntr.status -> status;
    +}
    +
  • +
  • MontiArc's main goal is to provide a textual notation for Component&Connector + diagrams, which is used quite often in various variants in industry. + E.g. SysML's BDD, UML's component composition diagrams use the same + paradigm.
  • +
  • MontiArc does not define data types for their signals, but assumes + that these types can be imported (e.g. from a class diagram).
  • +
  • MontiArc itself also has no timing predefined, but for a complete + language a concrete timing, such as formally grounded by Focus, + should be added.
  • +
  • Main grammar + MontiArc.mc4 + and + detailed description
  • +
+

OCL/P (MontiCore Stable)

+
    +
  • OCL/P is the textual representation of the UML OCL standard, adapted + with Java-like syntax. + Its main goal is the usage in combination with other languages like + CD4A or Object Diagrams as an integrated part of that languages.
  • +
  • +

    OCL/P allows to define invariants and pre-/post-conditions in + the known OCL style plus some extensions, such as + a generalized let construction. + Furthermore, it offers a large set expressions + to model constraints. These expressions include Java expressions, + set operations, list operations etc., completely covering the + OCL standard concepts, but extend it e.g. by set comprehensions + known from Haskell, a typesafe cast or a + transitive closure operator. + An example shows several of the above-mentioned syntactic features: +

    ocl Bookshop {
    +  context Shop s inv CustomerPaysBeforeNewOrder:      // invariant
    +    forall Customer c in s.customers:                 // quantifiers available
    +      c.allowedToOrder implies !exists Invoice i in s.invoices:
    +        i.customer == c && i.moneyPayed < i.invoiceAmount ;
    +
    +  // Method specification for selling a book
    +  context Invoice Stock.sellBook(String iban, int discountPercent, Customer c) 
    +    let availableBooks =                              // set comprehension
    +          { book | Book book in booksInStock, book.iban == iban }
    +    pre:  !availableBooks.isEmpty &&                  // precondition
    +          c.allowedToOrder;
    +    post: let discount = (100 - discountPercent)/100; // postcondition, let
    +              b = result.soldBook                     // result variable 
    +          in                                        
    +              !(b isin booksInStock) &&
    +              booksInStock.size@pre == booksInStock.size + 1 &&  // @pre
    +              result.invoiceAmount == b.price * discount;  // result variable 
    +}
    +

    +
  • +
  • +

    The OCL language component contains four grammars:

    +
  • +
  • OCL,
  • +
  • OCLExpressions,
  • +
  • OptionalOperators, and
  • +
  • SetExpressions.
  • +
  • The detailed description provides an in-depth guide for language engineers.
  • +
+

Object Diagrams (MontiCore Stable)

+
    +
  • OD is a language for textual denotation of object diagrams. The OD language + has several purposes (when combined with appropriate language extensions):
  • +
  • specification language for object structures (as part of the UML/P)
  • +
  • store and transport of data sets (e.g. the artifact analysis toolchain), and
  • +
  • report format for the MontiCore tool infrastructure.
  • +
  • OD covers named and anonymous objects, object types, links, attributes, attribute values, + lists, maps, and + visibilities. Special data types, such as Date allow comfortable + definition and reading of ODs. For a comfortable definition, objects may be nested + into trees while easily retaining their full graph structure. An example: +
    objectdiagram MyFamily {
    +  alice:Person {
    +    age = 29;
    +    cars = [
    +      :BMW {
    +        color = BLUE;
    +      },
    +      tiger:Jaguar {
    +        color = RED;
    +        length = 5.3; 
    +      }
    +    ];
    +  };
    +  bob:Person {
    +    nicknames = ["Bob", "Bobby", "Robert"];
    +    cars = [tiger];
    +  };
    +  link married alice <-> bob;
    +}
    +
  • +
  • If ODs are used as specification technique, e.g. for tests or forbidden + situations, + a more expressive version of expressions can be used for values + (e.g. by composing ODs with JavaExpressions). Furthermore, only + interesting attributes need to be defined (underspecification) and conformity + to a CD4A model can be checked.
  • +
  • The ODs differ from JSON structures, e.g., in + the possibility to give the object a name as it is the case for tiger, or alice + enabling the definition real graph structures.
  • +
  • Main grammars (directly usable): +
  • +
  • Main grammar components: +
  • +
  • Detailed description
  • +
+

Sequence Diagrams (MontiCore stable)

+
    +
  • A textual sequence diagram (SD) language.
  • +
  • Detailed description
  • +
  • The project includes grammars, a symbol table infrastructure, a PrettyPrinter, + and various CoCos for typechecking.
  • +
  • The language is divided into the two grammars SDBasis and SD4Development.
  • +
  • The grammar SDBasis is a component grammar providing basic SD language features.
  • +
  • The grammar SD4Development extends the grammar SDBasis with concepts used in + UML/P SDs.
  • +
  • SD4Development supports modeling objects, method calls, returns, exception + throws, dynamic object instantiation, various match modifiers for objects + (free, initial, visible, complete), lifelines with activation regions, + static method calls, intermediate + variable declarations by using OCL, and conditions by using OCL.
  • +
  • The grammars can easily be extended by further interactions and object modifiers.
  • +
  • The following depicts a simple SD in its textual syntax. +
    sequencediagram AuctionTest {
    +  kupfer912: Auction;         // Interacting objects
    +  bidPol: BiddingPolicy;
    +  timePol: TimingPolicy;
    +                              // Interaction sequence
    +  kupfer912 -> bidPol  : validateBid(bid)
    +  bidPol -> kupfer912  : return BiddingPolicy.OK;
    +  kupfer912 -> timePol : newCurrentClosingTime(kupfer912, bid) 
    +  timePol -> kupfer912 : return t;
    +  assert t.timeSec == bid.time.timeSec + extensionTime;
    +}
    +
  • +
+

SI Units (MontiCore Stable)

+
    +
  • The international system of units (SI units) is a physical unit system widely used in the entire world. + It is based on the basis units s, m, kg, A, K, mol, cd, + provides a variety of derived units, and can be refined using prefixes such + as m(milli), k(kilo), etc.
  • +
  • The SI Unit project aims to deliver SI units to MontiCore-based languages with expressions. + It provides a grammar for all types of SI units and prefixes usable for type + definition.
  • +
  • Second, it provides the SI Unit literals, such as 5 km as expression values + and a language for SI unit types, such as km/h or km/h<long>. Some examples: +
      km/h speed = 5 m / 27 s                         // variable definition using type km/h
    +  speed = (3 * 4m  +  17km/h * 10h) / 3.5h        // values with SI unit types
    +  °C/s<float> coolingSpeed;                       // types (°C/s) with precision (float)
    +  g/mm^2<int> pressure; 
    +  Map<Location,°C> temperatures;                  // nesting of types 
    +
  • +
  • The SI unit literals integrate with MontiCore's expressions and the + SI Unit types integrate with MontiCore's type system. + The SI unit language remains fully type safe.
  • +
  • The math version uses km/h as idealistic full precision real number, while the + computing version allows to contrain the precision with km/h<long>.
  • +
  • Main grammar components: +
  • +
  • Example projects: +
  • +
  • detailed description
  • +
+

Statecharts (MontiCore stable)

+
    +
  • A set of language variants for Statecharts (UML-like or also embedded SysML-like).
  • +
  • It is possible to define syntactically simpler or more complex and comfortable + forms of statecharts using a subset of the eleven provided language components. + Two complete Statechart language variants are composed for direct usability.
  • +
  • A compact teaser for one variant of the Statechart languages: +
    statechart Door {
    +  state Opened
    +  initial state Closed
    +  state Locked
    +
    +  Opened -> Closed close() /
    +  Closed -> Opened open(1) / {ringTheDoorBell();}
    +  Closed -> Locked timeOut(n) / { lockDoor(); } [doorIsLocked]
    +  Locked -> Closed [isAuthorized() && doorIsLocked] unlock() /
    +}
    +
  • +
  • This example models the different states of a door: Opened, Closed, and Locked. + A transition is triggered e.g. by function/method call close() that changes a from a state Opened to state Closed.
  • +
  • Transitions can have actions, such as {ringDoorBell();} containing in this case + Java statements, or preconditions, such as [ ... ] containing a Boolean expression.
  • +
  • State invariants and transition preconditions are defined using Expressions + and entry/exit/transition actions are defined using Statements.
  • +
  • A Statechart may also have hierarchically decomposed states and other forms of + events (not shown here).
  • +
  • Detailed description
  • +
+ +
    +
  • MontiCore languages for parsing artifacts of the SysML 2 language family. + Examples: +
    package 'Vehicles' {                      // a SysML block diagram
    +  private import ScalarValues::*; 
    +  block Vehicle; 
    +  block Truck is Vehicle; 
    +  value type Torque is ISQ::TorqueValue; 
    +}
    +
    +
    package 'Coffee' {                      // a SysML activity diagram
    +  activity BrewCoffee (in beans : CoffeeBeans, in, water : Water, out coffee : Coffee) { 
    +    bind grind::beans = beans;
    +    action grind : Grind (in beans, out powder);
    +    flow grind::powder to brew::powder;
    +    bind brew::water = water;
    +    action brew : Brew (in powder, in water, out coffee); 
    +    bind brew::coffee = coffee;
    +  }
    +}
    +
  • +
  • The SysML 2 grammars adhere to the general upcoming SysML 2 specification + (which is still under improvement currently).
  • +
  • Actually these grammars represents a slight superset to the official SysML 2 + standard. It is intended for parsing SysML 2-compliant models. + Well-formedness checks are kept to a minimum, because we assume to parse + correctly produced SysML 2 models only.
  • +
  • MontiCore's SysML 2 is a language family that comes with a textual + representation to describe SysML 2 diagrams with respect to the standard.
  • +
  • SysML 2 covers ADs, BDDs, IBDs, PackageDiagrams, + ParametricDiagrams, RequirementDiagrams, SDs, SMDs, + UseCaseDiagrams, and general SysMLBasics
  • +
  • Main grammars + and + detailed description
  • +
+ +
    +
  • Tags are known e.g. from the UML and SysML and mainly used to add + extra information to a model element. + Normally tags (and stereotypes) are inserted within the models, + which over time pollutes the models, especially when different sets of + tags are needed for different technical platforms.
  • +
  • MontiCore offers a solution that separates a model and its tags into + distinct artifacts. Several independent tagging artifacts + can be added without any need to adapt the core model. + This allows fo reuse even of fixed library models.
  • +
  • The tagging artifacts are dependent on two factors:
  • +
  • First, tags can be added to named elements of the base model. + It is of great help that we have an elegant symbol mechanism included + in the MontiCore generator.
  • +
  • Second, the set of allowed tags can be constrained, by an explicit + definition of allowed tag types and tag values and an explicit + declaration on which kinds of symbols a tag may be attached to.
  • +
  • Consequently, tagging is not a single language, but a method to + automatically and schematically derive languages:
      +
    • A tagging schema language TSL (dependent on the available symbol types + of the base grammar)
    • +
    • a tagging language TL (dependent on the tag schema models written in TSL)
    • +
    +
  • +
  • Because tagging models can e.g. be used as configuration techniques + in a code generator, appropriate infrastructure is generated as well.
  • +
  • Some tagging language examples
  • +
  • Although concrete languages (and their grammars) are themselves generated, + there is a + main grammar ocl.monticore.lang.Tagging, + where the tagging language is derived from. + See also detailed description
  • +
+

Use Case Diagrams (MontiCore stable)

+
    +
  • A textual use case diagram (UCD) language.
  • +
  • Detailed description
  • +
  • The project includes a grammar, a symbol table infrastructure, and a semantic differencing operator.
  • +
  • The language is defined by the grammar UCD.
  • +
  • It supports modeling actors, use cases, preconditions, associations between actors and use cases, + extend relations between use cases with guards, include relations between use cases, and + specialization relations between actors and use cases.
  • +
  • The grammars can easily be extended.
  • +
  • The following depicts a simple UCD in its textual syntax. +
    usecasediagram Example {
    +  @Player --
    +    Play,
    +    Pay,
    +    ChangeProfilePicture;
    +
    +  @AndroidPlayer specializes Player;
    +  @IOSPlayer specializes Player;
    +
    +  @Server --
    +    ShowAd,
    +    RegisterScore;
    +
    +  ShowAd extend Play [!isPremium];
    +  RegisterScore extend Play;
    +
    +  abstract Pay include CheckPremium;
    +  CreditCard specializes Pay;
    +  Bank specializes Pay;
    +  ChangeProfilePicture [isPremium];
    +}
    +
  • +
+

XML (MontiCore Stable)

+
    +
  • The MontiCore language for parsing XML artifacts. An example: +
    <Calendar>
    +  <Appointment name="lunch">
    +    <Date>24.04.2020</Date>
    +    <Time>11:30</Time>
    +    <Location>cafeteria</Location>
    +  </Appointment>
    +</Calendar>
    +
  • +
  • The XML grammar adheres to the common XML standard and allows parsing + arbitrary XML artifacts for further processing.
  • +
  • Actually the grammar represents a slight superset to the official XML standard. + It is intended for parsing XML-compliant artifacts. Further well-formedness + checks are not included, because we assume to parse correctly produced XML + documents only.
  • +
  • Please note that XML (like JSON or ASCII) is mainly a carrier language. + The concrete XML dialect and the question, how to recreate the + real objects / data structures, etc. behind the XML structure + is beyond this grammar, but can be applied to the AST defined here.
  • +
  • Main grammar + de.monticore.lang.XML + and + detailed description
  • +
+

JavaLight (MontiCore Stable)

+
    +
  • This is a reduced version of the Java language. + JavaLight is meant to be used to integrate simplified Java-like parts + in modeling languages but not to parse complete Java implementations.
  • +
  • It provides Java's attribute and method definitions, + statements and expressions, but + does not provide class or interface definitions and + also no wildcards in the type system.
  • +
  • One main usage of JavaLight is in the Grammar-language to model e.g. + Java methods. An example: +
    public void print(String name) {
    +  System.out.println("Hello " + name);
    +}
    +
  • +
  • Main grammar de.monticore.JavaLight + and + detailed description.
  • +
+ +
    +
  • This is the full Java' Language (as Opposed to JavaLight).
  • +
  • Main Grammar JavaDSL + and + detailed description.
  • +
+

Further Information

+ + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/docs/MontiCore--Selected-Languages.png b/docs/MontiCore--Selected-Languages.png new file mode 100644 index 0000000000..0e63c0ea9c Binary files /dev/null and b/docs/MontiCore--Selected-Languages.png differ diff --git a/docs/Notational-Conventions.png b/docs/Notational-Conventions.png new file mode 100644 index 0000000000..822d72970f Binary files /dev/null and b/docs/Notational-Conventions.png differ diff --git a/docs/Publications/index.html b/docs/Publications/index.html new file mode 100644 index 0000000000..13636cda08 --- /dev/null +++ b/docs/Publications/index.html @@ -0,0 +1,714 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Publications - MontiCore + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + +

Publications

+ + + + +

+Redirecting to https://www.se-rwth.de/research/MontiCore/ + +

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/docs/further_docs/Impressum/index.html b/docs/further_docs/Impressum/index.html new file mode 100644 index 0000000000..10abc42c45 --- /dev/null +++ b/docs/further_docs/Impressum/index.html @@ -0,0 +1,807 @@ + + + + + + + + + + + + + + + + + + + + + + + Impressum - MontiCore + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + + +

Impressum

+

According to German law, each website needs to have an "impressum", +declaring among other things who is responsible for the content. +A few things are currently unclear:

+
    +
  1. Is only github (in total) a website or each group +withing github, or each project?
  2. +
  3. What if the project is open source, do then all (German) + contributors have to add an impressum?
  4. +
  5. If the webpage is hosted outside Germany (like github), + does German law then apply at all?
  6. +
+

Although a lot of things are unclear and the +text below doesn't totally fit, we have opted to be on the +safe side and add our Impressum below. +Please note that this is an open source project +and each contributor may change any artifact of the project +-- even this text -- so we do not claim any responsibility +or credits for the project, even so we are currently having the +role of the curators.

+
+

Herausgeberin

+

Herausgegeben im Auftrag des Rektors der Rheinisch-Westfälischen Technischen Hochschule (RWTH) Aachen.

+

RWTH Aachen \ +Templergraben 55

+

52062 Aachen (Hausanschrift) \ +52056 Aachen (Postanschrift)

+

Telefon: +49 241 80 1 \ +Telefax: +49 241 80 92312

+

Internet: www.rwth-aachen.de

+

Die RWTH Aachen ist eine Körperschaft des öffentlichen Rechts. +Sie wird durch den Dr. rer. nat. Dr. h. c. mult. Ulrich Rüdiger vertreten.

+

Zuständige Aufsichtsbehörde

+

Ministerium für Kultur und Wissenschaft des Landes Nordrhein Westfalen, Völklinger Straße 49, 40221 Düsseldorf.

+

Umsatzsteuer-Identifikationsnummer

+

Gemäß Par. 27 a Umsatzsteuergesetz: DE 121689807

+

Inhaltliche Verantwortlichkeit

+

Webmaster / Webredaktion: \ +Bernhard Rumpe \ +E-Mail: webmaster@se-rwth.de

+

Die Webseiten der Zentralen Hochschulverwaltung, der Zentralen Einrichtungen, +der Gruppenvertretungen, der Institute, Lehrstühle sowie Lehr- und Forschungsgebiete werden von den +von diesen beauftragten Webkoordinatorinnen und Webkoordinatoren beziehungsweise deren Webmastern eigenständig betreut. +Details zu Fragen der Haftung sind in der Datenschutzerklärung nachzulesen.

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/docs/small_bulb.png b/docs/small_bulb.png new file mode 100644 index 0000000000..f9f0212a1f Binary files /dev/null and b/docs/small_bulb.png differ diff --git a/img/favicon.ico b/img/favicon.ico new file mode 100644 index 0000000000..9234cc2858 Binary files /dev/null and b/img/favicon.ico differ diff --git a/index.html b/index.html new file mode 100644 index 0000000000..da3591632a --- /dev/null +++ b/index.html @@ -0,0 +1,943 @@ + + + + + + + + + + + + + + + + + + + + + + + MontiCore + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + + +

+
+

+

+

MontiCore - Language Workbench and Development Tool Framework

+

MontiCore is a language workbench for the efficient +development of domain-specific languages (DSLs). It processes an extended +grammar format which defines the DSL and generates Java components for processing +the DSL documents. Examples for these components are parsers, +AST classes, symboltables or pretty printers. +This enables a user to rapidly define a language and use it together +with the MontiCore-framework to build domain-specific tools.

+

Some MontiCore advantages are the reusability of predefined language +components, conservative extension and composition mechanisms, and an +optimal integration of handwritten code into the generated tools. Its +grammar languages are comfortable to use.

+

Start here for developing with MontiCore.

+
+ + + +
[HKR21] Katrin Hölldobler, Oliver Kautz, Bernhard Rumpe:
+ MontiCore Language Workbench and Library Handbook: Edition 2021.
+ Shaker, 2021. +
+


+

A Teaser for MontiCore

+

To show a little of MontiCore's capabilities, the following (incomplete) +grammar might help:

+
grammar MyStatemachine extends Automata,                  // MontiCore grammar 
+                               MCBasicTypes, SetExpressions, MCCommonLiterals {     
+  start Automaton;
+
+  // overriding a nonterminal (to add optional conditions):
+  Transition = from:Name@State ":" Expression? "->" to:Name@State;
+
+  // add new variants of expressions
+  LogicalNotExpr implements Expression = "!" Expression;
+
+  XorExpr        implements Expression =
+        left:Expression "xor" right:Expression;
+
+  scope LetExpr  implements Expression =
+        "let" (VarDeclaration || ",")+ "in" Expression;
+
+  symbol VarDeclaration = MCType? Name "=" Expression ;
+}
+
+

The grammar language has a variety of mechanisms to define +new nonterminals using constants "!", +brackets (..), optionals ?, lists *, repetitions (..||..)+, etc. +The grammar builds an extended version of Statemachines reusing +existing grammar components, here Automata, MCBasicTypes, SetExpressions and MCCommonLiterals. +The grammar has 5 productions introducing 4 new nonterminals +and overrides Transition, +which is inherited from Automata. +Transition additionally has an optional Expression? as firing condition. +LogicalNotExpr, XorExpr, and LetExpr extend the already existing +Expression nonterminal and add new forms of expressions.

+

LetExpr introduces a new local variable, which is +visible only in that scope (indicated by keyword). +VarDeclaration defines the new place to define symbols (that have a Name). +There is an extensive infrastructure to manage the definition of names, visibility, etc.

+

MontiCore compiles the above grammar +into 78 classes with in +total 18629 lines of code that define the complete +frontend and a larger part of the backend of +a statemachine processor. +We now can write statemachines like:

+
statemachine PingPong {                                         // MyStatemachine
+  state Ping, Pong;
+  Ping : (speed > 14km/h && !missedBall) -> Pong
+}
+
+

MontiCore provides versions of expressions that use SI +Units like 240km/h or 14.2 m/s^2, but also Java +expressions like 2_000_000 and other variants including +appropriate type checks. +We include these forms of expressions by importing their grammars.

+

Please note that in both cases (extension and +overwriting existing nonterminals), we do not +touch nor copy/paste the predefined grammars, +but achieve an out-of-the-box reuse. +Out-of-the-box reuse also includes reuse of +predefined typechecks, code generation, etc. +They only need to be extended to the added variants. +Please also note that PlusExpr is mutually left-recursive. +-- Yes, that works in MontiCore 6.

+

Quick Start

+
$ cd /usr/local
+$ wget www.monticore.de/download/aut.tar.gz
+$ tar -xf aut.tar.gz
+$ cd mc-workspace
+$ wget www.monticore.de/download/monticore-cli.jar
+$ java -jar monticore-cli.jar -g Automata.mc4 -hcp hwc/ -mp monticore-cli.jar
+$ javac -cp monticore-cli.jar -sourcepath "src/;out/;hwc/" src/automata/AutomataTool.java
+$ java -cp "src/;out/;hwc/;monticore-cli.jar" automata.AutomataTool example/PingPong.aut PingPong.autsym
+
+

MontiCore has a Relaxed 3-Level License

+

Informal summary: +The MontiCore Language Workbench deals with three levels of code +(MontiCore LWB, tool derivates, product code). Each has its own licenses: +(1) Product code generated by a MontiCore tool derivate +is absolutely free for each form of use +including commercial use without any license restriction. +(2) Tool derivates created using the MontiCore language +workbench mention that it is built using MontiCore. There is +no other restriction. (BSD 3 Clause license) +(3) Adaptations of MontiCore should mention MontiCore and +results published back into this repository (LGPL license).

+

For details see Licenses.

+

More Information about MontiCore

+
    +
  • +

    MontiCore handbook. + The handbook describes how to use MontiCore as an out-of-the-box + language workbench, but also as grey box tooling framework. + It thus also gives an overview over a number of core mechanisms of MontiCore.

    +
  • +
  • +

    List of core grammars. + MontiCore concentrates on reuse. It therefore offers a set of + predefined language components, usually identified through an appropriate + component grammar allowing to define your own language as a + composition of reusable assets efficiently. reusable assets are among others: + several sets of literals, expressions, types, and statements, + which are freely composable.

    +
  • +
  • +

    List of languages. + This is a list of languages that can be used out of the box. Some of them + are in development, others rather stable. Several of these languages + are inspired by the UML/P (see [Rum16,Rum17]). + These complete languages are usually composed of a number of language + components.

    +
  • +
  • +

    This project is freely available software; you can redistribute + the MontiCore language workbench according to the rules described + in the licensing section.

    +
  • +
  • +

    Contributing to MontiCore: MontiCore is originally developed by the + Software Engineering group at RWTH. + Meanwhile, it is maintained by several groups and individual persons. + If you want to contribute to MontiCore, please create a + fork + and issue corresponding pull requests. + The RWTH and the Stuttgart development teams will review and merge changes. + (A more open process for common development is currently in discussion and + depends on interests from further people/groups.) + You may also ask to become a member of the project.

    +
  • +
  • +

    If questions appear e.g. on building an interpreter, please contact + monticore@se-rwth.de.

    +
  • +
+

Further Information

+ + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/mc-logo.png b/mc-logo.png new file mode 100644 index 0000000000..afa52ad892 Binary files /dev/null and b/mc-logo.png differ diff --git a/monticore-grammar/build.gradle b/monticore-grammar/build.gradle new file mode 100644 index 0000000000..991baab4a9 --- /dev/null +++ b/monticore-grammar/build.gradle @@ -0,0 +1,470 @@ +///* (c) https://github.com/MontiCore/monticore */ + +buildscript { + if (findProperty('bootstrap')) { + // fake a different group for Gradle to prevent it from including the build in it self + group = "de.mc" + } +} + +plugins { + id "java-library" + id "com.github.johnrengelman.shadow" version "7.1.2" + id "monticore" version "$version" + id "jacoco" +} + +description = 'MontiCore: Grammar' +if (!hasProperty('bootstrap')) { + group = "de.monticore" +} else { + group = "de.monticore.bs" +} + +def grammarOutDir = "$buildDir/generated-sources/monticore/sourcecode" +def testGrammarOutDir = "$buildDir/generated-test-sources/monticore/sourcecode" +def trafoOutDir = "$buildDir/generated-sources/monticore/trafo" + + +sourceSets { + main.java.srcDirs += [grammarOutDir] + test.java.srcDirs += [testGrammarOutDir] + + grammars { + java.srcDirs = [] + resources { + srcDirs([grammarDir, grammarOutDir]) + include "**/*.mc4" + include "**/*.mlc" + } + } + trafo { + java.srcDirs = [trafoOutDir] + } +} + +dependencies { + api project(':monticore-runtime') + implementation "com.google.guava:guava:$guava_version" + implementation "org.apache.commons:commons-lang3:$commons_lang3_version" + testImplementation "junit:junit:$junit_version" + trafoCompileOnly project(path) + // use compileOnly as the trafo-artifact is only an extension to the default artifact + // ensure the trafoCompileOnly dependency line is below the trafo sourceset definition +} + +task generate {} +task generateTR {} + +// one task per file +fileTree("src/main/grammars").matching { include '**/*.mc4' }.each { + def g = it + def taskname = "generateGrammar${it.getName().substring(0, it.getName().lastIndexOf('.'))}" + def withDSTLGen = ("true").equals(getProperty('genTR')) && !["ODRuleGeneration", "ODRules", "TFBasisExts", "TFCommons", // Do not generate TR grammars for TR-component grammars + "Grammar", "Grammar_WithConcepts"].contains(it.getName().substring(0, it.getName().lastIndexOf('.'))) + task "$taskname"(type: MCTask) { + grammar = file g + outputDir = file grammarOutDir + if (findProperty("ci") != null) { + script = "de/monticore/monticore_noreports.groovy" + } + def grammarIncludingPackage = file("src/main/grammars").toURI().relativize(g.toURI()).toString() + def uptoDate = incCheck(grammarIncludingPackage) + outputs.upToDateWhen { uptoDate } + } + generate.dependsOn("$taskname") + if (withDSTLGen) { // run MC on the TR grammar + task "${taskname}TR"(type: MCTask) { + outputDir = file trafoOutDir // output into separate sourceset for the trafo artifact + grammar = getTRFile(g, file(grammarOutDir)) + modelPath(grammarOutDir) + modelPath(file("src/main/grammars").toString()) + if (findProperty("ci") != null) { + script = "de/monticore/monticore_noreports.groovy" + } + def grammarIncludingPackage = file("src/main/grammars").toURI().relativize(g.toURI()).toString() + def trGrammarIncludingPackage = file(grammarOutDir).toURI().relativize(file(grammar).toURI()).toString() + outputs.upToDateWhen { incCheck(grammarIncludingPackage) } + outputs.upToDateWhen { incCheck(trGrammarIncludingPackage) } + isDSTL = true + dependsOn(generate) + } + generateTR.dependsOn("${taskname}TR") + } +} + +// grammar dependencies without generated TR grammars +def grammarDependencies = ext { + // no dependencies + MCBasics = [] + MCLiteralsBasis = [] + Completeness = [] + MCStatementsBasis = [] + Antlr = [] + + // one dependency + MCSynchronizedStatements = ["MCCommonStatements"] + CommonExpressions = ["ExpressionsBasis"] + BitExpressions = ["ExpressionsBasis"] + MCBasicTypes = ["MCBasics"] + MCFullGenericTypes = ["MCSimpleGenericTypes"] + ODRuleGeneration = ["MCFullGenericTypes"] + BasicSymbols = ["MCBasics"] + CompSymbols = ["BasicSymbols"] + AssignmentExpressions = ["ExpressionsBasis"] + UMLModifier = ["UMLStereotype"] + MCArrayTypes = ["MCBasicTypes"] + MCSimpleGenericTypes = ["MCCollectionTypes"] + MCCommonStatements = ["MCVarDeclarationStatements"] + MCCollectionTypes = ["MCBasicTypes"] + StreamExpressions = ["CommonExpressions"] + MCExceptionStatements = ["MCCommonStatements"] + MCFunctionTypes = ["MCBasicTypes"] + OOSymbols = ["BasicSymbols"] + TFCommons = ["TFBasisExts"] + MCJavaLiterals = ["MCCommonLiterals"] + UMLStereotype = ["MCCommonLiterals"] + MCArrayStatements = ["MCVarDeclarationStatements"] + + // two dependencies + Cardinality = ["MCBasics", "MCCommonLiterals"] + JavaClassExpressions = ["CommonExpressions", "MCVarDeclarationStatements"] + ExpressionsBasis = ["MCBasics", "MCLiteralsBasis"] + Grammar = ["MCCommonLiterals", "MCSimpleGenericTypes"] + MCReturnStatements = ["MCStatementsBasis", "ExpressionsBasis"] + MCCommonLiterals = ["MCBasics", "MCLiteralsBasis"] + MCLowLevelStatements = ["MCStatementsBasis", "MCBasics"] + MCAssertStatements = ["MCStatementsBasis", "ExpressionsBasis"] + + // three dependencies + LambdaExpressions = ["BasicSymbols", "MCBasicTypes", "ExpressionsBasis"] + TFBasisExts = ["JavaLight", "MCSimpleGenericTypes", "MCCommonLiterals"] + + // four dependencies + MCCommon = ["Cardinality", "Completeness", "UMLModifier", "UMLStereotype"] + ODRules = ["TFBasisExts", "MCCommonLiterals", "MCJavaLiterals", "UMLStereotype"] + MCVarDeclarationStatements = ["MCStatementsBasis", "MCBasicTypes", "ExpressionsBasis", "OOSymbols"] + + // five dependencies + JavaLight = ["AssignmentExpressions", "JavaClassExpressions", "MCCommonStatements", "MCArrayStatements", "MCReturnStatements"] + + // six dependencies + MCFullJavaStatements = ["MCAssertStatements", "MCExceptionStatements", "MCLowLevelStatements", "MCReturnStatements", "MCSynchronizedStatements", "MCArrayStatements"] + + // nine dependencies + Grammar_WithConcepts = ["Grammar", "MCCommonStatements", "MCReturnStatements", "MCExceptionStatements", "JavaClassExpressions", "JavaLight", "Antlr", "CommonExpressions", "BitExpressions"] +} + +compileJava { + tasks.findAll { task -> task.name.startsWith("generateGrammar") && !task.name.contains("TR") }.each { + def grammarName = it.getName().substring('generateGrammar'.length()) + def dependsOnGrammars = grammarDependencies[grammarName].collect { name -> tasks["generateGrammar${name}"] } + it.dependsOn dependsOnGrammars + } + + dependsOn project.collect { it.tasks.findAll { task -> task.name.startsWith("generateGrammar") } } +} + +compileTestJava { + dependsOn project.collect { it.tasks.withType(MCTask) } +} + +compileJava.dependsOn generate +compileTrafoJava.dependsOn generateTR + +sourcesJar.dependsOn project.collect { it.tasks.withType(MCTask) } +jar.dependsOn(sourcesJar) + +task generateTest {} + +// one task per file +fileTree("src/test/grammars").matching { include '**/*.mc4' }.each { + def g = it + task "generateTest${it.getName().substring(0, it.getName().lastIndexOf('.'))}"(type: MCTask) { + grammar = file g + outputDir = file testGrammarOutDir + handcodedPath "$projectDir/src/main/java", "$projectDir/src/test/java" + modelPath "$projectDir/$grammarDir", "$projectDir/src/test/grammars", "$projectDir/src/main/examples" + if (findProperty("ci") != null) { + script = "de/monticore/monticore_noreports.groovy" + } + def grammarIncludingPackage = file("src/test/grammars").toURI().relativize(g.toURI()).toString() + def uptoDate = incCheck(grammarIncludingPackage) + outputs.upToDateWhen { uptoDate } + } + generateTest.dependsOn("generateTest${it.getName().substring(0, it.getName().lastIndexOf('.'))}") +} + +fileTree("src/main/examples").matching { include '**/*.mc4' }.each { + def g = it + task "generateTest${it.getName().substring(0, it.getName().lastIndexOf('.'))}"(type: MCTask) { + grammar = file g + outputDir = file testGrammarOutDir + handcodedPath "$projectDir/src/main/java", "$projectDir/src/test/java" + modelPath "$projectDir/$grammarDir", "$projectDir/src/main/examples", "$projectDir/src/test/java" + if (findProperty("ci") != null) { + script = "de/monticore/monticore_noreports.groovy" + } + def grammarIncludingPackage = file("src/main/examples").toURI().relativize(g.toURI()).toString() + def uptoDate = incCheck(grammarIncludingPackage) + outputs.upToDateWhen { uptoDate } + } + generateTest.dependsOn("generateTest${it.getName().substring(0, it.getName().lastIndexOf('.'))}") +} + +compileTestJava.dependsOn(generateTest) +processGrammarsResources.dependsOn(generateTR) +processGrammarsResources.dependsOn(generate) + + +/** + * Integration with MLC language and tool + */ +task showArtifacts {} +task checkArtifacts {} +configurations { MLC } +dependencies { + MLC(group: 'de.monticore.lang', name: 'mlc-gradle', version: "$version") +} +StringJoiner joiner = new StringJoiner(" ") +configurations.compileClasspath.resolve().each { joiner.add(it.toString()) } +joiner.add "$projectDir/target/symbols" +String mp = joiner.toString() + +// two tasks per MLC file +// each task needs to depend on all internally promoted MLCs +fileTree("src").matching { include '**/*.mlc' }.each { + def f = it + def mlcName = it.getName().substring(0, it.getName().lastIndexOf('.')) + + task "showArtifacts${mlcName}"(type: JavaExec) { + classpath = configurations.MLC + group = 'MLC' + mainClass = 'de.monticore.mlc.MLCTool' + args "-input", f, "-projectDir", projectDir, "-mp", mp, "-s", "-reports", "${buildDir}/reports", "-a", "-noFQ" + dependsOn("generateGrammar${mlcName}") + showArtifacts.dependsOn("showArtifacts${mlcName}") + } + + task "checkArtifacts${mlcName}"(type: JavaExec) { + classpath = configurations.MLC + group = 'MLC' + mainClass = 'de.monticore.mlc.MLCTool' + args "-input", f, "-projectDir", projectDir, "-mp", mp, "-s", "-check", "-reports", "${buildDir}/reports" + dependsOn("generateGrammar${mlcName}") + checkArtifacts.dependsOn("checkArtifacts${mlcName}") + } +} + +///////////// dependencies between check artifacts tasks /////////////////////// + +checkArtifactsAssignmentExpressions.dependsOn(checkArtifactsExpressionsBasis) +checkArtifactsBitExpressions.dependsOn(checkArtifactsExpressionsBasis) +checkArtifactsCommonExpressions.dependsOn(checkArtifactsExpressionsBasis) +checkArtifactsExpressionsBasis.dependsOn(checkArtifactsMCBasics) +checkArtifactsExpressionsBasis.dependsOn(checkArtifactsMCLiteralsBasis) +checkArtifactsJavaClassExpressions.dependsOn(checkArtifactsCommonExpressions) + +checkArtifactsAntlr.dependsOn(checkArtifactsMCBasics) +checkArtifactsGrammar.dependsOn(checkArtifactsMCCommonLiterals) +checkArtifactsGrammar.dependsOn(checkArtifactsMCSimpleGenericTypes) +checkArtifactsGrammar_WithConcepts.dependsOn(checkArtifactsGrammar) +checkArtifactsGrammar_WithConcepts.dependsOn(checkArtifactsMCCommonStatements) +checkArtifactsGrammar_WithConcepts.dependsOn(checkArtifactsMCReturnStatements) +checkArtifactsGrammar_WithConcepts.dependsOn(checkArtifactsMCExceptionStatements) +checkArtifactsGrammar_WithConcepts.dependsOn(checkArtifactsJavaClassExpressions) +checkArtifactsGrammar_WithConcepts.dependsOn(checkArtifactsJavaLight) +checkArtifactsGrammar_WithConcepts.dependsOn(checkArtifactsAntlr) +checkArtifactsGrammar_WithConcepts.dependsOn(checkArtifactsCommonExpressions) +checkArtifactsGrammar_WithConcepts.dependsOn(checkArtifactsBitExpressions) + +checkArtifactsMCCommonLiterals.dependsOn(checkArtifactsMCLiteralsBasis) +checkArtifactsMCLiteralsBasis.dependsOn(checkArtifactsMCBasics) +checkArtifactsMCJavaLiterals.dependsOn(checkArtifactsMCCommonLiterals) + +checkArtifactsMCArrayStatements.dependsOn(checkArtifactsMCVarDeclarationStatements) +checkArtifactsMCAssertStatements.dependsOn(checkArtifactsMCStatementsBasis) +checkArtifactsMCAssertStatements.dependsOn(checkArtifactsExpressionsBasis) +checkArtifactsMCCommonStatements.dependsOn(checkArtifactsMCVarDeclarationStatements) +checkArtifactsMCExceptionStatements.dependsOn(checkArtifactsMCCommonStatements) +checkArtifactsMCFullJavaStatements.dependsOn(checkArtifactsMCAssertStatements) +checkArtifactsMCFullJavaStatements.dependsOn(checkArtifactsMCExceptionStatements) +checkArtifactsMCFullJavaStatements.dependsOn(checkArtifactsMCLowLevelStatements) +checkArtifactsMCFullJavaStatements.dependsOn(checkArtifactsMCReturnStatements) +checkArtifactsMCFullJavaStatements.dependsOn(checkArtifactsMCSynchronizedStatements) +checkArtifactsMCFullJavaStatements.dependsOn(checkArtifactsMCArrayStatements) +checkArtifactsMCLowLevelStatements.dependsOn(checkArtifactsMCStatementsBasis) +checkArtifactsMCReturnStatements.dependsOn(checkArtifactsMCStatementsBasis) +checkArtifactsMCReturnStatements.dependsOn(checkArtifactsExpressionsBasis) +checkArtifactsMCStatementsBasis.dependsOn(checkArtifactsMCBasics) +checkArtifactsMCSynchronizedStatements.dependsOn(checkArtifactsMCCommonStatements) +checkArtifactsMCVarDeclarationStatements.dependsOn(checkArtifactsMCStatementsBasis) +checkArtifactsMCVarDeclarationStatements.dependsOn(checkArtifactsMCBasicTypes) +checkArtifactsMCVarDeclarationStatements.dependsOn(checkArtifactsExpressionsBasis) +checkArtifactsMCVarDeclarationStatements.dependsOn(checkArtifactsOOSymbols) + +checkArtifactsBasicSymbols.dependsOn(checkArtifactsMCBasics) +checkArtifactsOOSymbols.dependsOn(checkArtifactsBasicSymbols) + +checkArtifactsODRuleGeneration.dependsOn(checkArtifactsMCFullGenericTypes) +checkArtifactsODRules.dependsOn(checkArtifactsMCFullGenericTypes) +checkArtifactsODRules.dependsOn(checkArtifactsTFBasisExts) +checkArtifactsODRules.dependsOn(checkArtifactsMCJavaLiterals) +checkArtifactsODRules.dependsOn(checkArtifactsUMLStereotype) +checkArtifactsTFBasisExts.dependsOn(checkArtifactsMCSimpleGenericTypes) +checkArtifactsTFBasisExts.dependsOn(checkArtifactsMCCommonLiterals) +checkArtifactsTFBasisExts.dependsOn(checkArtifactsJavaLight) +checkArtifactsTFCommons.dependsOn(checkArtifactsTFBasisExts) + +checkArtifactsMCArrayTypes.dependsOn(checkArtifactsMCBasicTypes) +checkArtifactsMCBasicTypes.dependsOn(checkArtifactsMCBasics) +checkArtifactsMCBasicTypes.dependsOn(checkArtifactsOOSymbols) +checkArtifactsMCCollectionTypes.dependsOn(checkArtifactsMCBasicTypes) +checkArtifactsMCFullGenericTypes.dependsOn(checkArtifactsMCSimpleGenericTypes) +checkArtifactsMCSimpleGenericTypes.dependsOn(checkArtifactsMCCollectionTypes) + +checkArtifactsCardinality.dependsOn(checkArtifactsMCBasics) +checkArtifactsCardinality.dependsOn(checkArtifactsMCCommonLiterals) +checkArtifactsJavaLight.dependsOn(checkArtifactsAssignmentExpressions) +checkArtifactsJavaLight.dependsOn(checkArtifactsJavaClassExpressions) +checkArtifactsJavaLight.dependsOn(checkArtifactsMCCommonStatements) +checkArtifactsJavaLight.dependsOn(checkArtifactsMCArrayStatements) +checkArtifactsJavaLight.dependsOn(checkArtifactsMCReturnStatements) +checkArtifactsMCCommon.dependsOn(checkArtifactsCardinality) +checkArtifactsMCCommon.dependsOn(checkArtifactsCompleteness) +checkArtifactsMCCommon.dependsOn(checkArtifactsUMLModifier) +checkArtifactsMCCommon.dependsOn(checkArtifactsUMLStereotype) +checkArtifactsUMLModifier.dependsOn(checkArtifactsUMLStereotype) +checkArtifactsUMLStereotype.dependsOn(checkArtifactsMCCommonLiterals) + +///////////// dependencies between show artifacts tasks /////////////////////// + +showArtifactsAssignmentExpressions.dependsOn(showArtifactsExpressionsBasis) +showArtifactsBitExpressions.dependsOn(showArtifactsExpressionsBasis) +showArtifactsCommonExpressions.dependsOn(showArtifactsExpressionsBasis) +showArtifactsExpressionsBasis.dependsOn(showArtifactsMCBasics) +showArtifactsExpressionsBasis.dependsOn(showArtifactsMCLiteralsBasis) +showArtifactsJavaClassExpressions.dependsOn(showArtifactsCommonExpressions) + +showArtifactsAntlr.dependsOn(showArtifactsMCBasics) +showArtifactsGrammar.dependsOn(showArtifactsMCCommonLiterals) +showArtifactsGrammar.dependsOn(showArtifactsMCSimpleGenericTypes) +showArtifactsGrammar_WithConcepts.dependsOn(showArtifactsGrammar) +showArtifactsGrammar_WithConcepts.dependsOn(showArtifactsMCCommonStatements) +showArtifactsGrammar_WithConcepts.dependsOn(showArtifactsMCReturnStatements) +showArtifactsGrammar_WithConcepts.dependsOn(showArtifactsMCExceptionStatements) +showArtifactsGrammar_WithConcepts.dependsOn(showArtifactsJavaClassExpressions) +showArtifactsGrammar_WithConcepts.dependsOn(showArtifactsJavaLight) +showArtifactsGrammar_WithConcepts.dependsOn(showArtifactsAntlr) +showArtifactsGrammar_WithConcepts.dependsOn(showArtifactsCommonExpressions) +showArtifactsGrammar_WithConcepts.dependsOn(showArtifactsBitExpressions) + +showArtifactsMCCommonLiterals.dependsOn(showArtifactsMCLiteralsBasis) +showArtifactsMCLiteralsBasis.dependsOn(showArtifactsMCBasics) +showArtifactsMCJavaLiterals.dependsOn(showArtifactsMCCommonLiterals) + +showArtifactsMCArrayStatements.dependsOn(showArtifactsMCVarDeclarationStatements) +showArtifactsMCAssertStatements.dependsOn(showArtifactsMCStatementsBasis) +showArtifactsMCAssertStatements.dependsOn(showArtifactsExpressionsBasis) +showArtifactsMCCommonStatements.dependsOn(showArtifactsMCVarDeclarationStatements) +showArtifactsMCExceptionStatements.dependsOn(showArtifactsMCCommonStatements) +showArtifactsMCFullJavaStatements.dependsOn(showArtifactsMCAssertStatements) +showArtifactsMCFullJavaStatements.dependsOn(showArtifactsMCExceptionStatements) +showArtifactsMCFullJavaStatements.dependsOn(showArtifactsMCLowLevelStatements) +showArtifactsMCFullJavaStatements.dependsOn(showArtifactsMCReturnStatements) +showArtifactsMCFullJavaStatements.dependsOn(showArtifactsMCSynchronizedStatements) +showArtifactsMCFullJavaStatements.dependsOn(showArtifactsMCArrayStatements) +showArtifactsMCLowLevelStatements.dependsOn(showArtifactsMCStatementsBasis) +showArtifactsMCReturnStatements.dependsOn(showArtifactsMCStatementsBasis) +showArtifactsMCReturnStatements.dependsOn(showArtifactsExpressionsBasis) +showArtifactsMCStatementsBasis.dependsOn(showArtifactsMCBasics) +showArtifactsMCSynchronizedStatements.dependsOn(showArtifactsMCCommonStatements) +showArtifactsMCVarDeclarationStatements.dependsOn(showArtifactsMCStatementsBasis) +showArtifactsMCVarDeclarationStatements.dependsOn(showArtifactsMCBasicTypes) +showArtifactsMCVarDeclarationStatements.dependsOn(showArtifactsExpressionsBasis) +showArtifactsMCVarDeclarationStatements.dependsOn(showArtifactsOOSymbols) + +showArtifactsBasicSymbols.dependsOn(showArtifactsMCBasics) +showArtifactsOOSymbols.dependsOn(showArtifactsBasicSymbols) + +showArtifactsODRuleGeneration.dependsOn(showArtifactsMCFullGenericTypes) +showArtifactsODRules.dependsOn(showArtifactsMCFullGenericTypes) +showArtifactsODRules.dependsOn(showArtifactsTFBasisExts) +showArtifactsODRules.dependsOn(showArtifactsMCJavaLiterals) +showArtifactsODRules.dependsOn(showArtifactsUMLStereotype) +showArtifactsTFBasisExts.dependsOn(showArtifactsMCSimpleGenericTypes) +showArtifactsTFBasisExts.dependsOn(showArtifactsMCCommonLiterals) +showArtifactsTFBasisExts.dependsOn(showArtifactsJavaLight) +showArtifactsTFCommons.dependsOn(showArtifactsTFBasisExts) + +showArtifactsMCArrayTypes.dependsOn(showArtifactsMCBasicTypes) +showArtifactsMCBasicTypes.dependsOn(showArtifactsMCBasics) +showArtifactsMCBasicTypes.dependsOn(showArtifactsOOSymbols) +showArtifactsMCCollectionTypes.dependsOn(showArtifactsMCBasicTypes) +showArtifactsMCFullGenericTypes.dependsOn(showArtifactsMCSimpleGenericTypes) +showArtifactsMCSimpleGenericTypes.dependsOn(showArtifactsMCCollectionTypes) + +showArtifactsCardinality.dependsOn(showArtifactsMCBasics) +showArtifactsCardinality.dependsOn(showArtifactsMCCommonLiterals) +showArtifactsJavaLight.dependsOn(showArtifactsAssignmentExpressions) +showArtifactsJavaLight.dependsOn(showArtifactsJavaClassExpressions) +showArtifactsJavaLight.dependsOn(showArtifactsMCCommonStatements) +showArtifactsJavaLight.dependsOn(showArtifactsMCArrayStatements) +showArtifactsJavaLight.dependsOn(showArtifactsMCReturnStatements) +showArtifactsMCCommon.dependsOn(showArtifactsCardinality) +showArtifactsMCCommon.dependsOn(showArtifactsCompleteness) +showArtifactsMCCommon.dependsOn(showArtifactsUMLModifier) +showArtifactsMCCommon.dependsOn(showArtifactsUMLStereotype) +showArtifactsUMLModifier.dependsOn(showArtifactsUMLStereotype) +showArtifactsUMLStereotype.dependsOn(showArtifactsMCCommonLiterals) + +java { + registerFeature('grammars') { + usingSourceSet(sourceSets.grammars) + } + registerFeature('tests') { + usingSourceSet(sourceSets.test) + } +} + +shadowJar { + from sourceSets.grammars.resources +} + +shadowJar.dependsOn(generate) +shadowJar.dependsOn(generateTest) + +jar.dependsOn(shadowJar) +jar.dependsOn(grammarsJar) + +if (findProperty('ci') == null) { + jar.dependsOn(shadowJar, testJar) +} + +if (("true").equals(getProperty('genTR'))) { + // Bundle and publish the generated TR artifacts separately as monticore-grammar-trafo + tasks.register('trafoJar', Jar) { + from sourceSets.trafo.output + group 'build' + } + tasks.register('trafoSourcesJar', Jar) { + from sourceSets.trafo.java + group 'build' + } + publishing { + publications { + "trafo"(MavenPublication) { + artifactId = project.name + "-trafo" + artifact tasks.trafoJar { appendix 'trafo' } + artifact tasks.trafoSourcesJar { appendix 'trafo'; classifier 'sources' } + } + } + } + // And expose the trafo jar artifact to other subprojects using a 'trafo' configuration + configurations { + trafo + } + artifacts { + trafo(trafoJar) + } +} diff --git a/monticore-grammar/src/main/examples/MCHexNumbers.mc4 b/monticore-grammar/src/main/examples/MCHexNumbers.mc4 new file mode 100644 index 0000000000..08f14de1ec --- /dev/null +++ b/monticore-grammar/src/main/examples/MCHexNumbers.mc4 @@ -0,0 +1,67 @@ +/* (c) https://github.com/MontiCore/monticore */ + +/** + * This grammar defines several additional literals for Numbers, + * which are common in computing, such as hexadecimals. + * The goal of this grammar is to ease the reuse of literals + * in Java-like sublanguages +*/ + +component grammar MCHexNumbers extends + MCNumbers, de.monticore.MCBasics { + + /*========================================================================*/ + /*== Hexadecimal =========================================================*/ + /*========================================================================*/ + + /** ASTHexadecimal represents a positive Hexadecimal number. + These numbers start with "0x". + @attribute source String-representation. + */ + Hexadecimal implements Number = + source:HexadecimalToken; + + astrule Hexadecimal = + method public int getValueInt() { + return Integer.parseInt(getSource().substring(2),16); + } + method public long getValue() { + return Long.parseLong(getSource().substring(2),16); + } + ; + + token HexadecimalToken + = '0' ('x' | 'X') HexDigit HexDigit*; + + fragment token HexDigit + = '0'..'9' | 'a'..'f' | 'A'..'F' ; + + /*========================================================================*/ + /*== Hexadezimal with negative number ====================================*/ + /*========================================================================*/ + + /** ASTHexInteger represents a positive or negative Hexadecimal number. + These numbers start with "0x" or "-0x". + @attribute source String-representation. + */ + HexInteger implements Number = + (negative:["-"])? hexadecimalpart:HexadecimalToken; + + astrule HexInteger = + method public int getValueInt() { + int a = Integer.parseInt(getHexadecimalpart().substring(2),16); + return negative ? -a : a; + } + method public long getValue() { + long a = Long.parseLong(getHexadecimalpart().substring(2),16); + return negative ? -a : a; + } + // source is handcoded: it adds the "-" + method public String getSource() { + String s = getHexadecimalpart(); + return (negative ? "-" +s : s); + } + ; + +} + diff --git a/monticore-grammar/src/main/examples/MCNumbers.mc4 b/monticore-grammar/src/main/examples/MCNumbers.mc4 new file mode 100644 index 0000000000..c10cf36bf6 --- /dev/null +++ b/monticore-grammar/src/main/examples/MCNumbers.mc4 @@ -0,0 +1,88 @@ +/* (c) https://github.com/MontiCore/monticore */ + +/** + * This grammar defines several literals for Integer. + * The scope of this grammar is to + * ease the reuse of literals structures in Java-like sublanguages, e.g., by + * grammar inheritance or grammar embedment. +*/ + +component grammar MCNumbers extends de.monticore.MCBasics { + + /*========================================================================*/ + /*== Number ==============================================================*/ + /*========================================================================*/ + + /** ASTNumber is the interface for longs + @method getSource() String-representation. + @method getValueInt() decoded value as int + @method getValue() decoded value as long + */ + interface Number; + + astrule Number = + method public String getSource() + { throw new UnsupportedOperationException( + "0xFF230 Method not implemented"); } + method public int getValueInt() + { throw new UnsupportedOperationException( + "0xFF231 Method not implemented"); } + method public long getValue() + { throw new UnsupportedOperationException( + "0xFF232 Method not implemented"); } + ; + + /*========================================================================*/ + /*== Decimal =============================================================*/ + /*========================================================================*/ + + /** ASTDecimal represents a positive Decimal number. + @attribute source String-representation. + */ + Decimal implements Number = + source:DecimalToken; + + astrule Decimal = + method public int getValueInt() { + return Integer.parseInt(getSource()); + } + method public long getValue() { + return Long.parseLong(getSource()); + } + ; + + token DecimalToken + = '0' | (NonZeroDigit Digit*); + + fragment token Digit = '0'..'9' ; + + fragment token NonZeroDigit = '1'..'9' ; + + /*========================================================================*/ + /*== Integer =============================================================*/ + /*========================================================================*/ + + /** ASTInteger represents a positive or negative Decimal number. + @attribute source String-representation. + */ + Integer implements Number = + (negative:["-"])? decimalpart:DecimalToken; + + astrule Integer = + method public int getValueInt() { + int a = Integer.parseInt(getDecimalpart()); + return negative ? -a : a; + } + method public long getValue() { + long a = Long.parseLong(getDecimalpart()); + return negative ? -a : a; + } + // source is handcoded: it adds the "-" + method public String getSource() { + String s = getDecimalpart(); + return (negative ? "-" +s : s); + } + ; + +} + diff --git a/monticore-grammar/src/main/examples/StringLiterals.mc4 b/monticore-grammar/src/main/examples/StringLiterals.mc4 new file mode 100644 index 0000000000..c396730ebf --- /dev/null +++ b/monticore-grammar/src/main/examples/StringLiterals.mc4 @@ -0,0 +1,99 @@ +/* (c) https://github.com/MontiCore/monticore */ + +/** + * This grammar defines Java compliant literals. The scope of this grammar is to + * ease the reuse of literals structures in Java-like sublanguages, e.g., by + * grammar inheritance or grammar embedment. + * The grammar contains literals from Java for: Char and String +*/ + +component grammar StringLiterals extends de.monticore.MCBasics { + + /*========================================================================*/ + /*== Characters ==========================================================*/ + /*========================================================================*/ + + /** ASTCharLiteral represents any valid character parenthesized with "'". + @attribute source String-representation (excluding "'"). + */ + CharLiteral = + source:CharToken; + + astrule CharLiteral = + method public char getValue() { + return + de.monticore.literals.MCLiteralsDecoder.decodeChar(getSource()); + } + ; + + token CharToken + = '\'' (SingleCharacter|EscapeSequence) '\'' + : {setText(getText().substring(1, getText().length() - 1));}; + + /*========================================================================*/ + /*== String ==============================================================*/ + /*========================================================================*/ + + /** ASTStringLiteral represents any valid character sequence parenthesized + with '"'. + @attribute source String-representation (excluding '"'). + @attribute content decoded String-representation (no '"' and escapes + are decoded). + */ + StringLiteral = + source:StringToken; + + astrule StringLiteral = + // Caution: decoded value is in cache: so change of value leads + // to outdated result in getValue() + content:String + method public String getValue() { + if(content == null) { + content = + de.monticore.literals.MCLiteralsDecoder.decodeString(getSource()); + } + return content; + }; + + token StringToken + = '"' (StringCharacters)? '"' + : {setText(getText().substring(1, getText().length() - 1));}; + + + /*========================================================================*/ + /*============================ LEXER RULES ===============================*/ + /*========================================================================*/ + + fragment token HexDigit + = '0'..'9' | 'a'..'f' | 'A'..'F' ; + + fragment token OctalDigit + = '0'..'7' ; + + fragment token SingleCharacter + = ~ ('\''); + + fragment token StringCharacters + = (StringCharacter)+; + + fragment token StringCharacter + = ~ ('"' | '\\') | EscapeSequence; + + // §3.10.6 Escape Sequences for Character and String Literals + fragment token EscapeSequence + = '\\' ('b' | 't' | 'n' | 'f' | 'r' | '"' | '\'' | '\\') + | OctalEscape | UnicodeEscape; + + fragment token OctalEscape + = '\\' OctalDigit + | '\\' OctalDigit OctalDigit + | '\\' ZeroToThree OctalDigit OctalDigit; + + fragment token UnicodeEscape + = '\\' 'u' HexDigit HexDigit HexDigit HexDigit; + + fragment token ZeroToThree + = '0'..'3' ; + +} + diff --git a/monticore-grammar/src/main/grammars/de/monticore/Cardinality.mc4 b/monticore-grammar/src/main/grammars/de/monticore/Cardinality.mc4 new file mode 100644 index 0000000000..fd6767f06b --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/Cardinality.mc4 @@ -0,0 +1,53 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore; + +/* This is a MontiCore stable grammar. + * Adaptations -- if any -- are conservative. */ + +import de.monticore.literals.*; +import de.monticore.*; + +/** + * Grammar to describe Cardinalities + * + * This grammar defines UML Cardinalities of forms "*", "[n..m]" "[n..*]" + * or also with spaces: "[ 3 .. * ]". + */ +component grammar Cardinality + extends de.monticore.MCBasics, + MCCommonLiterals +{ + + /** ASTCardinality represents a Cardinality in the UML/P + @attribute many True if "*" is set as Cardinality + @attribute lowerBound Minimum number of associated Classes/Objects + @attribute upperBound Maximum number of associated Classes/Objects; + zero denotes that there is no upper bound set. + @attribute lowerBoundLit Lower bound as Literal + @attribute upperBoundLit Upper bound as Literal + @attribute noUpperLimit True if a lower, but no upper bound exists "[n..*]" + */ + Cardinality = + "[" + ( many:["*"] {_builder.setLowerBound(0);_builder.setUpperBound(0);} + | lowerBoundLit:NatLiteral + { _builder.setLowerBound( + _builder.getLowerBoundLit().getValue()); + _builder.setUpperBound(_builder.getLowerBound()); } + ( ".." ( + upperBoundLit:NatLiteral + ( {_builder.setUpperBound( + _builder.getUpperBoundLit().getValue());}) + | + noUpperLimit:["*"] {_builder.setUpperBound(0);} ) )? + ) "]"; + + /** + A Cardinality Object stores the bounds as integers. + */ + + astrule Cardinality = + lowerBound:int + upperBound:int; + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/Cardinality.mlc b/monticore-grammar/src/main/grammars/de/monticore/Cardinality.mlc new file mode 100644 index 0000000000..b2b546cd9c --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/Cardinality.mlc @@ -0,0 +1,25 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore; + +mlc Cardinality { + + // export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/Cardinality.mc4"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/cardinality/**.java"; + } + + // export the prettyprinter + export "$projectDir/src/main/java" { + include "de/monticore/prettyprint/Cardinality*.java"; + } + + promote { + mlc "de.monticore.MCBasics"; + mlc "de.monticore.literals.MCCommonLiterals"; + } +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/Completeness.mc4 b/monticore-grammar/src/main/grammars/de/monticore/Completeness.mc4 new file mode 100644 index 0000000000..aafcc6b8cd --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/Completeness.mc4 @@ -0,0 +1,39 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore; + +/* This is a MontiCore stable grammar. + * Adaptations -- if any -- are conservative. */ + +/** + * This grammar defines completeness information in UML + * like "...", "(c)", but also "(...,c)" + * +*/ +component grammar Completeness { + + /** ASTCompleteness represents the completeness in + several kinds of diagrams of the UML/P; + Syntax: (left-completeness, right-completeness) + + For example: + CD: Diagramm: left: Types, right: Assoziations + Types: left: Attributes, right: Methods + @attribute incomplete true if left and right side are + incomplete (...) + @attribute complete true if left and right side are + complete (c) + @attribute rightComplete true if only right side is complete (...,c) + @attribute leftComplete true if only left is complete (c,...) + */ + Completeness = + // separate brackets to avoid lexer-symbol clashes + {noSpace(2,3)}? "(" [complete:"c"] ")" // "(c)" + | {noSpace(2,3)}? "(" [incomplete:"..."] ")" // "(...)" + | [incomplete:"(...,...)"] + | {noSpace(2,3,4,5)}? "(" [complete:"c"] "," "c" ")" // "(c,c)" + | [rightComplete:"(...,c)"] + | [leftComplete:"(c,...)"]; + + // to allow use of "c" e.g. as variable: + nokeyword "c"; +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/Completeness.mlc b/monticore-grammar/src/main/grammars/de/monticore/Completeness.mlc new file mode 100644 index 0000000000..1bfcd24f4d --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/Completeness.mlc @@ -0,0 +1,46 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore; + +/** + * Language component of the Completeness grammar. This language component + * promotes the usage of the MontiCore RTE, the JDK, etc. Hence, language + * components using this Completeness language component do not need to allow + * their usage again. + */ +mlc Completeness { + + // export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/Completeness.mc4"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/completeness/**.java"; + } + + // export the prettyprinter + export "$projectDir/src/main/java" { + include "de/monticore/prettyprint/Completeness*.java"; + } + + // promote using the JDK except for reflection + promote { + include "$mp/java/**"; + exclude "$mp/java/lang/reflect/**"; + } + + // promote using everything from MontiCore RTE and co. + promote { + include "$mp/de/monticore/ast/**"; + include "$mp/de/monticore/generating/**"; + include "$mp/de/monticore/io/**"; + include "$mp/de/monticore/parser/**"; + include "$mp/de/monticore/prettyprint/**"; + include "$mp/de/monticore/symboltable/**"; + include "$mp/de/monticore/utils/**"; + include "$mp/de/monticore/visitor/**"; + include "$mp/de/se_rwth/commons/**"; + include "$mp/org/antlr/v4/runtime/**"; + } +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/Grammars/index.html b/monticore-grammar/src/main/grammars/de/monticore/Grammars/index.html new file mode 100644 index 0000000000..db0ba39e32 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/Grammars/index.html @@ -0,0 +1,1596 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Overview - MontiCore + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + + + + + +

MontiCore Grammars for Expressions, Literals and Types - an Overview

+

MontiCore is a language workbench. It uses an +extended grammar format as primary mechanism to describe DSLs. This format +allows to compose language components by grammar +(1) inheritance, (2) extension, (3) embedding +and (4) aggregating. Please refer the +MontiCore handbook for more details.

+

MontiCore bundles a generator that can produce lots of infrastructure from +MontiCore grammars. Like grammars, this infrastructure is composable. and +can also be extended with handwrittten code. Most importantly, these +extensions and the grammar composition are compatible which +leads to optimal forms of reuse.

+

This documentation presents a library of language components provided by the +core of the MontiCore project together with short descriptions and status +information +(Status of Grammars). +The components are mainly defined by a primary grammar plus associated Java +and template files.

+

The presented components are mainly based on the grammars in the +MontiCore/monticore project. They are organized in the following +packages under the monticore-grammar/src/main/grammars/ folder hierarchy:

+
    +
  • de.monticore
  • +
  • de.monticore.expressions
  • +
  • de.monticore.literals
  • +
  • de.monticore.statements
  • +
  • de.monticore.symbols
  • +
  • de.monticore.types
  • +
+

Additionally, the documentation presents some expression/type related language +components in projects that extend MontiCore's core languages. +For more languages and language components, follow +this link.

+

General: List of Grammars in package de.monticore

+

MCBasics.mc4 (stable)

+
    +
  • This grammar defines basic rules for naming, spacing, and +Java-like comments being useful in many languages.
  • +
+

Types: List of Grammars in package de.monticore.types

+

These grammars generally deal with type definitions and build on each +other. Some snipets for type definitions:

+
grammars          some examples
+MCBasicTypes      boolean  byte  short  int
+                  long  char  float  double
+                  void  Person  a.b.Person
+                  import a.b.Foo.*;
+MCCollectionTypes List<.>   Set<.>
+                  Optional<.>   Map<.,.>
+MCSimpleGenericTypes
+                  Foo<.>  a.b.Bar<.,..,.>
+MCFullGenericTypes
+                  Foo<? extends .>
+                  Foo<? super .>
+MCArrayTypes      Person[]  int[][]
+SI Unit types     km/h  km/h<long>
+RegExType         R"[a-z][0-9*]"
+
+

MCBasicTypes.mc4 (stable)

+
    +
  • This grammar defines basic types and thus eases the reuse of type +structures in languages similar to Java. However, the type definitions in the +grammar are somewhat simplified, e.g., they do not comprise generics.
  • +
  • The grammar contains types from Java, e.g., primitives, void, and +classes (sometimes referred to as "reference types").
  • +
  • Example type definitions: boolean, byte, short, int, long, char, +float, double, void, Person, a.b.Person, import a.b.Foo.*;.
  • +
+

MCCollectionTypes.mc4 (stable)

+
    +
  • This grammar defines four generic types: List<A>, Map<A,B>, Set<A> and +Optional<A> on top of basic types.
  • +
  • These four generic types correspond to a typical predefined set of generic +types used, e.g., in connection with UML class diagrams or the +OCL. UML associations typically have those association multiplicities which is +why the generic types are of general interest.
  • +
  • The grammar eases the reuse of type structures in languages similar to Java +that are somewhat simplified, e.g., by omitting general generics.
  • +
  • Example type definitions: List<.>, Set<.>, Optional<.>, Map<.,.>.
  • +
+

MCSimpleGenericTypes.mc4 (stable)

+
    +
  • This grammar provides rules for the definition of custom generic types +such as Blubb<A>, Bla<B,C>, Foo<Blubb<D>>.
  • +
  • The grammar covers a wide range of uses for generic types. Unlike Java, it +does however not cover type restrictions on arguments.
  • +
  • Example type definitions: Foo<.>, a.b.Bar<.,..,.>.
  • +
+

MCFullGenericTypes.mc4 (stable)

+
    +
  • This grammar completes type definitions to support the full Java type system +including wildcards on generic types like Blubb<? extends A>.
  • +
  • A general advice: When you are not sure that you need this kind of +types, use a simpler version from above. Type checking is tricky.
  • +
  • Example type definitions: Foo<? extends .>, Foo<? super .>.
  • +
+

MCArrayTypes.mc4 (stable)

+
    +
  • The grammar provides means to define arrays.
  • +
  • Arrays are orthogonal to the generic extensions and may +thus be combined with any of the above grammars.
  • +
  • Example type definitions: Person[], int[][].
  • +
+

SIUnitTypes4Math.mc4 for Physical SI Units (stable)

+

The known units s, m, kg, A, K, mol, cd from the international system of +units (SI Units) and their combinations, such as km/h or mg, etc. can +be used as ordinary types (instead of only numbers). +The typecheck is extended to prevent e.g. assignment of a weight to a length +variable or to add appropriate conversion, e.g. when a km/h-based velocity is +e.g. stored in a m/s-based variable.

+

The grammar resides in the MontiCore/SIunits project.

+
    +
  • Example type definitions: km/h
  • +
+

SIUnitTypes4Computing.mc4 for Physical SI Units (stable)

+

Includes the types from SIUnitTypes4Math(see above), like km/h, but also allows to add a +resolution, such as km/h<int>. Here SI Unit types, +like km/h<.>, are used as generic type constructor that may take a number type, +such as int, long, double, float as argument.

+

The grammar resides in the MontiCore/SIunits project.

+
    +
  • Example type definitions: km/h<long>
  • +
+

RegExType.mc4 (stable)

+

Embedded in R"..." a regular expressions +can be used as ordinary type to constrain the values allowed for stored variables, attributes, +parameters. Types are e.g. , such as R"[a-z]" (single character) or R"^([01][0-9]|2[0-3])$" (hours). +A typecheck for these types can only be executed at runtime and e.g. issue +exceptions (or trigger repair functions) if violated. The static typecheck only uses String as +underlying carrier type.

+

This grammar resides in the MontiCore/RegEx project.

+

Symbols: List of Grammars in package de.monticore.symbols

+

These two grammars do not provide syntax themselves, but +characterize important forms of symbols, that will be used +in the type and the expression grammars to define shared +kinds of symbols.

+

BasicSymbols.mc4 (stable)

+
    +
  • This grammar defines symbols for Types (of all kinds), Functions, + Variables and TypeVariables.
  • +
  • The defined symbols are of general form and can be used in functional, OO + and other contexts. They do not preculde a concrete syntax and do not yet + embody OO specifics.
  • +
  • Remark: This grammar is not intended to define concrete or abstract syntax, but the + infrastructure for symbols.
  • +
+

OOSymbols.mc4 (stable)

+
    +
  • This grammar defines symbols for objectoriented Types, Methods, and + Fields by mainly extending the symbols defined in BasicTypeSymbols.
  • +
  • The newly defined symbols extend the general ones by typical + objectoriented features, such as private, static, etc. + Again they do not preculde a concrete syntax.
  • +
  • Remark: This grammar is not intended to define concrete or abstract syntax, but the + infrastructure for symbols in objectoriented context.
  • +
+

Expressions: List of Grammars in package de.monticore.expressions

+

Expressions are defined in several grammars forming a (nonlinear) hierarchy, +so that developers can choose the optimal grammar they want to build on +for their language and combine these with the appropriate typing +infrastructure.

+

This modularity of expressions and associated types greatly eases +the reuse of type structures in languages similar to Java. +Some snipets for operators defined in expressions:

+
grammar        operators and examples in this grammar
+CommonExp:     /  %  +  -  <=  >=  ==  >  <  !=  ~.  !.  .?.:.
+               &&  ||  ~. 
+AssigementExp: ++  --  =  +=  -=  *=  /=  &=  |=  ^=  >>=  >>>=  <<=  %=
+BitExp:        &  |  ^  <<  >>  >>>
+OclExp:        implies  <=>  |  &  forall  exists  let.in. .@pre  .[.]  .**
+               Set{.|.}
+SetExp:        .isin.  .in.  union  intersect  setand  setor
+               { item | specifier }
+OptionalOps:   ?:  ?<=  ?>=  ?<  ?>  ?==  ?!=  ?~~   ?!~ 
+LambdaExp:     i->i   (a,b)->a+b
+SIUnits:       5km  3,2m/s  22l  2.400J  
+JavaClass:     this  .[.]  (.).  super  .instanceof.
+
+

ExpressionsBasis.mc4 (stable)

+
    +
  • This grammar defines core interfaces for expressions and imports the +kinds of symbols necessary.
  • +
  • The symbols are taken over from the TypeSymbols grammar (see below).
  • +
  • A hierarchy of conservative extensions to this grammar realize +these interfaces in various forms.
  • +
+

CommonExpressions.mc4 (stable)

+
    +
  • This grammar defines a typical standard set of operations for +expressions.
  • +
  • This is a subset of Java as well as OCL/P, +mainly for arithmetic, comparisons, variable use (v), +attribute use (o.att), method call (foo(arg,arg2)) and brackets (exp).
  • +
+

AssignmentExpressions.mc4 (stable)

+
    +
  • This grammar defines all Java expressions that have side effects.
  • +
  • This includes assignment expressions like =, +=, etc. and +suffix and prefix expressions like ++, --, etc.
  • +
+

BitExpressions.mc4 (stable)

+
    +
  • This grammar defines a typical standard set of operations for +expressions.
  • +
  • This is a subset of Java for binary expressions +like <<, >>, >>>, &, ^ and |
  • +
+

OCLExpressions.mc4 (stable)

+
    +
  • This grammar defines expressions typical to UMLs OCL . + OCL expressions can savely be composed if with other forms of expressions
    + given in the MontiCore core project (i.e. as conservative extension).
  • +
  • It contains various logical operations, such as quantifiers, + the let and the @pre construct, and a transitive closure for + associations, as discussed in [Rum17,Rum17].
  • +
  • This grammar resides in the MontiCore/OCL project.
  • +
+

SetExpressions.mc4 (stable)

+
    +
  • This grammar defines set expressions like set union, intersection etc. +these operations are typical for a logic with set operations, like +UML's OCL. These operators are usually infix and are thus more intuitive +as they allow math oriented style of specification.
  • +
  • Most of these operators are in principle executable, so it might be interesting to include them in a high level programming language (see e.g. Haskell)
  • +
  • This grammar resides in the MontiCore/OCL project.
  • +
+

OptionalOperators.mc4 (stable)

+
    +
  • This grammar defines nine operators dealing with optional values, e.g. defined by + java.lang.Optional. The operators are also called Elvis operators.
  • +
  • E.g.: val ?: 42 equals to val.isPresent() ? val.get() : 42
  • +
  • x ?>= y equals x.isPresent() ? x.get() >= y : false
  • +
  • This grammar resides in the MontiCore/OCL project.
  • +
+

LambdaExpressions.mc4 (beta)

+
    +
  • This grammar defines lambda expressions. + Lambda expression define anonymous functions that can be passed around + as values e.g. to higher-order functions + and that can be evaluated on arguments of appropriate types.
  • +
  • Lambda expressions are fully typed (see e.g. in Haskell) and can be nested.
  • +
  • This is only the subset of Java's lambda expressions that allows to define + a functional programming style (thus preventing side effects).
  • +
+

SIUnits.mc4 for Physical SI Units (stable)

+
    +
  • This grammar the international system of units (SI units), based on + the basis units s, m, kg, A, K, mol, cd, + provides a variety of derived units, and can be refined using prefixes such + as m(milli), k(kilo), etc.
  • +
  • The SI Unit grammar provides an extension to expressions, but also to the + typing system, e.g. types such as km/h or km/h<long>, + and literals, such as e.g. 5.3 km/h.
  • +
  • The grammars reside in the MontiCore/SIunits project
  • +
+

JavaClassExpressions.mc4 (stable)

+
    +
  • This grammar defines Java specific class expressions like super, +this, type cast, etc.
  • +
  • This grammar should only be included, when a mapping to Java is +intended and the full power of Java should be available in the +modeling language.
  • +
+

Literals: List of Grammars in package de.monticore.literals

+

Literals are the basic elements of expressions, such as numbers, strings, +truth values. Some snipets:

+
grammar           examples of this grammar
+MCCommonLit       3  -3  2.17  -4  true  false  'c' 
+                  3L  2.17d  2.17f  0xAF  "string"  
+                  "str\uAF01\u0001"  null
+MCJavaLiterals    999_999  0x3F2A  0b0001_0101  0567  1.2e-7F
+SIUnitLiterals    5.3km/h  7mg
+
+

MCLiteralsBasis.mc4 (stable)

+
    +
  • This grammar defines core interface for literals.
  • +
  • Several conservative extensions to this grammar realize +various forms of literals.
  • +
+

MCCommonLiterals.mc4 (stable)

+
    +
  • This grammar defines the typical literals for an expression language, such as + characters: 'c', Strings "text", booleans: "true", "null", or numbers 10, + -23, 48l, 23.1f.
  • +
  • Strings and characters use the Java-like escapes like "\n".
  • +
  • Each defined nonterminal is extended by a conversion function getValue() + of appropriate type and a retrieve function getSource() for a text representation + of the literal.
  • +
+

MCJavaLiterals.mc4 (stable)

+
    +
  • This grammar defines Java compliant literals and builds on MCCommonLiterals.
  • +
  • The scope of this grammar is to + ease the reuse of literals structures in Java-like sublanguages.
  • +
  • The grammar contains literals from Java, e.g., Boolean, Char, String, ....
  • +
  • Please note that Java (and this grammar) + has an extended syntax e.g. for integers using underscores + or other kinds of encodings. They parse e.g. 999_999, 0x3F2A, or 0b10100.
  • +
  • Like above getValue() and getSource() allow to retrive the content + as value resp. as text string.
  • +
+

SIUnitLiterals.mc4 for Physical SI Units (stable)

+

Provides concrete values, such as 5.3 km/hor 7 mg for the international system of +units (SI Units). +The grammar resides in the MontiCore/SIunits project.

+

Statements: List of Grammars in package de.monticore.statements

+

Statements are the constructive part of programs: They allow to +change variables, call functions, send messages etc. +The following hierarchy of statement definitions should allow +the developers to choose needed forms of statements and extend it +by their own additional needs. The provided list of statements +is inspired by Java (actually subset of Java). Some example statements:

+
int i;   int j = 2;                     Person p[] = { foo(3+7), p2, ...}
+if (.) then . else .                    for ( i = .; .; .) {.}
+while (.) .                             do . while (.)
+switch (.) { case .: .; default: .}
+foo(1,2,3)                              return .                                
+assert . : "..."
+try {.} catch (.) {.} finally {.}       throw .           
+break .                                 continue .
+label:                                  private  static  final  native ...
+
+

MCStatementsBasis.mc4 (stable)

+
    +
  • This grammar defines the core interface for statements.
  • +
  • A hierarchy of conservative extensions to this grammar is provided below.
  • +
+

MCCommonStatements.mc4 (stable)

+
    +
  • This grammar defines typical statements, such as method calls + (which are actually expressions), + assignment of variables, if, for, while, switch statements, and blocks.
  • +
  • This embodies a complete structured statement language, however does not + provide return, assert, exceptions, and low-level constructs like break.
  • +
+

MCAssertStatements.mc4 (stable)

+
    +
  • This grammar defines exactly the assert statement as known from Java.
  • +
  • It can be used independently of other Java statements.
  • +
+

MCExceptionStatements.mc4 (stable)

+
    +
  • This grammar defines the exception statements.
  • +
  • This includes Java try with catch and finally, as well as throw.
  • +
+

MCSynchronizedStatements.mc4 (stable)

+
    +
  • This grammar defines the Java-like synchronized statement.
  • +
+

MCLowLevelStatements.mc4 (stable)

+
    +
  • This grammar defines three low-level statements that Java provides.
  • +
  • It contains the break and continue statements and the possibility to label a statement.
  • +
+

MCReturnStatements.mc4 (stable)

+
    +
  • This grammar defines the Java-like return statement.
  • +
+

MCFullJavaStatements.mc4 (stable)

+
    +
  • This grammar defines all Java statements.
  • +
  • This is neither a generalized approximation nor a restricted overapproximation, + but exact.
  • +
+

Further grammars in package de.monticore

+

several other grammars are also available:

+

RegularExpressions.mc4 (stable)

+
    +
  • This grammar defines regular expressions (RegEx) as used in Java (see e.g. java.util.regex.Pattern).
  • +
  • It provides common regex tokens such as
  • +
  • character classes, e.g., lowercase letters ([a-z]), the letters a, b, + and c ([abc])
  • +
  • anchors, e.g., start of line (^), end of line ($), word boundary (\b),
  • +
  • quantifiers, e.g., zero or one (?), zero or more (*), exactly 3 ({3}),
  • +
  • RegEx also supports to capture groups and referencing these captured groups + in replacements.
  • +
  • For example, ^([01][0-9]|2[0-3]):[0-5][0-9]$ matches all valid timestamps in + HH:MM format.
  • +
  • The main nonterminal RegularExpression is not part of the expression hierarchy and + thus regular expressions are not used as ordinary values. Instead + the nonterminal RegularExpression is can be used in aother places of a language + e.g. we do that as additional + restriction for String values in input/output channels in architectural langages.
  • +
  • This grammar resides in the MontiCore/RegEx project
  • +
+

Cardinality.mc4 (stable)

+
    +
  • This grammar defines UML Cardinalities of forms *, [n..m] or [n..*].
  • +
+

Completeness.mc4 (stable)

+
    +
  • This grammar defines completeness information in UML + like ..., (c), but also (...,c).
  • +
+

UMLModifier.mc4 (stable)

+
    +
  • The grammar contains the modifiers that UML provides.
  • +
  • This includes public private, protected, final, abstract, local, + derived, readonly, and static, but also the + compact syntactic versions +, #, -, / and ? (for readonly).
  • +
  • UML modifiers are not identical to Java modifiers (e.g. native or + threadsafe are missing.)
  • +
+

UMLStereotype.mc4 (stable)

+
    +
  • This grammars defines Stereotypes like <<val1,val2="text",...>>
  • +
  • Methods contains(name), getValue(name) assist Stereotype retrieval.
  • +
  • Values may only be of type String. + The real value unfortunately in UML is only encoded as String.
  • +
  • We suggest to use a tagging infrastructure that even allows to + type the possible forms of tags.
  • +
+

MCCommon.mc4 (stable)

+
    +
  • This grammar composes typical UML-like grammar components.
  • +
  • This includes Cardinality, Completeness, UMLModifier, and UMLStereotype.
  • +
+

JavaLight.mc4 (stable)

+
int age = 3+x; 
+List<Person> myParents;
+
+@Override
+public int print(String name, Set<Person> p) {
+  int a = 2 + name.length();
+  if(a < p.size()) {
+    System.out.println("Hello " + name);
+  }
+  return a;
+}
+
+
    +
  • JavaLight is a subset of Java that MontiCore itself + uses as intermediate language for the code generation process.
  • +
  • JavaLight doesn't provide all forms of classes (e.g., inner classes) + and reduces the type system to normal generic types.
    + However, that is sufficient for all code generated by MontiCore.
  • +
  • JavaLight supports Java expressions (including anonymous classes), + Java statements as relevant to MontiCore code generation, method declaration, + constructors, constants, interface methods, and annotations.
  • +
  • JavaLight composes the grammars CommonExpressions, + AssignmentExpressions, + JavaClassExpressions, + MCCommonStatements, + MCBasicTypes, and + OOSymbols.
  • +
  • JavaLight can be used for other generator tools than MontiCore, + especially as its core templates are reusable and new templates + for customized method bodies can be added using MontiCore's + Hook-Mechanisms.
  • +
+

MontiCore Example Grammars for the Interested Reader

+

The monticore-grammar/src/main/examples folder hosts the following example +grammars:

+ +

Further Information

+ + + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/monticore-grammar/src/main/grammars/de/monticore/JavaLight.mc4 b/monticore-grammar/src/main/grammars/de/monticore/JavaLight.mc4 new file mode 100644 index 0000000000..e672ac8c96 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/JavaLight.mc4 @@ -0,0 +1,106 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore; + +/* This is a MontiCore stable grammar. + * Adaptations -- if any -- are conservative. */ + +import de.monticore.statements.*; +import de.monticore.expressions.*; +import de.monticore.symbols.*; +import de.monticore.types.*; + +/** + * The JavaLight language defines a subset of the Java + * programming language. The language introduces Java + * method declarations, constructor declarations, + * interface method declarations, attributes, and + * annotations. The JavaLight language neither defines + * classes nor interfaces. However, it is easily reusable + * and extensible for the creation of more complex + * languages such as the complete Java programming language. + * For example, the JavaLight language is used in the + * MontiCore grammar language for specifying ast rules and + * symbol rules. +*/ + +component grammar JavaLight extends AssignmentExpressions, + JavaClassExpressions, + MCCommonStatements, + MCArrayStatements, + MCReturnStatements { + +external ExtTypeParameters; + +interface ClassBodyDeclaration; + +interface InterfaceBodyDeclaration; + +interface scope (shadowing non_exporting ordered) +symbol JavaMethod extends Method = Name; + +symbolrule JavaMethod = + exceptions: de.monticore.types.check.SymTypeExpression* + annotations: de.monticore.types.check.SymTypeExpression* + isAbstract: boolean + isSynchronized: boolean + isNative:boolean + isStrictfp: boolean + isDefault: boolean; + +MethodDeclaration implements JavaMethod, ClassBodyDeclaration, InterfaceBodyDeclaration + = MCModifier* ExtTypeParameters? + MCReturnType Name FormalParameters (dim:"[" "]")* + ("throws" Throws)? (MCJavaBlock | ";"); + +ConstructorDeclaration implements JavaMethod, ClassBodyDeclaration + = MCModifier* ExtTypeParameters? Name FormalParameters + ("throws" Throws)? MCJavaBlock; + +ConstDeclaration extends LocalVariableDeclarationStatement + implements ClassBodyDeclaration, + InterfaceBodyDeclaration + = LocalVariableDeclaration ";"; + +Throws + = (MCQualifiedName || ",")+; + +LastFormalParameter + = JavaModifier* MCType "..." DeclaratorId; + +FormalParameterListing + = (FormalParameter || ",")+ ("," LastFormalParameter)? + | LastFormalParameter; + +FormalParameters + = "(" FormalParameterListing? ")"; + + +// ANNOTATIONS + +Annotation implements MCModifier, ElementValue + = "@" annotationName:MCQualifiedName + ( "(" AnnotationArguments? ")" )?; + +interface AnnotationArguments ; + +AnnotationPairArguments implements AnnotationArguments + = (ElementValuePair || ",")+; + +interface ElementValue; + +ElementValueOrExpr implements AnnotationArguments + = ElementValue | Expression; + +ElementValuePair + = Name "=" ElementValueOrExpr; + +ElementValueArrayInitializer implements ElementValue + = "{" (ElementValueOrExpr || ",")* (",")? "}"; + +// ARRAY-Creator + +ArrayDimensionByInitializer implements ArrayDimensionSpecifier + = (dim:"[" "]")+ ArrayInit; + +} + diff --git a/monticore-grammar/src/main/grammars/de/monticore/JavaLight.mlc b/monticore-grammar/src/main/grammars/de/monticore/JavaLight.mlc new file mode 100644 index 0000000000..a0e21b19e4 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/JavaLight.mlc @@ -0,0 +1,30 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore; + +mlc JavaLight { + + // export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/JavaLight.mc4"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/javalight/**.java"; + } + + // export handwritten code + export "$projectDir/src/main/java" { + include "de/monticore/javalight/**.java"; + include "de/monticore/prettyprint/JavaLight*.java"; + } + + promote { + mlc "de.monticore.expressions.AssignmentExpressions"; + mlc "de.monticore.expressions.JavaClassExpressions"; + mlc "de.monticore.statements.MCCommonStatements"; + mlc "de.monticore.statements.MCArrayStatements"; + mlc "de.monticore.statements.MCReturnStatements"; + } + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/JavaLight/index.html b/monticore-grammar/src/main/grammars/de/monticore/JavaLight/index.html new file mode 100644 index 0000000000..b65f8cbcf4 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/JavaLight/index.html @@ -0,0 +1,839 @@ + + + + + + + + + + + + + + + + + + + + + JavaLight - MontiCore + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + + + + + +

JavaLight

+

The JavaLight language defines a subset of the Java +programming language. The JavaLight language is dedicated +for embedding Java language elementsin arbitrary DSLs. +It is therfore defined in a compositional style, +i.e. for black box reuse (without need for copy-paste).

+

The JavaLight language introduces +* all forms of attribute declarations, +* all forms of method declarations (including constructors), +* all forms of Java expressions (including those with side effects, + such as i++, but without anonymous classes), +* almost all Java statements, with the exception of + statements for exception handling, continue- and break-statement, and synchronization, + which are omitted because there are many DSLs, where these are of no use; +* and it allows to import usable types, method, and attribute symbols, + which may be predefined or imported from of Java-like models.

+

Example

+

int age = 3+x; 
+List<Person> myParents;
+
+@Override
+public int print(String name, Set<Person> p) {
+  int a = 2 + name.length();
+  if(a < p.size()) {
+    System.out.println("Hello " + name);
+  }
+  return a;
+}
+
+The example shows a Java method with one parameter and three statements. +Expressions are supported in all their forms.

+

Grammar

+
    +
  • The main grammar file is de.monticore.JavaLight. + It deals with the definition of the method and constructor signatures, + and the annotations, while it reuses MontiCore's library components + AssignmentExpressions, JavaClassExpressions, MCCommonStatements, + and MCArrayStatements for expressions and statements.
  • +
+

Extension of JavaLight

+

JavaLight is designed for easy black-box reuse.
+It can be extended by domain specific constructs, such as + 1. special statements for message processing (c?x; c!3+x), + 2. statements for testing such as Hoare-like asserts or pre/postconditions, or + 3. additional logical or otherwise interesting expression operators + (forall x in Set:). + 4. The omitted Java-special statements, such as eception handling, can also + be addded through a predefined language library component.

+

JavaLight is fully compatible with various expression language components +that MontiCore's library provides. These extensions can +simply be added by MontiCore's language composition mechanism +(see Handbook).

+

Embedding of JavaLight Expression or Statement

+

JavaLight's expressions can be embedded as expression sublanguage in any +other interesting domain specific language, such as Automata, Activity +Diagrams, etc. (even MontiCore's primary language uses this). +The statements can also be embedded as sublanguage e.g. as actions in +StateCharts.

+

JavaLight is a strict subset of the Java programming language and +thus can be mapped directly to itself when generating code for Java.

+

Parser

+
    +
  • JavaLight is a component grammar. To retrieve a parser it is to be + embedded into a full grammar.
  • +
+

Symboltable and Symbol classes

+
    +
  • JavaLight introduces the JavaMethodSymbol extending the existing MethodSymbol + for general object-oriented types. + The JavaMethodSymbol class carries the additional attributes:
  • +
  • annotations,
  • +
  • exceptions,
  • +
  • and Booleans for isEllipsisParameterMethod, isFinal, isAbstract, + isSynchronized, isNative, and isStrictfp.
  • +
+

Symbols (imported and exported)

+
    +
  • Import: the following symbols can be imported from outside, when the symbol table + in the embedding language provides these symbols:
  • +
  • VariableSymbol for attributes and otherwise accessible variables.
  • +
  • MethodSymbol for method and constructor calls (this includes also JavaMethodSymbol).
  • +
  • TypeSymbols for using types, e.g., defined via classes, interfaces, and enums.
  • +
  • Symbol definition and export: It is possible to define new symbols, for attributes, + methods, and constructors. The provided symbol table will include them as
  • +
  • VariableSymbol for attributes
  • +
  • MethodSymbol for methods and constructors + and thus will make the accessibility of these symbols available outside the JavaLight + sub-models.
  • +
+

Functionality

+

As usual functionality is implemented in a compositional form, +which means that in a composed language these functions should be +largely reusable as pre-compiled black-boxes.

+

Context Conditions

+

JavaLight defines the following CoCos: +* ConstructorFormalParametersDifferentName
+checks if all input parameters of a constructor have distinct names. +* ConstructorModifiersValid
+checks that a constructor is not marked as abstract, final, static, or native. +* ConstructorNoAccessModifierPair
+checks that no duplicate visibility occurs for a constructor. +* ConstructorNoDuplicateModifier
+checks that no duplicate modifier occurs for a constructor. +* MethodAbstractAndOtherModifiers
+checks that an abstract method is not marked as private, static, final, native, or synchronized. +* MethodBodyAbsence
+ensures that a method body may only be absent for abstract or native methods. +* MethodBodyPresence
+checks that a method with a present method body is neither abstract nor native. +* MethodExceptionThrows
+ensures that thrown exceptions are of type java.lang.Throwable. +* MethodFormalParametersDifferentName
+checks if all input parameters of a method have distinct names. +* MethodNoDuplicateModifier
+checks that no duplicate modifier occurs for a method. +* MethodNoNativeAndStrictfp
+checks that method is not marked as native and strictfp.

+

The CoCos of embedded languages, such as statements and expressions are defined there and reused as black-boxes.

+

PrettyPrinter

+
    +
  • +

    The basic pretty printer for JavaLight is de.monticore.prettyprint.JavaLightPrettyPrinter

    +
  • +
  • +

    When the expression language is used as high-level language, it might make sense to map attribute + access to get-functions respectively also use set-functions for modification. + This can be done using a more elaborated pretty printer.

    +
  • +
+

Further Information

+ + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/monticore-grammar/src/main/grammars/de/monticore/MCBasics.mc4 b/monticore-grammar/src/main/grammars/de/monticore/MCBasics.mc4 new file mode 100644 index 0000000000..79833eb684 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/MCBasics.mc4 @@ -0,0 +1,52 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore; + +/* This is a MontiCore stable grammar. + * Adaptations -- if any -- are conservative. */ + +/** + * This grammar defines absolute basics, such as spaces, + * Java-like comments and Names. + * It should be useful in many languages. + * + */ + +component grammar MCBasics { + + /** Name represents an ordinary (unqualified) name. + This nonterminal is used as core reference mechanism e.g. + for symbols. + */ + token Name = + ( 'a'..'z' | 'A'..'Z' | '_' | '$' ) + ( 'a'..'z' | 'A'..'Z' | '_' | '0'..'9' | '$' )*; + + /*=================================================================*/ + + fragment token NEWLINE = + ('\r' '\n' | '\r' | '\n' ): ; + + /** White Spaces. + They are not added to the parsing token list, but omitted right away. + */ + token WS = + (' ' | '\t' | '\r' | '\n' ) : ->skip; + + /*=================================================================*/ + + /** A single line comment in Java style: // + Comments are ignored by the parser. + */ + token SL_COMMENT = + "//" (~('\n' | '\r' ))* : ->skip + {storeComment();}; + + /** A multi line comment in Java style. + The comments are not nested. + Comments are ignored by the parser. + */ + token ML_COMMENT = + "/*" .*? "*/" : -> skip + {storeComment();}; + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/MCBasics.mlc b/monticore-grammar/src/main/grammars/de/monticore/MCBasics.mlc new file mode 100644 index 0000000000..0140e47b66 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/MCBasics.mlc @@ -0,0 +1,49 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore; + +/** + * Language component of the MCBasics grammar. This language component + * promotes the usage of the MontiCore RTE, the JDK, etc. Hence, language + * components using this MCBasics language component do not need to allow + * their usage again. + */ +mlc MCBasics { + + // export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/MCBasics.mc4"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/mcbasics/**.java"; + } + + // export the prettyprinter + export "$projectDir/src/main/java" { + include "de/monticore/prettyprint/MCBasics*.java"; + } + + // promote using the JDK except for reflection + promote { + include "$mp/java/**"; + exclude "$mp/java/lang/reflect/**"; + } + + // promote using everything from MontiCore RTE and co. + promote { + include "$mp/de/monticore/antlr4/**"; + include "$mp/de/monticore/ast/**"; + include "$mp/de/monticore/generating/**"; + include "$mp/de/monticore/io/**"; + include "$mp/de/monticore/parser/**"; + include "$mp/de/monticore/prettyprint/**"; + include "$mp/de/monticore/symboltable/**"; + include "$mp/de/monticore/utils/**"; + include "$mp/de/monticore/visitor/**"; + include "$mp/de/se_rwth/commons/**"; + include "$mp/org/antlr/v4/runtime/**"; + include "$mp/com/google/common/collect/**"; + } + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/MCCommon.mc4 b/monticore-grammar/src/main/grammars/de/monticore/MCCommon.mc4 new file mode 100644 index 0000000000..3da378d423 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/MCCommon.mc4 @@ -0,0 +1,21 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore; + +/* This is a MontiCore stable grammar. + * Adaptations -- if any -- are conservative. */ + +import de.monticore.*; + +/** + * This grammar composes typical UML like grammar components. +*/ + +/** + * Grammar for common elements of the UML/P-Language-Family + */ +component grammar MCCommon + extends Cardinality, + Completeness, + UMLModifier, + UMLStereotype { +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/MCCommon.mlc b/monticore-grammar/src/main/grammars/de/monticore/MCCommon.mlc new file mode 100644 index 0000000000..e35ba4321e --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/MCCommon.mlc @@ -0,0 +1,23 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore; + +mlc MCCommon { + + // export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/MCCommon.mc4"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/mccommon/**.java"; + } + + promote { + mlc "de.monticore.Cardinality"; + mlc "de.monticore.Completeness"; + mlc "de.monticore.UMLModifier"; + mlc "de.monticore.UMLStereotype"; + } + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/UMLModifier.mc4 b/monticore-grammar/src/main/grammars/de/monticore/UMLModifier.mc4 new file mode 100644 index 0000000000..7c9c459ec4 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/UMLModifier.mc4 @@ -0,0 +1,57 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore; + +/* This is a MontiCore stable grammar. + * Adaptations -- if any -- are conservative. */ + +import de.monticore.*; + +/** + * Grammar for common elements of the UML/P-Language-Family + * + * The grammar contains the modifiers that UML provides. + * This includes "public" "private", "protected", "final", "abstract", "local", + * "derived", "readonly", and "static", but also the + * compact syntactic versions "+", "#", "-", "/" and "?" (for readonly). + * + * UML modifiers are not identical to Java modifiers (e.g. "native" or + * "threadsafe" are missing.) +*/ + +component grammar UMLModifier extends UMLStereotype { + + /** ASTModifier represents a Modifier for Classes, Interfaces, Methods, + Constructors and Attributes in the UML/P + @attribute stereotype Optional Stereotype + @attribute public true if Modifier is public + (i.e. Modifier written as "public" or "+") + @attribute private true if Modifier is private + (i.e. Modifier written as "private" or "-") + @attribute protected true if Modifier is protected + (i.e. Modifier written as "protected" or "#") + @attribute final true if Modifier is final + (i.e. Modifier written as "final") + @attribute abstract true if Modifier is abstract + (i.e. Modifier written as "abstract") + @attribute local true if Modifier is local + (i.e. Modifier written as "local") + @attribute derived true if Modifier is derived + (i.e. Modifier written as "derived" or "/") + @attribute readonly true if Modifier is readonly + (i.e. Modifier written as "readonly" or "?") + @attribute static true if Modifier is static + (i.e. Modifier written as "static") + */ + Modifier = + Stereotype? + ( ["public"] | [public:"+"] + | ["private"] | [private:"-"] + | ["protected"] | [protected:"#"] + | ["final"] + | ["abstract"] + | ["local"] + | ["derived"] | [derived:"/"] + | ["readonly"] | [readonly:"?"] + | ["static"] + )*; +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/UMLModifier.mlc b/monticore-grammar/src/main/grammars/de/monticore/UMLModifier.mlc new file mode 100644 index 0000000000..2055b14722 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/UMLModifier.mlc @@ -0,0 +1,26 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore; + +mlc UMLModifier { + + // export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/UMLModifier.mc4"; + } + + //export handwritten code + export "$projectDir/src/main/java" { + include "de/monticore/umlmodifier/**.java"; + include "de/monticore/prettyprint/UMLModifier*.java"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/umlmodifier/**.java"; + } + + promote { + mlc "de.monticore.UMLStereotype"; + } + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/UMLStereotype.mc4 b/monticore-grammar/src/main/grammars/de/monticore/UMLStereotype.mc4 new file mode 100644 index 0000000000..9482985d30 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/UMLStereotype.mc4 @@ -0,0 +1,93 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore; + +/* This is a MontiCore stable grammar. + * Adaptations -- if any -- are conservative. */ + +import de.monticore.literals.*; + +/** + * Grammar for common elements of the UML/P-Language-Family + * + * This grammars defines Stereotypes like + * <> + * Values may only be of type String. + * The real value unfortunately in UML is only encoded as String. + * + * We therefore suggest not to use this form of stereotypes, but use a + * tagging infrastructure that even allows to type the possible forms + * of tags. + * + * Methods contains(name), getValue(name) assist Stereotype retrieval. + * + */ +component grammar UMLStereotype extends MCCommonLiterals { + + /*========================================================================*/ + /*============================== GRAMMAR =================================*/ + /*========================================================================*/ + + /** ASTStereotype represents Stereotypes in the UML/P + @attribute values List of Values of this Stereotype + */ + Stereotype = + "<<" values:(StereoValue || ",")+ ">>" ; + + // Due to possible scanner clashes with "List>" + // we split the token: + splittoken ">>"; + + /** ASTStereoValue represents a Value of a Stereotype in the UML/P + @attribute name Name of the Stereotype-Value + @attribute text Content of the Stereotype (String including '"'; + use getValue() for decoded String) + */ + StereoValue = + Name& ("=" text:StringLiteral)?; + + /*========================================================================*/ + /*======================= AST extension ================================*/ + /*========================================================================*/ + + astrule Stereotype = + method public boolean contains(String name) { + for (ASTStereoValue sv : values) { + if (sv.getName().equals(name)) { + return true; + } + } + return false; + } + method public boolean contains(String name, String value) { + for (ASTStereoValue sv : values) { + if (sv.getName().equals(name)) { + return sv.getValue().equals(value); + } + } + return false; + } + method public String getValue(String name) { + for (ASTStereoValue sv : values) { + if (sv.getName().equals(name)) { + return sv.getValue(); + } + } + throw new java.util.NoSuchElementException(); + }; + + astrule StereoValue = + // Caution: decoded value is in cache: so change of value leads + // to outdated result in getValue() + content:String + method public String getValue() { + if(content == null) { + if (text.isPresent()) { + content = de.monticore.literals.MCLiteralsDecoder.decodeString( + text.get().getValue()); + } else { + content = ""; // absent value + } + } + return content; + }; +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/UMLStereotype.mlc b/monticore-grammar/src/main/grammars/de/monticore/UMLStereotype.mlc new file mode 100644 index 0000000000..3ed7520dd4 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/UMLStereotype.mlc @@ -0,0 +1,25 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore; + +mlc UMLStereotype { + + // export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/UMLStereotype.mc4"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/umlstereotype/**.java"; + } + + // export handwritten code + export "$projectDir/src/main/java" { + include "de/monticore/umlstereotype/**.java"; + include "de/monticore/prettyprint/UMLStereotype*.java"; + } + + promote { + mlc "de.monticore.literals.MCCommonLiterals"; + } +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/expressions/AssignmentExpressions.mc4 b/monticore-grammar/src/main/grammars/de/monticore/expressions/AssignmentExpressions.mc4 new file mode 100644 index 0000000000..b9190d952e --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/expressions/AssignmentExpressions.mc4 @@ -0,0 +1,58 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions; + +/* This is a MontiCore stable grammar. + * Adaptations -- if any -- are conservative. */ + +import de.monticore.expressions.*; + +/** + * This grammar defines all Java expressions that have side effects, + * such as assignment expressions like =, +=, etc. and suffix and prefix + * expressions like ++, --, etc. + * + * There are also other kinds of expressions defined in the + * grammars mentioned below. These expression definitions can savely be + * composed if desired. + * + * This grammar is part of a hierarchy of expressions, namely + * * expressions/ExpressionsBasis.mc4 + * * -- expressions/CommonExpressions.mc4 + * * -- -- expressions/JavaClassExpressions.mc4 + * * -- -- expressions/OptionalExpressions.mc4 + * * -- expressions/AssignmentExpressions.mc4 + * * -- expressions/BitExpressions.mc4 + * * -- expressions/LambdaExpressions.mc4 + * * -- expressions/OCLExpressions.mc4 + * * -- expressions/SetExpressions.mc4 + * +*/ + +component grammar AssignmentExpressions + extends ExpressionsBasis { + + /*=================================================================*/ + + IncSuffixExpression implements Expression <220> = + Expression "++"; + + DecSuffixExpression implements Expression <220> = + Expression "--"; + + /*=================================================================*/ + + IncPrefixExpression implements Expression <210> = + "++" Expression; + + DecPrefixExpression implements Expression <210> = + "--" Expression; + + /*=================================================================*/ + + AssignmentExpression implements Expression <60> = + left:Expression + operator: [ "=" | "+=" | "-=" | "*=" | "/=" | "&=" | "|=" + | "^=" | ">>=" | ">>>=" | "<<=" | "%=" ] + right:Expression; + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/expressions/AssignmentExpressions.mlc b/monticore-grammar/src/main/grammars/de/monticore/expressions/AssignmentExpressions.mlc new file mode 100644 index 0000000000..13e88abf0e --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/expressions/AssignmentExpressions.mlc @@ -0,0 +1,25 @@ +package de.monticore.expressions; + +mlc AssignmentExpressions { + + //export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/expressions/AssignmentExpressions.mc4"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/expressions/assignmentexpressions/**.java"; + } + + // export the prettyprinter + export "$projectDir/src/main/java" { + include "de/monticore/expressions/prettyprint/AssignmentExpressions*.java"; + include "de/monticore/expressions/exptojava/AssignmentExpressions*.java"; + include "de/monticore/types/check/DeriveSymTypeOfAssignmentExpressions.java"; + } + + promote { + mlc "de.monticore.expressions.ExpressionsBasis"; + } +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/expressions/BitExpressions.mc4 b/monticore-grammar/src/main/grammars/de/monticore/expressions/BitExpressions.mc4 new file mode 100644 index 0000000000..ef467c20cf --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/expressions/BitExpressions.mc4 @@ -0,0 +1,82 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions; + +/* This is a MontiCore stable grammar. + * Adaptations -- if any -- are conservative. */ + +import de.monticore.expressions.*; + +/** + * This grammar defines a typical standard set of operations for + * expressions. This is a subset of Java for binary expressions + * like <<, >>, >>>, &, ^ and | + * + * There are also other kinds of expressions defined in the + * grammars mentioned below. These expression definitions can savely be + * composed if desired. + * + * This grammar is part of a hierarchy of expressions, namely + * * expressions/ExpressionsBasis.mc4 + * * -- expressions/CommonExpressions.mc4 + * * -- -- expressions/JavaClassExpressions.mc4 + * * -- -- expressions/OptionalExpressions.mc4 + * * -- expressions/AssignmentExpressions.mc4 + * * -- expressions/BitExpressions.mc4 + * * -- expressions/LambdaExpressions.mc4 + * * -- expressions/OCLExpressions.mc4 + * * -- expressions/SetExpressions.mc4 + * +*/ + +component grammar BitExpressions + extends ExpressionsBasis { + + /*=================================================================*/ + + interface ShiftExpression = + left:Expression shiftOp:"" right:Expression; + + /* The priorities (160, 120 and 110) are compatible with the priorities + * of the other infix operations e.g. in CommonExpressions + * leading to Java' like priorization. + */ + + /*=================================================================*/ + + /* Due to possible scanner clashes with "List>" + we split the tokens: + */ + splittoken ">>", ">>>"; + + LeftShiftExpression implements Expression <160>, ShiftExpression = + left:Expression + shiftOp:"<<" + right:Expression; + + RightShiftExpression implements Expression <160>, ShiftExpression = + left:Expression + shiftOp:">>" + right:Expression; + + LogicalRightShiftExpression implements Expression <160>, + ShiftExpression = + left:Expression + shiftOp:">>>" + right:Expression; + + + /*=================================================================*/ + + interface BinaryExpression = + left:Expression operator:"" right:Expression; + + BinaryAndExpression implements BinaryExpression, Expression <120> = + left:Expression operator:"&" right:Expression; + + BinaryXorExpression implements BinaryExpression, Expression <110> = + left:Expression operator:"^" right:Expression; + + BinaryOrOpExpression implements BinaryExpression, Expression <100> = + left:Expression operator:"|" right:Expression; + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/expressions/BitExpressions.mlc b/monticore-grammar/src/main/grammars/de/monticore/expressions/BitExpressions.mlc new file mode 100644 index 0000000000..92ee962555 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/expressions/BitExpressions.mlc @@ -0,0 +1,25 @@ +package de.monticore.expressions; + +mlc BitExpressions { + + //export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/expressions/BitExpressions.mc4"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/expressions/bitexpressions/**.java"; + } + + // export the prettyprinter + export "$projectDir/src/main/java" { + include "de/monticore/expressions/prettyprint/BitExpressions*.java"; + include "de/monticore/expressions/exptojava/BitExpressions*.java"; + include "de/monticore/types/check/DeriveSymTypeOfBitExpressions.java"; + } + + promote { + mlc "de.monticore.expressions.ExpressionsBasis"; + } +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/expressions/CommonExpressions.mc4 b/monticore-grammar/src/main/grammars/de/monticore/expressions/CommonExpressions.mc4 new file mode 100644 index 0000000000..95d5cc9c70 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/expressions/CommonExpressions.mc4 @@ -0,0 +1,145 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions; + +/* This is a MontiCore stable grammar. + * Adaptations -- if any -- are conservative. */ + +import de.monticore.expressions.*; + +/** + * This grammar defines a typical standard set of operations for + * expressions. This is a subset of Java as well as OCL/P, + * mainly for arithmetic, comparisons, variable use (v), + * attribute use (o.att), method call (foo(arg,arg2)) and brackets (exp). + * + * There are also other kinds of expressions defined in the + * grammars mentioned below. These expression definitions can savely be + * composed if desired. + * + * The language developer may choose the subset of expressions + * of interest and combine these with the appropriate typing infrastructure + * + * This modularity of expressions and associated types greatly eases + * the reuse of type structures in languages similar to Java + * + * This grammar is part of a hierarchy of expressions, namely + * * expressions/ExpressionsBasis.mc4 + * * -- expressions/CommonExpressions.mc4 + * * -- -- expressions/JavaClassExpressions.mc4 + * * -- -- expressions/OptionalExpressions.mc4 + * * -- expressions/AssignmentExpressions.mc4 + * * -- expressions/BitExpressions.mc4 + * * -- expressions/LambdaExpressions.mc4 + * * -- expressions/OCLExpressions.mc4 + * * -- expressions/SetExpressions.mc4 + * +*/ + +component grammar CommonExpressions + extends ExpressionsBasis { + + /*=================================================================*/ + + /** InfixExpression is the common interface for all forms of + * infixes defined there. The operator contains the concrete value, e.g. "+" + */ + interface InfixExpression = + left:Expression operator:"" right:Expression; + + + /*=================================================================*/ + /** CallExpression is a Method-Call like foo() + * Expression on the left can be an expression, like: + * a Class-Name: A.f(), variable-Name a.f(), + * BracketExpression ("foo"+"Bar").toLowerCase() + * an expression with value of type function: f()() or a[0]() + * or other Method Calls: * c.getA().getB() + */ + CallExpression implements Expression <240> = + Expression Arguments; + + /*=================================================================*/ + + /** Field access: e.g. obj.attr or also (b ? getobject() : obj).attr + * This is syntactically similar to package names: + * So when the language shall also allow QualifiedNames as Expressions, + * they might be parsed here. E.g. in "x.y.Person", "x" is + * parsed as NameExpression, even though it is a package name. + * This is to be resolved via SymbolTable. + * When resolved, the AST should be restructured directly after the parsing. + */ + FieldAccessExpression implements Expression <290> = + Expression "." Name; + + /*=================================================================*/ + + PlusPrefixExpression implements Expression <210> = + "+" Expression; + + MinusPrefixExpression implements Expression <210> = + "-" Expression; + + /*=================================================================*/ + + BooleanNotExpression implements Expression <200> = + "~" Expression; + + LogicalNotExpression implements Expression <190> = + "!" Expression; + + /*=================================================================*/ + + MultExpression implements Expression <180>, InfixExpression = + left:Expression operator:"*" right:Expression; + + DivideExpression implements Expression <180>, InfixExpression = + left:Expression operator:"/" right:Expression; + + ModuloExpression implements Expression <180>, InfixExpression = + left:Expression operator:"%" right:Expression; + + PlusExpression implements Expression <170>, InfixExpression = + left:Expression operator:"+" right:Expression; + + MinusExpression implements Expression <170>, InfixExpression = + left:Expression operator:"-" right:Expression; + + /*=================================================================*/ + + LessEqualExpression implements Expression <150>, InfixExpression = + left:Expression operator:"<=" right:Expression; + + GreaterEqualExpression implements Expression <150>, InfixExpression = + left:Expression operator:">=" right:Expression; + + LessThanExpression implements Expression <150>, InfixExpression = + left:Expression operator:"<" right:Expression; + + GreaterThanExpression implements Expression <150>, InfixExpression = + left:Expression operator:">" right:Expression; + + EqualsExpression implements Expression <130>, InfixExpression = + left:Expression operator:"==" right:Expression; + + NotEqualsExpression implements Expression <130>, InfixExpression = + left:Expression operator:"!=" right:Expression; + + /*=================================================================*/ + + BooleanAndOpExpression implements Expression <120>, InfixExpression = + left:Expression operator:"&&" right:Expression; + + BooleanOrOpExpression implements Expression <117>, InfixExpression = + left:Expression operator:"||" right:Expression; + + /*=================================================================*/ + + ConditionalExpression implements Expression <114> = + condition:Expression "?" trueExpression:Expression ":" falseExpression:Expression; + + /*=================================================================*/ + + BracketExpression implements Expression <310> + = "(" Expression ")"; + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/expressions/CommonExpressions.mlc b/monticore-grammar/src/main/grammars/de/monticore/expressions/CommonExpressions.mlc new file mode 100644 index 0000000000..ea3e69d387 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/expressions/CommonExpressions.mlc @@ -0,0 +1,27 @@ +package de.monticore.expressions; + +mlc CommonExpressions { + + //export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/expressions/CommonExpressions.mc4"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/expressions/commonexpressions/**.java"; + } + + // export the prettyprinter + export "$projectDir/src/main/java" { + include "de/monticore/expressions/commonexpressions/**.java"; + include "de/monticore/expressions/prettyprint/CommonExpressions*.java"; + include "de/monticore/expressions/exptojava/CommonExpressions*.java"; + include "de/monticore/types/check/DeriveSymTypeOfCommonExpressions.java"; + include "de/monticore/types/check/DeriveSymTypeOfBSCommonExpressions.java"; + } + + promote { + mlc "de.monticore.expressions.ExpressionsBasis"; + } +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/expressions/Expressions/index.html b/monticore-grammar/src/main/grammars/de/monticore/expressions/Expressions/index.html new file mode 100644 index 0000000000..9a8f2feeb3 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/expressions/Expressions/index.html @@ -0,0 +1,825 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Expressions - MontiCore + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + + + + + +

MontiCore - Expression-Language Modules

+

MC Expressions can be used to formulate mathematical and programmatic +expressions from a set of literals. MC Expressions are based on a system of +modular and pluggable grammar parts.

+

Given Expression languages in MontiCore

+

Currently, MontiCore comprises the following expression languages:

+ +

The OCL project defines additional expression languages:

+ +

Furthermore, composite SI unit expressions are defined in the SI Units project:

+
    +
  • SI Units (can be used to parse primitive units as well as their products, quotients, and powers)
  • +
+

Using Expressions

+

To use one or more of the existing expression languages in your MontiCore-based +language its grammar needs to extend those expression languages.

+

Creating your own Expression language

+

There are some expressions you need desperately and that are not covered +by the existing expression languages?
+In this case, you can create a new grammar that extends at least +ExpressionsBasis. In the extending grammar, you are now free to add your own +expressions which however must implement the Expression interface from +ExpressionsBasis grammar. To then include the new expressions in a language +let it extend the corresponding grammar. +See here +for an example.

+

Further Information

+ + + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/monticore-grammar/src/main/grammars/de/monticore/expressions/ExpressionsBasis.mc4 b/monticore-grammar/src/main/grammars/de/monticore/expressions/ExpressionsBasis.mc4 new file mode 100644 index 0000000000..ce4b714d92 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/expressions/ExpressionsBasis.mc4 @@ -0,0 +1,72 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions; + +/* This is a MontiCore stable grammar. + * Adaptations -- if any -- are conservative. */ + +import de.monticore.literals.*; +import de.monticore.types.*; +import de.monticore.*; + +/** + * This grammar defines core interfaces for expressions and the + * kinds of symbols necessary. + * A hierarchy of conservative extensions to this grammar realize + * these interfaces in various forms. + * + * The language developer may choose the subset of expressions + * of interest and combine these with the appropriate typing infrastructure + * + * This modularity of expressions and associated types greatly eases + * the reuse of type structures in languages similar to Java + * + * + * This grammar is part of a hierarchy of expressions, namely + * * expressions/ExpressionsBasis.mc4 + * * -- expressions/CommonExpressions.mc4 + * * -- -- expressions/JavaClassExpressions.mc4 + * * -- -- expressions/OptionalExpressions.mc4 + * * -- expressions/AssignmentExpressions.mc4 + * * -- expressions/BitExpressions.mc4 + * * -- expressions/LambdaExpressions.mc4 + * * -- expressions/OCLExpressions.mc4 + * * -- expressions/SetExpressions.mc4 + * +*/ + +component grammar ExpressionsBasis + extends MCBasics, MCLiteralsBasis { + + /*=================================================================*/ + + /* Top level interface for all kinds of expressions + */ + interface Expression; + + + /*=================================================================*/ + + + /** + * Various Forms of Names are allowed + * (we allow among others variable names, attribute names, rolenames + * and OCL e.g. also class names. These different forms of names + * prevent to use Name@SymbolForm) + */ + NameExpression implements Expression <350> + = Name; + + /** + * Atomic Elements of Expressions: the Literals + */ + LiteralExpression implements Expression <340> + = Literal; + + /** + * A list of expressions + */ + Arguments + = "(" (Expression || ",")* ")"; + + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/expressions/ExpressionsBasis.mlc b/monticore-grammar/src/main/grammars/de/monticore/expressions/ExpressionsBasis.mlc new file mode 100644 index 0000000000..bbbe96b0d0 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/expressions/ExpressionsBasis.mlc @@ -0,0 +1,26 @@ +package de.monticore.expressions; + +mlc ExpressionsBasis { + + //export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/expressions/ExpressionsBasis.mc4"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/expressions/expressionsbasis/**.java"; + } + + // export the prettyprinter + export "$projectDir/src/main/java" { + include "de/monticore/expressions/prettyprint/ExpressionsBasis*.java"; + include "de/monticore/expressions/exptojava/ExpressionsBasis*.java"; + include "de/monticore/types/check/DeriveSymTypeOfExpression.java"; + } + + promote { + mlc "de.monticore.MCBasics"; + mlc "de.monticore.literals.MCLiteralsBasis"; + } +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/expressions/JavaClassExpressions.mc4 b/monticore-grammar/src/main/grammars/de/monticore/expressions/JavaClassExpressions.mc4 new file mode 100644 index 0000000000..882c9a5031 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/expressions/JavaClassExpressions.mc4 @@ -0,0 +1,131 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions; + +/* This is a MontiCore stable grammar. + * Adaptations -- if any -- are conservative. */ + +import de.monticore.expressions.*; +import de.monticore.statements.*; + +/** + * This grammar defines Java specific class expressions like super, + * this, type cast, etc. + * + * This grammar should only be included, when a mapping to Java is + * intended and the full power of Java should be available in the + * modelling language. + * + * There are also other kinds of expressions defined in the + * grammars mentioned below. These expression definitions can safely be + * composed if desired. + * + * The grammar has several extension points for types to be filled when used. + * + * This grammar is part of a hierarchy of expressions, namely + * * expressions/ExpressionsBasis.mc4 + * * -- expressions/CommonExpressions.mc4 + * * -- -- expressions/JavaClassExpressions.mc4 + * * -- -- expressions/OptionalExpressions.mc4 + * * -- expressions/AssignmentExpressions.mc4 + * * -- expressions/BitExpressions.mc4 + * * -- expressions/LambdaExpressions.mc4 + * * -- expressions/OCLExpressions.mc4 + * * -- expressions/SetExpressions.mc4 + * +*/ + +component grammar JavaClassExpressions + extends CommonExpressions, MCVarDeclarationStatements { + + + /*=================================================================*/ + + /* + * The following are several extension points that allow to fill in the + * the desired type system: + * e.g. the complete type system of java (extending MCFullGenericType) + * or any smaller variant + */ + + // Types + external ExtType; + + // Types including a void return type + external ExtReturnType; + + // Type arguments + external ExtTypeArgument; + + /*=================================================================*/ + + PrimaryThisExpression implements Expression <320> = + "this"; + + ThisExpression implements Expression <280> = + Expression "." "this"; + + ArrayExpression implements Expression <250> = + Expression "[" indexExpression:Expression "]"; + + SuperExpression implements Expression <270> = + Expression "." "super" SuperSuffix; + + GenericInvocationExpression implements Expression <260> = + Expression "." PrimaryGenericInvocationExpression; + + // casting expression uses a type + TypeCastExpression implements Expression <230> = + "(" ExtType ")" Expression; + + PrimarySuperExpression implements Expression <330> = + "super"; + + CreatorExpression implements Expression <235> = + "new" Creator; + + interface Creator ; + + AnonymousClass implements Creator + = ExtType Arguments; + + ArrayCreator implements Creator + = ExtType ArrayDimensionSpecifier; + + interface ArrayDimensionSpecifier ; + + ArrayDimensionByExpression implements ArrayDimensionSpecifier + = ("[" Expression "]")+ (dim:"[" "]")* + ; + + /*=================================================================*/ + + // access of class object uses a type + ClassExpression implements Expression <360> = + ExtReturnType "." "class"; + + // generic invocation may specify type arguments + PrimaryGenericInvocationExpression implements Expression <370> = + "<"(ExtTypeArgument||",")+">" GenericInvocationSuffix; + + GenericInvocationSuffix + = ["super"] SuperSuffix + | ["this"] Arguments + | Name Arguments + ; + + SuperSuffix + = Arguments + | "." ("<"(ExtTypeArgument||",")+">")? Name Arguments? + ; + + /*=================================================================*/ + + // instance of needs a type or pattern as argument + InstanceofExpression implements Expression <140> = + Expression "instanceof" (ExtType | Pattern); + + interface Pattern; + + TypePattern implements Pattern = LocalVariableDeclaration; + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/expressions/JavaClassExpressions.mlc b/monticore-grammar/src/main/grammars/de/monticore/expressions/JavaClassExpressions.mlc new file mode 100644 index 0000000000..fe140f641d --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/expressions/JavaClassExpressions.mlc @@ -0,0 +1,25 @@ +package de.monticore.expressions; + +mlc JavaClassExpressions { + + //export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/expressions/JavaClassExpressions.mc4"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/expressions/javaclassexpressions/**.java"; + } + + // export the prettyprinter + export "$projectDir/src/main/java" { + include "de/monticore/expressions/prettyprint/JavaClassExpressions*.java"; + include "de/monticore/expressions/exptojava/JavaClassExpressions*.java"; + include "de/monticore/types/check/DeriveSymTypeOfJavaClassExpressions.java"; + } + + promote { + mlc "de.monticore.expressions.CommonExpressions"; + } +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/expressions/LambdaExpressions.mc4 b/monticore-grammar/src/main/grammars/de/monticore/expressions/LambdaExpressions.mc4 new file mode 100644 index 0000000000..90fec90692 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/expressions/LambdaExpressions.mc4 @@ -0,0 +1,72 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.expressions; + +/* Beta-version: This is intended to become a MontiCore stable grammar. */ + +import de.monticore.expressions.*; +import de.monticore.symbols.*; +import de.monticore.types.*; + +component grammar LambdaExpressions + extends BasicSymbols, + MCBasicTypes, + ExpressionsBasis { + + /** + * This grammar defines lambda expressions, e.g. + * (int x, int y) -> x * y, () -> 5, x -> x * 2 + * + * Not part of this grammar are Java-like lambdas with codeblocks, e.g. + * (int x) -> {int y = x + 2; return y;} + * + * The grammar has the extension point LambdaBody which can be used to allow different kinds of lambda bodies, e.g. + * codeblocks. + * + * There are also other kinds of expressions defined in the + * grammars mentioned below. These expression definitions can safely be + * composed if desired. + * + * This grammar is part of a hierarchy of expressions, namely + * * expressions/ExpressionsBasis.mc4 + * * -- expressions/CommonExpressions.mc4 + * * -- -- expressions/JavaClassExpressions.mc4 + * * -- -- expressions/OptionalExpressions.mc4 + * * -- expressions/AssignmentExpressions.mc4 + * * -- expressions/BitExpressions.mc4 + * * -- expressions/LambdaExpressions.mc4 + * * -- expressions/OCLExpressions.mc4 + * * -- expressions/SetExpressions.mc4 + * + */ + + /*=================================================================*/ + + /** + * LambdaParameters have optional explicit typing. + * However, not setting the type explicitly requires + * some sort of type inference to be present. + */ + LambdaParameter implements Variable = MCType? Name; + + LambdaParameters = + LambdaParameter + | parenthesis:"(" (LambdaParameter || ",")* ")" + ; + + /** + * Extension point that can be used to allow different bodies for lambda statements, e.g codeblocks. + */ + interface LambdaBody; + + astrule LambdaBody = + type: de.monticore.types.check.SymTypeExpression ; + + LambdaExpressionBody implements LambdaBody = Expression; + + scope (shadowing non_exporting ordered) LambdaExpression implements Expression <50> = + + LambdaParameters "->" + LambdaBody + ; + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/expressions/LambdaExpressions.mlc b/monticore-grammar/src/main/grammars/de/monticore/expressions/LambdaExpressions.mlc new file mode 100644 index 0000000000..8d949ffd79 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/expressions/LambdaExpressions.mlc @@ -0,0 +1,28 @@ +package de.monticore.expressions; + +mlc LambdaExpressions { + + //export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/expressions/LambdaExpressions.mc4"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/expressions/lambdaexpressions/**.java"; + } + + // export the prettyprinter + export "$projectDir/src/main/java" { + include "de/monticore/expressions/lambdaexpressions/**.java"; + include "de/monticore/expressions/prettyprint/LambdaExpressions*.java"; + include "de/monticore/expressions/exptojava/LambdaExpressions*.java"; + include "de/monticore/types/check/DeriveSymTypeOfLambdaExpressions.java"; + } + + promote { + mlc "de.monticore.symbols.BasicSymbols"; + mlc "de.monticore.types.MCBasicTypes"; + mlc "de.monticore.expressions.ExpressionsBasis"; + } +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/expressions/StreamExpressions.mc4 b/monticore-grammar/src/main/grammars/de/monticore/expressions/StreamExpressions.mc4 new file mode 100644 index 0000000000..c0239cea12 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/expressions/StreamExpressions.mc4 @@ -0,0 +1,76 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions; + +/* Alpha-version: This is intended to become a MontiCore stable grammar. */ + +import de.monticore.expressions.ExpressionsBasis; + +/** + * This grammar extends the available expressions with + * special operations for Streams as initially defined by + * Broy/Stoelen in [BS01] using the Syntax from + * Ringert/Rumpe [RR11] and + * J. Bürger, H. Kausch, D. Raco, J. Ringert, B. Rumpe, + * S. Stüber, M. Wiartalla [BKR+20] + * (see https://se-rwth.github.io/publications/) + * + * Stream expressions are partly + * 1) extending the Expression syntax by grammatical operations (see below), + * 2) are provided as library of available Stream operations, and + * 3) are based on the type constructor Stream (defined in Types) + * + * The language developer may choose the subset of expressions + * of interest and combine these with the appropriate typing infrastructure + * + * This modularity of expressions and associated types greatly eases + * the reuse of type structures in languages similar to Java + * + * This grammar is part of a hierarchy of expressions, namely + * * expressions/ExpressionsBasis.mc4 + * * -- expressions/CommonExpressions.mc4 + * * -- -- expressions/JavaClassExpressions.mc4 + * * -- -- expressions/OptionalExpressions.mc4 + * * -- expressions/AssignmentExpressions.mc4 + * * -- expressions/BitExpressions.mc4 + * * -- expressions/LambdaExpressions.mc4 + * * -- expressions/OCLExpressions.mc4 + * * -- expressions/SetExpressions.mc4 + * * -- expressions/StreamExpressions.mc4 + * +*/ + +component grammar StreamExpressions extends CommonExpressions { + + /*=================================================================*/ + + /* ------ Special Syntax for Functions on Untimed Streams -------- */ + + /* + * Empty Stream constant + */ + EmptyStreamExpression implements Expression = "<>"; + + /* + * Append first element + * a:b:c is equal to a:(b:c), hence + * a:b^^c is equal to a:(b^^c) + * a+b:c is equal to (a+b):c, and + * a <= b:c is equal to a <= (b:c), hence the priority 154, 156 + */ + AppendStreamExpression implements Expression <154>, InfixExpression = + left:Expression operator:":" right:Expression; + + /* + * Concatenation of streams + * a^^b^^c is equal to a^^(b^^c), hence + */ + ConcatStreamExpression implements Expression <156>, InfixExpression = + left:Expression operator:"^^" right:Expression; + + /* + * Length of stream + */ + LengthStreamExpression implements Expression <185> + = "#" Expression; + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/expressions/StreamExpressions.mlc b/monticore-grammar/src/main/grammars/de/monticore/expressions/StreamExpressions.mlc new file mode 100644 index 0000000000..b20b2d2610 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/expressions/StreamExpressions.mlc @@ -0,0 +1,23 @@ +package de.monticore.expressions; + +mlc StreamExpressions { + + //export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/expressions/StreamExpressions.mc4"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/expressions/streamexpressions/**.java"; + } + + // export the prettyprinter + export "$projectDir/src/main/java" { + include "de/monticore/expressions/prettyprint/StreamExpressions*.java"; + } + + promote { + mlc "de.monticore.expressions.CommonExpressions"; + } +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/grammar/Grammar.mc4 b/monticore-grammar/src/main/grammars/de/monticore/grammar/Grammar.mc4 new file mode 100644 index 0000000000..8a5c785519 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/grammar/Grammar.mc4 @@ -0,0 +1,687 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar; + +/** + The grammar describes the MontiCore grammar in its own format +*/ +component grammar Grammar extends de.monticore.literals.MCCommonLiterals, + de.monticore.types.MCSimpleGenericTypes { + + /** + Used for embedding actions + */ + external Action; + + /** + Used as Expression in syntactic predicates + */ + external ExpressionPredicate; + + /** + Used for embedding concepts + */ + external MCConcept; + + /** + A MontiCore grammar describes a context free grammar in an own format + @attribute name The name of this grammar + @attribute supergrammar Supergrammars of this grammar + @attribute GrammarOption Options for this grammar + @attribute LexProd Lexical productions + @attribute ClassProd Class productions + @attribute EnumProd Enumeration productions + @attribute ExternalProd External productions + @attribute InterfaceProd List of all interface productions of this grammar + @attribute AbstractProd List of all abstract productions of this grammar + @attribute ASTRule List of all ast rules of this grammar + @attribute Concepts List of additional concepts + @attribute StartRule defines the start rule + */ + symbol scope MCGrammar = + ("package" package:(Name& || ".")+ ";")? + (importStatement:MCImportStatement)* + + GrammarAnnotation? ["component"]? key("grammar") Name + ("extends" supergrammar:(GrammarReference || ",")+ )? + "{" + ( + GrammarOption + | + LexProd + | + ClassProd + | + EnumProd + | + ExternalProd + | + InterfaceProd + | + AbstractProd + | + ASTRule + | + SymbolRule + | + ScopeRule + | + Concept + | + StartRule + | + SplitRule + | + KeywordRule + | + ReplaceRule + )* + "}" ; + + // One OptionBlock, one StartRule, one ScopeRule only + astrule MCGrammar = + grammarOption:GrammarOption max=1 + startRule:StartRule max=1 + scopeRule:ScopeRule max=1 ; + + symbolrule MCGrammar = + isComponent:boolean + splitRules:String* + noKeywords:String*; + + + /** + A GrammarReference references another grammar + @attribute name Qualified name of the other grammar + */ + GrammarReference = + (Name& || ".")+ ; + + // -------------------------------------------------------------------- + // Options + + /** + Options for a grammar + @attribute followOption Expression that shall be realized by lexer rules + @attribute genericOption Expression that shall be realized by lexer rules + */ + GrammarOption= + "options" "{" + ( + FollowOption + | + AntlrOption + | + KeywordOption + )* + "}" ; + + /** + A FollowOption specifies an additional follow set for a production + @attribute prodName name of the production + @attribute alt the follow set + */ + FollowOption = + "follow" prodName:Name Alt ";"; + + + /** + A antlr option is an option that is passed on Anltr itself + @attribute name name of the option + @attribute value optional value + */ + AntlrOption = + Name ("=" value:Name |"=" value:String)? ; + + KeywordOption = + ["allkeywords"] | + "keywords" Name+ ";"; + + /** + The start rule should be the rule defining the whole model. + If there is no start rule the first rule of the grammar is + used as start rule. + @attribute ruleReference name of the start rule + */ + StartRule = + "start" Name ";"; + + GrammarAnnotation = + {noSpace(2)}? "@" ([key("Deprecated")] ( "(" message:String ")" )? + | [key("Override")] + | [key("NonConservative")]); + + // -------------------------------------------------------------------- + // Productions + + /** + Productions form the syntactical structure of the grammar + */ + interface symbol scope Prod = GrammarAnnotation* Name; + + symbolrule Prod = + isStartProd:boolean + isInterface:boolean + isAbstract:boolean + isExternal:boolean + isEnum:boolean + isLexerProd:boolean + isScopeSpanning:boolean + isSymbolDefinition:boolean + isDirectLeftRecursive:boolean + isIndirectLeftRecursive:boolean + ; + + /** + ParserProduction are handled by the parser + */ + interface ParserProd extends Prod; + + // -------------------------------------------------------------------- + // Productions + + /** + A LexProd is formed by the keyword ident in the beginning followed by + an option slash indicating that this LexProd is protected. + The following name, options and symbol are handed to antlr directly + @attribute Protected If true, this identifier is protected and call + only be called by + other identifiers + @attribute Name Name of this identifier, only uppercase letters should be used + @attribute LexOption Options for lexer rule + @attribute InitAction Init actions for lexer rule + @attribute Alts List of alts for this lexer rule + @attribute Variable Variable used for mapping + (if type != null) or standard type (if type == null) + @attribute Type Type of the lexer tokens + @attribute Block Block used if other than standard mapping shall be used + */ + LexProd implements Prod = + GrammarAnnotation* (["fragment"] | ["comment"])* + "token" Name ("(" mode: Name ")")? + ( + ( + LexOption ("{" initAction:Action "}")? // Statements + | + "{" initAction:Action "}" + )? + "=" alt:(LexAlt || "|")+ + + (":" ("->" lexerCommand:Name ( "(" parameter:(Name || ",")+ ")" )?)? ("{" endAction:Action "}")? (variable:Name& + ("->" type:( Name& || ".")+ (":" "{" block:Action "}" )? )? )? // Statements + )? + ) + ";"; + + + /** + An Enumeration-Production + @attribute name Name of the production + @attribute constant List of all constants + */ + EnumProd implements Prod = + GrammarAnnotation* "enum" Name "=" (Constant || "|")+ ";"; + + + /** + A ExternalProduction represents a hole in the grammar + where another production can be plugged in + @attribute name Name of the hole + @attribute MCType External type used for this hole rule + */ + ExternalProd implements Prod = + GrammarAnnotation* "external" SymbolDefinition* Name MCType? ";"; + + + /** + An ASTInterfaceProd represents an explicit definition for an + interface production and may overwrite the standard generated rule + which is mentioned in the productions + @attribute external True if this interface is programmed manually + @attribute name Name of the production + @attribute superInterfaceRule Super interfaces + @attribute aSTSuperInterface Super interfaces abstract syntax only + */ + InterfaceProd implements ParserProd = + GrammarAnnotation* "interface" SymbolDefinition* Name + ( + "extends" superInterfaceRule:(RuleReference || "," )+ + | + "astextends" aSTSuperInterface:(MCType || ",")+ + )* + ("=" alt:(Alt || "|")+ )? ";"; + + + /** + An ASTAbstractprod represents an abstract production in a + context free grammar + @attribute name Name of the production + @attribute superRule Super productions + @attribute superInterfaceRule Super interfaces + @attribute aSTSuperClass Super productions (abstract syntax only) + @attribute aSTSuperInterface Super interfaces (abstract syntax only) + */ + AbstractProd implements Prod = + GrammarAnnotation* "abstract" SymbolDefinition* Name + ( + "extends" superRule:(RuleReference || ",")+ + | + "implements" superInterfaceRule:(RuleReference || ",")+ + | + "astextends" aSTSuperClass:(MCType || ",")+ + | + "astimplements" aSTSuperInterface:(MCType || ",")+ + )* + ("=" alt:(Alt || "|")+ )? ";"; + + /** + A rule represents a rule (production) in a context free grammar. + @attribute name Name of the rule + @attribute superrule Rule reference to a super rule + @attribute superInterfaceRule Rule reference to a super interface rule + @attribute aSTSuperClass Rule reference to a super rule + (used for abstract syntax only) + @attribute aSTSuperInterfacerule Rule reference to a super rule + (used for abstract syntax only) + @attribute action Action executed priot to the rule body + @attribute alts List of alternatives representing the body of the rule + */ + ClassProd implements ParserProd = + GrammarAnnotation* SymbolDefinition* Name + ( + "extends" superRule:(RuleReference || ",")+ + | + "implements" superInterfaceRule:(RuleReference || ",")+ + | + "astextends" aSTSuperClass:(MCType || ",")+ + | + "astimplements" aSTSuperInterface:(MCType || ",")+ + )* + + ("{" Action "}")? // Statements + ("=" (Alt || "|")+ )? ";"; + + astrule ClassProd = + method public String toString() { return name; } ; + + + /** + Cardinality + @attribute True if unbounded cardinality (min and max are meaningless) + @attribute min Minimal cardinality + @attribute max Maximal cardinality + */ + Card = + ( + iteration:["?"|"*"|"+"] + | + "min" "=" min:Digits + ("max" "=" ( max:Digits | max:"*"))? + | + "max" "=" ( max:Digits | max:"*") + ); + + + + /** + A RuleReference refers to another production + */ + RuleReference = + SemanticpredicateOrAction? Name ("<"prio:Digits">")? ; + + astrule RuleReference = + method public String getTypeName() { + return getName(); + } + method public boolean isExternal() { + return false; + }; + + // -------------------------------------------------------------------- + // Production body + + + /** + An alternative represents an alternative in a rule or block and + contains (Rule)Components + @attribute rightAssoc The alternative is right associative (the default is left) + @attribute Components List of the rule components of this alternative + */ + Alt = + [rightAssoc:""]? GrammarAnnotation? component:RuleComponent* ; + + + /** + RuleComponents are parts of the alternatives + */ + interface symbol RuleComponent; + + symbolrule RuleComponent = + isTerminal:boolean + isNonterminal:boolean + isConstantGroup:boolean + isConstant:boolean + isLexerNonterminal:boolean + isList:boolean + isOptional:boolean + subProds:String* + referencedType:Name? + ; + + /** + A NonTerminalSeparator is a shortcut for the description of list + structures + @attribute variableName Name for a variable binding (Note: Only one attribute of + VariableName and UsuageName may have a value) + @attribute usageName Name for attribute + @attribute name The name of the nonterminal + @attribute separator The terminal as list separator + @attribute plusKeywords True indicates that all keywords are allowed as well + @attribute iteration Iteration of nonterminal + */ + NonTerminalSeparator implements RuleComponent = + (usageName:Name& ":")? + "(" Name ("@" referencedSymbol:Name)? plusKeywords:["&"]? "||" separator:String ")" iteration:["*"|"+"]; + + + + /** + A block is something used in rules which is surrounded by () + @attribute Option options for this look like a non-greedy behavior + @attribute Alts List of alternatives + @attribute Iteration Iteration of this block + */ + Block implements RuleComponent = + "(" + ( + ( + Option ( "init" "{" initAction:Action "}" )? // Statements + | + "init" "{" initAction:Action "}" // Statements + ) + ":" + )? + alt:(Alt || "|")+ ")" + iteration:["?"|"*"|"+"]?; + + + /** + Option values handed to Antlr + @attribute optionValue Value handes to Antlr + */ + Option = + "options" "{" OptionValue+ "}"; + + + /** + Key/Value pairs handed to Antlr + @attribute key Key + @attribute value Value + */ + OptionValue = + key:Name "=" value:Name ";" ; + + + /** + Reference to another rule + @attribute variableName Name for a variable binding + (Note: Only one attribute of VariableName and UsuageName + may have a value) + @attribute usageName Name for attribute + @attribute plusKeywords True indicates that all keywords are allowed as well + @attribute iteration Iteration of nonterminal + */ + NonTerminal implements RuleComponent = + (usageName:Name& ":")? + Name ("@" referencedSymbol:Name)? (genSymbol:["!!"] symbolName:Name?)? + plusKeywords:["&"]? iteration:["?"|"*"|"+"]?; + + + interface ITerminal = + usageName:Name?; + + astrule ITerminal = + Name; + + /** + A Terminal is usually something like "," or "start" + */ + Terminal implements RuleComponent, ITerminal = + (usageName:Name& ":")? + name:String + iteration:["?"|"*"|"+"]?; + + /** + Use KeyTerminal instead of Terminal if you don't want this to be a keyword + */ + KeyTerminal implements RuleComponent, ITerminal = + (usageName:Name& ":")? + KeyConstant + iteration:["?"|"*"|"+"]?; + + TokenTerminal implements RuleComponent, ITerminal = + (usageName:Name& ":")? + TokenConstant + iteration:["?"|"*"|"+"]?; + + TokenConstant = + "token" "(" String ")"; + + KeyConstant = + "key" "(" (String || "|")+ ")"; + + /** + Constants are constant string but lead to an attribute + */ + Constant implements ITerminal = + (usageName:Name& ":")? (String | KeyConstant | TokenConstant) ; + + /** + Constants are sth like keywords which one needs to know that they + where there, e.g. public. In the ast-class they are reflected + as int oder boolean isMethods + */ + ConstantGroup implements RuleComponent = + (usageName:Name& ":")? + "[" Constant ("|" Constant )* "]" + iteration:["?"|"*"|"+"]? ; + + + /** + Handed on as antlr action or predicate, realised by external JavaDSLParser + */ + SemanticpredicateOrAction implements RuleComponent= + "{" ExpressionPredicate "}" Predicate:["?"] + | + "{" Action "}" + ; + + + // -------------------------------------------------------------------- + // Concept + + + /** + The grammar can be extended by using concepts + @attribute name Name of the concept + @attribute concept The concept itself + */ + Concept= + "concept" Name& concept:MCConcept; + + // -------------------------------------------------------------------- + // Control rules + + /** + Allows splitting of token, such as ">>" to single character token + */ + SplitRule = + "splittoken" (String || ",")+ ";"; + + /** + Makes keywords local + */ + KeywordRule = + "nokeyword" (String || ",")+ ";"; + + ReplaceRule = + "replacekeyword" keyword:String ":" replacedKeyWord:(String || ",")+ ";"; + + // ---------------------------------------------------------------------- + // AST + + + /** + An ASTRule contains additional information for the class + generation process + @attribute type Type this rule refers to + @attribute aSTSuperInterface Super interfaces abstract syntax only + @attribute aSTSuperClass Super productions abstract syntax only + @attribute methods Methods to be added to the class generation + @attribute AdditionalAttribute Attributes to be added to the class generation + */ + ASTRule = + "astrule" type:Name + ( + "astextends" aSTSuperClass:(MCType || ",")+ + | + "astimplements" aSTSuperInterface:(MCType || ",")+ + )* + ( + "=" ( GrammarMethod | AdditionalAttribute )* + )? ";"; + + + /** + A method to be added to a certain ASTClass + @attribute public True if method is public + @attribute private True if method is private + @attribute protected True if method is protected + @attribute final True if method is final + @attribute static True if method is static + @attribute returnType Qualified name of the return type of the method + @attribute name Name of the method + @attribute methodParameter List of the parameters + @attribute exceptions Lit of the thrown exceptions + @attribute body Body of the Method (mostly a JavaLazy-Object) + */ + GrammarMethod= + "method" + (["public"] | ["private"] | ["protected"])? + ["final"]? + ["static"]? + MCReturnType Name + "(" (MethodParameter || ",")* ")" + ("throws" exception:(MCType || ",")+)? + "{" body:Action "}" ; // Statements + + + /** + Formal parameter of a method + @attribute type Qualified name of the type + @attribute name Name of the parameter + */ + MethodParameter = + type:MCType Name; + + + /** + An attribute to be added to a certain ASTClass + @attribute name Name of the attribute + @attribute genericType Name of the referring type + @attribute card Cardinality + */ + symbol AdditionalAttribute = + (Name ":")? + MCType + Card?; + + symbolrule AdditionalAttribute = + type:String + isAstAttr:boolean; + + // -------------------------------------------------------------------- + // Lexer + + LexAlt = + lexComponent:LexComponent*; + + interface LexComponent; + + LexBlock implements LexComponent = + negate:["~"]? "(" + ( + ( + option:LexOption ("init" "{" initAction:Action "}")? // Statements + | + "init" "{" initAction:Action "}" // Statements + ) + ":" + )? + lexAlt:(LexAlt || "|")+ ")" + (iteration:["?"|"*"|"+"])?; + + LexCharRange implements LexComponent = + negate:["~"]? lowerChar:Char ".." upperChar:Char; + + LexChar implements LexComponent = + negate:["~"]? Char ; + + LexAnyChar implements LexComponent = + "."; + + LexString implements LexComponent = + string:String ; + + LexActionOrPredicate implements LexComponent = + "{" ExpressionPredicate "}" Predicate:["?"] + ; + + LexNonTerminal implements LexComponent, RuleComponent = + Name ; + + LexSimpleIteration implements LexComponent = + (LexNonTerminal | LexString | LexChar | LexAnyChar) iteration:["?"|"*"|"+"] question:["?"]?; + + LexOption = + "options" "{" iD:Name "=" value:Name ";" "}"; + + + // -------------------------------------------------------------------- + // Symbol Table + + SymbolDefinition = + genSymbol:["symbol"] | + genScope:["scope"] ("(" (["shadowing"] | ["non_exporting"] | ["ordered"])+ ")")?; + + /** + An SymbolRule contains additional information for the class + generation process + @attribute type Type this rule refers to + @attribute superInterface Super interfaces abstract syntax only + @attribute superClass Super productions abstract syntax only + @attribute methods Methods to be added to the class generation + @attribute additionalAttribute Attributes to be added to the class generation + */ + SymbolRule = + "symbolrule" type:Name + ( + "extends" superClass:(MCType || ",")+ + | + "implements" superInterface:(MCType || ",")+ + )* + ( + "=" ( GrammarMethod | AdditionalAttribute )* + )? ";"; + + ScopeRule = + "scoperule" ( + "extends" superClass:(MCType || ",")+ + | + "implements" superInterface:(MCType || ",")+ + )* + ( + "=" ( GrammarMethod | AdditionalAttribute )* + )? + ";"; + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/grammar/Grammar.mlc b/monticore-grammar/src/main/grammars/de/monticore/grammar/Grammar.mlc new file mode 100644 index 0000000000..b39a3335aa --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/grammar/Grammar.mlc @@ -0,0 +1,40 @@ +package de.monticore.grammar; + +mlc Grammar { + + //export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/grammar/Grammar.mc4"; + } + + //export handwritten code + export "$projectDir/src/main/java" { + include "de/monticore/grammar/grammar/**.java"; + + // additional, general Java source code + include "de/monticore/grammar/*.java"; + + // pretty printer + include "de/monticore/grammar/prettyprint/GrammarPrettyPrinter.java"; + + // TODO: Discuss whether we want to include general CoCos here + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/grammar/grammar/**.java"; + } + + promote { + mlc "de.monticore.literals.MCCommonLiterals"; + mlc "de.monticore.types.MCSimpleGenericTypes"; + include "$mp/com/google/common/**.class"; + include "$mp/org/apache/commons/lang3/StringUtils.class"; + } + + uses { + // needs to be included because it is needed to build the symbol table -> cf. GrammarScopesGenitor + include "$projectDir/src/main/java/de/monticore/grammar/prettyprint/Grammar_WithConceptsFullPrettyPrinter.java"; + } + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/grammar/Grammar_WithConcepts.mc4 b/monticore-grammar/src/main/grammars/de/monticore/grammar/Grammar_WithConcepts.mc4 new file mode 100644 index 0000000000..a31c383f96 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/grammar/Grammar_WithConcepts.mc4 @@ -0,0 +1,41 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar; + +grammar Grammar_WithConcepts extends + de.monticore.grammar.Grammar, + de.monticore.statements.MCCommonStatements, + de.monticore.statements.MCReturnStatements, + de.monticore.statements.MCExceptionStatements, + de.monticore.expressions.JavaClassExpressions, + de.monticore.JavaLight, + de.monticore.grammar.concepts.antlr.Antlr, + de.monticore.expressions.CommonExpressions, + de.monticore.expressions.BitExpressions + { + + //CD4A keywords are no keywords here + nokeyword "association", "composition", "ordered", "targetimport", "classdiagram"; + + // keep the old axiom + start MCGrammar; + + // fill the external nonterminals + + MCConcept = ConceptAntlr; + + Action = MCBlockStatement*; + + ExpressionPredicate = Expression; + + JavaCode = ClassBodyDeclaration*; + + ExtType = MCType; + + ExtReturnType = MCReturnType; + + ExtTypeArgument = "<" (MCTypeArgument || ",")+ ">"*; + + // Empty TypeParameters + ExtTypeParameters = ; + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/grammar/Grammar_WithConcepts.mlc b/monticore-grammar/src/main/grammars/de/monticore/grammar/Grammar_WithConcepts.mlc new file mode 100644 index 0000000000..aa75f29563 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/grammar/Grammar_WithConcepts.mlc @@ -0,0 +1,36 @@ +package de.monticore.grammar; + +mlc Grammar_WithConcepts { + + //export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/grammar/Grammar_WithConcepts.mc4"; + } + + //export handwritten code + export "$projectDir/src/main/java" { + include "de/monticore/grammar/grammar_withconcepts/**.java"; + include "de/monticore/grammar/prettyprint/Grammar_WithConceptsFullPrettyPrinter.java"; + include "de/monticore/grammar/prettyprint/Grammar_WithConceptsPrettyPrinter.java"; + + // CoCos + include "de/monticore/grammar/cocos/**.java"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/grammar/grammar_withconcepts/**.java"; + } + + promote { + mlc "de.monticore.grammar.Grammar"; + mlc "de.monticore.statements.MCCommonStatements"; + mlc "de.monticore.statements.MCReturnStatements"; + mlc "de.monticore.statements.MCExceptionStatements"; + mlc "de.monticore.expressions.JavaClassExpressions"; + mlc "de.monticore.JavaLight"; + mlc "de.monticore.grammar.concepts.antlr.Antlr"; + mlc "de.monticore.expressions.CommonExpressions"; + mlc "de.monticore.expressions.BitExpressions"; + } +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/grammar/concepts/antlr/Antlr.mc4 b/monticore-grammar/src/main/grammars/de/monticore/grammar/concepts/antlr/Antlr.mc4 new file mode 100644 index 0000000000..b3e423cdac --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/grammar/concepts/antlr/Antlr.mc4 @@ -0,0 +1,38 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.concepts.antlr; + +component grammar Antlr { + + /** Used for embedding java declarations */ + external JavaCode; + + /** An AntlrConcept allows to specify additional antlr rules and Java code for Parser and lexer + @attribute AntlrParserCode Additional Antlr rules for the parser + @attribute AntlrParserAction Additional Java code for the parser + @attribute AntlrLexerCode Additional Antlr rules for the lexer + @attribute AntlrLexerAction Additional Java code for the lexer + */ + ConceptAntlr = + "{" + (AntlrParserAction | AntlrLexerAction)* + "}"; + + astrule ConceptAntlr = + method public String toString() { + return ""; + }; + + /** Additional Java code for the parser + @attribute Text Java Code + */ + AntlrParserAction= + "parserjava" "{" text:JavaCode "}"; + + /** Additional Additional Java code for the lexer + @attribute Text Java Code + */ + AntlrLexerAction= + "lexerjava" "{" text:JavaCode "}"; +} + + diff --git a/monticore-grammar/src/main/grammars/de/monticore/grammar/concepts/antlr/Antlr.mlc b/monticore-grammar/src/main/grammars/de/monticore/grammar/concepts/antlr/Antlr.mlc new file mode 100644 index 0000000000..d8acef739d --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/grammar/concepts/antlr/Antlr.mlc @@ -0,0 +1,23 @@ +package de.monticore.grammar.concepts.antlr; + +mlc Antlr { + + //export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/grammar/concepts/antlr/Antlr.mc4"; + } + + //export handwritten code + export "$projectDir/src/main/java" { + include "de/monticore/grammar/prettyprint/AntlrPrettyPrinter.java"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/grammar/concepts/antlr/antlr/**.java"; + } + + promote { + mlc "de.monticore.MCBasics"; + } +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/literals/Literals/index.html b/monticore-grammar/src/main/grammars/de/monticore/literals/Literals/index.html new file mode 100644 index 0000000000..23983e0f54 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/literals/Literals/index.html @@ -0,0 +1,806 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Literals - MontiCore + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + + + + + +

MontiCore - Literals

+

Literals are the basis to parse numbers, strings and other atomic language +elements. The language module MCLiteralBasis defines the root nonterminal +Literal but no other terminals representing literal terms. +MCCommonLiterals and +MCJavaLiterals define concrete literal terms that can +be included in a MontiCore-based language as desired.

+

Grammar MCCommonLiterals.mc4

+

This grammar includes the following parser rules:

+
    +
  • NullLiteral: Recognizes null.
  • +
  • BooleanLiteral: Recognizes true and false.
  • +
  • CharLiteral: Recognizes a, ... , Z.
  • +
  • StringLiteral: Recognizes "...".
  • +
  • NatLiteral: Recognizes literals like 123.
  • +
  • SignedNatLiteral: Recognizes literals like -13.
  • +
  • BasicLongLiteral: Recognizes literals like 6L and 6l.
  • +
  • SignedBasicLongLiteral: Recognizes literals like -6L, -6l, 6L?, and +6l.
  • +
  • BasicFloatLiteral: Recognizes literals like 1.2F and 1.2f.
  • +
  • SignedBasicFloatLiteral: Recognizes literals like -1.2F, -1.2f, 1.2F, +and 1.2f.
  • +
+

Grammar MCJavaLiterals.mc4

+

This grammar extends MCCommonLiterals and includes the following parser +rules:

+
    +
  • IntLiteral: Recognizes literals like 123, 0734, 1001001, and 0x1a.
  • +
  • LongLiteral: Recognizes literals like 2L, 0734l, 1001001L, and +0x1al.
  • +
  • FloatLiteral: Recognizes literals like 1.23F and 1.23E4f.
  • +
  • DoubleLiteral: Recognizes literals like 1.23, 1.23d, 1.23E4D.
  • +
+

Further Information

+ + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/monticore-grammar/src/main/grammars/de/monticore/literals/MCCommonLiterals.mc4 b/monticore-grammar/src/main/grammars/de/monticore/literals/MCCommonLiterals.mc4 new file mode 100644 index 0000000000..b9f379400d --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/literals/MCCommonLiterals.mc4 @@ -0,0 +1,297 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.literals; + +/* This is a MontiCore stable grammar. + * Adaptations -- if any -- are conservative. */ + +import de.monticore.literals.*; + +/** + * This grammar defines Java compliant literals. + * The scope of this grammar is to + * ease the reuse of literals structures in Java-like sublanguages, e.g., by + * grammar inheritance or grammar embedment. + * The grammar contains literals from Java, e.g., Boolean, Char, String, .... +*/ + +component grammar MCCommonLiterals + extends de.monticore.MCBasics, + MCLiteralsBasis { + + + /*========================================================================*/ + /*======================= INTERFACE DEFINITIONS ==========================*/ + /*========================================================================*/ + + + /** ASTSignedLiteral is the interface for all literals (NullLiteral, + BooleanLiteral, CharLiteral, StringLiteral and all NumericLiterals). + Compared to Literal it also includes negative NumericLiterals + */ + interface SignedLiteral; + + + /** The interface ASTNumericLiteral combines the numeric literal types for + Integer, Long, Float and Double without '-' at the beginning) + */ + interface NumericLiteral extends Literal <100>; + + + /** The interface ASTNumericLiteral combines the numeric literal types for + Integer, Long, Float and Double. + Compared to NumericLiteral it also includes negative numbers. + */ + interface SignedNumericLiteral extends SignedLiteral <100>; + + + /*========================================================================*/ + /*============================ PARSER RULES ==============================*/ + /*========================================================================*/ + + /** ASTNullLiteral represents 'null' + */ + NullLiteral implements Literal, SignedLiteral = + "null"; + + + /** ASTBooleanLiteral represents "true" or "false" + @attribute source String-representation (including '"'). + */ + BooleanLiteral implements Literal, SignedLiteral = + source:["true" | "false"]; + + + /** ASTCharLiteral represents any valid character parenthesized with "'". + @attribute source String-representation (including "'"). + */ + CharLiteral implements Literal, SignedLiteral = + source:Char; + + + /** ASTStringLiteral represents any valid character sequence parenthesized + with '"'. + @attribute source String-representation (including '"'). + */ + StringLiteral implements Literal, SignedLiteral = + source:String; + + + /** ASTNatLiteral represents a positive Decimal number. + @attribute source String-representation (including '"'). + */ + NatLiteral implements NumericLiteral<1> = + Digits; + + /** ASTSignedNatLiteral represents a positive or negative Decimal number. + @attribute source String-representation (including '"'). + */ + SignedNatLiteral implements SignedNumericLiteral<1> = + {noSpace(2)}? (negative:["-"]) Digits | + Digits; + + /** ASTLongLiteral represents a positive Decimal number. + @attribute source String-representation (including '"'). + */ + BasicLongLiteral implements NumericLiteral<1> = + { cmpToken(2,"l","L") && noSpace(2) }? Digits key("l" | "L"); + + /** ASTSignedLongLiteral represents a positive or negative Decimal number. + @attribute source String-representation (including '"'). + */ + SignedBasicLongLiteral implements SignedNumericLiteral<1> = + { cmpToken(3,"l","L") && noSpace(2,3) }? + negative:["-"] Digits key("l" | "L") + | + { cmpToken(2,"l","L") && noSpace(2) }? + Digits key("l" | "L"); + + /** ASTFloatLiteral represents a positive float. + @attribute source String-representation (including '"'). + */ + BasicFloatLiteral implements NumericLiteral<1> = + { cmpToken(4,"f","F") && noSpace(2,3,4) }? + pre:Digits "." post:Digits key("f" | "F"); + + /** ASTSignedFloatLiteral represents a positive or negative float. + @attribute source String-representation (including '"'). + */ + SignedBasicFloatLiteral implements SignedNumericLiteral<1> = + { cmpToken(5,"f","F") && noSpace(2,3,4,5)}? + negative:["-"] pre:Digits "." post:Digits key("f" | "F") | + { cmpToken(4,"f","F") && noSpace(2,3,4) }? + pre:Digits "." post:Digits key("f" | "F"); + + /** ASTDoubleLiteral represents a positive double. + @attribute source String-representation (including '"'). + */ + BasicDoubleLiteral implements NumericLiteral<1> = + { noSpace(2,3) }? pre:Digits "." post:Digits; + + /** ASTSignedDoubleLiteral represents a positive or negative double. + @attribute source String-representation (including '"'). + */ + SignedBasicDoubleLiteral implements SignedNumericLiteral<1> = + { noSpace(2,3,4) }? negative:["-"] pre:Digits "." post:Digits | + { noSpace(2,3) }? pre:Digits "." post:Digits; + + /*========================================================================*/ + /*============================ LEXER RULES ===============================*/ + /*========================================================================*/ + + + /*========================================================================*/ + /* The following section is adapted from */ + /* https://github.com/antlr/grammars-v4/blob/master/java/Java.g4 */ + /*========================================================================*/ + + // §3.10.1 Integer Literals + + token Digits + = Digit+; + + fragment token Digit + = '0'..'9'; + + // §3.10.4 Character Literals + token Char + = '\'' (SingleCharacter|EscapeSequence) '\'' + : {setText(getText().substring(1, getText().length() - 1));}; + + fragment token SingleCharacter + = ~ ('\''); + + + // §3.10.5 String Literals + token String + = '"' (StringCharacters)? '"' + : {setText(getText().substring(1, getText().length() - 1));}; + + fragment token StringCharacters + = (StringCharacter)+; + + fragment token StringCharacter + = ~ ('"' | '\\') | EscapeSequence; + + + // §3.10.6 Escape Sequences for Character and String Literals + fragment token EscapeSequence + = '\\' ('b' | 't' | 'n' | 'f' | 'r' | '"' | '\'' | '\\') + | OctalEscape | UnicodeEscape; + + fragment token OctalEscape + = '\\' OctalDigit | '\\' OctalDigit OctalDigit + | '\\' ZeroToThree OctalDigit OctalDigit; + + fragment token UnicodeEscape + = '\\' 'u' HexDigit HexDigit HexDigit HexDigit; + + fragment token ZeroToThree + = '0'..'3' ; + + fragment token HexDigit + = '0'..'9' | 'a'..'f' | 'A'..'F' ; + + fragment token OctalDigit + = '0'..'7' ; + + + /*========================================================================*/ + /*======================= AST DEFINITIONS ================================*/ + /*========================================================================*/ + + astrule BooleanLiteral = + method public boolean getValue() { + return this.source == ASTConstantsMCCommonLiterals.TRUE; + } + ; + + astrule CharLiteral = + method public char getValue() { + return de.monticore.literals.MCLiteralsDecoder.decodeChar( + getSource()); + } + ; + + astrule StringLiteral = + method public String getValue() { + return de.monticore.literals.MCLiteralsDecoder.decodeString( + getSource()); + } + ; + + astrule NatLiteral = + method public String getSource() { + return getDigits(); + } + method public int getValue() { + return de.monticore.literals.MCLiteralsDecoder.decodeNat( + getSource()); + } + ; + + astrule SignedNatLiteral = + method public String getSource() { + return (negative?"-":"") + getDigits(); + } + method public int getValue() { + return de.monticore.literals.MCLiteralsDecoder.decodeNat( + getSource()); + } + ; + + astrule BasicLongLiteral = + method public String getSource() { + return getDigits() + "L"; + } + method public long getValue() { + return de.monticore.literals.MCLiteralsDecoder.decodeLong(getSource()); + } + ; + + astrule SignedBasicLongLiteral = + method public String getSource() { + return (negative?"-":"") + getDigits() + "L"; + } + method public long getValue() { + return de.monticore.literals.MCLiteralsDecoder.decodeLong(getSource()); + } + ; + + astrule BasicFloatLiteral = + method public String getSource() { + return getPre() + "." + getPost() + "F"; + } + method public float getValue() { + return de.monticore.literals.MCLiteralsDecoder.decodeFloat(getSource()); + } + ; + + astrule SignedBasicFloatLiteral = + method public String getSource() { + return (isNegative()?"-":"") + getPre() + "." + getPost() + "F"; + } + method public float getValue() { + return de.monticore.literals.MCLiteralsDecoder.decodeFloat(getSource()); + } + ; + + astrule BasicDoubleLiteral = + method public String getSource() { + return getPre() + "." + getPost(); + } + method public double getValue() { + return de.monticore.literals.MCLiteralsDecoder.decodeDouble(getSource()); + } + ; + + astrule SignedBasicDoubleLiteral = + method public String getSource() { + return (isNegative()?"-":"") + getPre() + "." + getPost(); + } + method public double getValue() { + return de.monticore.literals.MCLiteralsDecoder.decodeDouble(getSource()); + } + ; + +} + diff --git a/monticore-grammar/src/main/grammars/de/monticore/literals/MCCommonLiterals.mlc b/monticore-grammar/src/main/grammars/de/monticore/literals/MCCommonLiterals.mlc new file mode 100644 index 0000000000..3a71af909d --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/literals/MCCommonLiterals.mlc @@ -0,0 +1,24 @@ +package de.monticore.literals; + +mlc MCCommonLiterals { + + export "$projectDir/src/main/grammars" { + include "de/monticore/literals/MCCommonLiterals.mc4"; + } + + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/literals/mccommonliterals/**.java"; + } + + //handwritten sources + export "$projectDir/src/main/java" { + include "de/monticore/literals/mccommonliterals/**.java"; + include "de/monticore/literals/prettyprint/MCCommonLiterals*.java"; + include "de/monticore/literals/MCLiteralsDecoder.java"; + include "de/monticore/types/check/DeriveSymTypeOfMCCommonLiterals.java"; + } + + promote { + mlc "de.monticore.literals.MCLiteralsBasis"; + } +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/literals/MCJavaLiterals.mc4 b/monticore-grammar/src/main/grammars/de/monticore/literals/MCJavaLiterals.mc4 new file mode 100644 index 0000000000..7cf48ba795 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/literals/MCJavaLiterals.mc4 @@ -0,0 +1,243 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.literals; + +/* This is a MontiCore stable grammar. + * Adaptations -- if any -- are conservative. */ + +import de.monticore.literals.*; + +/** + * + * This grammar defines Java compliant literals. The scope of this grammar + * is to ease the reuse of literals structures in Java-like sublanguages, + * e.g., by grammar inheritance or grammar embedment. + * The grammar contains literals from Java, e.g., Boolean, Char, String, .... + * Please note that Java has an extended syntax e.g. for integers + * using underscores or other kinds of encodings. + * They parse e.g. 999_999 or 0x3F2A + * +*/ + +component grammar MCJavaLiterals extends MCCommonLiterals { + + + + /*========================================================================*/ + /*======================= INTERFACE DEFINITIONS ==========================*/ + /*========================================================================*/ + + /*========================================================================*/ + /*============================ PARSER RULES ==============================*/ + /*========================================================================*/ + + + /** ASTIntLiteral represents a positive Integer number. + @attribute source String-representation (including '"'). + */ + IntLiteral implements NumericLiteral <100> = + source:Num_Int ; + + + /** ASTLongLiteral represents a positive Long number. + @attribute source String-representation (including '"'). + */ + LongLiteral implements NumericLiteral <99> = + source:Num_Long ; + + + /** ASTFloatLiteral represents a positive Float number. + @attribute source String-representation (including '"'). + */ + FloatLiteral implements NumericLiteral <100> = + source:Num_Float ; + + + + /** ASTDoubleLiteral represents a positive Double number. + @attribute source String-representation (including '"'). + */ + DoubleLiteral implements NumericLiteral <100> = + source:Num_Double ; + + + /*========================================================================*/ + /*============================ LEXER RULES ===============================*/ + /*========================================================================*/ + + + /*========================================================================*/ + /* The following section is adapted from */ + /* https://github.com/antlr/grammars-v4/blob/master/java/Java.g4 */ + /*========================================================================*/ + + // §3.10.1 Integer Literals + + token Num_Int + = DecimalIntegerLiteral | HexIntegerLiteral + | OctalIntegerLiteral | BinaryIntegerLiteral; + + token Num_Long + = DecimalIntegerLiteral IntegerTypeSuffix + | HexIntegerLiteral IntegerTypeSuffix + | OctalIntegerLiteral IntegerTypeSuffix + | BinaryIntegerLiteral IntegerTypeSuffix; + + fragment token DecimalIntegerLiteral + = DecimalNumeral; + + fragment token HexIntegerLiteral + = HexNumeral; + + fragment token OctalIntegerLiteral + = OctalNumeral; + + fragment token BinaryIntegerLiteral + = BinaryNumeral; + + fragment token IntegerTypeSuffix + = 'l' | 'L'; + + fragment token DecimalNumeral + = '0' | NonZeroDigit (Digits? | Underscores Digits); + + @Override + fragment token Digits + = Digit (DigitOrUnderscore* Digit)?; + + @Override + fragment token Digit + = '0' | NonZeroDigit; + + fragment token NonZeroDigit + = '1'..'9' ; + + fragment token DigitOrUnderscore + = Digit | '_'; + + fragment token Underscores + = '_'+; + + fragment token HexNumeral + = '0' ('x' | 'X') HexDigits; + + fragment token HexDigits + = HexDigit (HexDigitOrUnderscore* HexDigit)?; + + @Override + fragment token HexDigit + = '0'..'9' | 'a'..'f' | 'A'..'F' ; + + fragment token HexDigitOrUnderscore + = HexDigit | '_'; + + fragment token OctalNumeral + = '0' Underscores? OctalDigits; + + fragment token OctalDigits + = OctalDigit (OctalDigitOrUnderscore* OctalDigit)?; + + @Override + fragment token OctalDigit + = '0'..'7' ; + + fragment token OctalDigitOrUnderscore + = OctalDigit | '_'; + + fragment token BinaryNumeral + = '0' ('b' | 'B') BinaryDigits; + + fragment token BinaryDigits + = BinaryDigit (BinaryDigitOrUnderscore* BinaryDigit)?; + + fragment token BinaryDigit + = '0' | '1'; + + fragment token BinaryDigitOrUnderscore + = BinaryDigit | '_'; + + + // §3.10.2 Floating-Point Literals + + token Num_Float + = DecimalFloatingPointLiteral | HexadecimalFloatingPointLiteral; + + token Num_Double + = DecimalDoublePointLiteral | HexadecimalDoublePointLiteral; + + fragment token DecimalDoublePointLiteral + = Digits '.' Digits? ExponentPart? DoubleTypeSuffix? + | '.' Digits ExponentPart? DoubleTypeSuffix? + | Digits ExponentPart DoubleTypeSuffix? + | Digits DoubleTypeSuffix; + + fragment token DecimalFloatingPointLiteral + = Digits '.' Digits? ExponentPart? FloatTypeSuffix + | '.' Digits ExponentPart? FloatTypeSuffix + | Digits ExponentPart FloatTypeSuffix + | Digits FloatTypeSuffix; + + fragment token ExponentPart + = ExponentIndicator SignedInteger; + + fragment token ExponentIndicator + = 'e' | 'E'; + + fragment token SignedInteger + = Sign? Digits; + + fragment token Sign + = '+' | '-'; + + fragment token FloatTypeSuffix + = 'f' | 'F'; + + fragment token DoubleTypeSuffix + = 'd' | 'D'; + + fragment token HexadecimalDoublePointLiteral + = HexSignificand BinaryExponent DoubleTypeSuffix?; + + fragment token HexadecimalFloatingPointLiteral + = HexSignificand BinaryExponent FloatTypeSuffix; + + fragment token HexSignificand + = HexNumeral '.'? | '0' ('x' | 'X') HexDigits? '.' HexDigits; + + fragment token BinaryExponent + = BinaryExponentIndicator SignedInteger; + + fragment token BinaryExponentIndicator + = 'p' | 'P'; + + /*========================================================================*/ + /*======================= AST DEFINITIONS ================================*/ + /*========================================================================*/ + + + astrule IntLiteral = + method public int getValue() { + return de.monticore.literals.MCLiteralsDecoder.decodeInt(getSource()); + } + ; + + astrule LongLiteral = + method public long getValue() { + return de.monticore.literals.MCLiteralsDecoder.decodeLong(getSource()); + } + ; + + + astrule FloatLiteral = + method public float getValue() { + return de.monticore.literals.MCLiteralsDecoder.decodeFloat(getSource()); + } + ; + + + astrule DoubleLiteral = + method public double getValue() { + return de.monticore.literals.MCLiteralsDecoder.decodeDouble(getSource()); + } + ; + + } diff --git a/monticore-grammar/src/main/grammars/de/monticore/literals/MCJavaLiterals.mlc b/monticore-grammar/src/main/grammars/de/monticore/literals/MCJavaLiterals.mlc new file mode 100644 index 0000000000..a6e969a44b --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/literals/MCJavaLiterals.mlc @@ -0,0 +1,23 @@ +package de.monticore.literals; + +mlc MCJavaLiterals { + + export "$projectDir/src/main/grammars" { + include "de/monticore/literals/MCJavaLiterals.mc4"; + } + + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/literals/mcjavaliterals/**.java"; + } + + //handwritten sources + export "$projectDir/src/main/java" { + include "de/monticore/literals/mcjavaliterals/**.java"; + include "de/monticore/literals/prettyprint/MCJavaLiterals*.java"; + include "de/monticore/types/check/DeriveSymTypeOfMCJavaLiterals.java"; + } + + promote { + mlc "de.monticore.literals.MCCommonLiterals"; + } +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/literals/MCLiteralsBasis.mc4 b/monticore-grammar/src/main/grammars/de/monticore/literals/MCLiteralsBasis.mc4 new file mode 100644 index 0000000000..522f8b1fdc --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/literals/MCLiteralsBasis.mc4 @@ -0,0 +1,22 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.literals; + +/* This is a MontiCore stable grammar. + * Adaptations -- if any -- are conservative. */ + +component grammar MCLiteralsBasis { + + + /** ASTLiteral is the interface for all literals (NullLiteral, + BooleanLiteral, CharLiteral, StringLiteral and all NumericLiterals + without '-' at the beginning). + + We deliberately decided to introduce this Nonterminal as + interface without concrete Implementation here, + because it allows to include Literals of various Forms + by composing with the appropriate Literals grammar. + + */ + interface Literal; + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/literals/MCLiteralsBasis.mlc b/monticore-grammar/src/main/grammars/de/monticore/literals/MCLiteralsBasis.mlc new file mode 100644 index 0000000000..94ed929180 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/literals/MCLiteralsBasis.mlc @@ -0,0 +1,20 @@ +package de.monticore.literals; + +mlc MCLiteralsBasis { + + export "$projectDir/src/main/grammars" { + include "de/monticore/literals/MCLiteralsBasis.mc4"; + } + + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/literals/mcliteralsbasis/**.java"; + } + + export "$projectDir/src/main/java" { + include "de/monticore/types/check/DeriveSymTypeOfLiterals.java"; + } + + promote { + mlc "de.monticore.MCBasics"; + } +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/statements/MCArrayStatements.mc4 b/monticore-grammar/src/main/grammars/de/monticore/statements/MCArrayStatements.mc4 new file mode 100644 index 0000000000..a0efaeee77 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/statements/MCArrayStatements.mc4 @@ -0,0 +1,40 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements; + +/* This is a MontiCore stable grammar. + * Adaptations -- if any -- are conservative. */ + +import de.monticore.statements.MCVarDeclarationStatements; + +/** + * This grammar defines Java's mechanism for the introduction + * of local variables with a type, an array dim and + * including the possibility to initialize the + * variable, e.g. private int a[] = { ... } + * + * This grammar is part of a hierarchy of statements, namely + * * statements/MCStatementsBasis.mc4 + * * -- statements/MCAssertStatements.mc4 + * * -- statements/MCVarDeclarationStatements.mc4 + * * -- -- statements/MCArrayStatements.mc4 + * * -- -- statements/MCCommonStatements.mc4 + * * -- -- -- statements/MCExceptionStatements.mc4 + * * -- -- -- statements/MCSynchronizedStatements.mc4 + * * -- statements/MCLowLevelStatements.mc4 + * * -- statements/MCReturnStatements.mc4 + * + * and the composition of all statement grammars to full Java: + * * -- -- statements/MCFullJavaStatements.mc4 + * +*/ + +component grammar MCArrayStatements + extends MCVarDeclarationStatements { + + ArrayDeclaratorId implements Declarator + = Name (dim:"[" "]")+ ; + + ArrayInit implements VariableInit + = "{" (VariableInit || ",")* (",")? "}" ; + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/statements/MCArrayStatements.mlc b/monticore-grammar/src/main/grammars/de/monticore/statements/MCArrayStatements.mlc new file mode 100644 index 0000000000..668ea61585 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/statements/MCArrayStatements.mlc @@ -0,0 +1,24 @@ +package de.monticore.statements; + +mlc MCArrayStatements { + + //export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/statements/MCArrayStatements.mc4"; + } + + //export the pretty printer + export "$projectDir/src/main/java" { + include "de/monticore/statements/prettyprint/MCArrayStatements*.java"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/statements/mcarraystatements/**.java"; + } + + promote { + mlc "de.monticore.statements.MCVarDeclarationStatements"; + } + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/statements/MCAssertStatements.mc4 b/monticore-grammar/src/main/grammars/de/monticore/statements/MCAssertStatements.mc4 new file mode 100644 index 0000000000..db308b0611 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/statements/MCAssertStatements.mc4 @@ -0,0 +1,39 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements; + +/* This is a MontiCore stable grammar. + * Adaptations -- if any -- are conservative. */ + +import de.monticore.statements.*; +import de.monticore.expressions.*; + +/** + * This grammar defines the assert statement as known from Java + * assert Constraint : Message ; + * It can be used independently of other Java statements, + * but also can be left out if its applications would not make sense. + * + * This grammar is part of a hierarchy of statements, namely + * * statements/MCStatementsBasis.mc4 + * * -- statements/MCAssertStatements.mc4 + * * -- statements/MCVarDeclarationStatements.mc4 + * * -- -- statements/MCArrayStatements.mc4 + * * -- -- statements/MCCommonStatements.mc4 + * * -- -- -- statements/MCExceptionStatements.mc4 + * * -- -- -- statements/MCSynchronizedStatements.mc4 + * * -- statements/MCLowLevelStatements.mc4 + * * -- statements/MCReturnStatements.mc4 + * + * and the composition of all statement grammars to full Java: + * * -- -- statements/MCFullJavaStatements.mc4 + * +*/ + +component grammar MCAssertStatements + extends MCStatementsBasis, + ExpressionsBasis { + +AssertStatement implements MCStatement + = "assert" assertion:Expression (":" message:Expression)? ";" ; + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/statements/MCAssertStatements.mlc b/monticore-grammar/src/main/grammars/de/monticore/statements/MCAssertStatements.mlc new file mode 100644 index 0000000000..fe32e967ca --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/statements/MCAssertStatements.mlc @@ -0,0 +1,25 @@ +package de.monticore.statements; + +mlc MCAssertStatements { + + //export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/statements/MCAssertStatements.mc4"; + } + + //export the pretty printer + export "$projectDir/src/main/java" { + include "de/monticore/statements/prettyprint/MCAssertStatements*.java"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/statements/mcassertstatements/**.java"; + } + + promote { + mlc "de.monticore.statements.MCStatementsBasis"; + mlc "de.monticore.expressions.ExpressionsBasis"; + } + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/statements/MCCommonStatements.mc4 b/monticore-grammar/src/main/grammars/de/monticore/statements/MCCommonStatements.mc4 new file mode 100644 index 0000000000..1d50110164 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/statements/MCCommonStatements.mc4 @@ -0,0 +1,117 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements; + +/* This is a MontiCore stable grammar. + * Adaptations -- if any -- are conservative. */ + +import de.monticore.statements.*; + +/** + * This grammar defines typical statements, such as + * method calls (which are actually expressions), + * assignment of variables, if, for, while, switch statements, and blocks. + * + * This embodies a complete structured statement language, however does not + * provide return, assert, exceptions, and low-level constructs like break. + * + * This grammar is part of a hierarchy of statements, namely + * * statements/MCStatementsBasis.mc4 + * * -- statements/MCAssertStatements.mc4 + * * -- statements/MCVarDeclarationStatements.mc4 + * * -- -- statements/MCArrayStatements.mc4 + * * -- -- statements/MCCommonStatements.mc4 + * * -- -- -- statements/MCExceptionStatements.mc4 + * * -- -- -- statements/MCSynchronizedStatements.mc4 + * * -- statements/MCLowLevelStatements.mc4 + * * -- statements/MCReturnStatements.mc4 + * + * and the composition of all statement grammars to full Java: + * * -- -- statements/MCFullJavaStatements.mc4 + * +*/ + +component grammar MCCommonStatements + extends MCVarDeclarationStatements { + +/** + * Standard Form of a block { ... } + * it allows to define local variables that are not exported + * and can only be used after defined (typical within code bodies). + */ +scope(non_exporting ordered) MCJavaBlock implements MCStatement + = "{" MCBlockStatement* "}" ; + +/** + * All the Java Modifier + */ +JavaModifier implements MCModifier = + Modifier:["private" | "public" | "protected" | "static" + | "transient" | "final" | "abstract" | "native" + | "threadsafe" | "synchronized" | "const" | "volatile" + | "strictfp" | modifier_default:"default"] ; + +IfStatement implements MCStatement + = "if" "(" condition:Expression ")" + thenStatement:MCStatement + ("else" elseStatement:MCStatement)? ; + // we use "elseStatement", because the + // generated Java code doesn't allow "else" as Name + + +scope (non_exporting ordered) ForStatement implements MCStatement + = "for" "(" ForControl ")" MCStatement ; + +interface ForControl ; + +CommonForControl implements ForControl + = ForInit? ";" condition:Expression? ";" (Expression || ",")* ; + +ForInit + = ForInitByExpressions | LocalVariableDeclaration ; + +ForInitByExpressions + = (Expression || ",")+ ; + +EnhancedForControl implements ForControl + = FormalParameter ":" Expression; + +FormalParameter + = JavaModifier* MCType Declarator; + +WhileStatement implements MCStatement + = "while" "(" condition:Expression ")" MCStatement ; + +DoWhileStatement implements MCStatement + = "do" MCStatement "while" "(" condition:Expression ")" ";" ; + +SwitchStatement implements MCStatement + = "switch" "(" Expression ")" + "{" SwitchBlockStatementGroup* SwitchLabel* "}" ; + +EmptyStatement implements MCStatement + = ";" ; + +ExpressionStatement implements MCStatement + = Expression ";" ; + +// Matches cases then statements, both of which are mandatory. +// To handle empty cases at the end, SwitchLabel* is explicitly added +// in the statement body +SwitchBlockStatementGroup + = SwitchLabel+ MCBlockStatement+ ; + +interface SwitchLabel ; + +ConstantExpressionSwitchLabel implements SwitchLabel + = "case" constant:Expression ":" ; + +EnumConstantSwitchLabel implements SwitchLabel + = "case" enumConstant:Name ":" ; + +DefaultSwitchLabel implements SwitchLabel + = "default" ":" ; + +BreakStatement implements MCStatement + = "break" ";" ; + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/statements/MCCommonStatements.mlc b/monticore-grammar/src/main/grammars/de/monticore/statements/MCCommonStatements.mlc new file mode 100644 index 0000000000..4522f3cd40 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/statements/MCCommonStatements.mlc @@ -0,0 +1,27 @@ +package de.monticore.statements; + +mlc MCCommonStatements { + + //export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/statements/MCCommonStatements.mc4"; + } + + //export the handwritten TOP-classes and pretty printer + export "$projectDir/src/main/java" { + include "de/monticore/statements/mccommonstatements/**.java"; + include "de/monticore/statements/prettyprint/MCCommonStatements*.java"; + //TODO decide whether to include or exclude CoCos in MLCs + exclude "de/monticore/statements/mccommonstatements/cocos/*.java"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/statements/mccommonstatements/**.java"; + } + + promote { + mlc "de.monticore.statements.MCVarDeclarationStatements"; + } + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/statements/MCExceptionStatements.mc4 b/monticore-grammar/src/main/grammars/de/monticore/statements/MCExceptionStatements.mc4 new file mode 100644 index 0000000000..6f44cb4be9 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/statements/MCExceptionStatements.mc4 @@ -0,0 +1,65 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements; + +/* This is a MontiCore stable grammar. + * Adaptations -- if any -- are conservative. */ + +import de.monticore.statements.*; + +/** + * This grammar defines the exception statements. + * This includes Java try with catch and finally, as well as throw. + * + * This grammar is part of a hierarchy of statements, namely + * * statements/MCStatementsBasis.mc4 + * * -- statements/MCAssertStatements.mc4 + * * -- statements/MCVarDeclarationStatements.mc4 + * * -- -- statements/MCArrayStatements.mc4 + * * -- -- statements/MCCommonStatements.mc4 + * * -- -- -- statements/MCExceptionStatements.mc4 + * * -- -- -- statements/MCSynchronizedStatements.mc4 + * * -- statements/MCLowLevelStatements.mc4 + * * -- statements/MCReturnStatements.mc4 + * + * and the composition of all statement grammars to full Java: + * * -- -- statements/MCFullJavaStatements.mc4 + * +*/ + +component grammar MCExceptionStatements + extends MCCommonStatements { + +// Three different variants of the TryStatement differ in the +// optionality of their elements. + +TryStatement1 implements MCStatement + = "try" + core:MCJavaBlock + CatchClause+ + ("finally" finally:MCJavaBlock)? ; + +TryStatement2 implements MCStatement + = "try" + core:MCJavaBlock + CatchClause* + ("finally" finally:MCJavaBlock) ; + +TryStatement3 implements MCStatement + = "try" "(" (TryLocalVariableDeclaration || ";")+ ";"? ")" + core:MCJavaBlock + CatchClause* + ("finally" finally:MCJavaBlock)? ; + +TryLocalVariableDeclaration + = JavaModifier* MCType DeclaratorId "=" Expression ; + +CatchClause + = "catch" "(" JavaModifier* CatchTypeList Name ")" MCJavaBlock ; + +CatchTypeList + = (MCQualifiedName || "|")+ ; + +ThrowStatement implements MCStatement + = "throw" Expression ";" ; + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/statements/MCExceptionStatements.mlc b/monticore-grammar/src/main/grammars/de/monticore/statements/MCExceptionStatements.mlc new file mode 100644 index 0000000000..58ae7635c6 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/statements/MCExceptionStatements.mlc @@ -0,0 +1,24 @@ +package de.monticore.statements; + +mlc MCExceptionStatements { + + //export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/statements/MCExceptionStatements.mc4"; + } + + //export the pretty printer + export "$projectDir/src/main/java" { + include "de/monticore/statements/prettyprint/MCExceptionStatements*.java"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/statements/mcexceptionstatements/**.java"; + } + + promote { + mlc "de.monticore.statements.MCCommonStatements"; + } + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/statements/MCFullJavaStatements.mc4 b/monticore-grammar/src/main/grammars/de/monticore/statements/MCFullJavaStatements.mc4 new file mode 100644 index 0000000000..3f2bcdabc0 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/statements/MCFullJavaStatements.mc4 @@ -0,0 +1,39 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements; + +/* This is a MontiCore stable grammar. + * Adaptations -- if any -- are conservative. */ + +import de.monticore.statements.*; + +/** + * This grammar defines the all Java statements. + * This is neither a generalized approximation nor a restricted overapproximation, + * but exact. + * Because all nonterminals are inherited the grammar is basically empty. + * + * This grammar is part of a hierarchy of statements, namely + * * statements/MCStatementsBasis.mc4 + * * -- statements/MCAssertStatements.mc4 + * * -- statements/MCVarDeclarationStatements.mc4 + * * -- -- statements/MCArrayStatements.mc4 + * * -- -- statements/MCCommonStatements.mc4 + * * -- -- -- statements/MCExceptionStatements.mc4 + * * -- -- -- statements/MCSynchronizedStatements.mc4 + * * -- statements/MCLowLevelStatements.mc4 + * * -- statements/MCReturnStatements.mc4 + * + * and the composition of all statement grammars to full Java: + * * -- -- statements/MCFullJavaStatements.mc4 + * +*/ + +component grammar MCFullJavaStatements extends + MCAssertStatements, + MCExceptionStatements, + MCLowLevelStatements, + MCReturnStatements, + MCSynchronizedStatements, + MCArrayStatements { + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/statements/MCFullJavaStatements.mlc b/monticore-grammar/src/main/grammars/de/monticore/statements/MCFullJavaStatements.mlc new file mode 100644 index 0000000000..893db3f6ff --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/statements/MCFullJavaStatements.mlc @@ -0,0 +1,24 @@ +package de.monticore.statements; + +mlc MCFullJavaStatements { + + //export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/statements/MCFullJavaStatements.mc4"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/statements/mcfulljavastatements/**.java"; + } + + promote { + mlc "de.monticore.statements.MCAssertStatements"; + mlc "de.monticore.statements.MCExceptionStatements"; + mlc "de.monticore.statements.MCLowLevelStatements"; + mlc "de.monticore.statements.MCReturnStatements"; + mlc "de.monticore.statements.MCSynchronizedStatements"; + mlc "de.monticore.statements.MCArrayStatements"; + } + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/statements/MCLowLevelStatements.mc4 b/monticore-grammar/src/main/grammars/de/monticore/statements/MCLowLevelStatements.mc4 new file mode 100644 index 0000000000..fb9dc01c1d --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/statements/MCLowLevelStatements.mc4 @@ -0,0 +1,44 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements; + +/* This is a MontiCore stable grammar. + * Adaptations -- if any -- are conservative. */ + +import de.monticore.statements.*; +import de.monticore.*; + +/** + * This grammar defines three low-level statements that Java provides. + * It contains the break and continue statements and the possibility + * to label a statement. + * + * This grammar is part of a hierarchy of statements, namely + * * statements/MCStatementsBasis.mc4 + * * -- statements/MCAssertStatements.mc4 + * * -- statements/MCVarDeclarationStatements.mc4 + * * -- -- statements/MCArrayStatements.mc4 + * * -- -- statements/MCCommonStatements.mc4 + * * -- -- -- statements/MCExceptionStatements.mc4 + * * -- -- -- statements/MCSynchronizedStatements.mc4 + * * -- statements/MCLowLevelStatements.mc4 + * * -- statements/MCReturnStatements.mc4 + * + * and the composition of all statement grammars to full Java: + * * -- -- statements/MCFullJavaStatements.mc4 + * +*/ + +component grammar MCLowLevelStatements + extends MCStatementsBasis, + de.monticore.MCBasics { + +LabelledBreakStatement implements MCStatement + = "break" label:Name@Label? ";" ; + +ContinueStatement implements MCStatement + = "continue" label:Name@Label? ";" ; + +symbol Label implements MCStatement + = Name ":" MCStatement ; + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/statements/MCLowLevelStatements.mlc b/monticore-grammar/src/main/grammars/de/monticore/statements/MCLowLevelStatements.mlc new file mode 100644 index 0000000000..e68edb4d91 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/statements/MCLowLevelStatements.mlc @@ -0,0 +1,24 @@ +package de.monticore.statements; + +mlc MCLowLevelStatements { + + //export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/statements/MCLowLevelStatements.mc4"; + } + + //export the pretty printer + export "$projectDir/src/main/java" { + include "de/monticore/statements/prettyprint/MCLowLevelStatements*.java"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/statements/mclowlevelstatements/**.java"; + } + + promote { + mlc "de.monticore.statements.MCStatementsBasis"; + } + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/statements/MCReturnStatements.mc4 b/monticore-grammar/src/main/grammars/de/monticore/statements/MCReturnStatements.mc4 new file mode 100644 index 0000000000..e101a057b7 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/statements/MCReturnStatements.mc4 @@ -0,0 +1,36 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements; + +/* This is a MontiCore stable grammar. + * Adaptations -- if any -- are conservative. */ + +import de.monticore.statements.*; +import de.monticore.expressions.*; + +/** + * This grammar defines Java's return statement. + * + * This grammar is part of a hierarchy of statements, namely + * * statements/MCStatementsBasis.mc4 + * * -- statements/MCAssertStatements.mc4 + * * -- statements/MCVarDeclarationStatements.mc4 + * * -- -- statements/MCArrayStatements.mc4 + * * -- -- statements/MCCommonStatements.mc4 + * * -- -- -- statements/MCExceptionStatements.mc4 + * * -- -- -- statements/MCSynchronizedStatements.mc4 + * * -- statements/MCLowLevelStatements.mc4 + * * -- statements/MCReturnStatements.mc4 + * + * and the composition of all statement grammars to full Java: + * * -- -- statements/MCFullJavaStatements.mc4 + * +*/ + +component grammar MCReturnStatements + extends MCStatementsBasis, + ExpressionsBasis { + +ReturnStatement implements MCStatement + = "return" Expression? ";" ; + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/statements/MCReturnStatements.mlc b/monticore-grammar/src/main/grammars/de/monticore/statements/MCReturnStatements.mlc new file mode 100644 index 0000000000..3397f0396a --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/statements/MCReturnStatements.mlc @@ -0,0 +1,25 @@ +package de.monticore.statements; + +mlc MCReturnStatements { + + //export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/statements/MCReturnStatements.mc4"; + } + + //export the pretty printer + export "$projectDir/src/main/java" { + include "de/monticore/statements/prettyprint/MCReturnStatements*.java"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/statements/mcreturnstatements/**.java"; + } + + promote { + mlc "de.monticore.statements.MCStatementsBasis"; + mlc "de.monticore.expressions.ExpressionsBasis"; + } + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/statements/MCStatementsBasis.mc4 b/monticore-grammar/src/main/grammars/de/monticore/statements/MCStatementsBasis.mc4 new file mode 100644 index 0000000000..fe4b1fee98 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/statements/MCStatementsBasis.mc4 @@ -0,0 +1,57 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements; + +/* This is a MontiCore stable grammar. + * Adaptations -- if any -- are conservative. */ + +/** + * This grammar defines the core interface for statements. + * A hierarchy of conservative extensions to this grammar realize + * these interfaces in various forms. + * + * The language developer may choose the subset of statements + * of interest + * + * This modularity of statements greatly eases + * the reuse of statements in languages similar to Java. + * + * This grammar is part of a hierarchy of statements, namely + * * statements/MCStatementsBasis.mc4 + * * -- statements/MCAssertStatements.mc4 + * * -- statements/MCVarDeclarationStatements.mc4 + * * -- -- statements/MCArrayStatements.mc4 + * * -- -- statements/MCCommonStatements.mc4 + * * -- -- -- statements/MCExceptionStatements.mc4 + * * -- -- -- statements/MCSynchronizedStatements.mc4 + * * -- statements/MCLowLevelStatements.mc4 + * * -- statements/MCReturnStatements.mc4 + * + * and the composition of all statement grammars to full Java: + * * -- -- statements/MCFullJavaStatements.mc4 + * +*/ + +component grammar MCStatementsBasis { + +/** + * MCBlockStatements are typically the staments possible within blocks, such as + * { ... block ... } + * These MCBlockStatements contain all usuall statements plus the possibility + * to define local variables, such as: int a = 7; +*/ + +interface MCBlockStatement; + +/** + * MCStatement is the basic interface for all kinds of statements, + * which are defined in sub-grammars. + * It is explicitly meant for extension. + * Like in Java, ordinary MCStatements do not allow to introduce new + * local variables (this is only part of the MCBlockStatements) +*/ + +interface MCStatement extends MCBlockStatement; + +interface MCModifier; + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/statements/MCStatementsBasis.mlc b/monticore-grammar/src/main/grammars/de/monticore/statements/MCStatementsBasis.mlc new file mode 100644 index 0000000000..15c4e9c3f9 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/statements/MCStatementsBasis.mlc @@ -0,0 +1,19 @@ +package de.monticore.statements; + +mlc MCStatementsBasis { + + //export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/statements/MCStatementsBasis.mc4"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/statements/mcstatementsbasis/**.java"; + } + + promote { + mlc "de.monticore.MCBasics"; + } + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/statements/MCSynchronizedStatements.mc4 b/monticore-grammar/src/main/grammars/de/monticore/statements/MCSynchronizedStatements.mc4 new file mode 100644 index 0000000000..ed9fd9d00c --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/statements/MCSynchronizedStatements.mc4 @@ -0,0 +1,37 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements; + +/* This is a MontiCore stable grammar. + * Adaptations -- if any -- are conservative. */ + +import de.monticore.statements.*; + +/** + * This grammar defines the Java-like synchronized statement. + * The statement is extracted into its own grammar because in + * many languages that use statements, synchronization is done in + * different forms and sucgh this statement isn't needed. + * + * This grammar is part of a hierarchy of statements, namely + * * statements/MCStatementsBasis.mc4 + * * -- statements/MCAssertStatements.mc4 + * * -- statements/MCVarDeclarationStatements.mc4 + * * -- -- statements/MCArrayStatements.mc4 + * * -- -- statements/MCCommonStatements.mc4 + * * -- -- -- statements/MCExceptionStatements.mc4 + * * -- -- -- statements/MCSynchronizedStatements.mc4 + * * -- statements/MCLowLevelStatements.mc4 + * * -- statements/MCReturnStatements.mc4 + * + * and the composition of all statement grammars to full Java: + * * -- -- statements/MCFullJavaStatements.mc4 + * +*/ + +component grammar MCSynchronizedStatements + extends MCCommonStatements { + +SynchronizedStatement implements MCStatement + = "synchronized" "(" Expression ")" MCJavaBlock ; + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/statements/MCSynchronizedStatements.mlc b/monticore-grammar/src/main/grammars/de/monticore/statements/MCSynchronizedStatements.mlc new file mode 100644 index 0000000000..ff0686d782 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/statements/MCSynchronizedStatements.mlc @@ -0,0 +1,24 @@ +package de.monticore.statements; + +mlc MCSynchronizedStatements { + + //export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/statements/MCSynchronizedStatements.mc4"; + } + + //export the pretty printer + export "$projectDir/src/main/java" { + include "de/monticore/statements/prettyprint/MCSynchronizedStatements*.java"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/statements/mcsynchronizedstatements/**.java"; + } + + promote { + mlc "de.monticore.statements.MCCommonStatements"; + } + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/statements/MCVarDeclarationStatements.mc4 b/monticore-grammar/src/main/grammars/de/monticore/statements/MCVarDeclarationStatements.mc4 new file mode 100644 index 0000000000..b48225e2fb --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/statements/MCVarDeclarationStatements.mc4 @@ -0,0 +1,60 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements; + +/* This is a MontiCore stable grammar. + * Adaptations -- if any -- are conservative. */ + +import de.monticore.statements.*; +import de.monticore.expressions.*; +import de.monticore.symbols.*; +import de.monticore.types.*; + +/** + * This grammar defines Java's mechanism for the introduction + * of local variables with a type and + * the possibility to initialize the + * variable, e.g. private int a = ... + * + * This grammar is part of a hierarchy of statements, namely + * * statements/MCStatementsBasis.mc4 + * * -- statements/MCAssertStatements.mc4 + * * -- statements/MCVarDeclarationStatements.mc4 + * * -- -- statements/MCArrayStatements.mc4 + * * -- -- statements/MCCommonStatements.mc4 + * * -- -- -- statements/MCExceptionStatements.mc4 + * * -- -- -- statements/MCSynchronizedStatements.mc4 + * * -- statements/MCLowLevelStatements.mc4 + * * -- statements/MCReturnStatements.mc4 + * + * and the composition of all statement grammars to full Java: + * * -- -- statements/MCFullJavaStatements.mc4 + * +*/ + +component grammar MCVarDeclarationStatements + extends MCStatementsBasis, + MCBasicTypes, + ExpressionsBasis, + OOSymbols { + + LocalVariableDeclarationStatement implements MCBlockStatement + = LocalVariableDeclaration ";" ; + + LocalVariableDeclaration + = MCModifier* MCType (VariableDeclarator || ",")+ ; + + VariableDeclarator + = Declarator ("=" VariableInit)? ; + + interface Declarator extends Field + = Name; + + DeclaratorId implements Declarator + = Name; + + interface VariableInit; + + SimpleInit implements VariableInit + = Expression; + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/statements/MCVarDeclarationStatements.mlc b/monticore-grammar/src/main/grammars/de/monticore/statements/MCVarDeclarationStatements.mlc new file mode 100644 index 0000000000..bcf18a60c4 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/statements/MCVarDeclarationStatements.mlc @@ -0,0 +1,33 @@ +package de.monticore.statements; + +mlc MCVarDeclarationStatements { + + //export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/statements/MCVarDeclarationStatements.mc4"; + } + + //export the handwritten TOP-classes and pretty printer + export "$projectDir/src/main/java" { + include "de/monticore/statements/mcvardeclarationstatements/**.java"; + include "de/monticore/statements/prettyprint/MCVarDeclarationStatements*.java"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/statements/mcvardeclarationstatements/**.java"; + } + + promote { + mlc "de.monticore.statements.MCStatementsBasis"; + mlc "de.monticore.types.MCBasicTypes"; + mlc "de.monticore.expressions.ExpressionsBasis"; + mlc "de.monticore.symbols.OOSymbols"; + } + + promote { + //must be inlcuded to assign SymTypeExpression to the Declarator (FieldSymbol) of ASTLocalVariableDeclaration + include "$projectDir/src/main/java/de/monticore/grammar/grammar_withconcepts/FullSynthesizeFromMCSGT4Grammar.java"; + } + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/symbols/BasicSymbols.mc4 b/monticore-grammar/src/main/grammars/de/monticore/symbols/BasicSymbols.mc4 new file mode 100644 index 0000000000..ea2c1dbd86 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/symbols/BasicSymbols.mc4 @@ -0,0 +1,95 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symbols; + +/* This is a MontiCore stable grammar. + * Adaptations -- if any -- are conservative. */ + +import de.monticore.*; + +/** + * The grammar introduces reusable symbols for + * Types (of all kinds), + * Functions, + * Variables and + * TypeVariables + * + * The symbols are of general nature and do not precluse (and thus also + * not assist) static, private or other specialities. + * Extensions of these symbols can be found in the subgrammar + * OOSymbols that add such specific elements. + * + * This grammar is used to generate an abstract syntax + * (and especially symboltable infrastructure) only. + * These AST-classes are then used to store typing information + * in the SymbolTable. + * + * These symbols can be used in other grammars, when actual implementations + * of these symbols become available or these symbols need to be imported + * from other artifacts. + * + */ + +component grammar BasicSymbols extends de.monticore.MCBasics { + + /*=================================================================*/ + + /** + * A diagram has a name and carries a varying set of symbols . + * + * It may also contain potentially variables (record elements, fields, + * attributes, etc.) or associated functions (the OO methods). + * These are represented as Symbols in the body of the type + * and thus made available through the associated scope object. + */ + interface symbol Diagram = Name; + + + /*=================================================================*/ + + /** + * A type has a name and possible supertypes. + * + * It may also contain potentially variables (record elements, fields, + * attributes, etc.) or associated functions (the OO methods). + * These are represented as Symbols in the body of the type + * and thus made available through the associated scope object. + */ + interface scope symbol Type = Name ; + + symbolrule Type = + superTypes: de.monticore.types.check.SymTypeExpression* ; + + /*=================================================================*/ + + /** + * A typeVar is used as unbound argument in generic types, + * e.g. Map has a type variable T + * TypeVars are instantiated with concrete types upon use. + */ + interface symbol TypeVar extends Type = Name ; + + /*=================================================================*/ + + /** + * A Variable (Local variable, Java Attribute, etc.) has a type + */ + interface symbol Variable = Name ; + + symbolrule Variable = + type: de.monticore.types.check.SymTypeExpression + isReadOnly: boolean ; + + /*=================================================================*/ + + /** + * A function is defined by its signature. + * A signature consists of a return type, and a list of parameters. + * The parameters are stored as variable symbols (in an associated scope). + */ + interface scope symbol Function = Name ; + + symbolrule Function = + isElliptic: boolean + type: de.monticore.types.check.SymTypeExpression ; + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/symbols/BasicSymbols.mlc b/monticore-grammar/src/main/grammars/de/monticore/symbols/BasicSymbols.mlc new file mode 100644 index 0000000000..5ea02ec5ca --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/symbols/BasicSymbols.mlc @@ -0,0 +1,29 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symbols; + +mlc BasicSymbols { + + // export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/symbols/BasicSymbols.mc4"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/symbols/basicsymbols/**.java"; + } + + // export all handwritten TOP-mechanism extensions + export "$projectDir/src/main/java" { + include "de/monticore/symbols/basicsymbols/**.java"; + } + + promote mlc "de.monticore.MCBasics"; + + // locally allow using SymTypeExpressions + uses { + include "$projectDir/src/main/java/de/monticore/types/check/SymTypeExpression.java"; + include "$projectDir/src/main/java/de/monticore/types/check/SymTypeExpressionDeSer.java"; + } + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/symbols/CompSymbols.mc4 b/monticore-grammar/src/main/grammars/de/monticore/symbols/CompSymbols.mc4 new file mode 100644 index 0000000000..3702cdfc73 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/symbols/CompSymbols.mc4 @@ -0,0 +1,31 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symbols; + +/** + * Common symbols for component-connector ADLs. + */ +component grammar CompSymbols extends de.monticore.symbols.BasicSymbols { + + interface scope symbol Component = Name; + + symbolrule Component = + superComponents: de.monticore.types.check.CompKindExpression* + ; + + interface symbol Subcomponent = Name; + + symbolrule Subcomponent = + type: de.monticore.types.check.CompKindExpression + ; + + interface symbol Port = Name; + + symbolrule Port = + type: de.monticore.types.check.SymTypeExpression + incoming: boolean + outgoing: boolean + timing: de.monticore.symbols.compsymbols._symboltable.Timing + stronglyCausal: Boolean + ; + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/symbols/CompSymbols.mlc b/monticore-grammar/src/main/grammars/de/monticore/symbols/CompSymbols.mlc new file mode 100644 index 0000000000..adc0eaa3c1 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/symbols/CompSymbols.mlc @@ -0,0 +1,27 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symbols; + +mlc CompSymbols { + + // export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/symbols/ComponentSymbols.mc4"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/symbols/compsymbols/**.java"; + } + + // export all handwritten TOP-mechanism extensions + export "$projectDir/src/main/java" { + include "de/monticore/symbols/compsymbols/**.java"; + } + + promote mlc "de.monticore.MCBasics"; + + uses { + include "$projectDir/src/main/java/de/monticore/types/check/ComponentExpression.java"; + } + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/symbols/OOSymbols.mc4 b/monticore-grammar/src/main/grammars/de/monticore/symbols/OOSymbols.mc4 new file mode 100644 index 0000000000..9d60bb4aa4 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/symbols/OOSymbols.mc4 @@ -0,0 +1,103 @@ + +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symbols; + +/* This is a MontiCore stable grammar. + * Adaptations -- if any -- are conservative. */ + +import de.monticore.symbols.*; + +/** + * The grammar introduces reusable symbols for + * OOTypes, + * Methods, and + * Fields + * which are specializations of the alredy defined + * symbols from BasicSymbols.mc4 + * + * This grammar is used to generate an abstract syntax + * (and especially symboltable infrastructure) only. + * These AST-classes are then used to store typing information + * in the SymbolTable. + * + * Here we mainly add the possibilities to store static, private, etc. flags + * and extend the signature of methods for elliptic extensibility. + * These extensions are rather common in OO (and especially Java-like) + * languages. + * + * These symbols can be used elsewhere when actual implementations + * of these symbols become available. + * + * A symboltable can export these symbols, while they are imported + * (and thus used) as their more abstract counterparts from BasicSymbols. + * +*/ + +component grammar OOSymbols extends BasicSymbols { + + /*=================================================================*/ + + /** + * A type (in an OO sense) has a name, + * methods, fields, supertypes. + * + * It may also contain potentially fields and OO methods. + * These are represented as Symbols in the body of the type + * and thus made available through the associated scope object. + * + * The new symbol OOTypeSymbol mainly adds OO specific properties. + * + */ + interface scope symbol OOType extends Type = Name ; + + symbolrule OOType = + isClass: boolean + isInterface: boolean + isEnum: boolean + isAbstract: boolean + isPrivate: boolean + isProtected: boolean + isPublic: boolean + isStatic: boolean + isFinal: boolean ; + + /*=================================================================*/ + + /** + * A Field (local variable, method parameter, Java attribute) has a type + * and a visibility. It may also be used in isolation (static). + * + * The new symbol FieldSymbol inherits from VariableSymbol + * and mainly adds OO specific properties. + */ + interface symbol Field extends Variable = Name ; + + symbolrule Field = + isPrivate: boolean + isProtected: boolean + isPublic: boolean + isStatic:boolean + isFinal: boolean + isDerived: boolean ; + + /*=================================================================*/ + + /** + * A Method has a return type, and a list of parameters, which + * are stored as field symbols. + * + * The new symbol MethodSymbol inherits from FunctionSymbol and + * mainly adds OO specific properties. + */ + interface symbol Method extends Function = Name ; + + symbolrule Method = + isConstructor: boolean + isMethod: boolean + isPrivate: boolean + isProtected: boolean + isPublic: boolean + isStatic: boolean + isFinal: boolean ; + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/symbols/OOSymbols.mlc b/monticore-grammar/src/main/grammars/de/monticore/symbols/OOSymbols.mlc new file mode 100644 index 0000000000..0a8918f692 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/symbols/OOSymbols.mlc @@ -0,0 +1,29 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symbols; + +mlc OOSymbols { + + // export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/symbols/OOSymbols.mc4"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/symbols/oosymbols/**.java"; + } + + // export all handwritten TOP-mechanism extensions + export "$projectDir/src/main/java" { + include "de/monticore/symbols/oosymbols/**.java"; + } + + promote mlc "de.monticore.symbols.BasicSymbols"; + + // locally allow using SymTypeExpressions + uses { + include "$projectDir/src/main/java/de/monticore/types/check/SymTypeExpression.java"; + include "$projectDir/src/main/java/de/monticore/types/check/SymTypeExpressionDeSer.java"; + } + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/tf/ODRuleGeneration.mc4 b/monticore-grammar/src/main/grammars/de/monticore/tf/ODRuleGeneration.mc4 new file mode 100644 index 0000000000..e96f78afb9 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/tf/ODRuleGeneration.mc4 @@ -0,0 +1,124 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf; + +component grammar ODRuleGeneration extends de.monticore.types.MCFullGenericTypes { + + TransformationStructure = + package:Name + imports:Name* + classname:Name + Pattern + Replacement + constraintExpression:Name + doStatement:Name + undoStatement:Name + assignments:Name* + Variable*; + + + Pattern = + matchingObjects:MatchingObject* + lHSObjects:MatchingObject* + assoc:Association* + objectConditions:ObjectCondition* + linkConditions:LinkCondition* + types:Name*; //a Set? + + Replacement = + requirements:Requirement* + changes:Change* + createObjects:CreateOperation* + deleteObjects:DeleteOperation*; + + Variable = Name type:Name; + + MatchingObject = + objectName:Name + type:Name + listtype:Name? + listimpltype:Name? + ["LHSObject"]? + ["NotObject"]? + ["OptObject"]? + ["ListObject"]? + innerLinkObjectNames:Name*; + + Association = + Name + gname:Name; + + Condition = + objectName:Name + conditionString:Name + Dependency?; + + ObjectCondition extends Condition; + + LinkCondition extends Condition = + linktype:Name; + + Dependency = content:Name; + + Requirement = + type:Name + attribute:Name + getter:Name + object:Name; + + Change = + ["objectWithinOpt"]? + ["objectWithinList"]? + objectGetter:Name + ["valueWithinOpt"]? + ["valueWithinList"]? + valueGetter:Name? + ["oldValueWithinOpt"]? + ["oldValueWithinList"]? + oldValueGetter:Name? + + ["primitiveType"]? + ["attributeIterated"]? + ["attributeOptional"]? + ["copy"]? + ["objectInList"]? + ["valueStringList"]? + ["valueListObject"]? + ["composite"]? + type:Name + objectType:Name? + objectName:Name + attributeName:Name + value:Name? + oldValue:Name? + getter:Name + getIsPresent:Name? + setter:Name + unsetter:Name + simpleType:Name + boxingType:Name? + valueType:Name + insertPosition:Name? + genericType:Name; + + CreateOperation = + Name + type:Name + simpleType:Name + factoryName:Name; + + DeleteOperation = + Name + type:Name + ["list"]? + simpleType:Name + grammarType:Name + typepackage:Name + parentsSet:Name*; + + ChangeOperation = + setAttributeOperations:Change* + deleteOperations:DeleteOperation* + createOperations:CreateOperation*; + + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/tf/ODRuleGeneration.mlc b/monticore-grammar/src/main/grammars/de/monticore/tf/ODRuleGeneration.mlc new file mode 100644 index 0000000000..e357b1687e --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/tf/ODRuleGeneration.mlc @@ -0,0 +1,24 @@ +package de.monticore.tf; + +mlc ODRuleGeneration { + + //export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/tf/ODRuleGeneration.mc4"; + } + + //export handwritten top classes + export "$projectDir/src/main/java" { + include "de/monticore/tf/odrulegeneration/**.java"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/tf/odrulegeneration/**.java"; + } + + promote { + mlc "de.monticore.types.MCFullGenericTypes"; + } + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/tf/ODRules.mc4 b/monticore-grammar/src/main/grammars/de/monticore/tf/ODRules.mc4 new file mode 100644 index 0000000000..4d1ee251d7 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/tf/ODRules.mc4 @@ -0,0 +1,121 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf; + +grammar ODRules extends de.monticore.tf.TFBasisExts, + de.monticore.literals.MCCommonLiterals, + de.monticore.literals.MCJavaLiterals, //ToDo: Remove all int references in the rule2od + de.monticore.UMLStereotype { + + astrule ODRule = + variables:de.monticore.tf.rule2od.Variable2AttributeMap + name:String + method public String getGrammarPackageName() { + return de.se_rwth.commons.Joiners.DOT.join(getGrammarPackageList()); + } + ; + + ODRule = + ("package" package:(Name& || ".")+ ";")? + MCImportStatement* + "grammar" grammarPackage:(Name& || ".")* "."? grammarName:Name ";" + "pattern" lhs:ODDefinition + ("replacement" rhs:ODDefinition)? + ("folding" "{" FoldingSet* "}")? + ("where" "{" constraint:Expression "}")? + ("assign" "{" Assignment* "}")? + ("do" doBlock:MCJavaBlock )? + ("undo" undoBlock:MCJavaBlock )?; + + ODRuleBlock = "{" ODRule "}"; + + /** + * A folding tuple is a list of at least two foldable objects in parentheses + */ + FoldingSet = + "("objectNames:Name ("," objectNames:Name)+")"; + + Assignment = lhs:Name "=" rhs:Expression ";"; + + + /** ASTODDefinition represents a UML Objectdiagram + @attribute completeness Optional Comleteness of this Objectdiagramm + @attribute stereotype Optional Stereotype + @attribute name Name of this Objectdiagram + @attribute oDObjects List of Objects of this Objectdiagram + @attribute oDLinks List of Links of this Objectdiagram + @attribute invaritants List of Invaritants of this Objectdiagram + */ + symbol scope ODDefinition = + ("objectdiagram" | "astobjectdiagram") Name + "{" + ( + oDObject:ODObject + | + oDLink:ODLink + )* + "}"; + + + /** ASTODAttribute represents an Attribute of an Object + @attribute type Type of this Attribute + @attribute name Name of this Attribute + @attribute singleValue Value of this Attribute + */ + symbol ODAttribute = + MCType? + Name + attributeCardinality:Cardinality + ("=" (singleValue:Expression valueOptional:["[0..1]"]? | list:ArrayInit))? ";"; + + /** ASTODObject represents an Object in a UML Objectdiagram + @attribute stereotype Optional Stereotype of this Object + @attribute name Optional Name of this Object + @attribute type Optional Type of this Object + @attribute attributes List of Attributes of this Object + @attribute innerLinks List of inner Links of this Object + @attribute valueCollections List of value collections of this Object + */ + symbol scope ODObject = + Stereotype? + (Name (":" type:MCType)? | (":" type:MCType)) + ( + ("{" (attributes:ODAttribute | innerLinks:ODInnerLink )* "}") + | + ";" + ); + + /** ASTODLink represents a Link between Objects + @attribute stereotype Optional Stereotype + @attribute Name Name of the Association of this Link + @attribute leftReferenceNames List of References of the Objects on the + left side of this Link + @attribute leftRole Role of Objects on the Links left side + @attribute rightRole Role of Objects on the Links right side + @attribute rightReferenceNames List of References of the Objects on the + right side of this Link + */ + ODLink = + Stereotype? + ("composition" | ["link"]) + Name? + leftReferenceName:(MCQualifiedName || ",")+ + ("(" leftRole:Name ")")? + "--" + ("(" rightRole:Name ")")? + attributeCardinality:Cardinality + rightReferenceName:(MCQualifiedName || ",")+ + ";"; + + /** ASTCardinality represents a Cardinality in a Classdiagram + @attribute many True if "*" is set as Cardinality + @attribute one True if [1] is set as Cardinality + @attribute oneToMany True if [1..*] is set as Cardinality + @attribute optional True if [0..1] is set as Cardinality + */ + Cardinality = many:["[*]"] | one:["[1]"] | oneToMany:["[1..*]"] | optional:["[0..1]"]; + + + ODInnerLink = + (linkName:Name "=")? ODObject; + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/tf/ODRules.mlc b/monticore-grammar/src/main/grammars/de/monticore/tf/ODRules.mlc new file mode 100644 index 0000000000..bca01e882f --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/tf/ODRules.mlc @@ -0,0 +1,43 @@ +package de.monticore.tf; + +mlc ODRules { + + //export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/tf/ODRules.mc4"; + } + + //export handwritten top classes + export "$projectDir/src/main/java" { + include "de/monticore/tf/odrules/**.java"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/tf/odrules/**.java"; + } + + // promote using the JDK except for reflection + promote { + mlc "de.monticore.types.MCFullGenericTypes"; + mlc "de.monticore.tf.TFBasisExts"; + mlc "de.monticore.literals.MCJavaLiterals"; + mlc "de.monticore.UMLStereotype"; + + include "$mp/de/monticore/tf/rule2od/**.class"; + include "$mp/com/google/common/**.class"; + } + + uses { // TODO check whether this should be allowed + include "$projectDir/src/main/java/de/monticore/grammar/grammar/_symboltable/MCGrammarSymbol.java"; + include "$projectDir/src/main/java/de/monticore/grammar/grammar/_symboltable/ProdSymbol.java"; + include "$projectDir/src/main/java/de/monticore/grammar/grammar/_symboltable/RuleComponentSymbol.java"; + include "$projectDir/target/generated-sources/monticore/sourcecode/de/monticore/tf/odrulegeneration/ODRuleGenerationMill.java"; + include "$projectDir/target/generated-sources/monticore/sourcecode/de/monticore/tf/odrulegeneration/_ast/ASTChangeOperation.java"; + include "$projectDir/target/generated-sources/monticore/sourcecode/de/monticore/tf/odrulegeneration/_ast/ASTDependency.java"; + include "$projectDir/target/generated-sources/monticore/sourcecode/de/monticore/tf/odrulegeneration/_ast/ASTLinkCondition.java"; + include "$projectDir/target/generated-sources/monticore/sourcecode/de/monticore/tf/odrulegeneration/_ast/ASTMatchingObject.java"; + include "$projectDir/target/generated-sources/monticore/sourcecode/de/monticore/tf/odrulegeneration/_ast/ASTObjectCondition.java"; + } + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/tf/TFBasisExts.mc4 b/monticore-grammar/src/main/grammars/de/monticore/tf/TFBasisExts.mc4 new file mode 100644 index 0000000000..d3e1e0d642 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/tf/TFBasisExts.mc4 @@ -0,0 +1,16 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf; + +component grammar TFBasisExts extends de.monticore.JavaLight, + de.monticore.types.MCSimpleGenericTypes, + de.monticore.literals.MCCommonLiterals { + @Override + ExtType = MCType; + @Override + ExtReturnType = MCReturnType; + @Override + ExtTypeArgument = MCTypeArgument; + @Override + ExtTypeParameters = MCType; + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/tf/TFBasisExts.mlc b/monticore-grammar/src/main/grammars/de/monticore/tf/TFBasisExts.mlc new file mode 100644 index 0000000000..cec42c4910 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/tf/TFBasisExts.mlc @@ -0,0 +1,22 @@ +package de.monticore.tf; + +mlc TFBasisExts { + + //export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/tf/TFBasisExts.mc4"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/tf/tfbasisexts/**.java"; + } + + // promote using the JDK except for reflection + promote { + mlc "de.monticore.types.MCSimpleGenericTypes"; + mlc "de.monticore.literals.MCCommonLiterals"; + mlc "de.monticore.JavaLight"; + } + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/tf/TFCommons.mc4 b/monticore-grammar/src/main/grammars/de/monticore/tf/TFCommons.mc4 new file mode 100644 index 0000000000..33a454f3ba --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/tf/TFCommons.mc4 @@ -0,0 +1,109 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf; + +component grammar TFCommons extends de.monticore.tf.TFBasisExts { + + // A specific identifier production, implementing TfIdentifier, will be derived for every lexical production + interface TfIdentifier = identifierSchema:TFSchema? newIdentifierSchema:TFSchema? ; // ASTRules are used to add components/attributes + // These productions will follow this format (for an exemplary lexical production LexProd): + // identifierToken:LexProd - only match the token + // | ( "[[" identifierToken:LexProd ":-" (newIdentifierToken:LexProd | newIdentifierSchema:TFSchema )? "]]" ") - replace the token with either a new one, a schema-variable, or nothing + // | ( "[[" (newIdentifierToken:LexProd | newIdentifier:TFSchema)? ":-" newIdentifierToken:LexProd "]]" ") - replace a token, schema-variable, or nothing with a new token + // | "LexProd" identifierSchema:TFSchema ";" + // Extending rule 3a of [Hoe18], not only all Names (and Strings) are replaced, but now every lexical production with its specific tfidentifier + + + // Similar to Names, describes schema variables (either by name or dont-care), but requires the $ + TFSchema = {noSpace(2)}? "$" Name; + + + // The replacement of TFSchema with another TFSchema MUST only be possible in the Name-identifier (as otherwise every literal pattern would match for a name) + TfIdentifierName implements TfIdentifier = + (identifierToken:Name) + | + ("[[" (identifierToken:Name | identifierSchema: TFSchema )? ":-" (newIdentifierToken:Name | newIdentifierSchema: TFSchema)? "]]") + | + "Name" identifierToken:Name ";"; + + TFFolding = "folding" "{" FoldingSet* "}"; + + /** + * A folding tuple is a list of at least two foldable objects in parentheses + */ + FoldingSet = + "(" objectNames:Name ("," objectNames:Name)+ ")"; + + TFAssignments = "assign" "{" Assign* "}"; + + Assign = variable:Name "=" value:Expression ";" ; + + TFWhere = "where" "{" constraint:Expression "}"; + + TFDo = "do" MCJavaBlock ; + + TFUndo = "undo" MCJavaBlock ; + + + ReplacementOp astimplements de.monticore.tf.ast.IReplacementOperator = + ":-" | + (":" (first:"first" | last:"last" | relative:"relative" | inplace:"inplace") "-"); + + astrule ReplacementOp = + method public boolean isFirst() { + return isPresentFirst(); + } + method public boolean isLast() { + return isPresentLast(); + } + method public boolean isInplace() { + return isPresentInplace(); + } + method public boolean isRelative() { + return isPresentRelative(); + } + method public boolean isDefault() { + return !isFirst() && !isLast() && !isRelative(); + } + ; + + astrule TfIdentifier = + identifierToken: String? + newIdentifierToken: String? + method public boolean isPresentIdentifier() { + return this.isPresentIdentifierSchema() || this.isPresentIdentifierToken(); + } + method public String getIdentifier() { + return this.isPresentIdentifierSchema()? '$' + this.getIdentifierSchema().getName() : this.getIdentifierToken(); + } + method public boolean isPresentNewIdentifier() { + return this.isPresentNewIdentifierSchema() || this.isPresentNewIdentifierToken(); + } + method public String getNewIdentifier() { + return this.isPresentNewIdentifierSchema()? this.getNewIdentifierSchema().getName() : this.getNewIdentifierToken(); + } + method public boolean isNewIdentifierFix() { + return isPresentNewIdentifier() && !getNewIdentifier().startsWith("$"); + } + method public boolean isIdentifierFix() { + return isPresentIdentifier() && !getIdentifier().startsWith("$"); + } + method public boolean isIdentifierSchemaVar() { + return isPresentIdentifier() && getIdentifier().startsWith("$")&&!isIdentifierDontCare(); + } + method public boolean isIdentifierDontCare() { + return isPresentIdentifier() && getIdentifier().equals("$_"); + } + method public boolean isNewIdentifierSchemaVar() { + return isPresentNewIdentifier() && getNewIdentifier().startsWith("$") && !isNewIdentifierDontCare(); + } + method public boolean isNewIdentifierDontCare() { + return isPresentNewIdentifier() && getNewIdentifier().equals("$_"); + } + ; + + TFRule = + ((ITFPart)* TFFolding? TFAssignments? TFWhere? TFDo? TFUndo?) | + (("package" package:(Name& || ".")+ ";")? MCImportStatement* ("transformation" Name)? "{" (ITFPart)* TFFolding? TFAssignments? TFWhere? TFDo? TFUndo? "}"); + + interface ITFPart; +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/tf/TFCommons.mlc b/monticore-grammar/src/main/grammars/de/monticore/tf/TFCommons.mlc new file mode 100644 index 0000000000..085d1ff370 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/tf/TFCommons.mlc @@ -0,0 +1,20 @@ +package de.monticore.tf; + +mlc TFCommons { + + //export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/tf/TFCommons.mc4"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/tf/tfcommons/**.java"; + } + + // promote using the JDK except for reflection + promote { + mlc "de.monticore.tf.TFBasisExts"; + } + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/types/MCArrayTypes.mc4 b/monticore-grammar/src/main/grammars/de/monticore/types/MCArrayTypes.mc4 new file mode 100644 index 0000000000..8f5a12947a --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/types/MCArrayTypes.mc4 @@ -0,0 +1,34 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types; + +/* This is a MontiCore stable grammar. + * Adaptations -- if any -- are conservative. */ + +import de.monticore.types.*; + +/** + * This grammar completes the type definitions to + * support arrays like Person[][] + * + * This grammar is part of a hierarchy of types, namely + * * types.MCBasicTypes.mc4 + * * types/MCArrayTypes.mc4 + * * types/MCCollectionTypes.mc4 + * * types/MCSimpleGenericTypes.mc4 + * * types/MCFullGenericTypes.mc4 + * +*/ + +component grammar MCArrayTypes + extends MCBasicTypes { + + /** ASTArrayType introduces array for arbitrary types + */ + MCArrayType implements MCType = + MCType + ("[" "]" {_builder.setDimensions(_builder.getDimensions()+1);} )+; + + // counter dimensions counts the array depth + astrule MCArrayType = + dimensions:int; +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/types/MCArrayTypes.mlc b/monticore-grammar/src/main/grammars/de/monticore/types/MCArrayTypes.mlc new file mode 100644 index 0000000000..eb1dc9d0a8 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/types/MCArrayTypes.mlc @@ -0,0 +1,29 @@ +package de.monticore.types; + +mlc MCArrayTypes { + + //export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/types/MCArrayTypes.mc4"; + } + + //export handwritten code + export "$projectDir/src/main/java" { + include "de/monticore/types/mcarraytypes/**.java"; + include "de/monticore/types/prettyprint/MCArrayTypes**.java"; + include "de/monticore/types/MCArrayTypes**.java"; + + include "de/monticore/types/check/FullSynthesizeFromMCArrayTypes.java"; + include "de/monticore/types/check/SynthesizeSymTypeFromMCArrayTypes.java"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/types/mcarraytypes/**.java"; + } + + promote { + mlc "de.monticore.types.MCBasicTypes"; + } + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/types/MCBasicTypes.mc4 b/monticore-grammar/src/main/grammars/de/monticore/types/MCBasicTypes.mc4 new file mode 100644 index 0000000000..7c42b12b76 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/types/MCBasicTypes.mc4 @@ -0,0 +1,105 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types; + +/* This is a MontiCore stable grammar. + * Adaptations -- if any -- are conservative. */ + +import de.monticore.*; + +/** + * This grammar defines basic types. + * + * This eases the reuse of type structures in languages similar to Java, + * that are somewhat simplified, e.g. without generics. + * + * The grammar contains types from Java, e.g., primitives, void, + * classes (also sometimes called "reference types"). + * + * This grammar is part of a hierarchy of types, namely + * * types.MCBasicTypes.mc4 + * * types/MCArrayTypes.mc4 + * * types/MCCollectionTypes.mc4 + * * types/MCSimpleGenericTypes.mc4 + * * types/MCFullGenericTypes.mc4 + * +*/ + +component grammar MCBasicTypes extends de.monticore.MCBasics { + + /*=================================================================*/ + + /** ASTMCType is the top level interface for all kinds of types + * (except Void). + * It is also an extension point for other forms of types. + */ + interface MCType; + + /*=================================================================*/ + + /** The ASTMCQualifiedName represents a possibly qualified name. + The different parts of a qualified name are separated by '.'; they are + stored in an ASTStringList. + @attribute parts A List of ASTStringList concludes all name parts + */ + MCQualifiedName = + parts:(Name || ".")+; + + /*=================================================================*/ + + /** ASTMCPackageDeclaration represents a package declaration usable + for diagrams + E.g.: package A.b; + */ + MCPackageDeclaration = "package" MCQualifiedName& ";"; + + /** ASTMCImportStatement represents the import list for diagrams + E.g.: import A.b; + E.g.: import A.*; + */ + MCImportStatement = + "import" MCQualifiedName ("." Star:["*"])? ";" ; + + /*=================================================================*/ + /*= Primitives ====================================================*/ + + /** ASTMCPrimitiveType represents every primitive type supported by Java. + The type is not realized by an enumeration, because this form + of definitions can be conservatively extended. + @attribute primitive BOOLEAN, BYTE, CHAR, SHORT, INT, FLOAT, LONG, + or DOUBLE + */ + MCPrimitiveType implements MCType = + primitive: [ "boolean" | "byte" | "short" | "int" + | "long" | "char" |"float" | "double" ]; + + + /*=================================================================*/ + /*=========== Types (Classes, Interfaces in OO) ===================*/ + + /** ASTMCObjectType contains names of freely defined types (which in + * Java are mainly classes, interfaces, enums). + * They may be qualified by package names. + * + * This is also an extension point for generic types. + */ + interface MCObjectType extends MCType; + + /** ASTMCQualifiedType represents types like class or interface types + which could have a qualified name like this: a.b.c.D + */ + MCQualifiedType implements MCObjectType = MCQualifiedName; + + + /*=================================================================*/ + /*= ReturnTypes and void ==========================================*/ + + /* These special types do not implement interface ASTMCType, because + they will not appear in the type calculations etc. + */ + MCReturnType = MCVoidType | MCType; + + /** ASTVoidType represents the return type "void". + */ + MCVoidType = "void"; + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/types/MCBasicTypes.mlc b/monticore-grammar/src/main/grammars/de/monticore/types/MCBasicTypes.mlc new file mode 100644 index 0000000000..5ca9ddc240 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/types/MCBasicTypes.mlc @@ -0,0 +1,33 @@ +package de.monticore.types; + +mlc MCBasicTypes { + + //export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/types/MCBasicTypes.mc4"; + } + + //export handwritten code + export "$projectDir/src/main/java" { + include "de/monticore/types/mcbasictypes/**.java"; + include "de/monticore/types/prettyprint/MCBasicTypes**.java"; + include "de/monticore/types/MCBasicTypes**.java"; + + include "de/monticore/types/check/FullSynthesizeFromMCBasicTypes.java"; + include "de/monticore/types/check/SynthesizeSymTypeFromMCBasicTypes.java"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/types/mcbasictypes/**.java"; + } + + promote { + mlc "de.monticore.MCBasics"; + mlc "de.monticore.symbols.OOSymbols"; + } + + promote { + include "$projectDir/src/main/java/de/monticore/types/check/*.java"; + } +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/types/MCCollectionTypes.mc4 b/monticore-grammar/src/main/grammars/de/monticore/types/MCCollectionTypes.mc4 new file mode 100644 index 0000000000..e19a9cbfa6 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/types/MCCollectionTypes.mc4 @@ -0,0 +1,80 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types; + +/* This is a MontiCore stable grammar. + * Adaptations -- if any -- are conservative. */ + +import de.monticore.types.*; + +/** + * This grammar defines four generics: List<>, Map<,>, Set<> + * and Optional<> on top of basic types. + * + * These four generics correspond to a typical predefined set of generic + * types for example used in connection with UML class diagrams or the + * OCL. + * UML associations typically have those association multiplicities and + * therefore these types are of interest. + * + * This eases the reuse of type structures in languages similar to Java, + * that are somewhat simplified, e.g. without general generics. + * + * This grammar is part of a hierarchy of types, namely + * * types.MCBasicTypes.mc4 + * * types/MCArrayTypes.mc4 + * * types/MCCollectionTypes.mc4 + * * types/MCSimpleGenericTypes.mc4 + * * types/MCFullGenericTypes.mc4 + * +*/ + + +component grammar MCCollectionTypes + extends MCBasicTypes { + + + /*=================================================================*/ + + /** + * MCGenericType covers the following four forms of generics. + * E.g. in visitors it can also be used to restrict to these + * forms of generics even if further generics would be available. + */ + interface MCGenericType extends MCObjectType; + + /*=================================================================*/ + + /* The following definitions use the next-Command and thus + * avoid "List" etc. to become keywords + */ + + MCListType implements MCGenericType <200> = + key("List") "<" MCTypeArgument ">"; + + MCOptionalType implements MCGenericType <200> = + key("Optional") "<" MCTypeArgument ">"; + + MCMapType implements MCGenericType <200> = + key("Map") "<" key:MCTypeArgument "," value:MCTypeArgument ">"; + + MCSetType implements MCGenericType <200> = + key("Set") "<" MCTypeArgument ">"; + + /*=================================================================*/ + + + /** + * ASTMCTypeArgument interface describes arguments of generic types. + * Eg. such as qualified or primitive types. + * + * This is also an extension point for more general type arguments. + */ + interface MCTypeArgument; + + MCBasicTypeArgument implements MCTypeArgument <200> = + MCQualifiedType; + + MCPrimitiveTypeArgument implements MCTypeArgument <190> = + MCPrimitiveType; + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/types/MCCollectionTypes.mlc b/monticore-grammar/src/main/grammars/de/monticore/types/MCCollectionTypes.mlc new file mode 100644 index 0000000000..0d2e127fc4 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/types/MCCollectionTypes.mlc @@ -0,0 +1,29 @@ +package de.monticore.types; + +mlc MCCollectionTypes { + + //export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/types/MCCollectionTypes.mc4"; + } + + //export handwritten code + export "$projectDir/src/main/java" { + include "de/monticore/types/mccollectiontypes/**.java"; + include "de/monticore/types/prettyprint/MCCollectionTypes**.java"; + include "de/monticore/types/MCCollectionTypes**.java"; + + include "de/monticore/types/check/FullSynthesizeFromMCCollectionTypes.java"; + include "de/monticore/types/check/SynthesizeSymTypeFromMCCollectionTypes.java"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/types/mccollectiontypes/**.java"; + } + + promote { + mlc "de.monticore.types.MCBasicTypes"; + } + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/types/MCFullGenericTypes.mc4 b/monticore-grammar/src/main/grammars/de/monticore/types/MCFullGenericTypes.mc4 new file mode 100644 index 0000000000..adc4ccdf15 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/types/MCFullGenericTypes.mc4 @@ -0,0 +1,57 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types; + +/* This is a MontiCore stable grammar. + * Adaptations -- if any -- are conservative. */ + +import de.monticore.types.*; + +/** + * This grammar completes the type definitions to + * support the full Java type system including wildcards Blubb + * + * A general advice: When you are not sure that you need this kind of + * types, then use a simpler version. + * Type checking ist tricky. + * + * This grammar is part of a hierarchy of types, namely + * * types.MCBasicTypes.mc4 + * * types/MCArrayTypes.mc4 + * * types/MCCollectionTypes.mc4 + * * types/MCSimpleGenericTypes.mc4 + * * types/MCFullGenericTypes.mc4 + * +*/ + +component grammar MCFullGenericTypes + extends MCSimpleGenericTypes { + + /*=================================================================*/ + + /** ASTWildcardTypeArgument represents a wildcard type in a type argument + * (generics). It also contains either an upper- or a lower bound. + * + * @attribute upperBound Supertye of the type argument + * @attribute lowerBound Subtype of the type argument + */ + MCWildcardTypeArgument implements MCTypeArgument = + "?" ( ("extends" upperBound:MCType) + | ("super" lowerBound:MCType) )?; + + + /*=================================================================*/ + + /** ASTMCMultipleGenericType + * is only used for parsing, if referenced Type is + * generic AND has generic inner classes e.g. + * monticore.Generic1.GenericInnerClass + */ + MCMultipleGenericType implements MCGenericType, MCType = + MCBasicGenericType // complex Outer Type qualification + "." (MCInnerType || ".")+ ; + + // At this point it is not unique whether a type name is used or defined: + // this left open to the embedding nonterminal + MCInnerType = Name ("<" (MCTypeArgument || ",")+ ">")?; + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/types/MCFullGenericTypes.mlc b/monticore-grammar/src/main/grammars/de/monticore/types/MCFullGenericTypes.mlc new file mode 100644 index 0000000000..b041511893 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/types/MCFullGenericTypes.mlc @@ -0,0 +1,29 @@ +package de.monticore.types; + +mlc MCFullGenericTypes { + + //export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/types/MCFullGenericTypes.mc4"; + } + + //export handwritten code + export "$projectDir/src/main/java" { + include "de/monticore/types/mcfullgenerictypes/**.java"; + include "de/monticore/types/prettyprint/MCFullGenericTypes**.java"; + include "de/monticore/types/MCFullGenericTypes**.java"; + + include "de/monticore/types/check/FullSynthesizeFromMCFullGenericTypes.java"; + include "de/monticore/types/check/SynthesizeSymTypeFromMCFullGenericTypes.java"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/types/mcfullgenerictypes/**.java"; + } + + promote { + mlc "de.monticore.types.MCSimpleGenericTypes"; + } + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/types/MCFunctionTypes.mc4 b/monticore-grammar/src/main/grammars/de/monticore/types/MCFunctionTypes.mc4 new file mode 100644 index 0000000000..1fa7c19119 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/types/MCFunctionTypes.mc4 @@ -0,0 +1,41 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types; + +/* Beta-version: This is intended to become a MontiCore stable grammar. */ + +import de.monticore.types.*; + +/** + * This grammar introduces function types + * such as int -> int, (String -> int) -> void, (boolean, int) -> int + * + * This grammar is part of a hierarchy of types, namely + * * types.MCBasicTypes.mc4 + * * types/MCFunctionTypes.mc4 + * +*/ + +component grammar MCFunctionTypes + extends MCBasicTypes { + + + /*=================================================================*/ + + /** ASTMCFunctionTypesType represents function types. + * + * Any number of arguments is possible. + */ + MCFunctionParTypes = + "(" ( + (MCType || ",")+ + (isElliptic:"...")? + )? ")" + ; + + MCFunctionType implements MCType = + + MCFunctionParTypes "->" + MCReturnType + ; + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/types/MCFunctionTypes.mlc b/monticore-grammar/src/main/grammars/de/monticore/types/MCFunctionTypes.mlc new file mode 100644 index 0000000000..6c7fcf9680 --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/types/MCFunctionTypes.mlc @@ -0,0 +1,28 @@ +package de.monticore.types; + +mlc MCFunctionTypes { + + //export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/types/MCFunctionTypes.mc4"; + } + + //export handwritten code + export "$projectDir/src/main/java" { + include "de/monticore/types/mcfunctiontypes/**.java"; + include "de/monticore/types/prettyprint/MCFunctionTypes**.java"; + + include "de/monticore/types/check/FullSynthesizeFromMCFunctionTypes.java"; + include "de/monticore/types/check/SynthesizeSymTypeFromMCFunctionTypes.java"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/types/mcfunctiontypes/**.java"; + } + + promote { + mlc "de.monticore.types.MCBasicTypes"; + } + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/types/MCSimpleGenericTypes.mc4 b/monticore-grammar/src/main/grammars/de/monticore/types/MCSimpleGenericTypes.mc4 new file mode 100644 index 0000000000..38994079fa --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/types/MCSimpleGenericTypes.mc4 @@ -0,0 +1,56 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types; + +/* This is a MontiCore stable grammar. + * Adaptations -- if any -- are conservative. */ + +import de.monticore.types.*; + +/** + * This grammar introduces freely defined generic types + * such as Blubb, Bla, Foo> + * + * These generics are covering a wide range of uses for generic types, + * although they don't cover type restrictions on the arguments, like in + * Java. + * + * This eases the reuse of type structures in languages similar to Java, + * that are somewhat simplified. + * + * This grammar is part of a hierarchy of types, namely + * * types.MCBasicTypes.mc4 + * * types/MCArrayTypes.mc4 + * * types/MCCollectionTypes.mc4 + * * types/MCSimpleGenericTypes.mc4 + * * types/MCFullGenericTypes.mc4 + * +*/ + +component grammar MCSimpleGenericTypes + extends MCCollectionTypes { + + + /*=================================================================*/ + + /** ASTMCCollectionTypesType represents class or interface + * types which could have a qualified name like this: a.b.C. + * + * Any number of arguments is possible. + * + * @attribute name Name of the type + * @attribute typeArgumentList The types between '<...>' + */ + + MCBasicGenericType implements MCGenericType <20> = + (Name || ".")+ "<" (MCTypeArgument || ",")* ">"; + + + /*=================================================================*/ + + /** ASTMCCustomTypeArgument allows all MCTypes as TypeArguments. + * This extension of the imported grammar is conservative, and it + * enables recursive Generics e.g. List> + */ + MCCustomTypeArgument implements MCTypeArgument <20> = MCType; + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/types/MCSimpleGenericTypes.mlc b/monticore-grammar/src/main/grammars/de/monticore/types/MCSimpleGenericTypes.mlc new file mode 100644 index 0000000000..fccd257cdb --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/types/MCSimpleGenericTypes.mlc @@ -0,0 +1,29 @@ +package de.monticore.types; + +mlc MCSimpleGenericTypes { + + //export the grammar + export "$projectDir/src/main/grammars" { + include "de/monticore/types/MCSimpleGenericTypes.mc4"; + } + + //export handwritten code + export "$projectDir/src/main/java" { + include "de/monticore/types/mcsimplegenerictypes/**.java"; + include "de/monticore/types/prettyprint/MCSimpleGenericTypes**.java"; + include "de/monticore/types/MCSimpleGenericTypes**.java"; + + include "de/monticore/types/check/FullSynthesizeFromMCBasicTypes.java"; + include "de/monticore/types/check/SynthesizeSymTypeFromMCBasicTypes.java"; + } + + // export all Java files generated from the grammar + export "$projectDir/target/generated-sources/monticore/sourcecode" { + include "de/monticore/types/mcsimplegenerictypes/**.java"; + } + + promote { + mlc "de.monticore.types.MCCollectionTypes"; + } + +} diff --git a/monticore-grammar/src/main/grammars/de/monticore/types/Types/index.html b/monticore-grammar/src/main/grammars/de/monticore/types/Types/index.html new file mode 100644 index 0000000000..ebee5ea30f --- /dev/null +++ b/monticore-grammar/src/main/grammars/de/monticore/types/Types/index.html @@ -0,0 +1,847 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Types - MontiCore + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + + + + + +

MontiCore - Types

+

Type systems are available in a variety of (programming) languages and +facilitate programming because they allow for detecting typing errors already +at compile time. To express type usages in MontiCore-based languages a language +component hierarchy for type modeling was developed. The hierarchy consists of +the following language components:

+ +

MCBasicTypes

+

MCBasicTypes is the most basic language component. It provides the central +interface nonterminal MCType. Additionally, it defines nonterminals that +enable modeling primitive types as well as qualified and non-qualified +types. Furthermore, the component comprises a rule that covers return types +which can be MCTypes or voids. In general, the component represents a +relativity small, yet useful, collection of rules for type modeling that +supports statements such as int, Person, and java.lang.String.

+

MCCollectionTypes

+

This language component builds upon MCBasicTypes and enables to model four +kinds of generics:

+
    +
  • Set
  • +
  • List
  • +
  • Map
  • +
  • Optional
  • +
+

These generics cannot be nested as the purpose of the MCCollectionTypes +language component is the provisioning of some commonly used collection types +whose functionality is limited to support the construction of high-level models. +With the language component types such as List<Integer>, Set<char>, or +Map<java.lang.String, Person> become expressible.

+

MCSimpleGenericTypes

+

This language component extends the MCCollectionTypes component to allow the +expression of types with custom generics of arbitrary classes with arbitrary +arguments. When using the component, types such as Person<String> or +Map<Person<String>, Integer> are expressible. Please note that these types +still do not cover all possible Java types as Java additionally supports inner +types of generic types. Specifically, types such as a.b.C<D>.E.F<G>.H are +not expressible by MCSimpleGenericTypes.

+

MCFullGenericTypes

+

This language component extends the MCSimpleGenericTypes component to allow +the expression of inner generic types of arbitrary classes with arbitrary +arguments including wild card types. When using this language component, +types such as Person<?>, Map<Person<String>, ? extends Person>, and +a.b.C<D>.E.F<G>.H are expressible.

+

MCArrayTypes

+

This language component provides rules to express array types like Person[] or +int[][]. As array types are orthogonal to generic types, MCArrayTypes can be +combined with any of the above language components.

+

Further Information

+ + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/monticore-grammar/src/main/grammarsHC/de/monticore/literals/tr/MCCommonLiteralsTRHC.mc4 b/monticore-grammar/src/main/grammarsHC/de/monticore/literals/tr/MCCommonLiteralsTRHC.mc4 new file mode 100644 index 0000000000..4531ec44ce --- /dev/null +++ b/monticore-grammar/src/main/grammarsHC/de/monticore/literals/tr/MCCommonLiteralsTRHC.mc4 @@ -0,0 +1,216 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.literals.tr; + +/** + * Re-adds the literal related getValue ASTRules of literals for (MontiTrans) transformation grammars. + * Patterns will return their value, other TF-production-types will fail (but compilation will proceed). + */ +component grammar MCCommonLiteralsTRHC extends de.monticore.MCBasics, de.monticore.literals.tr.MCLiteralsBasisTR { + + TfIdentifierString implements TfIdentifier = + identifierToken:String + | "[[" identifierToken:String ":-" (newIdentifierSchema:TFSchema| newIdentifierToken:String)? "]]" + | "[[" (identifierSchema:TFSchema| identifierToken:String)? ":-" newIdentifierToken:String "]]" + // | "String" identifierSchema:TFSchema ";" // no abstract syntax matching for string literals - this (automatically derived) alternative is removed using the TRHC mechanism + ; + + + astrule ITFBooleanLiteral = + method public boolean getValue() { + throw new UnsupportedOperationException("0xD7761 Method not implemented for non patterns"); + } + ; + astrule BooleanLiteral_Pat = + method public Class _getTFElementType() { + return de.monticore.literals.mccommonliterals._ast.ASTBooleanLiteral.class; + } + method public boolean getValue() { + return this.isPresentSource() && this.getSource().getValue(); + } + ; + + astrule ITFCharLiteral = + method public char getValue() { + throw new UnsupportedOperationException("0xD7762 Method not implemented for non patterns"); + } + ; + astrule CharLiteral_Pat = + method public Class _getTFElementType() { + return de.monticore.literals.mccommonliterals._ast.ASTCharLiteral.class; + } + method public char getValue() { + return de.monticore.literals.MCLiteralsDecoder.decodeChar(getSource().getIdentifier()); + } + ; + + astrule ITFStringLiteral = + method public String getValue() { + throw new UnsupportedOperationException("0xD7763 Method not implemented for non patterns"); + } + ; + astrule StringLiteral_Pat = + method public Class _getTFElementType() { + return de.monticore.literals.mccommonliterals._ast.ASTStringLiteral.class; + } + method public String getValue() { + return de.monticore.literals.MCLiteralsDecoder.decodeString(getSource().getIdentifier()); + } + ; + + astrule ITFNatLiteral = + method public String getSource() { + throw new UnsupportedOperationException("0xD7764 Method not implemented for non patterns"); + } + method public int getValue() { + return de.monticore.literals.MCLiteralsDecoder.decodeNat(getSource()); + } + ; + astrule NatLiteral_Pat = + method public Class _getTFElementType() { + return de.monticore.literals.mccommonliterals._ast.ASTNatLiteral.class; + } + method public String getSource() { + return getDigits().getIdentifier(); + } + ; + + astrule ITFSignedNatLiteral = + method public String getSource() { + throw new UnsupportedOperationException("0xD7765 Method not implemented for non patterns"); + } + method public int getValue() { + return de.monticore.literals.MCLiteralsDecoder.decodeNat(getSource()); + } + ; + astrule SignedNatLiteral_Pat = + method public Class _getTFElementType() { + return de.monticore.literals.mccommonliterals._ast.ASTSignedNatLiteral.class; + } + method public String getSource() { + return (isPresentNegative() && getNegative().isNegative()?"-":"") + getDigits().getIdentifier(); + } + ; + + astrule ITFBasicLongLiteral = + method public String getSource() { + throw new UnsupportedOperationException("0xD7766 Method not implemented for non patterns"); + } + method public long getValue() { + return de.monticore.literals.MCLiteralsDecoder.decodeLong(getSource()); + } + ; + astrule BasicLongLiteral_Pat = + method public Class _getTFElementType() { + return de.monticore.literals.mccommonliterals._ast.ASTBasicLongLiteral.class; + } + method public String getSource() { + return getDigits().getIdentifier() + "L"; + } + ; + + astrule ITFSignedBasicLongLiteral = + method public String getSource() { + throw new UnsupportedOperationException("0xD7767 Method not implemented for non patterns"); + } + method public long getValue() { + return de.monticore.literals.MCLiteralsDecoder.decodeLong(getSource()); + } + ; + astrule SignedBasicLongLiteral_Pat = + method public Class _getTFElementType() { + return de.monticore.literals.mccommonliterals._ast.ASTSignedBasicLongLiteral.class; + } + method public String getSource() { + return (isPresentNegative() && getNegative().isNegative()?"-":"") + getDigits().getIdentifier() + "L"; + } + ; + + astrule ITFBasicFloatLiteral = + method public String getSource() { + throw new UnsupportedOperationException("0xD7768 Method not implemented for non patterns"); + } + method public Float getValue() { + return de.monticore.literals.MCLiteralsDecoder.decodeFloat(getSource()); + } + ; + astrule BasicFloatLiteral_Pat = + method public Class _getTFElementType() { + return de.monticore.literals.mccommonliterals._ast.ASTBasicFloatLiteral.class; + } + method public String getSource() { + return getPre().getIdentifier() + "." + getPost().getIdentifier() + "F"; + } + ; + + astrule ITFSignedBasicFloatLiteral = + method public String getSource() { + throw new UnsupportedOperationException("0xD7769 Method not implemented for non patterns"); + } + method public Float getValue() { + return de.monticore.literals.MCLiteralsDecoder.decodeFloat(getSource()); + } + ; + astrule SignedBasicFloatLiteral_Pat = + method public Class _getTFElementType() { + return de.monticore.literals.mccommonliterals._ast.ASTSignedBasicFloatLiteral.class; + } + method public String getSource() { + return (isPresentNegative() && getNegative().isNegative()?"-":"") + getPre().getIdentifier() + "." + getPost().getIdentifier() + "F"; + } + ; + + + astrule ITFBasicDoubleLiteral = + method public String getSource() { + throw new UnsupportedOperationException("0xD776A Method not implemented for non patterns"); + } + method public Double getValue() { + return de.monticore.literals.MCLiteralsDecoder.decodeDouble(getSource()); + } + ; + astrule BasicDoubleLiteral_Pat = + method public Class _getTFElementType() { + return de.monticore.literals.mccommonliterals._ast.ASTBasicDoubleLiteral.class; + } + method public String getSource() { + return getPre().getIdentifier() + "." + getPost().getIdentifier(); + } + ; + + astrule ITFSignedBasicDoubleLiteral = + method public String getSource() { + throw new UnsupportedOperationException("0xD776B Method not implemented for non patterns"); + } + method public Double getValue() { + return de.monticore.literals.MCLiteralsDecoder.decodeDouble(getSource()); + } + ; + astrule SignedBasicDoubleLiteral_Pat = + method public Class _getTFElementType() { + return de.monticore.literals.mccommonliterals._ast.ASTSignedBasicDoubleLiteral.class; + } + method public String getSource() { + return (isPresentNegative() && getNegative().isNegative()?"-":"") + getPre().getIdentifier() + "." + getPost().getIdentifier(); + } + ; + + // Constants are handled slightly different + + astrule ITFMCCommonLiterals_Source_Constant = + method public boolean getValue() { + throw new UnsupportedOperationException("0xD776C Method not implemented for non patterns"); + } + ; + astrule MCCommonLiterals_Source_Constant_Pat = + method public boolean getValue() { + return this.source == ASTConstantsMCCommonLiteralsTR.TRUE; + } + ; + + astrule ITFMCCommonLiterals_Negative_Constant = + method public boolean isNegative() { + throw new UnsupportedOperationException("0xD776D Method not implemented for non patterns"); + } + ; + +} diff --git a/monticore-grammar/src/main/java/de/monticore/completeness/_prettyprint/CompletenessPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/completeness/_prettyprint/CompletenessPrettyPrinter.java new file mode 100644 index 0000000000..8f4018f7cd --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/completeness/_prettyprint/CompletenessPrettyPrinter.java @@ -0,0 +1,34 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.completeness._prettyprint; + +import de.monticore.completeness._ast.ASTCompleteness; +import de.monticore.prettyprint.IndentPrinter; + +public class CompletenessPrettyPrinter extends CompletenessPrettyPrinterTOP { + + public CompletenessPrettyPrinter(IndentPrinter printer, boolean printComments) { + super(printer, printComments); + } + + @Override + public void handle(ASTCompleteness node) { + if (this.isPrintComments()) { + de.monticore.prettyprint.CommentPrettyPrinter.printPreComments(node, getPrinter()); + } + if (node.isComplete()) { + getPrinter().print("(c)"); + } + else if (node.isIncomplete()) { + getPrinter().print("(...)"); + } + else if (node.isLeftComplete()) { + getPrinter().print("(c,...)"); + } + else if (node.isRightComplete()) { + getPrinter().print("(...,c)"); + } + if (this.isPrintComments()) { + de.monticore.prettyprint.CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/expressions/assignmentexpressions/cocos/AssignmentExpressionsOnlyAssignToLValuesCoCo.java b/monticore-grammar/src/main/java/de/monticore/expressions/assignmentexpressions/cocos/AssignmentExpressionsOnlyAssignToLValuesCoCo.java new file mode 100644 index 0000000000..eea219e0f2 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/expressions/assignmentexpressions/cocos/AssignmentExpressionsOnlyAssignToLValuesCoCo.java @@ -0,0 +1,78 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.expressions.assignmentexpressions.cocos; + +import de.monticore.expressions.assignmentexpressions._ast.ASTAssignmentExpression; +import de.monticore.expressions.assignmentexpressions._ast.ASTDecPrefixExpression; +import de.monticore.expressions.assignmentexpressions._ast.ASTDecSuffixExpression; +import de.monticore.expressions.assignmentexpressions._ast.ASTIncPrefixExpression; +import de.monticore.expressions.assignmentexpressions._ast.ASTIncSuffixExpression; +import de.monticore.expressions.assignmentexpressions._cocos.AssignmentExpressionsASTAssignmentExpressionCoCo; +import de.monticore.expressions.assignmentexpressions._cocos.AssignmentExpressionsASTDecPrefixExpressionCoCo; +import de.monticore.expressions.assignmentexpressions._cocos.AssignmentExpressionsASTDecSuffixExpressionCoCo; +import de.monticore.expressions.assignmentexpressions._cocos.AssignmentExpressionsASTIncPrefixExpressionCoCo; +import de.monticore.expressions.assignmentexpressions._cocos.AssignmentExpressionsASTIncSuffixExpressionCoCo; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.expressionsbasis.types3.util.ILValueRelations; +import de.se_rwth.commons.logging.Log; + +public class AssignmentExpressionsOnlyAssignToLValuesCoCo implements + AssignmentExpressionsASTAssignmentExpressionCoCo, + AssignmentExpressionsASTDecPrefixExpressionCoCo, + AssignmentExpressionsASTDecSuffixExpressionCoCo, + AssignmentExpressionsASTIncPrefixExpressionCoCo, + AssignmentExpressionsASTIncSuffixExpressionCoCo { + + /** + * the lvalue relations are directly dependent + * of the other language components, + * as such there is no default + */ + ILValueRelations lValueRelations = null; + + public AssignmentExpressionsOnlyAssignToLValuesCoCo( + ILValueRelations lValueRelations) { + this.lValueRelations = lValueRelations; + } + + protected ILValueRelations getLValueRelations() { + return lValueRelations; + } + + @Override + public void check(ASTAssignmentExpression node) { + failIfNoLValue(node.getLeft()); + } + + @Override + public void check(ASTDecPrefixExpression node) { + failIfNoLValue(node.getExpression()); + } + + @Override + public void check(ASTDecSuffixExpression node) { + failIfNoLValue(node.getExpression()); + } + + @Override + public void check(ASTIncPrefixExpression node) { + failIfNoLValue(node.getExpression()); + } + + @Override + public void check(ASTIncSuffixExpression node) { + failIfNoLValue(node.getExpression()); + } + + // Helper + + protected void failIfNoLValue(ASTExpression expr) { + if (!getLValueRelations().isLValue(expr)) { + Log.error("0xFDD47 expression is not a lvalue " + + "but being assigned to", + expr.get_SourcePositionStart(), + expr.get_SourcePositionEnd() + ); + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/expressions/assignmentexpressions/types3/AssignmentExpressionsTypeVisitor.java b/monticore-grammar/src/main/java/de/monticore/expressions/assignmentexpressions/types3/AssignmentExpressionsTypeVisitor.java new file mode 100644 index 0000000000..69f526c1b4 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/expressions/assignmentexpressions/types3/AssignmentExpressionsTypeVisitor.java @@ -0,0 +1,299 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.assignmentexpressions.types3; + +import com.google.common.base.Preconditions; +import de.monticore.expressions.assignmentexpressions._ast.ASTAssignmentExpression; +import de.monticore.expressions.assignmentexpressions._ast.ASTConstantsAssignmentExpressions; +import de.monticore.expressions.assignmentexpressions._ast.ASTDecPrefixExpression; +import de.monticore.expressions.assignmentexpressions._ast.ASTDecSuffixExpression; +import de.monticore.expressions.assignmentexpressions._ast.ASTIncPrefixExpression; +import de.monticore.expressions.assignmentexpressions._ast.ASTIncSuffixExpression; +import de.monticore.expressions.assignmentexpressions._visitor.AssignmentExpressionsVisitor2; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types3.AbstractTypeVisitor; +import de.monticore.types3.ISymTypeRelations; +import de.monticore.types3.util.SymTypeRelations; +import de.se_rwth.commons.SourcePosition; +import de.se_rwth.commons.logging.Log; + +public class AssignmentExpressionsTypeVisitor extends AbstractTypeVisitor + implements AssignmentExpressionsVisitor2 { + + protected ISymTypeRelations typeRelations; + + public AssignmentExpressionsTypeVisitor( + ISymTypeRelations typeRelations) { + this.typeRelations = typeRelations; + } + + public AssignmentExpressionsTypeVisitor() { + //default values + this( + new SymTypeRelations() + ); + } + + public void setSymTypeRelations(ISymTypeRelations symTypeRelations) { + this.typeRelations = symTypeRelations; + } + + @Override + public void endVisit(ASTIncSuffixExpression expr) { + Preconditions.checkNotNull(expr); + SymTypeExpression symType = this.affix(expr.getExpression(), "++", + expr.get_SourcePositionStart()); + getType4Ast().setTypeOfExpression(expr, symType); + } + + @Override + public void endVisit(ASTDecSuffixExpression expr) { + Preconditions.checkNotNull(expr); + SymTypeExpression symType = this.affix(expr.getExpression(), "--", + expr.get_SourcePositionStart()); + getType4Ast().setTypeOfExpression(expr, symType); + } + + @Override + public void endVisit(ASTIncPrefixExpression expr) { + Preconditions.checkNotNull(expr); + SymTypeExpression symType = this.affix(expr.getExpression(), "++", + expr.get_SourcePositionStart()); + getType4Ast().setTypeOfExpression(expr, symType); + } + + @Override + public void endVisit(ASTDecPrefixExpression expr) { + Preconditions.checkNotNull(expr); + SymTypeExpression symType = this.affix(expr.getExpression(), "--", + expr.get_SourcePositionStart()); + getType4Ast().setTypeOfExpression(expr, symType); + } + + protected SymTypeExpression affix(ASTExpression expr, String op, SourcePosition pos) { + // calculate the type of the inner expressions + SymTypeExpression inner = getType4Ast().getPartialTypeOfExpr(expr); + + // result of inner type computation should be present + if (inner.isObscureType()) { + // if inner obscure then error already logged + return SymTypeExpressionFactory.createObscureType(); + } + else { + // else check with signature + return affix(inner, op, pos); + } + } + + protected SymTypeExpression affix(SymTypeExpression inner, String op, SourcePosition pos) { + if (typeRelations.isNumericType(inner)) { + SymTypeExpression unboxed = typeRelations.unbox(inner); + return SymTypeExpressionFactory.createPrimitive(unboxed.print()); + } + else { + Log.error("0xA0184 Operator '" + op + "' not applicable to " + "'" + inner.print() + "'", + pos); + return SymTypeExpressionFactory.createObscureType(); + } + } + + @Override + public void endVisit(ASTAssignmentExpression expr) { + Preconditions.checkNotNull(expr); + SymTypeExpression symType = this.derive(expr); + getType4Ast().setTypeOfExpression(expr, symType); + } + + protected SymTypeExpression derive(ASTAssignmentExpression expr) { + // calculate the type of inner expressions + SymTypeExpression left = getType4Ast().getPartialTypeOfExpr(expr.getLeft()); + SymTypeExpression right = getType4Ast().getPartialTypeOfExpr(expr.getRight()); + + // result of inner type computation should be present + if (left.isObscureType() || right.isObscureType()) { + // if left or right obscure then error already logged + return SymTypeExpressionFactory.createObscureType(); + } + else { + // else compare the inner results + return assignment(left, right, expr.getOperator(), expr.get_SourcePositionStart()); + } + } + + protected SymTypeExpression assignment(SymTypeExpression left, + SymTypeExpression right, + int op, SourcePosition pos) { + if (op == ASTConstantsAssignmentExpressions.PLUSEQUALS) { + return this.addAssignment(left, right, pos); // a += b + } + else if (op == ASTConstantsAssignmentExpressions.MINUSEQUALS) { + return this.subtractAssignment(left, right, pos); // a -= b + } + else if (op == ASTConstantsAssignmentExpressions.STAREQUALS) { + return this.multiplyAssignment(left, right, pos); // a *= b + } + else if (op == ASTConstantsAssignmentExpressions.SLASHEQUALS) { + return this.divideAssignment(left, right, pos); // a /= b + } + else if (op == ASTConstantsAssignmentExpressions.PERCENTEQUALS) { + return this.moduloAssignment(left, right, pos); // a %= b + } + else if (op == ASTConstantsAssignmentExpressions.AND_EQUALS) { + return this.andAssignment(left, right, pos); // a %= b + } + else if (op == ASTConstantsAssignmentExpressions.PIPEEQUALS) { + return this.orAssignment(left, right, pos); // a |= b + } + else if (op == ASTConstantsAssignmentExpressions.ROOFEQUALS) { + return this.xorAssignment(left, right, pos); // a ^= b + } + else if (op == ASTConstantsAssignmentExpressions.GTGTEQUALS) { + return this.rightShiftAssignment(left, right, pos); // a >>= b + } + else if (op == ASTConstantsAssignmentExpressions.LTLTEQUALS) { + return this.leftShiftAssignment(left, right, pos); // a <<= b + } + else if (op == ASTConstantsAssignmentExpressions.GTGTGTEQUALS) { + return this.unsignedRightShiftAssignment(left, right, pos); // a >>>= b + } + else { + return this.assignment(left, right, pos); // a = b + } + } + + protected SymTypeExpression addAssignment(SymTypeExpression left, + SymTypeExpression right, + SourcePosition pos) { + // anything on the rhs be converted to a String + if (typeRelations.isString(left)) { + return left; + } + else { + return arithmeticAssignment(left, right, "+=", pos); + } + } + + protected SymTypeExpression subtractAssignment(SymTypeExpression left, + SymTypeExpression right, + SourcePosition pos) { + return arithmeticAssignment(left, right, "-=", pos); + } + + protected SymTypeExpression multiplyAssignment(SymTypeExpression left, + SymTypeExpression right, + SourcePosition pos) { + return arithmeticAssignment(left, right, "*=", pos); + } + + protected SymTypeExpression moduloAssignment(SymTypeExpression left, + SymTypeExpression right, + SourcePosition pos) { + return arithmeticAssignment(left, right, "%=", pos); + } + + protected SymTypeExpression divideAssignment(SymTypeExpression left, + SymTypeExpression right, + SourcePosition pos) { + return arithmeticAssignment(left, right, "/=", pos); + } + + protected SymTypeExpression andAssignment(SymTypeExpression left, + SymTypeExpression right, + SourcePosition pos) { + return binaryAssignment(left, right, "&=", pos); + } + + protected SymTypeExpression orAssignment(SymTypeExpression left, + SymTypeExpression right, + SourcePosition pos) { + return binaryAssignment(left, right, "|=", pos); + } + + protected SymTypeExpression xorAssignment(SymTypeExpression left, + SymTypeExpression right, + SourcePosition pos) { + return binaryAssignment(left, right, "^=", pos); + } + + protected SymTypeExpression rightShiftAssignment(SymTypeExpression left, + SymTypeExpression right, + SourcePosition pos) { + return bitAssignment(left, right, ">>=", pos); + } + + protected SymTypeExpression leftShiftAssignment(SymTypeExpression left, + SymTypeExpression right, + SourcePosition pos) { + return bitAssignment(left, right, "<<=", pos); + } + + protected SymTypeExpression unsignedRightShiftAssignment(SymTypeExpression left, + SymTypeExpression right, + SourcePosition pos) { + return bitAssignment(left, right, ">>>=", pos); + } + + protected SymTypeExpression binaryAssignment(SymTypeExpression left, + SymTypeExpression right, + String op, SourcePosition pos) { + // both must be of integral or both must be of boolean type + if ((typeRelations.isIntegralType(left) && typeRelations.isIntegralType(right)) + || (typeRelations.isBoolean(left) && typeRelations.isBoolean(right))) { + return left; + } + else { + // else operator not applicable + Log.error("0xA0176 Operator '" + op + "' not applicable to " + + "'" + left.print() + "', '" + right.print() + "'", pos); + return SymTypeExpressionFactory.createObscureType(); + } + } + + protected SymTypeExpression bitAssignment(SymTypeExpression left, + SymTypeExpression right, + String op, + SourcePosition src) { + // both must be of integral type + if (typeRelations.isIntegralType(left) && typeRelations.isIntegralType(right)) { + return left; + } + else { + // else operator not applicable + Log.error("0xA0177 Operator '" + op + "' not applicable to " + + "'" + left.print() + "', '" + right.print() + "'", src); + return SymTypeExpressionFactory.createObscureType(); + } + } + + protected SymTypeExpression arithmeticAssignment(SymTypeExpression left, + SymTypeExpression right, + String op, + SourcePosition src) { + // both must be of numeric type + if (typeRelations.isNumericType(left) && typeRelations.isNumericType(right)) { + return left; + } + else { + // else operator not applicable + Log.error("0xA0178 Operator '" + op + "' not applicable to " + + "'" + left.print() + "', '" + right.print() + "'", src); + return SymTypeExpressionFactory.createObscureType(); + } + } + + protected SymTypeExpression assignment(SymTypeExpression left, + SymTypeExpression right, + SourcePosition src) { + // types must be compatible + if (typeRelations.isCompatible(left, right)) { + return left; + } + else { + // else type mismatch + Log.error("0xA0179 Incompatible types, required '" + left.print() + + "' but provided '" + right.print() + "'", src); + return SymTypeExpressionFactory.createObscureType(); + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/expressions/bitexpressions/types3/BitExpressionsTypeVisitor.java b/monticore-grammar/src/main/java/de/monticore/expressions/bitexpressions/types3/BitExpressionsTypeVisitor.java new file mode 100644 index 0000000000..3543dcba21 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/expressions/bitexpressions/types3/BitExpressionsTypeVisitor.java @@ -0,0 +1,185 @@ +package de.monticore.expressions.bitexpressions.types3; + +import com.google.common.base.Preconditions; +import de.monticore.expressions.bitexpressions._ast.ASTBinaryAndExpression; +import de.monticore.expressions.bitexpressions._ast.ASTBinaryExpression; +import de.monticore.expressions.bitexpressions._ast.ASTBinaryOrOpExpression; +import de.monticore.expressions.bitexpressions._ast.ASTBinaryXorExpression; +import de.monticore.expressions.bitexpressions._ast.ASTLeftShiftExpression; +import de.monticore.expressions.bitexpressions._ast.ASTLogicalRightShiftExpression; +import de.monticore.expressions.bitexpressions._ast.ASTRightShiftExpression; +import de.monticore.expressions.bitexpressions._ast.ASTShiftExpression; +import de.monticore.expressions.bitexpressions._visitor.BitExpressionsVisitor2; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types.check.SymTypePrimitive; +import de.monticore.types3.AbstractTypeVisitor; +import de.monticore.types3.ISymTypeRelations; +import de.monticore.types3.util.SymTypeRelations; +import de.se_rwth.commons.SourcePosition; +import de.se_rwth.commons.logging.Log; + +public class BitExpressionsTypeVisitor extends AbstractTypeVisitor + implements BitExpressionsVisitor2 { + + protected ISymTypeRelations typeRelations; + + public BitExpressionsTypeVisitor(ISymTypeRelations typeRelations) { + this.typeRelations = typeRelations; + } + + public BitExpressionsTypeVisitor() { + this(new SymTypeRelations()); + } + + public void setSymTypeRelations(ISymTypeRelations symTypeRelations) { + this.typeRelations = symTypeRelations; + } + + protected ISymTypeRelations getTypeRel() { + return typeRelations; + } + + @Override + public void endVisit(ASTLeftShiftExpression expr) { + Preconditions.checkNotNull(expr); + SymTypeExpression symType = this.deriveShift(expr, "<<"); + getType4Ast().setTypeOfExpression(expr, symType); + } + + @Override + public void endVisit(ASTRightShiftExpression expr) { + Preconditions.checkNotNull(expr); + SymTypeExpression symType = this.deriveShift(expr, ">>"); + getType4Ast().setTypeOfExpression(expr, symType); + } + + @Override + public void endVisit(ASTLogicalRightShiftExpression expr) { + Preconditions.checkNotNull(expr); + SymTypeExpression symType = this.deriveShift(expr, ">>>"); + getType4Ast().setTypeOfExpression(expr, symType); + } + + protected SymTypeExpression deriveShift(ASTShiftExpression expr, String op) { + // calculate the type of inner expressions + SymTypeExpression left = getType4Ast().getPartialTypeOfExpr(expr.getLeft()); + SymTypeExpression right = getType4Ast().getPartialTypeOfExpr(expr.getRight()); + + // result of inner type computation should be present + if (left.isObscureType() || right.isObscureType()) { + // if left or right obscure then error already logged + return SymTypeExpressionFactory.createObscureType(); + } + else { + return calculateTypeShift(left, right, op, expr.get_SourcePositionStart()); + } + } + + protected SymTypeExpression calculateTypeShift(SymTypeExpression leftResult, + SymTypeExpression rightResult, String op, SourcePosition pos) { + if (leftResult.isPrimitive() && rightResult.isPrimitive()) { + SymTypePrimitive leftEx = (SymTypePrimitive) leftResult; + SymTypePrimitive rightEx = (SymTypePrimitive) rightResult; + + //only defined on integral type - integral type + if (typeRelations.isIntegralType(leftEx) && typeRelations.isIntegralType(rightEx)) { + return shiftCalculator(leftResult, rightResult, op, pos); + } + } + //should not happen + Log.error("0xC0201 Operator " + op + " not applicable to the types" + + "'" + leftResult.print() + "', '" + rightResult.print() + "'"); + return SymTypeExpressionFactory.createObscureType(); + } + + /** + * helper method to calculate the type of the ShiftExpressions + * cannot be linked with the BinaryExpressions because they are not calculated the same way + */ + protected SymTypeExpression shiftCalculator(SymTypeExpression left, SymTypeExpression right, + String op, SourcePosition pos) { + if (!left.isPrimitive() || !right.isPrimitive()) { + Log.error("0xC0204 The operator " + op + " is only applicable to primitive types.", pos); + return SymTypeExpressionFactory.createObscureType(); + } + SymTypePrimitive leftResult = (SymTypePrimitive) left; + SymTypePrimitive rightResult = (SymTypePrimitive) right; + + //only defined on integral type - integral type + if (typeRelations.isIntegralType(leftResult) && typeRelations.isIntegralType(rightResult)) { + if (getTypeRel().isLong(rightResult)) { + if (getTypeRel().isLong(leftResult)) { + return SymTypeExpressionFactory.createPrimitive("long"); + } + else { + return SymTypeExpressionFactory.createPrimitive("int"); + } + } + else { + return SymTypeExpressionFactory.createPrimitive("int"); + } + } + //should never happen + Log.error("0xC0205 The operator " + op + " is only applicable to integral types.", pos); + return SymTypeExpressionFactory.createObscureType(); + } + + @Override + public void endVisit(ASTBinaryAndExpression expr) { + Preconditions.checkNotNull(expr); + SymTypeExpression symType = this.deriveBinary(expr, expr.getOperator()); + getType4Ast().setTypeOfExpression(expr, symType); + } + + @Override + public void endVisit(ASTBinaryOrOpExpression expr) { + Preconditions.checkNotNull(expr); + SymTypeExpression symType = this.deriveBinary(expr, expr.getOperator()); + getType4Ast().setTypeOfExpression(expr, symType); + } + + @Override + public void endVisit(ASTBinaryXorExpression expr) { + Preconditions.checkNotNull(expr); + SymTypeExpression symType = this.deriveBinary(expr, expr.getOperator()); + getType4Ast().setTypeOfExpression(expr, symType); + } + + protected SymTypeExpression deriveBinary(ASTBinaryExpression expr, String operator) { + // calculate the type of inner expressions + SymTypeExpression leftRes = getType4Ast().getPartialTypeOfExpr(expr.getLeft()); + SymTypeExpression rightRes = getType4Ast().getPartialTypeOfExpr(expr.getRight()); + + // result of inner type computation should be present + if (leftRes.isObscureType() || rightRes.isObscureType()) { + // if left or right obscure then error already logged + return SymTypeExpressionFactory.createObscureType(); + } + else { + return calculateTypeBinary(leftRes, rightRes, operator, expr.get_SourcePositionStart()); + } + } + + protected SymTypeExpression calculateTypeBinary(SymTypeExpression leftResult, + SymTypeExpression rightResult, String operator, SourcePosition pos) { + if (leftResult.isPrimitive() && rightResult.isPrimitive()) { + SymTypePrimitive leftEx = (SymTypePrimitive) leftResult; + SymTypePrimitive rightEx = (SymTypePrimitive) rightResult; + + //only defined on boolean - boolean and integral type - integral type + if (getTypeRel().isBoolean(leftResult) && + getTypeRel().isBoolean(rightResult)) { + return SymTypeExpressionFactory.createPrimitive("boolean"); + } + else if (getTypeRel().isIntegralType(leftEx) && getTypeRel().isIntegralType(rightEx)) { + return getTypeRel().numericPromotion(leftEx, rightEx); + } + } + //should not happen, no valid result, error will be handled in traverse + Log.error("0xC0203 The operator " + operator + " is not applicable to the types" + + "'" + leftResult + "', '" + rightResult + "'", pos); + return SymTypeExpressionFactory.createObscureType(); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/expressions/cocos/ExpressionValid.java b/monticore-grammar/src/main/java/de/monticore/expressions/cocos/ExpressionValid.java new file mode 100644 index 0000000000..f3992a8f6d --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/expressions/cocos/ExpressionValid.java @@ -0,0 +1,39 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.cocos; + +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.expressionsbasis._cocos.ExpressionsBasisASTExpressionCoCo; +import de.monticore.types.check.TypeCalculator; + +import java.util.Optional; + + +public class ExpressionValid implements ExpressionsBasisASTExpressionCoCo { + + // The error message is thrown in typeCheck + + protected Optional checkingNode = Optional.empty(); + + protected TypeCalculator typeCheck; + + public ExpressionValid(TypeCalculator typeCheck) { + this.typeCheck = typeCheck; + } + + @Override + public void endVisit(ASTExpression expr) { + if (checkingNode.isPresent() && checkingNode.get() == expr) { + checkingNode = Optional.empty(); + } + } + + @Override + public void check(ASTExpression expr) { + if (!checkingNode.isPresent()) { + // TypeCheck + typeCheck.typeOf(expr); + checkingNode = Optional.of(expr); + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/expressions/commonexpressions/_ast/ASTCallExpression.java b/monticore-grammar/src/main/java/de/monticore/expressions/commonexpressions/_ast/ASTCallExpression.java new file mode 100644 index 0000000000..55a5e0d5e3 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/expressions/commonexpressions/_ast/ASTCallExpression.java @@ -0,0 +1,19 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.commonexpressions._ast; + +import de.monticore.symboltable.ISymbol; + +import java.util.Optional; + +public class ASTCallExpression extends ASTCallExpressionTOP { + + protected ISymbol definingSymbol; + + public Optional getDefiningSymbol() { + return Optional.ofNullable(this.definingSymbol); + } + + public void setDefiningSymbol(ISymbol symbol) { + this.definingSymbol = symbol; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/expressions/commonexpressions/_ast/ASTFieldAccessExpression.java b/monticore-grammar/src/main/java/de/monticore/expressions/commonexpressions/_ast/ASTFieldAccessExpression.java new file mode 100644 index 0000000000..9896af968c --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/expressions/commonexpressions/_ast/ASTFieldAccessExpression.java @@ -0,0 +1,19 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.commonexpressions._ast; + +import de.monticore.symboltable.ISymbol; + +import java.util.Optional; + +public class ASTFieldAccessExpression extends ASTFieldAccessExpressionTOP { + + protected ISymbol definingSymbol; + + public Optional getDefiningSymbol() { + return Optional.ofNullable(this.definingSymbol); + } + + public void setDefiningSymbol(ISymbol symbol) { + this.definingSymbol = symbol; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/expressions/commonexpressions/types3/CommonExpressionsTypeVisitor.java b/monticore-grammar/src/main/java/de/monticore/expressions/commonexpressions/types3/CommonExpressionsTypeVisitor.java new file mode 100644 index 0000000000..b1f17b5e22 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/expressions/commonexpressions/types3/CommonExpressionsTypeVisitor.java @@ -0,0 +1,1078 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.commonexpressions.types3; + +import de.monticore.expressions.commonexpressions.CommonExpressionsMill; +import de.monticore.expressions.commonexpressions._ast.*; +import de.monticore.expressions.commonexpressions._util.CommonExpressionsTypeDispatcher; +import de.monticore.expressions.commonexpressions._visitor.CommonExpressionsHandler; +import de.monticore.expressions.commonexpressions._visitor.CommonExpressionsTraverser; +import de.monticore.expressions.commonexpressions._visitor.CommonExpressionsVisitor2; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.expressionsbasis._ast.ASTNameExpression; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symboltable.modifiers.AccessModifier; +import de.monticore.symboltable.modifiers.StaticAccessModifier; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types.check.SymTypeOfFunction; +import de.monticore.types.check.SymTypeOfIntersection; +import de.monticore.types3.AbstractTypeVisitor; +import de.monticore.types3.ISymTypeRelations; +import de.monticore.types3.util.FunctionRelations; +import de.monticore.types3.util.NameExpressionTypeCalculator; +import de.monticore.types3.util.SymTypeRelations; +import de.monticore.types3.util.TypeContextCalculator; +import de.monticore.types3.util.WithinTypeBasicSymbolsResolver; +import de.se_rwth.commons.Names; +import de.se_rwth.commons.logging.Log; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static de.monticore.types.check.SymTypeExpressionFactory.createObscureType; +import static de.monticore.types.check.SymTypeExpressionFactory.createPrimitive; +import static de.monticore.types.check.SymTypeExpressionFactory.createTypeObject; +import static de.monticore.types.check.SymTypeExpressionFactory.createUnion; + +/** + * This Visitor can calculate a SymTypeExpression (type) + * for the expressions in CommonExpressions. + * It can be combined with other expressions in your language + */ +public class CommonExpressionsTypeVisitor extends AbstractTypeVisitor + implements CommonExpressionsVisitor2, CommonExpressionsHandler { + + protected CommonExpressionsTraverser traverser; + + protected ISymTypeRelations typeRelations; + + protected FunctionRelations functionRelations; + + protected WithinTypeBasicSymbolsResolver withinTypeResolver; + + protected TypeContextCalculator typeCtxCalc; + + // should be the same as used in DeriveSymTypeOfExpressionBasis + protected NameExpressionTypeCalculator nameExpressionTypeCalculator; + + protected CommonExpressionsTypeVisitor( + ISymTypeRelations typeRelations, + FunctionRelations functionRelations, + WithinTypeBasicSymbolsResolver withinTypeResolver, + TypeContextCalculator typeCtxCalc, + NameExpressionTypeCalculator nameExpressionTypeCalculator) { + this.typeRelations = typeRelations; + this.functionRelations = functionRelations; + this.withinTypeResolver = withinTypeResolver; + this.typeCtxCalc = typeCtxCalc; + this.nameExpressionTypeCalculator = nameExpressionTypeCalculator; + } + + public CommonExpressionsTypeVisitor() { + // default values + this( + new SymTypeRelations(), + null, + new WithinTypeBasicSymbolsResolver(), + new TypeContextCalculator(), + new NameExpressionTypeCalculator() + ); + functionRelations = new FunctionRelations(getTypeRel()); + } + + @Override + public CommonExpressionsTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(CommonExpressionsTraverser traverser) { + this.traverser = traverser; + } + + public void setSymTypeRelations(ISymTypeRelations typeRelations) { + this.typeRelations = typeRelations; + } + + public void setFunctionRelations(FunctionRelations functionRelations) { + this.functionRelations = functionRelations; + } + + public void setWithinTypeBasicSymbolsResolver( + WithinTypeBasicSymbolsResolver withinTypeResolver) { + this.withinTypeResolver = withinTypeResolver; + } + + public void setTypeContextCalculator(TypeContextCalculator typeCtxCalc) { + this.typeCtxCalc = typeCtxCalc; + } + + public void setNameExpressionTypeCalculator( + NameExpressionTypeCalculator nameExpressionTypeCalculator) { + this.nameExpressionTypeCalculator = nameExpressionTypeCalculator; + } + + protected ISymTypeRelations getTypeRel() { + return typeRelations; + } + + protected FunctionRelations getFuncRel() { + return functionRelations; + } + + protected WithinTypeBasicSymbolsResolver getWithinTypeResolver() { + return withinTypeResolver; + } + + protected TypeContextCalculator getTypeCtxCalc() { + return typeCtxCalc; + } + + protected NameExpressionTypeCalculator getNameExpressionTypeCalculator() { + return nameExpressionTypeCalculator; + } + + // Prefix + + @Override + public void endVisit(ASTPlusPrefixExpression expr) { + SymTypeExpression symType = calculateNumericPrefix(expr.getExpression(), "+"); + getType4Ast().setTypeOfExpression(expr, symType); + } + + @Override + public void endVisit(ASTMinusPrefixExpression expr) { + SymTypeExpression symType = calculateNumericPrefix(expr.getExpression(), "-"); + getType4Ast().setTypeOfExpression(expr, symType); + } + + // Arithmetic + + @Override + public void endVisit(ASTPlusExpression expr) { + SymTypeExpression left = getType4Ast().getPartialTypeOfExpr(expr.getLeft()); + SymTypeExpression right = getType4Ast().getPartialTypeOfExpr(expr.getRight()); + + SymTypeExpression result; + + if (left.isObscureType() || right.isObscureType()) { + // if any inner obscure then error already logged + result = createObscureType(); + } + // if one part of the expression is a String + // then the whole expression is a String + else if (getTypeRel().isString(left)) { + result = createTypeObject(left.getTypeInfo()); + } + else if (getTypeRel().isString(right)) { + result = createTypeObject(right.getTypeInfo()); + } + // no String in the expression + // -> use the normal calculation for the basic arithmetic operators + else { + result = calculateArithmeticExpression(expr, expr.getOperator()); + } + getType4Ast().setTypeOfExpression(expr, result); + } + + @Override + public void endVisit(ASTMultExpression expr) { + getType4Ast().setTypeOfExpression(expr, + calculateArithmeticExpression(expr, expr.getOperator()) + ); + } + + @Override + public void endVisit(ASTDivideExpression expr) { + getType4Ast().setTypeOfExpression(expr, + calculateArithmeticExpression(expr, expr.getOperator()) + ); + } + + @Override + public void endVisit(ASTMinusExpression expr) { + getType4Ast().setTypeOfExpression(expr, + calculateArithmeticExpression(expr, expr.getOperator()) + ); + } + + @Override + public void endVisit(ASTModuloExpression expr) { + getType4Ast().setTypeOfExpression(expr, + calculateArithmeticExpression(expr, expr.getOperator()) + ); + } + + // Numeric Comparison + + @Override + public void endVisit(ASTLessEqualExpression expr) { + getType4Ast().setTypeOfExpression(expr, + calculateNumericComparison(expr, expr.getOperator()) + ); + } + + @Override + public void endVisit(ASTGreaterEqualExpression expr) { + getType4Ast().setTypeOfExpression(expr, + calculateNumericComparison(expr, expr.getOperator()) + ); + } + + @Override + public void endVisit(ASTLessThanExpression expr) { + getType4Ast().setTypeOfExpression(expr, + calculateNumericComparison(expr, expr.getOperator()) + ); + } + + @Override + public void endVisit(ASTGreaterThanExpression expr) { + getType4Ast().setTypeOfExpression(expr, + calculateNumericComparison(expr, expr.getOperator()) + ); + } + + // Equality + + @Override + public void endVisit(ASTEqualsExpression expr) { + getType4Ast().setTypeOfExpression(expr, + calculateEquality(expr, expr.getOperator()) + ); + } + + @Override + public void endVisit(ASTNotEqualsExpression expr) { + getType4Ast().setTypeOfExpression(expr, + calculateEquality(expr, expr.getOperator()) + ); + } + + // Conditional + + @Override + public void endVisit(ASTBooleanAndOpExpression expr) { + getType4Ast().setTypeOfExpression(expr, + calculateConditionalBooleanOp(expr, expr.getOperator()) + ); + } + + @Override + public void endVisit(ASTBooleanOrOpExpression expr) { + getType4Ast().setTypeOfExpression(expr, + calculateConditionalBooleanOp(expr, expr.getOperator()) + ); + } + + @Override + public void endVisit(ASTLogicalNotExpression expr) { + SymTypeExpression inner = + getType4Ast().getPartialTypeOfExpr(expr.getExpression()); + SymTypeExpression result; + if (inner.isObscureType()) { + // if inner obscure then error already logged + result = createObscureType(); + } + else if (getTypeRel().isBoolean(inner)) { + result = createPrimitive(BasicSymbolsMill.BOOLEAN); + } + else { + // operator not applicable + Log.error("0xB0164 Operator '!' not applicable to " + + "'" + inner.print() + "'", + expr.get_SourcePositionStart(), + expr.get_SourcePositionEnd() + ); + result = createObscureType(); + } + getType4Ast().setTypeOfExpression(expr, result); + } + + @Override + public void endVisit(ASTConditionalExpression expr) { + SymTypeExpression cond = + getType4Ast().getPartialTypeOfExpr(expr.getCondition()); + SymTypeExpression left = + getType4Ast().getPartialTypeOfExpr(expr.getTrueExpression()); + SymTypeExpression right = + getType4Ast().getPartialTypeOfExpr(expr.getFalseExpression()); + + SymTypeExpression result; + + if (Stream.of(cond, left, right) + .anyMatch(SymTypeExpression::isObscureType)) { + // if any inner is obscure then error already logged + result = createObscureType(); + } + // condition must be boolean + else if (!getTypeRel().isBoolean(cond)) { + Log.error("0xB0165 expected '" + BasicSymbolsMill.BOOLEAN + + "' but provided '" + cond.print() + "'", + expr.getCondition().get_SourcePositionStart(), + expr.getCondition().get_SourcePositionEnd() + ); + result = createObscureType(); + // boolean conditional expression + } + else { + // not normalized, as information may get missing + result = createUnion(left, right); + } + + getType4Ast().setTypeOfExpression(expr, result); + } + + @Override + public void endVisit(ASTBooleanNotExpression expr) { + SymTypeExpression inner = + getType4Ast().getPartialTypeOfExpr(expr.getExpression()); + SymTypeExpression result; + if (inner.isObscureType()) { + // if inner obscure then error already logged + result = createObscureType(); + } + else if (getTypeRel().isIntegralType(inner)) { + result = getTypeRel().numericPromotion(inner); + } + else { + // operator not applicable + Log.error("0xB0175 Operator '~' not applicable to " + + "'" + inner.print() + "'", + expr.get_SourcePositionStart(), + expr.get_SourcePositionEnd() + ); + result = createObscureType(); + } + getType4Ast().setTypeOfExpression(expr, result); + } + + @Override + public void endVisit(ASTBracketExpression expr) { + getType4Ast().setTypeOfExpression(expr, + getType4Ast().getPartialTypeOfExpr(expr.getExpression()) + ); + } + + // Field/MethodAccess + + @Override + public void endVisit(ASTCallExpression expr) { + // most of the time the expression within the call expression + // will be a (qualified) name of a function. + // here, we rely on the non-separation between functions and variables + // (in Java, we would need `::` instead of `.` to select a method) + // but as we support function types, the difference is nigh existent + SymTypeExpression type; + List args = new ArrayList<>(); + for (int i = 0; i < expr.getArguments().sizeExpressions(); i++) { + args.add(getType4Ast().getPartialTypeOfExpr(expr.getArguments().getExpression(i))); + } + Set inner; + if (getType4Ast().getPartialTypeOfExpr(expr.getExpression()) + .isIntersectionType()) { + inner = new HashSet<>( + ((SymTypeOfIntersection) getType4Ast() + .getPartialTypeOfExpr(expr.getExpression()) + ).getIntersectedTypeSet() + ); + } + else { + inner = new HashSet<>(); + inner.add(getType4Ast().getPartialTypeOfExpr(expr.getExpression())); + } + + // error already logged if Obscure + if (inner.stream().allMatch(SymTypeExpression::isObscureType)) { + type = SymTypeExpressionFactory.createObscureType(); + } + else if (args.stream().anyMatch(SymTypeExpression::isObscureType)) { + type = SymTypeExpressionFactory.createObscureType(); + } + // as we call, we require a function type + else if (inner.stream().noneMatch(SymTypeExpression::isFunctionType)) { + Log.error("0xFDABC expression does not seem to be a function, " + + "instead the (potential) type(s) are: " + + inner.stream() + .map(SymTypeExpression::printFullName) + .collect(Collectors.joining(", ")), + expr.get_SourcePositionStart(), + expr.get_SourcePositionEnd() + ); + type = SymTypeExpressionFactory.createObscureType(); + } + else { + Set funcs = inner.stream() + .filter(SymTypeExpression::isFunctionType) + .map(t -> (SymTypeOfFunction) t) + .collect(Collectors.toSet()); + // filter out all function that do not fit the arguments + Set callableFuncs = funcs.stream() + .filter(f -> getFuncRel().canBeCalledWith(f, args)) + .collect(Collectors.toSet()); + if (callableFuncs.isEmpty()) { + Log.error("0xFDABE with " + args.size() + " argument (" + + args.stream() + .map(SymTypeExpression::printFullName) + .collect(Collectors.joining(", ")) + + "), no potential function can be invoked:" + + System.lineSeparator() + + funcs.stream() + .map(SymTypeExpression::printFullName) + .collect(Collectors.joining(System.lineSeparator())), + expr.get_SourcePositionStart(), + expr.get_SourcePositionEnd() + ); + type = SymTypeExpressionFactory.createObscureType(); + } + else { + // fix arity according to the arguments + callableFuncs = callableFuncs.stream() + .map(f -> f.getWithFixedArity(args.size())) + .collect(Collectors.toSet()); + Optional mostSpecificFunction = + getFuncRel().getMostSpecificFunction(callableFuncs); + if (mostSpecificFunction.isPresent()) { + type = mostSpecificFunction.get().getType().deepClone(); + } + else { + type = createObscureType(); + } + } + } + getType4Ast().setTypeOfExpression(expr, type); + } + + @Override + public void traverse(ASTFieldAccessExpression expr) { + if (isSeriesOfNames(expr.getExpression())) { + // done without visitor + fieldAccessCustomTraverse(expr); + } + else { + // traverse as normal + expr.getExpression().accept(getTraverser()); + } + } + + /** + * custom traverse for ASTFieldAccessExpressions. + * Normal traversing would try to calculate types, + * however, not every ASTFieldAccessExpression is a field access expression; + * E.g., given the expression "a.b.c.d.e", "a.b" could be a package name, + * "a.b.c" a name of a typeId, "a.b.c.d" the name of a variable, + * and "a.b.c.d.e" the access of field "e" in "a.b.c.d". + *

+ * As such, given a series of names a.b.c, + * up until the most outer field access expression in the series, + * we allow to not find an expression (and thus have no type calculated). + */ + protected void fieldAccessCustomTraverse(ASTFieldAccessExpression expr) { + if (isSeriesOfNames(expr)) { + if (getTypeDispatcher().isASTFieldAccessExpression(expr.getExpression())) { + ASTFieldAccessExpression innerFieldAccessExpr = + getTypeDispatcher().asASTFieldAccessExpression(expr.getExpression()); + fieldAccessCustomTraverse(innerFieldAccessExpr); + // if expression or type identifier has been found, + // continue to require further results + boolean resultsAreOptional = + !getType4Ast().hasTypeOfExpression(expr.getExpression()) && + !getType4Ast().hasTypeOfTypeIdentifierForName(expr.getExpression()); + calculateFieldAccess(innerFieldAccessExpr, resultsAreOptional); + } + else if (getTypeDispatcher().isASTNameExpression(expr.getExpression())) { + ASTNameExpression nameExpr = getTypeDispatcher().asASTNameExpression(expr.getExpression()); + Optional nameAsExprType = + calculateExprQName(nameExpr); + Optional nameAsTypeIdType = + calculateTypeIdQName(nameExpr); + if (nameAsExprType.isPresent()) { + getType4Ast().setTypeOfExpression(nameExpr, nameAsExprType.get()); + } + else if (nameAsTypeIdType.isPresent()) { + getType4Ast().setTypeOfTypeIdentifierForName( + nameExpr, + nameAsTypeIdType.get() + ); + } + } + else { + Log.error("0xFD5AC internal error:" + + "expected a series of names (a.b.c)", + expr.get_SourcePositionStart(), + expr.get_SourcePositionEnd() + ); + } + } + else { + Log.error("0xFD5AD internal error:" + + "expected a series of names (a.b.c)", + expr.get_SourcePositionStart(), + expr.get_SourcePositionEnd() + ); + } + } + + @Override + public void endVisit(ASTFieldAccessExpression expr) { + calculateFieldAccess(expr, false); + } + + /** + * implementation for endVisit. + * + * @param resultsAreOptional s. {@link #fieldAccessCustomTraverse(ASTFieldAccessExpression)} + */ + protected void calculateFieldAccess( + ASTFieldAccessExpression expr, boolean resultsAreOptional) { + // after the non-visitor traversal, types have been calculated if they exist + Optional exprType; + Optional typeId = Optional.empty(); + // case: expression "." name, e.g., getX().var + if (getType4Ast().hasTypeOfExpression(expr.getExpression())) { + exprType = calculateExprFieldAccessOrLogError(expr, false); + } + // case: typeIdentifier "." name, e.g., XClass.staticVar + // in Java, if variable exists, typeIdentifier "." name is ignored, + // even if variable "." name does not exist + else if (getType4Ast().hasTypeOfTypeIdentifierForName(expr.getExpression())) { + exprType = calculateTypeIdFieldAccessOrLogError(expr, resultsAreOptional); + // case: typeid "." typeid2 ("." name), e.g., C1.CInner.staticVar + if (exprType.isEmpty() && resultsAreOptional) { + typeId = calculateInnerTypeIdFieldAccess(expr); + } + } + // case: qualifier "." name + else { + // case: qualifier "." name as Expression + exprType = calculateExprQNameOrLogError(expr, resultsAreOptional); + // case qualifier "." name as type identifier + // this requires an outer field-access (qualifier.name.field), + // as the end result has to be an expression + if (exprType.isEmpty() && resultsAreOptional) { + typeId = calculateTypeIdQName(expr); + } + } + + // store expression type + if (exprType.isPresent()) { + getType4Ast().setTypeOfExpression(expr, exprType.get()); + } + else if (!resultsAreOptional) { + // error already logged + getType4Ast().setTypeOfExpression(expr, SymTypeExpressionFactory.createObscureType()); + } + // store type id + if (typeId.isPresent()) { + getType4Ast().setTypeOfTypeIdentifierForName(expr, typeId.get()); + } + } + + // The following functions all interpret field access / name expressions + + /** + * case: expression "." name, + * e.g., getX().var. + * will log an error if necessary (resultsAreOptional). + */ + protected Optional calculateExprFieldAccessOrLogError( + ASTFieldAccessExpression expr, + boolean resultsAreOptional + ) { + Optional type = calculateExprFieldAccess(expr); + if (type.isEmpty() && !resultsAreOptional) { + Log.error("0xF737F given expression of type " + + getType4Ast().getPartialTypeOfExpr(expr.getExpression()).printFullName() + + " unable to derive the type of the access \"." + + expr.getName() + "\"", + expr.get_SourcePositionStart(), + expr.get_SourcePositionEnd() + ); + } + return type; + } + + /** + * calculates a.b with a being an expression, + * e.g., getX().var + */ + protected Optional calculateExprFieldAccess( + ASTFieldAccessExpression expr) { + Set types = new HashSet<>(); + final String name = expr.getName(); + if (!getType4Ast().hasTypeOfExpression(expr.getExpression())) { + Log.error("0xFD231 internal error:" + + "unable to find type identifier for field access", + expr.get_SourcePositionStart(), + expr.get_SourcePositionEnd() + ); + } + else { + SymTypeExpression innerAsExprType = + getType4Ast().getPartialTypeOfExpr(expr.getExpression()); + if (getWithinTypeResolver().canResolveIn(innerAsExprType)) { + AccessModifier modifier = innerAsExprType.hasTypeInfo() ? + getTypeCtxCalc().getAccessModifier( + innerAsExprType.getTypeInfo(), expr.getEnclosingScope() + ) : AccessModifier.ALL_INCLUSION; + Optional variable = + getWithinTypeResolver().resolveVariable(innerAsExprType, + name, + modifier, + v -> true + ); + if (variable.isPresent()) { + types.add(variable.get()); + } + Collection functions = + getWithinTypeResolver().resolveFunctions( + innerAsExprType, + name, + modifier, + f -> true + ); + types.addAll(functions); + } + // extension point + else { + Log.error("0xFDB3A unexpected field access \"" + + expr.getName() + + "\" for type " + + innerAsExprType.printFullName(), + expr.get_SourcePositionStart(), + expr.get_SourcePositionEnd() + ); + } + } + if (types.size() <= 1) { + return types.stream().findAny(); + } + else { + return Optional.of(SymTypeExpressionFactory.createIntersection(types)); + } + } + + /** + * case: typeIdentifier "." name, + * e.g., XClass.staticVar. + * will log an error if necessary (resultsAreOptional). + */ + protected Optional calculateTypeIdFieldAccessOrLogError( + ASTFieldAccessExpression expr, + boolean resultsAreOptional + ) { + Optional type = calculateTypeIdFieldAccess(expr); + if (type.isEmpty() && !resultsAreOptional) { + Log.error("0xF736F given type identifier of type " + + getType4Ast().getPartialTypeOfTypeIdForName(expr.getExpression()).printFullName() + + " unable to derive the type of the access \"." + + expr.getName() + "\"", + expr.get_SourcePositionStart(), + expr.get_SourcePositionEnd() + ); + } + return type; + } + + /** + * calculates a.b.c with a.b being a type identifier, + * e.g., XClass.staticVar + */ + protected Optional calculateTypeIdFieldAccess( + ASTFieldAccessExpression expr) { + final String name = expr.getName(); + Set types = new HashSet<>(); + if (!getType4Ast().hasTypeOfTypeIdentifierForName(expr.getExpression())) { + Log.error("0xFD232 internal error:" + + "unable to find type identifier for field access", + expr.get_SourcePositionStart(), + expr.get_SourcePositionEnd() + ); + } + else { + SymTypeExpression innerAsTypeIdType = + getType4Ast().getPartialTypeOfTypeIdForName(expr.getExpression()); + if (getWithinTypeResolver().canResolveIn(innerAsTypeIdType)) { + AccessModifier modifier = innerAsTypeIdType.hasTypeInfo() ? + getTypeCtxCalc().getAccessModifier( + innerAsTypeIdType.getTypeInfo(), + expr.getEnclosingScope(), + true + ) : StaticAccessModifier.STATIC; + Optional variable = + getWithinTypeResolver().resolveVariable( + innerAsTypeIdType, + name, + modifier, + v -> true + ); + variable.ifPresent(types::add); + Collection functions = + getWithinTypeResolver().resolveFunctions( + innerAsTypeIdType, + name, + modifier, + f -> true + ); + types.addAll(functions); + } + // extension point + else { + Log.error("0xFDE3A unexpected field access \"" + + expr.getName() + + "\" for type " + + innerAsTypeIdType.printFullName(), + expr.get_SourcePositionStart(), + expr.get_SourcePositionEnd() + ); + } + } + if (types.size() <= 1) { + return types.stream().findAny(); + } + else { + return Optional.of(SymTypeExpressionFactory.createIntersection(types)); + } + } + + /** + * calculates a.b.c as a type identifier with a.b being a type identifier, + * e.g., OuterClass.InnerClass.StaticVariable + */ + protected Optional calculateInnerTypeIdFieldAccess( + ASTFieldAccessExpression expr) { + final String name = expr.getName(); + Optional type = Optional.empty(); + if (!getType4Ast().hasTypeOfTypeIdentifierForName(expr.getExpression())) { + Log.error("0xFD233 internal error:" + + "unable to find type identifier for field access", + expr.get_SourcePositionStart(), + expr.get_SourcePositionEnd() + ); + } + else { + SymTypeExpression innerAsTypeIdType = + getType4Ast().getPartialTypeOfTypeIdForName(expr.getExpression()); + if (getWithinTypeResolver().canResolveIn(innerAsTypeIdType)) { + AccessModifier modifier = innerAsTypeIdType.hasTypeInfo() ? + getTypeCtxCalc().getAccessModifier( + innerAsTypeIdType.getTypeInfo(), + expr.getEnclosingScope(), + true + ) : StaticAccessModifier.STATIC; + type = getWithinTypeResolver().resolveType( + innerAsTypeIdType, + name, + getTypeCtxCalc().getAccessModifier( + innerAsTypeIdType.getTypeInfo(), + expr.getEnclosingScope(), + true + ), + t -> true + ); + } + else { + Log.error("0xFDE3A unexpected field access \"" + + expr.getName() + + "\" for type " + + innerAsTypeIdType.printFullName(), + expr.get_SourcePositionStart(), + expr.get_SourcePositionEnd() + ); + } + } + return type; + } + + /** + * case: qName "." name, + * e.g., package.artifact.staticVar. + * will log an error if necessary (resultsAreOptional). + */ + protected Optional calculateExprQNameOrLogError( + ASTFieldAccessExpression expr, + boolean resultsAreOptional + ) { + // case qualifier "." name as an expression + Optional type = calculateExprQName(expr); + if (type.isEmpty() && !resultsAreOptional) { + if (isSeriesOfNames(expr)) { + Log.error("0xF735F unable to interpret qualified name \"" + + getExprAsQName(expr).get() + + "\" as expression", + expr.get_SourcePositionStart(), + expr.get_SourcePositionEnd() + ); + } + else { + // error already logged + } + } + return type; + } + + /** + * calculates a.b.c as expression with a.b being a qualifier + */ + protected Optional calculateExprQName( + ASTFieldAccessExpression expr) { + Optional nameOpt = getExprAsQName(expr); + Optional type; + if (nameOpt.isPresent()) { + type = getNameExpressionTypeCalculator(). + typeOfNameAsExpr( + getAsBasicSymbolsScope(expr.getEnclosingScope()), + nameOpt.get() + ); + } + else { + type = Optional.empty(); + } + return type; + } + + /** + * calculates "a" as expression + */ + protected Optional calculateExprQName( + ASTNameExpression expr) { + return getNameExpressionTypeCalculator().typeOfNameAsExpr( + getAsBasicSymbolsScope(expr.getEnclosingScope()), + expr.getName() + ); + } + + /** + * calculates a.b.c as type identifier with a.b being a qualifier. + * only evaluates qualified names without type arguments + * + * s.a. {@link #getExprAsQName(ASTExpression)} + */ + protected Optional calculateTypeIdQName( + ASTFieldAccessExpression expr) { + Optional nameOpt = getExprAsQName(expr); + Optional type; + if (nameOpt.isPresent()) { + type = getNameExpressionTypeCalculator(). + typeOfNameAsTypeId( + getAsBasicSymbolsScope(expr.getEnclosingScope()), + nameOpt.get() + ); + } + else { + type = Optional.empty(); + } + return type; + } + + /** + * calculates "a" as type identifier + */ + protected Optional calculateTypeIdQName( + ASTNameExpression expr) { + return getNameExpressionTypeCalculator().typeOfNameAsTypeId( + getAsBasicSymbolsScope(expr.getEnclosingScope()), + expr.getName() + ); + } + + // Helper + + /** + * For FieldAccessExpression / CallExpression + * given expression "." name, + * expression may be a (qualified) name for + * * a type (for static members) + * * a value (global variable / function) + * this analyses the expression and returns a qualified name if possible, + * which _may_ be of a type / value + * Note: Java (Spec v.20 chapter 19: Syntax) does not allow type arguments, + * e.g., class C{T t;} C.t = 3.2; + */ + protected Optional getExprAsQName(ASTExpression expr) { + if (getTypeDispatcher().isASTNameExpression(expr)) { + ASTNameExpression nameExpr = (ASTNameExpression) expr; + return Optional.of(nameExpr.getName()); + } + else if (getTypeDispatcher().isASTFieldAccessExpression(expr)) { + ASTFieldAccessExpression fieldAccessExpression = + (ASTFieldAccessExpression) expr; + return getExprAsQName(fieldAccessExpression.getExpression()) + .map(qualifier -> + Names.getQualifiedName(qualifier, fieldAccessExpression.getName()) + ); + } + else { + return Optional.empty(); + } + } + + /** + * does the expression have a form like a.b.c.d? + */ + protected boolean isSeriesOfNames(ASTExpression expr) { + if (getTypeDispatcher().isASTNameExpression(expr)) { + return true; + } + if (getTypeDispatcher().isASTFieldAccessExpression(expr)) { + return isSeriesOfNames( + getTypeDispatcher().asASTFieldAccessExpression(expr).getExpression() + ); + } + else { + return false; + } + } + + protected SymTypeExpression calculateNumericPrefix( + ASTExpression innerExpr, String op) { + SymTypeExpression innerType = getType4Ast().getPartialTypeOfExpr(innerExpr); + if (innerType.isObscureType()) { + return createObscureType(); + } + if (!getTypeRel().isNumericType(innerType)) { + Log.error("0xA017D Prefix Operator '" + op + + "' not applicable to " + "'" + innerType.print() + "'", + innerExpr.get_SourcePositionStart(), + innerExpr.get_SourcePositionEnd() + ); + return createObscureType(); + } + // in Java, an evaluation of the actual value + // would take place (if possible) + return getTypeRel().numericPromotion(innerType); + } + + /** + * for <=, >=, <, > + * calculates the resulting type + */ + protected SymTypeExpression calculateNumericComparison( + ASTInfixExpression expr, String op) { + SymTypeExpression left = getType4Ast().getPartialTypeOfExpr(expr.getLeft()); + SymTypeExpression right = getType4Ast().getPartialTypeOfExpr(expr.getRight()); + + if (left.isObscureType() || right.isObscureType()) { + // if any inner obscure then error already logged + return createObscureType(); + } + // if the left and the right part of the expression are numerics, + // then the whole expression is a boolean + else if (getTypeRel().isNumericType(left) && getTypeRel().isNumericType(right)) { + return createPrimitive(BasicSymbolsMill.BOOLEAN); + } + else { + // operator not applicable + Log.error("0xB0167 Operator '" + op + "' not applicable to " + + "'" + left.print() + "', '" + + right.print() + "'", + expr.get_SourcePositionStart(), + expr.get_SourcePositionEnd() + ); + return createObscureType(); + } + } + + /** + * for +, -, *, /, % + * + -> String not supported + * calculates the resulting type + */ + protected SymTypeExpression calculateArithmeticExpression( + ASTInfixExpression expr, String op) { + SymTypeExpression left = getType4Ast().getPartialTypeOfExpr(expr.getLeft()); + SymTypeExpression right = getType4Ast().getPartialTypeOfExpr(expr.getRight()); + + if (left.isObscureType() || right.isObscureType()) { + // if any inner obscure then error already logged + return createObscureType(); + } + else if (getTypeRel().isNumericType(left) && getTypeRel().isNumericType(right)) { + return getTypeRel().numericPromotion(left, right); + } + else { + // operator not applicable + Log.error("0xB0163 Operator '" + op + "' not applicable to " + + "'" + left.print() + "', '" + + right.print() + "'", + expr.get_SourcePositionStart(), + expr.get_SourcePositionEnd() + ); + return createObscureType(); + } + } + + /** + * for ==, != + * calculates the resulting type + */ + protected SymTypeExpression calculateEquality( + ASTInfixExpression expr, String op) { + SymTypeExpression left = getType4Ast().getPartialTypeOfExpr(expr.getLeft()); + SymTypeExpression right = getType4Ast().getPartialTypeOfExpr(expr.getRight()); + + if (left.isObscureType() || right.isObscureType()) { + // if any inner obscure then error already logged + return createObscureType(); + } + else if (left.isPrimitive() || right.isPrimitive()) { + // skip unboxing + numeric promotion if applicable + if (getTypeRel().isNumericType(left) && getTypeRel().isNumericType(right)) { + return SymTypeExpressionFactory.createPrimitive(BasicSymbolsMill.BOOLEAN); + } + left = getTypeRel().unbox(left); + right = getTypeRel().unbox(right); + } + if (getTypeRel().isCompatible(left, right) + || getTypeRel().isCompatible(right, left)) { + return SymTypeExpressionFactory.createPrimitive(BasicSymbolsMill.BOOLEAN); + } + else { + Log.error("0xB0166 Operator '" + op + "' not applicable to " + + "'" + left.print() + "', '" + + right.print() + "'", + expr.get_SourcePositionStart(), + expr.get_SourcePositionEnd() + ); + return createObscureType(); + } + } + + /** + * for &&, || + * calculates the resulting type + */ + protected SymTypeExpression calculateConditionalBooleanOp( + ASTInfixExpression expr, String op) { + SymTypeExpression left = getType4Ast().getPartialTypeOfExpr(expr.getLeft()); + SymTypeExpression right = getType4Ast().getPartialTypeOfExpr(expr.getRight()); + + if (left.isObscureType() || right.isObscureType()) { + // if any inner obscure then error already logged + return createObscureType(); + } + else if (getTypeRel().isBoolean(left) && getTypeRel().isBoolean(right)) { + return createPrimitive(BasicSymbolsMill.BOOLEAN); + } + else { + // operator not applicable + Log.error("0xB0113 Operator '" + op + "' not applicable to " + + "'" + left.print() + "', '" + + right.print() + "'", + expr.get_SourcePositionStart(), + expr.get_SourcePositionEnd() + ); + return createObscureType(); + } + } + + protected CommonExpressionsTypeDispatcher getTypeDispatcher() { + return CommonExpressionsMill.typeDispatcher(); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/expressions/commonexpressions/types3/util/CommonExpressionsLValueRelations.java b/monticore-grammar/src/main/java/de/monticore/expressions/commonexpressions/types3/util/CommonExpressionsLValueRelations.java new file mode 100644 index 0000000000..4da91ba67a --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/expressions/commonexpressions/types3/util/CommonExpressionsLValueRelations.java @@ -0,0 +1,32 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.expressions.commonexpressions.types3.util; + +import de.monticore.expressions.commonexpressions.CommonExpressionsMill; +import de.monticore.expressions.commonexpressions._util.CommonExpressionsTypeDispatcher; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.expressionsbasis.types3.util.ILValueRelations; + +public class CommonExpressionsLValueRelations implements ILValueRelations { + + /** + * according to Java Spec 20 4.12.3 + * Note: this is not an "isAssignable"-check, + * as the variable might be final (s. OOSymbols) and already assigned to. + */ + @Override + public boolean isLValue(ASTExpression expression) { + CommonExpressionsTypeDispatcher dispatcher = + CommonExpressionsMill.typeDispatcher(); + boolean result; + if (dispatcher.isASTNameExpression(expression)) { + result = true; + } + else if (dispatcher.isASTFieldAccessExpression(expression)) { + result = true; + } + else { + result = false; + } + return result; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/expressions/expressionsbasis/_ast/ASTNameExpression.java b/monticore-grammar/src/main/java/de/monticore/expressions/expressionsbasis/_ast/ASTNameExpression.java new file mode 100644 index 0000000000..ee92f95a2e --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/expressions/expressionsbasis/_ast/ASTNameExpression.java @@ -0,0 +1,19 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.expressionsbasis._ast; + +import de.monticore.symboltable.ISymbol; + +import java.util.Optional; + +public class ASTNameExpression extends ASTNameExpressionTOP { + + protected ISymbol definingSymbol; + + public Optional getDefiningSymbol() { + return Optional.ofNullable(this.definingSymbol); + } + + public void setDefiningSymbol(ISymbol symbol) { + this.definingSymbol = symbol; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/expressions/expressionsbasis/types3/ExpressionBasisTypeVisitor.java b/monticore-grammar/src/main/java/de/monticore/expressions/expressionsbasis/types3/ExpressionBasisTypeVisitor.java new file mode 100644 index 0000000000..46b388a696 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/expressions/expressionsbasis/types3/ExpressionBasisTypeVisitor.java @@ -0,0 +1,74 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.expressionsbasis.types3; + +import de.monticore.expressions.expressionsbasis._ast.ASTLiteralExpression; +import de.monticore.expressions.expressionsbasis._ast.ASTNameExpression; +import de.monticore.expressions.expressionsbasis._visitor.ExpressionsBasisVisitor2; +import de.monticore.symbols.basicsymbols._symboltable.IBasicSymbolsScope; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types3.AbstractTypeVisitor; +import de.monticore.types3.util.NameExpressionTypeCalculator; +import de.se_rwth.commons.logging.Log; + +import java.util.Optional; + +public class ExpressionBasisTypeVisitor extends AbstractTypeVisitor + implements ExpressionsBasisVisitor2 { + + protected NameExpressionTypeCalculator nameExpressionTypeCalculator; + + public ExpressionBasisTypeVisitor() { + // default values + nameExpressionTypeCalculator = new NameExpressionTypeCalculator(); + } + + public void setNameExpressionTypeCalculator( + NameExpressionTypeCalculator nameExpressionTypeCalculator) { + this.nameExpressionTypeCalculator = nameExpressionTypeCalculator; + } + + protected NameExpressionTypeCalculator getNameExpressionTypeCalculator() { + return nameExpressionTypeCalculator; + } + + @Override + public void endVisit(ASTNameExpression expr) { + Optional wholeResult = calculateNameExpression(expr); + if (wholeResult.isPresent()) { + getType4Ast().setTypeOfExpression(expr, wholeResult.get()); + } + else { + Log.error("0xFD118 could not find symbol for expression \"" + + expr.getName() + "\"", + expr.get_SourcePositionStart(), + expr.get_SourcePositionEnd() + ); + getType4Ast().setTypeOfExpression(expr, SymTypeExpressionFactory.createObscureType()); + } + } + + protected Optional calculateNameExpression( + ASTNameExpression expr) { + if (expr.getEnclosingScope() == null) { + Log.error("0xFD161 internal error: " + + "enclosing scope of expression expected", + expr.get_SourcePositionStart(), + expr.get_SourcePositionEnd() + ); + return Optional.empty(); + } + + final String name = expr.getName(); + IBasicSymbolsScope enclosingScope = + getAsBasicSymbolsScope(expr.getEnclosingScope()); + return getNameExpressionTypeCalculator().typeOfNameAsExpr(enclosingScope, name); + } + + @Override + public void endVisit(ASTLiteralExpression expr) { + getType4Ast().setTypeOfExpression(expr, + getType4Ast().getPartialTypeOfExpr(expr.getLiteral()) + ); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/AssignmentExpressionsFullJavaPrinter.java b/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/AssignmentExpressionsFullJavaPrinter.java new file mode 100644 index 0000000000..62db9cae5d --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/AssignmentExpressionsFullJavaPrinter.java @@ -0,0 +1,33 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.exptojava; + +import de.monticore.expressions.assignmentexpressions.AssignmentExpressionsMill; +import de.monticore.expressions.assignmentexpressions._visitor.AssignmentExpressionsTraverser; +import de.monticore.prettyprint.IndentPrinter; + +@Deprecated(forRemoval = true) +public class AssignmentExpressionsFullJavaPrinter extends ExpressionsBasisFullJavaPrinter{ + + protected AssignmentExpressionsTraverser traverser; + + @Override + public AssignmentExpressionsTraverser getTraverser() { + return traverser; + } + + public void setTraverser(AssignmentExpressionsTraverser traverser) { + this.traverser = traverser; + } + + public AssignmentExpressionsFullJavaPrinter(IndentPrinter printer) { + super(printer); + this.traverser = AssignmentExpressionsMill.traverser(); + + AssignmentExpressionsJavaPrinter assignmentExpressions = new AssignmentExpressionsJavaPrinter(printer); + traverser.setAssignmentExpressionsHandler(assignmentExpressions); + traverser.add4AssignmentExpressions(assignmentExpressions); + ExpressionsBasisJavaPrinter basisExpression = new ExpressionsBasisJavaPrinter(printer); + traverser.setExpressionsBasisHandler(basisExpression); + traverser.add4ExpressionsBasis(basisExpression); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/AssignmentExpressionsJavaPrinter.java b/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/AssignmentExpressionsJavaPrinter.java new file mode 100644 index 0000000000..778b99328f --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/AssignmentExpressionsJavaPrinter.java @@ -0,0 +1,19 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.exptojava; + +import de.monticore.expressions.prettyprint.AssignmentExpressionsPrettyPrinter; +import de.monticore.prettyprint.IndentPrinter; + +@Deprecated(forRemoval = true) +public class AssignmentExpressionsJavaPrinter extends AssignmentExpressionsPrettyPrinter { + + public AssignmentExpressionsJavaPrinter(IndentPrinter printer) { + super(printer); + this.printer = printer; + } + + public AssignmentExpressionsJavaPrinter() { + super(new IndentPrinter()); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/BitExpressionsFullJavaPrinter.java b/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/BitExpressionsFullJavaPrinter.java new file mode 100644 index 0000000000..ffa20b2754 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/BitExpressionsFullJavaPrinter.java @@ -0,0 +1,39 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.exptojava;/* (c) https://github.com/MontiCore/monticore */ + +import de.monticore.expressions.bitexpressions.BitExpressionsMill; +import de.monticore.expressions.bitexpressions._visitor.BitExpressionsTraverser; +import de.monticore.expressions.prettyprint.BitExpressionsPrettyPrinter; +import de.monticore.expressions.prettyprint.ExpressionsBasisPrettyPrinter; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.prettyprint.MCBasicsPrettyPrinter; + +@Deprecated(forRemoval = true) +public class BitExpressionsFullJavaPrinter extends ExpressionsBasisFullJavaPrinter { + + protected BitExpressionsTraverser traverser; + + @Override + public BitExpressionsTraverser getTraverser() { + return traverser; + } + + public void setTraverser(BitExpressionsTraverser traverser) { + this.traverser = traverser; + } + + public BitExpressionsFullJavaPrinter(IndentPrinter printer) { + + super(printer); + this.traverser = BitExpressionsMill.traverser(); + + ExpressionsBasisJavaPrinter basisExpression = new ExpressionsBasisJavaPrinter(printer); + traverser.setExpressionsBasisHandler(basisExpression); + traverser.add4ExpressionsBasis(basisExpression); + BitExpressionsJavaPrinter bitExpressions = new BitExpressionsJavaPrinter(printer); + traverser.setBitExpressionsHandler(bitExpressions); + traverser.add4BitExpressions(bitExpressions); + + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/BitExpressionsJavaPrinter.java b/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/BitExpressionsJavaPrinter.java new file mode 100644 index 0000000000..8c48d8a8ad --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/BitExpressionsJavaPrinter.java @@ -0,0 +1,18 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.exptojava;/* (c) https://github.com/MontiCore/monticore */ + +import de.monticore.expressions.prettyprint.BitExpressionsPrettyPrinter; +import de.monticore.prettyprint.IndentPrinter; + +@Deprecated(forRemoval = true) +public class BitExpressionsJavaPrinter extends BitExpressionsPrettyPrinter { + + public BitExpressionsJavaPrinter(IndentPrinter printer) { + super(printer); + } + + public BitExpressionsJavaPrinter() { + super(new IndentPrinter()); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/CommonExpressionsFullJavaPrinter.java b/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/CommonExpressionsFullJavaPrinter.java new file mode 100644 index 0000000000..082fe7d606 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/CommonExpressionsFullJavaPrinter.java @@ -0,0 +1,33 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.exptojava; + +import de.monticore.expressions.commonexpressions.CommonExpressionsMill; +import de.monticore.expressions.commonexpressions._visitor.CommonExpressionsTraverser; +import de.monticore.prettyprint.IndentPrinter; + +@Deprecated(forRemoval = true) +public class CommonExpressionsFullJavaPrinter extends ExpressionsBasisFullJavaPrinter { + + protected CommonExpressionsTraverser traverser; + + public CommonExpressionsTraverser getTraverser() { + return traverser; + } + + public void setTraverser(CommonExpressionsTraverser traverser) { + this.traverser = traverser; + } + + public CommonExpressionsFullJavaPrinter(IndentPrinter printer) { + super(printer); + this.traverser = CommonExpressionsMill.traverser(); + + LegacyCommonExpressionsJavaPrinter commonExpressions = new LegacyCommonExpressionsJavaPrinter(printer); + traverser.setCommonExpressionsHandler(commonExpressions); + traverser.add4CommonExpressions(commonExpressions); + ExpressionsBasisJavaPrinter basicExpression = new ExpressionsBasisJavaPrinter(printer); + traverser.setExpressionsBasisHandler(basicExpression); + traverser.add4ExpressionsBasis(basicExpression); + + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/CommonExpressionsJavaPrinter.java b/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/CommonExpressionsJavaPrinter.java new file mode 100644 index 0000000000..5c79619fac --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/CommonExpressionsJavaPrinter.java @@ -0,0 +1,43 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.exptojava; + +import de.monticore.expressions.commonexpressions._ast.ASTFieldAccessExpression; +import de.monticore.expressions.commonexpressions._prettyprint.CommonExpressionsPrettyPrinter; +import de.monticore.expressions.commonexpressions._visitor.CommonExpressionsTraverser; +import de.monticore.prettyprint.IndentPrinter; + +public class CommonExpressionsJavaPrinter extends CommonExpressionsPrettyPrinter { + + public CommonExpressionsJavaPrinter(IndentPrinter printer, boolean printComments) { + super(printer, printComments); + } + + @Override + public void handle(ASTFieldAccessExpression node) { + if (this.isPrintComments()) { + de.monticore.prettyprint.CommentPrettyPrinter.printPreComments(node, getPrinter()); + } + node.getExpression().accept(getTraverser()); + getPrinter().stripTrailing(); + getPrinter().print("."); + String name = "get" + node.getName().substring(0, 1).toUpperCase() + node.getName().substring(1) + "()"; + getPrinter().print(name); + if (this.isPrintComments()) { + de.monticore.prettyprint.CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + } + + /** + * Apply this overridden handling of FieldAccessExpressions to a FullPrettyPrinter + * @param traverser the FullPrettyPrinters traverser + * @param indentPrinter the printer to use + * @param printComments whether to print comments + */ + public static void applyJavaPrinter(T traverser, IndentPrinter indentPrinter, boolean printComments){ + CommonExpressionsJavaPrinter commonExpressionsJavaPrinter = new CommonExpressionsJavaPrinter(indentPrinter, printComments); + traverser.getCommonExpressionsVisitorList().clear(); + traverser.add4CommonExpressions(commonExpressionsJavaPrinter); + traverser.setCommonExpressionsHandler(commonExpressionsJavaPrinter); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/ExpressionsBasisFullJavaPrinter.java b/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/ExpressionsBasisFullJavaPrinter.java new file mode 100644 index 0000000000..905ef4b770 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/ExpressionsBasisFullJavaPrinter.java @@ -0,0 +1,47 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.exptojava; + +import de.monticore.expressions.expressionsbasis.ExpressionsBasisMill; +import de.monticore.expressions.expressionsbasis._ast.ASTExpressionsBasisNode; +import de.monticore.expressions.expressionsbasis._visitor.ExpressionsBasisTraverser; +import de.monticore.prettyprint.IndentPrinter; + +@Deprecated(forRemoval = true) +public class ExpressionsBasisFullJavaPrinter { + + protected ExpressionsBasisTraverser traverser; + + protected IndentPrinter printer; + + public ExpressionsBasisFullJavaPrinter(IndentPrinter printer) { + this.printer = printer; + this.traverser = ExpressionsBasisMill.traverser(); + + ExpressionsBasisJavaPrinter basisExpression = new ExpressionsBasisJavaPrinter(printer); + traverser.setExpressionsBasisHandler(basisExpression); + traverser.add4ExpressionsBasis(basisExpression); + } + + public ExpressionsBasisTraverser getTraverser() { + return traverser; + } + + public void setTraverser(ExpressionsBasisTraverser traverser) { + this.traverser = traverser; + } + + public IndentPrinter getPrinter() { + return printer; + } + + public void setPrinter(IndentPrinter printer) { + this.printer = printer; + } + + public String print(ASTExpressionsBasisNode a) { + getPrinter().clearBuffer(); + a.accept(getTraverser()); + return getPrinter().getContent(); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/ExpressionsBasisJavaPrinter.java b/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/ExpressionsBasisJavaPrinter.java new file mode 100644 index 0000000000..63c1d1588a --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/ExpressionsBasisJavaPrinter.java @@ -0,0 +1,18 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.exptojava;/* (c) https://github.com/MontiCore/monticore */ + +import de.monticore.expressions.prettyprint.ExpressionsBasisPrettyPrinter; +import de.monticore.prettyprint.IndentPrinter; + +@Deprecated(forRemoval = true) +public class ExpressionsBasisJavaPrinter extends ExpressionsBasisPrettyPrinter { + + public ExpressionsBasisJavaPrinter(IndentPrinter printer) { + super(printer); + } + + public ExpressionsBasisJavaPrinter() { + super(new IndentPrinter()); + } + +} \ No newline at end of file diff --git a/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/JavaClassExpressionsFullJavaPrinter.java b/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/JavaClassExpressionsFullJavaPrinter.java new file mode 100644 index 0000000000..fb0ce0c493 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/JavaClassExpressionsFullJavaPrinter.java @@ -0,0 +1,66 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.exptojava; + +import de.monticore.expressions.javaclassexpressions.JavaClassExpressionsMill; +import de.monticore.expressions.javaclassexpressions._ast.ASTGenericInvocationSuffix; +import de.monticore.expressions.javaclassexpressions._ast.ASTTypePattern; +import de.monticore.expressions.javaclassexpressions._visitor.JavaClassExpressionsTraverser; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.statements.mcstatementsbasis._prettyprint.MCStatementsBasisPrettyPrinter; +import de.monticore.statements.prettyprint.MCVarDeclarationStatementsPrettyPrinter; +import de.monticore.types.prettyprint.MCBasicTypesPrettyPrinter; + +@Deprecated(forRemoval = true) +public class JavaClassExpressionsFullJavaPrinter extends CommonExpressionsFullJavaPrinter { + + protected JavaClassExpressionsTraverser traverser; + + @Override + public JavaClassExpressionsTraverser getTraverser() { + return traverser; + } + + public void setTraverser(JavaClassExpressionsTraverser traverser) { + this.traverser = traverser; + } + + public JavaClassExpressionsFullJavaPrinter(IndentPrinter printer) { + super(printer); + this.traverser = JavaClassExpressionsMill.traverser(); + + LegacyCommonExpressionsJavaPrinter commonExpression = new LegacyCommonExpressionsJavaPrinter(printer); + traverser.setCommonExpressionsHandler(commonExpression); + traverser.add4CommonExpressions(commonExpression); + ExpressionsBasisJavaPrinter expressionBasis = new ExpressionsBasisJavaPrinter(printer); + traverser.setExpressionsBasisHandler(expressionBasis); + traverser.add4ExpressionsBasis(expressionBasis); + JavaClassExpressionsJavaPrinter javaClassExpression = new JavaClassExpressionsJavaPrinter(printer); + traverser.setJavaClassExpressionsHandler(javaClassExpression); + traverser.add4JavaClassExpressions(javaClassExpression); + + MCBasicTypesPrettyPrinter mcBasicTypes = new MCBasicTypesPrettyPrinter(printer); + traverser.setMCBasicTypesHandler(mcBasicTypes); + traverser.add4MCBasicTypes(mcBasicTypes); + + MCStatementsBasisPrettyPrinter mcStatementsBasis = new MCStatementsBasisPrettyPrinter(printer, true); + traverser.setMCStatementsBasisHandler(mcStatementsBasis); + traverser.add4MCStatementsBasis(mcStatementsBasis); + + MCVarDeclarationStatementsPrettyPrinter mcVarDeclarationStatements = new MCVarDeclarationStatementsPrettyPrinter(printer); + traverser.setMCVarDeclarationStatementsHandler(mcVarDeclarationStatements); + traverser.add4MCVarDeclarationStatements(mcVarDeclarationStatements); + } + + public String print(ASTGenericInvocationSuffix node) { + getPrinter().clearBuffer(); + node.accept(getTraverser()); + return getPrinter().getContent(); + } + + public String print(ASTTypePattern node) { + getPrinter().clearBuffer(); + node.accept(getTraverser()); + return getPrinter().getContent(); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/JavaClassExpressionsJavaPrinter.java b/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/JavaClassExpressionsJavaPrinter.java new file mode 100644 index 0000000000..b2abe20aa8 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/JavaClassExpressionsJavaPrinter.java @@ -0,0 +1,18 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.exptojava;/* (c) https://github.com/MontiCore/monticore */ + +import de.monticore.expressions.prettyprint.JavaClassExpressionsPrettyPrinter; +import de.monticore.prettyprint.IndentPrinter; + +@Deprecated(forRemoval = true) +public class JavaClassExpressionsJavaPrinter extends JavaClassExpressionsPrettyPrinter { + + public JavaClassExpressionsJavaPrinter(IndentPrinter printer) { + super(printer); + } + + public JavaClassExpressionsJavaPrinter() { + super(new IndentPrinter()); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/LambdaExpressionsFullJavaPrinter.java b/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/LambdaExpressionsFullJavaPrinter.java new file mode 100644 index 0000000000..59557979fe --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/LambdaExpressionsFullJavaPrinter.java @@ -0,0 +1,41 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.exptojava; + +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.lambdaexpressions.LambdaExpressionsMill; +import de.monticore.expressions.lambdaexpressions._visitor.LambdaExpressionsTraverser; +import de.monticore.prettyprint.IndentPrinter; + +@Deprecated(forRemoval = true) +public class LambdaExpressionsFullJavaPrinter extends ExpressionsBasisFullJavaPrinter { + + protected LambdaExpressionsTraverser traverser; + + @Override + public LambdaExpressionsTraverser getTraverser() { + return traverser; + } + + public void setTraverser(LambdaExpressionsTraverser traverser) { + this.traverser = traverser; + } + + public LambdaExpressionsFullJavaPrinter(IndentPrinter printer) { + super(printer); + this.traverser = LambdaExpressionsMill.traverser(); + + ExpressionsBasisJavaPrinter expressionBasis = new ExpressionsBasisJavaPrinter(printer); + traverser.setExpressionsBasisHandler(expressionBasis); + traverser.add4ExpressionsBasis(expressionBasis); + LambdaExpressionsJavaPrinter javaClassExpression = new LambdaExpressionsJavaPrinter(printer); + traverser.setLambdaExpressionsHandler(javaClassExpression); + traverser.add4LambdaExpressions(javaClassExpression); + } + + public String print(ASTExpression node) { + getPrinter().clearBuffer(); + node.accept(getTraverser()); + return getPrinter().getContent(); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/LambdaExpressionsJavaPrinter.java b/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/LambdaExpressionsJavaPrinter.java new file mode 100644 index 0000000000..cec2edbda2 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/LambdaExpressionsJavaPrinter.java @@ -0,0 +1,18 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.exptojava;/* (c) https://github.com/MontiCore/monticore */ + +import de.monticore.expressions.prettyprint.LambdaExpressionsPrettyPrinter; +import de.monticore.prettyprint.IndentPrinter; + +@Deprecated(forRemoval = true) +public class LambdaExpressionsJavaPrinter extends LambdaExpressionsPrettyPrinter { + + public LambdaExpressionsJavaPrinter(IndentPrinter printer) { + super(printer); + } + + public LambdaExpressionsJavaPrinter() { + super(new IndentPrinter()); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/LegacyCommonExpressionsJavaPrinter.java b/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/LegacyCommonExpressionsJavaPrinter.java new file mode 100644 index 0000000000..e0217f788b --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/expressions/exptojava/LegacyCommonExpressionsJavaPrinter.java @@ -0,0 +1,53 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.exptojava; + +import de.monticore.expressions.commonexpressions._ast.ASTFieldAccessExpression; +import de.monticore.expressions.commonexpressions._visitor.CommonExpressionsTraverser; +import de.monticore.expressions.prettyprint.CommonExpressionsPrettyPrinter; +import de.monticore.prettyprint.IndentPrinter; + +/** + * @deprecated use {@link CommonExpressionsJavaPrinter} instead + */ +@Deprecated(forRemoval = true) +public class LegacyCommonExpressionsJavaPrinter extends CommonExpressionsPrettyPrinter { + + protected CommonExpressionsTraverser traverser; + + protected IndentPrinter printer; + + @Override + public CommonExpressionsTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(CommonExpressionsTraverser traverser) { + this.traverser = traverser; + } + + public IndentPrinter getPrinter() { + return printer; + } + + public void setPrinter(IndentPrinter printer) { + this.printer = printer; + } + + public LegacyCommonExpressionsJavaPrinter(IndentPrinter printer) { + super(printer); + this.printer = printer; + } + + public LegacyCommonExpressionsJavaPrinter(){ + super(new IndentPrinter()); + } + + @Override //TODO: READD ME + public void handle(ASTFieldAccessExpression node) { + node.getExpression().accept(getTraverser()); + String name = "get"+ node.getName().substring(0,1).toUpperCase() + node.getName().substring(1)+"()"; + getPrinter().print("."+name); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/expressions/lambdaexpressions/_symboltable/LambdaExpressionsSTCompleteTypes.java b/monticore-grammar/src/main/java/de/monticore/expressions/lambdaexpressions/_symboltable/LambdaExpressionsSTCompleteTypes.java new file mode 100644 index 0000000000..95fc2b40eb --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/expressions/lambdaexpressions/_symboltable/LambdaExpressionsSTCompleteTypes.java @@ -0,0 +1,39 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.expressions.lambdaexpressions._symboltable; + +import de.monticore.expressions.lambdaexpressions._ast.ASTLambdaParameter; +import de.monticore.expressions.lambdaexpressions._visitor.LambdaExpressionsVisitor2; +import de.monticore.types.check.ISynthesize; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeOfNull; +import de.monticore.types.check.TypeCheckResult; +import de.monticore.types.mcbasictypes._ast.ASTMCType; + +public class LambdaExpressionsSTCompleteTypes implements LambdaExpressionsVisitor2 { + + ISynthesize synthesize; + + public LambdaExpressionsSTCompleteTypes(ISynthesize synthesize) { + this.synthesize = synthesize; + } + + protected ISynthesize getSynthesize() { + return synthesize; + } + + @Override + public void endVisit(ASTLambdaParameter ast) { + if (ast.isPresentMCType()) { + ast.getSymbol().setType(createTypeLoader(ast.getMCType())); + } + } + + protected SymTypeExpression createTypeLoader(ASTMCType ast) { + TypeCheckResult typeCheckResult = getSynthesize().synthesizeType(ast); + if (typeCheckResult.isPresentResult()) { + return typeCheckResult.getResult(); + } + return new SymTypeOfNull(); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/expressions/lambdaexpressions/_symboltable/LambdaExpressionsSTCompleteTypes2.java b/monticore-grammar/src/main/java/de/monticore/expressions/lambdaexpressions/_symboltable/LambdaExpressionsSTCompleteTypes2.java new file mode 100644 index 0000000000..67c48f100b --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/expressions/lambdaexpressions/_symboltable/LambdaExpressionsSTCompleteTypes2.java @@ -0,0 +1,56 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.expressions.lambdaexpressions._symboltable; + +import de.monticore.expressions.lambdaexpressions._ast.ASTLambdaParameter; +import de.monticore.expressions.lambdaexpressions._visitor.LambdaExpressionsVisitor2; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types3.Type4Ast; +import de.monticore.visitor.ITraverser; + +public class LambdaExpressionsSTCompleteTypes2 implements LambdaExpressionsVisitor2 { + + // to be moved into the mill after details are discussed + @Deprecated + private Type4Ast type4Ast = null; + + // the traverser filling the type map + // not required, if the map is already filled + protected ITraverser typeTraverser; + + protected Type4Ast getTypeMap() { + return type4Ast; + } + + protected ITraverser getTypeTraverser() { + return typeTraverser; + } + + // after moving the typemap, the other constructor ought to be used + @Deprecated + public LambdaExpressionsSTCompleteTypes2(ITraverser typeTraverser, Type4Ast type4Ast) { + this(typeTraverser); + this.type4Ast = type4Ast; + } + + public LambdaExpressionsSTCompleteTypes2(ITraverser typeTraverser) { + this.typeTraverser = typeTraverser; + } + + @Override + public void endVisit(ASTLambdaParameter ast) { + if (ast.isPresentMCType()) { + ast.getSymbol().setType(calculateType(ast.getMCType())); + } + } + + // Helper + + protected SymTypeExpression calculateType(ASTMCType mcType) { + if (!getTypeMap().hasTypeOfTypeIdentifier(mcType)) { + mcType.accept(getTypeTraverser()); + } + return getTypeMap().getTypeOfTypeIdentifier(mcType); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/expressions/lambdaexpressions/types3/LambdaExpressionsTypeVisitor.java b/monticore-grammar/src/main/java/de/monticore/expressions/lambdaexpressions/types3/LambdaExpressionsTypeVisitor.java new file mode 100644 index 0000000000..f01c09801a --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/expressions/lambdaexpressions/types3/LambdaExpressionsTypeVisitor.java @@ -0,0 +1,56 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.lambdaexpressions.types3; + +import de.monticore.expressions.lambdaexpressions._ast.ASTLambdaExpression; +import de.monticore.expressions.lambdaexpressions._ast.ASTLambdaExpressionBody; +import de.monticore.expressions.lambdaexpressions._ast.ASTLambdaParameter; +import de.monticore.expressions.lambdaexpressions._visitor.LambdaExpressionsVisitor2; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types3.AbstractTypeVisitor; +import de.se_rwth.commons.logging.Log; + +import java.util.LinkedList; +import java.util.List; + +public class LambdaExpressionsTypeVisitor extends AbstractTypeVisitor + implements LambdaExpressionsVisitor2 { + + @Override + public void endVisit(ASTLambdaExpression exp) { + SymTypeExpression returnType = exp.getLambdaBody().getType(); + + List parameters = new LinkedList<>(); + boolean obscureParams = false; + for (ASTLambdaParameter parameter : exp.getLambdaParameters().getLambdaParameterList()) { + SymTypeExpression paramSymType = calculateTypeOfLambdaParameter(parameter); + parameters.add(paramSymType); + if (paramSymType.isObscureType()) { + obscureParams = true; + } + } + + if (!returnType.isObscureType() && !obscureParams) { + SymTypeExpression wholeResult = + SymTypeExpressionFactory.createFunction(returnType, parameters); + getType4Ast().setTypeOfExpression(exp, wholeResult); + } + else { + getType4Ast().setTypeOfExpression(exp, SymTypeExpressionFactory.createObscureType()); + } + } + + @Override + public void endVisit(ASTLambdaExpressionBody body) { + body.setType(getType4Ast().getPartialTypeOfExpr(body.getExpression())); + } + + protected SymTypeExpression calculateTypeOfLambdaParameter(ASTLambdaParameter parameter) { + if (parameter.isPresentMCType()) { + return getType4Ast().getPartialTypeOfTypeId(parameter.getMCType()); + } + Log.error("0xBC373 unable to calculate type of lambda parameter", + parameter.get_SourcePositionStart(), parameter.get_SourcePositionEnd()); + return SymTypeExpressionFactory.createObscureType(); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/expressions/prettyprint/AssignmentExpressionsFullPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/expressions/prettyprint/AssignmentExpressionsFullPrettyPrinter.java new file mode 100644 index 0000000000..738939afc8 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/expressions/prettyprint/AssignmentExpressionsFullPrettyPrinter.java @@ -0,0 +1,37 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.prettyprint; + +import de.monticore.expressions.assignmentexpressions.AssignmentExpressionsMill; +import de.monticore.expressions.assignmentexpressions._visitor.AssignmentExpressionsTraverser; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.prettyprint.MCBasicsPrettyPrinter; + + +@Deprecated(forRemoval = true) +public class AssignmentExpressionsFullPrettyPrinter extends ExpressionsBasisFullPrettyPrinter { + + protected AssignmentExpressionsTraverser traverser; + + public AssignmentExpressionsFullPrettyPrinter(IndentPrinter printer) { + super(printer); + this.traverser = AssignmentExpressionsMill.traverser(); + + AssignmentExpressionsPrettyPrinter assignmentExpressions = new AssignmentExpressionsPrettyPrinter(printer); + traverser.setAssignmentExpressionsHandler(assignmentExpressions); + traverser.add4AssignmentExpressions(assignmentExpressions); + ExpressionsBasisPrettyPrinter basisExpression = new ExpressionsBasisPrettyPrinter(printer); + traverser.setExpressionsBasisHandler(basisExpression); + traverser.add4ExpressionsBasis(basisExpression); + MCBasicsPrettyPrinter basic = new MCBasicsPrettyPrinter(printer); + traverser.add4MCBasics(basic); + } + + + public AssignmentExpressionsTraverser getTraverser() { + return traverser; + } + + public void setTraverser(AssignmentExpressionsTraverser traverser) { + this.traverser = traverser; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/expressions/prettyprint/AssignmentExpressionsPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/expressions/prettyprint/AssignmentExpressionsPrettyPrinter.java new file mode 100644 index 0000000000..36f028904d --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/expressions/prettyprint/AssignmentExpressionsPrettyPrinter.java @@ -0,0 +1,131 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.prettyprint; + +import de.monticore.expressions.assignmentexpressions._ast.*; +import de.monticore.expressions.assignmentexpressions._visitor.AssignmentExpressionsHandler; +import de.monticore.expressions.assignmentexpressions._visitor.AssignmentExpressionsTraverser; +import de.monticore.expressions.assignmentexpressions._visitor.AssignmentExpressionsVisitor2; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.prettyprint.CommentPrettyPrinter; +import de.monticore.prettyprint.IndentPrinter; +import de.se_rwth.commons.logging.Log; + +@Deprecated(forRemoval = true) +public class AssignmentExpressionsPrettyPrinter implements AssignmentExpressionsVisitor2, AssignmentExpressionsHandler { + + protected AssignmentExpressionsTraverser traverser; + + @Override + public AssignmentExpressionsTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(AssignmentExpressionsTraverser traverser) { + this.traverser = traverser; + } + + protected IndentPrinter printer; + + + public void setPrinter(IndentPrinter printer) { + this.printer = printer; + } + + public AssignmentExpressionsPrettyPrinter(IndentPrinter printer) { + this.printer = printer; + } + + @Override + public void handle(ASTIncSuffixExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + node.getExpression().accept(getTraverser()); + getPrinter().print("++"); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTDecSuffixExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + node.getExpression().accept(getTraverser()); + getPrinter().print("--"); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTIncPrefixExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + getPrinter().print("++"); + node.getExpression().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTDecPrefixExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + getPrinter().print("--"); + node.getExpression().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTAssignmentExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + node.getLeft().accept(getTraverser()); + // ["="|"+="|"-="|"*="|"/="|"&="|"|="|"^="|">>="|">>>="|"<<="|"%="] + switch (node.getOperator()) { + case ASTConstantsAssignmentExpressions.EQUALS: + getPrinter().print(("=")); + break; + case ASTConstantsAssignmentExpressions.PLUSEQUALS: + getPrinter().print(("+=")); + break; + case ASTConstantsAssignmentExpressions.MINUSEQUALS: + getPrinter().print(("-=")); + break; + case ASTConstantsAssignmentExpressions.STAREQUALS: + getPrinter().print(("*=")); + break; + case ASTConstantsAssignmentExpressions.SLASHEQUALS: + getPrinter().print(("/=")); + break; + case ASTConstantsAssignmentExpressions.AND_EQUALS: + getPrinter().print(("&=")); + break; + case ASTConstantsAssignmentExpressions.PIPEEQUALS: + getPrinter().print(("|=")); + break; + case ASTConstantsAssignmentExpressions.ROOFEQUALS: + getPrinter().print(("^=")); + break; + case ASTConstantsAssignmentExpressions.GTGTEQUALS: + getPrinter().print((">>=")); + break; + case ASTConstantsAssignmentExpressions.GTGTGTEQUALS: + getPrinter().print((">>>=")); + break; + case ASTConstantsAssignmentExpressions.LTLTEQUALS: + getPrinter().print(("<<=")); + break; + case ASTConstantsAssignmentExpressions.PERCENTEQUALS: + getPrinter().print(("%=")); + break; + default: + Log.error("0xA0114 Missing implementation for RegularAssignmentExpression"); + } + node.getRight().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + + public IndentPrinter getPrinter() { + return this.printer; + } + + public String prettyprint(ASTExpression node) { + getPrinter().clearBuffer(); + node.accept(getTraverser()); + return getPrinter().getContent(); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/expressions/prettyprint/BitExpressionsFullPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/expressions/prettyprint/BitExpressionsFullPrettyPrinter.java new file mode 100644 index 0000000000..29f02ae668 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/expressions/prettyprint/BitExpressionsFullPrettyPrinter.java @@ -0,0 +1,36 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.prettyprint; + +import de.monticore.expressions.bitexpressions.BitExpressionsMill; +import de.monticore.expressions.bitexpressions._visitor.BitExpressionsTraverser; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.prettyprint.MCBasicsPrettyPrinter; + +@Deprecated(forRemoval = true) +public class BitExpressionsFullPrettyPrinter extends ExpressionsBasisFullPrettyPrinter { + + protected BitExpressionsTraverser traverser; + + public BitExpressionsFullPrettyPrinter(IndentPrinter printer) { + super(printer); + this.traverser = BitExpressionsMill.traverser(); + + ExpressionsBasisPrettyPrinter basisExpression = new ExpressionsBasisPrettyPrinter(printer); + traverser.setExpressionsBasisHandler(basisExpression); + traverser.add4ExpressionsBasis(basisExpression); + MCBasicsPrettyPrinter basics = new MCBasicsPrettyPrinter(printer); + traverser.add4MCBasics(basics); + BitExpressionsPrettyPrinter bitExpressions = new BitExpressionsPrettyPrinter(printer); + traverser.setBitExpressionsHandler(bitExpressions); + traverser.add4BitExpressions(bitExpressions); + } + + @Override + public BitExpressionsTraverser getTraverser() { + return traverser; + } + + public void setTraverser(BitExpressionsTraverser traverser) { + this.traverser = traverser; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/expressions/prettyprint/BitExpressionsPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/expressions/prettyprint/BitExpressionsPrettyPrinter.java new file mode 100644 index 0000000000..37baf0a176 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/expressions/prettyprint/BitExpressionsPrettyPrinter.java @@ -0,0 +1,89 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.prettyprint; + +import de.monticore.expressions.bitexpressions._visitor.BitExpressionsHandler; +import de.monticore.expressions.bitexpressions._visitor.BitExpressionsTraverser; +import de.monticore.expressions.bitexpressions._visitor.BitExpressionsVisitor2; +import de.monticore.prettyprint.CommentPrettyPrinter; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.expressions.bitexpressions._ast.*; + +@Deprecated(forRemoval = true) +public class BitExpressionsPrettyPrinter implements BitExpressionsVisitor2, BitExpressionsHandler { + + protected BitExpressionsTraverser traverser; + + protected IndentPrinter printer; + + public BitExpressionsPrettyPrinter(IndentPrinter printer) { + this.printer = printer; + } + + @Override + public BitExpressionsTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(BitExpressionsTraverser traverser) { + this.traverser = traverser; + } + + @Override + public void handle(ASTLeftShiftExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + node.getLeft().accept(getTraverser()); + getPrinter().print("<<"); + node.getRight().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTRightShiftExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + node.getLeft().accept(getTraverser()); + getPrinter().print(">>"); + node.getRight().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTLogicalRightShiftExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + node.getLeft().accept(getTraverser()); + getPrinter().print(">>>"); + node.getRight().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTBinaryAndExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + node.getLeft().accept(getTraverser()); + getPrinter().print("&"); + node.getRight().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTBinaryXorExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + node.getLeft().accept(getTraverser()); + getPrinter().print("^"); + node.getRight().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTBinaryOrOpExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + node.getLeft().accept(getTraverser()); + getPrinter().print("|"); + node.getRight().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + public IndentPrinter getPrinter() { + return this.printer; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/expressions/prettyprint/CommonExpressionsFullPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/expressions/prettyprint/CommonExpressionsFullPrettyPrinter.java new file mode 100644 index 0000000000..af858fe3ff --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/expressions/prettyprint/CommonExpressionsFullPrettyPrinter.java @@ -0,0 +1,36 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.prettyprint; + +import de.monticore.expressions.commonexpressions.CommonExpressionsMill; +import de.monticore.expressions.commonexpressions._visitor.CommonExpressionsTraverser; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.prettyprint.MCBasicsPrettyPrinter; + +@Deprecated(forRemoval = true) +public class CommonExpressionsFullPrettyPrinter extends ExpressionsBasisFullPrettyPrinter { + protected CommonExpressionsTraverser traverser; + + @Override + public CommonExpressionsTraverser getTraverser() { + return traverser; + } + + public void setTraverser(CommonExpressionsTraverser traverser) { + this.traverser = traverser; + } + + public CommonExpressionsFullPrettyPrinter(IndentPrinter printer) { + super(printer); + this.traverser = CommonExpressionsMill.traverser(); + + CommonExpressionsPrettyPrinter commonExpressions = new CommonExpressionsPrettyPrinter(printer); + traverser.setCommonExpressionsHandler(commonExpressions); + traverser.add4CommonExpressions(commonExpressions); + ExpressionsBasisPrettyPrinter basicExpression = new ExpressionsBasisPrettyPrinter(printer); + traverser.setExpressionsBasisHandler(basicExpression); + traverser.add4ExpressionsBasis(basicExpression); + MCBasicsPrettyPrinter basic = new MCBasicsPrettyPrinter(printer); + traverser.add4MCBasics(basic); + } +} + diff --git a/monticore-grammar/src/main/java/de/monticore/expressions/prettyprint/CommonExpressionsPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/expressions/prettyprint/CommonExpressionsPrettyPrinter.java new file mode 100644 index 0000000000..9836769a3b --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/expressions/prettyprint/CommonExpressionsPrettyPrinter.java @@ -0,0 +1,239 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.prettyprint; + +import de.monticore.expressions.commonexpressions._ast.*; +import de.monticore.expressions.commonexpressions._visitor.CommonExpressionsHandler; +import de.monticore.expressions.commonexpressions._visitor.CommonExpressionsTraverser; +import de.monticore.expressions.commonexpressions._visitor.CommonExpressionsVisitor2; +import de.monticore.expressions.expressionsbasis._ast.ASTArguments; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.prettyprint.CommentPrettyPrinter; +import de.monticore.prettyprint.IndentPrinter; + +@Deprecated(forRemoval = true) +public class CommonExpressionsPrettyPrinter implements CommonExpressionsVisitor2, CommonExpressionsHandler { + + protected CommonExpressionsTraverser traverser; + + protected IndentPrinter printer; + + @Override + public void setTraverser(CommonExpressionsTraverser traverser) { + this.traverser = traverser; + } + + public void setPrinter(IndentPrinter printer) { + this.printer = printer; + } + + @Override + public CommonExpressionsTraverser getTraverser() { + return traverser; + } + + public CommonExpressionsPrettyPrinter(IndentPrinter printer) { + this.printer = printer; + } + + @Override + public void handle(ASTPlusPrefixExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + getPrinter().print("+"); + node.getExpression().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTMinusPrefixExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + getPrinter().print("-"); + node.getExpression().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTFieldAccessExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + node.getExpression().accept(getTraverser()); + getPrinter().print("." + node.getName()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTMultExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + node.getLeft().accept(getTraverser()); + getPrinter().print(" * "); + node.getRight().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTDivideExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + node.getLeft().accept(getTraverser()); + getPrinter().print(" / "); + node.getRight().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTModuloExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + node.getLeft().accept(getTraverser()); + getPrinter().print(" % "); + node.getRight().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTPlusExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + node.getLeft().accept(getTraverser()); + getPrinter().print(" + "); + node.getRight().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTMinusExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + node.getLeft().accept(getTraverser()); + getPrinter().print(" - "); + node.getRight().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTLessEqualExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + node.getLeft().accept(getTraverser()); + getPrinter().print(" <= "); + node.getRight().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTGreaterEqualExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + node.getLeft().accept(getTraverser()); + getPrinter().print(" >= "); + node.getRight().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTLessThanExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + node.getLeft().accept(getTraverser()); + getPrinter().print(" < "); + node.getRight().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTGreaterThanExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + node.getLeft().accept(getTraverser()); + getPrinter().print(" > "); + node.getRight().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTBooleanAndOpExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + node.getLeft().accept(getTraverser()); + getPrinter().print(" && "); + node.getRight().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTBooleanOrOpExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + node.getLeft().accept(getTraverser()); + getPrinter().print(" || "); + node.getRight().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTEqualsExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + node.getLeft().accept(getTraverser()); + getPrinter().print(" == "); + node.getRight().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTNotEqualsExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + node.getLeft().accept(getTraverser()); + getPrinter().print(" != "); + node.getRight().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTConditionalExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + node.getCondition().accept(getTraverser()); + getPrinter().print(" ? "); + node.getTrueExpression().accept(getTraverser()); + getPrinter().print(" : "); + node.getFalseExpression().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTBracketExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + getPrinter().print("("); + node.getExpression().accept(getTraverser()); + getPrinter().print(")"); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTBooleanNotExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + getPrinter().print("~"); + node.getExpression().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTLogicalNotExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + getPrinter().print("!"); + node.getExpression().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTCallExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + node.getExpression().accept(getTraverser()); + node.getArguments().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + public IndentPrinter getPrinter() { + return this.printer; + } + + public String prettyprint(ASTExpression node) { + getPrinter().clearBuffer(); + node.accept(getTraverser()); + return getPrinter().getContent(); + } + + public String prettyprint(ASTArguments node) { + getPrinter().clearBuffer(); + node.accept(getTraverser()); + return getPrinter().getContent(); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/expressions/prettyprint/ExpressionsBasisFullPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/expressions/prettyprint/ExpressionsBasisFullPrettyPrinter.java new file mode 100644 index 0000000000..38bf182ca9 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/expressions/prettyprint/ExpressionsBasisFullPrettyPrinter.java @@ -0,0 +1,54 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.prettyprint; + +import de.monticore.expressions.expressionsbasis.ExpressionsBasisMill; +import de.monticore.expressions.expressionsbasis._ast.ASTExpressionsBasisNode; +import de.monticore.expressions.expressionsbasis._visitor.ExpressionsBasisTraverser; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.prettyprint.MCBasicsPrettyPrinter; + +@Deprecated(forRemoval = true) +public class ExpressionsBasisFullPrettyPrinter { + protected ExpressionsBasisTraverser traverser; + + protected IndentPrinter printer; + + public ExpressionsBasisFullPrettyPrinter(IndentPrinter printer) { + this.printer = printer; + this.traverser = ExpressionsBasisMill.traverser(); + + ExpressionsBasisPrettyPrinter basisExpression = new ExpressionsBasisPrettyPrinter(printer); + traverser.setExpressionsBasisHandler(basisExpression); + traverser.add4ExpressionsBasis(basisExpression); + MCBasicsPrettyPrinter basics = new MCBasicsPrettyPrinter(printer); + traverser.add4MCBasics(basics); + } + + public IndentPrinter getPrinter() { + return printer; + } + + public void setPrinter(IndentPrinter printer) { + this.printer = printer; + } + + public ExpressionsBasisTraverser getTraverser() { + return traverser; + } + + public void setTraverser(ExpressionsBasisTraverser traverser) { + this.traverser = traverser; + } + + /** + * This method prettyprints a given node from type grammar. + * + * @param a A node from type grammar. + * @return String representation. + */ + public String prettyprint(ASTExpressionsBasisNode a) { + getPrinter().clearBuffer(); + a.accept(getTraverser()); + return getPrinter().getContent(); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/expressions/prettyprint/ExpressionsBasisPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/expressions/prettyprint/ExpressionsBasisPrettyPrinter.java new file mode 100644 index 0000000000..7475c18646 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/expressions/prettyprint/ExpressionsBasisPrettyPrinter.java @@ -0,0 +1,74 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.prettyprint; + +import de.monticore.expressions.expressionsbasis._ast.ASTArguments; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.expressionsbasis._ast.ASTLiteralExpression; +import de.monticore.expressions.expressionsbasis._ast.ASTNameExpression; +import de.monticore.expressions.expressionsbasis._visitor.ExpressionsBasisHandler; +import de.monticore.expressions.expressionsbasis._visitor.ExpressionsBasisTraverser; +import de.monticore.expressions.expressionsbasis._visitor.ExpressionsBasisVisitor2; +import de.monticore.prettyprint.CommentPrettyPrinter; +import de.monticore.prettyprint.IndentPrinter; + +@Deprecated(forRemoval = true) +public class ExpressionsBasisPrettyPrinter implements ExpressionsBasisVisitor2, ExpressionsBasisHandler { + + protected ExpressionsBasisTraverser traverser; + + @Override + public void setTraverser(ExpressionsBasisTraverser traverser) { + this.traverser = traverser; + } + + @Override + public ExpressionsBasisTraverser getTraverser() { + return traverser; + } + + protected IndentPrinter printer; + + public ExpressionsBasisPrettyPrinter(IndentPrinter printer) { + this.printer = printer; + } + + public IndentPrinter getPrinter() { + return printer; + } + + @Override + public void handle(ASTNameExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + getPrinter().print(node.getName()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTLiteralExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + node.getLiteral().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTArguments node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + getPrinter().print("("); + String divider = ""; + if (!node.isEmptyExpressions()) { + for (ASTExpression ast : node.getExpressionList()) { + getPrinter().print(divider); + ast.accept(getTraverser()); + divider = ","; + } + } + getPrinter().print(")"); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + public String prettyprint(ASTExpression node) { + getPrinter().clearBuffer(); + node.accept(getTraverser()); + return getPrinter().getContent(); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/expressions/prettyprint/JavaClassExpressionsFullPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/expressions/prettyprint/JavaClassExpressionsFullPrettyPrinter.java new file mode 100644 index 0000000000..99194c2a1d --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/expressions/prettyprint/JavaClassExpressionsFullPrettyPrinter.java @@ -0,0 +1,45 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.prettyprint; + +import de.monticore.expressions.javaclassexpressions.JavaClassExpressionsMill; +import de.monticore.expressions.javaclassexpressions._ast.ASTGenericInvocationSuffix; +import de.monticore.expressions.javaclassexpressions._visitor.JavaClassExpressionsTraverser; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.prettyprint.MCBasicsPrettyPrinter; + +@Deprecated(forRemoval = true) +public class JavaClassExpressionsFullPrettyPrinter extends CommonExpressionsFullPrettyPrinter { + + protected JavaClassExpressionsTraverser traverser; + + @Override + public JavaClassExpressionsTraverser getTraverser() { + return traverser; + } + + public void setTraverser(JavaClassExpressionsTraverser traverser) { + this.traverser = traverser; + } + + public JavaClassExpressionsFullPrettyPrinter(IndentPrinter printer) { + super(printer); + this.traverser = JavaClassExpressionsMill.traverser(); + CommonExpressionsPrettyPrinter commonExpression = new CommonExpressionsPrettyPrinter(printer); + traverser.setCommonExpressionsHandler(commonExpression); + traverser.add4CommonExpressions(commonExpression); + ExpressionsBasisPrettyPrinter expressionBasis = new ExpressionsBasisPrettyPrinter(printer); + traverser.setExpressionsBasisHandler(expressionBasis); + traverser.add4ExpressionsBasis(expressionBasis); + JavaClassExpressionsPrettyPrinter javaClassExpression = new JavaClassExpressionsPrettyPrinter(printer); + traverser.setJavaClassExpressionsHandler(javaClassExpression); + traverser.add4JavaClassExpressions(javaClassExpression); + MCBasicsPrettyPrinter basic = new MCBasicsPrettyPrinter(printer); + traverser.add4MCBasics(basic); + } + + public String prettyprint(ASTGenericInvocationSuffix node){ + getPrinter().clearBuffer(); + node.accept(getTraverser()); + return getPrinter().getContent(); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/expressions/prettyprint/JavaClassExpressionsPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/expressions/prettyprint/JavaClassExpressionsPrettyPrinter.java new file mode 100644 index 0000000000..2ab40162ea --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/expressions/prettyprint/JavaClassExpressionsPrettyPrinter.java @@ -0,0 +1,250 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.prettyprint; + +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.javaclassexpressions._ast.*; +import de.monticore.expressions.javaclassexpressions._visitor.JavaClassExpressionsHandler; +import de.monticore.expressions.javaclassexpressions._visitor.JavaClassExpressionsTraverser; +import de.monticore.expressions.javaclassexpressions._visitor.JavaClassExpressionsVisitor2; +import de.monticore.prettyprint.CommentPrettyPrinter; +import de.monticore.prettyprint.IndentPrinter; + +import java.util.List; + +@Deprecated(forRemoval = true) +public class JavaClassExpressionsPrettyPrinter implements JavaClassExpressionsVisitor2, JavaClassExpressionsHandler { + + protected JavaClassExpressionsTraverser traverser; + + @Override + public void setTraverser(JavaClassExpressionsTraverser traverser) { + this.traverser = traverser; + } + + @Override + public JavaClassExpressionsTraverser getTraverser() { + return traverser; + } + + public void setPrinter(IndentPrinter printer) { + this.printer = printer; + } + + protected IndentPrinter printer; + + public JavaClassExpressionsPrettyPrinter(IndentPrinter printer) { + this.printer = printer; + } + + @Override + public void visit(ASTPrimarySuperExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + getPrinter().print("super"); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTSuperSuffix node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + if (node.isPresentName()) { + getPrinter().print("."); + if (!node.getExtTypeArgumentList().isEmpty()) { + handleTypeArguments(node.getExtTypeArgumentList()); + } + getPrinter().print(node.getName()); + if (node.isPresentArguments()) { + node.getArguments().accept(getTraverser()); + } + } else { + node.getArguments().accept(getTraverser()); + } + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + protected void handleTypeArguments(List typeArguments){ + getPrinter().print("<"); + for (int i = 0; i < typeArguments.size(); i++) { + typeArguments.get(i).accept(getTraverser()); + if (i != typeArguments.size() - 1) { + getPrinter().print(","); + } + } + getPrinter().print(">"); + } + + @Override + public void handle(ASTSuperExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + node.getExpression().accept(getTraverser()); + getPrinter().print(".super"); + node.getSuperSuffix().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTClassExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + node.getExtReturnType().accept(getTraverser()); + getPrinter().print(".class"); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTTypeCastExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + getPrinter().print("("); + node.getExtType().accept(getTraverser()); + getPrinter().print(")"); + node.getExpression().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTGenericInvocationSuffix node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + if (node.isPresentSuperSuffix()) { + if (node.isSuper()) { + getPrinter().print("super"); + } + node.getSuperSuffix().accept(getTraverser()); + } else if (node.isPresentName()) { + getPrinter().print(node.getName()); + node.getArguments().accept(getTraverser()); + } else { + if (node.isThis()) { + getPrinter().print("this"); + } + node.getArguments().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + } + + @Override + public void handle(ASTGenericInvocationExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + node.getExpression().accept(getTraverser()); + getPrinter().print("."); + handle(node.getPrimaryGenericInvocationExpression()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTPrimaryGenericInvocationExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + getPrinter().print("<"); + for (int i = 0; i < node.getExtTypeArgumentList().size(); i++) { + node.getExtTypeArgument(i).accept(getTraverser()); + if (i != node.getExtTypeArgumentList().size() - 1) { + getPrinter().print(","); + } + } + getPrinter().print(">"); + getPrinter().print(" "); + node.getGenericInvocationSuffix().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTInstanceofExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + node.getExpression().accept(getTraverser()); + getPrinter().print(" instanceof "); + + if (node.isPresentExtType()) { + node.getExtType().accept(getTraverser()); + } else { + node.getPattern().accept(getTraverser()); + } + + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTThisExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + node.getExpression().accept(getTraverser()); + getPrinter().print("."); + getPrinter().print("this"); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTArrayExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + node.getExpression().accept(getTraverser()); + getPrinter().print("["); + node.getIndexExpression().accept(getTraverser()); + getPrinter().print("]"); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void visit(ASTPrimaryThisExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + getPrinter().print("this"); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTArrayCreator a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + a.getExtType().accept(getTraverser()); + a.getArrayDimensionSpecifier().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTArrayDimensionByExpression a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + for (ASTExpression astExpression : a.getExpressionList()) { + getPrinter().print("["); + astExpression.accept(getTraverser()); + getPrinter().print("]"); + } + for (int i = 0; i < a.getDimList().size(); i++) { + getPrinter().print("[]"); + } + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + + @Override + public void handle(ASTCreatorExpression a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + getPrinter().print(" new "); + a.getCreator().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTTypePattern node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + node.getLocalVariableDeclaration().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + public IndentPrinter getPrinter() { + return this.printer; + } + + public String prettyprint(ASTExpression node) { + getPrinter().clearBuffer(); + node.accept(getTraverser()); + return getPrinter().getContent(); + } + + public String prettyprint(ASTGenericInvocationSuffix node) { + getPrinter().clearBuffer(); + node.accept(getTraverser()); + return getPrinter().getContent(); + } + + public String prettyprint(ASTSuperSuffix node) { + getPrinter().clearBuffer(); + node.accept(getTraverser()); + return getPrinter().getContent(); + } + + +} diff --git a/monticore-grammar/src/main/java/de/monticore/expressions/prettyprint/LambdaExpressionsFullPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/expressions/prettyprint/LambdaExpressionsFullPrettyPrinter.java new file mode 100644 index 0000000000..95261d1d09 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/expressions/prettyprint/LambdaExpressionsFullPrettyPrinter.java @@ -0,0 +1,61 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.prettyprint; + +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.lambdaexpressions.LambdaExpressionsMill; +import de.monticore.expressions.lambdaexpressions._ast.ASTLambdaParameter; +import de.monticore.expressions.lambdaexpressions._ast.ASTLambdaParameters; +import de.monticore.expressions.lambdaexpressions._visitor.LambdaExpressionsTraverser; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.prettyprint.MCBasicsPrettyPrinter; +import de.monticore.types.prettyprint.MCBasicTypesPrettyPrinter; + +@Deprecated(forRemoval = true) +public class LambdaExpressionsFullPrettyPrinter extends ExpressionsBasisFullPrettyPrinter { + + protected LambdaExpressionsTraverser traverser; + + @Override + public LambdaExpressionsTraverser getTraverser() { + return traverser; + } + + public void setTraverser(LambdaExpressionsTraverser traverser) { + this.traverser = traverser; + } + + public LambdaExpressionsFullPrettyPrinter(IndentPrinter printer) { + super(printer); + this.traverser = LambdaExpressionsMill.traverser(); + ExpressionsBasisPrettyPrinter expressionBasis = new ExpressionsBasisPrettyPrinter(printer); + traverser.setExpressionsBasisHandler(expressionBasis); + traverser.add4ExpressionsBasis(expressionBasis); + MCBasicTypesPrettyPrinter mcBasicTypes = new MCBasicTypesPrettyPrinter(printer); + traverser.setMCBasicTypesHandler(mcBasicTypes); + traverser.add4MCBasicTypes(mcBasicTypes); + LambdaExpressionsPrettyPrinter lambdaExpression = new LambdaExpressionsPrettyPrinter(printer); + traverser.setLambdaExpressionsHandler(lambdaExpression); + traverser.add4LambdaExpressions(lambdaExpression); + MCBasicsPrettyPrinter basic = new MCBasicsPrettyPrinter(printer); + traverser.add4MCBasics(basic); + } + + public String prettyprint(ASTExpression node) { + getPrinter().clearBuffer(); + node.accept(getTraverser()); + return getPrinter().getContent(); + } + + public String prettyprint(ASTLambdaParameter node) { + getPrinter().clearBuffer(); + node.accept(getTraverser()); + return getPrinter().getContent(); + } + + public String prettyprint(ASTLambdaParameters node) { + getPrinter().clearBuffer(); + node.accept(getTraverser()); + return getPrinter().getContent(); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/expressions/prettyprint/LambdaExpressionsPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/expressions/prettyprint/LambdaExpressionsPrettyPrinter.java new file mode 100644 index 0000000000..2dfd4e7897 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/expressions/prettyprint/LambdaExpressionsPrettyPrinter.java @@ -0,0 +1,94 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.prettyprint; + +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.lambdaexpressions._ast.ASTLambdaExpression; +import de.monticore.expressions.lambdaexpressions._ast.ASTLambdaExpressionBody; +import de.monticore.expressions.lambdaexpressions._ast.ASTLambdaParameter; +import de.monticore.expressions.lambdaexpressions._ast.ASTLambdaParameters; +import de.monticore.expressions.lambdaexpressions._visitor.LambdaExpressionsHandler; +import de.monticore.expressions.lambdaexpressions._visitor.LambdaExpressionsTraverser; +import de.monticore.expressions.lambdaexpressions._visitor.LambdaExpressionsVisitor2; +import de.monticore.prettyprint.CommentPrettyPrinter; +import de.monticore.prettyprint.IndentPrinter; + +@Deprecated(forRemoval = true) +public class LambdaExpressionsPrettyPrinter + implements LambdaExpressionsVisitor2, LambdaExpressionsHandler { + + protected LambdaExpressionsTraverser traverser; + + @Override + public void setTraverser(LambdaExpressionsTraverser traverser) { + this.traverser = traverser; + } + + @Override + public LambdaExpressionsTraverser getTraverser() { + return traverser; + } + + protected IndentPrinter printer; + + public void setPrinter(IndentPrinter printer) { + this.printer = printer; + } + + public IndentPrinter getPrinter() { + return this.printer; + } + + public LambdaExpressionsPrettyPrinter(IndentPrinter printer) { + this.printer = printer; + } + + @Override + public void handle(ASTLambdaParameter node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + if (node.isPresentMCType()) { + node.getMCType().accept(getTraverser()); + getPrinter().print(" "); + } + getPrinter().print(node.getName()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTLambdaParameters node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + if (node.isPresentParenthesis()) { + getPrinter().print("("); + } + for (int i = 0; i < node.getLambdaParameterList().size(); i++) { + node.getLambdaParameterList().get(i).accept(getTraverser()); + if (i != node.getLambdaParameterList().size() - 1) { + getPrinter().print(","); + } + } + if (node.isPresentParenthesis()) { + getPrinter().print(")"); + } + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTLambdaExpression node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + node.getLambdaParameters().accept(getTraverser()); + getPrinter().print(" -> "); + node.getLambdaBody().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + @Override + public void handle(ASTLambdaExpressionBody node) { + node.getExpression().accept(getTraverser()); + } + + public String prettyprint(ASTExpression node) { + getPrinter().clearBuffer(); + node.accept(getTraverser()); + return getPrinter().getContent(); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/ComponentCollector.java b/monticore-grammar/src/main/java/de/monticore/grammar/ComponentCollector.java new file mode 100644 index 0000000000..bd0fee241f --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/ComponentCollector.java @@ -0,0 +1,56 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar; + +import com.google.common.collect.Lists; +import de.monticore.grammar.grammar.GrammarMill; +import de.monticore.grammar.grammar._ast.*; +import de.monticore.grammar.grammar._visitor.GrammarTraverser; +import de.monticore.grammar.grammar._visitor.GrammarVisitor2; + +import java.util.List; + +public class ComponentCollector { + + public static List getAllComponents(ASTGrammarNode node) { + CollectRuleComponents cv = new CollectRuleComponents(); + GrammarTraverser traverser = GrammarMill.traverser(); + traverser.add4Grammar(cv); + node.accept(traverser); + return cv.getRuleComponents(); + } + + protected static class CollectRuleComponents implements GrammarVisitor2 { + + public List ruleComponentList = Lists.newArrayList(); + + public List getRuleComponents() { + return ruleComponentList; + } + + @Override + public void visit(ASTNonTerminal node) { + ruleComponentList.add(node); + } + + @Override + public void visit(ASTTerminal node) { + ruleComponentList.add(node); + } + + @Override + public void visit(ASTKeyTerminal node) { + ruleComponentList.add(node); + } + + @Override + public void visit(ASTTokenTerminal node) { + ruleComponentList.add(node); + } + + @Override + public void visit(ASTConstantGroup node) { + ruleComponentList.add(node); + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/DirectLeftRecursionDetector.java b/monticore-grammar/src/main/java/de/monticore/grammar/DirectLeftRecursionDetector.java new file mode 100644 index 0000000000..e2d2fe4009 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/DirectLeftRecursionDetector.java @@ -0,0 +1,75 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar; + +import de.monticore.grammar.grammar._ast.ASTAlt; +import de.monticore.grammar.grammar._ast.ASTNonTerminal; +import de.monticore.grammar.grammar._ast.ASTRuleComponent; + +import java.util.Collection; +import java.util.List; + +/** + * Checks if a MC production is a left directly left recursive: e.g. of the form A -> A.* + * + */ +public class DirectLeftRecursionDetector { + + public boolean isAlternativeLeftRecursive(final ASTAlt productionAlternative, + final ASTNonTerminal actualNonTerminal) { + final String classProductionName = actualNonTerminal.getName(); + + final List nodes = ComponentCollector.getAllComponents(productionAlternative); + + if (nodes.isEmpty()) { + return false; + } + + if (nodes.get(0) instanceof ASTNonTerminal) { + ASTNonTerminal leftmostNonterminal = (ASTNonTerminal) nodes.get(0); + if ((leftmostNonterminal == actualNonTerminal) + && leftmostNonterminal.getName().equals(classProductionName)) { + return true; + } + } + + return false; + } + + public boolean isAlternativeLeftRecursive(final ASTAlt productionAlternative, + final String classProductionName) { + final List nodes = ComponentCollector.getAllComponents(productionAlternative); + + if (nodes.isEmpty()) { + return false; + } + + if (nodes.get(0) instanceof ASTNonTerminal) { + ASTNonTerminal leftmostNonterminal = (ASTNonTerminal) nodes.get(0); + if (leftmostNonterminal.getName().equals(classProductionName)) { + return true; + } + } + + return false; + } + + public boolean isAlternativeLeftRecursive(final ASTAlt productionAlternative, + final Collection names) { + final List nodes = ComponentCollector.getAllComponents(productionAlternative); + + if (nodes.isEmpty()) { + return false; + } + + if (nodes.get(0) instanceof ASTNonTerminal) { + ASTNonTerminal leftmostNonterminal = (ASTNonTerminal) nodes.get(0); + if (names.contains(leftmostNonterminal.getName())) { + return true; + } + } + + return false; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/LexNamer.java b/monticore-grammar/src/main/java/de/monticore/grammar/LexNamer.java new file mode 100644 index 0000000000..5cb5293f2c --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/LexNamer.java @@ -0,0 +1,172 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.se_rwth.commons.logging.Log; +import org.apache.commons.lang3.StringUtils; + +/** + * Class generates human readable names for Lexersymbols + * + */ +public class LexNamer { + + protected int constantCounter = 0; + + protected int lexCounter = 0; + + protected Map usedLex = new HashMap(); + + protected Map usedConstants = new HashMap(); + + protected static Map goodNames = null; + + public static Map getGoodNames() { + if (goodNames == null) { + goodNames = new HashMap(); + // Put all common names here, one character only, since all others are + // concatanation of these + goodNames.put(";", "SEMI"); + goodNames.put("@", "AT"); + goodNames.put("#", "HASH"); + goodNames.put(".", "POINT"); + goodNames.put(",", "COMMA"); + goodNames.put("?", "QUESTION"); + goodNames.put("§", "LEX"); + goodNames.put("\"", "QUOTE"); + goodNames.put("'", "APOSTROPHE"); + goodNames.put("$", "DOLLAR"); + goodNames.put("~", "TILDE"); + goodNames.put(">", "GT"); + goodNames.put("<", "LT"); + goodNames.put("=", "EQUALS"); + goodNames.put("+", "PLUS"); + goodNames.put("-", "MINUS"); + goodNames.put("*", "STAR"); + goodNames.put("%", "PERCENT"); + goodNames.put("/", "SLASH"); + goodNames.put("&", "AND_"); + goodNames.put("|", "PIPE"); + goodNames.put(":", "COLON"); + goodNames.put("!", "EXCLAMATIONMARK"); + goodNames.put("^", "ROOF"); + + // Don't change the following, unless you change Grammar2Antlr too + goodNames.put("(", "LPAREN"); + goodNames.put(")", "RPAREN"); + goodNames.put("[", "LBRACK"); + goodNames.put("]", "RBRACK"); + goodNames.put("{", "LCURLY"); + goodNames.put("}", "RCURLY"); + } + return goodNames; + } + + /** + * Returns a good name for the lex symbol or "" + */ + public static String createGoodName(String x) { + + if (x.matches("[a-zA-Z][a-zA-Z_0-9]*")) { + return x.toUpperCase()+Integer.toUnsignedString(x.hashCode()); + } + + if (x.matches("[^a-zA-Z0-9]+")) { + StringBuilder ret = new StringBuilder(); + for (int i = 0; i < x.length(); i++) { + + String substring = x.substring(i, i + 1); + if (getGoodNames().containsKey(substring)) { + ret.append(getGoodNames().get(substring)); + } else { + return ""; + } + } + return ret.toString(); + } + return ""; + + } + + /** + * Returns a good name for the lex symbol or "" + */ + public static String createSimpleGoodName(String x) { + + if (x.matches("[a-zA-Z][a-zA-Z_0-9]*")) { + return x.toUpperCase(); + } + + if (x.matches("[^a-zA-Z0-9]+")) { + StringBuilder ret = new StringBuilder(); + for (int i = 0; i < x.length(); i++) { + + String substring = x.substring(i, i + 1); + if (getGoodNames().containsKey(substring)) { + ret.append(getGoodNames().get(substring)); + } else { + return ""; + } + } + return ret.toString(); + } + return ""; + + } + + /** + * Returns Human-Readable, antlr conformed name for a lexsymbols nice names for common tokens + * (change constructor to add tokenes) LEXi where i is number for unknown ones + * + * @param sym lexer symbol + * @return Human-Readable, antlr conformed name for a lexsymbols + */ + public String getLexName(MCGrammarSymbol grammarSymbol, String sym) { + if (usedLex.containsKey(sym)) { + return usedLex.get(sym); + } + + String goodName = createGoodName(sym); + if (goodName.isEmpty() || grammarSymbol.getProd(goodName).isPresent()) { + goodName = "LEXNAME" + lexCounter++; + } + usedLex.put(sym, goodName); + Log.debug("Using lexer symbol " + goodName + " for symbol '" + sym + "'", "LexNamer"); + return goodName; + } + + public String getConstantName(String sym) { + String s = sym.intern(); + + if (!usedConstants.containsKey(s)) { + String goodName = createSimpleGoodName(s); + if (!goodName.isEmpty()) { + usedConstants.put(s, goodName); + } + else { + usedConstants.put(s, ("CONSTANT" + constantCounter++).intern()); + } + } + + String name = usedConstants.get(sym.intern()); + Log.debug("Using lexer constant " + name + " for symbol '" + s + "'", "LexNamer"); + + return name; + } + + protected String convertKeyword(String key) { + key = StringUtils.replace(key, "\\\"", "\""); + key = StringUtils.replace(key, "'", "\\'"); + return key; + } + + + public Set getLexnames() { + return usedLex.keySet(); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/MCGrammarSymbolTableHelper.java b/monticore-grammar/src/main/java/de/monticore/grammar/MCGrammarSymbolTableHelper.java new file mode 100644 index 0000000000..f8ca580f46 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/MCGrammarSymbolTableHelper.java @@ -0,0 +1,314 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import de.monticore.grammar.grammar._ast.*; +import de.monticore.grammar.grammar._symboltable.*; +import de.monticore.symboltable.IScopeSpanningSymbol; +import de.se_rwth.commons.StringTransformations; +import de.se_rwth.commons.Util; +import de.se_rwth.commons.logging.Log; + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.google.common.collect.Sets.newLinkedHashSet; + +public class MCGrammarSymbolTableHelper { + + public static final String AST_DOT_PACKAGE_SUFFIX_DOT = "._ast."; + protected static final Integer STAR = -1; + + public static Optional resolveRule(ASTMCGrammar astNode, String name) { + if (astNode.isPresentSymbol()) { + return astNode.getSymbol().getProdWithInherited(name); + } + return Optional.empty(); + } + + public static Optional resolveRuleInSupersOnly(ASTClassProd astNode, String name) { + Optional grammarSymbol = getMCGrammarSymbol(astNode.getEnclosingScope()); + Stream superGrammars = grammarSymbol + .map(symbol -> Util.preOrder(symbol, MCGrammarSymbol::getSuperGrammarSymbols) + .stream()) + .orElse(Stream.empty()).skip(1); + return superGrammars.map(superGrammar -> superGrammar.getProd(name)) + .filter(mcRuleSymbol -> mcRuleSymbol.isPresent()) + .map(Optional::get) + .findFirst(); + } + + public static Optional getMCGrammarSymbol(IGrammarScope scope) { + boolean exist = true; + while (exist) { + if (scope.isPresentSpanningSymbol() && scope.getSpanningSymbol() instanceof MCGrammarSymbol) { + return Optional.of((MCGrammarSymbol) scope.getSpanningSymbol()); + } + if (scope instanceof IGrammarGlobalScope) { + exist = false; + } else { + scope = scope.getEnclosingScope(); + } + } + return Optional.empty(); + } + + public static Optional getEnclosingRule(ASTRuleComponent astNode) { + if (astNode.getEnclosingScope().isPresentSpanningSymbol()) { + IScopeSpanningSymbol s = astNode.getEnclosingScope().getSpanningSymbol(); + if (s instanceof ProdSymbol) { + return Optional.of((ProdSymbol) s); + } + } + return Optional.empty(); + } + + public static Optional getEnclosingRule(RuleComponentSymbol prod) { + if (prod.getEnclosingScope().isPresentSpanningSymbol() && prod.getEnclosingScope().getSpanningSymbol() instanceof ProdSymbol) { + return Optional.of((ProdSymbol) prod.getEnclosingScope().getSpanningSymbol()); + } + return Optional.empty(); + } + + /** + * Returns a set of all super grammars of the given grammar (transitively) + * + * @return + */ + public static Set getAllSuperGrammars( + MCGrammarSymbol grammarSymbol) { + Set allSuperGrammars = new LinkedHashSet<>(); + Set tmpList = new LinkedHashSet<>(); + allSuperGrammars.addAll(grammarSymbol.getSuperGrammarSymbols()); + boolean modified = false; + do { + for (MCGrammarSymbol curGrammar : allSuperGrammars) { + tmpList.addAll(curGrammar.getSuperGrammarSymbols()); + } + modified = allSuperGrammars.addAll(tmpList); + tmpList.clear(); + } while (modified); + + return ImmutableSet.copyOf(allSuperGrammars); + } + + public static String getQualifiedName(ASTProd astNode, ProdSymbol symbol, String prefix, + String suffix) { + if (symbol.isIsExternal()) { + return symbol.getName(); + } else { + Optional grammarSymbol = getMCGrammarSymbol(astNode.getEnclosingScope()); + String string = (grammarSymbol.isPresent() + ? grammarSymbol.get().getFullName().toLowerCase() + : "") + + AST_DOT_PACKAGE_SUFFIX_DOT + prefix + + StringTransformations.capitalize(symbol.getName() + suffix); + + if (string.startsWith(".")) { + string = string.substring(1); + } + return string; + } + } + + + public static String getConstantGroupName(ASTConstantGroup ast) { + // setAttributeMinMax(a.getIteration(), att); + if (ast.isPresentUsageName()) { + return ast.getUsageName(); + } + // derive attribute name from constant entry (but only if we have + // one entry!) + else if (ast.getConstantList().size() == 1) { + return ast.getConstantList().get(0).getHumanName(); + } + + Log.error("0xA2345 The name of the constant group could't be ascertained", + ast.get_SourcePositionStart()); + + return ""; + } + + public static Set getAllSuperProds(ProdSymbol prod) { + Set supersHandled = new LinkedHashSet<>(); + List supersToHandle = new ArrayList<>(); + supersToHandle.addAll(getSuperProds(prod)); + Set supersNextRound = new LinkedHashSet<>(); + + while (!supersToHandle.isEmpty()) { + for (ProdSymbol superType : supersToHandle) { + if (!supersHandled.contains(superType)) { + supersNextRound.addAll(getSuperProds(superType)); + } + supersHandled.add(superType); + } + supersToHandle.clear(); + supersToHandle.addAll(supersNextRound); + supersNextRound.clear(); + } + return ImmutableSet.copyOf(supersHandled); + } + + public static Set getAllSuperInterfaces(ProdSymbol prod) { + return getAllSuperProds(prod).stream().filter(p -> p.isIsInterface()).collect(Collectors.toSet()); + } + + protected final static LoadingCache> superProdCache = CacheBuilder.newBuilder().maximumSize(10000) + .build(new CacheLoader>() { + @Override + public List load(ProdSymbol prod) { + List superTypes = prod.getSuperProds().stream().filter(s -> s.isSymbolPresent()) + .map(s -> s.lazyLoadDelegate()).collect(Collectors.toList()); + superTypes.addAll(prod.getSuperInterfaceProds().stream().filter(s -> s.isSymbolPresent()) + .map(s -> s.lazyLoadDelegate()).collect(Collectors.toList())); + + superTypes.addAll(prod.getAstSuperClasses().stream().filter(s -> s.isSymbolPresent()) + .map(s -> s.lazyLoadDelegate()).collect(Collectors.toList())); + superTypes.addAll(prod.getAstSuperInterfaces().stream().filter(s -> s.isSymbolPresent()) + .map(s -> s.lazyLoadDelegate()).collect(Collectors.toList())); + return ImmutableList.copyOf(superTypes); + } + }); + + /** + * @param prod + * @return + */ + public static List getSuperProds(ProdSymbol prod) { + return superProdCache.getUnchecked(prod); + } + + public static boolean isSubtype(ProdSymbol subType, ProdSymbol superType) { + return isSubtype(subType, superType, newLinkedHashSet(Arrays.asList(subType))); + } + + protected static boolean isSubtype(ProdSymbol subType, ProdSymbol superType, + Set handledTypes) { + if (areSameTypes(subType, superType)) { + return true; + } + + // Try to find superType in super types of this type + final Collection allSuperTypes = getAllSuperProds(subType); + if (allSuperTypes.contains(superType)) { + return true; + } + + // check transitive sub-type relation + for (ProdSymbol t : allSuperTypes) { + if (handledTypes.add(superType)) { + boolean subtypeOf = isSubtype(t, superType, handledTypes); + if (subtypeOf) { + return true; + } + } + } + + return false; + } + + public static boolean areSameTypes(ProdSymbol type1, ProdSymbol type2) { + Log.errorIfNull(type1); + Log.errorIfNull(type2); + + if (type1 == type2) { + return true; + } + + return type1.getFullName().equals(type2.getFullName()); + + } + + public static boolean isAssignmentCompatibleOrUndecidable(ProdSymbol subType, + ProdSymbol superType) { + return isAssignmentCompatibleOrUndecidable(subType, superType, + newLinkedHashSet(Arrays.asList(subType))); + } + + /** + * Returns the type of the collection types that is the sub type + * of all other types in this collection. Else, null is returned. + * + * @param types Collection of types + * @return type that is subtype of all other types or null. + */ + public static Optional findLeastType(Collection types) { + for (ProdSymbol t1 : types) { + boolean isLeastType = true; + for (ProdSymbol t2 : types) { + if (!isSubtype(t2, t1) && !areSameTypes(t2, t1)) { + isLeastType = false; + break; + } + } + if (isLeastType) { + return Optional.of(t1); + } + } + return Optional.empty(); + } + + public static boolean isAssignmentCompatibleOrUndecidable(ProdSymbol subType, + ProdSymbol superType, Set handledTypes) { + // Return true if this type or the other type are both external + // TODO GV: check, wenn Java angebunden + if (subType.isIsExternal() + || superType.isIsExternal()) { + return true; + } + + // Return true if this type and the other type are the same + if (areSameTypes(subType, superType)) { + return true; + } + + // Try to find superType in supertypes of this type + Collection allSuperTypes = getAllSuperProds(subType); + if (allSuperTypes.contains(superType)) { + return true; + } + + // check transitive sub-type relation + for (ProdSymbol t : allSuperTypes) { + if (handledTypes.add(superType)) { + boolean subtypeOf = isAssignmentCompatibleOrUndecidable(t, superType, handledTypes); + if (subtypeOf) { + return true; + } + } + } + + return false; + } + + + public static Optional getMin(AdditionalAttributeSymbol attrSymbol) { + if (!attrSymbol.isPresentAstNode()) { + return Optional.empty(); + } + return getMin(attrSymbol.getAstNode()); + } + + public static Optional getMin(ASTAdditionalAttribute ast) { + if (ast.isPresentCard() + && ast.getCard().isPresentMin()) { + String min = ast.getCard().getMin(); + try { + int x = Integer.parseInt(min); + return Optional.of(x); + } catch (NumberFormatException ignored) { + Log.warn("0xA0141 Failed to parse an integer value of max of ASTAdditionalAttribute " + + ast.getName() + " from string " + min); + } + } + return Optional.empty(); + } + + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/Multiplicity.java b/monticore-grammar/src/main/java/de/monticore/grammar/Multiplicity.java new file mode 100644 index 0000000000..48b6997c34 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/Multiplicity.java @@ -0,0 +1,222 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar; + +import de.monticore.ast.ASTNode; +import de.monticore.grammar.grammar.GrammarMill; +import de.monticore.grammar.grammar._ast.*; +import de.monticore.grammar.grammar._symboltable.IGrammarScope; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar._visitor.GrammarTraverser; +import de.monticore.symboltable.IGlobalScope; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.google.common.collect.Lists.newArrayList; +import static java.util.Collections.max; + +/** + * Denotes the multiplicity of nonterminals in a MC grammar such as '*', '+', or '?'. + * + */ +public enum Multiplicity { + + /** + * No quantifier present + */ + STANDARD, + /** + * '?' quantifier present + */ + OPTIONAL, + /** + * '*' or '+' quantifier present + */ + LIST; + + public static Multiplicity determineMultiplicity(ASTAdditionalAttribute attributeInAST) { + if (!attributeInAST.isPresentCard()) { + return STANDARD; + } + ASTCard cardinality = attributeInAST.getCard(); + if (cardinality.getIteration() == ASTConstantsGrammar.STAR + || cardinality.getIteration() == ASTConstantsGrammar.PLUS + || (cardinality.isPresentMax() && (cardinality.getMax().equals("*") || getMaxCardinality(cardinality) > 1))) { + return LIST; + } else if (cardinality.getIteration() == ASTConstantsGrammar.QUESTION + || (!cardinality.isPresentMin() || getMinCardinality(cardinality) == 0)) { + return OPTIONAL; + } + return STANDARD; + } + + protected static int getMaxCardinality(ASTCard cardinality) { + return Integer.parseInt(cardinality.getMax()); + } + + protected static int getMinCardinality(ASTCard cardinality) { + return Integer.parseInt(cardinality.getMin()); + } + + /** + * Performs the multiplicity calculation for inherited attributes. + * + * @param astNode The ast node. + * @return The multiplicity of the ast in the defining grammar. + */ + public static Multiplicity determineMultiplicity(ASTRuleComponent astNode) { + // multiplicity by inheritance is only relevant for nonterminals and can + // cause errors otherwise; cast rootNode to ASTMCGrammar for further use + // switch to default behavior without inheritance otherwise + if (astNode instanceof ASTConstantGroup) { + // constant groups are always standard iteration + return STANDARD; + } + IGrammarScope scope = astNode.getEnclosingScope(); + while (!(scope instanceof IGlobalScope) ) { + if (scope.isPresentSpanningSymbol() && scope.getSpanningSymbol() instanceof MCGrammarSymbol) { + return determineMultiplicity(((MCGrammarSymbol) scope.getSpanningSymbol()).getAstNode(), astNode); + } + scope = scope.getEnclosingScope(); + } + return Multiplicity.STANDARD; + + } + + public static Multiplicity determineMultiplicity(ASTNode astNode) { + if (astNode instanceof ASTRuleComponent) { + return determineMultiplicity((ASTRuleComponent) astNode); + } else if (astNode instanceof ASTAdditionalAttribute) { + return determineMultiplicity((ASTAdditionalAttribute) astNode); + } + return null; + } + + public static Multiplicity determineMultiplicity(ASTMCGrammar rootNode, ASTRuleComponent astNode) { + MultiplicityVisitor mv = new MultiplicityVisitor(astNode); + GrammarTraverser traverser = GrammarMill.traverser(); + traverser.add4Grammar(mv); + rootNode.accept(traverser); + List intermediates = mv.getComponents(); + Multiplicity byAlternative = multiplicityByAlternative(rootNode, astNode, intermediates); + Multiplicity byDuplicates = multiplicityByDuplicates(rootNode, astNode, intermediates); + Multiplicity byIteration = multiplicityByIteration(rootNode, astNode, intermediates); + ArrayList newArrayList = newArrayList(byDuplicates, byIteration, byAlternative); + return max(newArrayList); + } + + protected static Multiplicity multiplicityByAlternative(ASTMCGrammar rootNode, ASTRuleComponent astNode, List intermediates) { + boolean containedInAlternative = false; + for (ASTNode intermediate : intermediates) { + if (intermediate instanceof ASTClassProd) { + containedInAlternative |= ((ASTClassProd) intermediate).getAltList().size() > 1; + } else if (intermediate instanceof ASTBlock) { + containedInAlternative |= ((ASTBlock) intermediate).getAltList().size() > 1; + } + } + return containedInAlternative ? OPTIONAL : STANDARD; + } + + protected static Multiplicity multiplicityByDuplicates(ASTMCGrammar rootNode, ASTRuleComponent astNode, List intermediates) { + boolean hasDuplicate = getAllNodesInRelatedRuleComponents(rootNode, astNode, intermediates) + .anyMatch(sibling -> areDuplicates(rootNode, astNode, sibling)); + if (hasDuplicate) { + return LIST; + } else { + return STANDARD; + } + } + + public static Optional getUsageName(ASTNode ancestor) { + if (ancestor instanceof ASTConstantGroup && ((ASTConstantGroup) ancestor).isPresentUsageName()) { + return Optional.of(((ASTConstantGroup) ancestor).getUsageName()); + } + if (ancestor instanceof ASTNonTerminal && ((ASTNonTerminal) ancestor).isPresentUsageName()) { + return Optional.of(((ASTNonTerminal) ancestor).getUsageName()); + } + if (ancestor instanceof ASTNonTerminalSeparator) { + return Optional.of(((ASTNonTerminalSeparator) ancestor).getUsageName()); + } + if (ancestor instanceof ASTITerminal && ((ASTITerminal) ancestor).isPresentUsageName()) { + return Optional.of(((ASTITerminal) ancestor).getUsageName()); + } + if (ancestor instanceof ASTAdditionalAttribute && ((ASTAdditionalAttribute) ancestor).isPresentName()) { + return Optional.of(((ASTAdditionalAttribute) ancestor).getName()); + } + + return Optional.empty(); + } + + protected static boolean areDuplicates(ASTMCGrammar rootNode, ASTRuleComponent firstNode, ASTRuleComponent secondNode) { + Optional firstName = Optional.of(firstNode.getName()); + Optional firstUsageName = getUsageName(firstNode); + Optional secondName = Optional.of(secondNode.getName()); + Optional secondUsageName = getUsageName(secondNode); + + boolean bothUsageNamesAbsent = !firstUsageName.isPresent() && !secondUsageName.isPresent(); + boolean namesMatch = firstName.equals(secondName); + boolean usageNamesMatch = firstUsageName.equals(secondUsageName); + return (bothUsageNamesAbsent && namesMatch) || (!bothUsageNamesAbsent && usageNamesMatch); + } + + protected static Stream getAllNodesInRelatedRuleComponents(ASTMCGrammar rootNode, + ASTRuleComponent astNode, + List intermediates) { + + Set ancestorRuleComponents = intermediates.stream() + .filter(ASTRuleComponent.class::isInstance) + .map(ASTRuleComponent.class::cast) + .collect(Collectors.toSet()); + + return intermediates.stream() + .filter(ASTAlt.class::isInstance) + .map(ASTAlt.class::cast) + .flatMap(alt -> alt.getComponentList().stream()) + .filter(ruleComponent -> !ancestorRuleComponents.contains(ruleComponent)) + .flatMap(ruleComponent -> ComponentCollector.getAllComponents(ruleComponent).stream()); + } + + public static Multiplicity multiplicityByIteration(ASTMCGrammar rootNode, ASTRuleComponent astNode, List intermediates) { + Multiplicity multiplicity = STANDARD; + for (ASTNode intermediate :intermediates) { + int iteration = getIterationInt(intermediate); + + if (iteration == ASTConstantsGrammar.PLUS || iteration == ASTConstantsGrammar.STAR) { + multiplicity = LIST; + } + if (iteration == ASTConstantsGrammar.QUESTION && multiplicity != LIST) { + multiplicity = OPTIONAL; + } + } + return multiplicity; + } + + protected static int getIterationInt(ASTNode ancestor) { + int iteration = ASTConstantsGrammar.DEFAULT; + if (ancestor instanceof ASTBlock) { + iteration = ((ASTBlock) ancestor).getIteration(); + } + if (ancestor instanceof ASTNonTerminal) { + iteration = ((ASTNonTerminal) ancestor).getIteration(); + } + if (ancestor instanceof ASTTerminal) { + iteration = ((ASTTerminal) ancestor).getIteration(); + } + if (ancestor instanceof ASTKeyTerminal) { + iteration = ((ASTKeyTerminal) ancestor).getIteration(); + } + if (ancestor instanceof ASTTokenTerminal) { + iteration = ((ASTTokenTerminal) ancestor).getIteration(); + } + if (ancestor instanceof ASTConstantGroup) { + iteration = ((ASTConstantGroup) ancestor).getIteration(); + } + return iteration; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/MultiplicityVisitor.java b/monticore-grammar/src/main/java/de/monticore/grammar/MultiplicityVisitor.java new file mode 100644 index 0000000000..0e263a5244 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/MultiplicityVisitor.java @@ -0,0 +1,138 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar; + +import com.google.common.collect.Lists; +import de.monticore.ast.ASTNode; +import de.monticore.grammar.grammar._ast.*; +import de.monticore.grammar.grammar._visitor.GrammarVisitor2; + +import java.util.List; +import java.util.Stack; + +public class MultiplicityVisitor implements GrammarVisitor2 { + + + protected Stack components = new Stack<>(); + protected List result = Lists.newArrayList(); + + protected ASTNode last; + + public MultiplicityVisitor(ASTGrammarNode last) { + this.last = last; + } + + public List getComponents() { + return result; + } + + + @Override + public void visit(ASTClassProd node) { + components.push(node); + if (node==last) { + result.addAll(components); + } + } + + @Override + public void endVisit(ASTClassProd node) { + components.pop(); + if (node==last) { + result.addAll(components); + } + } + + @Override + public void visit(ASTAlt node) { + components.push(node); + if (node==last) { + result.addAll(components); + } + } + + @Override + public void endVisit(ASTAlt node) { + components.pop(); + if (node==last) { + result.addAll(components); + } + } + + @Override + public void visit(ASTBlock node) { + components.push(node); + if (node==last) { + result.addAll(components); + } + } + + @Override + public void endVisit(ASTBlock node) { + components.pop(); + } + + @Override + public void visit(ASTNonTerminal node) { + components.push(node); + if (node==last) { + result.addAll(components); + } + } + + @Override + public void endVisit(ASTNonTerminal node) { + components.pop(); + } + + @Override + public void visit(ASTTerminal node) { + components.push(node); + if (node==last) { + result.addAll(components); + } + } + + @Override + public void endVisit(ASTTerminal node) { + components.pop(); + } + @Override + public void visit(ASTKeyTerminal node) { + components.push(node); + if (node==last) { + result.addAll(components); + } + } + + @Override + public void endVisit(ASTKeyTerminal node) { + components.pop(); + } + + @Override + public void visit(ASTTokenTerminal node) { + components.push(node); + if (node==last) { + result.addAll(components); + } + } + + @Override + public void endVisit(ASTTokenTerminal node) { + components.pop(); + } + + @Override + public void visit(ASTConstantGroup node) { + components.push(node); + if (node==last) { + result.addAll(components); + } + } + + @Override + public void endVisit(ASTConstantGroup node) { + components.pop(); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/PredicatePair.java b/monticore-grammar/src/main/java/de/monticore/grammar/PredicatePair.java new file mode 100644 index 0000000000..d6ba78c599 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/PredicatePair.java @@ -0,0 +1,38 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar; + +import de.monticore.grammar.grammar._ast.ASTRuleReference; + +public class PredicatePair { + protected String classname; + + protected ASTRuleReference ruleReference; + + public ASTRuleReference getRuleReference() { + return ruleReference; + } + + public void setRuleReference(ASTRuleReference ruleReference) { + this.ruleReference = ruleReference; + } + + public String getClassname() { + return classname; + } + + public PredicatePair(String classname, ASTRuleReference ruleReference) { + this.classname = classname; + this.ruleReference = ruleReference; + } + + @Override + public boolean equals(Object o) { + return (o instanceof PredicatePair) && classname.equals(((PredicatePair) o).classname); + } + + @Override + public int hashCode() { + return classname.hashCode(); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/RegExpBuilder.java b/monticore-grammar/src/main/java/de/monticore/grammar/RegExpBuilder.java new file mode 100644 index 0000000000..196babb855 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/RegExpBuilder.java @@ -0,0 +1,163 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar; + +import de.monticore.grammar.grammar._ast.*; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.monticore.grammar.grammar._visitor.GrammarHandler; +import de.monticore.grammar.grammar._visitor.GrammarTraverser; +import de.monticore.grammar.grammar._visitor.GrammarVisitor2; + +import java.util.Optional; + +public class RegExpBuilder implements GrammarVisitor2, GrammarHandler { + + protected StringBuilder b; + + protected MCGrammarSymbol st; + + protected GrammarTraverser traverser; + + @Override + public GrammarTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(GrammarTraverser traverser) { + this.traverser = traverser; + } + + public RegExpBuilder(StringBuilder b, MCGrammarSymbol st) { + this.b = b; + this.st = st; + } + + /** + * Prints Lexer Rule + * + * @param a + */ + @Override + public void handle(ASTLexProd a) { + String del = ""; + for (ASTLexAlt alt: a.getAltList()) { + b.append(del); + alt.accept(getTraverser()); + del = "|"; + } + } + + + @Override + public void handle(ASTLexBlock a) { + + if (a.isNegate()) { + b.append("^"); + } + + b.append("("); + + // Visit all alternatives + String del = ""; + for (ASTLexAlt alt: a.getLexAltList()) { + b.append(del); + alt.accept(getTraverser()); + del = "|"; + } + + // Start of Block with iteration + b.append(")"); + b.append(printIteration(a.getIteration())); + + } + + @Override + public void visit(ASTLexCharRange a) { + + b.append("["); + if (a.isNegate()) { + b.append("^"); + } + b.append(a.getLowerChar()); + b.append("-"); + b.append(a.getUpperChar() + "]"); + + } + + @Override + public void visit(ASTLexChar a) { + + if (a.getChar().startsWith("\\")) { + b.append("("); + if (a.isNegate()) { + b.append("^"); + } + b.append(a.getChar() + ")"); + } + else { + + if ("[".equals(a.getChar()) || "]".equals(a.getChar())) { + + if (a.isNegate()) { + b.append("^"); + } + b.append(a.getChar()); + + } + else { + b.append("["); + if (a.isNegate()) { + b.append("^"); + } + b.append(a.getChar() + "]"); + } + } + } + + @Override + public void visit(ASTLexString a) { + + for (int i = 0; i < a.getString().length(); i++) { + + String x = a.getString().substring(i, i + 1); + if (x.startsWith("\\")) { + + b.append("(" + a.getString().substring(i, i + 2) + ")"); + i++; + } + else { + if (needsEscapeChar(x)) { + x = "\\".concat(x); + } + b.append("[" + x + "]"); + } + } + + } + + protected boolean needsEscapeChar(String x) { + return "^".equals(x); + } + + @Override + public void visit(ASTLexNonTerminal a) { + Optional lexrule = st.getProd(a.getName()); + b.append(lexrule.isPresent()? lexrule.get().getName():""); + + } + + protected String printIteration(int i) { + switch (i) { + case ASTConstantsGrammar.PLUS: + return "+"; + case ASTConstantsGrammar.STAR: + return "*"; + case ASTConstantsGrammar.QUESTION: + return "?"; + default: + return ""; + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ASTRuleAndNTUseSameAttrNameForDiffNTs.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ASTRuleAndNTUseSameAttrNameForDiffNTs.java new file mode 100644 index 0000000000..0a4549fa18 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ASTRuleAndNTUseSameAttrNameForDiffNTs.java @@ -0,0 +1,100 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.MCGrammarSymbolTableHelper; +import de.monticore.grammar.grammar._ast.ASTASTRule; +import de.monticore.grammar.grammar._cocos.GrammarASTASTRuleCoCo; +import de.monticore.grammar.grammar._symboltable.AdditionalAttributeSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.monticore.grammar.grammar._symboltable.RuleComponentSymbol; +import de.monticore.types.mcbasictypes._ast.ASTMCPrimitiveType; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.mcsimplegenerictypes.MCSimpleGenericTypesMill; +import de.se_rwth.commons.logging.Log; + +import java.util.List; +import java.util.Optional; + +/** + * + */ +public class ASTRuleAndNTUseSameAttrNameForDiffNTs implements GrammarASTASTRuleCoCo { + + public static final String ERROR_CODE = "0xA4028"; + + public static final String ERROR_MSG_FORMAT = " The AST rule for the nonterminal %s must not use the " + + "same attribute name %s as the corresponding production " + + "with the type %s is not " + + "identical to or a super type of %s."; + + @Override + public void check(ASTASTRule a) { + ProdSymbol prodSymbol = a.getEnclosingScope().resolveProd(a.getType()).get(); + for (AdditionalAttributeSymbol attr : prodSymbol.getSpannedScope().getLocalAdditionalAttributeSymbols()) { + List rcs = prodSymbol.getSpannedScope().resolveRuleComponentDownMany(attr.getName()); + if (!rcs.isEmpty()) { + RuleComponentSymbol rc = rcs.get(0); + if (rc.isIsNonterminal()) { + String typeName = attr.getAstNode().getMCType().printType(); + if (!typeName + .endsWith(rc.getReferencedProd().get().getName())) { + Optional attrType = a.getEnclosingScope() + .resolveProd(typeName); + Optional compType = a.getEnclosingScope() + .resolveProd(rc.getReferencedProd().get().getName()); + if (attrType.isPresent() && compType.isPresent()) { + if (MCGrammarSymbolTableHelper.isSubtype(compType.get(), attrType.get()) + || isCorrespondingJavaTypeFromToken(attrType.get(), compType.get())) { + continue; + } else { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, a.getType(), + attr.getName(), typeName, + rc.getReferencedProd().get().getName()), + a.get_SourcePositionStart()); + } + } + } + } else if (rc.isIsTerminal() || rc.isIsLexerNonterminal()) { + // Compare to String + if (!("String".equals(attr.getType()) || "java.lang.String".equals(attr.getType()))) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, a.getType(), + attr.getName(), attr.getType(), + rc.getName()), + a.get_SourcePositionStart()); + } + } else if (rc.isIsConstant()) { + // Compare to boolean + ASTMCType attrType = attr.getAstNode().getMCType(); + if (!((attrType instanceof ASTMCPrimitiveType) || ((ASTMCPrimitiveType) attrType).isBoolean())) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, a.getType(), + attr.getName(), attr.getType(), + rc.getName()), + a.get_SourcePositionStart()); + } + } else if (rc.isIsConstantGroup()) { + // Compare to int + ASTMCType attrType = attr.getAstNode().getMCType(); + if (!(attrType instanceof ASTMCPrimitiveType) || !((ASTMCPrimitiveType) attrType).isInt()) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, a.getType(), + attr.getName(), attr.getType(), + rc.getName()), + a.get_SourcePositionStart()); + } + } else { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, a.getType(), + attr.getName(), attr.getType(), + rc.getName()), + a.get_SourcePositionStart()); + } + } + } + } + + protected boolean isCorrespondingJavaTypeFromToken(ProdSymbol astRuleType, ProdSymbol compType) { + if ("Name".equals(compType.getName())) { + return "String".equals(astRuleType.getName()); + } + return false; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/AbstractNTNotExtendInterfaceOrExternalNTs.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/AbstractNTNotExtendInterfaceOrExternalNTs.java new file mode 100644 index 0000000000..ceeac33389 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/AbstractNTNotExtendInterfaceOrExternalNTs.java @@ -0,0 +1,45 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import java.util.List; +import java.util.Optional; + +import de.monticore.grammar.grammar._ast.ASTAbstractProd; +import de.monticore.grammar.grammar._ast.ASTRuleReference; +import de.monticore.grammar.grammar._cocos.GrammarASTAbstractProdCoCo; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that abstract nonterminals only extends abstract or normal nonterminals.. + * + + */ +public class AbstractNTNotExtendInterfaceOrExternalNTs implements GrammarASTAbstractProdCoCo { + + public static final String ERROR_CODE = "0xA2107"; + + public static final String ERROR_MSG_FORMAT = " The abstract nonterminal %s must not extend the %s nonterminal %s. " + + "Abstract nonterminals may only extend abstract or normal nonterminals."; + + @Override + public void check(ASTAbstractProd a) { + if (!a.getSuperRuleList().isEmpty()) { + List superRules = a.getSuperRuleList(); + for(ASTRuleReference sr : superRules){ + Optional ruleSymbol = a.getEnclosingScope().resolveProd(sr.getName()); + if(ruleSymbol.isPresent()){ + ProdSymbol r = ruleSymbol.get(); + boolean isInterface = r.isIsInterface(); + boolean isExternal = r.isIsExternal(); + if(isInterface || isExternal){ + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, a.getName(), isInterface? "interface": "external", r.getName()), + a.get_SourcePositionStart()); + } + } + } + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/AbstractNTOnlyExtendOrAstextendNTOrClass.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/AbstractNTOnlyExtendOrAstextendNTOrClass.java new file mode 100644 index 0000000000..96436e6410 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/AbstractNTOnlyExtendOrAstextendNTOrClass.java @@ -0,0 +1,27 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTAbstractProd; +import de.monticore.grammar.grammar._cocos.GrammarASTAbstractProdCoCo; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that abstract nonterminals do not extend and astextend a type. + * + */ +public class AbstractNTOnlyExtendOrAstextendNTOrClass implements GrammarASTAbstractProdCoCo { + + public static final String ERROR_CODE = "0xA4030"; + + public static final String ERROR_MSG_FORMAT = " The abstract nonterminal %s must not extend and astextend a type."; + + @Override + public void check(ASTAbstractProd a) { + if (!a.getSuperRuleList().isEmpty() && !a.getASTSuperClassList().isEmpty()) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, a.getName()), + a.get_SourcePositionStart()); + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/AbstractNTOnlyExtendsOneNTOrClass.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/AbstractNTOnlyExtendsOneNTOrClass.java new file mode 100644 index 0000000000..5c5e45332b --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/AbstractNTOnlyExtendsOneNTOrClass.java @@ -0,0 +1,32 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTAbstractProd; +import de.monticore.grammar.grammar._cocos.GrammarASTAbstractProdCoCo; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that abstract nonterminals do not extend more than one nonterminals/class. + * + + */ +public class AbstractNTOnlyExtendsOneNTOrClass implements GrammarASTAbstractProdCoCo { + + public static final String ERROR_CODE = "0xA4012"; + + public static final String ERROR_MSG_FORMAT = " The abstract nonterminal %s must not %s more than one %s."; + + @Override + public void check(ASTAbstractProd a) { + if (a.getSuperRuleList().size()>1) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, a.getName(), "extend", "nonterminal"), + a.get_SourcePositionStart()); + } + if(a.getASTSuperClassList().size()>1){ + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, a.getName(), "astextend", "class"), + a.get_SourcePositionStart()); + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/AbstractNTOnlyImplementInterfaceNTs.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/AbstractNTOnlyImplementInterfaceNTs.java new file mode 100644 index 0000000000..1159738e7a --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/AbstractNTOnlyImplementInterfaceNTs.java @@ -0,0 +1,42 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import java.util.List; +import java.util.Optional; + +import de.monticore.grammar.grammar._ast.ASTAbstractProd; +import de.monticore.grammar.grammar._ast.ASTRuleReference; +import de.monticore.grammar.grammar._cocos.GrammarASTAbstractProdCoCo; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that abstract nonterminals only implement interface nonterminals. + * + */ +public class AbstractNTOnlyImplementInterfaceNTs implements GrammarASTAbstractProdCoCo { + + public static final String ERROR_CODE = "0xA2106"; + + public static final String ERROR_MSG_FORMAT = " The abstract nonterminal %s must not implement the nonterminal %s. " + + + "Abstract nonterminals may only implement interface nonterminals."; + + @Override + public void check(ASTAbstractProd a) { + if (!a.getSuperInterfaceRuleList().isEmpty()) { + List interfaces = a.getSuperInterfaceRuleList(); + for (ASTRuleReference i : interfaces) { + Optional ruleSymbol = a.getEnclosingScope().resolveProd(i.getName()); + if (ruleSymbol.isPresent()) { + ProdSymbol r = ruleSymbol.get(); + if (!r.isIsInterface()) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, a.getName(), r.getName()), + a.get_SourcePositionStart()); + } + } + } + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/AbstractNTWithoutExtensionOnlyInComponentGrammar.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/AbstractNTWithoutExtensionOnlyInComponentGrammar.java new file mode 100644 index 0000000000..22c4d523a7 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/AbstractNTWithoutExtensionOnlyInComponentGrammar.java @@ -0,0 +1,100 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import com.google.common.collect.Lists; +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbolSurrogate; +import de.se_rwth.commons.StringTransformations; +import de.se_rwth.commons.logging.Log; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Checks that abstract nonterminals witheout extending productions only occur in a component grammar. + * + */ +public class AbstractNTWithoutExtensionOnlyInComponentGrammar implements GrammarASTMCGrammarCoCo { + + public static final String ERROR_CODE = "0xA0277"; + + public static final String ERROR_MSG_FORMAT = " The abstract nonterminal %s must not be used without nonterminals " + + "extending it in a grammar not marked as a grammar component."; + + @Override + public void check(ASTMCGrammar a) { + MCGrammarSymbol grammarSymbol = a.getSymbol(); + if (!a.isComponent()) { + + Collection localProds = grammarSymbol.getProds(); + List superGrammars = grammarSymbol.getAllSuperGrammars(); + + List abstractProds = localProds.stream(). + filter(ProdSymbol::isIsAbstract).collect(Collectors.toList()); + List prods = localProds.stream(). + filter(prodSymbol -> prodSymbol.isClass() || prodSymbol.isIsAbstract()).collect(Collectors.toList()); + + for(MCGrammarSymbol symbol: superGrammars){ + Collection prodSymbols = symbol.getProds(); + for(ProdSymbol mcProdSymbol : prodSymbols){ + if (mcProdSymbol.isIsAbstract()) { + abstractProds.add(mcProdSymbol); + } + if (mcProdSymbol.isIsAbstract() || mcProdSymbol.isClass()) { + prods.add(mcProdSymbol); + } + + } + } + + if(!abstractProds.isEmpty()) { + List temp = new ArrayList<>(abstractProds); + for(ProdSymbol abstractProdSymbol : abstractProds){ + for(ProdSymbolSurrogate abstractProdExtended : abstractProdSymbol.getSuperProds()){ + for(int i = abstractProds.size()-1;i>=0;--i){ + ProdSymbol abstractProd = abstractProds.get(i); + if(abstractProdExtended.lazyLoadDelegate().getName().equals(abstractProd.getName())){ + temp.remove(abstractProdExtended.lazyLoadDelegate()); + } + } + } + } + abstractProds = temp; + } + + if(!abstractProds.isEmpty()){ + for (ProdSymbol prodSymbol : prods) { + for (ProdSymbolSurrogate abstractProdImplemented : prodSymbol.getSuperProds()) { + for (int i = abstractProds.size() - 1; i >= 0; --i) { + ProdSymbol interfaceProd = abstractProds.get(i); + if (abstractProdImplemented.getName().equals(interfaceProd.getName())) { + abstractProds.remove(i); + } + } + } + } + } + + for (ProdSymbol prodSymbol: abstractProds) { + List checkList = Lists.newArrayList(prodSymbol.getName()); + prodSymbol.getSuperProds().stream().forEach(i -> checkList.add(i.getName())); + for (ProdSymbol prod : prods) { + for (String name: checkList) { + if (!prod.getSpannedScope().resolveRuleComponentDownMany(StringTransformations.uncapitalize(name)).isEmpty()) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, name), a.get_SourcePositionStart()); + } + } + } + } + + } + } + + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/AttributeNameLowerCase.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/AttributeNameLowerCase.java new file mode 100644 index 0000000000..db459b38eb --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/AttributeNameLowerCase.java @@ -0,0 +1,32 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTNonTerminal; +import de.monticore.grammar.grammar._cocos.GrammarASTNonTerminalCoCo; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that nonterminal names start lower-case. + * + + */ +public class AttributeNameLowerCase implements GrammarASTNonTerminalCoCo { + + public static final String ERROR_CODE = "0xA4005"; + + public static final String ERROR_MSG_FORMAT = " The name %s used for the nonterminal %s referenced by the production %s" + + " should start with a lower-case letter."; + + @Override + public void check(ASTNonTerminal a) { + if (a.isPresentUsageName()) { + if (!Character.isLowerCase(a.getUsageName().charAt(0))) { + String rulename = a.getEnclosingScope().getSpanningSymbol().getName(); + Log.warn(String.format(ERROR_CODE + ERROR_MSG_FORMAT, a.getUsageName(), a.getName(), rulename), + a.get_SourcePositionStart()); + } + + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ConservativeExtensionCheck.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ConservativeExtensionCheck.java new file mode 100644 index 0000000000..e18c9e0be4 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ConservativeExtensionCheck.java @@ -0,0 +1,74 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.MCGrammarSymbolTableHelper; +import de.monticore.grammar.grammar._ast.ASTGrammarAnnotation; +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.monticore.grammar.grammar._symboltable.RuleComponentSymbol; +import de.se_rwth.commons.logging.Log; + +import java.util.List; + +public class ConservativeExtensionCheck implements GrammarASTMCGrammarCoCo { + + // TODO: Multiple kinds of Errors, #2376 + + public static final String ERROR_CODE = "0xA2007"; + + public static final String ERROR_MSG_FORMAT = " Warning: Production %s does not extend %s in a conservative manner in component %s. This can lead to problems in the AST."; + + @Override + public void check(ASTMCGrammar node) { + MCGrammarSymbol g = node.getSymbol(); + for (ProdSymbol nt : g.getProds()) { + if (!hasNonConservativeAnno(nt.getAstNode().getGrammarAnnotationList())) { + //check when you extend a class not conservative directly (Subclass extends Superclass = ...) + if (nt.isClass() && !nt.getSuperProds().isEmpty() + && !MCGrammarSymbolTableHelper.getAllSuperProds(nt).isEmpty()) { + for (ProdSymbol superNt : MCGrammarSymbolTableHelper.getAllSuperProds(nt)) { + compareComponents(nt, superNt); + } + } + //checks when you define a Prod with the same Name as a Prod in a Supergrammar + if(!g.getSuperGrammarSymbols().isEmpty()){ + for(MCGrammarSymbol superg : g.getSuperGrammarSymbols()){ + for(ProdSymbol superNt : superg.getProds()){ + if(nt.getName().equals(superNt.getName())){ + compareComponents(nt, superNt); + } + } + } + } + } + } + } + + protected void compareComponents(ProdSymbol p, ProdSymbol superp) { + for (RuleComponentSymbol comp : superp.getProdComponents()) { + List prodComponents = p.getSpannedScope().resolveRuleComponentDownMany(comp.getName()); + if (prodComponents.isEmpty()) { + Log.warn(String.format(ERROR_CODE + ERROR_MSG_FORMAT, p.getName(), superp.getFullName(), comp.getName()), + p.getSourcePosition()); + }else if (prodComponents.get(0).isIsTerminal() != comp.isIsTerminal() || + prodComponents.get(0).isIsNonterminal() != comp.isIsNonterminal() || + prodComponents.get(0).isIsList() != comp.isIsList() || + prodComponents.get(0).isIsOptional() != comp.isIsOptional() || + !prodComponents.get(0).getName().equals(comp.getName())) { + Log.warn(String.format(ERROR_CODE + ERROR_MSG_FORMAT, p.getName(), superp.getFullName(), comp.getName()), + p.getSourcePosition()); + } + } + } + + protected boolean hasNonConservativeAnno(List grammarAnnotationsList) { + for (ASTGrammarAnnotation anno : grammarAnnotationsList) { + if (anno.isNonConservative()) { + return true; + } + } + return false; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/DerivedAndManualListName.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/DerivedAndManualListName.java new file mode 100644 index 0000000000..6a76049259 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/DerivedAndManualListName.java @@ -0,0 +1,63 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTNonTerminal; +import de.monticore.grammar.grammar._ast.ASTProd; +import de.monticore.grammar.grammar._cocos.GrammarASTProdCoCo; +import de.monticore.grammar.grammar._symboltable.RuleComponentSymbol; +import de.monticore.grammar.grammar._symboltable.RuleComponentSymbolTOP; +import de.se_rwth.commons.StringTransformations; +import de.se_rwth.commons.logging.Log; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * checks that one NonTerminal does not define a component with the same derived and a manual name at the same time + * for example: + * A = names:Name* Name*; + * is not allowed because both definition are merged into the same attribute but create different method names + * form names:Name* e.g. the method name getNameList() is created + * but from Name* e.g. the method name getNameList() is created + *

+ * this does not fit together and is therefore forbidden + */ +public class DerivedAndManualListName implements GrammarASTProdCoCo { + + public static final String ERROR_CODE = "0xA2008"; + + public static final String ERROR_MSG_FORMAT = " The production '%s' contains two list nonterminals " + + "that result in the attribute name '%s'. " + + "But one name is derived from the nonterminal name and one is set manually. This is not allowed."; + + @Override + public void check(ASTProd node) { + if (node.isPresentSymbol()) { + List listComponents = node.getSymbol().getProdComponents().stream() + .filter(RuleComponentSymbolTOP::isIsList) + .collect(Collectors.toList()); + for (int i = 0; i < listComponents.size(); i++) { + for (int j = i + 1; j < listComponents.size(); j++) { + if ((listComponents.get(i).isIsNonterminal() && listComponents.get(j).isIsNonterminal()) || + (listComponents.get(i).isIsLexerNonterminal() && listComponents.get(j).isIsLexerNonterminal())) { + ASTNonTerminal nonterminal1 = (ASTNonTerminal) listComponents.get(i).getAstNode(); + ASTNonTerminal nonterminal2 = (ASTNonTerminal) listComponents.get(j).getAstNode(); + if (getAttributeName(nonterminal1).equals(getAttributeName(nonterminal2)) && + nonterminal1.isPresentUsageName() != nonterminal2.isPresentUsageName()){ + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, node.getName(),getAttributeName(nonterminal1)), + node.get_SourcePositionStart()); + } + } + } + } + } + } + + protected String getAttributeName(ASTNonTerminal astNonTerminal) { + if (astNonTerminal.isPresentUsageName()) { + return astNonTerminal.getUsageName(); + } else { + return StringTransformations.uncapitalize(astNonTerminal.getName()) + "s"; + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/DuplicatedEnumConstant.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/DuplicatedEnumConstant.java new file mode 100644 index 0000000000..a56a10da4d --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/DuplicatedEnumConstant.java @@ -0,0 +1,36 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import java.util.ArrayList; +import java.util.List; + +import de.monticore.grammar.grammar._ast.ASTConstant; +import de.monticore.grammar.grammar._ast.ASTEnumProd; +import de.monticore.grammar.grammar._cocos.GrammarASTEnumProdCoCo; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that every EnumConstant is only used once + * + */ +public class DuplicatedEnumConstant implements GrammarASTEnumProdCoCo { + + public static final String ERROR_CODE = "0xA4014"; + + public static final String ERROR_MSG_FORMAT = " Duplicate enum constant: %s."; + public static final String HINT = "\nHint: The constants of enumerations must be unique within an enumeration."; + + @Override + public void check(ASTEnumProd a) { + List constants = new ArrayList<>(); + for(ASTConstant c: a.getConstantList()) { + if(!constants.contains(c.getName())){ + constants.add(c.getName()); + } else { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, c.getName()) + HINT, + c.get_SourcePositionStart()); + } + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/DuplicatedSymbolDefinitionInProd.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/DuplicatedSymbolDefinitionInProd.java new file mode 100644 index 0000000000..2f8bfb5064 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/DuplicatedSymbolDefinitionInProd.java @@ -0,0 +1,33 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTProd; +import de.monticore.grammar.grammar._ast.ASTSymbolDefinition; +import de.monticore.grammar.grammar._cocos.GrammarASTProdCoCo; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that Prods have one symbol and one scope keyword at most + * + */ +public class DuplicatedSymbolDefinitionInProd implements GrammarASTProdCoCo { + + public static final String ERROR_CODE = "0xA4041"; + + public static final String ERROR_MSG_FORMAT = " Symbol or scope is mentioned more than once in the declaration '%s'."; + + @Override + public void check(ASTProd a) { + boolean isScope = false; + boolean isSymbol = false; + for (ASTSymbolDefinition c : a.getSymbolDefinitionList()) { + if ((c.isGenScope() && isScope) || (c.isGenSymbol() && isSymbol)) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, a.getName()), a.get_SourcePositionStart()); + } + isScope |= c.isGenScope(); + isSymbol |= c.isGenSymbol(); + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ExternalNTNoASTRule.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ExternalNTNoASTRule.java new file mode 100644 index 0000000000..62b2225afa --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ExternalNTNoASTRule.java @@ -0,0 +1,25 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTASTRule; +import de.monticore.grammar.grammar._cocos.GrammarASTASTRuleCoCo; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.se_rwth.commons.logging.Log; + +import java.util.Optional; + +public class ExternalNTNoASTRule implements GrammarASTASTRuleCoCo { + + public static final String ERROR_CODE = "0xA4118"; + + public static final String ERROR_MSG_FORMAT = " The external production %s must not have a " + + "corresponding ASTRule."; + + @Override + public void check(ASTASTRule node) { + Optional prod = node.getEnclosingScope().resolveProd(node.getType()); + if(prod.isPresent() && prod.get().isIsExternal()){ + Log.error(ERROR_CODE+String.format(ERROR_MSG_FORMAT, prod.get().getName())); + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ExternalNTOnlyInComponentGrammar.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ExternalNTOnlyInComponentGrammar.java new file mode 100644 index 0000000000..930459dc92 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ExternalNTOnlyInComponentGrammar.java @@ -0,0 +1,77 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.se_rwth.commons.StringTransformations; +import de.se_rwth.commons.logging.Log; + +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Checks that external nonterminals only occur in a component grammar. + * + + */ +public class ExternalNTOnlyInComponentGrammar implements GrammarASTMCGrammarCoCo { + + public static final String ERROR_CODE = "0xA0276"; + + public static final String ERROR_MSG_FORMAT = " The external nonterminal %s must not be used in a grammar not marked " + + "as a grammar component."; + + @Override + public void check(ASTMCGrammar a) { + MCGrammarSymbol grammarSymbol = a.getSymbol(); + + if (!a.isComponent()) { + List externalProds = grammarSymbol.getProds().stream(). + filter(ProdSymbol::isIsExternal).collect(Collectors.toList()); + for(MCGrammarSymbol symbol: grammarSymbol.getAllSuperGrammars()){ + Collection prodSymbols = symbol.getProds(); + for(ProdSymbol mcProdSymbol : prodSymbols){ + if (mcProdSymbol.isIsExternal()) { + externalProds.add(mcProdSymbol); + } + } + } + + List prods = grammarSymbol.getProds().stream(). + filter(prodSymbol -> prodSymbol.isClass() || prodSymbol.isIsAbstract()).collect(Collectors.toList()); + for(MCGrammarSymbol symbol: grammarSymbol.getAllSuperGrammars()){ + Collection prodSymbols = symbol.getProds(); + for(ProdSymbol mcProdSymbol : prodSymbols){ + if (mcProdSymbol.isIsAbstract() || mcProdSymbol.isClass()) { + prods.add(mcProdSymbol); + } + } + } + + if(!externalProds.isEmpty()) { + for (ProdSymbol prodSymbol : prods) { + for (int i = externalProds.size()-1; i >= 0; i--) { + ProdSymbol externalProdSymbol = externalProds.get(i); + if (prodSymbol.getName().equals(externalProdSymbol.getName())) { + externalProds.remove(i); + } + } + } + } + + for (ProdSymbol prodSymbol: externalProds) { + for (ProdSymbol prod : prods) { + if (!prod.getSpannedScope().resolveRuleComponentMany(StringTransformations.uncapitalize(prodSymbol.getName())).isEmpty()) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, prodSymbol.getName()), a.get_SourcePositionStart()); + } + } + } + } + } + +} + diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/GrammarCoCos.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/GrammarCoCos.java new file mode 100644 index 0000000000..b0e92c01fe --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/GrammarCoCos.java @@ -0,0 +1,113 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; + +public class GrammarCoCos { + public Grammar_WithConceptsCoCoChecker getCoCoChecker() { + Grammar_WithConceptsCoCoChecker checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new OverridingNTsHaveNoSuperRules()); + checker.addCoCo(new OverridingAbstractNTsHaveNoSuperRules()); + checker.addCoCo(new OverridingEnumNTs()); + checker.addCoCo(new OverridingNTs()); + checker.addCoCo(new OverridingAbstractNTs()); + checker.addCoCo(new OverridingInterfaceNTs()); + checker.addCoCo(new DuplicatedEnumConstant()); + checker.addCoCo(new NTAndASTRuleExtendType()); + checker.addCoCo(new NoASTRuleForEnumNTs()); + checker.addCoCo(new NTForASTRuleExists()); + checker.addCoCo(new MultipleASTRules()); + checker.addCoCo(new NoASTExtendsForClasses()); + checker.addCoCo(new LexNTsOnlyUseLexNTs()); + checker.addCoCo(new UsedLexNTNotDefined()); + checker.addCoCo(new UsedNTNotDefined()); + checker.addCoCo(new InterfaceNTWithoutImplementationOnlyInComponentGrammar()); + checker.addCoCo(new ExternalNTOnlyInComponentGrammar()); + checker.addCoCo(new AbstractNTWithoutExtensionOnlyInComponentGrammar()); + checker.addCoCo(new ProdAndExtendedProdUseSameAttrNameForDiffNTs()); + checker.addCoCo(new GrammarNameUpperCase()); + checker.addCoCo(new GrammarExtensionOnce()); + checker.addCoCo(new AbstractNTNotExtendInterfaceOrExternalNTs()); + checker.addCoCo(new AbstractNTOnlyExtendOrAstextendNTOrClass()); + checker.addCoCo(new AbstractNTOnlyExtendsOneNTOrClass()); + checker.addCoCo(new AbstractNTOnlyImplementInterfaceNTs()); + checker.addCoCo(new AttributeNameLowerCase()); + checker.addCoCo(new InterfaceNTOnlyExtendInterfaceNTs()); + checker.addCoCo(new KeywordAlternativeName()); + checker.addCoCo(new NTNotExtendInterfaceOrExternalNTs()); + checker.addCoCo(new NTOnlyExtendOrAstextendNTOrClass()); + checker.addCoCo(new NTOnlyExtendsOneNTOrClass()); + checker.addCoCo(new NTOnlyImplementInterfaceNTs()); + checker.addCoCo(new ProdStartsWithCapital()); + checker.addCoCo(new ProdAndOverriddenProdUseSameAttrNameForDiffNTs()); + checker.addCoCo(new ProdWithExtensionMustNotBeOverridden()); + checker.addCoCo(new ASTRuleAndNTUseSameAttrNameForDiffNTs()); + checker.addCoCo(new OverridingLexNTs()); + checker.addCoCo(new GrammarInheritanceCycle()); + checker.addCoCo(new LeftRecursiveRulesInBlock()); + checker.addCoCo(new DuplicatedSymbolDefinitionInProd()); + checker.addCoCo(new SubrulesUseInterfaceNTs()); + checker.addCoCo(new ReferenceSymbolSameAttribute()); + checker.addCoCo(new ReferenceSymbolNotName()); + checker.addCoCo(new ReferencedSymbolExists()); + checker.addCoCo(new ConservativeExtensionCheck()); + checker.addCoCo(new NoTokenDefined()); + // checker.addCoCo(new InheritedSymbolProperty()); + // checker.addCoCo(new InheritedScopeProperty()); + checker.addCoCo(new SymbolRuleWithoutSymbolRef()); + checker.addCoCo(new SymbolRuleHasName()); + checker.addCoCo(new NoNestedGenericsInAdditionalAttributes()); + checker.addCoCo(new NoMultipleSymbolRule()); + checker.addCoCo(new SymbolProdOverwrittenBySymbol()); + checker.addCoCo(new ScopeProdOverwrittenByScope()); + checker.addCoCo(new UniqueProdNameInGrammar()); + checker.addCoCo(new ProdExtendsNotExistingProd()); + checker.addCoCo(new TokenConstantInvalid()); + checker.addCoCo(new SplitRuleInvalid()); + checker.addCoCo(new KeyConstantInvalid()); + checker.addCoCo(new KeywordRuleInvalid()); + checker.addCoCo(new TerminalCritical()); + checker.addCoCo(new PackageNameLowerCase()); + checker.addCoCo(new NoOverridingNTHasAnnotation()); + checker.addCoCo(new OverridingNTHasNoAnnotation()); + checker.addCoCo(new ProdWithDoubleAnnos()); + checker.addCoCo(new ExternalNTNoASTRule()); + checker.addCoCo(new DerivedAndManualListName()); + checker.addCoCo(new KeyRuleWithoutName()); + checker.addCoCo(new SymbolWithManyNames()); + checker.addCoCo(new OverridingAdditionalAttributes()); + checker.addCoCo(new NoExtensionOfSymbolThatOnlySpansScope()); + // checker.addCoCo(new NoNTInheritanceCycle()); + checker.addCoCo(new LexProdModeNameUpperCase()); + checker.addCoCo(new NoTokenModeInComponentGrammar()); + checker.addCoCo(new InheritedModiOverwrite()); + checker.addCoCo(new NoForbiddenGrammarName()); + checker.addCoCo(new NoForbiddenProdName()); + checker.addCoCo(new NoForbiddenProdAndSymbolName()); + checker.addCoCo(new NoForbiddenProdNameAddon()); + checker.addCoCo(new NoForbiddenSymbolName()); + checker.addCoCo(new NoForbiddenSymbolNameAddon()); + checker.addCoCo(new RuleComponentsCompatible()); + + return checker; + } + + public Grammar_WithConceptsCoCoChecker getSymbolTableCoCoChecker() { + Grammar_WithConceptsCoCoChecker checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new OverridingNTsHaveNoSuperRules()); + checker.addCoCo(new OverridingAbstractNTsHaveNoSuperRules()); + checker.addCoCo(new OverridingEnumNTs()); + checker.addCoCo(new OverridingNTs()); + checker.addCoCo(new OverridingAbstractNTs()); + checker.addCoCo(new UsedNTNotDefined()); + checker.addCoCo(new KeywordAlternativeName()); + checker.addCoCo(new NTDefinedByAtmostOneProduction()); + checker.addCoCo(new NTUniqueIgnoreCase()); + checker.addCoCo(new ReferencedNTNotDefined()); + checker.addCoCo(new KeywordInvalidName()); + checker.addCoCo(new LexNTsNotEmpty()); + + return checker; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/GrammarExtensionOnce.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/GrammarExtensionOnce.java new file mode 100644 index 0000000000..402d7bbaa8 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/GrammarExtensionOnce.java @@ -0,0 +1,31 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.se_rwth.commons.Names; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that a grammar does not extend the same grammar multiple times + */ +public class GrammarExtensionOnce implements GrammarASTMCGrammarCoCo { + + public static final String ERROR_CODE = "0xA4150"; + + public static final String ERROR_MSG_FORMAT = "A grammar must not extend another grammar multiple times."; + + @Override + public void check(ASTMCGrammar gr) { + for (int i = 0; i < gr.getSupergrammarList().size() - 1; i++) { + for (int j = i + 1; j < gr.getSupergrammarList().size(); j++) { + if (Names.getQualifiedName(gr.getSupergrammar(i).getNameList()).equals( + Names.getQualifiedName(gr.getSupergrammar(j).getNameList()))) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, gr.getName()), + gr.get_SourcePositionStart()); + } + } + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/GrammarInheritanceCycle.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/GrammarInheritanceCycle.java new file mode 100644 index 0000000000..fae90d86be --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/GrammarInheritanceCycle.java @@ -0,0 +1,35 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTGrammarReference; +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.se_rwth.commons.Names; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that nonterminal names start lower-case. + * + + */ +public class GrammarInheritanceCycle implements GrammarASTMCGrammarCoCo { + + public static final String ERROR_CODE = "0xA4023"; + + public static final String ERROR_MSG_FORMAT = " The grammar %s introduces an inheritance cycle."; + + @Override + public void check(ASTMCGrammar a) { + for(ASTGrammarReference ref : a.getSupergrammarList()) { + if (Names.getQualifiedName(ref.getNameList()).equals( + Names.getQualifiedName(a.getPackageList()) +"."+ a.getName())) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, a.getName()), + a.get_SourcePositionStart()); + return; + } + } + } + + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/GrammarNameUpperCase.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/GrammarNameUpperCase.java new file mode 100644 index 0000000000..5616218a18 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/GrammarNameUpperCase.java @@ -0,0 +1,28 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that nonterminal names start lower-case. + * + + */ +public class GrammarNameUpperCase implements GrammarASTMCGrammarCoCo { + + public static final String ERROR_CODE = "0xA4033"; + + public static final String ERROR_MSG_FORMAT = " The grammar's name %s should start with an upper-case letter."; + + @Override + public void check(ASTMCGrammar a) { + if (!Character.isUpperCase(a.getName().charAt(0))) { + Log.warn(String.format(ERROR_CODE + ERROR_MSG_FORMAT, a.getName()), + a.get_SourcePositionStart()); + } + + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/InheritedModiOverwrite.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/InheritedModiOverwrite.java new file mode 100644 index 0000000000..1af62cec7f --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/InheritedModiOverwrite.java @@ -0,0 +1,43 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTLexProd; +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.se_rwth.commons.Joiners; +import de.se_rwth.commons.logging.Log; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +public class InheritedModiOverwrite implements GrammarASTMCGrammarCoCo { + + public static final String ERROR_CODE = "0xA4069"; + public static final String ERROR_MSG_FORMAT = " The lexical production %s of the grammar %s will inherit the token mode %s as it overwrites the lexical production %s of the grammar %s"; + + + @Override + public void check(ASTMCGrammar node) { + String grammarName = node.getName(); + List superGrammars = node.getSymbol().getAllSuperGrammars(); + for (MCGrammarSymbol superGrammar : superGrammars) { + ASTMCGrammar astNode = superGrammar.getAstNode(); + String superGrammarName = superGrammar.getName(); + for (ASTLexProd lexProd : astNode.getLexProdList()) { + if (lexProd.isPresentMode()) { + String modeString = lexProd.getMode(); + String prodName = lexProd.getName(); + List supLexProdList = node.getLexProdList().stream().filter(prod -> prod.getName().equals(prodName)).collect(Collectors.toList()); + for (ASTLexProd lex : supLexProdList) { + if (!lex.isPresentMode()) { + //warn the user that he inherits a token mode + Log.warn(String.format(ERROR_CODE + ERROR_MSG_FORMAT, prodName, grammarName, modeString, prodName, superGrammarName)); + } + } + } + } + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/InheritedScopeProperty.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/InheritedScopeProperty.java new file mode 100644 index 0000000000..6fad4d90f1 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/InheritedScopeProperty.java @@ -0,0 +1,35 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.MCGrammarSymbolTableHelper; +import de.monticore.grammar.grammar._ast.ASTProd; +import de.monticore.grammar.grammar._cocos.GrammarASTProdCoCo; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.se_rwth.commons.logging.Log; + +import java.util.Set; + +/** + * Checks that prods do not inherit their symbols from more than one class + */ +public class InheritedScopeProperty implements GrammarASTProdCoCo { + + public static final String ERROR_CODE = "0xA0135"; + + public static final String ERROR_MSG_FORMAT = " The rule %s inherits scope properties from more than one class."; + + @Override + public void check(ASTProd a) { + ProdSymbol s = a.getSymbol(); + Set superProds = MCGrammarSymbolTableHelper.getAllSuperProds(s); + boolean found = s.isIsScopeSpanning(); + for (ProdSymbol prod : superProds) { + if (found && prod.isIsScopeSpanning()) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, a.getName()), a.get_SourcePositionStart()); + } else if (prod.isIsScopeSpanning()) { + found = true; + } + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/InheritedSymbolProperty.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/InheritedSymbolProperty.java new file mode 100644 index 0000000000..c975675cf4 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/InheritedSymbolProperty.java @@ -0,0 +1,39 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.MCGrammarSymbolTableHelper; +import de.monticore.grammar.grammar._ast.ASTProd; +import de.monticore.grammar.grammar._cocos.GrammarASTProdCoCo; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.se_rwth.commons.logging.Log; + +import java.util.Optional; +import java.util.Set; + +/** + * Checks that prods do not inherit their symbols from more than one class + */ +public class InheritedSymbolProperty implements GrammarASTProdCoCo { + + public static final String ERROR_CODE = "0xA0125"; + + public static final String ERROR_MSG_FORMAT = " The rule %s inherits symbols from more than one class."; + + @Override + public void check(ASTProd a) { + ProdSymbol s = a.getSymbol(); + Set superProds = MCGrammarSymbolTableHelper.getAllSuperProds(s); + Optional found = Optional.empty(); + for (ProdSymbol prod : superProds) { + if (found.isPresent() && prod.isIsSymbolDefinition()) { + if (!MCGrammarSymbolTableHelper.getAllSuperProds(found.get()).contains(prod)) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, a.getName()), a.get_SourcePositionStart()); + } + } else if (prod.isIsSymbolDefinition()) { + found = Optional.of(prod); + } + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/InterfaceNTOnlyExtendInterfaceNTs.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/InterfaceNTOnlyExtendInterfaceNTs.java new file mode 100644 index 0000000000..a37924dc55 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/InterfaceNTOnlyExtendInterfaceNTs.java @@ -0,0 +1,46 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import java.util.List; +import java.util.Optional; + +import de.monticore.grammar.grammar._ast.ASTInterfaceProd; +import de.monticore.grammar.grammar._ast.ASTRuleReference; +import de.monticore.grammar.grammar._cocos.GrammarASTInterfaceProdCoCo; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that nonterminals only extends abstract or normal nonterminals.. + * + */ +public class InterfaceNTOnlyExtendInterfaceNTs implements GrammarASTInterfaceProdCoCo { + + public static final String ERROR_CODE = "0xA2116"; + + public static final String ERROR_MSG_FORMAT = " The interface nonterminal %s must not extend the%s nonterminal %s. " + + + "Interface nonterminals may only extend interface nonterminals."; + + @Override + public void check(ASTInterfaceProd a) { + if (!a.getSuperInterfaceRuleList().isEmpty()) { + List superRules = a.getSuperInterfaceRuleList(); + for (ASTRuleReference sr : superRules) { + Optional ruleSymbol = a.getEnclosingScope().resolveProd(sr.getName()); + if (ruleSymbol.isPresent()) { + ProdSymbol r = ruleSymbol.get(); + boolean isAbstract = r.isIsAbstract(); + boolean isExternal = r.isIsExternal(); + if (!r.isIsInterface()) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, a.getName(), + isAbstract ? " abstract" : isExternal ? " external" : "", r.getName()), + a.get_SourcePositionStart()); + } + } + } + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/InterfaceNTWithoutImplementationOnlyInComponentGrammar.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/InterfaceNTWithoutImplementationOnlyInComponentGrammar.java new file mode 100644 index 0000000000..6c11d6e980 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/InterfaceNTWithoutImplementationOnlyInComponentGrammar.java @@ -0,0 +1,123 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import com.google.common.collect.Lists; +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbolSurrogate; +import de.monticore.grammar.grammar._symboltable.RuleComponentSymbol; +import de.se_rwth.commons.logging.Log; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * Checks that abstract nonterminals witheout extending productions only occur in a component grammar. + */ +public class InterfaceNTWithoutImplementationOnlyInComponentGrammar implements GrammarASTMCGrammarCoCo { + + public static final String ERROR_CODE = "0xA0278"; + + public static final String ERROR_MSG_FORMAT = " The interface nonterminal %s must not be used without nonterminals " + + "implementing it in a grammar not marked as a grammar component."; + + @Override + public void check(ASTMCGrammar a) { + MCGrammarSymbol grammarSymbol = a.getSymbol(); + + // if the given grammar is a component grammar we ignore it, since there we allow interfaces without an + // implementation + if (!a.isComponent()) { + + // we collect all interface productions from the grammar and all its super grammars and save them to the list + // interfaceProds + List interfaceProds = grammarSymbol.getProds().stream(). + filter(ProdSymbol::isIsInterface).collect(Collectors.toList()); + for (MCGrammarSymbol symbol : grammarSymbol.getAllSuperGrammars()) { + Collection prodSymbols = symbol.getProds(); + for (ProdSymbol mcProdSymbol : prodSymbols) { + if (mcProdSymbol.isIsInterface()) { + interfaceProds.add(mcProdSymbol); + } + } + } + + // for every interface production we get all other interfaces implementing it and save it to map of all interface + // productions mapping to all their subinterfaces + Map> subSymbols = new HashMap<>(); + for (ProdSymbol interfaceProd : interfaceProds) { + for (ProdSymbol superInterface : interfaceProd.getSuperInterfaceProds()) { + if (!subSymbols.containsKey(superInterface.getName())) { + subSymbols.put(superInterface.getName(), new ArrayList<>()); + } + subSymbols.get(superInterface.getName()).add(interfaceProd); + } + } + + // we collect all non-interface productions from the grammar and all its super-grammars and save them to the list + // prods + List prods = grammarSymbol.getProds().stream(). + filter(prodSymbol -> prodSymbol.isClass() || prodSymbol.isIsAbstract()).collect(Collectors.toList()); + for (MCGrammarSymbol symbol : grammarSymbol.getAllSuperGrammars()) { + Collection prodSymbols = symbol.getProds(); + for (ProdSymbol mcProdSymbol : prodSymbols) { + if (mcProdSymbol.isIsAbstract() || mcProdSymbol.isClass()) { + prods.add(mcProdSymbol); + } + } + } + + // from the list interfaceProds we remove every interface production that does not get used in a non-interface + // production + if (!interfaceProds.isEmpty()) { + List temp = new ArrayList<>(); + for (ProdSymbol prod : prods) { + for (RuleComponentSymbol component : prod.getProdComponents()) { + for (ProdSymbol interfaceProd : interfaceProds) { + if (component.isPresentReferencedType() && component.getReferencedType().equals(interfaceProd.getName())) { + temp.add(interfaceProd); + } + } + } + } + interfaceProds = temp; + } + + // for every remaining interface production we check if it or any subinterface gets implemented anywhere and + // otherwise log an error since it gets used in a production without getting implemented + for (ProdSymbol interf : interfaceProds) { + if (!isImplemented(interf, prods, subSymbols)) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, interf.getName()), a.get_SourcePositionStart()); + } + } + } + } + + protected boolean isImplemented(ProdSymbol interfaceSymbol, List prods, Map> subSymbols) { + + // if a production exists that directly implements the interface return true + for (ProdSymbol prod : prods) { + for (ProdSymbol implemented : prod.getSuperInterfaceProds()) { + if (implemented.getName().equals(interfaceSymbol.getName())) { + return true; + } + } + } + + // otherwise get all interfaces that extend this interface and recursively call this method on them. if any of them + // return true end the method and return true. otherwise, if none of its subinterface gets implemented, return false + if (subSymbols.containsKey(interfaceSymbol.getName())) { + for (ProdSymbol subSymbol : subSymbols.get(interfaceSymbol.getName())) { + if (isImplemented(subSymbol, prods, subSymbols)) { + return true; + } + } + } + + return false; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/KeyConstantInvalid.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/KeyConstantInvalid.java new file mode 100644 index 0000000000..b26f0703bf --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/KeyConstantInvalid.java @@ -0,0 +1,24 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTKeyConstant; +import de.monticore.grammar.grammar._cocos.GrammarASTKeyConstantCoCo; +import de.se_rwth.commons.logging.Log; + +public class KeyConstantInvalid implements GrammarASTKeyConstantCoCo { + + public static final String ERROR_CODE = "0xA4091"; + + public static final String ERROR_MSG_FORMAT = + " The string '%s' for key() must be compatible to 'Name'"; + + @Override + public void check(ASTKeyConstant a) { + for (String s :a.getStringList()) { + if (!s.matches("[a-zA-Z_$][a-zA-Z0-9_$]*")) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, s), a.get_SourcePositionStart()); + } + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/KeyRuleWithoutName.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/KeyRuleWithoutName.java new file mode 100644 index 0000000000..7f8361d878 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/KeyRuleWithoutName.java @@ -0,0 +1,48 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar.GrammarMill; +import de.monticore.grammar.grammar._ast.ASTGrammarNode; +import de.monticore.grammar.grammar._ast.ASTKeyConstant; +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar._visitor.GrammarTraverser; +import de.monticore.grammar.grammar._visitor.GrammarVisitor2; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that a grammar using keyword rules defines the token Name + */ +public class KeyRuleWithoutName implements GrammarASTMCGrammarCoCo { + + public static final String ERROR_CODE = "0xA0142"; + + public static final String ERROR_MSG_FORMAT = "Using the keyword rules a grammar must define the token Name."; + + @Override + public void check(ASTMCGrammar gr) { + MCGrammarSymbol grSymbol = gr.getSymbol(); + if (!gr.isComponent() && !grSymbol.getProdWithInherited("Name").isPresent()) { + if (!gr.getKeywordRuleList().isEmpty() || new FindKeyConstant().getResult(gr)) { + Log.error(ERROR_CODE + ERROR_MSG_FORMAT, gr.get_SourcePositionStart()); + } + } + } + + protected class FindKeyConstant implements GrammarVisitor2 { + protected boolean hasKeyConstant = false; + + public boolean getResult(ASTGrammarNode ast) { + GrammarTraverser traverser = GrammarMill.traverser(); + traverser.add4Grammar(this); + ast.accept(traverser); + return hasKeyConstant; + } + + public void visit(ASTKeyConstant ast) { + hasKeyConstant = true; + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/KeywordAlternativeName.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/KeywordAlternativeName.java new file mode 100644 index 0000000000..d87ab6fe0d --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/KeywordAlternativeName.java @@ -0,0 +1,31 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.MCGrammarSymbolTableHelper; +import de.monticore.grammar.grammar._ast.ASTConstantGroup; +import de.monticore.grammar.grammar._cocos.GrammarASTConstantGroupCoCo; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that alternatives of keywords are named. + * + */ +public class KeywordAlternativeName implements GrammarASTConstantGroupCoCo { + + public static final String ERROR_CODE = "0xA4019"; + + public static final String ERROR_MSG_FORMAT = " The production %s must not use a ConstantGroup with more than one element without naming it."; + + @Override + public void check(ASTConstantGroup a) { + if (!a.isPresentUsageName()&& a.getConstantList().size() >1) { + String rulename = MCGrammarSymbolTableHelper.getEnclosingRule(a).get().getName(); + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, rulename), + a.get_SourcePositionStart()); + + } + } + + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/KeywordInvalidName.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/KeywordInvalidName.java new file mode 100644 index 0000000000..ba6b8de860 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/KeywordInvalidName.java @@ -0,0 +1,36 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTConstant; +import de.monticore.grammar.grammar._ast.ASTConstantGroup; +import de.monticore.grammar.grammar._cocos.GrammarASTConstantGroupCoCo; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that alternatives of keywords are named. + * + + */ +public class KeywordInvalidName implements GrammarASTConstantGroupCoCo { + + public static final String ERROR_CODE = "0xA4018"; + + public static final String ERROR_MSG_FORMAT = " The production %s must not use the keyword %s without naming it."; + + @Override + public void check(ASTConstantGroup a) { + if (!a.isPresentUsageName()) { + for (ASTConstant c : a.getConstantList()) { + if (c.getHumanName().isEmpty()) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, + a.getEnclosingScope().getSpanningSymbol() + .getName(), + c.getName()), + a.get_SourcePositionStart()); + } + } + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/KeywordRuleInvalid.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/KeywordRuleInvalid.java new file mode 100644 index 0000000000..0e110c943e --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/KeywordRuleInvalid.java @@ -0,0 +1,24 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTKeywordRule; +import de.monticore.grammar.grammar._cocos.GrammarASTKeywordRuleCoCo; +import de.se_rwth.commons.logging.Log; + +public class KeywordRuleInvalid implements GrammarASTKeywordRuleCoCo { + + public static final String ERROR_CODE = "0xA4093"; + + public static final String ERROR_MSG_FORMAT = + " The string '%s' must be compatible to 'Name'"; + + @Override + public void check(ASTKeywordRule a) { + for (String s :a.getStringList()) { + if (!s.matches("[a-zA-Z_$][a-zA-Z0-9_$]*")) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, s), a.get_SourcePositionStart()); + } + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/LeftRecursiveRulesInBlock.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/LeftRecursiveRulesInBlock.java new file mode 100644 index 0000000000..fb3dc65836 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/LeftRecursiveRulesInBlock.java @@ -0,0 +1,47 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import com.google.common.collect.Lists; +import de.monticore.grammar.DirectLeftRecursionDetector; +import de.monticore.grammar.MCGrammarSymbolTableHelper; +import de.monticore.grammar.grammar._ast.ASTAlt; +import de.monticore.grammar.grammar._ast.ASTBlock; +import de.monticore.grammar.grammar._ast.ASTClassProd; +import de.monticore.grammar.grammar._cocos.GrammarASTClassProdCoCo; +import de.se_rwth.commons.logging.Log; + +import java.util.ArrayList; + +/** + * Checks that blocks do not contain left recursive rules + * If Antlr (Antlr 4.5 throws an exception) can take care of it, the check is + * no longer necessary. + * + */ +public class LeftRecursiveRulesInBlock implements GrammarASTClassProdCoCo { + + public static final String ERROR_CODE = "0xA4056"; + + public static final String ERROR_MSG_FORMAT = " The left recursive rule %s is not allowed in blocks, because it is not supported in Antlr. "; + + @Override + public void check(ASTClassProd a) { + if (!a.getSymbol().isIsDirectLeftRecursive() && !a.getSymbol().isIsIndirectLeftRecursive()) { + return; + } + DirectLeftRecursionDetector detector = new DirectLeftRecursionDetector(); + ArrayList ruleNames = Lists.newArrayList(a.getName()); + MCGrammarSymbolTableHelper.getAllSuperProds(a.getSymbol()).forEach(p -> ruleNames.add(p.getName())); + for (ASTAlt alt : a.getAltList()) { + if (!alt.getComponentList().isEmpty() && alt.getComponentList().get(0) instanceof ASTBlock) { + if (detector.isAlternativeLeftRecursive(alt, ruleNames)) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, a.getSymbol().getName()), + a.get_SourcePositionStart()); + return; + } + } + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/LexNTsNotEmpty.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/LexNTsNotEmpty.java new file mode 100644 index 0000000000..e9347e7ea5 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/LexNTsNotEmpty.java @@ -0,0 +1,64 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTConstantsGrammar; +import de.monticore.grammar.grammar._ast.ASTLexAlt; +import de.monticore.grammar.grammar._ast.ASTLexBlock; +import de.monticore.grammar.grammar._ast.ASTLexChar; +import de.monticore.grammar.grammar._ast.ASTLexCharRange; +import de.monticore.grammar.grammar._ast.ASTLexComponent; +import de.monticore.grammar.grammar._ast.ASTLexNonTerminal; +import de.monticore.grammar.grammar._ast.ASTLexProd; +import de.monticore.grammar.grammar._ast.ASTLexSimpleIteration; +import de.monticore.grammar.grammar._ast.ASTLexString; +import de.monticore.grammar.grammar._cocos.GrammarASTLexProdCoCo; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that used nonterminals are lexical nonterminals. + * + + */ +public class LexNTsNotEmpty implements GrammarASTLexProdCoCo { + + public static final String ERROR_CODE = "0xA4015"; + + public static final String ERROR_MSG_FORMAT = " The lexical production %s must not allow the empty token."; + + @Override + public void check(ASTLexProd a) { + for (ASTLexAlt alt : a.getAltList()) { + if (alt.getLexComponentList().isEmpty()) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, a.getName()), + a.get_SourcePositionStart()); + return; + } + else { + for (ASTLexComponent rc : alt.getLexComponentList()) { + if (rc instanceof ASTLexBlock) { + if (((ASTLexBlock) rc).getIteration() == ASTConstantsGrammar.PLUS + || ((ASTLexBlock) rc).getIteration() == ASTConstantsGrammar.DEFAULT) { + return; + } + } + else if (rc instanceof ASTLexSimpleIteration) { + if (((ASTLexSimpleIteration) rc).getIteration() == ASTConstantsGrammar.PLUS + || ((ASTLexSimpleIteration) rc).getIteration() == ASTConstantsGrammar.DEFAULT) { + return; + } + } + else if (rc instanceof ASTLexNonTerminal + || rc instanceof ASTLexString + || rc instanceof ASTLexChar + || rc instanceof ASTLexCharRange + || rc instanceof ASTLexString) { + return; + } + } + } + } + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, a.getName()), + a.get_SourcePositionStart()); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/LexNTsOnlyUseLexNTs.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/LexNTsOnlyUseLexNTs.java new file mode 100644 index 0000000000..0334352fa9 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/LexNTsOnlyUseLexNTs.java @@ -0,0 +1,41 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import java.util.Optional; + +import de.monticore.grammar.MCGrammarSymbolTableHelper; +import de.monticore.grammar.grammar._ast.ASTLexNonTerminal; +import de.monticore.grammar.grammar._cocos.GrammarASTLexNonTerminalCoCo; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that used nonterminals are lexical nonterminals. + * + */ +public class LexNTsOnlyUseLexNTs implements GrammarASTLexNonTerminalCoCo { + + public static final String ERROR_CODE = "0xA4017"; + + public static final String ERROR_MSG_FORMAT = " The lexical production %s must not use" + + " the nonterminal %s because %s is defined by a production of" + + " another type than lexical. Lexical productions may only reference nonterminals" + + " defined by lexical productions."; + + @Override + public void check(ASTLexNonTerminal a) { + Optional grammarSymbol = MCGrammarSymbolTableHelper + .getMCGrammarSymbol(a.getEnclosingScope()); + + Optional ruleSymbol = MCGrammarSymbolTableHelper.getEnclosingRule(a); + String ruleName = ruleSymbol.isPresent() ? ruleSymbol.get().getName() : ""; + if (grammarSymbol.isPresent() + && grammarSymbol.get().getProdWithInherited(a.getName()).isPresent() && + !grammarSymbol.get().getProdWithInherited(a.getName()).get().isIsLexerProd()) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, ruleName, a.getName(), a.getName()), + a.get_SourcePositionStart()); + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/LexProdModeNameUpperCase.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/LexProdModeNameUpperCase.java new file mode 100644 index 0000000000..6d4c99b1ae --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/LexProdModeNameUpperCase.java @@ -0,0 +1,26 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTLexProd; +import de.monticore.grammar.grammar._cocos.GrammarASTLexProdCoCo; +import de.se_rwth.commons.logging.Log; +import org.apache.commons.lang3.StringUtils; + +public class LexProdModeNameUpperCase implements GrammarASTLexProdCoCo { + public static final String ERROR_CODE = "0xA4038"; + + public static final String ERROR_MSG_FORMAT = " The lexical production %s must use Upper-case mode names."; + + @Override + public void check(ASTLexProd node) { + if (node.isPresentMode()) { + String m = node.getMode(); + if(!StringUtils.isAllUpperCase(m)){ + Log.warn(String.format(ERROR_CODE + ERROR_MSG_FORMAT, node.getName()), + node.get_SourcePositionStart()); + } + } + } +} + + diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/MultipleASTRules.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/MultipleASTRules.java new file mode 100644 index 0000000000..52ffdde98a --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/MultipleASTRules.java @@ -0,0 +1,38 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import java.util.ArrayList; +import java.util.List; + +import de.monticore.grammar.grammar._ast.ASTASTRule; +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that for each rule there exists max. one astrule + * + + */ +public class MultipleASTRules implements GrammarASTMCGrammarCoCo { + + public static final String ERROR_CODE = "0xA4020"; + + public static final String ERROR_MSG_FORMAT = " There must not exist more than one AST" + + " rule for the nonterminal %s."; + + @Override + public void check(ASTMCGrammar a) { + List nts = new ArrayList<>(); + for(ASTASTRule rule : a.getASTRuleList()){ + if (!nts.contains(rule.getType())) { + nts.add(rule.getType()); + } else { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, rule.getType()), + rule.get_SourcePositionStart()); + } + } + + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NTAndASTRuleExtendType.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NTAndASTRuleExtendType.java new file mode 100644 index 0000000000..9017610df7 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NTAndASTRuleExtendType.java @@ -0,0 +1,52 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.*; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.monticore.types.mcsimplegenerictypes.MCSimpleGenericTypesMill; +import de.se_rwth.commons.logging.Log; + +import java.util.Optional; + +/** + * Checks that nonterminal names start lower-case. + */ +public class NTAndASTRuleExtendType implements GrammarASTMCGrammarCoCo { + + public static final String ERROR_CODE = "0xA4013"; + + public static final String ERROR_MSG_FORMAT = " The AST rule for %s must not extend the type " + + "%s because the production already extends a type."; + + @Override + public void check(ASTMCGrammar a) { + MCGrammarSymbol grammarSymbol = a.getSymbol(); + for (ASTASTRule rule : a.getASTRuleList()) { + if (!rule.getASTSuperClassList().isEmpty()) { + Optional ruleSymbol = grammarSymbol.getProdWithInherited(rule.getType()); + if (ruleSymbol.isPresent()) { + if (ruleSymbol.get().isClass()) { + if (ruleSymbol.get().isPresentAstNode() + && (!((ASTClassProd) ruleSymbol.get().getAstNode()).getASTSuperClassList().isEmpty() + || !((ASTClassProd) ruleSymbol.get().getAstNode()).getSuperRuleList().isEmpty())) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, rule.getType(), + MCSimpleGenericTypesMill.prettyPrint(rule.getASTSuperClassList().get(0), false).trim()), + rule.get_SourcePositionStart()); + } + } else if (ruleSymbol.get().isPresentAstNode() + && ruleSymbol.get().getAstNode() instanceof ASTAbstractProd) { + ASTAbstractProd prod = (ASTAbstractProd) ruleSymbol.get().getAstNode(); + if (!prod.getASTSuperClassList().isEmpty() || !prod.getSuperRuleList().isEmpty()) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, rule.getType(), + MCSimpleGenericTypesMill.prettyPrint(rule.getASTSuperClassList().get(0), false).trim()), + rule.get_SourcePositionStart()); + } + } + } + } + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NTDefinedByAtmostOneProduction.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NTDefinedByAtmostOneProduction.java new file mode 100644 index 0000000000..4232d95348 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NTDefinedByAtmostOneProduction.java @@ -0,0 +1,44 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import java.util.ArrayList; +import java.util.List; + +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._ast.ASTProd; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that nonterminals only extends abstract or normal nonterminals. + * + */ +public class NTDefinedByAtmostOneProduction implements GrammarASTMCGrammarCoCo { + + public static final String ERROR_CODE = "0xA2025"; + + public static final String ERROR_MSG_FORMAT = " The nonterminal %s must not be defined by more than one production."; + + @Override + public void check(ASTMCGrammar a) { + List prodnames = new ArrayList<>(); + List prods = new ArrayList<>(); + prods.addAll(a.getAbstractProdList()); + prods.addAll(a.getClassProdList()); + prods.addAll(a.getEnumProdList()); + prods.addAll(a.getInterfaceProdList()); + prods.addAll(a.getLexProdList()); + prods.addAll(a.getExternalProdList()); + + for(ASTProd p: prods){ + if(prodnames.contains(p.getName())){ + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, p.getName()), + p.get_SourcePositionStart()); + } else { + prodnames.add(p.getName()); + } + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NTForASTRuleExists.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NTForASTRuleExists.java new file mode 100644 index 0000000000..ab96a5519b --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NTForASTRuleExists.java @@ -0,0 +1,46 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import java.util.Map; + +import de.monticore.grammar.grammar._ast.ASTASTRule; +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that nonterminal names start lower-case. + * + + */ +public class NTForASTRuleExists implements GrammarASTMCGrammarCoCo { + + public static final String ERROR_CODE = "0xA4021"; + + public static final String ERROR_MSG_FORMAT = " There must not exist an AST rule for the nonterminal %s" + + " because there exists no production defining %s"; + + @Override + public void check(ASTMCGrammar a) { + MCGrammarSymbol grammarSymbol = a.getSymbol(); + boolean prodFound = false; + for(ASTASTRule astrule : a.getASTRuleList()){ + if(!grammarSymbol.getProdWithInherited(astrule.getType()).isPresent()){ + for(Map.Entry entry : grammarSymbol.getProdsWithInherited().entrySet()){ + ProdSymbol rs = (ProdSymbol) entry.getValue(); + if (astrule.getType().equals(rs.getName())) { + prodFound = true; + break ; + } + } + if (!prodFound) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, astrule.getType(), astrule.getType()), + astrule.get_SourcePositionStart()); + } + } + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NTNotExtendInterfaceOrExternalNTs.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NTNotExtendInterfaceOrExternalNTs.java new file mode 100644 index 0000000000..e2e5c9022d --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NTNotExtendInterfaceOrExternalNTs.java @@ -0,0 +1,47 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import java.util.List; +import java.util.Optional; + +import de.monticore.grammar.grammar._ast.ASTClassProd; +import de.monticore.grammar.grammar._ast.ASTRuleReference; +import de.monticore.grammar.grammar._cocos.GrammarASTClassProdCoCo; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that nonterminals only extends abstract or normal nonterminals. + * + + */ +public class NTNotExtendInterfaceOrExternalNTs implements GrammarASTClassProdCoCo { + + public static final String ERROR_CODE = "0xA2103"; + + public static final String ERROR_MSG_FORMAT = " The nonterminal %s must not extend the %s nonterminal %s. " + + + "Nonterminals may only extend abstract or normal nonterminals."; + + @Override + public void check(ASTClassProd a) { + if (!a.getSuperRuleList().isEmpty()) { + List superRules = a.getSuperRuleList(); + for (ASTRuleReference sr : superRules) { + Optional ruleSymbol = a.getEnclosingScope().resolveProd(sr.getName()); + if (ruleSymbol.isPresent()) { + ProdSymbol r = ruleSymbol.get(); + boolean isInterface = r.isIsInterface(); + boolean isExternal = r.isIsExternal(); + if (isInterface || isExternal) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, a.getName(), + isInterface ? "interface" : "external", r.getName()), + a.get_SourcePositionStart()); + } + } + } + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NTOnlyExtendOrAstextendNTOrClass.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NTOnlyExtendOrAstextendNTOrClass.java new file mode 100644 index 0000000000..721b0febf9 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NTOnlyExtendOrAstextendNTOrClass.java @@ -0,0 +1,27 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTClassProd; +import de.monticore.grammar.grammar._cocos.GrammarASTClassProdCoCo; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that nonterminals do not extend and astextend a type. + * + */ +public class NTOnlyExtendOrAstextendNTOrClass implements GrammarASTClassProdCoCo { + + public static final String ERROR_CODE = "0xA4029"; + + public static final String ERROR_MSG_FORMAT = " The nonterminal %s must not extend and astextend a type."; + + @Override + public void check(ASTClassProd a) { + if (!a.getSuperRuleList().isEmpty() && !a.getASTSuperClassList().isEmpty()) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, a.getName()), + a.get_SourcePositionStart()); + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NTOnlyExtendsOneNTOrClass.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NTOnlyExtendsOneNTOrClass.java new file mode 100644 index 0000000000..097d9babf5 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NTOnlyExtendsOneNTOrClass.java @@ -0,0 +1,32 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTClassProd; +import de.monticore.grammar.grammar._cocos.GrammarASTClassProdCoCo; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that nonterminals do not extend more than one nonterminals/class. + * + + */ +public class NTOnlyExtendsOneNTOrClass implements GrammarASTClassProdCoCo { + + public static final String ERROR_CODE = "0xA4011"; + + public static final String ERROR_MSG_FORMAT = " The nonterminal %s must not %s more than one %s."; + + @Override + public void check(ASTClassProd a) { + if (a.getSuperRuleList().size()>1) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, a.getName(), "extend", "nonterminal"), + a.get_SourcePositionStart()); + } + if(a.getASTSuperClassList().size()>1){ + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, a.getName(), "astextend", "class"), + a.get_SourcePositionStart()); + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NTOnlyImplementInterfaceNTs.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NTOnlyImplementInterfaceNTs.java new file mode 100644 index 0000000000..666f1e7a51 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NTOnlyImplementInterfaceNTs.java @@ -0,0 +1,43 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import java.util.List; +import java.util.Optional; + +import de.monticore.grammar.grammar._ast.ASTClassProd; +import de.monticore.grammar.grammar._ast.ASTRuleReference; +import de.monticore.grammar.grammar._cocos.GrammarASTClassProdCoCo; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that nonterminals only implement interface nonterminals. + * + + */ +public class NTOnlyImplementInterfaceNTs implements GrammarASTClassProdCoCo { + + public static final String ERROR_CODE = "0xA2102"; + + public static final String ERROR_MSG_FORMAT = " The nonterminal %s must not implement the nonterminal %s. " + + + "Nonterminals may only implement interface nonterminals."; + + @Override + public void check(ASTClassProd a) { + if (!a.getSuperInterfaceRuleList().isEmpty()) { + List interfaces = a.getSuperInterfaceRuleList(); + for (ASTRuleReference i : interfaces) { + Optional ruleSymbol = a.getEnclosingScope().resolveProd(i.getName()); + if (ruleSymbol.isPresent()) { + ProdSymbol r = ruleSymbol.get(); + if (!r.isIsInterface()) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, a.getName(), r.getName()), + a.get_SourcePositionStart()); + } + } + } + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NTUniqueIgnoreCase.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NTUniqueIgnoreCase.java new file mode 100644 index 0000000000..1092d5659d --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NTUniqueIgnoreCase.java @@ -0,0 +1,47 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import java.util.ArrayList; +import java.util.List; + +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._ast.ASTProd; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that nonterminals or only overridden by normal nonterminals. + * + + */ +public class NTUniqueIgnoreCase implements GrammarASTMCGrammarCoCo { + +public static final String ERROR_CODE = "0xA2026"; + + public static final String ERROR_MSG_FORMAT = " The nonterminal %s must not be defined by more than one production: nonterminals aren't case-sensitive."; + + @Override + public void check(ASTMCGrammar a) { + List prodnames = new ArrayList<>(); + List prodnamesIgnoreCase = new ArrayList<>(); + List prods = new ArrayList<>(); + prods.addAll(a.getAbstractProdList()); + prods.addAll(a.getClassProdList()); + prods.addAll(a.getEnumProdList()); + prods.addAll(a.getInterfaceProdList()); + prods.addAll(a.getLexProdList()); + prods.addAll(a.getExternalProdList()); + + for(ASTProd p: prods){ + if(!prodnames.contains(p.getName()) && prodnamesIgnoreCase.contains(p.getName().toLowerCase())){ + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, p.getName()), + a.get_SourcePositionStart()); + } else { + prodnames.add(p.getName()); + prodnamesIgnoreCase.add(p.getName().toLowerCase()); + } + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoASTExtendsForClasses.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoASTExtendsForClasses.java new file mode 100644 index 0000000000..66c96016f7 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoASTExtendsForClasses.java @@ -0,0 +1,72 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTASTRule; +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbolSurrogate; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.mccollectiontypes._ast.ASTMCGenericType; +import de.monticore.types.mcsimplegenerictypes.MCSimpleGenericTypesMill; +import de.se_rwth.commons.Names; +import de.se_rwth.commons.logging.Log; + +import java.util.Map; + +/** + * Checks that no ast rules exist for enum nonterminals. + * + */ +public class NoASTExtendsForClasses implements GrammarASTMCGrammarCoCo { + + public static final String ERROR_CODE = "0xA4097"; + + public static final String ERROR_MSG_FORMAT = " It is forbidden to extend the rule %s with the external class %s."; + + @Override + public void check(ASTMCGrammar a) { + MCGrammarSymbol grammarSymbol = a.getSymbol(); + Map allProds = grammarSymbol.getProdsWithInherited(); + + for (ProdSymbol classProd : grammarSymbol.getProds()) { + for (ProdSymbolSurrogate sClass : classProd.getAstSuperClasses()) { + if (!allProds.containsKey( + sClass.getName().substring("AST".length()))) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, + classProd.getName(), + Names.getSimpleName(sClass.getName()), + classProd.getAstNode().get_SourcePositionStart())); + } + } + } + + for (ASTASTRule rule : a.getASTRuleList()) { + if (allProds.containsKey(rule.getType())) { + ProdSymbol prod = allProds.get(rule.getType()); + if (prod.isClass()) { + for (ASTMCType type : rule.getASTSuperClassList()) { + String simpleName = simpleName(type); + if (!allProds.containsKey(simpleName.substring("AST".length()))) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, + rule.getType(), simpleName, + rule.get_SourcePositionStart())); + } + } + } + } + } + } + + protected static String simpleName(ASTMCType type) { + String name; + if (type instanceof ASTMCGenericType) { + name = ((ASTMCGenericType) type).printWithoutTypeArguments(); + } else { + name = type.printType(); + } + return Names.getSimpleName(name); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoASTRuleForEnumNTs.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoASTRuleForEnumNTs.java new file mode 100644 index 0000000000..20444d9bf3 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoASTRuleForEnumNTs.java @@ -0,0 +1,37 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import java.util.Optional; + +import de.monticore.grammar.grammar._ast.ASTASTRule; +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that no ast rules exist for enum nonterminals. + * + + */ +public class NoASTRuleForEnumNTs implements GrammarASTMCGrammarCoCo { + + public static final String ERROR_CODE = "0xA4032"; + + public static final String ERROR_MSG_FORMAT = " There must not exist an AST rule for the enum nonterminal %s."; + + @Override + public void check(ASTMCGrammar a) { + MCGrammarSymbol grammarSymbol = a.getSymbol(); + for (ASTASTRule rule : a.getASTRuleList()) { + Optional ruleSymbol = grammarSymbol.getProdWithInherited(rule.getType()); + if (ruleSymbol.isPresent() && ruleSymbol.get().isIsEnum()) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, rule.getType()), + rule.get_SourcePositionStart()); + } + } + + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoExtensionOfSymbolThatOnlySpansScope.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoExtensionOfSymbolThatOnlySpansScope.java new file mode 100644 index 0000000000..11f74bf24a --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoExtensionOfSymbolThatOnlySpansScope.java @@ -0,0 +1,70 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbolSurrogate; +import de.se_rwth.commons.logging.Log; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +public class NoExtensionOfSymbolThatOnlySpansScope implements GrammarASTMCGrammarCoCo { + + public static final String ERROR_CODE = "0xA0810"; + + public static final String ERROR_MSG_FORMAT = " The production %s extends the symbol production %s and spans a scope " + + "without being a symbol itself."; + + protected boolean hasSymbol; + protected boolean hasScope; + protected ProdSymbol symbolProd; + + @Override + public void check(ASTMCGrammar node) { + MCGrammarSymbol symbol = node.getSymbol(); + Map prodsWithInherited = symbol.getProdsWithInherited(); + Collection prods = symbol.getProds(); + for(ProdSymbol prod: prods){ + hasSymbol = false; + hasScope = false; + if(prod.isIsScopeSpanning() && !prod.isIsSymbolDefinition()) { + checkProd(prod, prodsWithInherited); + if (hasSymbol && !hasScope) { + logError(prod, symbolProd); + } + } + } + } + + protected void checkProd(ProdSymbol prod, Map prodsWithInherited){ + List superProds = new ArrayList<>(prod.getSuperProds()); + superProds.addAll(prod.getSuperInterfaceProds()); + checkProdsAndLogError(superProds, prod, prodsWithInherited); + } + + protected void checkProdsAndLogError(List superProds, ProdSymbol prod, Map prodsWithInherited){ + for(ProdSymbolSurrogate surrogate: superProds){ + ProdSymbol superProd = prodsWithInherited.get(surrogate.getName()); + if(superProd.isIsSymbolDefinition()){ + hasSymbol = true; + symbolProd = superProd; + } + if (superProd.isIsScopeSpanning()) { + hasScope = true; + } + checkProd(superProd, prodsWithInherited); + } + } + + protected void logError(ProdSymbol original, ProdSymbol superProd){ + Log.error(ERROR_CODE + String.format(ERROR_MSG_FORMAT, original, superProd)); + } + + + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoForbiddenGrammarName.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoForbiddenGrammarName.java new file mode 100644 index 0000000000..f971e17a77 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoForbiddenGrammarName.java @@ -0,0 +1,30 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import com.google.common.collect.Lists; +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.se_rwth.commons.logging.Log; + +import java.util.List; +import java.util.Collections; + +public class NoForbiddenGrammarName implements GrammarASTMCGrammarCoCo { + + public static final String ERROR_CODE = "0xA4036"; + + public static final String ERROR_MSG_FORMAT = " There must not exist a grammar with the name %s."; + + protected static final List forbiddenNames = Collections.unmodifiableList(Lists.newArrayList("I")); + + @Override + public void check (ASTMCGrammar node){ + String grammarName = node.getName(); + if(forbiddenNames.contains(grammarName)){ + Log.error(ERROR_CODE + String.format(ERROR_MSG_FORMAT, grammarName)); + } + } + + + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoForbiddenProdAndSymbolName.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoForbiddenProdAndSymbolName.java new file mode 100644 index 0000000000..61a50473db --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoForbiddenProdAndSymbolName.java @@ -0,0 +1,49 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.se_rwth.commons.logging.Log; + +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +public class NoForbiddenProdAndSymbolName implements GrammarASTMCGrammarCoCo { + + public static final String ERROR_CODE = "0xA4122"; + + public static final String ERROR_MSG_FORMAT = " There must not exist a production with the name %s in the grammar %s if " + + "there already exists a symbol with the name %s."; + + public static final String SYMBOL = "Symbol"; + + @Override + public void check(ASTMCGrammar node) { + String grammarName = node.getName(); + MCGrammarSymbol symbol = node.getSymbol(); + Collection prods = symbol.getProdsWithInherited().values(); + List symbolProds = prods.stream().filter(ProdSymbol::isIsSymbolDefinition).collect(Collectors.toList()); + for(ProdSymbol prod:prods){ + String prodName = prod.getName(); + if(prodName.endsWith(SYMBOL)){ + handle(grammarName, SYMBOL, prodName, symbolProds); + } + } + } + + protected void handle(String grammarName, String addon, String prodName, Collection prods){ + String prodNameWithoutAddon = prodName.substring(0, prodName.lastIndexOf(addon)); + List forbidden = prods.stream() + .filter(p -> p.getName().equals(prodNameWithoutAddon)) + .collect(Collectors.toList()); + + if(!forbidden.isEmpty()){ + for(ProdSymbol prod: forbidden){ + Log.error(ERROR_CODE + String.format(ERROR_MSG_FORMAT, prodName, grammarName, prod.getName())); + } + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoForbiddenProdName.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoForbiddenProdName.java new file mode 100644 index 0000000000..73747e65ee --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoForbiddenProdName.java @@ -0,0 +1,46 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import com.google.common.collect.Lists; +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.se_rwth.commons.logging.Log; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +public class NoForbiddenProdName implements GrammarASTMCGrammarCoCo { + + public static final String ERROR_CODE = "0xA4096"; + + public static final String ERROR_MSG_FORMAT = " There must not exist a production with the name %s in the grammar %s."; + + protected static final List forbiddenNames = Collections.unmodifiableList(Lists.newArrayList("EnclosingScope", "SpannedScope", "Node", "CNode", + "Class", "Traverser", "ScopesGenitor", "ScopesGenitorDelegator", "Scope", "ArtifactScope", "GlobalScope", + // Antlr + "Mode", "Parser", "Lexer", "Options", "Returns")); + + protected static final String NODE = "Node"; + + protected static final String CONSTANTS = "Constants"; + + @Override + public void check(ASTMCGrammar node) { + String grammarName = node.getName(); + MCGrammarSymbol symbol = node.getSymbol(); + Collection prods = symbol.getProdsWithInherited().values(); + for(ProdSymbol prod: prods){ + String prodName = prod.getName(); + if(forbiddenNames.contains(prodName)){ + Log.error(ERROR_CODE + String.format(ERROR_MSG_FORMAT, prodName, grammarName)); + } + if((grammarName+NODE).equals(prodName) || (CONSTANTS+grammarName).equals(prodName)){ + Log.error(ERROR_CODE + String.format(ERROR_MSG_FORMAT, prodName, grammarName)); + } + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoForbiddenProdNameAddon.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoForbiddenProdNameAddon.java new file mode 100644 index 0000000000..414f39c276 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoForbiddenProdNameAddon.java @@ -0,0 +1,47 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.se_rwth.commons.logging.Log; + +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +public class NoForbiddenProdNameAddon implements GrammarASTMCGrammarCoCo { + + public static final String ERROR_CODE = "0xA4120"; + + public static final String ERROR_MSG_FORMAT = " There must not exist a production with the name %s in the grammar %s if there is already a production with the name %s."; + + protected static final String BUILDER = "Builder"; + + @Override + public void check(ASTMCGrammar node) { + String grammarName = node.getName(); + MCGrammarSymbol symbol = node.getSymbol(); + Collection prods = symbol.getProdsWithInherited().values(); + for(ProdSymbol prod:prods){ + String prodName = prod.getName(); + if(prodName.endsWith(BUILDER)){ + handle(grammarName, BUILDER, prodName, prods); + } + } + } + + protected void handle(String grammarName, String addon, String prodName, Collection prods){ + String prodNameWithoutAddon = prodName.substring(0, prodName.lastIndexOf(addon)); + List forbidden = prods.stream() + .filter(p -> p.getName().equals(prodNameWithoutAddon)) + .collect(Collectors.toList()); + + if(!forbidden.isEmpty()){ + for(ProdSymbol prod: forbidden){ + Log.error(ERROR_CODE + String.format(ERROR_MSG_FORMAT, prodName, grammarName, prod.getName())); + } + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoForbiddenSymbolName.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoForbiddenSymbolName.java new file mode 100644 index 0000000000..d8dd1790c7 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoForbiddenSymbolName.java @@ -0,0 +1,42 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.se_rwth.commons.logging.Log; + +import java.util.List; +import java.util.stream.Collectors; + +public class NoForbiddenSymbolName implements GrammarASTMCGrammarCoCo { + + public static final String ERROR_CODE = "0xA4099"; + + public static final String ERROR_MSG_FORMAT = " There must not exist a symbol production with the name %s in the grammar %s."; + + protected static final String SYMBOL = "Symbol"; + + @Override + public void check(ASTMCGrammar node) { + String grammarName = node.getName(); + MCGrammarSymbol symbol = node.getSymbol(); + List symbolProds = symbol.getProdsWithInherited().values() + .stream() + .filter(ProdSymbol::isIsSymbolDefinition) + .collect(Collectors.toList()); + if(grammarName.endsWith(SYMBOL)){ + String nameWithoutSymbol = grammarName.substring(0,grammarName.lastIndexOf(SYMBOL)); + List forbidden = symbolProds.stream() + .filter(p -> p.getName().equals(nameWithoutSymbol)) + .collect(Collectors.toList()); + if(!forbidden.isEmpty()){ + for(ProdSymbol prod: forbidden){ + Log.error(ERROR_CODE + String.format(ERROR_MSG_FORMAT, prod.getName(), grammarName)); + } + } + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoForbiddenSymbolNameAddon.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoForbiddenSymbolNameAddon.java new file mode 100644 index 0000000000..44bee40a69 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoForbiddenSymbolNameAddon.java @@ -0,0 +1,66 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.se_rwth.commons.logging.Log; + +import java.util.List; +import java.util.stream.Collectors; + +public class NoForbiddenSymbolNameAddon implements GrammarASTMCGrammarCoCo { + + public static final String ERROR_CODE = "0xA4121"; + + public static final String ERROR_MSG_FORMAT = " There must not exist a symbol production with the name %s in the grammar %s if there is already a symbol production %s."; + + protected static final String MANY = "Many"; + + protected static final String DOWN = "Down"; + + protected static final String LOCALLY = "Locally"; + + protected static final String ADAPTED = "Adapted"; + + @Override + public void check(ASTMCGrammar node) { + String grammarName = node.getName(); + MCGrammarSymbol symbol = node.getSymbol(); + List symbolProds = symbol.getProdsWithInherited().values() + .stream() + .filter(ProdSymbol::isIsSymbolDefinition) + .collect(Collectors.toList()); + for(ProdSymbol prod: symbolProds){ + String prodName = prod.getName(); + if(prodName.endsWith(MANY)){ + handle(grammarName, MANY, prodName, symbolProds); + }else if(prodName.endsWith(DOWN)) { + handle(grammarName, DOWN, prodName, symbolProds); + }else if(prodName.endsWith(LOCALLY)){ + handle(grammarName, LOCALLY, prodName, symbolProds); + }else if(prodName.startsWith(ADAPTED)){ + handle(grammarName, ADAPTED, prodName, symbolProds); + } + } + } + + protected void handle(String grammarName, String addon, String prodName, List potentialSymbolProds){ + String prodNameWithoutAddon; + if(addon.equals(ADAPTED)){ + prodNameWithoutAddon = prodName.substring(addon.length()); + }else{ + prodNameWithoutAddon = prodName.substring(0, prodName.lastIndexOf(addon)); + } + List forbidden = potentialSymbolProds.stream() + .filter(p -> p.getName().equals(prodNameWithoutAddon)) + .collect(Collectors.toList()); + + if(!forbidden.isEmpty()){ + for(ProdSymbol prod: forbidden){ + Log.error(ERROR_CODE + String.format(ERROR_MSG_FORMAT, prodName, grammarName, prod.getName())); + } + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoMultipleSymbolRule.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoMultipleSymbolRule.java new file mode 100644 index 0000000000..796d38329a --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoMultipleSymbolRule.java @@ -0,0 +1,33 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._ast.ASTSymbolRule; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.se_rwth.commons.logging.Log; + +/** + * Checks only at most 1 symbolrule exists per nonterminal + */ +public class NoMultipleSymbolRule implements GrammarASTMCGrammarCoCo { + + public static final String ERROR_CODE = "0xA4151"; + + public static final String ERROR_MSG_FORMAT = " A symbolRule must not exist twice for a single nonterminal. Violation by %s"; + + @Override + public void check(ASTMCGrammar g) { + for (ASTSymbolRule rule : g.getSymbolRuleList()) { + int count = 0; + for (ASTSymbolRule r2 : g.getSymbolRuleList()) { + if (rule.getType().equals(r2.getType())) { + count++; + } + } + if (count != 1) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, rule.getType()), rule.get_SourcePositionStart()); + } + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoNTInheritanceCycle.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoNTInheritanceCycle.java new file mode 100644 index 0000000000..65a3ce8cf2 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoNTInheritanceCycle.java @@ -0,0 +1,34 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.MCGrammarSymbolTableHelper; +import de.monticore.grammar.grammar._ast.ASTProd; +import de.monticore.grammar.grammar._cocos.GrammarASTProdCoCo; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that nonterminals do not have inheritance cycles. + * + + */ +public class NoNTInheritanceCycle implements GrammarASTProdCoCo { + + public static final String ERROR_CODE = "0xA4022"; + + public static final String ERROR_MSG_FORMAT = " The production %s introduces an inheritance" + + " cycle. Inheritance may not be cyclic."; + + @Override + public void check(ASTProd a) { + ProdSymbol symbol = a.getSymbol(); + for (ProdSymbol sr : MCGrammarSymbolTableHelper.getAllSuperProds(symbol)) { + if (sr.getFullName().equals(symbol.getFullName())) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, symbol.getFullName()), + a.get_SourcePositionStart()); + } + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoNestedGenericsInAdditionalAttributes.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoNestedGenericsInAdditionalAttributes.java new file mode 100644 index 0000000000..40d61ab18f --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoNestedGenericsInAdditionalAttributes.java @@ -0,0 +1,126 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.*; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.mccollectiontypes._ast.ASTMCGenericType; +import de.monticore.types.mcsimplegenerictypes.MCSimpleGenericTypesMill; +import de.se_rwth.commons.logging.Log; + +import java.util.List; + +import static de.monticore.grammar.grammar._ast.ASTConstantsGrammar.*; + +/** + * CoCo that checks if in a additional attribute of a astrule, symbolrule or scoperule a generic type does not contain a MCCustomTypeArgument + * with the *,+,? and min, max notation this can be created in different ways + * these cases are covered e.g.: + * A> + * A*, A+, A? + * A min = 0, A max = 5, A max = * + * because these cases cannot be handled in the generator so far and will generate the wrong type + */ +public class NoNestedGenericsInAdditionalAttributes implements GrammarASTMCGrammarCoCo { + + public static final String ERROR_CODE = "0xA4102"; + + public static final String ERROR_MSG_FORMAT = " %srule does not allow the definition of nested generics. " + + "Problem in grammar '%s', rule for '%s', with additional attribute: '%s'."; + + @Override + public void check(ASTMCGrammar node) { + String grammarName = node.getName(); + // astrule + for (ASTASTRule astastRule : node.getASTRuleList()) { + findMultipleGenericAttributes(astastRule.getAdditionalAttributeList(), "Ast", grammarName, astastRule.getType()); + } + // symbolrule + for (ASTSymbolRule astSymbolRule : node.getSymbolRuleList()) { + findMultipleGenericAttributes(astSymbolRule.getAdditionalAttributeList(), "Symbol", grammarName, astSymbolRule.getType()); + } + // scoperule + if (node.isPresentScopeRule()) { + findMultipleGenericAttributes(node.getScopeRule().getAdditionalAttributeList(), "Scope", grammarName, grammarName + "Scope"); + } + } + + protected void findMultipleGenericAttributes(List astAdditionalAttributes, String ruleName, + String grammarName, String prodName) { + for (ASTAdditionalAttribute astAdditionalAttribute : astAdditionalAttributes) { + ASTMCType mcType = astAdditionalAttribute.getMCType(); + + if (mcType instanceof ASTMCGenericType) { + if (hasNestedGeneric(mcType)) { + // for e.g. A> + logError(ruleName, grammarName, prodName, astAdditionalAttribute); + } else if (astAdditionalAttribute.isPresentCard()) { + if (hasGenericIteration(astAdditionalAttribute)) { + // for e.g. A* + logError(ruleName, grammarName, prodName, astAdditionalAttribute); + } else if (hasGenericMaxValue(astAdditionalAttribute)) { + // for e.g. A min=0 or A max=2 or A max=* + logError(ruleName, grammarName, prodName, astAdditionalAttribute); + } + } + } + } + } + + /** + * for e.g. A> + */ + protected boolean hasNestedGeneric(ASTMCType mcType){ + return((ASTMCGenericType) mcType).getMCTypeArgumentList() + .stream() + .filter(ta -> ta.getMCTypeOpt().isPresent()) + .anyMatch(ta -> ta.getMCTypeOpt().get() instanceof ASTMCGenericType); + } + + /** + * for e.g. A*, A+, A? + */ + protected boolean hasGenericIteration(ASTAdditionalAttribute astAdditionalAttribute){ + return astAdditionalAttribute.getCard().getIteration() == STAR || astAdditionalAttribute.getCard().getIteration() == PLUS || + astAdditionalAttribute.getCard().getIteration() == QUESTION; + } + + /** + * for e.g. A min=0, A max=2, A max=* + */ + protected boolean hasGenericMaxValue(ASTAdditionalAttribute astAdditionalAttribute){ + return (astAdditionalAttribute.getCard().isPresentMax() && ("*".equals(astAdditionalAttribute.getCard().getMax()) || + Integer.parseInt(astAdditionalAttribute.getCard().getMax()) > 1) || + (astAdditionalAttribute.getCard().isPresentMin() && Integer.parseInt(astAdditionalAttribute.getCard().getMin()) == 0)); + } + + protected void logError(String ruleName, String grammarName, String prodName, ASTAdditionalAttribute astAdditionalAttribute) { + Log.error(ERROR_CODE + String.format(ERROR_MSG_FORMAT, ruleName, grammarName, prodName, + printASTAdditionalAttribute(astAdditionalAttribute))); + } + + protected String printASTAdditionalAttribute(ASTAdditionalAttribute astAdditionalAttribute) { + String attribute = ""; + if (astAdditionalAttribute.isPresentName()) { + attribute += astAdditionalAttribute.getName() + ":"; + } + attribute += MCSimpleGenericTypesMill.prettyPrint(astAdditionalAttribute.getMCType(), false).trim(); + if (astAdditionalAttribute.isPresentCard()) { + ASTCard card = astAdditionalAttribute.getCard(); + if (card.getIteration() == STAR) { + attribute += "*"; + } else if (card.getIteration() == PLUS) { + attribute += "+"; + } else if (card.getIteration() == QUESTION) { + attribute += "?"; + } + if (card.isPresentMin()) { + attribute += " min=" + card.getMin(); + } + if (card.isPresentMax()) { + attribute += " max=" + card.getMax(); + } + } + return attribute; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoOverridingNTHasAnnotation.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoOverridingNTHasAnnotation.java new file mode 100644 index 0000000000..f1c5743d1c --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoOverridingNTHasAnnotation.java @@ -0,0 +1,36 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.MCGrammarSymbolTableHelper; +import de.monticore.grammar.grammar._ast.ASTClassProd; +import de.monticore.grammar.grammar._cocos.GrammarASTClassProdCoCo; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.se_rwth.commons.logging.Log; + +import java.util.Optional; + +/** + * Checks if nonterminals with an override annotation really overrides a class + * + */ +public class NoOverridingNTHasAnnotation implements GrammarASTClassProdCoCo { + + public static final String ERROR_CODE = "0xA4094"; + + public static final String ERROR_MSG_FORMAT = " The production %s does not override any production."; + + @Override + public void check(ASTClassProd a) { + if (a.getGrammarAnnotationList().stream().anyMatch(s -> s.isOverride())) { + Optional grammarSymbol = MCGrammarSymbolTableHelper + .getMCGrammarSymbol(a.getEnclosingScope()); + + if (!grammarSymbol.get().getInheritedProd(a.getName()).isPresent()) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, a.getName()), + a.get_SourcePositionStart()); + } + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoReplaceKeywordRuleOnConstantGroup.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoReplaceKeywordRuleOnConstantGroup.java new file mode 100644 index 0000000000..7a97e175f2 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoReplaceKeywordRuleOnConstantGroup.java @@ -0,0 +1,50 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTConstant; +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._ast.ASTTerminal; +import de.monticore.grammar.grammar._cocos.GrammarASTConstantCoCo; +import de.monticore.grammar.grammar._cocos.GrammarASTTerminalCoCo; +import de.monticore.grammar.grammar._visitor.GrammarVisitor2; +import de.se_rwth.commons.logging.Log; + +import java.util.ArrayList; +import java.util.List; + +public class NoReplaceKeywordRuleOnConstantGroup implements GrammarASTConstantCoCo, GrammarVisitor2 { + + /** + * Coco that checks whether a constant-rule constant + * is replaced wit a replace keword rule. + * Due to the generated Antlr parser files not respecting these replaced keywords, + * this CoCo ensures that replacekeyword does not target constants. + */ + + public static final String ERROR_CODE = "0xA4162"; + + public static final String ERROR_MSG_FORMAT = " There is a replacekeyword rule targeting a constant-group constant: '%s'. "; + + + protected List replacedKeywords = new ArrayList<>(); + + + @Override + public void visit(ASTMCGrammar node) { + // Cache the replaced keywords for this grammar + this.replacedKeywords.addAll(node.getSymbol().getReplacedKeywordsWithInherited().keySet()); + } + + @Override + public void endVisit(ASTMCGrammar node) { + this.replacedKeywords.clear(); + } + + @Override + public void check(ASTConstant node) { + if (this.replacedKeywords.contains(node.getName())) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, node.getName()), + node.get_SourcePositionStart()); + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoReplaceKeywordRuleOnUsageNamedAttribute.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoReplaceKeywordRuleOnUsageNamedAttribute.java new file mode 100644 index 0000000000..452826644e --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoReplaceKeywordRuleOnUsageNamedAttribute.java @@ -0,0 +1,51 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._ast.ASTTerminal; +import de.monticore.grammar.grammar._cocos.GrammarASTTerminalCoCo; +import de.monticore.grammar.grammar._visitor.GrammarVisitor2; +import de.se_rwth.commons.logging.Log; + +import java.util.ArrayList; +import java.util.List; + +public class NoReplaceKeywordRuleOnUsageNamedAttribute implements GrammarASTTerminalCoCo, GrammarVisitor2 { + + /** + * Coco that checks whether an attributed terminal (terminal with a usage name) + * is replaced wit a replace keword rule. + * Due to the generated Antlr actions not setting the AST attribute to the replace value, + * and in cases with multiple replaced keyword values more distinction would be required, + * this CoCo ensures that replacekeyword does not target such an terminal. + */ + + public static final String ERROR_CODE = "0xA4161"; + + public static final String ERROR_MSG_FORMAT = " There is a replacekeyword rule targeting a terminal with present usage-name: '%s'. "; + + + protected List replacedKeywords = new ArrayList<>(); + + + @Override + public void visit(ASTMCGrammar node) { + // Cache the replaced keywords for this grammar + this.replacedKeywords.addAll(node.getSymbol().getReplacedKeywordsWithInherited().keySet()); + } + + @Override + public void endVisit(ASTMCGrammar node) { + this.replacedKeywords.clear(); + } + + @Override + public void check(ASTTerminal node) { + if (node.isPresentUsageName()) { + if (this.replacedKeywords.contains(node.getName())) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, node.getName()), + node.get_SourcePositionStart()); + } + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoTokenDefined.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoTokenDefined.java new file mode 100644 index 0000000000..371fb650e6 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoTokenDefined.java @@ -0,0 +1,52 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar.GrammarMill; +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar._visitor.GrammarTraverser; +import de.se_rwth.commons.logging.Log; + +import java.util.List; +import java.util.stream.Collectors; + +public class NoTokenDefined implements GrammarASTMCGrammarCoCo { + + /** + * Coco that checks whether a token is defined by the grammar or any super grammar. + * If not, there will be no lexer generated, so the parser will not compile + * This coco ensures that this will not happen + */ + + public static final String ERROR_CODE = "0xA4101"; + + public static final String ERROR_MSG_FORMAT = " There is no production defining a token in Grammar : '%s'. "; + + @Override + public void check(ASTMCGrammar node) { + if (node.isPresentSymbol() && !node.getSymbol().isIsComponent()) { + MCGrammarSymbol symbol = node.getSymbol(); + List superGrammars = symbol.getAllSuperGrammars().stream() + .filter(x -> x.isPresentAstNode()) + .map(x -> (ASTMCGrammar) x.getAstNode()) + .collect(Collectors.toList()); + //check for own and super grammars tokens + if (!hasTokenDefinition(node) && superGrammars.stream().noneMatch(this::hasTokenDefinition)) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, node.getName()), + node.get_SourcePositionStart()); + } + } + } + + protected boolean hasTokenDefinition(ASTMCGrammar node) { + //if there is a body check if the body contains tokens + GrammarTraverser traverser = GrammarMill.traverser(); + NoTokenDefinedVisitor visitor = new NoTokenDefinedVisitor(); + traverser.add4Grammar(visitor); + traverser.setGrammarHandler(visitor); + node.accept(traverser); + return visitor.foundTerminal(); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoTokenDefinedVisitor.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoTokenDefinedVisitor.java new file mode 100644 index 0000000000..73f8ea614b --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoTokenDefinedVisitor.java @@ -0,0 +1,56 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.*; +import de.monticore.grammar.grammar._visitor.GrammarHandler; +import de.monticore.grammar.grammar._visitor.GrammarTraverser; +import de.monticore.grammar.grammar._visitor.GrammarVisitor2; + +public class NoTokenDefinedVisitor implements GrammarVisitor2, GrammarHandler { + + protected boolean terminalFound = false; + + GrammarTraverser traverser; + + @Override + public GrammarTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(GrammarTraverser traverser) { + this.traverser = traverser; + } + + @Override + public void visit(ASTTerminal node) { + terminalFound = true; + } + + @Override + public void visit(ASTConstant node) { + terminalFound = true; + } + + @Override + public void visit(ASTLexProd node) { + terminalFound = true; + } + + @Override + public void handle(ASTAbstractProd node){ + //do not navigate here + //because tokes in abstract prods still lead to the compile error + //due to the problem, that nor lexer is generated + } + + @Override + public void handle(ASTNonTerminalSeparator node) { + terminalFound = true; + } + + public boolean foundTerminal() { + return terminalFound; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoTokenModeInComponentGrammar.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoTokenModeInComponentGrammar.java new file mode 100644 index 0000000000..f1039dc16e --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/NoTokenModeInComponentGrammar.java @@ -0,0 +1,25 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTLexProd; +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.se_rwth.commons.logging.Log; + +public class NoTokenModeInComponentGrammar implements GrammarASTMCGrammarCoCo { + public static final String ERROR_CODE = "0xA4068"; + + public static final String ERROR_MSG_FORMAT = " The lexical production %s must not define a mode in a Component Grammar."; + @Override + public void check(ASTMCGrammar node) { + if( node.isComponent()){ + for(ASTLexProd prod : node.getLexProdList()){ + if (prod.isPresentMode()){ + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, prod.getName()), + node.get_SourcePositionStart()); + } + } + + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/OverridingAbstractNTs.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/OverridingAbstractNTs.java new file mode 100644 index 0000000000..56d138fc1c --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/OverridingAbstractNTs.java @@ -0,0 +1,58 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import java.util.List; +import java.util.Optional; + +import de.monticore.grammar.grammar._ast.ASTEnumProd; +import de.monticore.grammar.grammar._ast.ASTExternalProd; +import de.monticore.grammar.grammar._ast.ASTInterfaceProd; +import de.monticore.grammar.grammar._ast.ASTLexProd; +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that nonterminals only extends abstract or normal nonterminals. + * + + */ +public class OverridingAbstractNTs implements GrammarASTMCGrammarCoCo { + + public static final String ERROR_CODE = "0xA4008"; + + public static final String ERROR_MSG_FORMAT = " The production for the abstract nonterminal %s must not be overridden\n" + + + "by a production for an %s nonterminal."; + + @Override + public void check(ASTMCGrammar a) { + MCGrammarSymbol grammarSymbol = a.getSymbol(); + List grammarSymbols = grammarSymbol.getSuperGrammarSymbols(); + + for (MCGrammarSymbol s : grammarSymbols) { + for (ASTEnumProd p : a.getEnumProdList()) { + doCheck(s.getProd(p.getName()), "enum"); + } + for (ASTExternalProd p : a.getExternalProdList()) { + doCheck(s.getProd(p.getName()), "external"); + } + for (ASTInterfaceProd p : a.getInterfaceProdList()) { + doCheck(s.getProd(p.getName()), "interface"); + } + for (ASTLexProd p : a.getLexProdList()) { + doCheck(s.getProd(p.getName()), "lexical"); + } + } + } + + protected void doCheck(Optional typeSymbol, String type) { + if (typeSymbol.isPresent() && typeSymbol.get().isIsAbstract()) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, typeSymbol.get().getName(), type)); + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/OverridingAbstractNTsHaveNoSuperRules.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/OverridingAbstractNTsHaveNoSuperRules.java new file mode 100644 index 0000000000..78d430a286 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/OverridingAbstractNTsHaveNoSuperRules.java @@ -0,0 +1,52 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import java.util.List; +import java.util.Optional; + +import de.monticore.grammar.MCGrammarSymbolTableHelper; +import de.monticore.grammar.grammar._ast.ASTAbstractProd; +import de.monticore.grammar.grammar._cocos.GrammarASTAbstractProdCoCo; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.types.mcsimplegenerictypes.MCSimpleGenericTypesMill; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that overriding abstract nonterminals do not have super rules or classes. + * + */ +public class OverridingAbstractNTsHaveNoSuperRules implements GrammarASTAbstractProdCoCo { + + public static final String ERROR_CODE = "0xA4002"; + + public static final String ERROR_MSG_FORMAT = " The abstract production %s overriding a production of " + + "a sub grammar must not extend the production %s.\n" + + "Hint: Overriding productions can only implement interfaces."; + + @Override + public void check(ASTAbstractProd a) { + Optional grammarSymbol = MCGrammarSymbolTableHelper + .getMCGrammarSymbol(a.getEnclosingScope()); + List grammarSymbols = grammarSymbol.get().getSuperGrammarSymbols(); + + if (!a.getSuperRuleList().isEmpty() || !a.getASTSuperClassList().isEmpty()) { + String extendedType; + if (!a.getSuperRuleList().isEmpty()){ + extendedType = a.getSuperRuleList().get(0).getName(); + } + else{ + extendedType = MCSimpleGenericTypesMill + .prettyPrint(a.getASTSuperClassList().get(0), false).trim(); + } + + for (MCGrammarSymbol s : grammarSymbols) { + if (s.getProd(a.getName()).isPresent()) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, a.getName(), extendedType), + a.get_SourcePositionStart()); + } + } + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/OverridingAdditionalAttributes.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/OverridingAdditionalAttributes.java new file mode 100644 index 0000000000..585837effa --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/OverridingAdditionalAttributes.java @@ -0,0 +1,39 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import com.google.common.collect.Lists; +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.monticore.grammar.grammar._symboltable.AdditionalAttributeSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.se_rwth.commons.logging.Log; + +import java.util.List; + +/** + * Checks, if additional attributes are declared twice + */ +public class OverridingAdditionalAttributes implements GrammarASTMCGrammarCoCo { + + public static final String ERROR_CODE = "0xA4035"; + + public static final String ERROR_MSG_FORMAT = " The additional attribute %s is defined twice for the " + + "rule %s"; + + @Override + public void check(ASTMCGrammar a) { + for (ProdSymbol prodSymbol: a.getSymbol().getSpannedScope().getLocalProdSymbols()) { + List attributes = prodSymbol.getSpannedScope().getLocalAdditionalAttributeSymbols(); + List superAttributes = Lists.newArrayList(attributes); + prodSymbol.getSuperProds().forEach(p -> superAttributes.addAll(p.lazyLoadDelegate().getSpannedScope().getLocalAdditionalAttributeSymbols())); + for (AdditionalAttributeSymbol ad: attributes) { + if (superAttributes.stream().filter(m -> ad.getName().equals(m.getName()) + && ad.isIsAstAttr()==m.isIsAstAttr()).count()>1) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, ad.getName(), prodSymbol.getName())); + } + } + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/OverridingEnumNTs.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/OverridingEnumNTs.java new file mode 100644 index 0000000000..9ba722dfb3 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/OverridingEnumNTs.java @@ -0,0 +1,46 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._ast.ASTProd; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that nonterminals only extends abstract or normal nonterminals. + * + */ +public class OverridingEnumNTs implements GrammarASTMCGrammarCoCo { + + public static final String ERROR_CODE = "0xA4027"; + + public static final String ERROR_MSG_FORMAT = " The production for the enum nonterminal %s must not be overridden."; + + @Override + public void check(ASTMCGrammar a) { + List prods = new ArrayList<>(a.getClassProdList()); + prods.addAll(a.getExternalProdList()); + prods.addAll(a.getLexProdList()); + prods.addAll(a.getInterfaceProdList()); + prods.addAll(a.getEnumProdList()); + prods.addAll(a.getAbstractProdList()); + + MCGrammarSymbol grammarSymbol = a.getSymbol(); + + for (ASTProd p : prods) { + Optional typeSymbol = grammarSymbol.getInheritedProd(p.getName()); + if (typeSymbol.isPresent() && typeSymbol.get().isIsEnum()) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, p.getName())); + } + } + + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/OverridingInterfaceNTs.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/OverridingInterfaceNTs.java new file mode 100644 index 0000000000..c4edf4aa99 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/OverridingInterfaceNTs.java @@ -0,0 +1,49 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._ast.ASTProd; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that nonterminals only extends abstract or normal nonterminals. + * + + */ +public class OverridingInterfaceNTs implements GrammarASTMCGrammarCoCo { + + public static final String ERROR_CODE = "0xA4007"; + + public static final String ERROR_MSG_FORMAT = " The production for the interface nonterminal %s must not be overridden."; + + @Override + public void check(ASTMCGrammar a) { + List prods = new ArrayList<>(a.getClassProdList()); + prods.addAll(a.getExternalProdList()); + prods.addAll(a.getLexProdList()); + prods.addAll(a.getInterfaceProdList()); + prods.addAll(a.getEnumProdList()); + prods.addAll(a.getAbstractProdList()); + MCGrammarSymbol grammarSymbol = a.getSymbol(); + List grammarSymbols = grammarSymbol.getSuperGrammarSymbols(); + + for (ASTProd p : prods) { + for (MCGrammarSymbol s : grammarSymbols) { + Optional typeSymbol = s.getProd(p.getName()); + if (typeSymbol.isPresent() && typeSymbol.get().isIsInterface()) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, typeSymbol.get().getName())); + } + } + } + + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/OverridingLexNTs.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/OverridingLexNTs.java new file mode 100644 index 0000000000..e248fe8195 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/OverridingLexNTs.java @@ -0,0 +1,47 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import java.util.List; +import java.util.Optional; + +import de.monticore.grammar.grammar._ast.ASTLexProd; +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that nonterminals or only overridden by normal nonterminals. + * + + */ +public class OverridingLexNTs implements GrammarASTMCGrammarCoCo { + + public static final String ERROR_CODE = "0xA4026"; + + public static final String ERROR_MSG_FORMAT = " The lexical production %s must not use a different type to " + + "store the token than the overridden production."; + + @Override + public void check(ASTMCGrammar a) { + MCGrammarSymbol grammarSymbol = a.getSymbol(); + List grammarSymbols = grammarSymbol.getSuperGrammarSymbols(); + + for (MCGrammarSymbol s : grammarSymbols) { + for (ASTLexProd p : a.getLexProdList()) { + doCheck(s.getProdWithInherited(p.getName()), p); + } + } + } + + protected void doCheck(Optional prodSymbol, ASTLexProd lexProd) { + if (prodSymbol.isPresent() && prodSymbol.get().isIsLexerProd() + && !((ASTLexProd) prodSymbol.get().getAstNode()).getTypeList() + .equals(lexProd.getTypeList())) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, lexProd.getName())); + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/OverridingNTHasNoAnnotation.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/OverridingNTHasNoAnnotation.java new file mode 100644 index 0000000000..0e82df74bf --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/OverridingNTHasNoAnnotation.java @@ -0,0 +1,44 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTGrammarAnnotation; +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.se_rwth.commons.logging.Log; + +import java.util.List; + +/** + * Checks if nonterminals with an override annotation really overrides a class + */ +public class OverridingNTHasNoAnnotation implements GrammarASTMCGrammarCoCo { + + public static final String ERROR_CODE = "0xA4098"; + + public static final String ERROR_MSG_FORMAT = " Warning: The production %s overrides production %s without annotation."; + + @Override + public void check(ASTMCGrammar a) { + MCGrammarSymbol gSymbol = a.getSymbol(); + for (ProdSymbol p : gSymbol.getProds()) { + if (!hasOverrideAnno(p.getAstNode().getGrammarAnnotationList()) && gSymbol.getInheritedProd(p.getName()).isPresent()) { + String fullName = gSymbol.getInheritedProd(p.getName()).get().getFullName(); + Log.warn(String.format(ERROR_CODE + ERROR_MSG_FORMAT, p.getName(), fullName), + p.getAstNode().get_SourcePositionStart()); + } + } + } + + protected boolean hasOverrideAnno(List grammarAnnotationsList) { + for (ASTGrammarAnnotation anno : grammarAnnotationsList) { + if (anno.isOverride()) { + return true; + } + } + return false; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/OverridingNTs.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/OverridingNTs.java new file mode 100644 index 0000000000..cb35cc9087 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/OverridingNTs.java @@ -0,0 +1,61 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import java.util.List; +import java.util.Optional; + +import de.monticore.grammar.grammar._ast.ASTAbstractProd; +import de.monticore.grammar.grammar._ast.ASTEnumProd; +import de.monticore.grammar.grammar._ast.ASTExternalProd; +import de.monticore.grammar.grammar._ast.ASTInterfaceProd; +import de.monticore.grammar.grammar._ast.ASTLexProd; +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that nonterminals or only overridden by normal nonterminals. + * + + */ +public class OverridingNTs implements GrammarASTMCGrammarCoCo { + + public static final String ERROR_CODE = "0xA4009"; + + public static final String ERROR_MSG_FORMAT = " The production for the nonterminal %s must not be overridden " + + "by a production for an %s nonterminal."; + + @Override + public void check(ASTMCGrammar a) { + MCGrammarSymbol grammarSymbol = a.getSymbol(); + List grammarSymbols = grammarSymbol.getSuperGrammarSymbols(); + + for(MCGrammarSymbol s: grammarSymbols) { + for (ASTEnumProd p : a.getEnumProdList()) { + doCheck(s.getProd(p.getName()), "enum"); + } + for (ASTExternalProd p : a.getExternalProdList()) { + doCheck(s.getProd(p.getName()), "external"); + } + for (ASTInterfaceProd p : a.getInterfaceProdList()) { + doCheck(s.getProd(p.getName()), "interface"); + } + for (ASTLexProd p : a.getLexProdList()) { + doCheck(s.getProd(p.getName()), "lexical"); + } + for (ASTAbstractProd p : a.getAbstractProdList()) { + doCheck(s.getProd(p.getName()), "abstract"); + } + } + } + + protected void doCheck(Optional typeSymbol, String type) { + if (typeSymbol.isPresent() && typeSymbol.get().isClass() && !typeSymbol.get().isIsAbstract()) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, typeSymbol.get().getName(), type)); + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/OverridingNTsHaveNoSuperRules.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/OverridingNTsHaveNoSuperRules.java new file mode 100644 index 0000000000..75db3e1269 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/OverridingNTsHaveNoSuperRules.java @@ -0,0 +1,53 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.MCGrammarSymbolTableHelper; +import de.monticore.grammar.grammar._ast.ASTClassProd; +import de.monticore.grammar.grammar._cocos.GrammarASTClassProdCoCo; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.types.mcsimplegenerictypes.MCSimpleGenericTypesMill; +import de.se_rwth.commons.logging.Log; + +import java.util.List; +import java.util.Optional; + +/** + * Checks that overriding nonterminals do not have super rules or + * classes. + * + + */ +public class OverridingNTsHaveNoSuperRules implements GrammarASTClassProdCoCo { + + public static final String ERROR_CODE = "0xA4001"; + + public static final String ERROR_MSG_FORMAT = " The production %s overriding a production of " + + "a sub grammar must not extend the production %s.\n" + + "Hint: Overriding productions can only implement interfaces."; + + @Override + public void check(ASTClassProd a) { + Optional grammarSymbol = MCGrammarSymbolTableHelper + .getMCGrammarSymbol(a.getEnclosingScope()); + List grammarSymbols = grammarSymbol.get().getSuperGrammarSymbols(); + + if (!a.getSuperRuleList().isEmpty() || !a.getASTSuperClassList().isEmpty()) { + String extendedType; + if (!a.getSuperRuleList().isEmpty()){ + extendedType = a.getSuperRuleList().get(0).getName(); + } + else{ + extendedType = MCSimpleGenericTypesMill + .prettyPrint(a.getASTSuperClassList().get(0), false).trim(); + } + for (MCGrammarSymbol s : grammarSymbols) { + if (s.getProd(a.getName()).isPresent()) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, a.getName(), extendedType), + a.get_SourcePositionStart()); + } + } + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/PackageNameLowerCase.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/PackageNameLowerCase.java new file mode 100644 index 0000000000..73397e6a04 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/PackageNameLowerCase.java @@ -0,0 +1,33 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.se_rwth.commons.Joiners; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that the package name is lowercase + * + */ +public class PackageNameLowerCase implements GrammarASTMCGrammarCoCo { + + public static final String ERROR_CODE = "0xA4006"; + + public static final String ERROR_MSG_FORMAT = " The package name %s contains uppercase letters!"; + + @Override + public void check(ASTMCGrammar a) { + for (String p: a.getPackageList()) { + for (char c:p.toCharArray()) { + if (Character.isUpperCase(c)) { + Log.warn(String.format(ERROR_CODE + ERROR_MSG_FORMAT, Joiners.DOT.join(a.getPackageList())), + a.get_SourcePositionStart()); + return; + } + } + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ProdAndExtendedProdUseSameAttrNameForDiffNTs.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ProdAndExtendedProdUseSameAttrNameForDiffNTs.java new file mode 100644 index 0000000000..3e0a97bcee --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ProdAndExtendedProdUseSameAttrNameForDiffNTs.java @@ -0,0 +1,101 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import com.google.common.collect.Lists; +import de.monticore.grammar.MCGrammarSymbolTableHelper; +import de.monticore.grammar.grammar._ast.ASTNonTerminal; +import de.monticore.grammar.grammar._ast.ASTProd; +import de.monticore.grammar.grammar._cocos.GrammarASTNonTerminalCoCo; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbolSurrogate; +import de.monticore.grammar.grammar._symboltable.RuleComponentSymbol; +import de.se_rwth.commons.StringTransformations; +import de.se_rwth.commons.logging.Log; + +import java.util.List; +import java.util.Optional; + +/** + * Checks that an attribute name is not used twice for different nonterminals. + */ +public class ProdAndExtendedProdUseSameAttrNameForDiffNTs implements GrammarASTNonTerminalCoCo { + + public static final String ERROR_CODE = "0xA4024"; + + public static final String ERROR_MSG_FORMAT = " The production %s extending the production %s must not use the\n" + + + "name %s for the nonterminal %s as %s already uses this name for the %s."; + + @Override + public void check(ASTNonTerminal a) { + if (a.isPresentUsageName() && a.isPresentSymbol()) { + String attributename = a.getUsageName(); + RuleComponentSymbol componentSymbol = a.getSymbol(); + Optional optProd = MCGrammarSymbolTableHelper.getEnclosingRule(a); + if (optProd.isPresent() && optProd.get().isClass()) { + ProdSymbol prod = optProd.get(); + for (ProdSymbolSurrogate s : getAllSuperProds(prod)) { + ProdSymbol superprod = s.lazyLoadDelegate(); + List componentSymbolList = superprod.getSpannedScope().resolveRuleComponentDownMany(attributename); + if (!componentSymbolList.isEmpty()) { + for (RuleComponentSymbol symbol : componentSymbolList) { + if (symbol.isIsLexerNonterminal()) { + logError(prod.getAstNode(), superprod, attributename, componentSymbol, + "production that is a lexical nonTerminal", a); + } else if (symbol.isIsConstant()) { + logError(prod.getAstNode(), superprod, attributename, componentSymbol, + "production that is not a constant", a); + } else if (symbol.isIsConstantGroup()) { + logError(prod.getAstNode(), superprod, attributename, componentSymbol, + "production that is not a constant group", a); + } else if (symbol.isIsTerminal()) { + logError(prod.getAstNode(), superprod, attributename, componentSymbol, + "production that is a terminal named " + symbol.getName(), a); + } else if (symbol.isIsNonterminal() && symbol.getReferencedProd().isPresent() + && !symbol.getReferencedProd().get().getName().equals(componentSymbol.getReferencedProd().get().getName())) { + if (!(symbol.getReferencedProd().get().isIsLexerProd() && componentSymbol.getReferencedProd().get().isIsLexerProd())) { + logError(prod.getAstNode(), superprod, attributename, + componentSymbol, "nonterminal " + symbol.getReferencedProd().get().getName(), a); + } + } + } + } else { + //try to find NonTerminal with same Name, but with capitalised start -> will both become the same attribute + componentSymbolList = superprod.getSpannedScope().resolveRuleComponentDownMany(StringTransformations.capitalize(attributename)); + for (RuleComponentSymbol ruleComponentSymbol : componentSymbolList) { + if (ruleComponentSymbol.isIsNonterminal() && ruleComponentSymbol.getReferencedProd().isPresent() + && !ruleComponentSymbol.getReferencedProd().get().getName().equals(componentSymbol.getReferencedProd().get().getName())) { + // logs error when e.g. State = F; A extends State = f:R; + // because F form State will evaluate to attributeName with small f + logError(prod.getAstNode(), superprod, attributename, + componentSymbol, "nonterminal " + ruleComponentSymbol.getReferencedProd().get().getName(), a); + } + } + } + } + } + } + } + + protected List getAllSuperProds(ProdSymbol prod) { + List ret = Lists.newArrayList(prod.getSuperProds()); + for (ProdSymbolSurrogate surrogate: prod.getSuperProds()) { + ProdSymbol superProd = surrogate.lazyLoadDelegate(); + ret.addAll(getAllSuperProds(superProd)); + } + return ret; + } + + protected void logError(ASTProd prod, ProdSymbol ruleSymbol, String attributename, + RuleComponentSymbol componentSymbol, String actualType, ASTNonTerminal a) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, + prod.getName(), + ruleSymbol.getName(), + attributename, + componentSymbol.getReferencedProd().get().getName(), + ruleSymbol.getName(), + actualType, + a.get_SourcePositionStart())); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ProdAndOverriddenProdUseSameAttrNameForDiffNTs.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ProdAndOverriddenProdUseSameAttrNameForDiffNTs.java new file mode 100644 index 0000000000..0303db4abb --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ProdAndOverriddenProdUseSameAttrNameForDiffNTs.java @@ -0,0 +1,80 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.MCGrammarSymbolTableHelper; +import de.monticore.grammar.grammar._ast.ASTNonTerminal; +import de.monticore.grammar.grammar._cocos.GrammarASTNonTerminalCoCo; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.monticore.grammar.grammar._symboltable.RuleComponentSymbol; +import de.se_rwth.commons.StringTransformations; +import de.se_rwth.commons.logging.Log; + +import java.util.List; +import java.util.Optional; + +/** + * Checks that an attribute name is not used twice for different nonterminals. + */ +public class ProdAndOverriddenProdUseSameAttrNameForDiffNTs implements GrammarASTNonTerminalCoCo { + + public static final String ERROR_CODE = "0xA4025"; + + public static final String ERROR_MSG_FORMAT = " The overriding production %s must not use " + + "the name %s for the nonterminal %s as the overridden production uses this name for the %s"; + + @Override + public void check(ASTNonTerminal a) { + if (a.isPresentUsageName()) { + String attributename = a.getUsageName(); + RuleComponentSymbol componentSymbol = a.getSymbol(); + Optional rule = MCGrammarSymbolTableHelper.getEnclosingRule(a); + Optional grammarSymbol = MCGrammarSymbolTableHelper + .getMCGrammarSymbol(a.getEnclosingScope()); + for (MCGrammarSymbol g : grammarSymbol.get().getSuperGrammarSymbols()) { + Optional ruleSymbol = g.getProd(rule.get().getName()); + if (ruleSymbol.isPresent()) { + List rcs = ruleSymbol.get().getSpannedScope() + .resolveRuleComponentMany(attributename); + if (!rcs.isEmpty()) { + if (rcs.get(0).isIsTerminal()) { + logError(rule.get(), attributename, componentSymbol, "production of a terminal", a); + } else if (rcs.get(0).isIsConstantGroup()) { + logError(rule.get(), attributename, componentSymbol, "production of a constant group", a); + } else if (rcs.get(0).isIsConstant()) { + logError(rule.get(), attributename, componentSymbol, "production of a constant", a); + } else if (rcs.get(0).isIsLexerNonterminal()) { + logError(rule.get(), attributename, componentSymbol, "production of a lexer nonterminal", a); + } else if (rcs.get(0).isIsNonterminal() && rcs.get(0).getReferencedProd().isPresent() + && (rcs.get(0).getReferencedProd().get().isIsLexerProd() && componentSymbol.getReferencedProd().get().isIsLexerProd()) || !rcs.get(0).getReferencedProd().get().getName().equals(componentSymbol.getReferencedProd().get().getName())) { + if (!(rcs.get(0).getReferencedProd().get().isIsLexerProd() && componentSymbol.getReferencedProd().get().isIsLexerProd())) { + logError(rule.get(), attributename, componentSymbol, "nonterminal " + rcs.get(0).getReferencedProd().get().getName(), a); + } + } + } else { + //try to find NonTerminal with same Name, but with capitalised start -> will both become the same attribute + rcs = ruleSymbol.get().getSpannedScope().resolveRuleComponentDownMany(StringTransformations.capitalize(attributename)); + if (!rcs.isEmpty() && rcs.get(0).isIsNonterminal() && rcs.get(0).getReferencedProd().isPresent() + && !rcs.get(0).getReferencedProd().get().getName().equals(componentSymbol.getReferencedProd().get().getName())) { + // logs error when e.g. State = F; A extends State = f:R; + // because F form State will evaluate to attributeName with small f + logError(ruleSymbol.get(), attributename, + componentSymbol, "nonterminal " + rcs.get(0).getReferencedProd().get().getName(), a); + } + } + } + } + } + } + + protected void logError(ProdSymbol ruleSymbol, String attributename, + RuleComponentSymbol componentSymbol, String actualType, ASTNonTerminal a) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, + ruleSymbol.getName(), + attributename, + componentSymbol.getReferencedProd().get().getName(), + actualType, + a.get_SourcePositionStart())); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ProdExtendsNotExistingProd.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ProdExtendsNotExistingProd.java new file mode 100644 index 0000000000..57f0ea772d --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ProdExtendsNotExistingProd.java @@ -0,0 +1,34 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTProd; +import de.monticore.grammar.grammar._cocos.GrammarASTProdCoCo; +import de.monticore.grammar.grammar._symboltable.ProdSymbolSurrogate; +import de.se_rwth.commons.logging.Log; + +public class ProdExtendsNotExistingProd implements GrammarASTProdCoCo { + + public static final String ERROR_CODE = "0xA0113"; + + public static final String ERROR_MSG_FORMAT = " The production %s extends or implements the non-existent production %s"; + + @Override + public void check(ASTProd node) { + for(ProdSymbolSurrogate loader: node.getSymbol().getSuperProds()){ + if(!node.getEnclosingScope().resolveProd(loader.getName()).isPresent()){ + logError(node.getName(), loader.getName()); + } + } + + for(ProdSymbolSurrogate loader: node.getSymbol().getSuperInterfaceProds()){ + if(!node.getEnclosingScope().resolveProd(loader.getName()).isPresent()){ + logError(node.getName(), loader.getName()); + } + } + } + + public void logError(String name, String undefinedName){ + Log.error(String.format(ERROR_CODE+ERROR_MSG_FORMAT,name, undefinedName)); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ProdStartsWithCapital.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ProdStartsWithCapital.java new file mode 100644 index 0000000000..dd273f503a --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ProdStartsWithCapital.java @@ -0,0 +1,24 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTProd; +import de.monticore.grammar.grammar._cocos.GrammarASTProdCoCo; + +import static de.se_rwth.commons.logging.Log.error; +import static java.lang.Character.isLowerCase; +import static java.lang.String.format; + +public class ProdStartsWithCapital implements GrammarASTProdCoCo { + + public static final String ERROR_CODE = "0xA4031"; + + public static final String ERROR_MSG_FORMAT = " The nonterminal %s should not start with a lower-case letter."; + + @Override + public void check(ASTProd node) { + if (isLowerCase(node.getName().charAt(0))) { + error(format(ERROR_CODE + ERROR_MSG_FORMAT, node.getName()), node.get_SourcePositionStart()); + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ProdWithDoubleAnnos.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ProdWithDoubleAnnos.java new file mode 100644 index 0000000000..a9372ecc59 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ProdWithDoubleAnnos.java @@ -0,0 +1,34 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTGrammarAnnotation; +import de.monticore.grammar.grammar._ast.ASTProd; +import de.monticore.grammar.grammar._cocos.GrammarASTProdCoCo; + +import static de.se_rwth.commons.logging.Log.error; +import static java.lang.String.format; + +public class ProdWithDoubleAnnos implements GrammarASTProdCoCo { + + public static final String ERROR_CODE = "0xA4119"; + + public static final String ERROR_MSG_FORMAT = " The production %s should not use the annotation %s twice."; + + @Override + public void check(ASTProd node) { + boolean isOverriden = false; + boolean isDeprecated = false; + for (ASTGrammarAnnotation anno: node.getGrammarAnnotationList()) { + if (isDeprecated && anno.isDeprecated()) { + error(format(ERROR_CODE + ERROR_MSG_FORMAT, node.getName(), "@Deprecated"), node.get_SourcePositionStart()); + } + if (isOverriden && anno.isOverride()) { + error(format(ERROR_CODE + ERROR_MSG_FORMAT, node.getName(), "@Override"), node.get_SourcePositionStart()); + } + isDeprecated = anno.isDeprecated(); + isOverriden= anno.isOverride(); + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ProdWithExtensionMustNotBeOverridden.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ProdWithExtensionMustNotBeOverridden.java new file mode 100644 index 0000000000..5695a8ec23 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ProdWithExtensionMustNotBeOverridden.java @@ -0,0 +1,59 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTProd; +import de.monticore.grammar.grammar._cocos.GrammarASTProdCoCo; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbolSurrogate; + +import java.util.Map.Entry; +import java.util.Optional; + +import static de.monticore.grammar.MCGrammarSymbolTableHelper.getAllSuperGrammars; +import static de.monticore.grammar.MCGrammarSymbolTableHelper.getMCGrammarSymbol; +import static de.se_rwth.commons.logging.Log.error; +import static java.lang.String.format; + +public class ProdWithExtensionMustNotBeOverridden implements GrammarASTProdCoCo { + + public static final String ERROR_CODE = "0xA4010"; + + public static final String ERROR_MSG_FORMAT = " The production %s must not be overridden because there" + + " already exist productions extending it."; + + @Override + public void check(ASTProd a) { + + Optional grammarSymbol = getMCGrammarSymbol(a.getEnclosingScope()); + + boolean isOverriding = false; + for (MCGrammarSymbol sup : getAllSuperGrammars(grammarSymbol.get())) { + if (sup.getProd(a.getName()).isPresent()) { + isOverriding = true; + break; + } + } + if (!isOverriding) { + return; + } + + boolean extensionFound = false; + entryLoop: + for (Entry entry : grammarSymbol.get().getProdsWithInherited() + .entrySet()) { + ProdSymbol rs = entry.getValue(); + for (ProdSymbolSurrogate typeSymbol : rs.getSuperProds()) { + if (a.getName().equals(typeSymbol.getName())) { + extensionFound = true; + break entryLoop; + } + } + } + if (extensionFound) { + error(format(ERROR_CODE + ERROR_MSG_FORMAT, a.getName()), + a.get_SourcePositionStart()); + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ReferenceSymbolNotName.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ReferenceSymbolNotName.java new file mode 100644 index 0000000000..2eef5eb694 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ReferenceSymbolNotName.java @@ -0,0 +1,22 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTNonTerminal; +import de.monticore.grammar.grammar._cocos.GrammarASTNonTerminalCoCo; +import de.se_rwth.commons.logging.Log; + +public class ReferenceSymbolNotName implements GrammarASTNonTerminalCoCo { + + public static final String ERROR_CODE = "0xA4039"; + + public static final String ERROR_MSG_FORMAT = " You can only refer to other symbols on the nonterminal Name."; + + @Override + public void check(ASTNonTerminal node) { + if (node.isPresentReferencedSymbol() && !"Name".equals(node.getName())) { + Log.error(ERROR_CODE + ERROR_MSG_FORMAT, + node.get_SourcePositionStart()); + } + } +} + diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ReferenceSymbolSameAttribute.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ReferenceSymbolSameAttribute.java new file mode 100644 index 0000000000..7dbdcde249 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ReferenceSymbolSameAttribute.java @@ -0,0 +1,32 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar.GrammarMill; +import de.monticore.grammar.grammar._ast.*; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.monticore.grammar.grammar._visitor.GrammarTraverser; + +public class ReferenceSymbolSameAttribute implements GrammarASTMCGrammarCoCo { + + @Override + public void check(ASTMCGrammar node) { + for (ASTClassProd classProd : node.getClassProdList()) { + ReferenceSymbolSameAttributeVisitor visitor = new ReferenceSymbolSameAttributeVisitor(); + GrammarTraverser traverser = GrammarMill.traverser(); + traverser.add4Grammar(visitor); + classProd.accept(traverser); + } + for (ASTAbstractProd abstractProd : node.getAbstractProdList()) { + ReferenceSymbolSameAttributeVisitor visitor = new ReferenceSymbolSameAttributeVisitor(); + GrammarTraverser traverser = GrammarMill.traverser(); + traverser.add4Grammar(visitor); + abstractProd.accept(traverser); + } + for (ASTInterfaceProd interfaceProd : node.getInterfaceProdList()) { + ReferenceSymbolSameAttributeVisitor visitor = new ReferenceSymbolSameAttributeVisitor(); + GrammarTraverser traverser = GrammarMill.traverser(); + traverser.add4Grammar(visitor); + interfaceProd.accept(traverser); + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ReferenceSymbolSameAttributeVisitor.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ReferenceSymbolSameAttributeVisitor.java new file mode 100644 index 0000000000..a17934debb --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ReferenceSymbolSameAttributeVisitor.java @@ -0,0 +1,29 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._visitor.GrammarVisitor2; +import de.se_rwth.commons.logging.Log; + +import java.util.HashMap; +import java.util.Map; + +public class ReferenceSymbolSameAttributeVisitor implements GrammarVisitor2 { + + public static final String ERROR_CODE = "0xA4100"; + + public static final String ERROR_MSG_FORMAT = " The attributes with the UsageName %s, cannot reference to the different symbols %s and %s."; + protected Map map = new HashMap<>(); + + @Override + public void visit(de.monticore.grammar.grammar._ast.ASTNonTerminal node) { + if(node.isPresentUsageName() && node.isPresentReferencedSymbol()){ + String usageName = node.getUsageName(); + if(map.containsKey(usageName) && !map.get(usageName).equals(node.getReferencedSymbol())){ + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, "\""+usageName+"\"", + map.get(usageName), node.getReferencedSymbol(), node.get_SourcePositionStart())); + } + map.put(usageName, node.getReferencedSymbol()); + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ReferencedNTNotDefined.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ReferencedNTNotDefined.java new file mode 100644 index 0000000000..0868caa28e --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ReferencedNTNotDefined.java @@ -0,0 +1,78 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.*; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that nonterminals only extends abstract or normal nonterminals. + * + + */ +public class ReferencedNTNotDefined implements GrammarASTMCGrammarCoCo { + + public static final String ERROR_CODE = "0xA2030"; + + public static final String ERROR_MSG_FORMAT = " The production %s must not reference the " + + "%snonterminal %s because there exists no defining production for %s."; + + @Override + public void check(ASTMCGrammar a) { + MCGrammarSymbol grammarSymbol = a.getSymbol(); + for (ASTClassProd p : a.getClassProdList()) { + if (!p.getSuperRuleList().isEmpty() && p.isPresentSymbol()) { + for (ASTRuleReference sr : p.getSuperRuleList()) { + if (!grammarSymbol.getProdWithInherited(sr.getName()).isPresent()) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, p.getName(), "", sr.getName(), + sr.getName()), + p.get_SourcePositionStart()); + } + } + } + if (!p.getSuperInterfaceRuleList().isEmpty()) { + for (ASTRuleReference sr : p.getSuperInterfaceRuleList()) { + if (!grammarSymbol.getProdWithInherited(sr.getName()).isPresent()) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, p.getName(), "interface ", + sr.getName(), sr.getName()), + p.get_SourcePositionStart()); + } + } + } + } + for (ASTAbstractProd p : a.getAbstractProdList()) { + if (!p.getSuperRuleList().isEmpty() && p.isPresentSymbol()) { + for (ASTRuleReference sr : p.getSuperRuleList()) { + if (!grammarSymbol.getProdWithInherited(sr.getName()).isPresent()) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, p.getName(), "", sr.getName(), + sr.getName()), + p.get_SourcePositionStart()); + } + } + } + if (!p.getSuperInterfaceRuleList().isEmpty() && p.isPresentSymbol()) { + for (ASTRuleReference sr : p.getSuperInterfaceRuleList()) { + if (!grammarSymbol.getProdWithInherited(sr.getName()).isPresent()) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, p.getName(), "interface ", + sr.getName(), sr.getName()), + p.get_SourcePositionStart()); + } + } + } + } + for (ASTInterfaceProd p : a.getInterfaceProdList()) { + if (!p.getSuperInterfaceRuleList().isEmpty() && p.isPresentSymbol()) { + for (ASTRuleReference sr : p.getSuperInterfaceRuleList()) { + if (!grammarSymbol.getProdWithInherited(sr.getName()).isPresent()) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, p.getName(), "interface ", + sr.getName(), sr.getName()), + p.get_SourcePositionStart()); + } + } + } + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ReferencedSymbolExists.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ReferencedSymbolExists.java new file mode 100644 index 0000000000..1eab82bfdd --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ReferencedSymbolExists.java @@ -0,0 +1,33 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.MCGrammarSymbolTableHelper; +import de.monticore.grammar.grammar._ast.ASTNonTerminal; +import de.monticore.grammar.grammar._cocos.GrammarASTNonTerminalCoCo; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.se_rwth.commons.logging.Log; + +import java.util.Optional; + +public class ReferencedSymbolExists implements GrammarASTNonTerminalCoCo { + + public static final String ERROR_CODE = "0xA4037"; + + public static final String ERROR_MSG_FORMAT = " The production for the referenced symbol %s does not exist as a symbol or not at all."; + + @Override + public void check(ASTNonTerminal node) { + Optional grammarSymbol = MCGrammarSymbolTableHelper + .getMCGrammarSymbol(node.getEnclosingScope()); + if (node.isPresentReferencedSymbol()) { + String symbol = node.getReferencedSymbol(); + if (grammarSymbol.get().getProdWithInherited(symbol).isPresent() && + grammarSymbol.get().getProdWithInherited(symbol).get().isIsSymbolDefinition()) { + return; + } + Log.error(String.format(ERROR_CODE + String.format(ERROR_MSG_FORMAT, symbol), + node.get_SourcePositionStart())); + } + } +} + diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/RuleComponentsCompatible.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/RuleComponentsCompatible.java new file mode 100644 index 0000000000..85cb84b86a --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/RuleComponentsCompatible.java @@ -0,0 +1,79 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTProd; +import de.monticore.grammar.grammar._cocos.GrammarASTProdCoCo; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.monticore.grammar.grammar._symboltable.RuleComponentSymbol; +import de.se_rwth.commons.logging.Log; + +/** + * checks for each prod if there exist RuleComponents which have the same usageName but do not have compatible types + * not compatible if: + * -> one NonTerminal and one Terminal + * -> two different NonTerminal types + */ +public class RuleComponentsCompatible implements GrammarASTProdCoCo { + + public static final String ERROR_CODE = "0xA4090"; + + public static final String ERROR_MSG_FORMAT = " The prod: '%s' contains different rule components with the same name: " + + "'%s' with incompatible types."; + + @Override + public void check(ASTProd node) { + ProdSymbol symbol = node.getSymbol(); + outer: + for (int i = 0; i < symbol.getProdComponents().size(); i++) { + for (int j = i + 1; j < symbol.getProdComponents().size(); j++) { + if (!symbol.getProdComponents().get(i).getName().isEmpty() && symbol.getProdComponents().get(i).getName().equals(symbol.getProdComponents().get(j).getName())) { + boolean compatible = areTypesCompatible(symbol.getProdComponents().get(i), symbol.getProdComponents().get(j), node.getName()); + if (!compatible) { + break outer; + } + } + } + } + } + + protected boolean areTypesCompatible(RuleComponentSymbol firstSymbol, RuleComponentSymbol secondSymbol, + String prodName) { + if (firstSymbol.isIsTerminal()) { + if (secondSymbol.isIsTerminal() || (secondSymbol.isIsNonterminal() && secondSymbol.getReferencedProd().get().isIsLexerProd())) { + return true; + } + logError(prodName, firstSymbol.getName()); + return false; + } else if (firstSymbol.isIsConstantGroup()) { + if (!secondSymbol.isIsConstantGroup()) { + logError(prodName, firstSymbol.getName()); + } + return false; + } else if (firstSymbol.isIsConstant()) { + if (!secondSymbol.isIsConstant()) { + logError(prodName, firstSymbol.getName()); + } + return false; + } else if (secondSymbol.isIsTerminal()) { + if (firstSymbol.isIsNonterminal() && firstSymbol.getReferencedProd().get().isIsLexerProd()) { + return true; + } + logError(prodName, firstSymbol.getName()); + return false; + } else if (secondSymbol.isIsConstant() || secondSymbol.isIsConstantGroup()) { + logError(prodName, firstSymbol.getName()); + return false; + } else if (!firstSymbol.getReferencedType().equals(secondSymbol.getReferencedType())) { + // two different NonTerminals + if (!(firstSymbol.getReferencedProd().get().isIsLexerProd() && secondSymbol.getReferencedProd().get().isIsLexerProd())) { + logError(prodName, firstSymbol.getName()); + return false; + } + } + return true; + } + + protected void logError(String prodName, String ruleCompName) { + Log.error(ERROR_CODE + String.format(ERROR_MSG_FORMAT, prodName, ruleCompName)); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ScopeProdOverwrittenByScope.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ScopeProdOverwrittenByScope.java new file mode 100644 index 0000000000..f5c48c5e34 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/ScopeProdOverwrittenByScope.java @@ -0,0 +1,48 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.se_rwth.commons.logging.Log; + +import java.util.List; +import java.util.Optional; + +/** + * checks if a prod is overwriting a prod of a super grammar + * and logs an error if both prods define a scope + * e.g. grammar A { scope Foo; } grammar B extends A { scope Foo; } + * only one of the prods can should a scope + * the scope property is inherited from the super prod and does not need to be defined again + */ +public class ScopeProdOverwrittenByScope implements GrammarASTMCGrammarCoCo { + + public static final String ERROR_CODE = "0xA0275"; + + public static final String ERROR_MSG_FORMAT = "Production %s from grammar %s is a scope and overwritten by the prod %s of grammar %s that also defines a scope." + + "Remove the second scope definition, because the scope property is inherited anyway."; + + @Override + public void check(ASTMCGrammar node) { + MCGrammarSymbol grammarSymbol = node.getSymbol(); + // get all super grammars + List superGrammarSymbols = grammarSymbol.getSuperGrammarSymbols(); + for (MCGrammarSymbol superGrammar : superGrammarSymbols) { + // for every prod of the current grammar + for (ProdSymbol prod : grammarSymbol.getProds()) { + // enums cannot define symbols + if (!prod.isIsEnum()) { + // finds a prod with the same name in the super grammar if one is present + Optional superProd = superGrammar.getProd(prod.getName()); + if (superProd.isPresent() && superProd.get().isIsScopeSpanning() && prod.isIsScopeSpanning()) { + // log error if both prod define a scope themselves + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, superProd.get().getName(), superGrammar.getName(), + prod.getName(), grammarSymbol.getName())); + } + } + } + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/SplitRuleInvalid.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/SplitRuleInvalid.java new file mode 100644 index 0000000000..4e5f4ad174 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/SplitRuleInvalid.java @@ -0,0 +1,26 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTSplitRule; +import de.monticore.grammar.grammar._cocos.GrammarASTSplitRuleCoCo; +import de.se_rwth.commons.logging.Log; + + +public class SplitRuleInvalid implements GrammarASTSplitRuleCoCo { + + public static final String ERROR_CODE = "0xA4079"; + + public static final String ERROR_MSG_FORMAT = + " The string '%s' for splittoken may not contain any letters or digits and must be longer than 2."; + + @Override + public void check(ASTSplitRule a) { + for (String s:a.getStringList()) { + if (s.length() < 2 || s.matches(".*[a-zA-Z0-9].*")) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, s), a.get_SourcePositionStart()); + } + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/SubrulesUseInterfaceNTs.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/SubrulesUseInterfaceNTs.java new file mode 100644 index 0000000000..492552ae5f --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/SubrulesUseInterfaceNTs.java @@ -0,0 +1,111 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.MCGrammarSymbolTableHelper; +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbolSurrogate; +import de.monticore.grammar.grammar._symboltable.RuleComponentSymbol; +import de.se_rwth.commons.logging.Log; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * Checks that the productions, which implement an interface, use the + * non-terminals of that interface. + * + */ +public class SubrulesUseInterfaceNTs implements GrammarASTMCGrammarCoCo { + + public static final String ERROR_CODE = "0xA4047"; + + public static final String ERROR_MSG_FORMAT = " The production %s must use the Component %s from interface %s."; + + @Override + public void check(ASTMCGrammar a) { + MCGrammarSymbol symbol = a.getSymbol(); + + for (ProdSymbol prodSymbol : symbol.getProds()) { + if (!prodSymbol.isIsInterface()) { + for (ProdSymbol interfaceSymbol : MCGrammarSymbolTableHelper + .getAllSuperInterfaces(prodSymbol)) { + if (prodSymbol.getProdComponents().isEmpty() && !interfaceSymbol.getProdComponents().isEmpty()) { + compareWithSuperProd(symbol, prodSymbol, interfaceSymbol); + } else { + compareComponents(prodSymbol, interfaceSymbol); + } + } + } + } + } + + /** + * used when the Prod which implements the interface is overwriting a prod from a super grammar + * only when the overwriting prod is not specifying a right side (no RuleComponents) + * e.g. grammar A { B = Name;} + * grammar C { interface I = Name; + * B implements I;} + */ + protected void compareWithSuperProd(MCGrammarSymbol grammarSymbol, ProdSymbol prodSymbol, ProdSymbol interfaceSymbol) { + List overwrittenProds = grammarSymbol.getAllSuperGrammars() + .stream() + .map(MCGrammarSymbol::getProds) + .flatMap(Collection::stream) + .filter(x -> x.getName().equals(prodSymbol.getName())) + .collect(Collectors.toList()); + if (overwrittenProds.isEmpty()) { + logError(prodSymbol, interfaceSymbol, interfaceSymbol.getProdComponents().stream().findFirst().get()); + } + for (ProdSymbol overwrittenProd : overwrittenProds) { + compareComponents(overwrittenProd, interfaceSymbol); + } + } + + protected void compareComponents(ProdSymbol prodSymbol, ProdSymbol interfaceSymbol) { + for (RuleComponentSymbol interfaceComponent : interfaceSymbol.getProdComponents()) { + List prodComponents = prodSymbol.getSpannedScope().resolveRuleComponentDownMany(interfaceComponent.getName()); + if (prodComponents.isEmpty()) { + logError(prodSymbol, interfaceSymbol, interfaceComponent); + continue; + } + boolean found = false; + Iterator it = prodComponents.iterator(); + while (!found && it.hasNext()) { + RuleComponentSymbol prodComponent = it.next(); + if ((prodComponent.isIsList() == interfaceComponent.isIsList()) + && (prodComponent.isIsOptional() == interfaceComponent.isIsOptional())) { + if ((prodComponent.isIsTerminal() && interfaceComponent.isIsTerminal()) || + (prodComponent.isIsConstantGroup() && interfaceComponent.isIsConstantGroup()) || + (prodComponent.isIsConstant() && interfaceComponent.isIsConstant())) { + found = true; + } else if (prodComponent.isIsNonterminal() && interfaceComponent.isIsNonterminal()) { + Optional prodComponentRefOpt = prodComponent.getReferencedProd(); + Optional interfaceComponentRefOpt = interfaceComponent.getReferencedProd(); + if (prodComponentRefOpt.isPresent() && interfaceComponentRefOpt.isPresent()) { + found = (prodComponentRefOpt.get().isIsLexerProd() && interfaceComponentRefOpt.get().isIsLexerProd()) + || prodComponentRefOpt.get().getName().equals(interfaceComponentRefOpt.get().getName()); + } + } + } + } + if (!found ){ + logError(prodSymbol, interfaceSymbol, interfaceComponent); + } + } + } + + protected void logError(ProdSymbol prodSymbol, ProdSymbol interfaceSymbol, RuleComponentSymbol interfaceComponent) { + String suffix = interfaceComponent.isIsList() ? "*" : interfaceComponent.isIsOptional() ? "?" : ""; + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, prodSymbol.getName(), + interfaceComponent.getName() + suffix, interfaceSymbol.getName()), + prodSymbol.getSourcePosition()); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/SymbolProdOverwrittenBySymbol.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/SymbolProdOverwrittenBySymbol.java new file mode 100644 index 0000000000..c0ea1ca93f --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/SymbolProdOverwrittenBySymbol.java @@ -0,0 +1,48 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.se_rwth.commons.logging.Log; + +import java.util.List; +import java.util.Optional; + +/** + * checks if a prod is overwriting a prod of a super grammar + * and logs an error if both prods define a symbol + * e.g. grammar A { symbol Foo; } grammar B extends A { symbol Foo; } + * only one of the prods can define a symbol + * the symbol property is inherited from the super prod and does not need to be defined again + */ +public class SymbolProdOverwrittenBySymbol implements GrammarASTMCGrammarCoCo { + + public static final String ERROR_CODE = "0xA0274"; + + public static final String ERROR_MSG_FORMAT = "Production %s from grammar %s is a symbol and overwritten by the prod %s of grammar %s that also defines a symbol." + + "Remove the second symbol definition, because the symbol property is inherited anyway."; + + @Override + public void check(ASTMCGrammar node) { + MCGrammarSymbol grammarSymbol = node.getSymbol(); + // get all super grammars + List superGrammarSymbols = grammarSymbol.getSuperGrammarSymbols(); + for (MCGrammarSymbol superGrammar : superGrammarSymbols) { + // for every prod of the current grammar + for (ProdSymbol prod : grammarSymbol.getProds()) { + // enums cannot define symbols + if (!prod.isIsEnum()) { + // finds a prod with the same name in the super grammar if one is present + Optional superProd = superGrammar.getProd(prod.getName()); + if (superProd.isPresent() && superProd.get().isIsSymbolDefinition() && prod.isIsSymbolDefinition()) { + // log error if both prod define a symbol themselves + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, superProd.get().getName(), superGrammar.getName(), + prod.getName(), grammarSymbol.getName())); + } + } + } + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/SymbolRuleHasName.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/SymbolRuleHasName.java new file mode 100644 index 0000000000..3c5076ee20 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/SymbolRuleHasName.java @@ -0,0 +1,23 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTSymbolRule; +import de.monticore.grammar.grammar._cocos.GrammarASTSymbolRuleCoCo; +import de.se_rwth.commons.logging.Log; + +public class SymbolRuleHasName implements GrammarASTSymbolRuleCoCo { + + public static final String ERROR_CODE = "0xA0118"; + + public static final String ERROR_MSG = " SymbolRule Attribute at %s does not have a name."; + + @Override + public void check(ASTSymbolRule node) { + node.getAdditionalAttributeList().forEach(a -> { + if (!a.isPresentName()) { + Log.error(String.format(ERROR_CODE + ERROR_MSG, a.get_SourcePositionStart())); + } + }); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/SymbolRuleWithoutSymbolRef.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/SymbolRuleWithoutSymbolRef.java new file mode 100644 index 0000000000..2dfe3be9f2 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/SymbolRuleWithoutSymbolRef.java @@ -0,0 +1,27 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTSymbolRule; +import de.monticore.grammar.grammar._cocos.GrammarASTSymbolRuleCoCo; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.se_rwth.commons.logging.Log; + +import java.util.Optional; + +public class SymbolRuleWithoutSymbolRef implements GrammarASTSymbolRuleCoCo { + + public static final String ERROR_CODE = "0xA0117"; + + public static final String ERROR_MSG_FORMAT = " There is no symbol defining rule that belongs to symbolrule %s"; + + @Override + public void check(ASTSymbolRule a) { + Optional symbol = a.getEnclosingScope().resolveProd(a.getType()); + if (!symbol.isPresent() || !symbol.get().isIsSymbolDefinition()) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, a.getType()), + a.get_SourcePositionStart()); + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/SymbolWithManyNames.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/SymbolWithManyNames.java new file mode 100644 index 0000000000..592dad391d --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/SymbolWithManyNames.java @@ -0,0 +1,36 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.monticore.grammar.grammar._symboltable.RuleComponentSymbol; +import de.se_rwth.commons.logging.Log; + +/** + * checks if a prod defining a symbol has a list of names. + */ +public class SymbolWithManyNames implements GrammarASTMCGrammarCoCo { + + public static final String ERROR_CODE = "0xA0279"; + + public static final String ERROR_MSG_FORMAT = "Production %s is a symbol and defines a list of names"; + + @Override + public void check(ASTMCGrammar node) { + MCGrammarSymbol grammarSymbol = node.getSymbol(); + // for every prod of the current grammar + for (ProdSymbol prod : grammarSymbol.getProds()) { + if (prod.isIsSymbolDefinition()) { + for (RuleComponentSymbol component : prod.getProdComponents()) { + if ("name".equals(component.getName()) && component.isIsList()) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, prod.getName()), + prod.getAstNode().get_SourcePositionStart()); + } + } + } + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/TerminalCritical.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/TerminalCritical.java new file mode 100644 index 0000000000..73d8c99c9f --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/TerminalCritical.java @@ -0,0 +1,27 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTTerminal; +import de.monticore.grammar.grammar._cocos.GrammarASTTerminalCoCo; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that used terminals are not digits. + * + */ +public class TerminalCritical implements GrammarASTTerminalCoCo { + + public static final String ERROR_CODE = "0xA4058"; + + public static final String ERROR_MSG_FORMAT = + " If the string %s is defined as terminal, this string can no longer be part of an expression"; + + @Override + public void check(ASTTerminal a) { + if(a.getName().matches("[0-9]+")) { + Log.warn(String.format(ERROR_CODE + ERROR_MSG_FORMAT, a.getName()), a.get_SourcePositionStart()); + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/TerminalEmptyString.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/TerminalEmptyString.java new file mode 100644 index 0000000000..bb464bc240 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/TerminalEmptyString.java @@ -0,0 +1,26 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTTerminal; +import de.monticore.grammar.grammar._cocos.GrammarASTTerminalCoCo; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that used terminals are not empty strings. + * + + */ +public class TerminalEmptyString implements GrammarASTTerminalCoCo { + + public static final String ERROR_CODE = "0xA4054"; + + public static final String ERROR_MSG_FORMAT = " The empty string cannot be used as a keyword."; + + @Override + public void check(ASTTerminal a) { + if(a.getName().isEmpty()) { + Log.error(ERROR_CODE + ERROR_MSG_FORMAT, a.get_SourcePositionStart()); + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/TokenConstantInvalid.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/TokenConstantInvalid.java new file mode 100644 index 0000000000..e0ad6b8f99 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/TokenConstantInvalid.java @@ -0,0 +1,21 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTTokenConstant; +import de.monticore.grammar.grammar._cocos.GrammarASTTokenConstantCoCo; +import de.se_rwth.commons.logging.Log; + +public class TokenConstantInvalid implements GrammarASTTokenConstantCoCo { + + public static final String ERROR_CODE = "0xA4059"; + + public static final String ERROR_MSG_FORMAT = + " The string '%s' may not contain any letters or digits and must be longer than 2."; + + @Override + public void check(ASTTokenConstant a) { + if (a.getString().length()<2 || a.getString().matches(".*[a-zA-Z0-9].*")) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, a.getString()), a.get_SourcePositionStart()); + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/UniqueProdNameInGrammar.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/UniqueProdNameInGrammar.java new file mode 100644 index 0000000000..67985219f3 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/UniqueProdNameInGrammar.java @@ -0,0 +1,41 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + + +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._cocos.GrammarASTMCGrammarCoCo; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.se_rwth.commons.logging.Log; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * checks whether a grammar contains two or more prods that have the same name. + * e.g. grammar A { B = "b"; B = "a";} is not allowed + * prod names must be unique within a grammar + */ + +public class UniqueProdNameInGrammar implements GrammarASTMCGrammarCoCo { + + public static final String ERROR_CODE = "0xA0112"; + + public static final String ERROR_MSG_FORMAT = " Grammar '%s' contains two productions named '%s'. Production names must be unique within a grammar."; + + @Override + public void check(ASTMCGrammar node) { + MCGrammarSymbol grammarSymbol = node.getSymbol(); + List prodNames = grammarSymbol.getProds() + .stream() + .map(ProdSymbol::getName) + .collect(Collectors.toList()); + for (int i = 0; i < prodNames.size(); i++) { + for (int j = i + 1; j < prodNames.size(); j++) { + if (prodNames.get(i).equals(prodNames.get(j))) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, node.getName(), prodNames.get(i))); + } + } + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/UnnamedTerminalInInterface.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/UnnamedTerminalInInterface.java new file mode 100644 index 0000000000..1640ba1343 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/UnnamedTerminalInInterface.java @@ -0,0 +1,40 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.*; +import de.monticore.grammar.grammar._cocos.GrammarASTInterfaceProdCoCo; +import de.se_rwth.commons.logging.Log; + +import java.util.List; + +/** + * this coco checks that an interface does not contain unnamed terminals + * because they do not influence the ast structure + */ +public class UnnamedTerminalInInterface implements GrammarASTInterfaceProdCoCo { + + public static final String ERROR_CODE = "0xA0120"; + + public static final String ERROR_MSG_FORMAT = " Interface '%s' is not allowed to contain the unnamed %s \"%s\"."; + + @Override + public void check(ASTInterfaceProd node) { + checkUnnamedTerminal(node.getName(), node.getAltList()); + } + + protected void checkUnnamedTerminal(String interfaceName, List astAltList) { + for (ASTAlt astAlt : astAltList) { + for (ASTRuleComponent astRuleComponent : astAlt.getComponentList()) { + if (astRuleComponent instanceof ASTTerminal && !((ASTTerminal) astRuleComponent).isPresentUsageName()) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, interfaceName, "Terminal", astRuleComponent.getName())); + } else if (astRuleComponent instanceof ASTKeyTerminal && !((ASTKeyTerminal) astRuleComponent).isPresentUsageName()) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, interfaceName, "KeyTerminal", astRuleComponent.getName())); + } else if (astRuleComponent instanceof ASTTokenTerminal && !((ASTTokenTerminal) astRuleComponent).isPresentUsageName()) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, interfaceName, "KeyTerminal", astRuleComponent.getName())); + } else if (astRuleComponent instanceof ASTBlock) { + checkUnnamedTerminal(interfaceName, ((ASTBlock) astRuleComponent).getAltList()); + } + } + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/UsedLexNTNotDefined.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/UsedLexNTNotDefined.java new file mode 100644 index 0000000000..36fbc0f361 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/UsedLexNTNotDefined.java @@ -0,0 +1,39 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import java.util.Optional; + +import de.monticore.grammar.MCGrammarSymbolTableHelper; +import de.monticore.grammar.grammar._ast.ASTLexNonTerminal; +import de.monticore.grammar.grammar._cocos.GrammarASTLexNonTerminalCoCo; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that used nonterminals are defined. + * + + */ +public class UsedLexNTNotDefined implements GrammarASTLexNonTerminalCoCo { + + public static final String ERROR_CODE = "0xA4016"; + + public static final String ERROR_MSG_FORMAT = " The lexical production %s must not" + + " use the nonterminal %s because there exists no lexical production defining %s."; + + @Override + public void check(ASTLexNonTerminal a) { + Optional grammarSymbol = MCGrammarSymbolTableHelper + .getMCGrammarSymbol(a.getEnclosingScope()); + + Optional ruleSymbol = MCGrammarSymbolTableHelper.getEnclosingRule(a); + String ruleName = ruleSymbol.isPresent() ? ruleSymbol.get().getName() : ""; + if (grammarSymbol.isPresent() + && !grammarSymbol.get().getProdWithInherited(a.getName()).isPresent()) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, ruleName, a.getName(), a.getName()), + a.get_SourcePositionStart()); + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/cocos/UsedNTNotDefined.java b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/UsedNTNotDefined.java new file mode 100644 index 0000000000..58d84a4efd --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/cocos/UsedNTNotDefined.java @@ -0,0 +1,38 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import java.util.Optional; + +import de.monticore.grammar.MCGrammarSymbolTableHelper; +import de.monticore.grammar.grammar._ast.ASTNonTerminal; +import de.monticore.grammar.grammar._cocos.GrammarASTNonTerminalCoCo; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that used nonterminals are defined. + * + + */ +public class UsedNTNotDefined implements GrammarASTNonTerminalCoCo { + + public static final String ERROR_CODE = "0xA2031"; + + public static final String ERROR_MSG_FORMAT = " The production %s must not use the nonterminal " + + "%s because there exists no production defining %s."; + + @Override + public void check(ASTNonTerminal a) { + Optional grammarSymbol = MCGrammarSymbolTableHelper + .getMCGrammarSymbol(a.getEnclosingScope()); + Optional ruleSymbol = MCGrammarSymbolTableHelper.getEnclosingRule(a); + String ruleName = ruleSymbol.isPresent()? ruleSymbol.get().getName() : ""; + if (grammarSymbol.isPresent() && !grammarSymbol.get().getProdWithInherited(a.getName()).isPresent()) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, ruleName, a.getName(), + a.getName()), + a.get_SourcePositionStart()); + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_ast/ASTConstant.java b/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_ast/ASTConstant.java new file mode 100644 index 0000000000..a1e88c1116 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_ast/ASTConstant.java @@ -0,0 +1,52 @@ + +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.grammar._ast; + +import de.monticore.grammar.LexNamer; + +public class ASTConstant extends ASTConstantTOP { + + @Override + public String getName() { + if (isPresentKeyConstant()) { + return getKeyConstant().getString(0); + } + if (isPresentTokenConstant()) { + return getTokenConstant().getString(); + } + return getString(); + } + + public String getHumanName() { + String name; + + if (isPresentUsageName()) { + name = getUsageName(); + } else { + String constName = getName(); + if (matchesJavaIdentifier(constName)) { + name = constName; + } else { + name = LexNamer.createGoodName(constName); + } + } + return name; + } + + protected boolean matchesJavaIdentifier(String checkedString) { + if (checkedString == null || checkedString.length() == 0) { + return false; + } + char[] stringAsChars = checkedString.toCharArray(); + if (!Character.isJavaIdentifierStart(stringAsChars[0])) { + return false; + } + for (int i = 1; i < stringAsChars.length; i++) { + if (!Character.isJavaIdentifierPart(stringAsChars[i])) { + return false; + } + } + return true; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_ast/ASTKeyTerminal.java b/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_ast/ASTKeyTerminal.java new file mode 100644 index 0000000000..154360c60a --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_ast/ASTKeyTerminal.java @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.grammar._ast; + +public class ASTKeyTerminal extends ASTKeyTerminalTOP { + + public String getName() { + return getKeyConstant().getString(0); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_ast/ASTParserProd.java b/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_ast/ASTParserProd.java new file mode 100644 index 0000000000..76ba990a34 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_ast/ASTParserProd.java @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.grammar._ast; + +import java.util.List; + +public interface ASTParserProd extends ASTParserProdTOP { + + public List getSuperInterfaceRuleList(); +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_ast/ASTProd.java b/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_ast/ASTProd.java new file mode 100644 index 0000000000..dabe224641 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_ast/ASTProd.java @@ -0,0 +1,16 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.grammar._ast; + +import java.util.ArrayList; + +public interface ASTProd extends ASTProdTOP { + + default java.util.List getSymbolDefinitionList() { + return new ArrayList(); + } + + +} + + diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_ast/ASTRuleComponent.java b/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_ast/ASTRuleComponent.java new file mode 100644 index 0000000000..c8bcbf0793 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_ast/ASTRuleComponent.java @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.grammar._ast; + + +public interface ASTRuleComponent extends ASTRuleComponentTOP { + + default String getName() { + return ""; + } + +} \ No newline at end of file diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_ast/ASTTokenTerminal.java b/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_ast/ASTTokenTerminal.java new file mode 100644 index 0000000000..c5319ecf21 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_ast/ASTTokenTerminal.java @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.grammar._ast; + +public class ASTTokenTerminal extends ASTTokenTerminalTOP { + + public String getName() { + return getTokenConstant().getString(); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_prettyprint/GrammarPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_prettyprint/GrammarPrettyPrinter.java new file mode 100644 index 0000000000..7c098155ba --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_prettyprint/GrammarPrettyPrinter.java @@ -0,0 +1,315 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.grammar.grammar._prettyprint; + +import de.monticore.ast.ASTNode; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.types.mcbasictypes._ast.ASTMCImportStatement; +import de.se_rwth.commons.Names; + +import java.util.Iterator; + +public class GrammarPrettyPrinter extends GrammarPrettyPrinterTOP { + public GrammarPrettyPrinter(IndentPrinter printer, boolean printComments) { + super(printer, printComments); + } + + public void handle(de.monticore.grammar.grammar._ast.ASTMCGrammar node) { + if (this.isPrintComments()) { + de.monticore.prettyprint.CommentPrettyPrinter.printPreComments(node, getPrinter()); + } + + if (!node.getPackageList().isEmpty()) { + getPrinter().print("package "); + getPrinter().print(Names.constructQualifiedName(node.getPackageList())); + getPrinter().print(";"); + } + + for (ASTMCImportStatement importStatement : node.getImportStatementList()) { + importStatement.accept(getTraverser()); + } + + if (node.isPresentGrammarAnnotation()) { + node.getGrammarAnnotation().accept(getTraverser()); + } + + if (node.isComponent()) { + getPrinter().print("component "); + } + getPrinter().print("grammar "); + getPrinter().print(node.getName()); + getPrinter().print(" "); + + if (!node.getSupergrammarList().isEmpty()) { + getPrinter().print("extends "); + printList(node.iteratorSupergrammar(), ","); + } + + getPrinter().println("{"); + getPrinter().indent(); + + if (node.isPresentGrammarOption()) { + node.getGrammarOption().accept(getTraverser()); + } + printList(node.iteratorLexProds(), ""); + printList(node.iteratorClassProds(), ""); + printList(node.iteratorEnumProds(), ""); + printList(node.iteratorExternalProds(), ""); + printList(node.iteratorInterfaceProds(), ""); + printList(node.iteratorAbstractProds(), ""); + printList(node.iteratorASTRules(), ""); + printList(node.iteratorSymbolRules(), ""); + if (node.isPresentScopeRule()) { + node.getScopeRule().accept(getTraverser()); + } + printList(node.iteratorConcepts(), ""); + if (node.isPresentStartRule()) { + node.getStartRule().accept(getTraverser()); + } + printList(node.iteratorSplitRules(), ""); + printList(node.iteratorKeywordRules(), ""); + printList(node.iteratorReplaceRules(), ""); + + + getPrinter().unindent(); + getPrinter().print("}"); + + + if (this.isPrintComments()) { + de.monticore.prettyprint.CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + getPrinter().println(); + } + + public void handle(de.monticore.grammar.grammar._ast.ASTInterfaceProd node) { + if (this.isPrintComments()) { + de.monticore.prettyprint.CommentPrettyPrinter.printPreComments(node, getPrinter()); + } + + printList(node.iteratorGrammarAnnotations(), " "); + getPrinter().print("interface "); + printList(node.iteratorSymbolDefinitions(), " "); + getPrinter().print(node.getName()); + getPrinter().print(" "); + + if (!node.isEmptySuperInterfaceRule()) { + getPrinter().print("extends "); + printList(node.iteratorSuperInterfaceRule(),", "); + } + if (!node.isEmptyASTSuperInterface()) { + getPrinter().print("astextends "); + printList(node.iteratorASTSuperInterface(),", "); + } + if (!node.isEmptyAlt()) { + getPrinter().print("= "); + printList(node.iteratorAlt(),"|"); + } + getPrinter().println(";"); + + if (this.isPrintComments()) { + de.monticore.prettyprint.CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + } + + public void handle(de.monticore.grammar.grammar._ast.ASTAbstractProd node) { + if (this.isPrintComments()) { + de.monticore.prettyprint.CommentPrettyPrinter.printPreComments(node, getPrinter()); + } + + printList(node.iteratorGrammarAnnotations(), " "); + getPrinter().print("abstract "); + printList(node.iteratorSymbolDefinitions(), " "); + getPrinter().print(node.getName()); + getPrinter().print(" "); + + if (!node.isEmptySuperRule()) { + getPrinter().print("extends "); + printList(node.iteratorSuperRule(),", "); + } + if (!node.isEmptySuperInterfaceRule()) { + getPrinter().print("implements "); + printList(node.iteratorSuperInterfaceRule(),", "); + } + if (!node.isEmptyASTSuperClass()) { + getPrinter().print("astextends "); + printList(node.iteratorASTSuperClass(),", "); + } + if (!node.isEmptyASTSuperInterface()) { + getPrinter().print("astimplements "); + printList(node.iteratorASTSuperInterface(),", "); + } + if (!node.isEmptyAlt()) { + getPrinter().print("= "); + printList(node.iteratorAlt(),"|"); + } + getPrinter().println(";"); + + if (this.isPrintComments()) { + de.monticore.prettyprint.CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + } + + public void handle(de.monticore.grammar.grammar._ast.ASTClassProd node) { + if (this.isPrintComments()) { + de.monticore.prettyprint.CommentPrettyPrinter.printPreComments(node, getPrinter()); + } + + printList(node.iteratorGrammarAnnotations(), " "); + printList(node.iteratorSymbolDefinitions(), " "); + getPrinter().print(" "); + getPrinter().print(node.getName()); + getPrinter().print(" "); + + if (!node.isEmptySuperRule()) { + getPrinter().print("extends "); + printList(node.iteratorSuperRule(),", "); + getPrinter().print(" "); + } + if (!node.isEmptySuperInterfaceRule()) { + getPrinter().print("implements "); + printList(node.iteratorSuperInterfaceRule(),", "); + getPrinter().print(" "); + } + if (!node.isEmptyASTSuperClass()) { + getPrinter().print("astextends "); + printList(node.iteratorASTSuperClass(),", "); + getPrinter().print(" "); + } + if (!node.isEmptyASTSuperInterface()) { + getPrinter().print("astimplements "); + printList(node.iteratorASTSuperInterface(),", "); + getPrinter().print(" "); + } + + if (node.isPresentAction()) { + getPrinter().print("{"); + node.getAction().accept(getTraverser()); + getPrinter().print("} "); + } + + if (!node.isEmptyAlts()) { + getPrinter().print("= "); + printList(node.iteratorAlts(),"|"); + } + getPrinter().println(";"); + + if (this.isPrintComments()) { + de.monticore.prettyprint.CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + } + + public void handle(de.monticore.grammar.grammar._ast.ASTASTRule node) { + if (this.isPrintComments()) { + de.monticore.prettyprint.CommentPrettyPrinter.printPreComments(node, getPrinter()); + } + + getPrinter().print("astrule "); + getPrinter().print(node.getType()); + getPrinter().print(" "); + + if (!node.isEmptyASTSuperClass()) { + getPrinter().print("astextends "); + printList(node.iteratorASTSuperClass(),", "); + getPrinter().print(" "); + } + if (!node.isEmptyASTSuperInterface()) { + getPrinter().print("astimplements "); + printList(node.iteratorASTSuperInterface(),", "); + getPrinter().print(" "); + } + + if (!node.isEmptyGrammarMethods() || !node.isEmptyAdditionalAttributes()) { + getPrinter().print("= "); + printList(node.iteratorGrammarMethods(), " "); + printList(node.iteratorAdditionalAttributes(), " "); + } + + getPrinter().println(";"); + + if (this.isPrintComments()) { + de.monticore.prettyprint.CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + } + + public void handle(de.monticore.grammar.grammar._ast.ASTSymbolRule node) { + if (this.isPrintComments()) { + de.monticore.prettyprint.CommentPrettyPrinter.printPreComments(node, getPrinter()); + } + + getPrinter().print("symbolrule "); + getPrinter().print(node.getType()); + getPrinter().print(" "); + + if (!node.isEmptySuperClass()) { + getPrinter().print("extends "); + printList(node.iteratorSuperClass(),", "); + getPrinter().print(" "); + } + if (!node.isEmptySuperInterface()) { + getPrinter().print("implements "); + printList(node.iteratorSuperInterface(),", "); + getPrinter().print(" "); + } + + if (!node.isEmptyGrammarMethods() || !node.isEmptyAdditionalAttributes()) { + getPrinter().print("= "); + printList(node.iteratorGrammarMethods(), " "); + printList(node.iteratorAdditionalAttributes(), " "); + } + + getPrinter().println(";"); + + if (this.isPrintComments()) { + de.monticore.prettyprint.CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + } + + public void handle(de.monticore.grammar.grammar._ast.ASTScopeRule node) { + if (this.isPrintComments()) { + de.monticore.prettyprint.CommentPrettyPrinter.printPreComments(node, getPrinter()); + } + + getPrinter().print("scoperule "); + + if (!node.isEmptySuperClass()) { + getPrinter().print("extends "); + printList(node.iteratorSuperClass(),", "); + getPrinter().print(" "); + } + if (!node.isEmptySuperInterface()) { + getPrinter().print("implements "); + printList(node.iteratorSuperInterface(),", "); + getPrinter().print(" "); + } + + if (!node.isEmptyGrammarMethods() || !node.isEmptyAdditionalAttributes()) { + getPrinter().print("= "); + printList(node.iteratorGrammarMethods(), " "); + printList(node.iteratorAdditionalAttributes(), " "); + } + + getPrinter().println(";"); + + if (this.isPrintComments()) { + de.monticore.prettyprint.CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + } + + /** + * Prints a list + * + * @param iter iterator for the list + * @param seperator string for seperating list + */ + protected void printList(Iterator iter, String seperator) { + // print by iterate through all items + String sep = ""; + while (iter.hasNext()) { + getPrinter().print(sep); + iter.next().accept(getTraverser()); + sep = seperator; + } + } + + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_symboltable/GrammarSTCompleteTypes.java b/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_symboltable/GrammarSTCompleteTypes.java new file mode 100644 index 0000000000..c2953bfb83 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_symboltable/GrammarSTCompleteTypes.java @@ -0,0 +1,339 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.grammar._symboltable; + +import com.google.common.collect.Lists; +import de.monticore.grammar.DirectLeftRecursionDetector; +import de.monticore.grammar.MCGrammarSymbolTableHelper; +import de.monticore.grammar.Multiplicity; +import de.monticore.grammar.grammar._ast.*; +import de.monticore.grammar.grammar._visitor.GrammarVisitor2; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.mcsimplegenerictypes.MCSimpleGenericTypesMill; + +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import static com.google.common.collect.Sets.newLinkedHashSet; +import static de.monticore.grammar.Multiplicity.*; +import static de.se_rwth.commons.logging.Log.error; +import static java.util.Collections.emptyList; +import static java.util.Collections.max; + + +public class GrammarSTCompleteTypes implements GrammarVisitor2 { + + protected MCGrammarSymbol grammarSymbol; + protected ASTMCGrammar astGrammar; + + @Override + public void visit(ASTSplitRule ast) { + grammarSymbol.addAllSplitRules(ast.getStringList()); + } + + @Override + public void visit(ASTMCGrammar ast) { + this.astGrammar = ast; + this.grammarSymbol = ast.getSymbol(); + } + + @Override + public void endVisit(ASTMCGrammar ast) { + setComponentsCardinality(ast); + + computeStartParserProd(ast); + + ast.getClassProdList().forEach(p -> setIfProdIsLeftRecursive(p)); + } + + @Override + public void visit(ASTKeywordRule ast) { + grammarSymbol.addAllNoKeywords(ast.getStringList()); + } + + @Override + public void visit(ASTTokenConstant node) { + grammarSymbol.splitRules.add(node.getString()); + } + + @Override + public void visit(ASTReplaceRule node) { + grammarSymbol.addKeywords(node.getKeyword(), node.getReplacedKeyWordList()); + } + + @Override + public void visit(ASTKeyConstant node) { + grammarSymbol.noKeywords.addAll(node.getStringList()); + } + + @Override + public void visit(ASTLexProd node) { + GrammarVisitor2.super.visit(node); + if (node.isPresentMode()) { + grammarSymbol.addMode(node.getMode(), node.getName()); + } else { + grammarSymbol.addMode(MCGrammarSymbol.DEFAULT_MODE, node.getName()); + + } + } + + @Override + public void endVisit(ASTTerminal node) { + if (node.isPresentUsageName()) { + RuleComponentSymbol prodComponent = node.getSymbol(); + prodComponent.setIsTerminal(true); + setComponentMultiplicity(prodComponent, node); + } + } + + @Override + public void endVisit(ASTKeyTerminal node) { + if (node.isPresentUsageName()) { + RuleComponentSymbol prodComponent = node.getSymbol(); + prodComponent.setIsTerminal(true); + setComponentMultiplicity(prodComponent, node); + } + } + + @Override + public void endVisit(ASTTokenTerminal node) { + if (node.isPresentUsageName()) { + RuleComponentSymbol prodComponent = node.getSymbol(); + prodComponent.setIsTerminal(true); + setComponentMultiplicity(prodComponent, node); + } + } + + @Override + public void endVisit(ASTNonTerminal node) { + RuleComponentSymbol prodComponent = node.getSymbol(); + prodComponent.setReferencedType(node.getName()); + prodComponent.setIsNonterminal(true); + } + + @Override + public void endVisit(ASTLexNonTerminal node) { + RuleComponentSymbol prodComponent = node.getSymbol(); + prodComponent.setReferencedType(node.getName()); + prodComponent.setIsLexerNonterminal(true); + } + + @Override + public void endVisit(ASTInterfaceProd node) { + ProdSymbol prodSymbol = node.getSymbol(); + prodSymbol.setIsInterface(true); + + setSymbolDefinition(prodSymbol, node.getSymbolDefinitionList()); + + setSuperProdsAndTypes(prodSymbol, emptyList(), + emptyList(), node.getSuperInterfaceRuleList(), node.getASTSuperInterfaceList()); + } + + @Override + public void endVisit(ASTLexProd node) { + ProdSymbol prodSymbol = node.getSymbol(); + prodSymbol.setIsLexerProd(true); + } + + @Override + public void endVisit(ASTEnumProd node) { + ProdSymbol prodSymbol = node.getSymbol(); + prodSymbol.setIsEnum(true); + // TODO Behandlung der Constants fehlt noch + } + + @Override + public void endVisit(ASTClassProd ast) { + ProdSymbol prodSymbol = ast.getSymbol(); + setSymbolDefinition(prodSymbol, ast.getSymbolDefinitionList()); + + setSuperProdsAndTypes(prodSymbol, ast.getSuperRuleList(), + ast.getASTSuperClassList(), ast.getSuperInterfaceRuleList(), ast.getASTSuperInterfaceList()); + } + + @Override + public void endVisit(ASTAbstractProd ast) { + ProdSymbol prodSymbol = ast.getSymbol(); + prodSymbol.setIsAbstract(true); + + setSymbolDefinition(prodSymbol, ast.getSymbolDefinitionList()); + + setSuperProdsAndTypes(prodSymbol, ast.getSuperRuleList(), + ast.getASTSuperClassList(), ast.getSuperInterfaceRuleList(), ast.getASTSuperInterfaceList()); + } + + @Override + public void endVisit(ASTExternalProd ast) { + ProdSymbol prodSymbol = ast.getSymbol(); + prodSymbol.setIsExternal(true); + + setSymbolDefinition(prodSymbol, ast.getSymbolDefinitionList()); + } + + @Override + public void endVisit(ASTConstantGroup node) { + RuleComponentSymbol symbol = node.getSymbol(); + symbol.setIsConstantGroup(true); + for (ASTConstant c : node.getConstantList()) { + if (c.isPresentUsageName()) { + symbol.addSubProds(c.getUsageName()); + } else if (c.isPresentKeyConstant()) { + symbol.addSubProds(c.getKeyConstant().getString(0)); + } else { + symbol.addSubProds(c.getName()); + } + } + } + + protected void setComponentMultiplicity(RuleComponentSymbol prod, ASTRuleComponent ast) { + Multiplicity multiplicity = determineMultiplicity(astGrammar, ast); + if (multiplicity == LIST) { + prod.setIsList(true); + } else if (multiplicity == OPTIONAL) { + prod.setIsOptional(true); + } + } + + protected void setSymbolDefinition(ProdSymbol prodSymbol, + List listOfDefs) { + for (ASTSymbolDefinition symbolDefinition : listOfDefs) { + if (symbolDefinition.isGenSymbol()) { + prodSymbol.setIsSymbolDefinition(true); + } + if (symbolDefinition.isGenScope()) { + prodSymbol.setIsScopeSpanning(true); + } + } + } + + protected void setSuperProdsAndTypes(ProdSymbol prodSymbol, List superProds, + List astSuperClasses, List superInterfaceProds, + List astSuperInterfaces) { + final IGrammarScope enclosingScope = grammarSymbol.getSpannedScope(); + + // A extends B + for (ASTRuleReference astSuperProd : superProds) { + ProdSymbolSurrogate superProd = new ProdSymbolSurrogate(astSuperProd.getTypeName()); + superProd.setEnclosingScope(enclosingScope); + prodSymbol.addSuperProd(superProd); + } + + // A astextends B + for (ASTMCType astSuperClass : astSuperClasses) { + ProdSymbolSurrogate superClass = new ProdSymbolSurrogate( + MCSimpleGenericTypesMill.prettyPrint(astSuperClass, false)); + superClass.setEnclosingScope(enclosingScope); + prodSymbol.addAstSuperClass(superClass); + } + + // A implements B + for (ASTRuleReference astInterface : superInterfaceProds) { + ProdSymbolSurrogate superProd = new ProdSymbolSurrogate(astInterface.getTypeName()); + superProd.setEnclosingScope(enclosingScope); + prodSymbol.addSuperInterfaceProd(superProd); + } + + // A astimplements B + for (ASTMCType astInterface : astSuperInterfaces) { + ProdSymbolSurrogate superClass = new ProdSymbolSurrogate(MCSimpleGenericTypesMill.prettyPrint(astInterface, false)); + superClass.setEnclosingScope(enclosingScope); + prodSymbol.addAstSuperInterface(superClass); + } + } + + /** + * Set cardinality of all grammar's nonterminals + */ + protected void setComponentsCardinality(ASTMCGrammar astGrammar) { + for (ProdSymbol prodSymbol : astGrammar.getSymbol().getProds()) { + Collection astAttributes = prodSymbol.getSpannedScope().getLocalAdditionalAttributeSymbols(); + for (String compName : prodSymbol.getSpannedScope().getRuleComponentSymbols().keySet()) { + Optional attribute = astAttributes.stream() + .filter(a -> a.getName().equals(compName)).findAny(); + Multiplicity multiplicity = STANDARD; + if (attribute.isPresent()) { + multiplicity = determineMultiplicity(attribute.get().getAstNode()); + } else { + for (RuleComponentSymbol component : prodSymbol.getSpannedScope().getRuleComponentSymbols().get(compName)) { + if (component.isIsNonterminal()) { + Multiplicity mult = determineMultiplicity(component.getAstNode()); + multiplicity = max(Lists.newArrayList(mult, multiplicity)); + } + } + } + for (RuleComponentSymbol component : prodSymbol.getSpannedScope().getRuleComponentSymbols().get(compName)) { + if (component.isIsNonterminal()) { + component.setIsList(multiplicity == LIST); + component.setIsOptional(multiplicity == OPTIONAL); + } + } + } + } + } + + protected void computeStartParserProd(ASTMCGrammar astGrammar) { + if (astGrammar.isPresentStartRule()) { + String name = astGrammar.getStartRule().getName(); + Optional prod = astGrammar.getSymbol().getProdWithInherited(name); + if (!prod.isPresent()) { + error("0xA0243 Rule " + name + " couldn't be found!"); + } else { + prod.get().setIsStartProd(true); + astGrammar.getSymbol().setStartProd(prod.get()); + } + } else { + final Set firstProductions = newLinkedHashSet(); + // The start rule for parsing is the first occurring Interface-, Abstract- + // or Class-Production in this grammar + if (!astGrammar.getClassProdList().isEmpty()) { + firstProductions.add(astGrammar.getClassProdList().get(0)); + } + if (!astGrammar.getInterfaceProdList().isEmpty()) { + firstProductions.add(astGrammar.getInterfaceProdList().get(0)); + } + if (!astGrammar.getAbstractProdList().isEmpty()) { + firstProductions.add(astGrammar.getAbstractProdList().get(0)); + } + setStartProd(astGrammar, firstProductions); + } + } + + /** + * Set start parser production + */ + protected void setStartProd(ASTMCGrammar astGrammar, Set firstProductions) { + // Set start parser rule + ASTProd firstProduction = null; + for (ASTProd prod : firstProductions) { + if ((firstProduction == null) + || (firstProduction.get_SourcePositionStart() + .compareTo(prod.get_SourcePositionStart()) > 0)) { + firstProduction = prod; + } + } + + if (firstProduction != null) { + ProdSymbol prod = firstProduction.getSymbol(); + prod.setIsStartProd(true); + astGrammar.getSymbol().setStartProd(prod); + } + } + + protected void setIfProdIsLeftRecursive(ASTClassProd ast) { + ProdSymbol prodSymbol = ast.getSymbol(); + Set superProds = MCGrammarSymbolTableHelper.getAllSuperProds(prodSymbol); + Collection names = Lists.newArrayList(); + superProds.forEach(s -> names.add(s.getName())); + DirectLeftRecursionDetector detector = new DirectLeftRecursionDetector(); + for (ASTAlt alt : ast.getAltList()) { + if (detector.isAlternativeLeftRecursive(alt, names)) { + prodSymbol.setIsIndirectLeftRecursive(true); + superProds.stream().filter(s -> s.isInterface).forEach(s ->s.setIsIndirectLeftRecursive(true)); + } else if (detector.isAlternativeLeftRecursive(alt, ast.getName())) { + prodSymbol.setIsDirectLeftRecursive(true); + } + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_symboltable/GrammarScopesGenitor.java b/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_symboltable/GrammarScopesGenitor.java new file mode 100644 index 0000000000..a5412e4c2f --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_symboltable/GrammarScopesGenitor.java @@ -0,0 +1,369 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.grammar._symboltable; + +import com.google.common.collect.Lists; +import de.monticore.grammar.MCGrammarSymbolTableHelper; +import de.monticore.grammar.grammar.GrammarMill; +import de.monticore.grammar.grammar._ast.*; +import de.monticore.grammar.grammar_withconcepts._prettyprint.Grammar_WithConceptsFullPrettyPrinter; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.types.mcsimplegenerictypes.MCSimpleGenericTypesMill; +import de.se_rwth.commons.Names; +import de.se_rwth.commons.StringTransformations; +import de.se_rwth.commons.logging.Log; + +import java.util.List; +import java.util.Optional; + +import static de.se_rwth.commons.Names.getQualifiedName; +import static de.se_rwth.commons.logging.Log.error; +import static java.util.Optional.empty; +import static java.util.Optional.of; + +public class GrammarScopesGenitor extends GrammarScopesGenitorTOP { + + public GrammarScopesGenitor(){ + super(); + } + + protected static final String SET_SCOPE_ERROR = "Could not set enclosing scope of ASTNode \"%s\", because no scope is set yet!"; + + protected MCGrammarSymbol grammarSymbol; + + @Override + public void visit(ASTMCGrammar ast){ + MCGrammarSymbolBuilder symbolBuilder = de.monticore.grammar.grammar.GrammarMill.mCGrammarSymbolBuilder().setName(ast.getName()); + symbolBuilder.setIsComponent(ast.isComponent()); + addSuperGrammars(ast, symbolBuilder); + MCGrammarSymbol symbol = symbolBuilder.build(); + if (getCurrentScope().isPresent()) { + getCurrentScope().get().add(symbol); + } else { + Log.warn("0xAE867 Symbol cannot be added to current scope, since no scope exists."); + } + IGrammarScope scope = createScope(false); + putOnStack(scope); + symbol.setSpannedScope(scope); + // symbol -> ast + symbol.setAstNode(ast); + + // ast -> symbol + ast.setSymbol(symbol); + ast.setEnclosingScope(symbol.getEnclosingScope()); + + // scope -> ast + scope.setAstNode(ast); + + // ast -> scope + ast.setSpannedScope(scope); + initMCGrammarHP1(ast.getSymbol()); + grammarSymbol = ast.getSymbol(); + } + + @Override + public void endVisit(ASTMCGrammar astGrammar) { + // remove grammar scope + removeCurrentScope(); + removeCurrentScope(); + } + + @Override + public void visit (ASTTerminal node) { + // only create a symbol for ASTKeyTerminals that have a usage name + // only with usage name is shown in AST + if(node.isPresentUsageName()){ + RuleComponentSymbolBuilder symbolBuilder = GrammarMill.ruleComponentSymbolBuilder().setName(node.getName()); + symbolBuilder.setName(node.isPresentUsageName()?node.getUsageName():""); + RuleComponentSymbol symbol = symbolBuilder.build(); + if (getCurrentScope().isPresent()) { + getCurrentScope().get().add(symbol); + } else { + Log.warn("0xAE862 Symbol cannot be added to current scope, since no scope exists."); + } + // symbol -> ast + symbol.setAstNode(node); + + // ast -> symbol + node.setSymbol(symbol); + node.setEnclosingScope(symbol.getEnclosingScope()); + + initRuleComponentHP1(node.getSymbol()); + } else { + // must still add the scope to the ASTKeyTerminal, even if it defines no symbol + if (getCurrentScope().isPresent()) { + node.setEnclosingScope(getCurrentScope().get()); + } + else { + Log.error("Could not set enclosing scope of ASTNode \"" + node + + "\", because no scope is set yet!"); + } + } + } + + @Override + public void visit(ASTKeyTerminal node) { + // only create a symbol for ASTKeyTerminals that have a usage name + // only with usage name is shown in AST + grammarSymbol.noKeywords.addAll(node.getKeyConstant().getStringList()); + if(node.isPresentUsageName()){ + RuleComponentSymbolBuilder symbolBuilder = GrammarMill.ruleComponentSymbolBuilder().setName(node.getName()); + symbolBuilder.setName(node.isPresentUsageName()?node.getUsageName():""); + RuleComponentSymbol symbol = symbolBuilder.build(); + if (getCurrentScope().isPresent()) { + getCurrentScope().get().add(symbol); + } else { + Log.warn("0xAE863 Symbol cannot be added to current scope, since no scope exists."); + } + // symbol -> ast + symbol.setAstNode(node); + + // ast -> symbol + node.setSymbol(symbol); + node.setEnclosingScope(symbol.getEnclosingScope()); + + initRuleComponentHP1(node.getSymbol()); + } else { + // must still add the scope to the ASTKeyTerminal, even if it defines no symbol + if (getCurrentScope().isPresent()) { + node.setEnclosingScope(getCurrentScope().get()); + } else { + Log.error(String.format(SET_SCOPE_ERROR, node)); + } + } + } + + @Override + public void visit(ASTTokenTerminal node) { + // only create a symbol for ASTKeyTerminals that have a usage name + // only with usage name is shown in AST + if(node.isPresentUsageName()){ + RuleComponentSymbolBuilder symbolBuilder = GrammarMill.ruleComponentSymbolBuilder().setName(node.getName()); + symbolBuilder.setName(node.isPresentUsageName()?node.getUsageName():""); + RuleComponentSymbol symbol = symbolBuilder.build(); + if (getCurrentScope().isPresent()) { + getCurrentScope().get().add(symbol); + } else { + Log.warn("0xAE864 Symbol cannot be added to current scope, since no scope exists."); + } + // symbol -> ast + symbol.setAstNode(node); + + // ast -> symbol + node.setSymbol(symbol); + node.setEnclosingScope(symbol.getEnclosingScope()); + + initRuleComponentHP1(node.getSymbol()); + } else { + // must still add the scope to the ASTKeyTerminal, even if it defines no symbol + if (getCurrentScope().isPresent()) { + node.setEnclosingScope(getCurrentScope().get()); + } else { + Log.error(String.format(SET_SCOPE_ERROR, node)); + } + } + } + + @Override + public void visit(ASTNonTerminal node) { + RuleComponentSymbolBuilder symbolBuilder = GrammarMill.ruleComponentSymbolBuilder().setName(node.getName()); + symbolBuilder.setName(node.isPresentUsageName()?node.getUsageName(): StringTransformations.uncapitalize(node.getName())); + RuleComponentSymbol symbol = symbolBuilder.build(); + if (getCurrentScope().isPresent()) { + getCurrentScope().get().add(symbol); + } else { + Log.warn("0xAE865 Symbol cannot be added to current scope, since no scope exists."); + } + // symbol -> ast + symbol.setAstNode(node); + + // ast -> symbol + node.setSymbol(symbol); + node.setEnclosingScope(symbol.getEnclosingScope()); + + initRuleComponentHP1(node.getSymbol()); + } + + @Override + public void visit(ASTASTRule ast) { + final Optional prodSymbol = grammarSymbol.getProd(ast.getType()); + if (prodSymbol.isPresent()) { + ast.getAdditionalAttributeList().forEach(a -> addAttributeInAST(prodSymbol.get(), a, true)); + } else { + error( + "0xA4076 There must not exist an AST rule for the nonterminal " + ast.getType() + + " because there exists no production defining " + ast.getType(), + ast.get_SourcePositionStart()); + } + ast.setEnclosingScope(getCurrentScope().get()); + } + + @Override + public void visit(ASTSymbolRule ast) { + final Optional prodSymbol = grammarSymbol.getProd(ast.getType()); + if (prodSymbol.isPresent()) { + ast.getAdditionalAttributeList().forEach(a -> addAttributeInAST(prodSymbol.get(), a, false)); + } else { + error( + "0xA4077 There must not exist an symbol rule for the nonterminal " + ast.getType() + + " because there exists no production defining " + ast.getType(), + ast.get_SourcePositionStart()); + } + ast.setEnclosingScope(getCurrentScope().get()); + } + + protected AdditionalAttributeSymbolBuilder createAdditionalAttribute(ASTAdditionalAttribute ast) { + String symbolName; + if (ast.isPresentName()) { + symbolName = ast.getName(); + } else { + String typeName = MCSimpleGenericTypesMill.prettyPrint(ast.getMCType(), false); + symbolName = StringTransformations.uncapitalize(Names.getSimpleName(typeName)); + } + return new AdditionalAttributeSymbolBuilder().setName(symbolName); + } + + @Override + public void visit(ASTAdditionalAttribute ast) { + // Do nothing: see method visit(ASTASTRule ast) + if (getCurrentScope().isPresent()) { + ast.setEnclosingScope(getCurrentScope().get()); + } else { + Log.error(String.format(SET_SCOPE_ERROR, ast)); + } + } + + @Override + public void endVisit(ASTAdditionalAttribute node) { + // Do nothing + } + + @Override + public void visit(ASTBlock ast) { + // Do nothing: + if (getCurrentScope().isPresent()) { + ast.setEnclosingScope(getCurrentScope().get()); + } else { + Log.error(String.format(SET_SCOPE_ERROR, ast)); + } + } + + @Override + public void endVisit(ASTBlock node) { + // Do nothing + } + + /** + * Create entry for an implicit rule defined in another lexrule by using an + * action and changing the type of the token + */ + @Override + public void visit(ASTLexActionOrPredicate action) { + Grammar_WithConceptsFullPrettyPrinter prettyPrinter = new Grammar_WithConceptsFullPrettyPrinter(new IndentPrinter()); + for (String typeName : findImplicitTypes(action, prettyPrinter)) { + // Create rule if needed + Optional rule = grammarSymbol.getProd(typeName); + if (!rule.isPresent()) { + // Create entry for an implicit rule + final ProdSymbol prodSymbol = new ProdSymbol(typeName); + prodSymbol.setIsLexerProd(true); + } + } + super.visit(action); + } + + @Override + public void visit(ASTConstantGroup node) { + RuleComponentSymbolBuilder symbolBuilder = de.monticore.grammar.grammar.GrammarMill.ruleComponentSymbolBuilder().setName(node.getName()); + symbolBuilder.setName(MCGrammarSymbolTableHelper.getConstantGroupName(node)); + RuleComponentSymbol symbol = symbolBuilder.build(); + if (getCurrentScope().isPresent()) { + getCurrentScope().get().add(symbol); + } else { + Log.warn("0xAE866 Symbol cannot be added to current scope, since no scope exists."); + } + // symbol -> ast + symbol.setAstNode(node); + + // ast -> symbol + node.setSymbol(symbol); + node.setEnclosingScope(symbol.getEnclosingScope()); + + initRuleComponentHP1(node.getSymbol()); + } + + protected void addSuperGrammars(ASTMCGrammar astGrammar, MCGrammarSymbolBuilder grammarSymbol) { + for (ASTGrammarReference ref : astGrammar.getSupergrammarList()) { + final String superGrammarName = getQualifiedName(ref.getNameList()); + + final MCGrammarSymbolSurrogate superGrammar = new MCGrammarSymbolSurrogate( + superGrammarName); + superGrammar.setEnclosingScope(getCurrentScope().orElse(null)); + + grammarSymbol.addSuperGrammar(superGrammar); + } + } + + /** + * @param mcProdSymbol + * @param astAttribute + */ + protected void addAttributeInAST(ProdSymbol mcProdSymbol, ASTAdditionalAttribute astAttribute, boolean isAstAttr) { + AdditionalAttributeSymbol symbol = createAdditionalAttribute(astAttribute).setIsAstAttr(isAstAttr).build(); + symbol.setType(astAttribute.getMCType().printType()); + mcProdSymbol.getSpannedScope().add(symbol); + // symbol -> ast + symbol.setAstNode(astAttribute); + + // ast -> symbol + astAttribute.setSymbol(symbol); + astAttribute.setEnclosingScope(symbol.getEnclosingScope()); + } + + public final Optional getProdSymbol() { + if (getCurrentScope().isPresent()) { + IGrammarScope scope = getCurrentScope().get(); + if (scope.isPresentSpanningSymbol() && scope.getSpanningSymbol() instanceof ProdSymbol) { + return of((ProdSymbol) scope.getSpanningSymbol()); + } + } + return empty(); + } + + protected List findImplicitTypes(ASTLexActionOrPredicate action, + Grammar_WithConceptsFullPrettyPrinter prettyPrinter) { + List ret = Lists.newArrayList(); + StringBuilder buffer = new StringBuilder(); + buffer.append(prettyPrinter.prettyprint(action.getExpressionPredicate())); + String actionText = buffer.toString(); + if (actionText.contains("_ttype")) { + String[] split = actionText.split("_ttype"); + + for (int i = 1; i < split.length; i++) { + String rest = split[i].trim(); + if (rest.length() > 1 && rest.startsWith("=")) { + rest = rest.substring(1).trim(); + if (!rest.startsWith("Token")) { + String string = rest.split("[ ;]")[0]; + ret.add(string); + } + } + } + } + if (actionText.contains("$setType(")) { + String[] split = actionText.split("[$]setType[(]"); + + for (int i = 1; i < split.length; i++) { + String rest = split[i].trim(); + if (rest.length() > 0) { + + if (!rest.startsWith("Token")) { + String string = rest.split("[ )]")[0]; + ret.add(string); + } + } + } + } + return ret; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_symboltable/IGrammarScope.java b/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_symboltable/IGrammarScope.java new file mode 100644 index 0000000000..8bc66b7679 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_symboltable/IGrammarScope.java @@ -0,0 +1,116 @@ +/* (c) https://github.com/MontiCore/monticore */ +/* generated by template symboltable.ScopeInterface*/ + +package de.monticore.grammar.grammar._symboltable; + +import de.monticore.grammar.MCGrammarSymbolTableHelper; +import de.monticore.symboltable.modifiers.AccessModifier; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import static de.monticore.symboltable.modifiers.AccessModifier.ALL_INCLUSION; +import static java.util.Optional.empty; + +public interface IGrammarScope extends IGrammarScopeTOP { + + // all resolveImported Methods for ProdSymbol + default public Optional resolveProdImported(String name, AccessModifier modifier) { + Optional s = this.resolveProdLocally(name); + if (s.isPresent()) { + return s; + } + return resolveInSuperGrammars(name, modifier); + } + + + default public List resolveProdMany(boolean foundSymbols, String name, AccessModifier modifier, Predicate predicate) { + if (!isProdSymbolsAlreadyResolved()) { + setProdSymbolsAlreadyResolved(true); + } else { + return new ArrayList<>(); + } + + final List resolvedSymbols = this.resolveProdLocallyMany(foundSymbols, name, modifier, predicate); + if (!resolvedSymbols.isEmpty()) { + setProdSymbolsAlreadyResolved(false); + return resolvedSymbols; + } + + resolveInSuperGrammars(name, modifier).ifPresent(resolvedSymbols::add); + if (!resolvedSymbols.isEmpty()) { + setProdSymbolsAlreadyResolved(false); + return resolvedSymbols; + } + + resolvedSymbols.addAll(resolveAdaptedProdLocallyMany(foundSymbols, name, modifier, predicate)); + if (!resolvedSymbols.isEmpty()) { + setProdSymbolsAlreadyResolved(false); + return resolvedSymbols; + } + final Collection resolvedFromEnclosing = continueProdWithEnclosingScope((foundSymbols | !resolvedSymbols.isEmpty()), name, modifier, predicate); + resolvedSymbols.addAll(resolvedFromEnclosing); + setProdSymbolsAlreadyResolved(false); + return resolvedSymbols; + } + + default Optional resolveInSuperGrammars(String name, AccessModifier modifier) { + Optional resolvedSymbol = empty(); + + // TODO (GV, MB) + // Die Methode muss überarbeitet werden. GrammarSymbols sollen nicht gefunden werden? Dann braucht man u.U. + // checkIfContinueWithSuperGrammar gar nicht mehr ... + Optional spanningSymbol = MCGrammarSymbolTableHelper.getMCGrammarSymbol(this); + if (spanningSymbol.isPresent()) { + MCGrammarSymbol grammarSymbol = spanningSymbol.get(); + for (MCGrammarSymbolSurrogate superGrammarRef : grammarSymbol.getSuperGrammars()) { + final MCGrammarSymbol superGrammar = superGrammarRef.lazyLoadDelegate(); + resolvedSymbol = resolveInSuperGrammar(name, superGrammar); + // Stop as soon as symbol is found in a super grammar. + if (resolvedSymbol.isPresent()) { + break; + } + } + } + return resolvedSymbol; + } + + default Optional resolveInSuperGrammar(String name, MCGrammarSymbol superGrammar) { + return superGrammar.getSpannedScope().resolveProdImported(name, ALL_INCLUSION); + } + + default boolean isQualified(MCGrammarSymbolSurrogate grammarRef) { + if (grammarRef.getName().contains(".")) { + return true; + } + MCGrammarSymbol grammarSymbol = grammarRef.lazyLoadDelegate(); + if (!grammarSymbol.getFullName().contains(".")) { + // The complete name has no package name, therefore the grammarRefName + // without "." is qualified + return true; + } + + return false; + } + + default boolean isQualified(String name) { + if (name.contains(".")) { + return true; + } + return false; + } + + default List getAstAttributeList () { + return getLocalAdditionalAttributeSymbols().stream().filter(a -> a.isAstAttr).collect(Collectors.toList()); + } + + default List getSymbolAttributeList () { + return getLocalAdditionalAttributeSymbols().stream().filter(a -> !a.isAstAttr).collect(Collectors.toList()); + } + +} + diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_symboltable/MCGrammarSymbol.java b/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_symboltable/MCGrammarSymbol.java new file mode 100644 index 0000000000..66a0b6c0c5 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_symboltable/MCGrammarSymbol.java @@ -0,0 +1,324 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.grammar._symboltable; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import de.monticore.grammar.MCGrammarSymbolTableHelper; +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.se_rwth.commons.Names; + +import java.util.*; + +import static com.google.common.collect.ImmutableList.copyOf; +import static de.se_rwth.commons.logging.Log.errorIfNull; +import static java.util.Optional.ofNullable; +import static java.util.stream.Collectors.toList; + +public class MCGrammarSymbol extends MCGrammarSymbolTOP { + + public static final String DEFAULT_MODE = "DEFAULT_MODE"; + + protected final List superGrammars = new ArrayList<>(); + + protected Map> tokenModes = Maps.newHashMap(); + + protected Map> replacedKeywords = Maps.newHashMap(); + + protected final LoadingCache> prodCache = CacheBuilder.newBuilder() + .maximumSize(10000) + .build(new CacheLoader>() { + @Override + public Optional load(String key) { + Optional mcProd = getProd(key); + if (mcProd.isPresent()) { + return mcProd; + } + return getInheritedProd(key); + } + }); + + // the start production of the grammar + protected ProdSymbol startProd; + + public MCGrammarSymbol(String name) { + super(name); + } + + public void setStartProd(ProdSymbol startRule) { + this.startProd = startRule; + } + + /** + * The start production typically is the first defined production in the + * grammar. + * + * @return the start production of the grammar, if not a component grammar + */ + public Optional getStartProd() { + return ofNullable(startProd); + } + + public List getSuperGrammars() { + return copyOf(superGrammars); + } + + public List getSuperGrammarSymbols() { + return copyOf(superGrammars.stream() + .map(g -> g.lazyLoadDelegate()) + .collect(toList())); + } + + public List getAllSuperGrammars() { + List supGrammars = new ArrayList<>(this.getSuperGrammarSymbols()); + List superSuperGrammars = new ArrayList<>(); + for (MCGrammarSymbol superGrammar : supGrammars) { + superGrammar.getAllSuperGrammars().stream().filter(s -> !superSuperGrammars.contains(s)).forEach(s -> superSuperGrammars.add(s)); + } + superSuperGrammars.stream().filter(s -> !supGrammars.contains(s)).forEach(s->supGrammars.add(s)); + return copyOf(supGrammars); + } + + public void addSuperGrammar(MCGrammarSymbolSurrogate superGrammarRef) { + this.superGrammars.add(errorIfNull(superGrammarRef)); + } + + public Collection getProds() { + return this.getSpannedScope().getLocalProdSymbols(); + } + + public Collection getProdNames() { + final Set prodNames = new LinkedHashSet<>(); + + for (final ProdSymbol prodSymbol : getProds()) { + prodNames.add(prodSymbol.getName()); + } + + return ImmutableSet.copyOf(prodNames); + } + + public Optional getProd(String prodName) { + return this.getSpannedScope().resolveProdLocally(prodName); + } + + // return local prod or prod from supergrammars + public Optional getProdWithInherited(String ruleName) { + return prodCache.getUnchecked(ruleName); + } + + // return only prod from supergrammars + public Optional getInheritedProd(String ruleName) { + final Map map = new LinkedHashMap<>(); + + for (int i = superGrammars.size() - 1; i >= 0; i--) { + final MCGrammarSymbol superGrammar = superGrammars.get(i).lazyLoadDelegate(); + Optional inheritedProd = superGrammar.getProdWithInherited(ruleName); + if (inheritedProd.isPresent()) { + return inheritedProd; + } + } + return Optional.empty(); + } + + // return local prods and prods from supergrammars + public Map getProdsWithInherited() { + final Map ret = new LinkedHashMap<>(); + + for (int i = superGrammars.size() - 1; i >= 0; i--) { + final MCGrammarSymbolSurrogate superGrammarRef = superGrammars.get(i); + + for (ProdSymbol prod:superGrammarRef.lazyLoadDelegate().getProdsWithInherited().values()) { + if (ret.containsKey(prod.getName())) { + ProdSymbol superProd = ret.get(prod.getName()); + if (MCGrammarSymbolTableHelper.getAllSuperProds(prod).contains(superProd)) { + ret.put(prod.getName(), prod); + } + } else { + ret.put(prod.getName(), prod); + } + } + } + + for (final ProdSymbol prodSymbol : getProds()) { + ret.put(prodSymbol.getName(), prodSymbol); + } + + return ret; + } + + public Collection getTokenRulesWithInherited() { + final Collection ret = Sets.newHashSet(); + + for (int i = superGrammars.size() - 1; i >= 0; i--) { + final MCGrammarSymbolSurrogate superGrammarRef = superGrammars.get(i); + + ret.addAll(superGrammarRef.lazyLoadDelegate().getTokenRulesWithInherited()); + } + forEachSplitRules(t -> ret.add(t)); + return ret; + } + + public Collection getKeywordRulesWithInherited() { + final Collection ret = Sets.newHashSet(); + + for (int i = superGrammars.size() - 1; i >= 0; i--) { + final MCGrammarSymbolSurrogate superGrammarRef = superGrammars.get(i); + + ret.addAll(superGrammarRef.lazyLoadDelegate().getKeywordRulesWithInherited()); + } + forEachNoKeywords(t -> ret.add(t)); + return ret; + } + + public Map> getTokenModesWithInherited() { + // A token may only be assigned to one mode + final Map> ret = Maps.newHashMap(tokenModes); + // allToken is the list of all already assigned characters + Collection allToken = Sets.newHashSet(); + ret.forEach((k,v) -> v.forEach(t -> allToken.add(t))); + for (MCGrammarSymbol superGrammar: getAllSuperGrammars()) { + for (Map.Entry> superMode: superGrammar.getTokenModes().entrySet()) { + Collection superTokenSet; + if (ret.containsKey(superMode.getKey())) { + // the mode already exists + superTokenSet = ret.get(superMode.getKey()); + } else { + superTokenSet = Sets.newHashSet(); + } + superMode.getValue().stream().filter(t ->!allToken.contains(t)).forEach(t ->superTokenSet.add(t)); + if (!superTokenSet.isEmpty()) { + ret.put(superMode.getKey(), superTokenSet); + allToken.addAll(superTokenSet); + } + } + } + return ret; + } + + public Map> getReplacedKeywordsWithInherited() { + final Map> ret = Maps.newHashMap(replacedKeywords); + for (MCGrammarSymbol superGrammar: getAllSuperGrammars()) { + for (Map.Entry> keyword: superGrammar.getAdditionalKeywords().entrySet()) { + Collection superKeywords; + if (ret.containsKey(keyword.getKey())) { + // the keyword already exists + superKeywords = ret.get(keyword.getKey()); + } else { + superKeywords = Sets.newHashSet(); + } + superKeywords.addAll(keyword.getValue()); + ret.put(keyword.getKey(), superKeywords); + } + } + return ret; + } + + public Map> getTokenModes() { + return Maps.newHashMap(tokenModes); + } + + @Deprecated // Use getReplacedKeywords() + public Map> getAdditionalKeywords() { + return Maps.newHashMap(replacedKeywords); + } + + public Map> getReplacedKeywords() { + return Maps.newHashMap(replacedKeywords); + } + + public Optional getAstGrammar() { + return this.astNode; + } + + /** + * Determines dynamically the full name of the symbol. + * + * @return the full name of the symbol determined dynamically + */ + protected String determineFullName() { + if (enclosingScope == null) { + // There should not be a symbol that is not defined in any scope. This case should only + // occur while the symbol is built (by the symbol table creator). So, here the full name + // should not be cached yet. + return name; + } + + final Deque nameParts = new ArrayDeque<>(); + nameParts.addFirst(name); + + Optional optCurrentScope = Optional.of(enclosingScope); + + while (optCurrentScope.isPresent()) { + final IGrammarScope currentScope = optCurrentScope.get(); + if (currentScope.isPresentSpanningSymbol()) { + // If one of the enclosing scope(s) is spanned by a symbol, the full name + // of that symbol is the missing prefix, and hence, the calculation + // ends here. This check is important, since the full name of the enclosing + // symbol might be set manually. + nameParts.addFirst(currentScope.getSpanningSymbol().getFullName()); + break; + } + + if (!(currentScope instanceof IGrammarGlobalScope)) { + if (currentScope instanceof IGrammarArtifactScope) { + // We have reached the artifact scope. Get the package name from the + // symbol itself, since it might be set manually. + if (!getPackageName().isEmpty()) { + nameParts.addFirst(getPackageName()); + } + } else { + if (currentScope.isPresentName()) { + nameParts.addFirst(currentScope.getName()); + } + // ...else stop? If one of the enclosing scopes is unnamed, + // the full name is same as the simple name. + } + optCurrentScope = Optional.ofNullable(currentScope.getEnclosingScope()); + } else { + optCurrentScope = Optional.empty(); + } + } + + return Names.getQualifiedName(nameParts); + } + + protected String determinePackageName() { + Optional optCurrentScope = Optional.ofNullable(enclosingScope); + + while (optCurrentScope.isPresent()) { + final IGrammarScope currentScope = optCurrentScope.get(); + if (currentScope.isPresentSpanningSymbol()) { + // If one of the enclosing scope(s) is spanned by a symbol, take its + // package name. This check is important, since the package name of the + // enclosing symbol might be set manually. + return currentScope.getSpanningSymbol().getPackageName(); + } else if (currentScope instanceof IGrammarArtifactScope) { + return ((IGrammarArtifactScope) currentScope).getPackageName(); + } + + optCurrentScope = Optional.of(currentScope.getEnclosingScope()); + } + + return ""; + } + + public void addMode(String modeName, String tokenName) { + if (tokenModes.containsKey(modeName)) { + tokenModes.get(modeName).add(tokenName); + } else { + tokenModes.put(modeName, Sets.newHashSet(tokenName)); + } + } + + public void addKeywords(String keyword, Collection additionalKeywords) { + if (this.replacedKeywords.containsKey(keyword)) { + this.replacedKeywords.get(keyword).addAll(additionalKeywords); + } else { + this.replacedKeywords.put(keyword, Sets.newHashSet(additionalKeywords)); + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_symboltable/MCGrammarSymbolBuilder.java b/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_symboltable/MCGrammarSymbolBuilder.java new file mode 100644 index 0000000000..c183c28c76 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_symboltable/MCGrammarSymbolBuilder.java @@ -0,0 +1,23 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.grammar._symboltable; + +import java.util.ArrayList; +import java.util.List; + +import static de.se_rwth.commons.logging.Log.errorIfNull; + +public class MCGrammarSymbolBuilder extends MCGrammarSymbolBuilderTOP { + + protected final List superGrammars = new ArrayList<>(); + + public void addSuperGrammar(MCGrammarSymbolSurrogate superGrammarRef) { + this.superGrammars.add(errorIfNull(superGrammarRef)); + } + + public MCGrammarSymbol build(){ + MCGrammarSymbol symbol = super.build(); + superGrammars.forEach(symbol::addSuperGrammar); + return symbol; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_symboltable/ProdSymbol.java b/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_symboltable/ProdSymbol.java new file mode 100644 index 0000000000..4e00e710ff --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_symboltable/ProdSymbol.java @@ -0,0 +1,90 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.grammar._symboltable; + + +import java.util.ArrayList; +import java.util.List; + +import static com.google.common.collect.ImmutableList.copyOf; +import static de.se_rwth.commons.logging.Log.errorIfNull; + +public class ProdSymbol extends ProdSymbolTOP { + + + /** + * A extends B, C = ... + */ + protected final List superProds = new ArrayList<>(); + + /** + * A implements B, C = ... + */ + protected final List superInterfaceProds = new ArrayList<>(); + + /** + * A astextends B, C, external.java.Type + */ + protected List astSuperClasses = new ArrayList<>(); + + /** + * A implements B, C, external.java.Type + */ + protected List astSuperInterfaces = new ArrayList<>(); + + public ProdSymbol(String name) { + super(name); + } + + public List getProdComponents() { + return getSpannedScope().getLocalRuleComponentSymbols(); + } + + public void addSuperProd(ProdSymbolSurrogate superProdRef) { + this.superProds.add(errorIfNull(superProdRef)); + } + + public List getSuperProds() { + return copyOf(superProds); + } + + public void addSuperInterfaceProd(ProdSymbolSurrogate superInterfaceProdRef) { + this.superInterfaceProds.add(errorIfNull(superInterfaceProdRef)); + } + + public List getSuperInterfaceProds() { + return copyOf(superInterfaceProds); + } + + public void addAstSuperClass(ProdSymbolSurrogate ref) { + astSuperClasses.add(errorIfNull(ref)); + } + + public List getAstSuperClasses() { + return copyOf(astSuperClasses); + } + + public void addAstSuperInterface(ProdSymbolSurrogate ref) { + astSuperInterfaces.add(errorIfNull(ref)); + } + + public List getAstSuperInterfaces() { + return copyOf(astSuperInterfaces); + } + + public boolean isParserProd() { + return isClass() || isIsAbstract(); + } + + /** + * @return true, if production is a class production (which is the default) + */ + public boolean isClass() { + return !isIsInterface() && !isIsAbstract() && !isIsExternal() && !isIsEnum() && !isIsLexerProd(); + } + + @Override + public String toString() { + return getName(); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_symboltable/ProdSymbolSurrogate.java b/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_symboltable/ProdSymbolSurrogate.java new file mode 100644 index 0000000000..d434179565 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_symboltable/ProdSymbolSurrogate.java @@ -0,0 +1,25 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.grammar._symboltable; + +import java.util.Optional; + +@Deprecated +public class ProdSymbolSurrogate extends ProdSymbolSurrogateTOP { + + public ProdSymbolSurrogate(String name) { + super(name); + } + + public boolean isSymbolPresent() { + if(!delegate.isPresent()){ + + Optional resolvedSymbol = enclosingScope.resolveProd(name); + + if (resolvedSymbol.isPresent()) { + delegate = Optional.of(resolvedSymbol.get()); + } + } + return delegate.isPresent(); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_symboltable/RuleComponentSymbol.java b/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_symboltable/RuleComponentSymbol.java new file mode 100644 index 0000000000..b626ee11ad --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/grammar/_symboltable/RuleComponentSymbol.java @@ -0,0 +1,28 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.grammar._symboltable; + +import java.util.Optional; + + +public class RuleComponentSymbol extends RuleComponentSymbolTOP { + + public RuleComponentSymbol(String name) { + super(name); + } + + /** + * @return A reference to the defining production of this component, e.g., the + * defining prod for the nonterminal ... = s:A is the production + * A = .... + */ + public Optional getReferencedProd() { + if (isPresentReferencedType()) { + ProdSymbolSurrogate s = new ProdSymbolSurrogate(getReferencedType()); + s.setEnclosingScope(getEnclosingScope()); + return Optional.of(s); + } + return Optional.empty(); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/grammar_withconcepts/FullSynthesizeFromMCSGT4Grammar.java b/monticore-grammar/src/main/java/de/monticore/grammar/grammar_withconcepts/FullSynthesizeFromMCSGT4Grammar.java new file mode 100644 index 0000000000..f277e52555 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/grammar_withconcepts/FullSynthesizeFromMCSGT4Grammar.java @@ -0,0 +1,40 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.grammar_withconcepts; + +import de.monticore.types.check.*; +import de.monticore.types.mcbasictypes._ast.ASTMCQualifiedName; +import de.monticore.types.mcbasictypes._ast.ASTMCReturnType; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.mcsimplegenerictypes.MCSimpleGenericTypesMill; +import de.monticore.types.mcsimplegenerictypes._visitor.MCSimpleGenericTypesTraverser; + +public class FullSynthesizeFromMCSGT4Grammar extends AbstractSynthesize { + + public FullSynthesizeFromMCSGT4Grammar(){ + this(MCSimpleGenericTypesMill.traverser()); + } + + public FullSynthesizeFromMCSGT4Grammar(MCSimpleGenericTypesTraverser traverser){ + super(traverser); + init(traverser); + } + + public void init(MCSimpleGenericTypesTraverser traverser) { + SynthesizeSymTypeFromMCFullGenericTypes synFromFull = new SynthesizeSymTypeFromMCFullGenericTypes(); + synFromFull.setTypeCheckResult(typeCheckResult); + SynthesizeSymTypeFromMCSimpleGenericTypes synFromSimple = new SynthesizeFromMCSGT4Grammar(); + synFromSimple.setTypeCheckResult(typeCheckResult); + SynthesizeSymTypeFromMCCollectionTypes synFromCollection = new SynthesizeSymTypeFromMCCollectionTypes(); + synFromCollection.setTypeCheckResult(typeCheckResult); + SynthesizeSymTypeFromMCBasicTypes synFromBasic = new SynthesizeFromMCBT4Grammar(); + synFromBasic.setTypeCheckResult(typeCheckResult); + + traverser.add4MCSimpleGenericTypes(synFromSimple); + traverser.setMCSimpleGenericTypesHandler(synFromSimple); + traverser.add4MCCollectionTypes(synFromCollection); + traverser.setMCCollectionTypesHandler(synFromCollection); + traverser.add4MCBasicTypes(synFromBasic); + traverser.setMCBasicTypesHandler(synFromBasic); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/grammar_withconcepts/SynthesizeFromMCBT4Grammar.java b/monticore-grammar/src/main/java/de/monticore/grammar/grammar_withconcepts/SynthesizeFromMCBT4Grammar.java new file mode 100644 index 0000000000..8757e4af21 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/grammar_withconcepts/SynthesizeFromMCBT4Grammar.java @@ -0,0 +1,22 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.grammar_withconcepts; + +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbolSurrogate; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types.check.SynthesizeSymTypeFromMCBasicTypes; +import de.monticore.types.mcbasictypes._ast.ASTMCQualifiedName; + +public class SynthesizeFromMCBT4Grammar extends SynthesizeSymTypeFromMCBasicTypes { + + @Override + public void endVisit(ASTMCQualifiedName qName) { + TypeSymbol surrogate = new TypeSymbolSurrogate(qName.getQName()); + surrogate.setEnclosingScope(getScope(qName.getEnclosingScope())); + SymTypeExpression symType = SymTypeExpressionFactory.createTypeObject(surrogate); + + typeCheckResult.setResult(symType); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/grammar_withconcepts/SynthesizeFromMCSGT4Grammar.java b/monticore-grammar/src/main/java/de/monticore/grammar/grammar_withconcepts/SynthesizeFromMCSGT4Grammar.java new file mode 100644 index 0000000000..404b06e628 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/grammar_withconcepts/SynthesizeFromMCSGT4Grammar.java @@ -0,0 +1,22 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.grammar_withconcepts; + +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbolSurrogate; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types.check.SynthesizeSymTypeFromMCSimpleGenericTypes; +import de.monticore.types.mccollectiontypes._ast.ASTMCGenericType; + +import java.util.List; +import java.util.Optional; + +public class SynthesizeFromMCSGT4Grammar extends SynthesizeSymTypeFromMCSimpleGenericTypes { + + @Override + protected SymTypeExpression handleIfNotFound(ASTMCGenericType type, List arguments) { + TypeSymbol surrogate = new TypeSymbolSurrogate(String.join(".", type.getNameList())); + surrogate.setEnclosingScope(getScope(type.getEnclosingScope())); + return SymTypeExpressionFactory.createGenerics(surrogate, arguments); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/grammar_withconcepts/_parser/GrammarTransformer.java b/monticore-grammar/src/main/java/de/monticore/grammar/grammar_withconcepts/_parser/GrammarTransformer.java new file mode 100644 index 0000000000..99d3bb7ec3 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/grammar_withconcepts/_parser/GrammarTransformer.java @@ -0,0 +1,140 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.grammar_withconcepts._parser; + +import de.monticore.grammar.grammar._ast.*; +import de.monticore.grammar.grammar_withconcepts.Grammar_WithConceptsMill; +import de.monticore.grammar.grammar_withconcepts._visitor.Grammar_WithConceptsTraverser; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.mccollectiontypes._ast.ASTMCGenericType; +import de.monticore.types.mcsimplegenerictypes.MCSimpleGenericTypesMill; +import de.se_rwth.commons.Names; +import de.se_rwth.commons.StringTransformations; +import de.se_rwth.commons.logging.Log; + +import java.io.IOException; +import java.io.StringReader; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; + + +/** + * Static facade for the transformation of MC AST. + */ +public class GrammarTransformer { + + private GrammarTransformer() { + // noninstantiable + } + + public static void transform(ASTMCGrammar grammar) { + removeNonTerminalSeparators(grammar); + uncapitalizeMultivaluedAttributes(grammar); + } + + /** + * The shortcut NonTerminalSeparator is replaced by the detailed description. + * Example: List(Element || ',')* ==> (List:Element (',' List:Element)+) + */ + public static void removeNonTerminalSeparators(ASTMCGrammar grammar) { + Map map = new HashMap(); + RuleComponentListFinder componentListTransformer = new RuleComponentListFinder(map); + + Grammar_WithConceptsTraverser traverser = Grammar_WithConceptsMill.traverser(); + traverser.add4Grammar(componentListTransformer); + // execute the search + grammar.accept(traverser); + + // execute the transformation + for (Entry entry : map.entrySet()) { + Log.debug("Find NonTerminalSeparator", GrammarTransformer.class.getName()); + Optional block = transform(entry.getKey()); + if (block.isPresent()) { + ASTAlt parent = entry.getValue(); + int ind = parent.getComponentList().indexOf(entry.getKey()); + if (ind >= 0) { + Log.debug("Remove NonTerminalSeparator", GrammarTransformer.class.getName()); + parent.getComponentList().remove(ind); + Log.debug("Added new generated block", GrammarTransformer.class.getName()); + parent.getComponentList().add(ind, block.get()); + } else { + Log.error("0xA1009 Can't transform grammar"); + } + } + } + } + + /** + * Append suffix "List" to the names of multi-valued att * Append suffix "List" to + * the names of multi-valued attributes (NonTerminals and attributesinAst) if + * no usage names were set. Examples: Name ("." Name&)* ==> names:Name ("." + * names:Name&)* (State | Transition)* ==> (states:State | + * transitions:Transition)* + */ + public static void uncapitalizeMultivaluedAttributes(ASTMCGrammar grammar) { + grammar.getASTRuleList().forEach(c -> transformAttributesInAST(c)); + } + + protected static String simpleName(ASTMCType type) { + String name; + if (type instanceof ASTMCGenericType) { + name = ((ASTMCGenericType) type).printWithoutTypeArguments(); + } else { + name = type.printType(); + } + return Names.getSimpleName(name); + } + + protected static void transformAttributesInAST(ASTASTRule astRule) { + astRule.getAdditionalAttributeList().forEach( + attributeInAST -> { + if (!attributeInAST.isPresentName()) { + String simpleName = simpleName(attributeInAST.getMCType()); + String typeName = simpleName.startsWith("AST") ? + simpleName.replaceFirst("AST", "") : simpleName; + attributeInAST.setName(StringTransformations.uncapitalize(typeName)); + Log.debug("Change the name of ast-rule " + astRule.getType() + + " list-attribute: " + attributeInAST.getMCType(), + GrammarTransformer.class.getName()); + } + }); + } + + /** + * @param nonTerminalSep + * @return + */ + protected static Optional transform(ASTNonTerminalSeparator nonTerminalSep) { + String name = ""; + if (nonTerminalSep.isPresentUsageName()) { + name = nonTerminalSep.getUsageName() + ":"; + } + String referencedSymbol = nonTerminalSep.isPresentReferencedSymbol() ? "@" + nonTerminalSep.getReferencedSymbol() : ""; + String plusKeywords = (nonTerminalSep.isPlusKeywords()) ? "&" : ""; + String iteration = (nonTerminalSep.getIteration() == ASTConstantsGrammar.STAR) ? "?" : ""; + + String extendedList = "(%usageName% %nonTerminal% %refSymbol% %plusKeywords% (\"%terminal%\" %usageName% %nonTerminal% %refSymbol% %plusKeywords%)*)%iterator%"; + extendedList = extendedList.replaceAll("%usageName%", name); + extendedList = extendedList.replaceAll("%nonTerminal%", nonTerminalSep.getName()); + extendedList = extendedList.replaceAll("%refSymbol%", referencedSymbol); + extendedList = extendedList.replaceAll("%plusKeywords%", plusKeywords); + extendedList = extendedList.replaceAll("%terminal%", nonTerminalSep.getSeparator()); + extendedList = extendedList.replaceAll("%iterator%", iteration); + + Grammar_WithConceptsParser parser = Grammar_WithConceptsMill.parser(); + Optional block = null; + try { + Log.debug("Create ast for " + extendedList, GrammarTransformer.class.getName()); + block = parser.parseBlock(new StringReader(extendedList)); + if (parser.hasErrors()) { + Log.error("0xA0362 RecognitionException during parsing " + extendedList); + } + } catch (IOException e) { + Log.error("0xA0361 IOException during parsing " + extendedList); + } + return block; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/grammar_withconcepts/_parser/Grammar_WithConceptsParser.java b/monticore-grammar/src/main/java/de/monticore/grammar/grammar_withconcepts/_parser/Grammar_WithConceptsParser.java new file mode 100644 index 0000000000..b7b8ae0edf --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/grammar_withconcepts/_parser/Grammar_WithConceptsParser.java @@ -0,0 +1,74 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.grammar_withconcepts._parser; + +import com.google.common.io.Files; +import de.monticore.antlr4.MCConcreteParser; +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.se_rwth.commons.Names; +import de.se_rwth.commons.logging.Log; + +import java.io.IOException; +import java.io.Reader; +import java.nio.file.Paths; +import java.util.Optional; + +public class Grammar_WithConceptsParser extends Grammar_WithConceptsParserTOP { + + @Override + public Optional parse_String(String str) throws IOException { + Optional grammar = super.parse_String(str); + if (grammar.isPresent()) { + GrammarTransformer.transform(grammar.get()); + } + return grammar; + } + + @Override + public Optional parse(Reader reader) throws IOException { + Optional grammar = super.parse(reader); + if (grammar.isPresent()) { + GrammarTransformer.transform(grammar.get()); + } + return grammar; + } + + /** + * @see MCConcreteParser#parse(String) + */ + @Override + public Optional parse(String fileName) throws IOException { + Grammar_WithConceptsParser parser = new Grammar_WithConceptsParser(); + Optional ast = parseMCGrammar(fileName); + if (ast.isPresent()) { + + // Use pathName instead of filename (because of correct separators) + String pathName = Paths.get(fileName).toString(); + String simpleFileName = Files.getNameWithoutExtension(fileName); + String modelName = ast.get().getName(); + String packageName = Names.getPathFromFilename(pathName); + String packageDeclaration = Names.getPathFromPackage(Names.getQualifiedName(ast.get().getPackageList())); + if (!modelName.equals(simpleFileName)) { + Log.error("0xA4003 The grammar name " + modelName + " must be identical to the file name " + + simpleFileName + " of " + + "the grammar (without its file extension)."); + } + + if(!packageName.endsWith(packageDeclaration)){ + Log.error("0xA4004 The package declaration " + Names.getQualifiedName(ast.get().getPackageList()) + " of the grammar must not differ from the " + + "package of the grammar file."); + } + + // Transform + GrammarTransformer.transform(ast.get()); + } + + Optional result = Optional.empty(); + if (ast.isPresent()) { + result = Optional.of(ast.get()); + } + return result; + } + +} + diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/grammar_withconcepts/_parser/RuleComponentListFinder.java b/monticore-grammar/src/main/java/de/monticore/grammar/grammar_withconcepts/_parser/RuleComponentListFinder.java new file mode 100644 index 0000000000..02360f9866 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/grammar_withconcepts/_parser/RuleComponentListFinder.java @@ -0,0 +1,34 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.grammar_withconcepts._parser; + +import de.monticore.grammar.grammar._ast.ASTAlt; +import de.monticore.grammar.grammar._ast.ASTNonTerminalSeparator; +import de.monticore.grammar.grammar._ast.ASTRuleComponent; +import de.monticore.grammar.grammar._visitor.GrammarVisitor2; + +import java.util.Map; + +public class RuleComponentListFinder implements GrammarVisitor2 { + + protected Map map; + + /** + * Constructor for de.monticore.grammar.grammar_withconcepts._parser.RuleComponentListFinder. + * @param map + */ + public RuleComponentListFinder(Map map) { + super(); + this.map = map; + } + + public void visit(ASTAlt alt) { + for (ASTRuleComponent component: alt.getComponentList()) { + if (component instanceof ASTNonTerminalSeparator) { + map.put((ASTNonTerminalSeparator) component, alt); + } + } + } + + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/grammar_withconcepts/_symboltable/Grammar_WithConceptsGlobalScope.java b/monticore-grammar/src/main/java/de/monticore/grammar/grammar_withconcepts/_symboltable/Grammar_WithConceptsGlobalScope.java new file mode 100644 index 0000000000..a53b7aadf8 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/grammar_withconcepts/_symboltable/Grammar_WithConceptsGlobalScope.java @@ -0,0 +1,71 @@ +/* generated from model Grammar_WithConcepts */ +/* generated by template core.Class*/ + +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.grammar_withconcepts._symboltable; + +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar_withconcepts.Grammar_WithConceptsMill; +import de.monticore.io.FileReaderWriter; +import de.monticore.io.paths.MCPath; +import de.se_rwth.commons.Names; +import de.se_rwth.commons.logging.Log; + +import java.io.IOException; +import java.io.Reader; +import java.net.URL; +import java.nio.file.Paths; +import java.util.Optional; + + +public class Grammar_WithConceptsGlobalScope extends Grammar_WithConceptsGlobalScopeTOP { + public Grammar_WithConceptsGlobalScope(MCPath symbolPath, String modelFileExtension) { + super(symbolPath, modelFileExtension); + } + + public Grammar_WithConceptsGlobalScope() { + super(); + } + + @Override + public Grammar_WithConceptsGlobalScope getRealThis() { + return this; + } + + @Override + public void loadFileForModelName (String modelName) { + // 1. call super implementation to start with employing the DeSer + // super.loadFileForModelName(modelName); + + String filePath = Paths + .get(Names.getPathFromPackage(modelName) + ".mc4").toString(); + + + if (!isFileLoaded(filePath)) { + + // 2. calculate potential location of model file and try to find it in model path + Optional url = getSymbolPath().find(Names.getPathFromPackage(modelName)+".mc4"); + + // 3. if the file was found, parse the model and create its symtab + if (url.isPresent()) { + ASTMCGrammar ast = parse(url.get()); + IGrammar_WithConceptsArtifactScope artScope = new Grammar_WithConceptsPhasedSTC().createFromAST(ast); + addSubScope(artScope); + addLoadedFile(filePath); + } + } + } + + protected ASTMCGrammar parse(URL url){ + try (Reader reader = FileReaderWriter.getReader(url)){ + Optional optAST = Grammar_WithConceptsMill.parser().parse(reader); + if(optAST.isPresent()){ + return optAST.get(); + } + } + catch (IOException e) { + Log.error("0x1A236 Error while parsing model", e); + } + return null; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/grammar_withconcepts/_symboltable/Grammar_WithConceptsPhasedSTC.java b/monticore-grammar/src/main/java/de/monticore/grammar/grammar_withconcepts/_symboltable/Grammar_WithConceptsPhasedSTC.java new file mode 100644 index 0000000000..57b4b51bb5 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/grammar_withconcepts/_symboltable/Grammar_WithConceptsPhasedSTC.java @@ -0,0 +1,41 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.grammar_withconcepts._symboltable; + +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._symboltable.GrammarSTCompleteTypes; +import de.monticore.grammar.grammar_withconcepts.Grammar_WithConceptsMill; +import de.monticore.grammar.grammar_withconcepts._visitor.Grammar_WithConceptsTraverser; +import de.monticore.javalight._symboltable.JavaLightSTCompleteTypes; +import de.monticore.statements.mccommonstatements._symboltable.MCCommonStatementsSTCompleteTypes; +import de.monticore.statements.mcvardeclarationstatements._symboltable.MCVarDeclarationStatementsSTCompleteTypes; + +import java.util.ArrayList; +import java.util.List; + +public class Grammar_WithConceptsPhasedSTC { + + protected IGrammar_WithConceptsGlobalScope globalScope; + + protected Grammar_WithConceptsScopesGenitorDelegator scopesGenitorDelegator; + + protected List priorityList; + + public Grammar_WithConceptsPhasedSTC(){ + this.globalScope = Grammar_WithConceptsMill.globalScope(); + this.scopesGenitorDelegator = Grammar_WithConceptsMill.scopesGenitorDelegator(); + this.priorityList = new ArrayList<>(); + Grammar_WithConceptsTraverser traverser = Grammar_WithConceptsMill.traverser(); + traverser.add4Grammar(new GrammarSTCompleteTypes()); + traverser.add4JavaLight(new JavaLightSTCompleteTypes()); + traverser.add4MCCommonStatements(new MCCommonStatementsSTCompleteTypes()); + traverser.add4MCVarDeclarationStatements(new MCVarDeclarationStatementsSTCompleteTypes()); + priorityList.add(traverser); + } + + public IGrammar_WithConceptsArtifactScope createFromAST(ASTMCGrammar node){ + IGrammar_WithConceptsArtifactScope as = scopesGenitorDelegator.createFromAST(node); + priorityList.forEach(node::accept); + return as; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/grammar_withconcepts/_symboltable/Grammar_WithConceptsScopesGenitor.java b/monticore-grammar/src/main/java/de/monticore/grammar/grammar_withconcepts/_symboltable/Grammar_WithConceptsScopesGenitor.java new file mode 100644 index 0000000000..6aa85ad7f2 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/grammar_withconcepts/_symboltable/Grammar_WithConceptsScopesGenitor.java @@ -0,0 +1,38 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.grammar_withconcepts._symboltable; + +import de.monticore.symboltable.ImportStatement; +import de.se_rwth.commons.Names; +import de.se_rwth.commons.logging.Log; + +import java.util.ArrayList; +import java.util.Deque; +import java.util.List; +import java.util.Optional; + +public class Grammar_WithConceptsScopesGenitor extends Grammar_WithConceptsScopesGenitorTOP { + + public Grammar_WithConceptsScopesGenitor() { + super(); + } + + /** + * Creates the symbol table starting from the rootNode and + * returns the first scope that was created. + * + * @param rootNode the root node + * @return the first scope that was created + */ + public Grammar_WithConceptsArtifactScope createFromAST(de.monticore.grammar.grammar._ast.ASTMCGrammar rootNode) { + Log.errorIfNull(rootNode, "0xA7FE4 Error by creating of the Grammar_WithConceptsScopesGenitor symbol table: top ast node is null"); + List imports = new ArrayList<>(); + rootNode.getImportStatementList().stream().forEach(i -> imports.add(new ImportStatement(i.getQName(), i.isStar()))); + Grammar_WithConceptsArtifactScope artifactScope = new Grammar_WithConceptsArtifactScope(Optional.empty(), Names.getQualifiedName(rootNode.getPackageList()), imports); + artifactScope.setName(rootNode.getName()); + putOnStack(artifactScope); + rootNode.accept(getTraverser()); + return artifactScope; + } + + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/grammar_withconcepts/_symboltable/Grammar_WithConceptsScopesGenitorDelegator.java b/monticore-grammar/src/main/java/de/monticore/grammar/grammar_withconcepts/_symboltable/Grammar_WithConceptsScopesGenitorDelegator.java new file mode 100644 index 0000000000..eac1dac4dc --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/grammar_withconcepts/_symboltable/Grammar_WithConceptsScopesGenitorDelegator.java @@ -0,0 +1,27 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.grammar_withconcepts._symboltable; + +public class Grammar_WithConceptsScopesGenitorDelegator extends Grammar_WithConceptsScopesGenitorDelegatorTOP { + + public Grammar_WithConceptsScopesGenitorDelegator() { + super(); + } + + /** + * Creates the symbol table starting from the rootNode and + * returns the first scope that was created. + * + * @param rootNode the root node + * @return the first scope that was created + */ + public Grammar_WithConceptsArtifactScope createFromAST(de.monticore.grammar.grammar._ast.ASTMCGrammar rootNode) { + Grammar_WithConceptsArtifactScope as = symbolTable.createFromAST(rootNode); + if (!as.getPackageName().isEmpty()){ + globalScope.addLoadedFile(as.getPackageName() + "." + as.getName()); + } else { + globalScope.addLoadedFile(as.getName()); + } + return as; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/prettyprint/AntlrPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/grammar/prettyprint/AntlrPrettyPrinter.java new file mode 100644 index 0000000000..89b9a031ef --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/prettyprint/AntlrPrettyPrinter.java @@ -0,0 +1,75 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.prettyprint; + +import de.monticore.grammar.concepts.antlr.antlr._ast.ASTAntlrLexerAction; +import de.monticore.grammar.concepts.antlr.antlr._ast.ASTAntlrNode; +import de.monticore.grammar.concepts.antlr.antlr._ast.ASTAntlrParserAction; +import de.monticore.grammar.concepts.antlr.antlr._visitor.AntlrHandler; +import de.monticore.grammar.concepts.antlr.antlr._visitor.AntlrTraverser; +import de.monticore.grammar.concepts.antlr.antlr._visitor.AntlrVisitor2; +import de.monticore.prettyprint.CommentPrettyPrinter; +import de.monticore.prettyprint.IndentPrinter; + +@Deprecated(forRemoval = true) +public class AntlrPrettyPrinter implements AntlrVisitor2, AntlrHandler { + + // printer to use + protected IndentPrinter printer = null; + + protected AntlrTraverser traverser; + + public AntlrPrettyPrinter(IndentPrinter out) { + printer = out; + } + + @Override + public void handle(ASTAntlrLexerAction a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + getPrinter().print("lexerjava "); + getPrinter().print(" {"); + getPrinter().println(); + getPrinter().indent(); + a.getText().accept(getTraverser()); + getPrinter().unindent(); + getPrinter().print("}"); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTAntlrParserAction a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + getPrinter().print("parserjava "); + getPrinter().print(" {"); + getPrinter().println(); + getPrinter().indent(); + a.getText().accept(getTraverser()); + getPrinter().unindent(); + getPrinter().print("}"); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + public String prettyprint(ASTAntlrNode a) { + getPrinter().clearBuffer(); + a.accept(getTraverser()); + return getPrinter().getContent(); + } + + public IndentPrinter getPrinter() { + return printer; + } + + public void setPrinter(IndentPrinter printer) { + this.printer = printer; + } + + @Override + public AntlrTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(AntlrTraverser traverser) { + this.traverser = traverser; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/prettyprint/GrammarPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/grammar/prettyprint/GrammarPrettyPrinter.java new file mode 100644 index 0000000000..02c7f68097 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/prettyprint/GrammarPrettyPrinter.java @@ -0,0 +1,1193 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.prettyprint; + +import de.monticore.grammar.grammar._ast.*; +import de.monticore.grammar.grammar._visitor.GrammarHandler; +import de.monticore.grammar.grammar._visitor.GrammarTraverser; +import de.monticore.grammar.grammar._visitor.GrammarVisitor2; +import de.monticore.prettyprint.CommentPrettyPrinter; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.types.mcbasictypes._ast.ASTMCBasicTypesNode; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.se_rwth.commons.Names; + +import java.util.Iterator; + +@Deprecated(forRemoval = true) +public class GrammarPrettyPrinter implements GrammarVisitor2, GrammarHandler { + + protected static final String QUOTE = "\""; + + protected GrammarTraverser traverser; + + protected IndentPrinter printer; + + public GrammarPrettyPrinter(IndentPrinter printer) { + this.printer = printer; + printer.setIndentLength(2); + } + + @Override + public void handle(ASTSemanticpredicateOrAction a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + + if (a.isPresentExpressionPredicate()) { + print(" {"); + getPrinter().println(); + getPrinter().indent(); + a.getExpressionPredicate().accept(getTraverser()); + getPrinter().unindent(); + print("}"); + print(" ?"); + } + if (a.isPresentAction()) { + print(" {"); + getPrinter().println(); + getPrinter().indent(); + a.getAction().accept(getTraverser()); + getPrinter().unindent(); + print("}"); + } + + getPrinter().print(" "); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + + } + + @Override + public void handle(ASTExternalProd a) { + + CommentPrettyPrinter.printPreComments(a, getPrinter()); + printList(a.getGrammarAnnotationList().iterator(), ""); + print("external "); + + printList(a.getSymbolDefinitionList().iterator(), " "); + getPrinter().print(a.getName()); + + if (a.isPresentMCType()) { + a.getMCType().accept(getTraverser()); + } + getPrinter().print(";"); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + getPrinter().println(); + + } + + @Override + public void handle(ASTGrammarOption a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + println("options {"); + getPrinter().indent(); + + printList(a.getFollowOptionList().iterator(), ""); + + getPrinter().unindent(); + print("}"); + + CommentPrettyPrinter.printPostComments(a, getPrinter()); + + println(); + println(); + + } + + @Override + public void handle(ASTNonTerminal a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + + if (a.isPresentUsageName()) { + print("" + a.getUsageName() + ":"); + } + + print(a.getName()); + if (a.isPresentReferencedSymbol()) { + print("@"); + print(a.getReferencedSymbol()); + } + + if (a.isGenSymbol()) { + print("!!"); + if (a.isPresentSymbolName()) { + print(a.getSymbolName()); + } + } + + if (a.isPlusKeywords()) { + print("& "); + } + + outputIteration(a.getIteration()); + + CommentPrettyPrinter.printPostComments(a, getPrinter()); + + } + + @Override + public void handle(ASTKeyTerminal a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + if (a.isPresentUsageName()) { + print("" + a.getUsageName() + ":"); + } + a.getKeyConstant().accept(getTraverser()); + outputIteration(a.getIteration()); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTKeyConstant a) { + print(" key("); + String sep = ""; + for (String name: a.getStringList()) { + print(sep); + print("\"" + name + "\""); + sep = " | "; + } + print(")"); + } + + @Override + public void handle(ASTTokenTerminal a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + if (a.isPresentUsageName()) { + print("" + a.getUsageName() + ":"); + } + a.getTokenConstant().accept(getTraverser()); + outputIteration(a.getIteration()); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTTokenConstant a) { + print(" token("); + print(a.getString()); + print(")"); + } + + @Override + public void handle(ASTSplitRule a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + print("split_token "); + String sep = ""; + for (String s: a.getStringList()) { + print(sep); + sep = ", "; + print(s); + } + println (";"); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTReplaceRule a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + print("additionalkeyword "); + print(a.getKeyword()); + String sep = ""; + for (String s: a.getReplacedKeyWordList()) { + print(sep); + sep = ", "; + print(s); + } + println (";"); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTKeywordRule a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + print("nokeyword "); + String sep = ""; + for (String s: a.getStringList()) { + print(sep); + sep = ", "; + print("\""); + print(s); + print("\""); + } + println (";"); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTTerminal a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + // output("ASTTerminal Iteration " + a.getIteration()); + if (a.isPresentUsageName()) { + print("" + a.getUsageName() + ":"); + } + print("\"" + a.getName() + "\""); + outputIteration(a.getIteration()); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTBlock a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + print("("); + + if (a.isPresentOption()) { + print("options {"); + + for (ASTOptionValue x : a.getOption().getOptionValueList()) { + print(x.getKey() + "=" + x.getValue() + ";"); + } + + print("} "); + } + + if (a.isPresentInitAction()) { + getPrinter().print("init "); + print(" {"); + getPrinter().println(); + getPrinter().indent(); + a.getInitAction().accept(getTraverser()); + getPrinter().unindent(); + print("}"); + } + + if (a.isPresentInitAction() || a.isPresentOption()) { + print(": "); + } + + printList(a.getAltList().iterator(), "| "); + print(")"); + outputIteration(a.getIteration()); + + CommentPrettyPrinter.printPostComments(a, getPrinter()); + + getPrinter().optionalBreak(); + + } + + /** + * Visiting an ASTConcept #not sure for complete children methods + * + * @param a + */ + @Override + public void handle(ASTConcept a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + println("concept " + a.getName() + "{ "); + + a.getConcept().accept(getTraverser()); + + CommentPrettyPrinter.printPostComments(a, getPrinter()); + println("}"); + } + + /** + * #complete children calls + * + * @param a + */ + @Override + public void handle(ASTConstant a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + if (a.isPresentUsageName()) { + print(a.getUsageName() + ":"); + } + if (a.isPresentKeyConstant()) { + a.getKeyConstant().accept(getTraverser()); + }else if (a.isPresentTokenConstant()) { + a.getTokenConstant().accept(getTraverser()); + } else { + print(QUOTE + a.getName() + QUOTE); + } + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + /** + * #complete children calls is usagename ever used?? + * + * @param a + */ + @Override + public void handle(ASTConstantGroup a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + if (a.isPresentUsageName()) { + print(a.getUsageName()); + print(":"); + } + print("["); + printList(a.getConstantList().iterator(), " | "); + print("]"); + outputIteration(a.getIteration()); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + /** + * #complete children calls + * + * @param a + */ + @Override + public void handle(ASTAlt a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + if (a.isRightAssoc()) { + getPrinter().print(" "); + } + if (a.isPresentGrammarAnnotation()) { + a.getGrammarAnnotation().accept(getTraverser()); + } + printList(a.getComponentList().iterator(), " "); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTInterfaceProd a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + + printList(a.getGrammarAnnotationList().iterator(), ""); + print("interface "); + + printList(a.getSymbolDefinitionList().iterator(), " "); + + print(a.getName()); + + if (!a.getSuperInterfaceRuleList().isEmpty()) { + getPrinter().print(" extends "); + String comma = ""; + for (ASTRuleReference x : a.getSuperInterfaceRuleList()) { + getPrinter().print(comma); + x.accept(getTraverser()); + comma = ", "; + } + } + + if (!a.getASTSuperInterfaceList().isEmpty()) { + getPrinter().print(" astextends "); + String comma = ""; + for (ASTMCType x : a.getASTSuperInterfaceList()) { + getPrinter().print(comma); + x.accept(getTraverser()); + comma = ", "; + } + } + + if (!a.getAltList().isEmpty()) { + println(" ="); + + getPrinter().indent(); + printList(a.getAltList().iterator(), " | "); + } + + getPrinter().print(";"); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + getPrinter().println(); + getPrinter().println(); + + } + + @Override + public void handle(ASTEnumProd a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + printList(a.getGrammarAnnotationList().iterator(), ""); + print("enum "); + print(a.getName()); + + getPrinter().print(" = "); + String sep = ""; + for (ASTConstant ref : a.getConstantList()) { + print(sep); + ref.accept(getTraverser()); + sep = " | "; + } + getPrinter().print(" "); + + getPrinter().print(";"); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + getPrinter().println(); + getPrinter().println(); + + } + + @Override + public void handle(ASTASTRule a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + print("astrule "); + + print(a.getType()); + + if (!a.getASTSuperClassList().isEmpty()) { + getPrinter().print(" astextends "); + String comma = ""; + for (ASTMCType x : a.getASTSuperClassList()) { + getPrinter().print(comma); + x.accept(getTraverser()); + comma = ", "; + } + } + + if (!a.getASTSuperInterfaceList().isEmpty()) { + getPrinter().print(" astimplements "); + String comma = ""; + for (ASTMCType x : a.getASTSuperInterfaceList()) { + getPrinter().print(comma); + x.accept(getTraverser()); + comma = ", "; + } + } + + if (!a.getGrammarMethodList().isEmpty() || !a.getAdditionalAttributeList().isEmpty()) { + + println(" = "); + getPrinter().indent(); + printList(a.getAdditionalAttributeList().iterator(), ""); + printList(a.getGrammarMethodList().iterator(), ""); + } + + getPrinter().print(";"); + getPrinter().unindent(); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + getPrinter().println(); + + } + + @Override + public void handle(ASTSymbolRule a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + print("symbolrule "); + + print(a.getType()); + + if (!a.getSuperClassList().isEmpty()) { + getPrinter().print(" extends "); + String comma = ""; + for (ASTMCType x : a.getSuperClassList()) { + getPrinter().print(comma); + x.accept(getTraverser()); + comma = ", "; + } + } + + if (!a.getSuperInterfaceList().isEmpty()) { + getPrinter().print(" astimplements "); + String comma = ""; + for (ASTMCType x : a.getSuperInterfaceList()) { + getPrinter().print(comma); + x.accept(getTraverser()); + comma = ", "; + } + } + + if (!a.getGrammarMethodList().isEmpty() || !a.getAdditionalAttributeList().isEmpty()) { + + println(" = "); + getPrinter().indent(); + printList(a.getAdditionalAttributeList().iterator(), ""); + printList(a.getGrammarMethodList().iterator(), ""); + } + + getPrinter().print(";"); + getPrinter().unindent(); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + getPrinter().println(); + getPrinter().println(); + + } + + @Override + public void handle(ASTScopeRule a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + print("scoperule "); + + if (!a.getSuperClassList().isEmpty()) { + getPrinter().print(" extends "); + String comma = ""; + for (ASTMCType x : a.getSuperClassList()) { + getPrinter().print(comma); + x.accept(getTraverser()); + comma = ", "; + } + } + + if (!a.getSuperInterfaceList().isEmpty()) { + getPrinter().print(" astimplements "); + String comma = ""; + for (ASTMCType x : a.getSuperInterfaceList()) { + getPrinter().print(comma); + x.accept(getTraverser()); + comma = ", "; + } + } + + if (!a.getGrammarMethodList().isEmpty() || !a.getAdditionalAttributeList().isEmpty()) { + + println(" = "); + getPrinter().indent(); + printList(a.getAdditionalAttributeList().iterator(), ""); + printList(a.getGrammarMethodList().iterator(), ""); + } + + getPrinter().print(";"); + getPrinter().unindent(); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + getPrinter().println(); + getPrinter().println(); + + } + + @Override + public void handle(ASTGrammarMethod a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + print("method "); + + if (a.isFinal()) { + print("final "); + } + if (a.isStatic()) { + print("static "); + } + if (a.isPrivate()) { + print("private "); + } + if (a.isPublic()) { + print("public "); + } + if (a.isProtected()) { + print("protected "); + } + + a.getMCReturnType().accept(getTraverser()); + + print(" " + a.getName() + "("); + + String comma = ""; + for (ASTMethodParameter x : a.getMethodParameterList()) { + getPrinter().print(comma); + x.getType().accept(getTraverser()); + getPrinter().print(" " + x.getName()); + comma = ", "; + } + + print(")"); + + if (!a.getExceptionList().isEmpty()) { + + print("throws "); + comma = ""; + for (ASTMCType x : a.getExceptionList()) { + getPrinter().print(comma); + x.accept(getTraverser()); + comma = ", "; + } + + } + + // a.getBody()); + print(" {"); + getPrinter().println(); + getPrinter().indent(); + a.getBody().accept(getTraverser()); + getPrinter().unindent(); + print("}"); + + CommentPrettyPrinter.printPostComments(a, getPrinter()); + getPrinter().println(); + + } + + @Override + public void handle(ASTNonTerminalSeparator node) { + if (node.isPresentUsageName()) { + getPrinter().print(node.getUsageName()); + getPrinter().print(":"); + } + getPrinter().print(" ("); + getPrinter().print(node.getName()); + if (node.isPresentReferencedSymbol()) { + getPrinter().print("@"); + getPrinter().print(node.getReferencedSymbol()); + } + if (node.isPlusKeywords()) { + getPrinter().print("&"); + } + getPrinter().print(" || \""); + getPrinter().print(node.getSeparator()); + getPrinter().print("\" )"); + outputIteration(node.getIteration()); + } + + @Override + public void visit(ASTMethodParameter a) { + a.accept(getTraverser()); + print(a.getName()); + } + + @Override + public void handle(ASTAdditionalAttribute a) { + + if (a.isPresentName()) { + getPrinter().print(a.getName()); + getPrinter().print(":"); + } + a.getMCType().accept(getTraverser()); + if (a.isPresentCard()) { + ASTCard card = a.getCard(); + if (card.getIteration() != ASTConstantsGrammar.DEFAULT) { + outputIteration(card.getIteration()); + } + if (card.isPresentMin()) { + print(" min = " + card.getMin()); + } + if (card.isPresentMax()) { + print(" max = " + card.getMax()); + } + } + println(); + } + + /** + * Visiting an ASTRule #complete children calls + * + * @param a + */ + @Override + public void handle(ASTClassProd a) { + + CommentPrettyPrinter.printPreComments(a, getPrinter()); + printList(a.getGrammarAnnotationList().iterator(), ""); + printList(a.getSymbolDefinitionList().iterator(), " "); + getPrinter().print(a.getName()); + + if (!a.getSuperRuleList().isEmpty()) { + getPrinter().print(" extends "); + printList(a.getSuperRuleList().iterator(), " "); + } + + if (!a.getSuperInterfaceRuleList().isEmpty()) { + getPrinter().print(" implements "); + printList(a.getSuperInterfaceRuleList().iterator(), ", "); + } + + if (!a.getASTSuperClassList().isEmpty()) { + getPrinter().print(" astextends "); + printMCSimpleGenericList(a.getASTSuperClassList().iterator(), ""); + } + + if (!a.getASTSuperInterfaceList().isEmpty()) { + getPrinter().print(" astimplements "); + printMCSimpleGenericList(a.getASTSuperInterfaceList().iterator(), ", "); + } + + if (a.isPresentAction()) { + print(" {"); + getPrinter().println(); + getPrinter().indent(); + a.getAction().accept(getTraverser()); + getPrinter().unindent(); + print("}"); + } + + if (!a.getAltList().isEmpty()) { + println(" ="); + + getPrinter().indent(); + printList(a.getAltList().iterator(), " | "); + } + println(";"); + + CommentPrettyPrinter.printPostComments(a, getPrinter()); + getPrinter().unindent(); + getPrinter().println(); + } + + /** + * Visiting a LexRule #complete children calls + * + * @param a the LexRule + */ + @Override + public void handle(ASTLexProd a) { + + printList(a.getGrammarAnnotationList().iterator(), ""); + + if (a.isFragment()) { + print("fragment "); + } + CommentPrettyPrinter.printPreComments(a, getPrinter()); + print("token "); + + println(a.getName()); + getPrinter().indent(); + + if (a.isPresentMode()) { + print("("); + print(a.getMode()); + print(")"); + } + + if (a.isPresentLexOption()) { + a.getLexOption().accept(getTraverser()); + } + if (a.isPresentInitAction()) { + print(" {"); + getPrinter().println(); + getPrinter().indent(); + a.getInitAction().accept(getTraverser()); + getPrinter().unindent(); + print("}"); + } + + getPrinter().print("="); + + printList(a.getAltList().iterator(), " | "); + + if (a.isPresentLexerCommand() || a.isPresentEndAction() || a.isPresentVariable()) { + getPrinter().print(" : "); + + if(a.isPresentLexerCommand()) { + getPrinter().print("->"); + getPrinter().print(a.getLexerCommand()); + } + + if (a.isPresentEndAction()) { + print(" {"); + getPrinter().println(); + getPrinter().indent(); + a.getEndAction().accept(getTraverser()); + getPrinter().unindent(); + print("}"); + } + + if (a.isPresentVariable()) { + getPrinter().print(a.getVariable()); + + if (!a.getTypeList().isEmpty()) { + getPrinter().print("->"); + getPrinter().print(Names.getQualifiedName(a.getTypeList())); + + if (a.isPresentBlock()) { + getPrinter().print(":"); + if (a.isPresentBlock()) { + print(" {"); + getPrinter().println(); + getPrinter().indent(); + a.getBlock().accept(getTraverser()); + getPrinter().unindent(); + print("}"); + } + } + + } + + } + + + } + + + print(";"); + + CommentPrettyPrinter.printPostComments(a, getPrinter()); + + println(); + getPrinter().unindent(); + println(); + } + + @Override + public void handle(ASTLexBlock a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + if (a.isNegate()) { + getPrinter().print("~ "); + } + + print("("); + if (a.isPresentOption()) { + print("options {"); + print(a.getOption().getID() + "=" + a.getOption().getValue() + ";"); + print("} "); + } + + if (a.isPresentInitAction()) { + getPrinter().print("init "); + print(" {"); + getPrinter().println(); + getPrinter().indent(); + a.getInitAction().accept(getTraverser()); + getPrinter().unindent(); + print("}"); + } + + if (a.isPresentInitAction() || a.isPresentOption()) { + print(": "); + } + + printList(a.getLexAltList().iterator(), " | "); + print(")"); + outputIteration(a.getIteration()); + + CommentPrettyPrinter.printPostComments(a, getPrinter()); + + getPrinter().optionalBreak(); + + } + + /** + * Visit method for the ASTGrammar (the root object) we have to use the handle method because + * neither the visit/endVisit nor the traverseOrder merhods allow us to visit Packagename before + * the AstGrammar itself #complete children calls + * + * @param a The ASTGrammar + */ + @Override + public void handle(ASTMCGrammar a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + + if (a.isPresentGrammarAnnotation()) { + a.getGrammarAnnotation().accept(getTraverser()); + } + + if (!a.getPackageList().isEmpty()) { + print("package "); + print(Names.getQualifiedName(a.getPackageList())); + println(";"); + } + + println(); + if (a.isComponent()) { + print("component "); + } + print("grammar " + a.getName()); + + if (!a.getSupergrammarList().isEmpty()) { + print(" extends "); + String comma = ""; + for (ASTGrammarReference sgrammar : a.getSupergrammarList()) { + print(comma + Names.getQualifiedName(sgrammar.getNameList())); + comma = ", "; + } + } + println(" {"); + getPrinter().indent(); + if (a.isPresentGrammarOption()) { + a.getGrammarOption().accept(getTraverser()); + } + printList(a.getLexProdList().iterator(), ""); + printList(a.getClassProdList().iterator(), ""); + printList(a.getExternalProdList().iterator(), ""); + printList(a.getEnumProdList().iterator(), ""); + printList(a.getInterfaceProdList().iterator(), ""); + printList(a.getAbstractProdList().iterator(), ""); + printList(a.getASTRuleList().iterator(), ""); + printList(a.getConceptList().iterator(), ""); + if (a.isPresentStartRule()) { + a.getStartRule().accept(getTraverser()); + } + printList(a.getSymbolRuleList().iterator(), ""); + if (a.isPresentScopeRule()) { + a.getScopeRule().accept(getTraverser()); + } + + getPrinter().unindent(); + print("}"); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + + println(); + } + + // helper fuctions + + /** + * returns the right String for the Iteration value + * + * @param i .getIteration() value + */ + protected void outputIteration(int i) { + if (i == ASTConstantsGrammar.QUESTION) { + print("?"); + } + else if (i == ASTConstantsGrammar.STAR) { + print("*"); + } + else if (i == ASTConstantsGrammar.PLUS) { + print("+"); + } + else { + print(""); + } + } + + protected void print(String o) { + getPrinter().print(o); + } + + protected void println(String o) { + getPrinter().println(o); + } + + protected void println() { + getPrinter().println(); + } + + @Override + public void handle(ASTLexAlt a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + + String sep = ""; + for (ASTLexComponent c : a.getLexComponentList()) { + print(sep); + c.accept(getTraverser()); + sep = " "; + } + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTLexChar a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + + if (a.isNegate()) { + getPrinter().print("~"); + } + + getPrinter().print("'" + a.getChar() + "'"); + + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTLexAnyChar a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + + getPrinter().print("."); + + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTLexCharRange a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + + if (a.isNegate()) { + getPrinter().print("~"); + } + + getPrinter().print("'" + a.getLowerChar() + "'.."); + getPrinter().print("'" + a.getUpperChar() + "' "); + + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTRuleReference a) { + if (a.isPresentSemanticpredicateOrAction()) { + a.getSemanticpredicateOrAction().accept(getTraverser()); + } + getPrinter().print(a.getName()); + if (a.isPresentPrio()) { + getPrinter().print(" <" + a.getPrio() + "> "); + } + } + + public void handle(ASTLexString a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + + getPrinter().print("\"" + a.getString() + "\""); + + CommentPrettyPrinter.printPostComments(a, getPrinter()); + + } + + @Override + public void endVisit(ASTLexSimpleIteration a) { + outputIteration(a.getIteration()); + if(a.isQuestion()) { + getPrinter().print("?"); + } + } + + @Override + public void handle(ASTLexActionOrPredicate a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + + print(" {"); + getPrinter().println(); + getPrinter().indent(); + a.getExpressionPredicate().accept(getTraverser()); + getPrinter().unindent(); + print("}"); + + if (a.isPredicate()) { + print("?"); + } + + CommentPrettyPrinter.printPostComments(a, getPrinter()); + + } + + @Override + public void handle(ASTLexNonTerminal a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + + getPrinter().print(a.getName()); + + CommentPrettyPrinter.printPostComments(a, getPrinter()); + + } + + @Override + public void handle(ASTLexOption a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + + getPrinter().print("options "); + + getPrinter().print("{" + a.getID() + "=" + a.getValue() + ";}"); + + CommentPrettyPrinter.printPostComments(a, getPrinter()); + + } + + @Override + public void handle(ASTAbstractProd a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + + printList(a.getGrammarAnnotationList().iterator(), ""); + getPrinter().print("abstract "); + printList(a.getSymbolDefinitionList().iterator(), " "); + getPrinter().print(a.getName() + " "); + if (!a.getSuperRuleList().isEmpty()) { + getPrinter().print("extends "); + printList(a.getSuperRuleList().iterator(), " "); + getPrinter().print(" "); + } + if (!a.getSuperInterfaceRuleList().isEmpty()) { + getPrinter().print("implements "); + printList(a.getSuperInterfaceRuleList().iterator(), ", "); + getPrinter().print(" "); + } + if (!a.getASTSuperClassList().isEmpty()) { + getPrinter().print("astextends "); + printMCSimpleGenericList(a.getASTSuperClassList().iterator(), " "); + getPrinter().print(" "); + } + if (!a.getASTSuperInterfaceList().isEmpty()) { + getPrinter().print("astimplements "); + printMCSimpleGenericList(a.getASTSuperInterfaceList().iterator(), ", "); + getPrinter().print(" "); + } + + if (!a.getAltList().isEmpty()) { + println(" ="); + + getPrinter().indent(); + printList(a.getAltList().iterator(), " | "); + } + + getPrinter().println(";"); + + CommentPrettyPrinter.printPostComments(a, getPrinter()); + getPrinter().println(); + } + + + @Override + public void handle(ASTFollowOption a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + print("follow " + a.getProdName() + " "); + a.getAlt().accept(getTraverser()); + println(";"); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTSymbolDefinition node) { + if (node.isGenSymbol()) { + getPrinter().print(" symbol "); + } + if (node.isGenScope()) { + getPrinter().print(" scope "); + if (node.isOrdered() || node.isShadowing() || node.isNon_exporting()) { + getPrinter().print("("); + if (node.isOrdered()) { + getPrinter().print(" ordered "); + } + if (node.isShadowing()) { + getPrinter().print(" shadowing "); + } + if (node.isNon_exporting()) { + getPrinter().print(" non_exporting "); + } + getPrinter().print(")"); + } + } + } + + public void handle(ASTGrammarAnnotation node) { + if (node.isOverride()) { + getPrinter().println("@Override"); + } else if (node.isDeprecated()) { + getPrinter().print("@Deprecated"); + if (node.isPresentMessage()) { + getPrinter().print(("(\"")); + getPrinter().print(node.getMessage()); + getPrinter().print("\")"); + } + getPrinter().println(); + } + } + + + @Override + public void handle(ASTStartRule node) { + getPrinter().println(" start " + node.getName() + ";"); + } + + public String prettyprint(ASTGrammarNode a) { + getPrinter().clearBuffer(); + a.accept(getTraverser()); + return getPrinter().getContent(); + } + + /** + * Prints a list + * + * @param iter iterator for the list + * @param seperator string for seperating list + */ + protected void printList(Iterator iter, String seperator) { + // print by iterate through all items + String sep = ""; + while (iter.hasNext()) { + getPrinter().print(sep); + iter.next().accept(getTraverser()); + sep = seperator; + } + } + /** + * Prints a list + * + * @param iter iterator for the list + * @param seperator string for seperating list + */ + protected void printMCSimpleGenericList(Iterator iter, String seperator) { + // print by iterate through all items + String sep = ""; + while (iter.hasNext()) { + getPrinter().print(sep); + iter.next().accept(getTraverser()); + sep = seperator; + } + } + + @Override + public GrammarTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(GrammarTraverser traverser) { + this.traverser = traverser; + } + + public IndentPrinter getPrinter() { + return printer; + } + + public void setPrinter(IndentPrinter printer) { + this.printer = printer; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/prettyprint/Grammar_WithConceptsFullPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/grammar/prettyprint/Grammar_WithConceptsFullPrettyPrinter.java new file mode 100644 index 0000000000..130c606e1f --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/prettyprint/Grammar_WithConceptsFullPrettyPrinter.java @@ -0,0 +1,195 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.prettyprint; + + +import de.monticore.expressions.assignmentexpressions._ast.ASTAssignmentExpressionsNode; +import de.monticore.expressions.bitexpressions._ast.ASTBitExpressionsNode; +import de.monticore.expressions.commonexpressions._ast.ASTCommonExpressionsNode; +import de.monticore.expressions.expressionsbasis._ast.ASTExpressionsBasisNode; +import de.monticore.expressions.javaclassexpressions._ast.ASTJavaClassExpressionsNode; +import de.monticore.expressions.prettyprint.*; +import de.monticore.grammar.concepts.antlr.antlr._ast.ASTAntlrNode; +import de.monticore.grammar.grammar._ast.ASTGrammarNode; +import de.monticore.grammar.grammar_withconcepts.Grammar_WithConceptsMill; +import de.monticore.grammar.grammar_withconcepts._ast.ASTGrammar_WithConceptsNode; +import de.monticore.grammar.grammar_withconcepts._visitor.Grammar_WithConceptsTraverser; +import de.monticore.javalight._ast.ASTJavaLightNode; +import de.monticore.literals.prettyprint.MCCommonLiteralsPrettyPrinter; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.prettyprint.JavaLightPrettyPrinter; +import de.monticore.prettyprint.MCBasicsPrettyPrinter; +import de.monticore.statements.mccommonstatements._ast.ASTMCCommonStatementsNode; +import de.monticore.statements.mcstatementsbasis._ast.ASTMCStatementsBasisNode; +import de.monticore.statements.prettyprint.*; +import de.monticore.types.prettyprint.MCBasicTypesPrettyPrinter; +import de.monticore.types.prettyprint.MCCollectionTypesPrettyPrinter; +import de.monticore.types.prettyprint.MCSimpleGenericTypesPrettyPrinter; + +@Deprecated(forRemoval = true) +public class Grammar_WithConceptsFullPrettyPrinter { + + + protected IndentPrinter printer; + + protected Grammar_WithConceptsTraverser traverser; + + public Grammar_WithConceptsFullPrettyPrinter(IndentPrinter printer) { + this.printer = printer; + printer.setIndentLength(2); + traverser = Grammar_WithConceptsMill.traverser(); + + AntlrPrettyPrinter antlr = new AntlrPrettyPrinter(printer); + traverser.add4Antlr(antlr); + traverser.setAntlrHandler(antlr); + + GrammarPrettyPrinter grammar = new GrammarPrettyPrinter(printer); + traverser.add4Grammar(grammar); + traverser.setGrammarHandler(grammar); + + AssignmentExpressionsPrettyPrinter assigments = new AssignmentExpressionsPrettyPrinter(printer); + traverser.add4AssignmentExpressions(assigments); + traverser.setAssignmentExpressionsHandler(assigments); + + ExpressionsBasisPrettyPrinter basisExpr = new ExpressionsBasisPrettyPrinter(printer); + traverser.add4ExpressionsBasis(basisExpr); + traverser.setExpressionsBasisHandler(basisExpr); + + CommonExpressionsPrettyPrinter commonExpr = new CommonExpressionsPrettyPrinter(printer); + traverser.add4CommonExpressions(commonExpr); + traverser.setCommonExpressionsHandler(commonExpr); + + JavaClassExpressionsPrettyPrinter classExpr = new JavaClassExpressionsPrettyPrinter(printer); + traverser.add4JavaClassExpressions(classExpr); + traverser.setJavaClassExpressionsHandler(classExpr); + + BitExpressionsPrettyPrinter bitExpr = new BitExpressionsPrettyPrinter(printer); + traverser.add4BitExpressions(bitExpr); + traverser.setBitExpressionsHandler(bitExpr); + + MCBasicsPrettyPrinter basics = new MCBasicsPrettyPrinter(printer); + traverser.add4MCBasics(basics); + + MCCommonLiteralsPrettyPrinter commonLiterals = new MCCommonLiteralsPrettyPrinter(printer); + traverser.add4MCCommonLiterals(commonLiterals); + traverser.setMCCommonLiteralsHandler(commonLiterals); + + MCBasicTypesPrettyPrinter basicTypes = new MCBasicTypesPrettyPrinter(printer); + traverser.add4MCBasicTypes(basicTypes); + traverser.setMCBasicTypesHandler(basicTypes); + + MCCollectionTypesPrettyPrinter collectionTypes = new MCCollectionTypesPrettyPrinter(printer); + traverser.add4MCCollectionTypes(collectionTypes); + traverser.setMCCollectionTypesHandler(collectionTypes); + + MCSimpleGenericTypesPrettyPrinter simpleGenerics = new MCSimpleGenericTypesPrettyPrinter(printer); + traverser.add4MCSimpleGenericTypes(simpleGenerics); + traverser.setMCSimpleGenericTypesHandler(simpleGenerics); + + MCExceptionStatementsPrettyPrinter exceptionStS = new MCExceptionStatementsPrettyPrinter(printer); + traverser.add4MCExceptionStatements(exceptionStS); + traverser.setMCExceptionStatementsHandler(exceptionStS); + + MCReturnStatementsPrettyPrinter returnSts = new MCReturnStatementsPrettyPrinter(printer); + traverser.add4MCReturnStatements(returnSts); + traverser.setMCReturnStatementsHandler(returnSts); + + MCCommonStatementsPrettyPrinter commonSts = new MCCommonStatementsPrettyPrinter(printer); + traverser.add4MCCommonStatements(commonSts); + traverser.setMCCommonStatementsHandler(commonSts); + + MCVarDeclarationStatementsPrettyPrinter varDeclSts = new MCVarDeclarationStatementsPrettyPrinter(printer); + traverser.add4MCVarDeclarationStatements(varDeclSts); + traverser.setMCVarDeclarationStatementsHandler(varDeclSts); + + MCArrayStatementsPrettyPrinter arraySts = new MCArrayStatementsPrettyPrinter(printer); + traverser.add4MCArrayStatements(arraySts); + traverser.setMCArrayStatementsHandler(arraySts); + + JavaLightPrettyPrinter javaLight = new JavaLightPrettyPrinter(printer); + traverser.add4JavaLight(javaLight); + traverser.setJavaLightHandler(javaLight); + } + + public String prettyprint(ASTGrammar_WithConceptsNode a) { + printer.clearBuffer(); + a.accept(getTraverser()); + return printer.getContent(); + } + + public String prettyprint(ASTGrammarNode a) { + printer.clearBuffer(); + a.accept(getTraverser()); + return printer.getContent(); + } + + public String prettyprint(ASTAntlrNode a) { + printer.clearBuffer(); + a.accept(getTraverser()); + return printer.getContent(); + } + + public String prettyprint(ASTJavaLightNode a) { + printer.clearBuffer(); + a.accept(getTraverser()); + return printer.getContent(); + } + + public IndentPrinter getPrinter() { + return printer; + } + + public void setPrinter(IndentPrinter printer) { + this.printer = printer; + } + + public Grammar_WithConceptsTraverser getTraverser() { + return traverser; + } + + public void setTraverser(Grammar_WithConceptsTraverser traverser) { + this.traverser = traverser; + } + + public String prettyprint(ASTMCCommonStatementsNode a) { + printer.clearBuffer(); + a.accept(getTraverser()); + return printer.getContent(); + } + + public String prettyprint(ASTExpressionsBasisNode a) { + printer.clearBuffer(); + a.accept(getTraverser()); + return printer.getContent(); + } + + public String prettyprint(ASTBitExpressionsNode a) { + printer.clearBuffer(); + a.accept(getTraverser()); + return printer.getContent(); + } + + public String prettyprint(ASTCommonExpressionsNode a) { + printer.clearBuffer(); + a.accept(getTraverser()); + return printer.getContent(); + } + + public String prettyprint(ASTAssignmentExpressionsNode a) { + printer.clearBuffer(); + a.accept(getTraverser()); + return printer.getContent(); + } + + public String prettyprint(ASTJavaClassExpressionsNode a) { + printer.clearBuffer(); + a.accept(getTraverser()); + return printer.getContent(); + } + + public String prettyprint(ASTMCStatementsBasisNode a) { + printer.clearBuffer(); + a.accept(getTraverser()); + return printer.getContent(); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/grammar/prettyprint/Grammar_WithConceptsPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/grammar/prettyprint/Grammar_WithConceptsPrettyPrinter.java new file mode 100644 index 0000000000..27c65e066e --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/grammar/prettyprint/Grammar_WithConceptsPrettyPrinter.java @@ -0,0 +1,135 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.prettyprint; + + +import de.monticore.expressions.assignmentexpressions._ast.ASTAssignmentExpressionsNode; +import de.monticore.expressions.bitexpressions._ast.ASTBitExpressionsNode; +import de.monticore.expressions.commonexpressions._ast.ASTCommonExpressionsNode; +import de.monticore.expressions.expressionsbasis._ast.ASTExpressionsBasisNode; +import de.monticore.expressions.javaclassexpressions._ast.ASTJavaClassExpressionsNode; +import de.monticore.grammar.grammar_withconcepts._ast.*; +import de.monticore.grammar.grammar_withconcepts._visitor.Grammar_WithConceptsHandler; +import de.monticore.grammar.grammar_withconcepts._visitor.Grammar_WithConceptsTraverser; +import de.monticore.grammar.grammar_withconcepts._visitor.Grammar_WithConceptsVisitor2; +import de.monticore.prettyprint.CommentPrettyPrinter; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.statements.mccommonstatements._ast.ASTMCCommonStatementsNode; +import de.monticore.statements.mcstatementsbasis._ast.ASTMCStatementsBasisNode; +import de.monticore.types.mccollectiontypes._ast.ASTMCTypeArgument; + + +@Deprecated(forRemoval = true) +public class Grammar_WithConceptsPrettyPrinter implements Grammar_WithConceptsVisitor2, + Grammar_WithConceptsHandler { + + protected IndentPrinter printer; + + protected Grammar_WithConceptsTraverser traverser; + + + public Grammar_WithConceptsPrettyPrinter(IndentPrinter out) { + printer = out; + out.setIndentLength(2); + } + + public String prettyprint(ASTGrammar_WithConceptsNode a) { + printer.clearBuffer(); + a.accept(getTraverser()); + return printer.getContent(); + } + + @Override + public void handle(ASTExtReturnType node) { + CommentPrettyPrinter.printPreComments(node, printer); + node.getMCReturnType().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(node, printer); + } + + @Override + public void handle(ASTExtType node) { + CommentPrettyPrinter.printPreComments(node, printer); + node.getMCType().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(node, printer); + } + + @Override + public void handle(ASTAction node) { + CommentPrettyPrinter.printPreComments(node, printer); + node.getMCBlockStatementList().stream().forEach(a -> a.accept(getTraverser())); + CommentPrettyPrinter.printPostComments(node, printer); + } + + @Override + public void handle(ASTExtTypeArgument node) { + CommentPrettyPrinter.printPreComments(node, printer); + printer.print("<"); + String sep = ""; + for (ASTMCTypeArgument t :node.getMCTypeArgumentList()) { + printer.print(sep); + t.accept(getTraverser()); + sep = ", "; + } + CommentPrettyPrinter.printPostComments(node, printer); + } + + public String prettyprint(ASTMCCommonStatementsNode a) { + printer.clearBuffer(); + a.accept(getTraverser()); + return printer.getContent(); + } + + public String prettyprint(ASTExpressionsBasisNode a) { + printer.clearBuffer(); + a.accept(getTraverser()); + return printer.getContent(); + } + + public String prettyprint(ASTBitExpressionsNode a) { + printer.clearBuffer(); + a.accept(getTraverser()); + return printer.getContent(); + } + + public String prettyprint(ASTCommonExpressionsNode a) { + printer.clearBuffer(); + a.accept(getTraverser()); + return printer.getContent(); + } + + public String prettyprint(ASTAssignmentExpressionsNode a) { + printer.clearBuffer(); + a.accept(getTraverser()); + return printer.getContent(); + } + + public String prettyprint(ASTJavaClassExpressionsNode a) { + printer.clearBuffer(); + a.accept(getTraverser()); + return printer.getContent(); + } + + public String prettyprint(ASTMCStatementsBasisNode a) { + printer.clearBuffer(); + a.accept(getTraverser()); + return printer.getContent(); + } + + public IndentPrinter getPrinter() { + return printer; + } + + public void setPrinter(IndentPrinter printer) { + this.printer = printer; + } + + @Override + public Grammar_WithConceptsTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(Grammar_WithConceptsTraverser traverser) { + this.traverser = traverser; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/javalight/_symboltable/JavaLightSTCompleteTypes.java b/monticore-grammar/src/main/java/de/monticore/javalight/_symboltable/JavaLightSTCompleteTypes.java new file mode 100644 index 0000000000..d55ab9aca3 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/javalight/_symboltable/JavaLightSTCompleteTypes.java @@ -0,0 +1,137 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.javalight._symboltable; + +import de.monticore.grammar.grammar_withconcepts.FullSynthesizeFromMCSGT4Grammar; +import de.monticore.javalight._ast.*; +import de.monticore.javalight._visitor.JavaLightVisitor2; +import de.monticore.statements.mccommonstatements._ast.ASTJavaModifier; +import de.monticore.statements.mcstatementsbasis._ast.ASTMCModifier; +import de.monticore.symbols.oosymbols._symboltable.FieldSymbol; +import de.monticore.symbols.oosymbols._symboltable.IOOSymbolsScope; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types.check.SymTypeOfNull; +import de.monticore.types.check.TypeCheckResult; +import de.monticore.types.mcbasictypes._ast.ASTMCQualifiedName; +import de.monticore.types.mcbasictypes._ast.ASTMCReturnType; +import de.monticore.types.mcbasictypes._ast.ASTMCType; + +import static de.monticore.statements.mccommonstatements._ast.ASTConstantsMCCommonStatements.*; + +public class JavaLightSTCompleteTypes implements JavaLightVisitor2 { + + @Override + public void endVisit(ASTLastFormalParameter ast) { + FieldSymbol symbol = ast.getDeclaratorId().getSymbol(); + symbol.setType(createTypeLoader(ast.getMCType())); + } + + @Override + public void endVisit(ASTMethodDeclaration ast){ + JavaMethodSymbol symbol = ast.getSymbol(); + addModifiersToMethOrConstr(symbol, ast.getMCModifierList()); + symbol.setType(createTypeLoader(ast.getMCReturnType())); + if (ast.isPresentThrows()) { + addThrowsToMethod(symbol, ast.getThrows()); + } + if (ast.getFormalParameters().isPresentFormalParameterListing() + && ast.getFormalParameters().getFormalParameterListing().isPresentLastFormalParameter()) { + symbol.setIsElliptic(true); + } + } + + @Override + public void endVisit(ASTConstructorDeclaration ast){ + JavaMethodSymbol symbol = ast.getSymbol(); + addModifiersToMethOrConstr(symbol, ast.getMCModifierList()); + if (ast.isPresentThrows()) { + addThrowsToMethod(symbol, ast.getThrows()); + } + if (ast.getFormalParameters().isPresentFormalParameterListing() + && ast.getFormalParameters().getFormalParameterListing().isPresentLastFormalParameter()) { + symbol.setIsElliptic(true); + } + } + + protected void addModifiersToMethOrConstr(JavaMethodSymbol javaMethodSymbol, + Iterable astModifierList) { + for (ASTMCModifier modifier : astModifierList) { + if (modifier instanceof ASTJavaModifier) { + // visibility + switch (((ASTJavaModifier) modifier).getModifier()) { + case PUBLIC: + javaMethodSymbol.setIsPublic(true); + break; + case PROTECTED: + javaMethodSymbol.setIsProtected(true); + break; + case PRIVATE: + javaMethodSymbol.setIsPrivate(true); + // other variable modifiers as in jls7 8.3.1 Field Modifiers + break; + case ABSTRACT: + javaMethodSymbol.setIsAbstract(true); + break; + case STATIC: + javaMethodSymbol.setIsStatic(true); + break; + case FINAL: + javaMethodSymbol.setIsFinal(true); + break; + case NATIVE: + javaMethodSymbol.setIsNative(true); + break; + case STRICTFP: + javaMethodSymbol.setIsStrictfp(true); + break; + case SYNCHRONIZED: + javaMethodSymbol.setIsSynchronized(true); + break; + case MODIFIER_DEFAULT: + javaMethodSymbol.setIsDefault(true); + default: + break; + } + } else if (modifier instanceof ASTAnnotation) { + ASTAnnotation astAnnotation = (ASTAnnotation) modifier; + javaMethodSymbol.addAnnotations(createTypeLoader(astAnnotation.getAnnotationName())); + } + } + } + + protected void addThrowsToMethod(JavaMethodSymbol javaMethodSymbol, ASTThrows throws1) { + for (ASTMCQualifiedName astQualifiedName : throws1.getMCQualifiedNameList()) { + javaMethodSymbol.addExceptions(createTypeLoader(astQualifiedName)); + } + } + + protected SymTypeExpression createTypeLoader(ASTMCQualifiedName ast) { + FullSynthesizeFromMCSGT4Grammar synFromFull = new FullSynthesizeFromMCSGT4Grammar(); + // Start visitor + TypeCheckResult typeCheckResult = synFromFull.synthesizeType(ast); + if(typeCheckResult.isPresentResult()){ + return typeCheckResult.getResult(); + } + return new SymTypeOfNull(); + } + + protected SymTypeExpression createTypeLoader(ASTMCType ast) { + FullSynthesizeFromMCSGT4Grammar synFromFull = new FullSynthesizeFromMCSGT4Grammar(); + // Start visitor + TypeCheckResult typeCheckResult = synFromFull.synthesizeType(ast); + if(typeCheckResult.isPresentResult()){ + return typeCheckResult.getResult(); + } + return new SymTypeOfNull(); + } + + protected SymTypeExpression createTypeLoader(ASTMCReturnType ast) { + if (ast.isPresentMCType()) { + return createTypeLoader(ast.getMCType()); + } else { + // TODO Bessere Lösung + return SymTypeExpressionFactory.createTypeObject("void", (IOOSymbolsScope) ast.getEnclosingScope()); + } + + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/javalight/_symboltable/JavaMethodSymbolDeSer.java b/monticore-grammar/src/main/java/de/monticore/javalight/_symboltable/JavaMethodSymbolDeSer.java new file mode 100644 index 0000000000..3874eb7a85 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/javalight/_symboltable/JavaMethodSymbolDeSer.java @@ -0,0 +1,42 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.javalight._symboltable; + +import de.monticore.symboltable.serialization.json.JsonObject; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionDeSer; + +import java.util.List; + +public class JavaMethodSymbolDeSer extends JavaMethodSymbolDeSerTOP { + + @Override + protected void serializeExceptions(List exceptions, JavaLightSymbols2Json s2j) { + SymTypeExpressionDeSer.serializeMember(s2j.getJsonPrinter(), "exceptions", exceptions); + } + + @Override + protected void serializeAnnotations(List annotations, JavaLightSymbols2Json s2j) { + SymTypeExpressionDeSer.serializeMember(s2j.getJsonPrinter(), "annotations", annotations); + } + + @Override + protected void serializeType(SymTypeExpression type, JavaLightSymbols2Json s2j) { + SymTypeExpressionDeSer.serializeMember(s2j.getJsonPrinter(), "type", type); + } + + @Override + protected List deserializeExceptions(JsonObject symbolJson) { + return SymTypeExpressionDeSer.deserializeListMember("exceptions", symbolJson); + } + + @Override + protected List deserializeAnnotations(JsonObject symbolJson) { + return SymTypeExpressionDeSer.deserializeListMember("annotations", symbolJson); + } + + @Override + protected SymTypeExpression deserializeType(JsonObject symbolJson) { + return SymTypeExpressionDeSer.deserializeMember("type", symbolJson); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ConstructorFormalParametersDifferentName.java b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ConstructorFormalParametersDifferentName.java new file mode 100644 index 0000000000..11433a9a7e --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ConstructorFormalParametersDifferentName.java @@ -0,0 +1,38 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.javalight.cocos; + +import de.monticore.javalight._ast.ASTConstructorDeclaration; +import de.monticore.javalight._cocos.JavaLightASTConstructorDeclarationCoCo; +import de.monticore.statements.mccommonstatements._ast.ASTFormalParameter; +import de.se_rwth.commons.logging.Log; + +import java.util.ArrayList; +import java.util.List; + +public class ConstructorFormalParametersDifferentName implements JavaLightASTConstructorDeclarationCoCo { + + public static final String ERROR_CODE = "0xA0301"; + + public static final String ERROR_MSG_FORMAT = " Formal parameter '%s' is already declared in constructor '%s'. "; + + @Override + public void check(ASTConstructorDeclaration node) { + List names = new ArrayList<>(); + if (node.getFormalParameters().isPresentFormalParameterListing()) { + if(node.getFormalParameters().getFormalParameterListing().isPresentLastFormalParameter()){ + names.add(node.getFormalParameters().getFormalParameterListing().getLastFormalParameter() + .getDeclaratorId().getName()); + } + for (ASTFormalParameter formalParameter : node.getFormalParameters() + .getFormalParameterListing().getFormalParameterList()) { + if (names.contains(formalParameter.getDeclarator().getName())) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, formalParameter.getDeclarator().getName(), node.getName()), + node.get_SourcePositionStart()); + } + else { + names.add(formalParameter.getDeclarator().getName()); + } + } + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ConstructorModifiersValid.java b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ConstructorModifiersValid.java new file mode 100644 index 0000000000..e1289961e8 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ConstructorModifiersValid.java @@ -0,0 +1,23 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.javalight.cocos; + +import de.monticore.javalight._ast.ASTConstructorDeclaration; +import de.monticore.javalight._cocos.JavaLightASTConstructorDeclarationCoCo; +import de.monticore.javalight._symboltable.JavaMethodSymbol; +import de.se_rwth.commons.logging.Log; + +public class ConstructorModifiersValid implements JavaLightASTConstructorDeclarationCoCo { + + public static final String ERROR_CODE = "0xA0820"; + + public static final String ERROR_MSG_FORMAT = " Constructor '%s' cannot be declared 'abstract', 'final', 'static' or 'native'."; + + @Override + public void check(ASTConstructorDeclaration node) { + JavaMethodSymbol symbol = node.getSymbol(); + if (symbol.isIsAbstract() || symbol.isIsFinal() || symbol.isIsStatic() || symbol.isIsNative()) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT ,node.getName()), + node.get_SourcePositionStart()); + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ConstructorNoAccessModifierPair.java b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ConstructorNoAccessModifierPair.java new file mode 100644 index 0000000000..b87be6b395 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ConstructorNoAccessModifierPair.java @@ -0,0 +1,32 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.javalight.cocos; + +import de.monticore.javalight._ast.ASTConstructorDeclaration; +import de.monticore.javalight._cocos.JavaLightASTConstructorDeclarationCoCo; +import de.monticore.javalight._symboltable.JavaMethodSymbol; +import de.se_rwth.commons.logging.Log; + +import java.util.ArrayList; +import java.util.List; + +public class ConstructorNoAccessModifierPair implements JavaLightASTConstructorDeclarationCoCo { + + public static final String ERROR_CODE = "0xA0809"; + + public static final String ERROR_MSG_FORMAT = " Invalid modifiers are mentioned in constructor's '%s' declaration."; + + // JLS3 8.8.3-2 + @Override + public void check(ASTConstructorDeclaration node) { + JavaMethodSymbol symbol = node.getSymbol(); + if((symbol.isIsPublic() && symbol.isIsProtected() && symbol.isIsPrivate()) + || (symbol.isIsPublic() && symbol.isIsProtected()) + || (symbol.isIsPublic() && symbol.isIsPrivate()) + || (symbol.isIsProtected() && symbol.isIsPrivate())) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, node.getName(), + node.get_SourcePositionStart())); + } + } +} + + diff --git a/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ConstructorNoDuplicateModifier.java b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ConstructorNoDuplicateModifier.java new file mode 100644 index 0000000000..de510c483b --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ConstructorNoDuplicateModifier.java @@ -0,0 +1,54 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.javalight.cocos; + +import de.monticore.javalight._ast.ASTConstructorDeclaration; +import de.monticore.javalight._cocos.JavaLightASTConstructorDeclarationCoCo; +import de.monticore.javalight._prettyprint.JavaLightFullPrettyPrinter; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.statements.mcstatementsbasis._ast.ASTMCModifier; +import de.se_rwth.commons.logging.Log; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class ConstructorNoDuplicateModifier implements JavaLightASTConstructorDeclarationCoCo { + + public static final String ERROR_CODE = "0xA0808"; + + public static final String ERROR_MSG_FORMAT = " modifier '%s' is mentioned more than once in the constructor '%s'. "; + + protected String prettyprint(ASTMCModifier a) { + JavaLightFullPrettyPrinter printer = new JavaLightFullPrettyPrinter(new IndentPrinter()); + a.accept(printer.getTraverser()); + return printer.getPrinter().getContent().trim(); + } + + public Set findDuplicates(List listContainingDuplicates) { + final Set setToReturn = new HashSet<>(); + final Set set1 = new HashSet<>(); + + for (String yourString : listContainingDuplicates) { + if (!set1.add(yourString)) { + setToReturn.add(yourString); + } + } + return setToReturn; + } + + // JLS3 8.8.3-1 + @Override + public void check(ASTConstructorDeclaration node) { + //print the modifier -> add to list + List listModifier = new ArrayList<>(); + for (ASTMCModifier modifier : node.getMCModifierList()) { + listModifier.add(prettyprint(modifier)); + } + Set duplicates = findDuplicates(listModifier); + for (String duplicate : duplicates) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, duplicate, node.getName()), + node.get_SourcePositionStart()); + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/javalight/cocos/MethodAbstractAndOtherModifiers.java b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/MethodAbstractAndOtherModifiers.java new file mode 100644 index 0000000000..ba65f41a3f --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/MethodAbstractAndOtherModifiers.java @@ -0,0 +1,31 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.javalight.cocos; + + +import de.monticore.javalight._ast.ASTMethodDeclaration; +import de.monticore.javalight._cocos.JavaLightASTMethodDeclarationCoCo; +import de.monticore.javalight._symboltable.JavaMethodSymbol; +import de.se_rwth.commons.logging.Log; + +public class MethodAbstractAndOtherModifiers implements JavaLightASTMethodDeclarationCoCo { + + public static final String ERROR_CODE = "0xA0802"; + + public static final String ERROR_MSG_FORMAT = " The abstract method %s must be public. "; + + //JLS3 8.4.3-3 + @Override + public void check(ASTMethodDeclaration node) { + JavaMethodSymbol methodSymbol = node.getSymbol(); + if (methodSymbol.isIsAbstract()) { + if (methodSymbol.isIsPrivate() || methodSymbol.isIsStatic() || methodSymbol.isIsFinal() || + methodSymbol.isIsNative() || methodSymbol.isIsStrictfp() || methodSymbol.isIsSynchronized()) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, node.getName()), + node.get_SourcePositionStart()); + } + } + } + +} + diff --git a/monticore-grammar/src/main/java/de/monticore/javalight/cocos/MethodBodyAbsence.java b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/MethodBodyAbsence.java new file mode 100644 index 0000000000..32fa68913d --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/MethodBodyAbsence.java @@ -0,0 +1,26 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.javalight.cocos; + +import de.monticore.javalight._ast.ASTMethodDeclaration; +import de.monticore.javalight._cocos.JavaLightASTMethodDeclarationCoCo; +import de.monticore.javalight._symboltable.JavaMethodSymbol; +import de.se_rwth.commons.logging.Log; + +public class MethodBodyAbsence implements JavaLightASTMethodDeclarationCoCo { + + public static final String ERROR_CODE = "0xA0804"; + + public static final String ERROR_MESSAGE = "Method '%s' must be 'abstract' or 'native' if it doesn't specify a body."; + + @Override + public void check(ASTMethodDeclaration node) { + if (node.isPresentSymbol()) { + JavaMethodSymbol methodSymbol = node.getSymbol(); + // JLS3 8.4.7-1 + if ((!node.isPresentMCJavaBlock() && !methodSymbol.isIsAbstract()) && (!node.isPresentMCJavaBlock() && !methodSymbol.isIsNative())) { + Log.error(String.format(ERROR_CODE + ERROR_MESSAGE, methodSymbol.getName()), + node.get_SourcePositionStart()); + } + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/javalight/cocos/MethodBodyPresence.java b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/MethodBodyPresence.java new file mode 100644 index 0000000000..9cf6beace8 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/MethodBodyPresence.java @@ -0,0 +1,25 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.javalight.cocos; + +import de.monticore.javalight._ast.ASTMethodDeclaration; +import de.monticore.javalight._cocos.JavaLightASTMethodDeclarationCoCo; +import de.monticore.javalight._symboltable.JavaMethodSymbol; +import de.se_rwth.commons.logging.Log; + +public class MethodBodyPresence implements JavaLightASTMethodDeclarationCoCo { + public static final String ERROR_CODE = "0xA0803"; + + public static final String ERROR_MESSAGE = "Method '%s' must not be 'abstract' or 'native' if it specifies a body."; + + @Override + public void check(ASTMethodDeclaration node) { + if (node.isPresentSymbol()) { + JavaMethodSymbol methodSymbol = node.getSymbol(); + // JLS3 8.4.7-1 + if ((node.isPresentMCJavaBlock() && methodSymbol.isIsAbstract()) || (node.isPresentMCJavaBlock() && methodSymbol.isIsNative())) { + Log.error(String.format(ERROR_CODE + ERROR_MESSAGE, methodSymbol.getName()), + node.get_SourcePositionStart()); + } + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/javalight/cocos/MethodExceptionThrows.java b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/MethodExceptionThrows.java new file mode 100644 index 0000000000..3a456d81e3 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/MethodExceptionThrows.java @@ -0,0 +1,31 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.javalight.cocos; + +import de.monticore.javalight._ast.ASTMethodDeclaration; +import de.monticore.javalight._cocos.JavaLightASTMethodDeclarationCoCo; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types.check.TypeCheck; +import de.se_rwth.commons.logging.Log; + +public class MethodExceptionThrows implements JavaLightASTMethodDeclarationCoCo { + + public static final String ERROR_CODE = "0xA0811"; + + public static final String ERROR_MSG_FORMAT = " No exception of type '%s' can be thrown. An exception must be a subtype of Throwable."; + + @Override + public void check(ASTMethodDeclaration node) { + if (node.isPresentThrows()) { + SymTypeExpression throwable = SymTypeExpressionFactory.createTypeObject("java.lang.Throwable", node.getEnclosingScope()); + for (SymTypeExpression exception : node.getSymbol().getExceptionsList()) { + if (!TypeCheck.isSubtypeOf(exception, throwable)) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, exception.print()), + node.get_SourcePositionStart()); + } + } + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/javalight/cocos/MethodFormalParametersDifferentName.java b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/MethodFormalParametersDifferentName.java new file mode 100644 index 0000000000..5ffb8679b2 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/MethodFormalParametersDifferentName.java @@ -0,0 +1,42 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.javalight.cocos; + +import de.monticore.javalight._ast.ASTMethodDeclaration; +import de.monticore.javalight._cocos.JavaLightASTMethodDeclarationCoCo; +import de.monticore.statements.mccommonstatements._ast.ASTFormalParameter; +import de.se_rwth.commons.logging.Log; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; + +public class MethodFormalParametersDifferentName implements JavaLightASTMethodDeclarationCoCo { + + public static final String ERROR_CODE = "0xA0812"; + + public static final String ERROR_MSG_FORMAT = " Formal parameter '%s' is already declared in method '%s'."; + + //JLS3 8.4.1-1 + @Override + public void check(ASTMethodDeclaration node) { + Collection names = new HashSet<>(); + if (node.getFormalParameters().isPresentFormalParameterListing()) { + if(node.getFormalParameters().getFormalParameterListing().isPresentLastFormalParameter()){ + names.add(node.getFormalParameters().getFormalParameterListing().getLastFormalParameter() + .getDeclaratorId().getName()); + } + for (ASTFormalParameter formalParameter : node.getFormalParameters() + .getFormalParameterListing().getFormalParameterList()) { + if (names.contains(formalParameter.getDeclarator().getName())) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, formalParameter.getDeclarator().getName(), node.getName()), + node.get_SourcePositionStart()); + } + else { + names.add(formalParameter.getDeclarator().getName()); + } + } + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/javalight/cocos/MethodNoDuplicateModifier.java b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/MethodNoDuplicateModifier.java new file mode 100644 index 0000000000..77894a6a7b --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/MethodNoDuplicateModifier.java @@ -0,0 +1,55 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.javalight.cocos; + +import de.monticore.javalight._ast.ASTMethodDeclaration; +import de.monticore.javalight._cocos.JavaLightASTMethodDeclarationCoCo; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.javalight._prettyprint.JavaLightFullPrettyPrinter; +import de.monticore.statements.mcstatementsbasis._ast.ASTMCModifier; +import de.se_rwth.commons.logging.Log; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class MethodNoDuplicateModifier implements JavaLightASTMethodDeclarationCoCo { + + public static final String ERROR_CODE = "0xA0818"; + + public static final String ERROR_MSG_FORMAT = "modifier '%s' is declared more than once in method %s"; + + protected String prettyprint(ASTMCModifier a) { + JavaLightFullPrettyPrinter printer = new JavaLightFullPrettyPrinter(new IndentPrinter()); + a.accept(printer.getTraverser()); + return printer.getPrinter().getContent(); + } + + public Set findDuplicates(List listContainingDuplicates) { + final Set setToReturn = new HashSet<>(); + final Set set1 = new HashSet<>(); + + for (String modifierName : listContainingDuplicates) { + if (!set1.add(modifierName)) { + setToReturn.add(modifierName); + } + } + return setToReturn; + } + + //JLS3 8.4.3-1 + @Override + public void check(ASTMethodDeclaration node) { + //print the modifier -> add to list + List listModifier = new ArrayList<>(); + for (ASTMCModifier modifier : node.getMCModifierList()) { + listModifier.add(prettyprint(modifier)); + } + Set duplicates = findDuplicates(listModifier); + for (String duplicate : duplicates) { + Log.error(String.format(ERROR_CODE + ERROR_MSG_FORMAT, duplicate, node.getName()), + node.get_SourcePositionStart()); + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/javalight/cocos/MethodNoNativeAndStrictfp.java b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/MethodNoNativeAndStrictfp.java new file mode 100644 index 0000000000..fadb56c544 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/MethodNoNativeAndStrictfp.java @@ -0,0 +1,26 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.javalight.cocos; + +import de.monticore.javalight._ast.ASTMethodDeclaration; +import de.monticore.javalight._cocos.JavaLightASTMethodDeclarationCoCo; +import de.monticore.javalight._symboltable.JavaMethodSymbol; +import de.se_rwth.commons.logging.Log; + +public class MethodNoNativeAndStrictfp implements JavaLightASTMethodDeclarationCoCo { + + public static final String ERROR_CODE = "0xA0819"; + + public static final String ERROR_MESSAGE = "Method %s must not be both 'native' and 'strictfp'."; + + @Override + public void check(ASTMethodDeclaration node) { + if (node.isPresentSymbol()) { + JavaMethodSymbol methodSymbol = node.getSymbol(); + if (methodSymbol.isIsNative() && methodSymbol.isIsStrictfp()) { + Log.error(String.format(ERROR_CODE + ERROR_MESSAGE ,node.getName()), + node.get_SourcePositionStart()); + } + } + + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ReturnTypeAssignmentIsValid.java b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ReturnTypeAssignmentIsValid.java new file mode 100644 index 0000000000..682ef800b9 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/javalight/cocos/ReturnTypeAssignmentIsValid.java @@ -0,0 +1,91 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.javalight.cocos; + +import de.monticore.javalight.JavaLightMill; +import de.monticore.javalight._ast.ASTMethodDeclaration; +import de.monticore.javalight._cocos.JavaLightASTMethodDeclarationCoCo; +import de.monticore.javalight._visitor.JavaLightTraverser; +import de.monticore.statements.mcreturnstatements._ast.ASTReturnStatement; +import de.monticore.statements.mcreturnstatements._visitor.MCReturnStatementsVisitor2; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.TypeCheck; +import de.monticore.types.check.TypeCalculator; +import de.se_rwth.commons.logging.Log; + +import java.util.ArrayList; +import java.util.List; + +public class ReturnTypeAssignmentIsValid implements JavaLightASTMethodDeclarationCoCo { + + public static final String ERROR_CODE = "0xA0910 "; + + public static final String ERROR_MSG_FORMAT = "Return statements of void methods must all be empty."; + + public static final String ERROR_CODE_2 = "0xA0911 "; + + public static final String ERROR_MSG_FORMAT_2 = "Return statements of non void methods must not be empty."; + + public static final String ERROR_CODE_3 = "0xA0912 "; + + public static final String ERROR_MSG_FORMAT_3 = "Return statement must be of the type of the method or a subtype of it."; + + TypeCalculator typeCheck; + + public ReturnTypeAssignmentIsValid(TypeCalculator typeCheck) { + this.typeCheck = typeCheck; + } + + @Override + public void check(ASTMethodDeclaration node) { + + // Collect return-statements + JavaLightTraverser traverser = JavaLightMill.traverser(); + JavaReturnStatementCollector returnStatementCollector = new JavaReturnStatementCollector(); + traverser.add4MCReturnStatements(returnStatementCollector); + node.accept(traverser); + List returnStatements = returnStatementCollector.getReturnStatementList(); + + SymTypeExpression typeOfMethod = typeCheck.symTypeFromAST(node.getMCReturnType()); + + // Check return-Statements + if (node.isPresentMCJavaBlock()) { + if (TypeCheck.isVoid(typeOfMethod)) { + for (ASTReturnStatement statement : returnStatements) { + if (statement.isPresentExpression()) { + Log.error(ERROR_CODE + ERROR_MSG_FORMAT, node.get_SourcePositionStart()); + } + } + } + if (!TypeCheck.isVoid(typeOfMethod) && returnStatements.isEmpty()) { + Log.error(ERROR_CODE_2 + ERROR_MSG_FORMAT_2, node.get_SourcePositionStart()); + } + if (!TypeCheck.isVoid(typeOfMethod) && !returnStatements.isEmpty()) { + for (ASTReturnStatement returnStatement : returnStatements) { + if (!returnStatement.isPresentExpression()) { + Log.error(ERROR_CODE_2 + ERROR_MSG_FORMAT_2, node.get_SourcePositionStart()); + } else { + SymTypeExpression returnType = typeCheck.typeOf(returnStatement.getExpression()); + if (!returnType.deepEquals(typeOfMethod)) { + Log.error(ERROR_CODE_3 + ERROR_MSG_FORMAT_3, node.get_SourcePositionStart()); + } + } + } + } + } + } + + private class JavaReturnStatementCollector implements MCReturnStatementsVisitor2 { + + List returnStatementList = new ArrayList<>(); + + private List getReturnStatementList() { + return this.returnStatementList; + } + + @Override + public void visit(ASTReturnStatement node) { + returnStatementList.add(node); + } + + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/literals/MCLiteralsDecoder.java b/monticore-grammar/src/main/java/de/monticore/literals/MCLiteralsDecoder.java new file mode 100644 index 0000000000..f400cf5891 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/literals/MCLiteralsDecoder.java @@ -0,0 +1,219 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.literals; + +import de.se_rwth.commons.logging.Log; + +/** + * This class provides methods for converting literals. The LiteralsHelper is a singleton. + * + */ +public class MCLiteralsDecoder { + + /** + * Decodes a char literal into a char + * + * @param s char literal as string including "'" + * @return decoded char + */ + public static char decodeChar(String s) { + if (s.length() == 1) { // single char + return s.charAt(0); + } + else if (s.length() == 2 && s.charAt(0) == '\\') { // escape sequence + switch (s.charAt(1)) { + case 'b': + return '\b'; + case 't': + return '\t'; + case 'n': + return '\n'; + case 'f': + return '\f'; + case 'r': + return '\r'; + case '"': + return '\"'; + case '\'': + return '\''; + case '\\': + return '\\'; + default: + break; + } + } + else if (s.charAt(0) == '\\' && s.charAt(1) == 'u') { // unicode + return (char) Integer.parseInt(s.substring(2), 16); + } + Log.error("0xA4080 Unable to convert String " + s + " to char."); + // Noramlly this statement is not reachable + throw new IllegalStateException(); + } + + /** + * Decodes a string literal into a string + * + * @param s string literal excluding '"' + * @return decoded string + */ + public static String decodeString(String s) { + StringBuilder ret = new StringBuilder(); + String in = s; + + while (in.length() != 0) { + if (in.charAt(0) == '\\') { + if (in.charAt(1) == 'u') { // unicode + ret.append(decodeChar(in.substring(0, 6))); + in = in.substring(6); + } + else { // escape sequence + ret.append(decodeChar(in.substring(0, 2))); + in = in.substring(2); + } + } + else { // single char + ret.append(in.charAt(0)); + in = in.substring(1); + } + } + return ret.toString(); + } + + /** + * Decodes an int literal into an int + * + * @param s int literal as string including '"' + * @return decoded int + */ + public static int decodeInt(String s) { + int radix = 10; + String in = removeUnderscores(s); + if (in.startsWith("0") && in.length() > 1) { + if (in.startsWith("0x") || in.startsWith("0X")) { + return Integer.parseInt(in.substring(2), 16); + } + else if (in.startsWith("0b") || in.startsWith("0B")) { + return Integer.parseInt(in.substring(2), 2); + } + else { + radix = 8; + } + } + return Integer.parseInt(in, radix); + } + + /** + * Decodes an int literal into an int + * + * @param s int literal as string including '"' + * @return decoded int + */ + public static int decodeNat(String s) { + int radix = 10; + String in = removeUnderscores(s); + return Integer.parseInt(in, radix); + } + + /** + * Decodes a long literal into a long + * + * @param s long literal as string including '"' + * @return decoded long + */ + public static long decodeLong(String s) { + int radix = 10; + s = s.substring(0, s.length() - 1); + String in = removeUnderscores(s); + if (in.startsWith("0") && in.length() > 2) { + if (in.startsWith("0x") || in.startsWith("0X")) { + radix = 16; + in = in.substring(2); + } + else if (in.startsWith("0b") || in.startsWith("0B")) { + radix = 2; + in = in.substring(2); + } + else { + radix = 8; + } + } + return Long.parseLong(in, radix); + } + + /** + * Decodes a float literal into a float + * + * @param s float literal as string including '"' + * @return decoded float + */ + public static float decodeFloat(String s) { + s = s.substring(0, s.length() - 1); + s = removeUnderscores(s); + if (s.startsWith("0x") || s.startsWith("0X")) { + return Float.valueOf(s); + } + // workaround as parseFloat() does not parse 0xp1F correctly + if (s.toLowerCase().startsWith("0xp")) { + return Float.parseFloat("0x0p" + s.substring(3)); // 0xp1F == 0x0p1F == 0.0 + } + return Float.parseFloat(s); + } + + /** + * Decodes a double literal into a double + * + * @param s double literal as string including '"' + * @return decoded double + */ + public static double decodeDouble(String s) { + if (s.endsWith("d") || s.endsWith("D")) { + s = s.substring(0, s.length() - 1); + } + s = removeUnderscores(s); + if (s.startsWith("0x") || s.startsWith("0X")) { + return Double.valueOf(s); + } + // workaround as parseDouble() does not parse 0xp1 correctly + if (s.toLowerCase().startsWith("0xp")) { + return Double.parseDouble("0x0p" + s.substring(3)); // 0xp1 == 0x0p1 == 0.0 + } + return Double.parseDouble(s); + } + + protected static String removeUnderscores(String s) { + if (s.contains("_")) { + + if (s.indexOf("_") == 0) { + Log.error("0xA4112 Do not put underscores at the beginning of the Number " + s); + } + + if (s.contains("e") + && (s.indexOf("_") == s.indexOf("e") - 1 || s.indexOf("_") == s.indexOf("e") + 1)) { + Log.error("0xA4082 Do not put underscores before or after an 'e' in the Number" + s); + } + + if (s.contains("p") + && (s.indexOf("_") == s.indexOf("p") - 1 || s.indexOf("_") == s.indexOf("p") + 1)) { + Log.error("0xA4083 Do not put underscores before or after an 'p' in the Number" + s); + } + + if (s.contains(".") + && (s.indexOf("_") == s.indexOf(".") - 1 || s.indexOf("_") == s.indexOf(".") + 1)) { + Log.error("0xA4084 Do not put underscores before or after an '.' in the Number" + s); + } + + if (s.startsWith("0x") || s.startsWith("0X") || s.startsWith("0b") || s.startsWith("0B")) { + if (s.indexOf("_") == 2) { + Log.error("0xA4113 Do not put underscores at the beginning of the Number " + s); + } + } + if (s.endsWith("_")) { + Log.error("0xA4081 Do not put underscores at the end of the Number " + s); + } + s = s.replaceAll("_", ""); + } + + return s; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/literals/mccommonliterals/_prettyprint/MCCommonLiteralsPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/literals/mccommonliterals/_prettyprint/MCCommonLiteralsPrettyPrinter.java new file mode 100644 index 0000000000..784dce627d --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/literals/mccommonliterals/_prettyprint/MCCommonLiteralsPrettyPrinter.java @@ -0,0 +1,38 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.literals.mccommonliterals._prettyprint; + +import de.monticore.prettyprint.IndentPrinter; + +/** + * This hand-written literals pretty printer handles the noSpace option + * and further special cases for string literals + */ +public class MCCommonLiteralsPrettyPrinter + extends MCCommonLiteralsPrettyPrinterTOP { + + public MCCommonLiteralsPrettyPrinter(IndentPrinter printer, boolean printComments) { + super(printer, printComments); + } + + @Override + public void handle(de.monticore.literals.mccommonliterals._ast.ASTCharLiteral node) { + if (this.isPrintComments()) { + de.monticore.prettyprint.CommentPrettyPrinter.printPreComments(node, getPrinter()); + } + getPrinter().print("'" + node.getSource() + "'"); + if (this.isPrintComments()) { + de.monticore.prettyprint.CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + } + + @Override + public void handle(de.monticore.literals.mccommonliterals._ast.ASTStringLiteral node) { + if (this.isPrintComments()) { + de.monticore.prettyprint.CommentPrettyPrinter.printPreComments(node, getPrinter()); + } + getPrinter().print("\"" + node.getSource() + "\""); + if (this.isPrintComments()) { + de.monticore.prettyprint.CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/literals/mccommonliterals/cocos/BasicDoubleLiteralRangeCoCo.java b/monticore-grammar/src/main/java/de/monticore/literals/mccommonliterals/cocos/BasicDoubleLiteralRangeCoCo.java new file mode 100644 index 0000000000..8e5b1bc9e6 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/literals/mccommonliterals/cocos/BasicDoubleLiteralRangeCoCo.java @@ -0,0 +1,37 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.literals.mccommonliterals.cocos; + +import de.monticore.literals.mccommonliterals._ast.ASTBasicDoubleLiteral; +import de.monticore.literals.mccommonliterals._cocos.MCCommonLiteralsASTBasicDoubleLiteralCoCo; +import de.se_rwth.commons.logging.Log; + +import java.math.BigDecimal; + +public class BasicDoubleLiteralRangeCoCo implements MCCommonLiteralsASTBasicDoubleLiteralCoCo { + + public static final String ERROR_MSG = " number %s not in range [%s,%s] for BasicDoubleLiteral"; + public static final String ERROR_CODE = "0xA0212"; + + protected BigDecimal min; + protected BigDecimal max; + + public BasicDoubleLiteralRangeCoCo(){ + this.min = BigDecimal.valueOf(-Double.MAX_VALUE); + this.max = BigDecimal.valueOf(Double.MAX_VALUE); + } + + public BasicDoubleLiteralRangeCoCo(BigDecimal min, BigDecimal max){ + this.min = min; + this.max = max; + } + + + @Override + public void check(ASTBasicDoubleLiteral node) { + BigDecimal nodeValue = new BigDecimal(node.getSource()); + if(nodeValue.compareTo(this.min) < 0 || nodeValue.compareTo(this.max) > 0) { + Log.error(String.format(ERROR_CODE + ERROR_MSG, nodeValue, min, max)); + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/literals/mccommonliterals/cocos/BasicFloatLiteralRangeCoCo.java b/monticore-grammar/src/main/java/de/monticore/literals/mccommonliterals/cocos/BasicFloatLiteralRangeCoCo.java new file mode 100644 index 0000000000..16dcc5a59a --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/literals/mccommonliterals/cocos/BasicFloatLiteralRangeCoCo.java @@ -0,0 +1,37 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.literals.mccommonliterals.cocos; + +import de.monticore.literals.mccommonliterals._ast.ASTBasicFloatLiteral; +import de.monticore.literals.mccommonliterals._cocos.MCCommonLiteralsASTBasicFloatLiteralCoCo; +import de.se_rwth.commons.logging.Log; + +import java.math.BigDecimal; + +public class BasicFloatLiteralRangeCoCo implements MCCommonLiteralsASTBasicFloatLiteralCoCo { + + public static final String ERROR_MSG = " number %s not in range [%s,%s] for BasicFloatLiteral"; + public static final String ERROR_CODE = "0xA0213"; + + protected BigDecimal min; + protected BigDecimal max; + + public BasicFloatLiteralRangeCoCo(){ + this.min = BigDecimal.valueOf(-Float.MAX_VALUE); + this.max = BigDecimal.valueOf(Float.MAX_VALUE); + } + + public BasicFloatLiteralRangeCoCo(BigDecimal min, BigDecimal max){ + this.min = min; + this.max = max; + } + + + @Override + public void check(ASTBasicFloatLiteral node) { + BigDecimal nodeValue = new BigDecimal(node.getSource().substring(0, node.getSource().length()-1)); + if(nodeValue.compareTo(this.min) < 0 || nodeValue.compareTo(this.max) > 0) { + Log.error(String.format(ERROR_CODE + ERROR_MSG, nodeValue, min, max)); + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/literals/mccommonliterals/cocos/BasicLongLiteralRangeCoCo.java b/monticore-grammar/src/main/java/de/monticore/literals/mccommonliterals/cocos/BasicLongLiteralRangeCoCo.java new file mode 100644 index 0000000000..90ce3113a0 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/literals/mccommonliterals/cocos/BasicLongLiteralRangeCoCo.java @@ -0,0 +1,37 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.literals.mccommonliterals.cocos; + +import de.monticore.literals.mccommonliterals._ast.ASTBasicLongLiteral; +import de.monticore.literals.mccommonliterals._cocos.MCCommonLiteralsASTBasicLongLiteralCoCo; +import de.se_rwth.commons.logging.Log; + +import java.math.BigInteger; + +public class BasicLongLiteralRangeCoCo implements MCCommonLiteralsASTBasicLongLiteralCoCo { + + public static final String ERROR_MSG = " number %s not in range [%s,%s] for BasicLongLiteral"; + public static final String ERROR_CODE = "0xA0209"; + + protected BigInteger min; + protected BigInteger max; + + public BasicLongLiteralRangeCoCo(){ + this.min = BigInteger.valueOf(Long.MIN_VALUE); + this.max = BigInteger.valueOf(Long.MAX_VALUE); + } + + public BasicLongLiteralRangeCoCo(BigInteger min, BigInteger max){ + this.min = min; + this.max = max; + } + + + @Override + public void check(ASTBasicLongLiteral node) { + BigInteger nodeValue = new BigInteger(node.getSource().substring(0, node.getSource().length()-1)); + if(nodeValue.compareTo(this.min) < 0 || nodeValue.compareTo(this.max) > 0) { + Log.error(String.format(ERROR_CODE + ERROR_MSG, nodeValue, min, max)); + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/literals/mccommonliterals/cocos/NatLiteralRangeCoCo.java b/monticore-grammar/src/main/java/de/monticore/literals/mccommonliterals/cocos/NatLiteralRangeCoCo.java new file mode 100644 index 0000000000..b62d7139ad --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/literals/mccommonliterals/cocos/NatLiteralRangeCoCo.java @@ -0,0 +1,37 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.literals.mccommonliterals.cocos; + +import de.monticore.literals.mccommonliterals._ast.ASTNatLiteral; +import de.monticore.literals.mccommonliterals._cocos.MCCommonLiteralsASTNatLiteralCoCo; +import de.se_rwth.commons.logging.Log; + +import java.math.BigInteger; + +public class NatLiteralRangeCoCo implements MCCommonLiteralsASTNatLiteralCoCo { + + public static final String ERROR_MSG = " number %s not in range [%s,%s] for NatLiteral"; + public static final String ERROR_CODE = "0xA0208"; + + protected BigInteger min; + protected BigInteger max; + + public NatLiteralRangeCoCo(){ + this.min = BigInteger.valueOf(Integer.MIN_VALUE); + this.max = BigInteger.valueOf(Integer.MAX_VALUE); + } + + public NatLiteralRangeCoCo(BigInteger min, BigInteger max){ + this.min = min; + this.max = max; + } + + + @Override + public void check(ASTNatLiteral node) { + BigInteger nodeValue = new BigInteger(node.getSource()); + if(nodeValue.compareTo(this.min) < 0 || nodeValue.compareTo(this.max) > 0) { + Log.error(String.format(ERROR_CODE + ERROR_MSG, nodeValue, min, max)); + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/literals/mccommonliterals/cocos/SignedBasicDoubleLiteralRangeCoCo.java b/monticore-grammar/src/main/java/de/monticore/literals/mccommonliterals/cocos/SignedBasicDoubleLiteralRangeCoCo.java new file mode 100644 index 0000000000..5509a0eef4 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/literals/mccommonliterals/cocos/SignedBasicDoubleLiteralRangeCoCo.java @@ -0,0 +1,37 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.literals.mccommonliterals.cocos; + +import de.monticore.literals.mccommonliterals._ast.ASTSignedBasicDoubleLiteral; +import de.monticore.literals.mccommonliterals._cocos.MCCommonLiteralsASTSignedBasicDoubleLiteralCoCo; +import de.se_rwth.commons.logging.Log; + +import java.math.BigDecimal; + +public class SignedBasicDoubleLiteralRangeCoCo implements MCCommonLiteralsASTSignedBasicDoubleLiteralCoCo { + + public static final String ERROR_MSG = " number %s not in range [%s,%s] for SignedBasicDoubleLiteral"; + public static final String ERROR_CODE = "0xA0214"; + + protected BigDecimal min; + protected BigDecimal max; + + public SignedBasicDoubleLiteralRangeCoCo(){ + this.min = BigDecimal.valueOf(-Double.MAX_VALUE); + this.max = BigDecimal.valueOf(Double.MAX_VALUE); + } + + public SignedBasicDoubleLiteralRangeCoCo(BigDecimal min, BigDecimal max){ + this.min = min; + this.max = max; + } + + + @Override + public void check(ASTSignedBasicDoubleLiteral node) { + BigDecimal nodeValue = new BigDecimal(node.getSource()); + if(nodeValue.compareTo(this.min) < 0 || nodeValue.compareTo(this.max) > 0) { + Log.error(String.format(ERROR_CODE + ERROR_MSG, nodeValue, min, max)); + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/literals/mccommonliterals/cocos/SignedBasicFloatLiteralRangeCoCo.java b/monticore-grammar/src/main/java/de/monticore/literals/mccommonliterals/cocos/SignedBasicFloatLiteralRangeCoCo.java new file mode 100644 index 0000000000..36d978ec4a --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/literals/mccommonliterals/cocos/SignedBasicFloatLiteralRangeCoCo.java @@ -0,0 +1,37 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.literals.mccommonliterals.cocos; + +import de.monticore.literals.mccommonliterals._ast.ASTSignedBasicFloatLiteral; +import de.monticore.literals.mccommonliterals._cocos.MCCommonLiteralsASTSignedBasicFloatLiteralCoCo; +import de.se_rwth.commons.logging.Log; + +import java.math.BigDecimal; + +public class SignedBasicFloatLiteralRangeCoCo implements MCCommonLiteralsASTSignedBasicFloatLiteralCoCo { + + public static final String ERROR_MSG = " number %s not in range [%s,%s] for SignedBasicFloatLiteral"; + public static final String ERROR_CODE = "0xA0215"; + + protected BigDecimal min; + protected BigDecimal max; + + public SignedBasicFloatLiteralRangeCoCo(){ + this.min = BigDecimal.valueOf(-Float.MAX_VALUE); + this.max = BigDecimal.valueOf(Float.MAX_VALUE); + } + + public SignedBasicFloatLiteralRangeCoCo(BigDecimal min, BigDecimal max){ + this.min = min; + this.max = max; + } + + + @Override + public void check(ASTSignedBasicFloatLiteral node) { + BigDecimal nodeValue = new BigDecimal(node.getSource().substring(0, node.getSource().length()-1)); + if(nodeValue.compareTo(this.min) < 0 || nodeValue.compareTo(this.max) > 0) { + Log.error(String.format(ERROR_CODE + ERROR_MSG, nodeValue, min, max)); + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/literals/mccommonliterals/cocos/SignedBasicLongLiteralRangeCoCo.java b/monticore-grammar/src/main/java/de/monticore/literals/mccommonliterals/cocos/SignedBasicLongLiteralRangeCoCo.java new file mode 100644 index 0000000000..4f67c246a9 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/literals/mccommonliterals/cocos/SignedBasicLongLiteralRangeCoCo.java @@ -0,0 +1,37 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.literals.mccommonliterals.cocos; + +import de.monticore.literals.mccommonliterals._ast.ASTSignedBasicLongLiteral; +import de.monticore.literals.mccommonliterals._cocos.MCCommonLiteralsASTSignedBasicLongLiteralCoCo; +import de.se_rwth.commons.logging.Log; + +import java.math.BigInteger; + +public class SignedBasicLongLiteralRangeCoCo implements MCCommonLiteralsASTSignedBasicLongLiteralCoCo { + + public static final String ERROR_MSG = " number %s not in range [%s,%s] for SignedBasicLongLiteral"; + public static final String ERROR_CODE = "0xA0211"; + + protected BigInteger min; + protected BigInteger max; + + public SignedBasicLongLiteralRangeCoCo(){ + this.min = BigInteger.valueOf(Long.MIN_VALUE); + this.max = BigInteger.valueOf(Long.MAX_VALUE); + } + + public SignedBasicLongLiteralRangeCoCo(BigInteger min, BigInteger max){ + this.min = min; + this.max = max; + } + + + @Override + public void check(ASTSignedBasicLongLiteral node) { + BigInteger nodeValue = new BigInteger(node.getSource().substring(0, node.getSource().length()-1)); + if(nodeValue.compareTo(this.min) < 0 || nodeValue.compareTo(this.max) > 0) { + Log.error(String.format(ERROR_CODE + ERROR_MSG, nodeValue, min, max)); + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/literals/mccommonliterals/cocos/SignedNatLiteralRangeCoCo.java b/monticore-grammar/src/main/java/de/monticore/literals/mccommonliterals/cocos/SignedNatLiteralRangeCoCo.java new file mode 100644 index 0000000000..9fd5693f9a --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/literals/mccommonliterals/cocos/SignedNatLiteralRangeCoCo.java @@ -0,0 +1,37 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.literals.mccommonliterals.cocos; + +import de.monticore.literals.mccommonliterals._ast.ASTSignedNatLiteral; +import de.monticore.literals.mccommonliterals._cocos.MCCommonLiteralsASTSignedNatLiteralCoCo; +import de.se_rwth.commons.logging.Log; + +import java.math.BigInteger; + +public class SignedNatLiteralRangeCoCo implements MCCommonLiteralsASTSignedNatLiteralCoCo { + + public static final String ERROR_MSG = " number %s not in range [%s,%s] for SignedNatLiteral"; + public static final String ERROR_CODE = "0xA0210"; + + protected BigInteger min; + protected BigInteger max; + + public SignedNatLiteralRangeCoCo(){ + this.min = BigInteger.valueOf(Integer.MIN_VALUE); + this.max = BigInteger.valueOf(Integer.MAX_VALUE); + } + + public SignedNatLiteralRangeCoCo(BigInteger min, BigInteger max){ + this.min = min; + this.max = max; + } + + + @Override + public void check(ASTSignedNatLiteral node) { + BigInteger nodeValue = new BigInteger(node.getSource()); + if(nodeValue.compareTo(this.min) < 0 || nodeValue.compareTo(this.max) > 0) { + Log.error(String.format(ERROR_CODE + ERROR_MSG, nodeValue, min, max)); + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/literals/mccommonliterals/types3/MCCommonLiteralsTypeVisitor.java b/monticore-grammar/src/main/java/de/monticore/literals/mccommonliterals/types3/MCCommonLiteralsTypeVisitor.java new file mode 100644 index 0000000000..af6fd8d58f --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/literals/mccommonliterals/types3/MCCommonLiteralsTypeVisitor.java @@ -0,0 +1,144 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.literals.mccommonliterals.types3; + +import de.monticore.literals.mccommonliterals._ast.ASTBasicDoubleLiteral; +import de.monticore.literals.mccommonliterals._ast.ASTBasicFloatLiteral; +import de.monticore.literals.mccommonliterals._ast.ASTBasicLongLiteral; +import de.monticore.literals.mccommonliterals._ast.ASTBooleanLiteral; +import de.monticore.literals.mccommonliterals._ast.ASTCharLiteral; +import de.monticore.literals.mccommonliterals._ast.ASTNatLiteral; +import de.monticore.literals.mccommonliterals._ast.ASTNullLiteral; +import de.monticore.literals.mccommonliterals._ast.ASTSignedBasicDoubleLiteral; +import de.monticore.literals.mccommonliterals._ast.ASTSignedBasicFloatLiteral; +import de.monticore.literals.mccommonliterals._ast.ASTSignedBasicLongLiteral; +import de.monticore.literals.mccommonliterals._ast.ASTSignedLiteral; +import de.monticore.literals.mccommonliterals._ast.ASTSignedNatLiteral; +import de.monticore.literals.mccommonliterals._ast.ASTStringLiteral; +import de.monticore.literals.mccommonliterals._visitor.MCCommonLiteralsVisitor2; +import de.monticore.literals.mcliteralsbasis._ast.ASTLiteral; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types3.AbstractTypeVisitor; +import de.se_rwth.commons.SourcePosition; +import de.se_rwth.commons.logging.Log; + +import java.util.Optional; + +/** + * Visitor for Derivation of SymType from Literals + * i.e. for + * literals/MCLiteralsBasis.mc4 + */ +public class MCCommonLiteralsTypeVisitor extends AbstractTypeVisitor + implements MCCommonLiteralsVisitor2 { + + @Override + public void endVisit(ASTNatLiteral lit) { + derivePrimitive(lit, BasicSymbolsMill.INT); + } + + @Override + public void endVisit(ASTCharLiteral lit) { + derivePrimitive((ASTLiteral) lit, BasicSymbolsMill.CHAR); + } + + @Override + public void endVisit(ASTBooleanLiteral lit) { + derivePrimitive((ASTLiteral) lit, BasicSymbolsMill.BOOLEAN); + } + + @Override + public void endVisit(ASTBasicDoubleLiteral lit) { + derivePrimitive(lit, BasicSymbolsMill.DOUBLE); + } + + @Override + public void endVisit(ASTBasicFloatLiteral lit) { + derivePrimitive(lit, BasicSymbolsMill.FLOAT); + } + + @Override + public void endVisit(ASTBasicLongLiteral lit) { + derivePrimitive(lit, BasicSymbolsMill.LONG); + } + + @Override + public void endVisit(ASTStringLiteral lit) { + // tries to find String + // String added into global scope analogous to primitive types + Optional stringType = + BasicSymbolsMill.globalScope().resolveType("String"); + // otherwise, java.util.String, most likely per Class2MC + if (stringType.isEmpty()) { + stringType = getAsBasicSymbolsScope(lit.getEnclosingScope()) + .resolveType("java.lang.String"); + } + if (stringType.isPresent()) { + getType4Ast().setTypeOfExpression((ASTLiteral) lit, + SymTypeExpressionFactory.createTypeObject(stringType.get()) + ); + } + else { + Log.error("0xD02A6 The type String could not be resolved."); + getType4Ast().setTypeOfExpression((ASTLiteral) lit, + SymTypeExpressionFactory.createObscureType()); + } + } + + @Override + public void endVisit(ASTSignedNatLiteral lit) { + derivePrimitive(lit, BasicSymbolsMill.INT); + } + + @Override + public void endVisit(ASTSignedBasicDoubleLiteral lit) { + derivePrimitive(lit, BasicSymbolsMill.DOUBLE); + } + + @Override + public void endVisit(ASTSignedBasicFloatLiteral lit) { + derivePrimitive(lit, BasicSymbolsMill.FLOAT); + } + + @Override + public void endVisit(ASTSignedBasicLongLiteral lit) { + derivePrimitive(lit, BasicSymbolsMill.LONG); + } + + protected void derivePrimitive(ASTLiteral lit, String primitive) { + getType4Ast().setTypeOfExpression( + lit, getPrimitive(primitive, lit.get_SourcePositionStart())); + } + + protected void derivePrimitive(ASTSignedLiteral lit, String primitive) { + getType4Ast().setTypeOfExpression( + lit, getPrimitive(primitive, lit.get_SourcePositionStart())); + } + + /** + * Literal "null" gets marked with implicit SymType _null + */ + @Override + public void endVisit(ASTNullLiteral lit) { + getType4Ast() + .setTypeOfExpression((ASTLiteral) lit, + SymTypeExpressionFactory.createTypeOfNull()); + } + + // Helper + + protected SymTypeExpression getPrimitive(String type, SourcePosition pos) { + Optional primitive = BasicSymbolsMill.globalScope().resolveType(type); + if (primitive.isPresent()) { + return SymTypeExpressionFactory.createPrimitive(primitive.get()); + } + else { + Log.error("0xD0207 The primitive type " + type + " could not be resolved." + + "Did you add primitive types to your language?", pos); + return SymTypeExpressionFactory.createObscureType(); + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/literals/mcjavaliterals/cocos/DoubleLiteralRangeCoCo.java b/monticore-grammar/src/main/java/de/monticore/literals/mcjavaliterals/cocos/DoubleLiteralRangeCoCo.java new file mode 100644 index 0000000000..a9d698bc0e --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/literals/mcjavaliterals/cocos/DoubleLiteralRangeCoCo.java @@ -0,0 +1,37 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.literals.mcjavaliterals.cocos; + +import de.monticore.literals.mcjavaliterals._ast.ASTDoubleLiteral; +import de.monticore.literals.mcjavaliterals._cocos.MCJavaLiteralsASTDoubleLiteralCoCo; +import de.se_rwth.commons.logging.Log; + +import java.math.BigDecimal; + +public class DoubleLiteralRangeCoCo implements MCJavaLiteralsASTDoubleLiteralCoCo { + + public static final String ERROR_MSG = " number %s not in range [%s,%s] for DoubleLiteral"; + public static final String ERROR_CODE = "0xA0218"; + + protected BigDecimal min; + protected BigDecimal max; + + public DoubleLiteralRangeCoCo(){ + this.min = BigDecimal.valueOf(-Double.MAX_VALUE); + this.max = BigDecimal.valueOf(Double.MAX_VALUE); + } + + public DoubleLiteralRangeCoCo(BigDecimal min, BigDecimal max){ + this.min = min; + this.max = max; + } + + + @Override + public void check(ASTDoubleLiteral node) { + BigDecimal nodeValue = new BigDecimal(node.getSource()); + if(nodeValue.compareTo(this.min) < 0 || nodeValue.compareTo(this.max) > 0) { + Log.error(String.format(ERROR_CODE + ERROR_MSG, nodeValue, min, max)); + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/literals/mcjavaliterals/cocos/FloatLiteralRangeCoCo.java b/monticore-grammar/src/main/java/de/monticore/literals/mcjavaliterals/cocos/FloatLiteralRangeCoCo.java new file mode 100644 index 0000000000..dce6846477 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/literals/mcjavaliterals/cocos/FloatLiteralRangeCoCo.java @@ -0,0 +1,37 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.literals.mcjavaliterals.cocos; + +import de.monticore.literals.mcjavaliterals._ast.ASTFloatLiteral; +import de.monticore.literals.mcjavaliterals._cocos.MCJavaLiteralsASTFloatLiteralCoCo; +import de.se_rwth.commons.logging.Log; + +import java.math.BigDecimal; + +public class FloatLiteralRangeCoCo implements MCJavaLiteralsASTFloatLiteralCoCo { + + public static final String ERROR_MSG = " number %s not in range [%s,%s] for FloatLiteral"; + public static final String ERROR_CODE = "0xA0219"; + + protected BigDecimal min; + protected BigDecimal max; + + public FloatLiteralRangeCoCo(){ + this.min = BigDecimal.valueOf(-Float.MAX_VALUE); + this.max = BigDecimal.valueOf(Float.MAX_VALUE); + } + + public FloatLiteralRangeCoCo(BigDecimal min, BigDecimal max){ + this.min = min; + this.max = max; + } + + + @Override + public void check(ASTFloatLiteral node) { + BigDecimal nodeValue = new BigDecimal(node.getSource().substring(0, node.getSource().length()-1)); + if(nodeValue.compareTo(this.min) < 0 || nodeValue.compareTo(this.max) > 0) { + Log.error(String.format(ERROR_CODE + ERROR_MSG, nodeValue, min, max)); + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/literals/mcjavaliterals/cocos/IntLiteralRangeCoCo.java b/monticore-grammar/src/main/java/de/monticore/literals/mcjavaliterals/cocos/IntLiteralRangeCoCo.java new file mode 100644 index 0000000000..747d480147 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/literals/mcjavaliterals/cocos/IntLiteralRangeCoCo.java @@ -0,0 +1,51 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.literals.mcjavaliterals.cocos; + +import de.monticore.literals.mcjavaliterals._ast.ASTIntLiteral; +import de.monticore.literals.mcjavaliterals._cocos.MCJavaLiteralsASTIntLiteralCoCo; +import de.se_rwth.commons.logging.Log; + +import java.math.BigInteger; + +public class IntLiteralRangeCoCo implements MCJavaLiteralsASTIntLiteralCoCo { + + public static final String ERROR_MSG = " number %s not in range [%s,%s] for IntLiteral"; + public static final String ERROR_CODE = "0xA0216"; + + protected BigInteger min; + protected BigInteger max; + + public IntLiteralRangeCoCo(){ + this.min = BigInteger.valueOf(Integer.MIN_VALUE); + this.max = BigInteger.valueOf(Integer.MAX_VALUE); + } + + public IntLiteralRangeCoCo(BigInteger min, BigInteger max){ + this.min = min; + this.max = max; + } + + + @Override + public void check(ASTIntLiteral node) { + BigInteger nodeValue; + if(node.getSource().startsWith("-0x") || node.getSource().startsWith("-0X")){ + nodeValue = new BigInteger("-" + node.getSource().substring(3), 16); + }else if(node.getSource().startsWith("0X") || node.getSource().startsWith("0x")){ + nodeValue = new BigInteger(node.getSource().substring(2), 16); + }else if(node.getSource().startsWith("-0") || node.getSource().startsWith("0")){ + nodeValue = new BigInteger(node.getSource(), 8); + }else if(node.getSource().startsWith("-0b") || node.getSource().startsWith("-0B")){ + nodeValue = new BigInteger("-" + node.getSource().substring(3), 2); + }else if(node.getSource().startsWith("0b") || node.getSource().startsWith("0B")){ + nodeValue = new BigInteger(node.getSource().substring(2), 2); + }else{ + nodeValue = new BigInteger(node.getSource()); + } + if(nodeValue.compareTo(this.min) < 0 || nodeValue.compareTo(this.max) > 0) { + Log.error(String.format(ERROR_CODE + ERROR_MSG, nodeValue, min, max)); + } + } + + +} diff --git a/monticore-grammar/src/main/java/de/monticore/literals/mcjavaliterals/cocos/LongLiteralRangeCoCo.java b/monticore-grammar/src/main/java/de/monticore/literals/mcjavaliterals/cocos/LongLiteralRangeCoCo.java new file mode 100644 index 0000000000..e02c724b26 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/literals/mcjavaliterals/cocos/LongLiteralRangeCoCo.java @@ -0,0 +1,51 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.literals.mcjavaliterals.cocos; + +import de.monticore.literals.mcjavaliterals._ast.ASTLongLiteral; +import de.monticore.literals.mcjavaliterals._cocos.MCJavaLiteralsASTLongLiteralCoCo; +import de.se_rwth.commons.logging.Log; + +import java.math.BigInteger; + +public class LongLiteralRangeCoCo implements MCJavaLiteralsASTLongLiteralCoCo { + + public static final String ERROR_MSG = " number %s not in range [%s,%s] for LongLiteral"; + public static final String ERROR_CODE = "0xA0217"; + + protected BigInteger min; + protected BigInteger max; + + public LongLiteralRangeCoCo(){ + this.min = BigInteger.valueOf(Long.MIN_VALUE); + this.max = BigInteger.valueOf(Long.MAX_VALUE); + } + + public LongLiteralRangeCoCo(BigInteger min, BigInteger max){ + this.min = min; + this.max = max; + } + + + @Override + public void check(ASTLongLiteral node) { + BigInteger nodeValue; + String source = node.getSource().substring(0, node.getSource().length()-1); + if(source.startsWith("-0x") || source.startsWith("-0X")){ + nodeValue = new BigInteger("-" + source.substring(3), 16); + }else if(source.startsWith("0X") || source.startsWith("0x")){ + nodeValue = new BigInteger(source.substring(2), 16); + }else if(source.startsWith("-0") || source.startsWith("0")){ + nodeValue = new BigInteger(source, 8); + }else if(source.startsWith("-0b") || source.startsWith("-0B")){ + nodeValue = new BigInteger("-" + source.substring(3), 2); + }else if(source.startsWith("0b") || source.startsWith("0B")){ + nodeValue = new BigInteger(source.substring(2), 2); + }else{ + nodeValue = new BigInteger(source); + } + if(nodeValue.compareTo(this.min) < 0 || nodeValue.compareTo(this.max) > 0) { + Log.error(String.format(ERROR_CODE + ERROR_MSG, nodeValue, min, max)); + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/literals/mcjavaliterals/types3/MCJavaLiteralsTypeVisitor.java b/monticore-grammar/src/main/java/de/monticore/literals/mcjavaliterals/types3/MCJavaLiteralsTypeVisitor.java new file mode 100644 index 0000000000..35e083bd9e --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/literals/mcjavaliterals/types3/MCJavaLiteralsTypeVisitor.java @@ -0,0 +1,39 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.literals.mcjavaliterals.types3; + +import de.monticore.literals.mcjavaliterals._visitor.MCJavaLiteralsVisitor2; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types3.AbstractTypeVisitor; + +public class MCJavaLiteralsTypeVisitor extends AbstractTypeVisitor + implements MCJavaLiteralsVisitor2 { + + public void endVisit(de.monticore.literals.mcjavaliterals._ast.ASTIntLiteral node) { + getType4Ast().setTypeOfExpression( + node, + SymTypeExpressionFactory.createPrimitive(BasicSymbolsMill.INT) + ); + } + + public void endVisit(de.monticore.literals.mcjavaliterals._ast.ASTLongLiteral node) { + getType4Ast().setTypeOfExpression( + node, + SymTypeExpressionFactory.createPrimitive(BasicSymbolsMill.LONG) + ); + } + + public void endVisit(de.monticore.literals.mcjavaliterals._ast.ASTFloatLiteral node) { + getType4Ast().setTypeOfExpression( + node, + SymTypeExpressionFactory.createPrimitive(BasicSymbolsMill.FLOAT) + ); + } + + public void endVisit(de.monticore.literals.mcjavaliterals._ast.ASTDoubleLiteral node) { + getType4Ast().setTypeOfExpression( + node, + SymTypeExpressionFactory.createPrimitive(BasicSymbolsMill.DOUBLE) + ); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/literals/prettyprint/MCCommonLiteralsFullPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/literals/prettyprint/MCCommonLiteralsFullPrettyPrinter.java new file mode 100644 index 0000000000..d6b6b03542 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/literals/prettyprint/MCCommonLiteralsFullPrettyPrinter.java @@ -0,0 +1,49 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.literals.prettyprint; + +import de.monticore.literals.mccommonliterals.MCCommonLiteralsMill; +import de.monticore.literals.mccommonliterals._ast.ASTMCCommonLiteralsNode; +import de.monticore.literals.mccommonliterals._visitor.MCCommonLiteralsTraverser; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.prettyprint.MCBasicsPrettyPrinter; + +@Deprecated(forRemoval = true) +public class MCCommonLiteralsFullPrettyPrinter { + + protected MCCommonLiteralsTraverser traverser; + + protected IndentPrinter printer; + + public MCCommonLiteralsFullPrettyPrinter(IndentPrinter printer){ + this.printer = printer; + traverser = MCCommonLiteralsMill.traverser(); + + MCCommonLiteralsPrettyPrinter commonLiterals = new MCCommonLiteralsPrettyPrinter(printer); + traverser.setMCCommonLiteralsHandler(commonLiterals); + traverser.add4MCCommonLiterals(commonLiterals); + + MCBasicsPrettyPrinter basics = new MCBasicsPrettyPrinter(printer); + traverser.add4MCBasics(basics); + } + + public MCCommonLiteralsTraverser getTraverser() { + return traverser; + } + + public void setTraverser(MCCommonLiteralsTraverser traverser) { + this.traverser = traverser; + } + + public IndentPrinter getPrinter() { + return printer; + } + + public void setPrinter(IndentPrinter printer) { + this.printer = printer; + } + + public String prettyprint(ASTMCCommonLiteralsNode node){ + node.accept(getTraverser()); + return printer.getContent(); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/literals/prettyprint/MCCommonLiteralsPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/literals/prettyprint/MCCommonLiteralsPrettyPrinter.java new file mode 100644 index 0000000000..de97edd086 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/literals/prettyprint/MCCommonLiteralsPrettyPrinter.java @@ -0,0 +1,153 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.literals.prettyprint; + +import de.monticore.literals.mccommonliterals._ast.*; +import de.monticore.literals.mccommonliterals._visitor.MCCommonLiteralsHandler; +import de.monticore.literals.mccommonliterals._visitor.MCCommonLiteralsTraverser; +import de.monticore.literals.mccommonliterals._visitor.MCCommonLiteralsVisitor2; +import de.monticore.prettyprint.IndentPrinter; + +@Deprecated(forRemoval = true) +public class MCCommonLiteralsPrettyPrinter implements MCCommonLiteralsVisitor2, MCCommonLiteralsHandler { + + protected MCCommonLiteralsTraverser traverser; + + protected IndentPrinter printer; + + public MCCommonLiteralsPrettyPrinter(IndentPrinter printer) { + this.printer = printer; + } + + @Override + public MCCommonLiteralsTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(MCCommonLiteralsTraverser traverser) { + this.traverser = traverser; + } + + /** + * Prints a "null" literal + * + * @param a null literal + */ + @Override + public void visit(ASTNullLiteral a) { + printer.print("null"); + } + + /** + * Prints a boolean literal + * + * @param a boolean literal + */ + @Override + public void visit(ASTBooleanLiteral a) { + printer.print(a.getValue()); + } + + /** + * Prints a char literal + * + * @param a char literal + */ + @Override + public void visit(ASTCharLiteral a) { + printer.print("'" + a.getSource() + "'"); + } + + /** + * Prints a string literal + * + * @param a string literal + */ + @Override + public void visit(ASTStringLiteral a) { + printer.print("\"" + a.getSource() + "\""); + } + + + /** + * Prints a natural literal + * + * @param a Nat literal + */ + @Override + public void visit(ASTNatLiteral a) { + printer.print(a.getSource()); + } + + /** + * Prints a natural literal + * + * @param a SignedNat literal + */ + @Override + public void visit(ASTSignedNatLiteral a) { + printer.print(a.getSource()); + } + + /** + * Prints a natural literal + * + * @param a long literal + */ + @Override + public void visit(ASTBasicLongLiteral a) { + printer.print(a.getSource()); + } + + /** + * Prints a natural literal + * + * @param a SignedLong literal + */ + @Override + public void visit(ASTSignedBasicLongLiteral a) { + printer.print(a.getSource()); + } + + /** + * Prints a natural literal + * + * @param a double literal + */ + @Override + public void visit(ASTBasicDoubleLiteral a) { + printer.print(a.getSource()); + } + + /** + * Prints a natural literal + * + * @param a SignedDourble literal + */ + @Override + public void visit(ASTSignedBasicDoubleLiteral a) { + printer.print(a.getSource()); + } + + /** + * Prints a natural literal + * + * @param a float literal + */ + @Override + public void visit(ASTBasicFloatLiteral a) { + printer.print(a.getSource()); + } + + /** + * Prints a natural literal + * + * @param a SignedNat literal + */ + @Override + public void visit(ASTSignedBasicFloatLiteral a) { + printer.print(a.getSource()); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/literals/prettyprint/MCJavaLiteralsFullPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/literals/prettyprint/MCJavaLiteralsFullPrettyPrinter.java new file mode 100644 index 0000000000..c45aafb3ac --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/literals/prettyprint/MCJavaLiteralsFullPrettyPrinter.java @@ -0,0 +1,38 @@ +/*(c) https://github.com/MontiCore/monticore*/ +package de.monticore.literals.prettyprint; + +import de.monticore.literals.mcjavaliterals.MCJavaLiteralsMill; +import de.monticore.literals.mcjavaliterals._visitor.MCJavaLiteralsTraverser; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.prettyprint.MCBasicsPrettyPrinter; + +@Deprecated(forRemoval = true) +public class MCJavaLiteralsFullPrettyPrinter extends MCCommonLiteralsFullPrettyPrinter { + + protected MCJavaLiteralsTraverser traverser; + + public MCJavaLiteralsFullPrettyPrinter(IndentPrinter printer) { + super(printer); + this.traverser = MCJavaLiteralsMill.traverser(); + + MCJavaLiteralsPrettyPrinter javaLiterals = new MCJavaLiteralsPrettyPrinter(printer); + traverser.setMCJavaLiteralsHandler(javaLiterals); + traverser.add4MCJavaLiterals(javaLiterals); + + MCCommonLiteralsPrettyPrinter commonLiterals = new MCCommonLiteralsPrettyPrinter(printer); + traverser.setMCCommonLiteralsHandler(commonLiterals); + traverser.add4MCCommonLiterals(commonLiterals); + + MCBasicsPrettyPrinter basics = new MCBasicsPrettyPrinter(printer); + traverser.add4MCBasics(basics); + } + + public void setTraverser(MCJavaLiteralsTraverser traverser) { + this.traverser = traverser; + } + + @Override + public MCJavaLiteralsTraverser getTraverser() { + return traverser; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/literals/prettyprint/MCJavaLiteralsPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/literals/prettyprint/MCJavaLiteralsPrettyPrinter.java new file mode 100644 index 0000000000..f3c6cd7a5f --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/literals/prettyprint/MCJavaLiteralsPrettyPrinter.java @@ -0,0 +1,82 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.literals.prettyprint; + +import de.monticore.literals.mcjavaliterals._ast.*; +import de.monticore.literals.mcjavaliterals._visitor.MCJavaLiteralsHandler; +import de.monticore.literals.mcjavaliterals._visitor.MCJavaLiteralsTraverser; +import de.monticore.literals.mcjavaliterals._visitor.MCJavaLiteralsVisitor2; +import de.monticore.prettyprint.IndentPrinter; + +@Deprecated(forRemoval = true) +public class MCJavaLiteralsPrettyPrinter implements MCJavaLiteralsVisitor2, MCJavaLiteralsHandler { + + protected MCJavaLiteralsTraverser traverser; + + // printer to use + protected IndentPrinter printer; + + /** + * Constructor + * @param printer + */ + public MCJavaLiteralsPrettyPrinter(IndentPrinter printer) { + this.printer = printer; + } + + /** + * @return the printer + */ + public IndentPrinter getPrinter() { + return this.printer; + } + + public MCJavaLiteralsTraverser getTraverser() { + return traverser; + } + + public void setTraverser(MCJavaLiteralsTraverser traverser) { + this.traverser = traverser; + } + + /** + * Prints a int literal + * + * @param a int literal + */ + @Override + public void visit(ASTIntLiteral a) { + printer.print(a.getSource()); + } + + /** + * Prints a long literal + * + * @param a long literal + */ + @Override + public void visit(ASTLongLiteral a) { + printer.print(a.getSource()); + } + + /** + * Prints a float literal + * + * @param a float literal + */ + @Override + public void visit(ASTFloatLiteral a) { + printer.print(a.getSource()); + } + + /** + * Prints a double literal + * + * @param a double literal + */ + @Override + public void visit(ASTDoubleLiteral a) { + printer.print(a.getSource()); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/prettyprint/CardinalityFullPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/prettyprint/CardinalityFullPrettyPrinter.java new file mode 100644 index 0000000000..9d9a5c04c2 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/prettyprint/CardinalityFullPrettyPrinter.java @@ -0,0 +1,50 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.prettyprint; + +import de.monticore.cardinality.CardinalityMill; +import de.monticore.cardinality._ast.ASTCardinalityNode; +import de.monticore.cardinality._visitor.CardinalityTraverser; +import de.monticore.literals.prettyprint.MCCommonLiteralsPrettyPrinter; + +@Deprecated(forRemoval = true) +public class CardinalityFullPrettyPrinter { + + protected CardinalityTraverser traverser; + + protected IndentPrinter printer; + + public CardinalityFullPrettyPrinter(IndentPrinter printer){ + this.printer = printer; + this.traverser = CardinalityMill.traverser(); + + CardinalityPrettyPrinter cardinality = new CardinalityPrettyPrinter(printer); + traverser.add4Cardinality(cardinality); + traverser.setCardinalityHandler(cardinality); + + MCCommonLiteralsPrettyPrinter commonLiterals = new MCCommonLiteralsPrettyPrinter(printer); + traverser.add4MCCommonLiterals(commonLiterals); + traverser.setMCCommonLiteralsHandler(commonLiterals); + + traverser.add4MCBasics(new MCBasicsPrettyPrinter(printer)); + } + + public CardinalityTraverser getTraverser() { + return traverser; + } + + public void setTraverser(CardinalityTraverser traverser) { + this.traverser = traverser; + } + + public IndentPrinter getPrinter() { + return printer; + } + + public String prettyprint(ASTCardinalityNode node) { + getPrinter().clearBuffer(); + node.accept(getTraverser()); + return getPrinter().getContent(); + } + + +} diff --git a/monticore-grammar/src/main/java/de/monticore/prettyprint/CardinalityPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/prettyprint/CardinalityPrettyPrinter.java new file mode 100644 index 0000000000..5f7ef97e44 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/prettyprint/CardinalityPrettyPrinter.java @@ -0,0 +1,56 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.prettyprint; + +import de.monticore.cardinality._ast.ASTCardinality; +import de.monticore.cardinality._ast.ASTCardinalityNode; +import de.monticore.cardinality._visitor.CardinalityHandler; +import de.monticore.cardinality._visitor.CardinalityTraverser; +import de.monticore.cardinality._visitor.CardinalityVisitor2; + +@Deprecated(forRemoval = true) +public class CardinalityPrettyPrinter implements CardinalityVisitor2, CardinalityHandler { + + protected CardinalityTraverser traverser; + + protected IndentPrinter printer; + + public CardinalityPrettyPrinter(IndentPrinter printer) { + this.printer = printer; + } + + @Override + public void handle(ASTCardinality node) { + getPrinter().print("["); + if (node.isMany()) { + getPrinter().print("*"); + } + else { + getPrinter().print(node.getLowerBound()); + if (node.getLowerBound() != node.getUpperBound() || node.isNoUpperLimit()) { + getPrinter().print(".."); + if (node.isNoUpperLimit()) { + getPrinter().print("*"); + } + else { + getPrinter().print(node.getUpperBound()); + } + } + } + getPrinter().print("]"); + } + + @Override + public CardinalityTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(CardinalityTraverser traverser) { + this.traverser = traverser; + } + + public IndentPrinter getPrinter() { + return this.printer; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/prettyprint/CompletenessFullPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/prettyprint/CompletenessFullPrettyPrinter.java new file mode 100644 index 0000000000..cd644e6a1f --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/prettyprint/CompletenessFullPrettyPrinter.java @@ -0,0 +1,41 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.prettyprint; + +import de.monticore.completeness.CompletenessMill; +import de.monticore.completeness._ast.ASTCompletenessNode; +import de.monticore.completeness._visitor.CompletenessTraverser; + +@Deprecated(forRemoval = true) +public class CompletenessFullPrettyPrinter { + + protected CompletenessTraverser traverser; + + protected IndentPrinter printer; + + public CompletenessFullPrettyPrinter(IndentPrinter printer){ + this.printer = printer; + this.traverser = CompletenessMill.traverser(); + + CompletenessPrettyPrinter completeness = new CompletenessPrettyPrinter(printer); + traverser.add4Completeness(completeness); + traverser.setCompletenessHandler(completeness); + } + + public CompletenessTraverser getTraverser() { + return traverser; + } + + public void setTraverser(CompletenessTraverser traverser) { + this.traverser = traverser; + } + + public IndentPrinter getPrinter() { + return printer; + } + + public String prettyprint(ASTCompletenessNode node) { + getPrinter().clearBuffer(); + node.accept(getTraverser()); + return getPrinter().getContent(); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/prettyprint/CompletenessPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/prettyprint/CompletenessPrettyPrinter.java new file mode 100644 index 0000000000..148db4fcd1 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/prettyprint/CompletenessPrettyPrinter.java @@ -0,0 +1,51 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.prettyprint; + +import de.monticore.completeness._ast.ASTCompleteness; +import de.monticore.completeness._ast.ASTCompletenessNode; +import de.monticore.completeness._visitor.CompletenessHandler; +import de.monticore.completeness._visitor.CompletenessTraverser; +import de.monticore.completeness._visitor.CompletenessVisitor2; + +@Deprecated(forRemoval = true) +public class CompletenessPrettyPrinter implements CompletenessVisitor2, CompletenessHandler { + + protected CompletenessTraverser traverser; + + protected IndentPrinter printer; + + public CompletenessPrettyPrinter(IndentPrinter printer) { + this.printer = printer; + } + + @Override + public void handle(ASTCompleteness node) { + if (node.isComplete()) { + getPrinter().print("(c)"); + } + else if (node.isIncomplete()) { + getPrinter().print("(...)"); + } + else if (node.isLeftComplete()) { + getPrinter().print("(c,...)"); + } + else if (node.isRightComplete()) { + getPrinter().print("(...,c)"); + } + } + + @Override + public CompletenessTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(CompletenessTraverser traverser) { + this.traverser = traverser; + } + + public IndentPrinter getPrinter() { + return this.printer; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/prettyprint/JavaLightFullPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/prettyprint/JavaLightFullPrettyPrinter.java new file mode 100644 index 0000000000..c48c267063 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/prettyprint/JavaLightFullPrettyPrinter.java @@ -0,0 +1,83 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.prettyprint; + +import de.monticore.expressions.prettyprint.AssignmentExpressionsPrettyPrinter; +import de.monticore.expressions.prettyprint.ExpressionsBasisPrettyPrinter; +import de.monticore.expressions.prettyprint.JavaClassExpressionsPrettyPrinter; +import de.monticore.javalight.JavaLightMill; +import de.monticore.javalight._ast.ASTJavaLightNode; +import de.monticore.javalight._visitor.JavaLightTraverser; +import de.monticore.statements.prettyprint.MCArrayStatementsPrettyPrinter; +import de.monticore.statements.prettyprint.MCCommonStatementsPrettyPrinter; +import de.monticore.statements.prettyprint.MCReturnStatementsPrettyPrinter; +import de.monticore.statements.prettyprint.MCVarDeclarationStatementsPrettyPrinter; +import de.monticore.types.prettyprint.MCBasicTypesPrettyPrinter; + +@Deprecated(forRemoval = true) +public class JavaLightFullPrettyPrinter { + + protected JavaLightTraverser traverser; + + protected IndentPrinter printer; + + public JavaLightFullPrettyPrinter(IndentPrinter printer) { + this.printer = printer; + this.traverser = JavaLightMill.traverser(); + + MCBasicTypesPrettyPrinter basicTypes = new MCBasicTypesPrettyPrinter(printer); + traverser.add4MCBasicTypes(basicTypes); + traverser.setMCBasicTypesHandler(basicTypes); + + ExpressionsBasisPrettyPrinter expressionsBasis = new ExpressionsBasisPrettyPrinter(printer); + traverser.add4ExpressionsBasis(expressionsBasis); + traverser.setExpressionsBasisHandler(expressionsBasis); + + MCCommonStatementsPrettyPrinter commonStatements = new MCCommonStatementsPrettyPrinter(printer); + traverser.add4MCCommonStatements(commonStatements); + traverser.setMCCommonStatementsHandler(commonStatements); + + AssignmentExpressionsPrettyPrinter assignmentExpressions = new AssignmentExpressionsPrettyPrinter(printer); + traverser.add4AssignmentExpressions(assignmentExpressions); + traverser.setAssignmentExpressionsHandler(assignmentExpressions); + + MCVarDeclarationStatementsPrettyPrinter varDecl = new MCVarDeclarationStatementsPrettyPrinter(printer); + traverser.add4MCVarDeclarationStatements(varDecl); + traverser.setMCVarDeclarationStatementsHandler(varDecl); + + JavaLightPrettyPrinter javaLight = new JavaLightPrettyPrinter(printer); + traverser.add4JavaLight(javaLight); + traverser.setJavaLightHandler(javaLight); + + JavaClassExpressionsPrettyPrinter javaClassExpressions = new JavaClassExpressionsPrettyPrinter(printer); + traverser.add4JavaClassExpressions(javaClassExpressions); + traverser.setJavaClassExpressionsHandler(javaClassExpressions); + + MCArrayStatementsPrettyPrinter arrayStatements = new MCArrayStatementsPrettyPrinter(printer); + traverser.add4MCArrayStatements(arrayStatements); + traverser.setMCArrayStatementsHandler(arrayStatements); + + MCReturnStatementsPrettyPrinter returnStatements = new MCReturnStatementsPrettyPrinter(printer); + traverser.add4MCReturnStatements(returnStatements); + traverser.setMCReturnStatementsHandler(returnStatements); + + traverser.add4MCBasics(new MCBasicsPrettyPrinter(printer)); + } + + public IndentPrinter getPrinter() { + return this.printer; + } + + public String prettyprint(ASTJavaLightNode a) { + getPrinter().clearBuffer(); + a.accept(getTraverser()); + return getPrinter().getContent(); + } + + public JavaLightTraverser getTraverser() { + return traverser; + } + + public void setTraverser(JavaLightTraverser traverser) { + this.traverser = traverser; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/prettyprint/JavaLightPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/prettyprint/JavaLightPrettyPrinter.java new file mode 100644 index 0000000000..d139b3cdf7 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/prettyprint/JavaLightPrettyPrinter.java @@ -0,0 +1,214 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.prettyprint; + +import de.monticore.javalight._ast.*; +import de.monticore.javalight._visitor.JavaLightHandler; +import de.monticore.javalight._visitor.JavaLightTraverser; +import de.monticore.javalight._visitor.JavaLightVisitor2; +import de.monticore.statements.mccommonstatements._ast.ASTMCCommonStatementsNode; + +import java.util.Iterator; + +@Deprecated(forRemoval = true) +public class JavaLightPrettyPrinter implements + JavaLightVisitor2, JavaLightHandler { + + protected JavaLightTraverser traverser; + + protected IndentPrinter printer; + + public JavaLightPrettyPrinter(IndentPrinter out) { + this.printer = out; + } + + @Override + public JavaLightTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(JavaLightTraverser traverser) { + this.traverser = traverser; + } + + public IndentPrinter getPrinter() { + return this.printer; + } + + protected void printNode(String s) { + getPrinter().print(s); + } + + @Override + public void handle(ASTMethodDeclaration a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + a.getMCModifierList().stream().forEach(m -> {getPrinter().print(" "); m.accept(getTraverser()); getPrinter().print(" ");}); + if (a.isPresentExtTypeParameters()) { + a.getExtTypeParameters().accept(getTraverser()); + } + a.getMCReturnType().accept(getTraverser()); + getPrinter().print(" "); + printNode(a.getName()); + a.getFormalParameters().accept(getTraverser()); + for (int i = 0; i < a.getDimList().size(); i++) { + getPrinter().print("[]"); + } + if (a.isPresentThrows()) { + getPrinter().print(" throws "); + a.getThrows().accept(getTraverser()); + } + if (a.isPresentMCJavaBlock()) { + a.getMCJavaBlock().accept(getTraverser()); + } else { + getPrinter().println(";"); + } + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + + @Override + public void handle(ASTThrows a) { + for (int i = 0; i < a.getMCQualifiedNameList().size(); i++) { + if (i != 0) { + getPrinter().print(", "); + } + a.getMCQualifiedName(i).accept(getTraverser()); + } + } + + + @Override + public void handle(ASTConstructorDeclaration a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + getPrinter().println(); + a.getMCModifierList().stream().forEach(m -> {getPrinter().print(" "); m.accept(getTraverser()); getPrinter().print(" ");}); + if (a.isPresentExtTypeParameters()) { + a.getExtTypeParameters().accept(getTraverser()); + } + printNode(a.getName()); + a.getFormalParameters().accept(getTraverser()); + + if (a.isPresentThrows()) { + getPrinter().print(" throws "); + a.getThrows().accept(getTraverser()); + } + a.getMCJavaBlock().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTConstDeclaration a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + a.getLocalVariableDeclaration().accept(getTraverser()); + getPrinter().println(";"); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTFormalParameters a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + getPrinter().print("("); + if (a.isPresentFormalParameterListing()) { + a.getFormalParameterListing().accept(getTraverser()); + } + getPrinter().print(")"); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTFormalParameterListing a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + printSeparated(a.getFormalParameterList().iterator(), ","); + if (!a.getFormalParameterList().isEmpty() && a.isPresentLastFormalParameter()) { + getPrinter().print(","); + } + if (a.isPresentLastFormalParameter()) { + a.getLastFormalParameter().accept(getTraverser()); + } + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTLastFormalParameter a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + printSeparated(a.getJavaModifierList().iterator(), " "); + a.getMCType().accept(getTraverser()); + getPrinter().print(" ... "); + a.getDeclaratorId().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTAnnotation a) { + getPrinter().print("@"); + a.getAnnotationName().accept(getTraverser()); + if (a.isPresentAnnotationArguments()) { + getPrinter().print("("); + a.getAnnotationArguments().accept(getTraverser()); + getPrinter().print(")"); + } + } + + @Override + public void handle(ASTAnnotationPairArguments a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + printJavaLightList(a.getElementValuePairList().iterator(), ", "); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTElementValuePair a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + printNode(a.getName()); + getPrinter().print(" = "); + a.getElementValueOrExpr().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTElementValueArrayInitializer a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + getPrinter().print("{"); + for (int i = 0; i < a.getElementValueOrExprList().size(); i++) { + if (i != 0) { + getPrinter().print(", "); + } + a.getElementValueOrExpr(i).accept(getTraverser()); + } + getPrinter().print("}"); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTArrayDimensionByInitializer a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + for (int i = 0; i < a.getDimList().size(); i++) { + getPrinter().print("[]"); + } + getPrinter().print(" "); + a.getArrayInit().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + protected void printJavaLightList(Iterator iter, String separator) { + // print by iterate through all items + String sep = ""; + while (iter.hasNext()) { + getPrinter().print(sep); + iter.next().accept(getTraverser()); + sep = separator; + } + } + + protected void printSeparated(Iterator iter, String separator) { + // print by iterate through all items + String sep = ""; + while (iter.hasNext()) { + getPrinter().print(sep); + iter.next().accept(getTraverser()); + sep = separator; + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/prettyprint/MCBasicsPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/prettyprint/MCBasicsPrettyPrinter.java new file mode 100644 index 0000000000..d84160eb2b --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/prettyprint/MCBasicsPrettyPrinter.java @@ -0,0 +1,32 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.prettyprint; + +import de.monticore.mcbasics._visitor.MCBasicsVisitor2; + +/** + * This class is responsible for pretty-printing types of the common type system. It is implemented + * using the Visitor pattern. The Visitor pattern traverses a tree in depth first, the visit and + * ownVisit-methods are called when a node is traversed, the endVisit methods are called when the + * whole subtree of a node has been traversed. The ownVisit-Methods stop the automatic traversal + * order and allow to explictly visit subtrees by calling getVisitor().startVisit(ASTNode) + */ +@Deprecated(forRemoval = true) +public class MCBasicsPrettyPrinter implements MCBasicsVisitor2 { + + protected IndentPrinter printer; + + /** + * Constructor. + * + * @param printer the printer to write to. + */ + public MCBasicsPrettyPrinter(IndentPrinter printer) { + this.printer = printer; + } + + public IndentPrinter getPrinter() { + return printer; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/prettyprint/UMLModifierFullPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/prettyprint/UMLModifierFullPrettyPrinter.java new file mode 100644 index 0000000000..1800955c05 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/prettyprint/UMLModifierFullPrettyPrinter.java @@ -0,0 +1,53 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.prettyprint; + +import de.monticore.literals.prettyprint.MCCommonLiteralsPrettyPrinter; +import de.monticore.umlmodifier.UMLModifierMill; +import de.monticore.umlmodifier._ast.ASTUMLModifierNode; +import de.monticore.umlmodifier._visitor.UMLModifierTraverser; + +@Deprecated(forRemoval = true) +public class UMLModifierFullPrettyPrinter { + + protected UMLModifierTraverser traverser; + + protected IndentPrinter printer; + + public UMLModifierFullPrettyPrinter(IndentPrinter printer){ + this.printer = printer; + this.traverser = UMLModifierMill.traverser(); + + UMLModifierPrettyPrinter umlModifier = new UMLModifierPrettyPrinter(printer); + traverser.add4UMLModifier(umlModifier); + traverser.setUMLModifierHandler(umlModifier); + + MCCommonLiteralsPrettyPrinter commonLiterals = new MCCommonLiteralsPrettyPrinter(printer); + traverser.add4MCCommonLiterals(commonLiterals); + traverser.setMCCommonLiteralsHandler(commonLiterals); + + UMLStereotypePrettyPrinter umlStereotype = new UMLStereotypePrettyPrinter(printer); + traverser.add4UMLStereotype(umlStereotype); + traverser.setUMLStereotypeHandler(umlStereotype); + + traverser.add4MCBasics(new MCBasicsPrettyPrinter(printer)); + } + + public UMLModifierTraverser getTraverser() { + return traverser; + } + + public void setTraverser(UMLModifierTraverser traverser) { + this.traverser = traverser; + } + + public IndentPrinter getPrinter() { + return printer; + } + + public String prettyprint(ASTUMLModifierNode node) { + getPrinter().clearBuffer(); + node.accept(getTraverser()); + return getPrinter().getContent(); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/prettyprint/UMLModifierPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/prettyprint/UMLModifierPrettyPrinter.java new file mode 100644 index 0000000000..4575721d54 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/prettyprint/UMLModifierPrettyPrinter.java @@ -0,0 +1,71 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.prettyprint; + +import de.monticore.umlmodifier._ast.ASTModifier; +import de.monticore.umlmodifier._ast.ASTUMLModifierNode; +import de.monticore.umlmodifier._visitor.UMLModifierHandler; +import de.monticore.umlmodifier._visitor.UMLModifierTraverser; +import de.monticore.umlmodifier._visitor.UMLModifierVisitor2; + +@Deprecated(forRemoval = true) +public class UMLModifierPrettyPrinter implements UMLModifierVisitor2, UMLModifierHandler { + + protected UMLModifierTraverser traverser; + + protected IndentPrinter printer; + + public UMLModifierPrettyPrinter(IndentPrinter printer) { + this.printer = printer; + } + + @Override + public void handle(ASTModifier a) { + // print stereotypes + if (a.isPresentStereotype()) { + a.getStereotype().accept(getTraverser()); + getPrinter().print(" "); + } + if (a.isPublic()) { + getPrinter().print("public "); + } + if (a.isPrivate()) { + getPrinter().print("private "); + } + if (a.isProtected()) { + getPrinter().print("protected "); + } + if (a.isFinal()) { + getPrinter().print("final "); + } + if (a.isAbstract()) { + getPrinter().print("abstract "); + } + if (a.isLocal()) { + getPrinter().print("local "); + } + if (a.isDerived()) { + getPrinter().print("derived "); + } + if (a.isReadonly()) { + getPrinter().print("readonly "); + } + if (a.isStatic()) { + getPrinter().print("static "); + } + } + + @Override + public UMLModifierTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(UMLModifierTraverser traverser) { + this.traverser = traverser; + } + + public IndentPrinter getPrinter() { + return this.printer; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/prettyprint/UMLStereotypeFullPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/prettyprint/UMLStereotypeFullPrettyPrinter.java new file mode 100644 index 0000000000..91a2527a8e --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/prettyprint/UMLStereotypeFullPrettyPrinter.java @@ -0,0 +1,50 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.prettyprint; + +import de.monticore.literals.prettyprint.MCCommonLiteralsPrettyPrinter; +import de.monticore.umlstereotype.UMLStereotypeMill; +import de.monticore.umlstereotype._ast.ASTUMLStereotypeNode; +import de.monticore.umlstereotype._visitor.UMLStereotypeTraverser; + +@Deprecated(forRemoval = true) +public class UMLStereotypeFullPrettyPrinter { + + protected UMLStereotypeTraverser traverser; + + protected IndentPrinter printer; + + public UMLStereotypeFullPrettyPrinter(IndentPrinter printer){ + this.printer = printer; + this.traverser = UMLStereotypeMill.traverser(); + + UMLStereotypePrettyPrinter umlStereotype = new UMLStereotypePrettyPrinter(printer); + traverser.add4UMLStereotype(umlStereotype); + traverser.setUMLStereotypeHandler(umlStereotype); + + MCCommonLiteralsPrettyPrinter commonLiterals = new MCCommonLiteralsPrettyPrinter(printer); + traverser.add4MCCommonLiterals(commonLiterals); + traverser.setMCCommonLiteralsHandler(commonLiterals); + + traverser.add4MCBasics(new MCBasicsPrettyPrinter(printer)); + } + + public UMLStereotypeTraverser getTraverser() { + return traverser; + } + + public void setTraverser(UMLStereotypeTraverser traverser) { + this.traverser = traverser; + } + + public IndentPrinter getPrinter() { + return printer; + } + + public String prettyprint(ASTUMLStereotypeNode node) { + getPrinter().clearBuffer(); + node.accept(getTraverser()); + return getPrinter().getContent(); + } + + +} diff --git a/monticore-grammar/src/main/java/de/monticore/prettyprint/UMLStereotypePrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/prettyprint/UMLStereotypePrettyPrinter.java new file mode 100644 index 0000000000..ad2fbc3d9f --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/prettyprint/UMLStereotypePrettyPrinter.java @@ -0,0 +1,56 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.prettyprint; + +import de.monticore.umlstereotype._ast.ASTStereoValue; +import de.monticore.umlstereotype._ast.ASTStereotype; +import de.monticore.umlstereotype._ast.ASTUMLStereotypeNode; +import de.monticore.umlstereotype._visitor.UMLStereotypeHandler; +import de.monticore.umlstereotype._visitor.UMLStereotypeTraverser; +import de.monticore.umlstereotype._visitor.UMLStereotypeVisitor2; + +@Deprecated(forRemoval = true) +public class UMLStereotypePrettyPrinter implements UMLStereotypeVisitor2, UMLStereotypeHandler { + + protected UMLStereotypeTraverser traverser; + + protected IndentPrinter printer; + + public UMLStereotypePrettyPrinter(IndentPrinter printer) { + this.printer = printer; + } + + @Override + public void handle(ASTStereotype a) { + getPrinter().print("<<"); + String sep = ""; + for (ASTStereoValue value : a.getValuesList()) { + getPrinter().print(sep); + value.accept(getTraverser()); + sep = ", "; + } + getPrinter().print(">>"); + } + + @Override + public void handle(ASTStereoValue a) { + getPrinter().print(a.getName()); + if (a.isPresentText()) { + getPrinter().print("=" + "\"" + a.getText().getSource() + "\""); + } + } + + @Override + public UMLStereotypeTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(UMLStereotypeTraverser traverser) { + this.traverser = traverser; + } + + public IndentPrinter getPrinter() { + return this.printer; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/_symboltable/MCCommonStatementsPhasedSymbolTableCreatorDelegator.java b/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/_symboltable/MCCommonStatementsPhasedSymbolTableCreatorDelegator.java new file mode 100644 index 0000000000..9a3b88b306 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/_symboltable/MCCommonStatementsPhasedSymbolTableCreatorDelegator.java @@ -0,0 +1,34 @@ +/*(c) https://github.com/MontiCore/monticore*/ +package de.monticore.statements.mccommonstatements._symboltable; + +import de.monticore.statements.mccommonstatements.MCCommonStatementsMill; +import de.monticore.statements.mccommonstatements._ast.ASTMCJavaBlock; +import de.monticore.statements.mccommonstatements._visitor.MCCommonStatementsTraverser; + +import java.util.ArrayList; +import java.util.List; + +public class MCCommonStatementsPhasedSymbolTableCreatorDelegator { + + protected IMCCommonStatementsGlobalScope globalScope; + + protected MCCommonStatementsScopesGenitorDelegator scopesGenitorDelegator; + + protected List priorityList; + + public MCCommonStatementsPhasedSymbolTableCreatorDelegator(){ + this.globalScope = MCCommonStatementsMill.globalScope(); + this.scopesGenitorDelegator = MCCommonStatementsMill.scopesGenitorDelegator(); + this.priorityList = new ArrayList<>(); + MCCommonStatementsTraverser traverser = MCCommonStatementsMill.traverser(); + traverser.add4MCCommonStatements(new MCCommonStatementsSTCompleteTypes()); + this.priorityList.add(traverser); + } + + public IMCCommonStatementsArtifactScope createFromAST(ASTMCJavaBlock rootNode){ + IMCCommonStatementsArtifactScope as = scopesGenitorDelegator.createFromAST(rootNode); + this.priorityList.forEach(rootNode::accept); + return as; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/_symboltable/MCCommonStatementsSTCompleteTypes.java b/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/_symboltable/MCCommonStatementsSTCompleteTypes.java new file mode 100644 index 0000000000..407165d926 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/_symboltable/MCCommonStatementsSTCompleteTypes.java @@ -0,0 +1,31 @@ +/*(c) https://github.com/MontiCore/monticore*/ +package de.monticore.statements.mccommonstatements._symboltable; + +import de.monticore.grammar.grammar_withconcepts.FullSynthesizeFromMCSGT4Grammar; +import de.monticore.statements.mccommonstatements._ast.ASTFormalParameter; +import de.monticore.statements.mccommonstatements._visitor.MCCommonStatementsVisitor2; +import de.monticore.symbols.oosymbols._symboltable.FieldSymbol; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeOfNull; +import de.monticore.types.check.TypeCheckResult; +import de.monticore.types.mcbasictypes._ast.ASTMCType; + +public class MCCommonStatementsSTCompleteTypes implements MCCommonStatementsVisitor2 { + + @Override + public void endVisit(ASTFormalParameter ast) { + FieldSymbol symbol = ast.getDeclarator().getSymbol(); + symbol.setType(createTypeLoader(ast.getMCType())); + } + + protected SymTypeExpression createTypeLoader(ASTMCType ast) { + FullSynthesizeFromMCSGT4Grammar synFromFull = new FullSynthesizeFromMCSGT4Grammar(); + // Start visitor + TypeCheckResult typeCheckResult = synFromFull.synthesizeType(ast); + if(typeCheckResult.isPresentResult()){ + return typeCheckResult.getResult(); + } + return new SymTypeOfNull(); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/cocos/AssertIsValid.java b/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/cocos/AssertIsValid.java new file mode 100644 index 0000000000..2df7449a3c --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/cocos/AssertIsValid.java @@ -0,0 +1,45 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements.mccommonstatements.cocos; + +import de.monticore.statements.mcassertstatements._ast.ASTAssertStatement; +import de.monticore.statements.mcassertstatements._cocos.MCAssertStatementsASTAssertStatementCoCo; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.TypeCheck; +import de.monticore.types.check.TypeCalculator; +import de.se_rwth.commons.logging.Log; + +public class AssertIsValid implements MCAssertStatementsASTAssertStatementCoCo { + + public static final String ERROR_CODE = "0xA0901"; + + public static final String ERROR_MSG_FORMAT = "Assert-statement must be of boolean type."; + + public static final String ERROR_CODE_2 = "0xA0902"; + + public static final String ERROR_MSG_FORMAT_2 = "Assert-statement must not be of void type."; + + TypeCalculator typeCheck; + + public AssertIsValid(TypeCalculator typeCheck){ + + this.typeCheck = typeCheck; + + } + + @Override + public void check(ASTAssertStatement node) { + + SymTypeExpression result = typeCheck.typeOf(node.getAssertion()); + + if(!TypeCheck.isBoolean(result)){ + Log.error(ERROR_CODE + ERROR_MSG_FORMAT, node.getAssertion().get_SourcePositionStart()); + } + + if(node.isPresentMessage()) { + result = typeCheck.typeOf(node.getMessage()); + if (!TypeCheck.isVoid(result)) { + Log.error(ERROR_CODE_2 + ERROR_MSG_FORMAT_2, node.getMessage().get_SourcePositionStart()); + } + } + } +} \ No newline at end of file diff --git a/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/cocos/CatchIsValid.java b/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/cocos/CatchIsValid.java new file mode 100644 index 0000000000..c54acbe333 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/cocos/CatchIsValid.java @@ -0,0 +1,41 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements.mccommonstatements.cocos; + +import de.monticore.statements.mcexceptionstatements._ast.ASTCatchClause; +import de.monticore.statements.mcexceptionstatements._cocos.MCExceptionStatementsASTCatchClauseCoCo; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types.check.TypeCheck; +import de.monticore.types.check.TypeCalculator; +import de.se_rwth.commons.logging.Log; + +public class CatchIsValid implements MCExceptionStatementsASTCatchClauseCoCo { + + TypeCalculator typeCheck; + + public static final String ERROR_CODE = "0xA0903"; + + public static final String ERROR_MSG_FORMAT = "Parameter in catch-statement has to be throwable or subtype of it."; + + public CatchIsValid(TypeCalculator typeCheck){ + + this.typeCheck = typeCheck; + + } + + @Override + public void check(ASTCatchClause node) { + + SymTypeExpression throwable = SymTypeExpressionFactory.createTypeObject("java.lang.Throwable", node.getEnclosingScope()); + + for (int i = 0; i < node.getCatchTypeList().sizeMCQualifiedNames(); i++) { + String name = node.getCatchTypeList().getMCQualifiedName(i).getQName(); + + SymTypeExpression result = SymTypeExpressionFactory.createTypeObject(name, node.getEnclosingScope()); + + if (!TypeCheck.isSubtypeOf(result, throwable)) { + Log.error(ERROR_CODE + ERROR_MSG_FORMAT, node.get_SourcePositionStart()); + } + } + } +} \ No newline at end of file diff --git a/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/cocos/DoWhileConditionHasBooleanType.java b/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/cocos/DoWhileConditionHasBooleanType.java new file mode 100644 index 0000000000..51ee53d5c0 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/cocos/DoWhileConditionHasBooleanType.java @@ -0,0 +1,33 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements.mccommonstatements.cocos; + +import de.monticore.statements.mccommonstatements._ast.ASTDoWhileStatement; +import de.monticore.statements.mccommonstatements._cocos.MCCommonStatementsASTDoWhileStatementCoCo; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.TypeCheck; +import de.monticore.types.check.TypeCalculator; +import de.se_rwth.commons.logging.Log; + +public class DoWhileConditionHasBooleanType implements MCCommonStatementsASTDoWhileStatementCoCo { + + TypeCalculator typeCheck; + + public static final String ERROR_CODE = "0xA0905"; + + public static final String ERROR_MSG_FORMAT = "Condition in do-statement must be a boolean expression."; + + public DoWhileConditionHasBooleanType(TypeCalculator typeCheck){ + this.typeCheck = typeCheck; + } + + @Override + public void check(ASTDoWhileStatement node) { + + SymTypeExpression result = typeCheck.typeOf(node.getCondition()); + + if(!TypeCheck.isBoolean(result)){ + Log.error(ERROR_CODE + ERROR_MSG_FORMAT, node.get_SourcePositionStart()); + } + + } +} \ No newline at end of file diff --git a/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/cocos/ExpressionStatementIsValid.java b/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/cocos/ExpressionStatementIsValid.java new file mode 100644 index 0000000000..a93b747caf --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/cocos/ExpressionStatementIsValid.java @@ -0,0 +1,28 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements.mccommonstatements.cocos; + +import com.google.common.base.Preconditions; +import de.monticore.statements.mccommonstatements._ast.ASTExpressionStatement; +import de.monticore.statements.mccommonstatements._cocos.MCCommonStatementsASTExpressionStatementCoCo; +import de.monticore.types.check.TypeCalculator; + +public class ExpressionStatementIsValid implements MCCommonStatementsASTExpressionStatementCoCo { + + protected TypeCalculator typeCheck; + + public ExpressionStatementIsValid(TypeCalculator typeCheck) { + Preconditions.checkNotNull(typeCheck); + + this.typeCheck = typeCheck; + } + + protected TypeCalculator getTypeCheck() { + return this.typeCheck; + } + + @Override + public void check(ASTExpressionStatement statement) { + Preconditions.checkNotNull(statement); + this.getTypeCheck().typeOf(statement.getExpression()); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/cocos/ForConditionHasBooleanType.java b/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/cocos/ForConditionHasBooleanType.java new file mode 100644 index 0000000000..17785e7209 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/cocos/ForConditionHasBooleanType.java @@ -0,0 +1,34 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements.mccommonstatements.cocos; + +import de.monticore.statements.mccommonstatements._ast.ASTCommonForControl; +import de.monticore.statements.mccommonstatements._ast.ASTForStatement; +import de.monticore.statements.mccommonstatements._cocos.MCCommonStatementsASTForStatementCoCo; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.TypeCheck; +import de.monticore.types.check.TypeCalculator; +import de.se_rwth.commons.logging.Log; + +public class ForConditionHasBooleanType implements MCCommonStatementsASTForStatementCoCo { + + TypeCalculator typeCheck; + + public static final String ERROR_CODE = "0xA0906"; + + public static final String ERROR_MSG_FORMAT = "Condition of for-loop must be a boolean expression."; + + public ForConditionHasBooleanType(TypeCalculator typeCheck){ + this.typeCheck = typeCheck; + } + + @Override + public void check(ASTForStatement node) { + + SymTypeExpression result = typeCheck.typeOf(((ASTCommonForControl)node.getForControl()).getCondition()); + + if(!TypeCheck.isBoolean(result)){ + Log.error(ERROR_CODE + ERROR_MSG_FORMAT, node.get_SourcePositionStart()); + } + + } +} \ No newline at end of file diff --git a/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/cocos/ForEachIsValid.java b/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/cocos/ForEachIsValid.java new file mode 100644 index 0000000000..456b7b38db --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/cocos/ForEachIsValid.java @@ -0,0 +1,37 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements.mccommonstatements.cocos; + +import de.monticore.statements.mccommonstatements._ast.ASTEnhancedForControl; +import de.monticore.statements.mccommonstatements._cocos.MCCommonStatementsASTEnhancedForControlCoCo; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types.check.TypeCheck; +import de.monticore.types.check.TypeCalculator; +import de.se_rwth.commons.logging.Log; + +public class ForEachIsValid implements MCCommonStatementsASTEnhancedForControlCoCo { + + TypeCalculator typeCheck; + + public static final String ERROR_CODE = "0xA0907 "; + + public static final String ERROR_MSG_FORMAT = "For-each loop expression must be an array of subtype of list."; + + public ForEachIsValid(TypeCalculator typeCheck) { + this.typeCheck = typeCheck; + } + + @Override + public void check(ASTEnhancedForControl node) { + + SymTypeExpression expression = typeCheck.typeOf(node.getExpression()); + SymTypeExpression arrays = SymTypeExpressionFactory.createTypeObject("java.util.Arrays", node.getEnclosingScope()); + SymTypeExpression lists = SymTypeExpressionFactory.createTypeObject("java.lang.Iterable", node.getEnclosingScope()); + + if (!TypeCheck.isSubtypeOf(expression, arrays)) { + if (!TypeCheck.isSubtypeOf(expression, lists)) { + Log.error(ERROR_CODE + ERROR_MSG_FORMAT, node.get_SourcePositionStart()); + } + } + } +} \ No newline at end of file diff --git a/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/cocos/IfConditionHasBooleanType.java b/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/cocos/IfConditionHasBooleanType.java new file mode 100644 index 0000000000..4e4864e99a --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/cocos/IfConditionHasBooleanType.java @@ -0,0 +1,33 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements.mccommonstatements.cocos; + +import de.monticore.statements.mccommonstatements._ast.ASTIfStatement; +import de.monticore.statements.mccommonstatements._cocos.MCCommonStatementsASTIfStatementCoCo; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.TypeCheck; +import de.monticore.types.check.TypeCalculator; +import de.se_rwth.commons.logging.Log; + +public class IfConditionHasBooleanType implements MCCommonStatementsASTIfStatementCoCo { + + TypeCalculator typeCheck; + + public static final String ERROR_CODE = "0xA0909"; + + public static final String ERROR_MSG_FORMAT = " Condition in if-statement must be a boolean expression."; + + public IfConditionHasBooleanType(TypeCalculator typeCheck){ + this.typeCheck = typeCheck; + } + + //JLS3 14.9-1 + @Override + public void check(ASTIfStatement node) { + + SymTypeExpression result = typeCheck.typeOf(node.getCondition()); + + if(!TypeCheck.isBoolean(result)){ + Log.error(ERROR_CODE + ERROR_MSG_FORMAT, node.get_SourcePositionStart()); + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/cocos/ResourceInTryStatementCloseable.java b/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/cocos/ResourceInTryStatementCloseable.java new file mode 100644 index 0000000000..f34a4039a8 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/cocos/ResourceInTryStatementCloseable.java @@ -0,0 +1,36 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements.mccommonstatements.cocos; + +import de.monticore.statements.mcexceptionstatements._ast.ASTTryLocalVariableDeclaration; +import de.monticore.statements.mcexceptionstatements._ast.ASTTryStatement3; +import de.monticore.statements.mcexceptionstatements._cocos.MCExceptionStatementsASTTryStatement3CoCo; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types.check.TypeCheck; +import de.monticore.types.check.TypeCalculator; +import de.se_rwth.commons.logging.Log; + +public class ResourceInTryStatementCloseable implements MCExceptionStatementsASTTryStatement3CoCo { + + TypeCalculator typeCheck; + + public static final String ERROR_CODE = "0xA0920"; + + public static final String ERROR_MSG_FORMAT = " Resource in try-statement must be closeable or subtype of it."; + + public ResourceInTryStatementCloseable(TypeCalculator typeCheck){ + this.typeCheck = typeCheck; + } + + @Override + public void check(ASTTryStatement3 node) { + + SymTypeExpression closeable = SymTypeExpressionFactory.createTypeObject("java.io.Closeable", node.getEnclosingScope()); + + for (ASTTryLocalVariableDeclaration dec: node.getTryLocalVariableDeclarationList()){ + if (!TypeCheck.isSubtypeOf(typeCheck.typeOf(dec.getExpression()), closeable)) { + Log.error(ERROR_CODE + ERROR_MSG_FORMAT, node.get_SourcePositionStart()); + } + } + } +} \ No newline at end of file diff --git a/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/cocos/SwitchStatementValid.java b/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/cocos/SwitchStatementValid.java new file mode 100644 index 0000000000..ec7ed441e4 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/cocos/SwitchStatementValid.java @@ -0,0 +1,35 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements.mccommonstatements.cocos; + +import de.monticore.statements.mccommonstatements._ast.ASTSwitchStatement; +import de.monticore.statements.mccommonstatements._cocos.MCCommonStatementsASTSwitchStatementCoCo; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.TypeCheck; +import de.monticore.types.check.TypeCalculator; +import de.se_rwth.commons.logging.Log; + +public class SwitchStatementValid implements MCCommonStatementsASTSwitchStatementCoCo { + + TypeCalculator typeCheck; + + public static final String ERROR_CODE = "0xA0917"; + + public static final String ERROR_MSG_FORMAT = "Switch expression in the switch-statement must be char, byte, short, int, Character," + + "Byte, Short, Integer, or an enum type."; + + public SwitchStatementValid(TypeCalculator typeCheck){ + this.typeCheck = typeCheck; + } + + //JLS3 14.11 + @Override + public void check(ASTSwitchStatement node) { + + SymTypeExpression result = typeCheck.typeOf(node.getExpression()); + + if(!(TypeCheck.isChar(result)|| TypeCheck.isByte(result)|| TypeCheck.isShort(result)|| TypeCheck.isInt(result))){ + Log.error(ERROR_CODE + ERROR_MSG_FORMAT, node.get_SourcePositionStart()); + } + + } +} \ No newline at end of file diff --git a/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/cocos/SynchronizedArgIsReftype.java b/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/cocos/SynchronizedArgIsReftype.java new file mode 100644 index 0000000000..c7aa422bbd --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/cocos/SynchronizedArgIsReftype.java @@ -0,0 +1,31 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements.mccommonstatements.cocos; + +import de.monticore.statements.mcsynchronizedstatements._ast.ASTSynchronizedStatement; +import de.monticore.statements.mcsynchronizedstatements._cocos.MCSynchronizedStatementsASTSynchronizedStatementCoCo; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.TypeCalculator; +import de.se_rwth.commons.logging.Log; + +public class SynchronizedArgIsReftype implements MCSynchronizedStatementsASTSynchronizedStatementCoCo { + + TypeCalculator typeCheck; + + public static final String ERROR_CODE = "0xA0918 "; + + public static final String ERROR_MSG_FORMAT = "Expression in synchronized-statement must have a reference type."; + + public SynchronizedArgIsReftype(TypeCalculator typeCheck){ + this.typeCheck = typeCheck; + } + + @Override + public void check(ASTSynchronizedStatement node) { + + SymTypeExpression result = typeCheck.typeOf(node.getExpression()); + + if(!result.isObjectType()) { + Log.error(ERROR_CODE + ERROR_MSG_FORMAT, node.get_SourcePositionStart()); + } + } +} \ No newline at end of file diff --git a/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/cocos/ThrowIsValid.java b/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/cocos/ThrowIsValid.java new file mode 100644 index 0000000000..66e1266b1b --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/cocos/ThrowIsValid.java @@ -0,0 +1,35 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements.mccommonstatements.cocos; + +import de.monticore.statements.mcexceptionstatements._ast.ASTThrowStatement; +import de.monticore.statements.mcexceptionstatements._cocos.MCExceptionStatementsASTThrowStatementCoCo; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types.check.TypeCheck; +import de.monticore.types.check.TypeCalculator; +import de.se_rwth.commons.logging.Log; + +public class ThrowIsValid implements MCExceptionStatementsASTThrowStatementCoCo{ + + TypeCalculator typeCheck; + + public static final String ERROR_CODE = "0xA0918"; + + public static final String ERROR_MSG_FORMAT = " Exception in throw-statement must be Throwable or subtype of it."; + + public ThrowIsValid(TypeCalculator typeCheck){ + this.typeCheck = typeCheck; + } + + //JLS3 14.18-1 + @Override + public void check(ASTThrowStatement node) { + + SymTypeExpression throwable = SymTypeExpressionFactory.createTypeObject("java.lang.Throwable", node.getEnclosingScope()); + + if(!TypeCheck.isSubtypeOf(typeCheck.typeOf(node.getExpression()),throwable)){ + Log.error(ERROR_CODE + ERROR_MSG_FORMAT, node.get_SourcePositionStart()); + } + + } +} \ No newline at end of file diff --git a/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/cocos/WhileConditionHasBooleanType.java b/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/cocos/WhileConditionHasBooleanType.java new file mode 100644 index 0000000000..2407f902ac --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/statements/mccommonstatements/cocos/WhileConditionHasBooleanType.java @@ -0,0 +1,32 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements.mccommonstatements.cocos; + +import de.monticore.statements.mccommonstatements._ast.ASTWhileStatement; +import de.monticore.statements.mccommonstatements._cocos.MCCommonStatementsASTWhileStatementCoCo; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.TypeCheck; +import de.monticore.types.check.TypeCalculator; +import de.se_rwth.commons.logging.Log; + +public class WhileConditionHasBooleanType implements MCCommonStatementsASTWhileStatementCoCo { + + TypeCalculator typeCheck; + + public static final String ERROR_CODE = "0xA0919"; + + public static final String ERROR_MSG_FORMAT = "Condition in while-statement must be a boolean expression."; + + public WhileConditionHasBooleanType(TypeCalculator typeCheck){ + this.typeCheck = typeCheck; + } + + //JLS3 14.12-1 + @Override + public void check(ASTWhileStatement node) { + SymTypeExpression result = typeCheck.typeOf(node.getCondition()); + + if(!TypeCheck.isBoolean(result)){ + Log.error(ERROR_CODE + ERROR_MSG_FORMAT, node.get_SourcePositionStart()); + } + } +} \ No newline at end of file diff --git a/monticore-grammar/src/main/java/de/monticore/statements/mcvardeclarationstatements/_cocos/VarDeclarationInitializationHasCorrectType.java b/monticore-grammar/src/main/java/de/monticore/statements/mcvardeclarationstatements/_cocos/VarDeclarationInitializationHasCorrectType.java new file mode 100644 index 0000000000..8634936313 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/statements/mcvardeclarationstatements/_cocos/VarDeclarationInitializationHasCorrectType.java @@ -0,0 +1,77 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements.mcvardeclarationstatements._cocos; + +import de.monticore.statements.mcvardeclarationstatements._ast.ASTSimpleInit; +import de.monticore.statements.mcvardeclarationstatements._ast.ASTVariableDeclarator; +import de.monticore.types.check.*; +import de.se_rwth.commons.logging.Log; + +/** + * Checks that the initialization expressions of variable declaration statements are compatible to the types of the + * variables. + */ +public class VarDeclarationInitializationHasCorrectType + implements MCVarDeclarationStatementsASTVariableDeclaratorCoCo { + + /** + * Used to derive the {@link SymTypeExpression} to which initialization expressions evaluate to. + */ + protected final IDerive typeDeriver; + + /** + * Indicates that the type of the initialization expression is not compatible with the type of the assigned variable. + */ + public static final String ERROR_CODE = "0xA0921"; + + /** + * Indicates that the initialization expression does not evaluate to a value, but to a type name. Example: + * {@code String foo = String;} + */ + public static final String TYPE_REF_ASSIGNMENT_ERROR_CODE = "0xA0922"; + + public static final String ERROR_MSG_FORMAT = "Incompatible type '%s' of the initialization expression for variable '%s' " + + "that is of type '%s'."; + + public static final String TYPE_REF_ASSIGNMENT_ERROR_MSG_FORMAT = "The initialization expression for variable " + + "'%s' represents the type '%s'. As types do not evaluate to values, they can not be used in assignments / " + + "initializations."; + + /** + * @param typeDeriver Used to derive the {@link SymTypeExpression} to which initialization expressions evaluate to. + */ + public VarDeclarationInitializationHasCorrectType(IDerive typeDeriver) { + this.typeDeriver = typeDeriver; + } + + @Override + public void check(ASTVariableDeclarator node) { + if(!node.isPresentVariableInit() || !(node.getVariableInit() instanceof ASTSimpleInit)) { + return; // We can only check initializations of the form of expressions (as defined in SimpleInit). + + } else if(!node.getDeclarator().isPresentSymbol()) { + Log.error(String.format("Could not find a symbol for variable '%s', thus can not check coco '%s'. Check " + + "whether you have run the symbol table creation before running this coco.", + node.getDeclarator().getName(), this.getClass().getSimpleName())); + + } else { // Proceed with checking the coco + SymTypeExpression varType = node.getDeclarator().getSymbol().getType(); + TypeCheckResult initType = typeDeriver.deriveType(((ASTSimpleInit) node.getVariableInit()).getExpression()); + + if(!initType.isPresentResult()) { + // The error is already printed by the IDerive visitors, thus we would spam the log if we would log an error + // again. Therefore, we only leave a note in the debug log. + Log.debug(String.format("As the initialization expression for variable '%s' at %s is invalid, coco '%s' " + + "will not be checked.", + node.getDeclarator().getName(), node.get_SourcePositionStart(), this.getClass().getSimpleName()), "Cocos"); + + } else if(initType.isType()) { + Log.error(TYPE_REF_ASSIGNMENT_ERROR_CODE + " " + String.format(TYPE_REF_ASSIGNMENT_ERROR_MSG_FORMAT, + node.getDeclarator().getName(), initType.getResult().print())); + + } else if(!TypeCheck.compatible(varType, initType.getResult())) { + Log.error(ERROR_CODE + " " + String.format(ERROR_MSG_FORMAT, + initType.getResult().print(), node.getDeclarator().getName(), varType.print())); + } + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/statements/mcvardeclarationstatements/_symboltable/MCVarDeclarationStatementsPhasedSymbolTableCreatorDelegator.java b/monticore-grammar/src/main/java/de/monticore/statements/mcvardeclarationstatements/_symboltable/MCVarDeclarationStatementsPhasedSymbolTableCreatorDelegator.java new file mode 100644 index 0000000000..1bb248fc40 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/statements/mcvardeclarationstatements/_symboltable/MCVarDeclarationStatementsPhasedSymbolTableCreatorDelegator.java @@ -0,0 +1,34 @@ +/*(c) https://github.com/MontiCore/monticore*/ +package de.monticore.statements.mcvardeclarationstatements._symboltable; + +import de.monticore.statements.mcvardeclarationstatements.MCVarDeclarationStatementsMill; +import de.monticore.statements.mcvardeclarationstatements._ast.ASTLocalVariableDeclarationStatement; +import de.monticore.statements.mcvardeclarationstatements._visitor.MCVarDeclarationStatementsTraverser; + +import java.util.ArrayList; +import java.util.List; + +public class MCVarDeclarationStatementsPhasedSymbolTableCreatorDelegator { + + protected IMCVarDeclarationStatementsGlobalScope globalScope; + + protected MCVarDeclarationStatementsScopesGenitorDelegator scopesGenitorDelegator; + + protected List priorityList; + + public MCVarDeclarationStatementsPhasedSymbolTableCreatorDelegator(){ + this.globalScope = MCVarDeclarationStatementsMill.globalScope(); + this.scopesGenitorDelegator = MCVarDeclarationStatementsMill.scopesGenitorDelegator(); + this.priorityList = new ArrayList<>(); + MCVarDeclarationStatementsTraverser traverser = MCVarDeclarationStatementsMill.traverser(); + traverser.add4MCVarDeclarationStatements(new MCVarDeclarationStatementsSTCompleteTypes()); + this.priorityList.add(traverser); + } + + public IMCVarDeclarationStatementsArtifactScope createFromAST(ASTLocalVariableDeclarationStatement rootNode){ + IMCVarDeclarationStatementsArtifactScope as = scopesGenitorDelegator.createFromAST(rootNode); + this.priorityList.forEach(rootNode::accept); + return as; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/statements/mcvardeclarationstatements/_symboltable/MCVarDeclarationStatementsSTCompleteTypes.java b/monticore-grammar/src/main/java/de/monticore/statements/mcvardeclarationstatements/_symboltable/MCVarDeclarationStatementsSTCompleteTypes.java new file mode 100644 index 0000000000..7a9f124daf --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/statements/mcvardeclarationstatements/_symboltable/MCVarDeclarationStatementsSTCompleteTypes.java @@ -0,0 +1,50 @@ +/*(c) https://github.com/MontiCore/monticore*/ +package de.monticore.statements.mcvardeclarationstatements._symboltable; + +import com.google.common.collect.Lists; +import de.monticore.grammar.grammar_withconcepts.FullSynthesizeFromMCSGT4Grammar; +import de.monticore.statements.mcvardeclarationstatements._ast.ASTLocalVariableDeclaration; +import de.monticore.statements.mcvardeclarationstatements._ast.ASTVariableDeclarator; +import de.monticore.statements.mcvardeclarationstatements._visitor.MCVarDeclarationStatementsVisitor2; +import de.monticore.symbols.oosymbols._symboltable.FieldSymbol; +import de.monticore.types.check.*; +import de.monticore.types.mcbasictypes._ast.ASTMCType; + +import java.util.List; + +public class MCVarDeclarationStatementsSTCompleteTypes implements MCVarDeclarationStatementsVisitor2 { + + protected ISynthesize typeSynthesizer; + + public MCVarDeclarationStatementsSTCompleteTypes() { + this(new FullSynthesizeFromMCSGT4Grammar()); + } + + public MCVarDeclarationStatementsSTCompleteTypes(ISynthesize typeSynthesizer) { + this.typeSynthesizer = typeSynthesizer; + } + + public ISynthesize getTypeSynthesizer() { + return this.typeSynthesizer; + } + + public void endVisit(ASTLocalVariableDeclaration ast) { + List symbols = Lists.newArrayList(); + for (ASTVariableDeclarator v : ast.getVariableDeclaratorList()) { + SymTypeExpression simpleType = createTypeLoader(ast.getMCType()); + v.getDeclarator().getSymbol().setType(simpleType); + symbols.add(v.getDeclarator().getSymbol()); + } + } + + protected SymTypeExpression createTypeLoader(ASTMCType ast) { + // Start visitor + TypeCheckResult typeCheckResult = this.getTypeSynthesizer().synthesizeType(ast); + if(typeCheckResult.isPresentResult()){ + return typeCheckResult.getResult(); + } + return new SymTypeOfNull(); + } + + +} diff --git a/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCArrayStatementsFullPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCArrayStatementsFullPrettyPrinter.java new file mode 100644 index 0000000000..acd62e9d2b --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCArrayStatementsFullPrettyPrinter.java @@ -0,0 +1,48 @@ +/*(c) https://github.com/MontiCore/monticore*/ +package de.monticore.statements.prettyprint; + +import de.monticore.expressions.prettyprint.ExpressionsBasisPrettyPrinter; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.prettyprint.MCBasicsPrettyPrinter; +import de.monticore.statements.mcarraystatements.MCArrayStatementsMill; +import de.monticore.statements.mcarraystatements._visitor.MCArrayStatementsTraverser; +import de.monticore.types.prettyprint.MCBasicTypesPrettyPrinter; + +@Deprecated(forRemoval = true) +public class MCArrayStatementsFullPrettyPrinter extends MCVarDeclarationStatementsFullPrettyPrinter { + + protected MCArrayStatementsTraverser traverser; + + public MCArrayStatementsFullPrettyPrinter(IndentPrinter printer) { + super(printer); + this.traverser = MCArrayStatementsMill.traverser(); + + ExpressionsBasisPrettyPrinter expressionsBasis = new ExpressionsBasisPrettyPrinter(printer); + traverser.add4ExpressionsBasis(expressionsBasis); + traverser.setExpressionsBasisHandler(expressionsBasis); + + MCBasicsPrettyPrinter basics = new MCBasicsPrettyPrinter(printer); + traverser.add4MCBasics(basics); + + MCBasicTypesPrettyPrinter basicTypes = new MCBasicTypesPrettyPrinter(printer); + traverser.add4MCBasicTypes(basicTypes); + traverser.setMCBasicTypesHandler(basicTypes); + + MCVarDeclarationStatementsPrettyPrinter varDecl = new MCVarDeclarationStatementsPrettyPrinter(printer); + traverser.add4MCVarDeclarationStatements(varDecl); + traverser.setMCVarDeclarationStatementsHandler(varDecl); + + MCArrayStatementsPrettyPrinter arrayStatements = new MCArrayStatementsPrettyPrinter(printer); + traverser.add4MCArrayStatements(arrayStatements); + traverser.setMCArrayStatementsHandler(arrayStatements); + } + + @Override + public MCArrayStatementsTraverser getTraverser() { + return traverser; + } + + public void setTraverser(MCArrayStatementsTraverser traverser) { + this.traverser = traverser; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCArrayStatementsPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCArrayStatementsPrettyPrinter.java new file mode 100644 index 0000000000..9368fc2614 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCArrayStatementsPrettyPrinter.java @@ -0,0 +1,65 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.statements.prettyprint; + +import de.monticore.prettyprint.CommentPrettyPrinter; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.statements.mcarraystatements._ast.ASTArrayDeclaratorId; +import de.monticore.statements.mcarraystatements._ast.ASTArrayInit; +import de.monticore.statements.mcarraystatements._visitor.MCArrayStatementsHandler; +import de.monticore.statements.mcarraystatements._visitor.MCArrayStatementsTraverser; +import de.monticore.statements.mcarraystatements._visitor.MCArrayStatementsVisitor2; +import de.monticore.statements.mcvardeclarationstatements._ast.ASTVariableInit; + +@Deprecated(forRemoval = true) +public class MCArrayStatementsPrettyPrinter implements + MCArrayStatementsVisitor2, MCArrayStatementsHandler { + + protected MCArrayStatementsTraverser traverser; + + protected IndentPrinter printer; + + public MCArrayStatementsPrettyPrinter(IndentPrinter out) { + this.printer = out; + } + + @Override + public MCArrayStatementsTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(MCArrayStatementsTraverser traverser) { + this.traverser = traverser; + } + + public IndentPrinter getPrinter() { + return this.printer; + } + + @Override + public void handle(ASTArrayDeclaratorId a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + getPrinter().print(a.getName()); + for (int i = 0; i < a.getDimList().size(); i++) { + getPrinter().print("[]"); + } + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTArrayInit a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + getPrinter().print("{"); + String sep = ""; + for (ASTVariableInit v: a.getVariableInitList()) { + getPrinter().print(sep); + sep = ", "; + v.accept(getTraverser()); + } + getPrinter().print("}"); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + +} diff --git a/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCAssertStatementsFullPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCAssertStatementsFullPrettyPrinter.java new file mode 100644 index 0000000000..c202419998 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCAssertStatementsFullPrettyPrinter.java @@ -0,0 +1,55 @@ +/*(c) https://github.com/MontiCore/monticore*/ +package de.monticore.statements.prettyprint; + +import de.monticore.expressions.prettyprint.ExpressionsBasisPrettyPrinter; +import de.monticore.expressions.prettyprint.ExpressionsBasisFullPrettyPrinter; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.prettyprint.MCBasicsPrettyPrinter; +import de.monticore.statements.mcassertstatements.MCAssertStatementsMill; +import de.monticore.statements.mcassertstatements._ast.ASTMCAssertStatementsNode; +import de.monticore.statements.mcassertstatements._visitor.MCAssertStatementsTraverser; + +@Deprecated(forRemoval = true) +public class MCAssertStatementsFullPrettyPrinter extends ExpressionsBasisFullPrettyPrinter { + + protected MCAssertStatementsTraverser traverser; + + public MCAssertStatementsFullPrettyPrinter(IndentPrinter printer){ + super(printer); + this.traverser = MCAssertStatementsMill.traverser(); + + ExpressionsBasisPrettyPrinter expressionsBasis = new ExpressionsBasisPrettyPrinter(printer); + traverser.add4ExpressionsBasis(expressionsBasis); + traverser.setExpressionsBasisHandler(expressionsBasis); + + MCAssertStatementsPrettyPrinter assertStatements = new MCAssertStatementsPrettyPrinter(printer); + traverser.add4MCAssertStatements(assertStatements); + traverser.setMCAssertStatementsHandler(assertStatements); + + traverser.add4MCBasics(new MCBasicsPrettyPrinter(printer)); + } + + public MCAssertStatementsTraverser getTraverser() { + return traverser; + } + + public void setTraverser(MCAssertStatementsTraverser traverser) { + this.traverser = traverser; + } + + /** + * This method prettyprints a given node from Java. + * + * @param a A node from Java. + * @return String representation. + */ + public String prettyprint(ASTMCAssertStatementsNode a) { + getPrinter().clearBuffer(); + a.accept(getTraverser()); + return getPrinter().getContent(); + } + + + + +} diff --git a/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCAssertStatementsPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCAssertStatementsPrettyPrinter.java new file mode 100644 index 0000000000..aaf62394cd --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCAssertStatementsPrettyPrinter.java @@ -0,0 +1,50 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.statements.prettyprint; + +import de.monticore.prettyprint.CommentPrettyPrinter; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.statements.mcassertstatements._ast.ASTAssertStatement; +import de.monticore.statements.mcassertstatements._visitor.MCAssertStatementsHandler; +import de.monticore.statements.mcassertstatements._visitor.MCAssertStatementsTraverser; +import de.monticore.statements.mcassertstatements._visitor.MCAssertStatementsVisitor2; + +@Deprecated(forRemoval = true) +public class MCAssertStatementsPrettyPrinter implements MCAssertStatementsVisitor2, MCAssertStatementsHandler { + + protected MCAssertStatementsTraverser traverser; + + protected IndentPrinter printer; + + public MCAssertStatementsPrettyPrinter(IndentPrinter out) { + this.printer = out; + } + + public IndentPrinter getPrinter() { + return this.printer; + } + + @Override + public void setTraverser(MCAssertStatementsTraverser traverser) { + this.traverser = traverser; + } + + @Override + public MCAssertStatementsTraverser getTraverser() { + return traverser; + } + + @Override + public void handle(ASTAssertStatement a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + getPrinter().print("assert "); + a.getAssertion().accept(getTraverser()); + if (a.isPresentMessage()) { + getPrinter().print(" : "); + a.getMessage().accept(getTraverser()); + } + getPrinter().println(";"); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCCommonStatementsFullPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCCommonStatementsFullPrettyPrinter.java new file mode 100644 index 0000000000..49061a701f --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCCommonStatementsFullPrettyPrinter.java @@ -0,0 +1,67 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements.prettyprint; + +import de.monticore.expressions.prettyprint.ExpressionsBasisPrettyPrinter; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.prettyprint.MCBasicsPrettyPrinter; +import de.monticore.statements.mccommonstatements.MCCommonStatementsMill; +import de.monticore.statements.mccommonstatements._ast.ASTMCCommonStatementsNode; +import de.monticore.statements.mccommonstatements._visitor.MCCommonStatementsTraverser; +import de.monticore.statements.mcvardeclarationstatements._ast.ASTMCVarDeclarationStatementsNode; +import de.monticore.types.prettyprint.MCBasicTypesPrettyPrinter; + +@Deprecated(forRemoval = true) +public class MCCommonStatementsFullPrettyPrinter { + + protected MCCommonStatementsTraverser traverser; + + protected IndentPrinter printer; + + public MCCommonStatementsFullPrettyPrinter(IndentPrinter printer) { + this.traverser = MCCommonStatementsMill.traverser(); + this.printer = printer; + + ExpressionsBasisPrettyPrinter expressionsBasis = new ExpressionsBasisPrettyPrinter(printer); + traverser.add4ExpressionsBasis(expressionsBasis); + traverser.setExpressionsBasisHandler(expressionsBasis); + + MCBasicTypesPrettyPrinter basicTypes = new MCBasicTypesPrettyPrinter(printer); + traverser.setMCBasicTypesHandler(basicTypes); + traverser.add4MCBasicTypes(basicTypes); + + MCCommonStatementsPrettyPrinter commonStatements = new MCCommonStatementsPrettyPrinter(printer); + traverser.setMCCommonStatementsHandler(commonStatements); + traverser.add4MCCommonStatements(commonStatements); + + MCVarDeclarationStatementsPrettyPrinter varDecl = new MCVarDeclarationStatementsPrettyPrinter(printer); + traverser.setMCVarDeclarationStatementsHandler(varDecl); + traverser.add4MCVarDeclarationStatements(varDecl); + + MCBasicsPrettyPrinter basics = new MCBasicsPrettyPrinter(printer); + traverser.add4MCBasics(basics); + } + + public MCCommonStatementsTraverser getTraverser() { + return traverser; + } + + public void setTraverser(MCCommonStatementsTraverser traverser) { + this.traverser = traverser; + } + + protected IndentPrinter getPrinter() { + return this.printer; + } + + public String prettyprint(ASTMCCommonStatementsNode a) { + getPrinter().clearBuffer(); + a.accept(getTraverser()); + return getPrinter().getContent(); + } + + public String prettyprint(ASTMCVarDeclarationStatementsNode a) { + getPrinter().clearBuffer(); + a.accept(getTraverser()); + return getPrinter().getContent(); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCCommonStatementsPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCCommonStatementsPrettyPrinter.java new file mode 100644 index 0000000000..19f2385b66 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCCommonStatementsPrettyPrinter.java @@ -0,0 +1,276 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.statements.prettyprint; + +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.prettyprint.CommentPrettyPrinter; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.statements.mccommonstatements._ast.*; +import de.monticore.statements.mccommonstatements._visitor.MCCommonStatementsHandler; +import de.monticore.statements.mccommonstatements._visitor.MCCommonStatementsTraverser; +import de.monticore.statements.mccommonstatements._visitor.MCCommonStatementsVisitor2; +import de.monticore.types.mcbasictypes._ast.ASTMCType; + +import java.util.Iterator; + +@Deprecated(forRemoval = true) +public class MCCommonStatementsPrettyPrinter implements + MCCommonStatementsVisitor2, MCCommonStatementsHandler { + + protected MCCommonStatementsTraverser traverser; + + protected IndentPrinter printer; + + public MCCommonStatementsPrettyPrinter(IndentPrinter out) { + this.printer = out; + } + + @Override + public MCCommonStatementsTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(MCCommonStatementsTraverser traverser) { + this.traverser = traverser; + } + + public IndentPrinter getPrinter() { + return this.printer; + } + + protected void printNode(String s) { + getPrinter().print(s); + } + + protected void printExpressionsList(Iterator iter, String separator) { + // print by iterate through all items + String sep = ""; + while (iter.hasNext()) { + getPrinter().print(sep); + iter.next().accept(getTraverser()); + sep = separator; + } + } + + protected void printMCTypeList(Iterator iter, String separator) { + // print by iterate through all items + String sep = ""; + while (iter.hasNext()) { + getPrinter().print(sep); + iter.next().accept(getTraverser()); + sep = separator; + } + } + + @Override + public void handle(ASTMCJavaBlock a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + getPrinter().println("{"); + getPrinter().indent(); + a.getMCBlockStatementList().stream().forEach(m -> m.accept(getTraverser())); + getPrinter().unindent(); + getPrinter().println("}"); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTIfStatement a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + getPrinter().print("if ("); + a.getCondition().accept(getTraverser()); + getPrinter().print(") "); + a.getThenStatement().accept(getTraverser()); + if (a.isPresentElseStatement()) { + getPrinter().println("else "); + a.getElseStatement().accept(getTraverser()); + } + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTForStatement a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + getPrinter().print("for ("); + a.getForControl().accept(getTraverser()); + getPrinter().print(")"); + a.getMCStatement().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTWhileStatement a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + getPrinter().print("while ("); + a.getCondition().accept(getTraverser()); + getPrinter().print(")"); + a.getMCStatement().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTDoWhileStatement a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + getPrinter().print("do "); + a.getMCStatement().accept(getTraverser()); + getPrinter().print("while ("); + a.getCondition().accept(getTraverser()); + getPrinter().println(");"); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTCommonForControl a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + if (a.isPresentForInit()) { + a.getForInit().accept(getTraverser()); + } + getPrinter().print(";"); + if (a.isPresentCondition()) { + a.getCondition().accept(getTraverser()); + } + getPrinter().print(";"); + printExpressionsList(a.getExpressionList().iterator(), ","); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTForInitByExpressions a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + getPrinter().print(" "); + printExpressionsList(a.getExpressionList().iterator(), ", "); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTEnhancedForControl a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + a.getFormalParameter().accept(getTraverser()); + getPrinter().print(": "); + a.getExpression().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTJavaModifier a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + getPrinter().print(" " + printModifier(a.getModifier()) + " "); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTFormalParameter a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + printSeparated(a.getJavaModifierList().iterator(), " "); + a.getMCType().accept(getTraverser()); + getPrinter().print(" "); + a.getDeclarator().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTEmptyStatement a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + getPrinter().println(";"); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTExpressionStatement a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + a.getExpression().accept(getTraverser()); + getPrinter().println(";"); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTSwitchStatement a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + getPrinter().print("switch ("); + a.getExpression().accept(getTraverser()); + getPrinter().println(") {"); + getPrinter().indent(); + printSeparated(a.getSwitchBlockStatementGroupList().iterator(), ""); + printSeparated(a.getSwitchLabelList().iterator(), ""); + getPrinter().unindent(); + getPrinter().println("}"); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTConstantExpressionSwitchLabel a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + getPrinter().println("case "); + a.getConstant().accept(getTraverser()); + getPrinter().println(":"); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTEnumConstantSwitchLabel a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + getPrinter().println("case "); + printNode(a.getEnumConstant()); + getPrinter().println(":"); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTDefaultSwitchLabel a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + getPrinter().println("default:"); + } + + @Override + public void handle(ASTBreakStatement a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + getPrinter().println("break;"); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + protected void printSeparated(Iterator iter, String separator) { + // print by iterate through all items + String sep = ""; + while (iter.hasNext()) { + getPrinter().print(sep); + iter.next().accept(getTraverser()); + sep = separator; + } + } + + protected String printModifier(int constant) { + + switch (constant) { + case ASTConstantsMCCommonStatements.PRIVATE: + return "private"; + case ASTConstantsMCCommonStatements.PUBLIC: + return "public"; + case ASTConstantsMCCommonStatements.PROTECTED: + return "protected"; + case ASTConstantsMCCommonStatements.STATIC: + return "static"; + case ASTConstantsMCCommonStatements.TRANSIENT: + return "transient"; + case ASTConstantsMCCommonStatements.FINAL: + return "final"; + case ASTConstantsMCCommonStatements.ABSTRACT: + return "abstract"; + case ASTConstantsMCCommonStatements.NATIVE: + return "native"; + case ASTConstantsMCCommonStatements.THREADSAFE: + return "threadsafe"; + case ASTConstantsMCCommonStatements.SYNCHRONIZED: + return "synchronized"; + case ASTConstantsMCCommonStatements.VOLATILE: + return "volatile"; + case ASTConstantsMCCommonStatements.STRICTFP: + return "strictfp"; + case ASTConstantsMCCommonStatements.MODIFIER_DEFAULT: + return "default"; + default: + return null; + } + } + + +} diff --git a/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCExceptionStatementsFullPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCExceptionStatementsFullPrettyPrinter.java new file mode 100644 index 0000000000..710a325e1d --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCExceptionStatementsFullPrettyPrinter.java @@ -0,0 +1,59 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements.prettyprint; + +import de.monticore.expressions.prettyprint.ExpressionsBasisPrettyPrinter; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.prettyprint.MCBasicsPrettyPrinter; +import de.monticore.statements.mcexceptionstatements.MCExceptionStatementsMill; +import de.monticore.statements.mcexceptionstatements._ast.ASTMCExceptionStatementsNode; +import de.monticore.statements.mcexceptionstatements._visitor.MCExceptionStatementsTraverser; +import de.monticore.types.prettyprint.MCBasicTypesPrettyPrinter; + +@Deprecated(forRemoval = true) +public class MCExceptionStatementsFullPrettyPrinter extends MCCommonStatementsFullPrettyPrinter { + + protected MCExceptionStatementsTraverser traverser; + + public MCExceptionStatementsFullPrettyPrinter(IndentPrinter printer) { + super(printer); + this.traverser = MCExceptionStatementsMill.traverser(); + + ExpressionsBasisPrettyPrinter expressionsBasis = new ExpressionsBasisPrettyPrinter(printer); + traverser.add4ExpressionsBasis(expressionsBasis); + traverser.setExpressionsBasisHandler(expressionsBasis); + + MCBasicTypesPrettyPrinter basicTypes = new MCBasicTypesPrettyPrinter(printer); + traverser.setMCBasicTypesHandler(basicTypes); + traverser.add4MCBasicTypes(basicTypes); + + MCCommonStatementsPrettyPrinter commonStatements = new MCCommonStatementsPrettyPrinter(printer); + traverser.setMCCommonStatementsHandler(commonStatements); + traverser.add4MCCommonStatements(commonStatements); + + MCVarDeclarationStatementsPrettyPrinter varDecl = new MCVarDeclarationStatementsPrettyPrinter(printer); + traverser.setMCVarDeclarationStatementsHandler(varDecl); + traverser.add4MCVarDeclarationStatements(varDecl); + + MCBasicsPrettyPrinter basics = new MCBasicsPrettyPrinter(printer); + traverser.add4MCBasics(basics); + + MCExceptionStatementsPrettyPrinter exceptionStatements = new MCExceptionStatementsPrettyPrinter(printer); + traverser.setMCExceptionStatementsHandler(exceptionStatements); + traverser.add4MCExceptionStatements(exceptionStatements); + } + + @Override + public MCExceptionStatementsTraverser getTraverser() { + return traverser; + } + + public void setTraverser(MCExceptionStatementsTraverser traverser) { + this.traverser = traverser; + } + + public String prettyprint(ASTMCExceptionStatementsNode a) { + getPrinter().clearBuffer(); + a.accept(getTraverser()); + return getPrinter().getContent(); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCExceptionStatementsPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCExceptionStatementsPrettyPrinter.java new file mode 100644 index 0000000000..033d16b843 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCExceptionStatementsPrettyPrinter.java @@ -0,0 +1,154 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.statements.prettyprint; + +import de.monticore.prettyprint.CommentPrettyPrinter; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.statements.mcexceptionstatements._ast.*; +import de.monticore.statements.mcexceptionstatements._visitor.MCExceptionStatementsHandler; +import de.monticore.statements.mcexceptionstatements._visitor.MCExceptionStatementsTraverser; +import de.monticore.statements.mcexceptionstatements._visitor.MCExceptionStatementsVisitor2; +import de.monticore.types.mcbasictypes._ast.ASTMCQualifiedName; + +import java.util.Iterator; + +@Deprecated(forRemoval = true) +public class MCExceptionStatementsPrettyPrinter implements + MCExceptionStatementsVisitor2, MCExceptionStatementsHandler { + + protected MCExceptionStatementsTraverser traverser; + + protected IndentPrinter printer; + + public MCExceptionStatementsPrettyPrinter(IndentPrinter out) { + this.printer = out; + } + + @Override + public MCExceptionStatementsTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(MCExceptionStatementsTraverser traverser) { + this.traverser = traverser; + } + + public IndentPrinter getPrinter() { + return printer; + } + + public void setPrinter(IndentPrinter printer) { + this.printer = printer; + } + + @Override + public void handle(ASTTryStatement1 a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + getPrinter().println("try "); + a.getCore().accept(getTraverser()); + printExceptionStatementsList(a.getCatchClauseList().iterator(), ""); + getPrinter().println(); + if (a.isPresentFinally()) { + getPrinter().print(" finally "); + a.getFinally().accept(getTraverser()); + } + getPrinter().println(); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTTryStatement2 a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + getPrinter().println("try "); + a.getCore().accept(getTraverser()); + printExceptionStatementsList(a.getCatchClauseList().iterator(), ""); + getPrinter().println(); + getPrinter().print(" finally "); + a.getFinally().accept(getTraverser()); + getPrinter().println(); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTTryStatement3 a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + getPrinter().println("try ("); + String sep = ""; + for (ASTTryLocalVariableDeclaration l: a.getTryLocalVariableDeclarationList()) { + getPrinter().print(sep); + sep = "; "; + l.accept(getTraverser()); + } + getPrinter().print(")"); + a.getCore().accept(getTraverser()); + printExceptionStatementsList(a.getCatchClauseList().iterator(), ""); + getPrinter().println(); + if (a.isPresentFinally()) { + getPrinter().print(" finally "); + a.getFinally().accept(getTraverser()); + } + getPrinter().println(); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTTryLocalVariableDeclaration a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + a.getJavaModifierList().stream().forEach(m -> {m.accept(getTraverser()); getPrinter().print(" ");}); + a.getMCType().accept(getTraverser()); + getPrinter().print(" "); + a.getDeclaratorId().accept(getTraverser()); + getPrinter().print(" = "); + a.getExpression().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + + @Override + public void handle(ASTThrowStatement a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + getPrinter().print("throw "); + a.getExpression().accept(getTraverser()); + getPrinter().println(";"); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTCatchClause a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + getPrinter().print("catch ("); + a.getJavaModifierList().stream().forEach(m -> {m.accept(getTraverser()); getPrinter().print(" ");}); + a.getCatchTypeList().accept(getTraverser()); + getPrinter().print(" "); + getPrinter().print(a.getName()); + getPrinter().print(") "); + a.getMCJavaBlock().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTCatchTypeList a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + String sep = ""; + for (ASTMCQualifiedName q: a.getMCQualifiedNameList()) { + getPrinter().print(sep); + q.accept(getTraverser()); + sep = "|"; + } + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + + protected void printExceptionStatementsList(Iterator iter, String separator) { + // print by iterate through all items + String sep = ""; + while (iter.hasNext()) { + getPrinter().print(sep); + iter.next().accept(getTraverser()); + sep = separator; + } + } + + +} diff --git a/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCLowLevelStatementsFullPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCLowLevelStatementsFullPrettyPrinter.java new file mode 100644 index 0000000000..7c8b90a274 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCLowLevelStatementsFullPrettyPrinter.java @@ -0,0 +1,55 @@ +/*(c) https://github.com/MontiCore/monticore*/ +package de.monticore.statements.prettyprint; + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.prettyprint.MCBasicsPrettyPrinter; +import de.monticore.statements.mclowlevelstatements.MCLowLevelStatementsMill; +import de.monticore.statements.mclowlevelstatements._ast.ASTMCLowLevelStatementsNode; +import de.monticore.statements.mclowlevelstatements._visitor.MCLowLevelStatementsTraverser; + +@Deprecated(forRemoval = true) +public class MCLowLevelStatementsFullPrettyPrinter { + + protected MCLowLevelStatementsTraverser traverser; + + protected IndentPrinter printer; + + public MCLowLevelStatementsFullPrettyPrinter(IndentPrinter printer){ + this.printer = printer; + this.traverser = MCLowLevelStatementsMill.traverser(); + + traverser.add4MCBasics(new MCBasicsPrettyPrinter(printer)); + + MCLowLevelStatementsPrettyPrinter lowLevel = new MCLowLevelStatementsPrettyPrinter(printer); + traverser.setMCLowLevelStatementsHandler(lowLevel); + traverser.add4MCLowLevelStatements(lowLevel); + } + + public void setTraverser(MCLowLevelStatementsTraverser traverser) { + this.traverser = traverser; + } + + public MCLowLevelStatementsTraverser getTraverser() { + return traverser; + } + + public IndentPrinter getPrinter() { + return printer; + } + + public void setPrinter(IndentPrinter printer) { + this.printer = printer; + } + + /** + * This method prettyprints a given node from Java. + * + * @param a A node from Java. + * @return String representation. + */ + public String prettyprint(ASTMCLowLevelStatementsNode a) { + getPrinter().clearBuffer(); + a.accept(getTraverser()); + return getPrinter().getContent(); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCLowLevelStatementsPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCLowLevelStatementsPrettyPrinter.java new file mode 100644 index 0000000000..b48a1f7cd2 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCLowLevelStatementsPrettyPrinter.java @@ -0,0 +1,70 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.statements.prettyprint; + +import de.monticore.prettyprint.CommentPrettyPrinter; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.statements.mclowlevelstatements._ast.ASTLabelledBreakStatement; +import de.monticore.statements.mclowlevelstatements._ast.ASTContinueStatement; +import de.monticore.statements.mclowlevelstatements._ast.ASTLabel; +import de.monticore.statements.mclowlevelstatements._visitor.MCLowLevelStatementsHandler; +import de.monticore.statements.mclowlevelstatements._visitor.MCLowLevelStatementsTraverser; +import de.monticore.statements.mclowlevelstatements._visitor.MCLowLevelStatementsVisitor2; + +@Deprecated(forRemoval = true) +public class MCLowLevelStatementsPrettyPrinter implements MCLowLevelStatementsVisitor2, MCLowLevelStatementsHandler { + + protected MCLowLevelStatementsTraverser traverser; + + protected IndentPrinter printer; + + public MCLowLevelStatementsPrettyPrinter(IndentPrinter out) { + this.printer = out; + } + + public IndentPrinter getPrinter() { + return this.printer; + } + + @Override + public MCLowLevelStatementsTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(MCLowLevelStatementsTraverser traverser) { + this.traverser = traverser; + } + + @Override + public void handle(ASTLabel a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + getPrinter().print(a.getName()); + getPrinter().print(" : "); + a.getMCStatement().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTLabelledBreakStatement a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + getPrinter().print("break "); + if (a.isPresentLabel()) { + getPrinter().print(a.getLabel()); + } + getPrinter().println(";"); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTContinueStatement a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + getPrinter().print("continue "); + if (a.isPresentLabel()) { + getPrinter().print(a.getLabel()); + } + getPrinter().println(";"); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCReturnStatementsFullPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCReturnStatementsFullPrettyPrinter.java new file mode 100644 index 0000000000..753df405c7 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCReturnStatementsFullPrettyPrinter.java @@ -0,0 +1,57 @@ +/*(c) https://github.com/MontiCore/monticore*/ +package de.monticore.statements.prettyprint; + +import de.monticore.expressions.prettyprint.ExpressionsBasisPrettyPrinter; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.prettyprint.MCBasicsPrettyPrinter; +import de.monticore.statements.mcreturnstatements.MCReturnStatementsMill; +import de.monticore.statements.mcreturnstatements._ast.ASTMCReturnStatementsNode; +import de.monticore.statements.mcreturnstatements._visitor.MCReturnStatementsTraverser; + +@Deprecated(forRemoval = true) +public class MCReturnStatementsFullPrettyPrinter { + + protected MCReturnStatementsTraverser traverser; + + protected IndentPrinter printer; + + public MCReturnStatementsFullPrettyPrinter(IndentPrinter printer){ + this.printer = printer; + this.traverser = MCReturnStatementsMill.traverser(); + + ExpressionsBasisPrettyPrinter expressionsBasis = new ExpressionsBasisPrettyPrinter(printer); + traverser.add4ExpressionsBasis(expressionsBasis); + traverser.setExpressionsBasisHandler(expressionsBasis); + + MCReturnStatementsPrettyPrinter returnStatements = new MCReturnStatementsPrettyPrinter(printer); + traverser.add4MCReturnStatements(returnStatements); + traverser.setMCReturnStatementsHandler(returnStatements); + + traverser.add4MCBasics(new MCBasicsPrettyPrinter(printer)); + } + + public MCReturnStatementsTraverser getTraverser() { + return traverser; + } + + public void setTraverser(MCReturnStatementsTraverser traverser) { + this.traverser = traverser; + } + + public IndentPrinter getPrinter() { + return printer; + } + + /** + * This method prettyprints a given node from Java. + * + * @param a A node from Java. + * @return String representation. + */ + public String prettyprint(ASTMCReturnStatementsNode a) { + getPrinter().clearBuffer(); + a.accept(getTraverser()); + return getPrinter().getContent(); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCReturnStatementsPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCReturnStatementsPrettyPrinter.java new file mode 100644 index 0000000000..84c1e80183 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCReturnStatementsPrettyPrinter.java @@ -0,0 +1,48 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.statements.prettyprint; + +import de.monticore.prettyprint.CommentPrettyPrinter; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.statements.mcreturnstatements._ast.ASTReturnStatement; +import de.monticore.statements.mcreturnstatements._visitor.MCReturnStatementsHandler; +import de.monticore.statements.mcreturnstatements._visitor.MCReturnStatementsTraverser; +import de.monticore.statements.mcreturnstatements._visitor.MCReturnStatementsVisitor2; + +@Deprecated(forRemoval = true) +public class MCReturnStatementsPrettyPrinter implements MCReturnStatementsVisitor2, MCReturnStatementsHandler { + + protected MCReturnStatementsTraverser traverser; + + protected IndentPrinter printer; + + public MCReturnStatementsPrettyPrinter(IndentPrinter out) { + this.printer = out; + } + + @Override + public MCReturnStatementsTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(MCReturnStatementsTraverser traverser) { + this.traverser = traverser; + } + + public IndentPrinter getPrinter() { + return this.printer; + } + + @Override + public void handle(ASTReturnStatement a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + getPrinter().print("return "); + if (a.isPresentExpression()) { + a.getExpression().accept(getTraverser()); + } + getPrinter().println(";"); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCSynchronizedStatementsFullPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCSynchronizedStatementsFullPrettyPrinter.java new file mode 100644 index 0000000000..29c411e50a --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCSynchronizedStatementsFullPrettyPrinter.java @@ -0,0 +1,59 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements.prettyprint; + +import de.monticore.expressions.prettyprint.ExpressionsBasisPrettyPrinter; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.prettyprint.MCBasicsPrettyPrinter; +import de.monticore.statements.mcsynchronizedstatements.MCSynchronizedStatementsMill; +import de.monticore.statements.mcsynchronizedstatements._ast.ASTMCSynchronizedStatementsNode; +import de.monticore.statements.mcsynchronizedstatements._visitor.MCSynchronizedStatementsTraverser; +import de.monticore.types.prettyprint.MCBasicTypesPrettyPrinter; + +@Deprecated(forRemoval = true) +public class MCSynchronizedStatementsFullPrettyPrinter extends MCCommonStatementsFullPrettyPrinter { + + protected MCSynchronizedStatementsTraverser traverser; + + public MCSynchronizedStatementsFullPrettyPrinter(IndentPrinter printer) { + super(printer); + this.traverser = MCSynchronizedStatementsMill.traverser(); + + ExpressionsBasisPrettyPrinter expressionsBasis = new ExpressionsBasisPrettyPrinter(printer); + traverser.add4ExpressionsBasis(expressionsBasis); + traverser.setExpressionsBasisHandler(expressionsBasis); + + MCBasicTypesPrettyPrinter basicTypes = new MCBasicTypesPrettyPrinter(printer); + traverser.setMCBasicTypesHandler(basicTypes); + traverser.add4MCBasicTypes(basicTypes); + + MCCommonStatementsPrettyPrinter commonStatements = new MCCommonStatementsPrettyPrinter(printer); + traverser.setMCCommonStatementsHandler(commonStatements); + traverser.add4MCCommonStatements(commonStatements); + + MCVarDeclarationStatementsPrettyPrinter varDecl = new MCVarDeclarationStatementsPrettyPrinter(printer); + traverser.setMCVarDeclarationStatementsHandler(varDecl); + traverser.add4MCVarDeclarationStatements(varDecl); + + MCBasicsPrettyPrinter basics = new MCBasicsPrettyPrinter(printer); + traverser.add4MCBasics(basics); + + MCSynchronizedStatementsPrettyPrinter synchronizedStatements = new MCSynchronizedStatementsPrettyPrinter(printer); + traverser.setMCSynchronizedStatementsHandler(synchronizedStatements); + traverser.add4MCSynchronizedStatements(synchronizedStatements); + } + + public String prettyprint(ASTMCSynchronizedStatementsNode a) { + getPrinter().clearBuffer(); + a.accept(getTraverser()); + return getPrinter().getContent(); + } + + @Override + public MCSynchronizedStatementsTraverser getTraverser() { + return traverser; + } + + public void setTraverser(MCSynchronizedStatementsTraverser traverser) { + this.traverser = traverser; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCSynchronizedStatementsPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCSynchronizedStatementsPrettyPrinter.java new file mode 100644 index 0000000000..f77f33ef7a --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCSynchronizedStatementsPrettyPrinter.java @@ -0,0 +1,51 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.statements.prettyprint; + +import de.monticore.prettyprint.CommentPrettyPrinter; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.statements.mcsynchronizedstatements._ast.ASTSynchronizedStatement; +import de.monticore.statements.mcsynchronizedstatements._visitor.MCSynchronizedStatementsHandler; +import de.monticore.statements.mcsynchronizedstatements._visitor.MCSynchronizedStatementsTraverser; +import de.monticore.statements.mcsynchronizedstatements._visitor.MCSynchronizedStatementsVisitor2; + +@Deprecated(forRemoval = true) +public class MCSynchronizedStatementsPrettyPrinter implements MCSynchronizedStatementsVisitor2, MCSynchronizedStatementsHandler { + + protected MCSynchronizedStatementsTraverser traverser; + + protected IndentPrinter printer; + + public MCSynchronizedStatementsPrettyPrinter(IndentPrinter out) { + this.printer = out; + } + + public IndentPrinter getPrinter() { + return printer; + } + + public void setPrinter(IndentPrinter printer) { + this.printer = printer; + } + + @Override + public MCSynchronizedStatementsTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(MCSynchronizedStatementsTraverser traverser) { + this.traverser = traverser; + } + + @Override + public void handle(ASTSynchronizedStatement a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + getPrinter().print("synchronized ("); + a.getExpression().accept(getTraverser()); + getPrinter().print(") "); + a.getMCJavaBlock().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCVarDeclarationStatementsFullPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCVarDeclarationStatementsFullPrettyPrinter.java new file mode 100644 index 0000000000..3d89901974 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCVarDeclarationStatementsFullPrettyPrinter.java @@ -0,0 +1,60 @@ +/*(c) https://github.com/MontiCore/monticore*/ +package de.monticore.statements.prettyprint; + +import de.monticore.expressions.prettyprint.ExpressionsBasisPrettyPrinter; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.prettyprint.MCBasicsPrettyPrinter; +import de.monticore.statements.mcvardeclarationstatements.MCVarDeclarationStatementsMill; +import de.monticore.statements.mcvardeclarationstatements._ast.ASTMCVarDeclarationStatementsNode; +import de.monticore.statements.mcvardeclarationstatements._visitor.MCVarDeclarationStatementsTraverser; +import de.monticore.types.prettyprint.MCBasicTypesPrettyPrinter; + +@Deprecated(forRemoval = true) +public class MCVarDeclarationStatementsFullPrettyPrinter { + + protected MCVarDeclarationStatementsTraverser traverser; + + protected IndentPrinter printer; + + public MCVarDeclarationStatementsFullPrettyPrinter(IndentPrinter printer){ + this.printer = printer; + this.traverser = MCVarDeclarationStatementsMill.traverser(); + + ExpressionsBasisPrettyPrinter expressionsBasis = new ExpressionsBasisPrettyPrinter(printer); + traverser.add4ExpressionsBasis(expressionsBasis); + traverser.setExpressionsBasisHandler(expressionsBasis); + + MCBasicsPrettyPrinter basics = new MCBasicsPrettyPrinter(printer); + traverser.add4MCBasics(basics); + + MCBasicTypesPrettyPrinter basicTypes = new MCBasicTypesPrettyPrinter(printer); + traverser.add4MCBasicTypes(basicTypes); + traverser.setMCBasicTypesHandler(basicTypes); + + MCVarDeclarationStatementsPrettyPrinter varDecl = new MCVarDeclarationStatementsPrettyPrinter(printer); + traverser.add4MCVarDeclarationStatements(varDecl); + traverser.setMCVarDeclarationStatementsHandler(varDecl); + } + + public MCVarDeclarationStatementsTraverser getTraverser() { + return traverser; + } + + public void setTraverser(MCVarDeclarationStatementsTraverser traverser) { + this.traverser = traverser; + } + + public IndentPrinter getPrinter() { + return printer; + } + + public void setPrinter(IndentPrinter printer) { + this.printer = printer; + } + + public String prettyprint(ASTMCVarDeclarationStatementsNode a) { + getPrinter().clearBuffer(); + a.accept(getTraverser()); + return getPrinter().getContent(); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCVarDeclarationStatementsPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCVarDeclarationStatementsPrettyPrinter.java new file mode 100644 index 0000000000..a4ba6ba3cc --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/statements/prettyprint/MCVarDeclarationStatementsPrettyPrinter.java @@ -0,0 +1,83 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.statements.prettyprint; + +import de.monticore.prettyprint.CommentPrettyPrinter; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.statements.mcvardeclarationstatements._ast.ASTDeclaratorId; +import de.monticore.statements.mcvardeclarationstatements._ast.ASTLocalVariableDeclaration; +import de.monticore.statements.mcvardeclarationstatements._ast.ASTLocalVariableDeclarationStatement; +import de.monticore.statements.mcvardeclarationstatements._ast.ASTVariableDeclarator; +import de.monticore.statements.mcvardeclarationstatements._visitor.MCVarDeclarationStatementsHandler; +import de.monticore.statements.mcvardeclarationstatements._visitor.MCVarDeclarationStatementsTraverser; +import de.monticore.statements.mcvardeclarationstatements._visitor.MCVarDeclarationStatementsVisitor2; + +@Deprecated(forRemoval = true) +public class MCVarDeclarationStatementsPrettyPrinter implements + MCVarDeclarationStatementsVisitor2, MCVarDeclarationStatementsHandler { + + protected IndentPrinter printer; + + protected MCVarDeclarationStatementsTraverser traverser; + + public MCVarDeclarationStatementsPrettyPrinter(IndentPrinter out) { + this.printer = out; + } + + @Override + public MCVarDeclarationStatementsTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(MCVarDeclarationStatementsTraverser traverser) { + this.traverser = traverser; + } + + public IndentPrinter getPrinter() { + return this.printer; + } + + @Override + public void handle(ASTVariableDeclarator a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + a.getDeclarator().accept(getTraverser()); + if (a.isPresentVariableInit()) { + getPrinter().print(" = "); + a.getVariableInit().accept(getTraverser()); + } + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTDeclaratorId a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + getPrinter().print(a.getName()); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTLocalVariableDeclarationStatement a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + a.getLocalVariableDeclaration().accept(getTraverser()); + getPrinter().println(";"); + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + + @Override + public void handle(ASTLocalVariableDeclaration a) { + CommentPrettyPrinter.printPreComments(a, getPrinter()); + a.getMCModifierList().stream().forEach(m -> {getPrinter().print(" "); m.accept(getTraverser()); getPrinter().print(" ");}); + getPrinter().print(" "); + a.getMCType().accept(getTraverser()); + getPrinter().print(" "); + String sep = ""; + for (ASTVariableDeclarator v: a.getVariableDeclaratorList()) { + getPrinter().print(sep); + sep = ", "; + v.accept(getTraverser()); + } + CommentPrettyPrinter.printPostComments(a, getPrinter()); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/symbols/basicsymbols/BasicSymbolsMill.java b/monticore-grammar/src/main/java/de/monticore/symbols/basicsymbols/BasicSymbolsMill.java new file mode 100644 index 0000000000..5c036e7363 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/symbols/basicsymbols/BasicSymbolsMill.java @@ -0,0 +1,74 @@ +/*(c) https://github.com/MontiCore/monticore*/ +package de.monticore.symbols.basicsymbols; + +import com.google.common.collect.Lists; +import de.monticore.symbols.basicsymbols._symboltable.IBasicSymbolsGlobalScope; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.symboltable.modifiers.AccessModifier; + +import java.util.List; +import java.util.Collections; + +public class BasicSymbolsMill extends BasicSymbolsMillTOP { + + protected static BasicSymbolsMill primitiveTypesInitializer; + + public static void initMe (BasicSymbolsMill a) { + BasicSymbolsMillTOP.initMe(a); + primitiveTypesInitializer = a; + } + + public static final String INT = "int"; + + public static final String DOUBLE = "double"; + + public static final String FLOAT = "float"; + + public static final String SHORT = "short"; + + public static final String LONG = "long"; + + public static final String BOOLEAN = "boolean"; + + public static final String BYTE = "byte"; + + public static final String CHAR = "char"; + + public static final String NULL = "null"; + + public static final String VOID = "void"; + + public static final List PRIMITIVE_LIST = Collections.unmodifiableList(Lists.newArrayList(INT, DOUBLE, FLOAT, SHORT, LONG, BOOLEAN, BYTE, CHAR, NULL, VOID)); + + + public static void initializePrimitives(){ + if(primitiveTypesInitializer == null){ + primitiveTypesInitializer = getMill(); + } + primitiveTypesInitializer._initializePrimitives(); + } + + public void _initializePrimitives(){ + IBasicSymbolsGlobalScope gs = globalScope(); + + for(String primitive: PRIMITIVE_LIST){ + gs.add(createPrimitive(primitive)); + } + } + + protected TypeSymbol createPrimitive(String name){ + return typeSymbolBuilder() + .setName(name) + .setEnclosingScope(globalScope()) + .setFullName(name) + .setSpannedScope(scope()) + .setAccessModifier(AccessModifier.ALL_INCLUSION) + .build(); + } + + public static void reset () { + BasicSymbolsMillTOP.reset(); + primitiveTypesInitializer = null; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/symbols/basicsymbols/_symboltable/FunctionSymbol.java b/monticore-grammar/src/main/java/de/monticore/symbols/basicsymbols/_symboltable/FunctionSymbol.java new file mode 100644 index 0000000000..c72c25649e --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/symbols/basicsymbols/_symboltable/FunctionSymbol.java @@ -0,0 +1,112 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symbols.basicsymbols._symboltable; + +import com.google.common.collect.Lists; +import de.monticore.symboltable.modifiers.AccessModifier; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types.check.SymTypeOfFunction; +import de.se_rwth.commons.logging.Log; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public class FunctionSymbol extends FunctionSymbolTOP { + + public FunctionSymbol(String name){ + super(name); + } + + public FunctionSymbol deepClone(){ + FunctionSymbol clone = new FunctionSymbol(name); + clone.setType(this.getType().deepClone()); + clone.setIsElliptic(this.isIsElliptic()); + clone.setEnclosingScope(this.enclosingScope); + clone.setFullName(this.fullName); + if(isPresentAstNode()) { + clone.setAstNode(this.getAstNode()); + } + clone.setAccessModifier(this.accessModifier); + clone.setSpannedScope(this.spannedScope); + return clone; + } + + public List getTypeVariableList(){ + if (spannedScope == null) { + return Lists.newArrayList(); + } + return spannedScope.getLocalTypeVarSymbols(); + } + + public List getAllAccessibleTypeVariables(){ + List typeVarSymbolList = getTypeVariableList(); + typeVarSymbolList.addAll(getTypeVariablesOfEnclosingType()); + return typeVarSymbolList; + } + + public List getTypeVariablesOfEnclosingType(){ + List typeVarSymbolList = new ArrayList<>(); + IBasicSymbolsScope scope = getSpannedScope(); + while(scope.getEnclosingScope()!=null){ + scope = scope.getEnclosingScope(); + if(scope.isPresentSpanningSymbol() && scope.getSpanningSymbol() instanceof TypeSymbol){ + typeVarSymbolList.addAll(((TypeSymbol)(scope.getSpanningSymbol())).getTypeParameterList()); + } + } + return typeVarSymbolList; + } + + public List getParameterList(){ + //TODO: how to filter for parameters? + return Lists.newArrayList(getSpannedScope().getLocalVariableSymbols()); + } + + public void replaceTypeVariables(Map replaceMap){ + //return type + SymTypeExpression type = this.getType(); + TypeSymbol realTypeInfo; + TypeSymbol typeInfo = type.getTypeInfo(); + if(typeInfo instanceof TypeSymbolSurrogate){ + realTypeInfo = ((TypeSymbolSurrogate) type.getTypeInfo()).lazyLoadDelegate(); + }else{ + realTypeInfo = typeInfo; + } + if(type.isTypeVariable() && realTypeInfo instanceof TypeVarSymbol){ + Optional typeVar = replaceMap.keySet().stream().filter(t -> t.getName().equals(realTypeInfo.getName())).findAny(); + typeVar.ifPresent(typeVarSymbol -> this.setType(replaceMap.get(typeVarSymbol))); + }else{ + type.replaceTypeVariables(replaceMap); + } + + for(VariableSymbol parameter: this.getParameterList()){ + parameter.replaceTypeVariables(replaceMap); + } + } + + /* + * Returns the type of the function. + * Note that {@link getType()} only provides the return type of the function + */ + public SymTypeOfFunction getFunctionType() { + SymTypeExpression returnType = getType(); + List parameterTypes = new ArrayList<>(); + for(VariableSymbol parameter : getParameterList()) { + parameterTypes.add(parameter.getType()); + } + return SymTypeExpressionFactory.createFunction(this, returnType, parameterTypes, isIsElliptic()); + } + + @Override + public AccessModifier getAccessModifier() { + // supporting legacy source code... + if(accessModifier == null) { + Log.trace("AccessModifier of function '" + + getFullName() + "' was not set (null)", + "BasicSymbols"); + accessModifier = AccessModifier.ALL_INCLUSION; + } + return accessModifier; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/symbols/basicsymbols/_symboltable/FunctionSymbolDeSer.java b/monticore-grammar/src/main/java/de/monticore/symbols/basicsymbols/_symboltable/FunctionSymbolDeSer.java new file mode 100644 index 0000000000..872b6223cd --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/symbols/basicsymbols/_symboltable/FunctionSymbolDeSer.java @@ -0,0 +1,20 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symbols.basicsymbols._symboltable; + +import de.monticore.symboltable.serialization.json.JsonObject; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionDeSer; + +public class FunctionSymbolDeSer extends FunctionSymbolDeSerTOP { + + @Override + protected void serializeType(SymTypeExpression type, BasicSymbolsSymbols2Json s2j) { + SymTypeExpressionDeSer.serializeMember(s2j.getJsonPrinter(), "type", type); + } + + @Override + public SymTypeExpression deserializeType(JsonObject symbolJson) { + return SymTypeExpressionDeSer.deserializeMember("type", symbolJson); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/symbols/basicsymbols/_symboltable/IBasicSymbolsScope.java b/monticore-grammar/src/main/java/de/monticore/symbols/basicsymbols/_symboltable/IBasicSymbolsScope.java new file mode 100644 index 0000000000..eb33d4730c --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/symbols/basicsymbols/_symboltable/IBasicSymbolsScope.java @@ -0,0 +1,78 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.symbols.basicsymbols._symboltable; + +import de.monticore.symboltable.IScopeSpanningSymbol; +import de.monticore.symboltable.modifiers.AccessModifier; +import de.monticore.types.check.SymTypeExpression; + +import java.util.List; +import java.util.function.Predicate; + +public interface IBasicSymbolsScope extends IBasicSymbolsScopeTOP { + + /** + * returns whether a type variable is bound within this scope + * e.g. class C {} // T is bound within the class + */ + default boolean isTypeVariableBound(TypeVarSymbol typeVar) { + if (getLocalTypeVarSymbols().stream() + .anyMatch(otherTypeVar -> otherTypeVar == typeVar)) { + return true; + } + else if (getEnclosingScope() == null) { + return false; + } + else { + return getEnclosingScope().isTypeVariableBound(typeVar); + } + } + + /** + * override method from ExpressionBasisScope to resolve all methods correctly + * method needed to be overridden because of special cases: if the scope is spanned by a type symbol you have to look for fitting methods in its super types too because of inheritance + * the method resolves the methods like the overridden method and if the spanning symbol is a type symbol it additionally looks for methods in its super types + * it is used by the method getMethodList in SymTypeExpression + */ + @Override + default List resolveFunctionLocallyMany(boolean foundSymbols, String name, AccessModifier modifier, + Predicate predicate) { + //resolve methods by using overridden method + List set = IBasicSymbolsScopeTOP.super.resolveFunctionLocallyMany(foundSymbols,name,modifier,predicate); + if(this.isPresentSpanningSymbol()){ + IScopeSpanningSymbol spanningSymbol = getSpanningSymbol(); + //if the methodsymbol is in the spanned scope of a typesymbol then look for method in super types too + if(spanningSymbol instanceof TypeSymbol){ + TypeSymbol typeSymbol = ((TypeSymbol) spanningSymbol); + for(SymTypeExpression t : typeSymbol.getSuperTypesList()){ + set.addAll(t.getMethodList(name, true, modifier)); + } + } + } + return set; + } + + /** + * override method from ExpressionBasisScope to resolve all fields correctly + * method needed to be overridden because of special cases: if the scope is spanned by a type symbol you have to look for fitting fields in its super types too because of inheritance + * the method resolves the fields like the overridden method and if the spanning symbol is a type symbol it additionally looks for fields in its super types + * it is used by the method getFieldList in SymTypeExpression + */ + @Override + default List resolveVariableLocallyMany(boolean foundSymbols, String name, AccessModifier modifier, Predicate predicate){ + //resolve methods by using overridden method + List result = IBasicSymbolsScopeTOP.super.resolveVariableLocallyMany(foundSymbols,name,modifier,predicate); + if(this.isPresentSpanningSymbol()){ + IScopeSpanningSymbol spanningSymbol = getSpanningSymbol(); + //if the fieldsymbol is in the spanned scope of a typesymbol then look for method in super types too + if(spanningSymbol instanceof TypeSymbol){ + TypeSymbol typeSymbol = (TypeSymbol) spanningSymbol; + for(SymTypeExpression superType : typeSymbol.getSuperTypesList()){ + result.addAll(superType.getFieldList(name, true, modifier)); + } + } + } + return result; + } + + +} diff --git a/monticore-grammar/src/main/java/de/monticore/symbols/basicsymbols/_symboltable/TypeSymbol.java b/monticore-grammar/src/main/java/de/monticore/symbols/basicsymbols/_symboltable/TypeSymbol.java new file mode 100644 index 0000000000..df33675d64 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/symbols/basicsymbols/_symboltable/TypeSymbol.java @@ -0,0 +1,109 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symbols.basicsymbols._symboltable; + +import com.google.common.collect.Lists; +import de.monticore.symboltable.modifiers.AccessModifier; +import de.monticore.types.check.SymTypeExpression; +import de.se_rwth.commons.logging.Log; + +import java.util.List; + +public class TypeSymbol extends TypeSymbolTOP { + + public TypeSymbol(String name){ + super(name); + } + + public void setFunctionList(List methodList){ + for(FunctionSymbol method: methodList){ + getSpannedScope().add(method); + } + } + + public List getSuperClassesOnly(){ + return superTypes; + } + + public List getInterfaceList(){ + return superTypes; + } + + /** + * get a list of all the methods the type definition can access + */ + public List getFunctionList() { + if (spannedScope == null) { + return Lists.newArrayList(); + } + return getSpannedScope().getLocalFunctionSymbols(); + } + + /** + * search in the scope for methods with a specific name + */ + public List getFunctionList(String methodname) { + return getSpannedScope().resolveFunctionMany(methodname); + } + + /** + * get a list of all the fields the type definition can access + */ + public List getVariableList() { + if (spannedScope == null) { + return Lists.newArrayList(); + } + return getSpannedScope().getLocalVariableSymbols(); + } + + /** + * search in the scope for methods with a specific name + */ + public List getVariableList(String fieldname) { + return getSpannedScope().resolveVariableMany(fieldname); + } + + public List getTypeParameterList() { + if(spannedScope==null){ + return Lists.newArrayList(); + } + return getSpannedScope().getLocalTypeVarSymbols(); + } + + + public void addTypeVarSymbol(TypeVarSymbol t) { + getSpannedScope().add(t); + } + + public void addVariableSymbol(VariableSymbol f) { + getSpannedScope().add(f); + } + + public void addFunctionSymbol(FunctionSymbol m) { + getSpannedScope().add(m); + } + + public boolean isPresentSuperClass() { + return !getSuperClassesOnly().isEmpty(); + } + + public SymTypeExpression getSuperClass() { + if (isPresentSuperClass()) { + return getSuperClassesOnly().get(0); + } + Log.error("0xA1068 SuperClass does not exist"); + // Normally this statement is not reachable + throw new IllegalStateException(); + } + + @Override + public AccessModifier getAccessModifier() { + // supporting legacy source code... + if(accessModifier == null) { + Log.trace("AccessModifier of type '" + + getFullName() + "' was not set (null)", + "BasicSymbols"); + accessModifier = AccessModifier.ALL_INCLUSION; + } + return accessModifier; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/symbols/basicsymbols/_symboltable/TypeSymbolDeSer.java b/monticore-grammar/src/main/java/de/monticore/symbols/basicsymbols/_symboltable/TypeSymbolDeSer.java new file mode 100644 index 0000000000..947fb6c34a --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/symbols/basicsymbols/_symboltable/TypeSymbolDeSer.java @@ -0,0 +1,23 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symbols.basicsymbols._symboltable; + +import de.monticore.symboltable.serialization.json.JsonObject; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionDeSer; + +import java.util.List; + +public class TypeSymbolDeSer extends TypeSymbolDeSerTOP { + + @Override + protected void serializeSuperTypes(List superTypes, + BasicSymbolsSymbols2Json s2j) { + SymTypeExpressionDeSer.serializeMember(s2j.getJsonPrinter(), "superTypes", superTypes); + } + + @Override + public List deserializeSuperTypes(JsonObject symbolJson) { + return SymTypeExpressionDeSer.deserializeListMember("superTypes", symbolJson); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/symbols/basicsymbols/_symboltable/TypeSymbolSurrogate.java b/monticore-grammar/src/main/java/de/monticore/symbols/basicsymbols/_symboltable/TypeSymbolSurrogate.java new file mode 100644 index 0000000000..f56a904f39 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/symbols/basicsymbols/_symboltable/TypeSymbolSurrogate.java @@ -0,0 +1,45 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symbols.basicsymbols._symboltable; + +import com.google.common.collect.Lists; +import de.monticore.types.check.SymTypeExpression; + +import java.util.List; + +public class TypeSymbolSurrogate extends TypeSymbolSurrogateTOP { + + public TypeSymbolSurrogate(String name){ + super(name); + } + + public IBasicSymbolsScope getSpannedScope(){ + if(!checkLazyLoadDelegate()) { + return spannedScope; + } + return lazyLoadDelegate().getSpannedScope(); + } + + @Override + public List getTypeParameterList(){ + if (!checkLazyLoadDelegate()) { + return Lists.newArrayList(); + } + return lazyLoadDelegate().getSpannedScope().getLocalTypeVarSymbols(); + } + + @Override + public List getSuperClassesOnly(){ + if (!checkLazyLoadDelegate()) { + return Lists.newArrayList(); + } + return lazyLoadDelegate().getSuperClassesOnly(); + } + + @Override + public String getFullName() { + if (checkLazyLoadDelegate()) { + return lazyLoadDelegate().getFullName(); + } + return getName(); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/symbols/basicsymbols/_symboltable/TypeVarSymbol.java b/monticore-grammar/src/main/java/de/monticore/symbols/basicsymbols/_symboltable/TypeVarSymbol.java new file mode 100644 index 0000000000..3734ac63a2 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/symbols/basicsymbols/_symboltable/TypeVarSymbol.java @@ -0,0 +1,41 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.symbols.basicsymbols._symboltable; + +import de.monticore.symboltable.modifiers.AccessModifier; +import de.se_rwth.commons.logging.Log; + +public class TypeVarSymbol extends TypeVarSymbolTOP { + + public TypeVarSymbol(String name) { + super(name); + } + + public boolean deepEquals(TypeVarSymbol other) { + if (this == other) { + return true; + } + if (this.getEnclosingScope() != other.getEnclosingScope()) { + return false; + } + // allow null as spanned scope + if (this.spannedScope != other.spannedScope) { + return false; + } + if (!this.getName().equals(other.getName())) { + return false; + } + return true; + } + + @Override + public AccessModifier getAccessModifier() { + // supporting legacy source code... + if(accessModifier == null) { + Log.trace("AccessModifier of type variable '" + + getFullName() + "' was not set (null)", + "BasicSymbols"); + accessModifier = AccessModifier.ALL_INCLUSION; + } + return accessModifier; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/symbols/basicsymbols/_symboltable/TypeVarSymbolDeSer.java b/monticore-grammar/src/main/java/de/monticore/symbols/basicsymbols/_symboltable/TypeVarSymbolDeSer.java new file mode 100644 index 0000000000..e5857b8f22 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/symbols/basicsymbols/_symboltable/TypeVarSymbolDeSer.java @@ -0,0 +1,23 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symbols.basicsymbols._symboltable; + +import de.monticore.symboltable.serialization.json.JsonObject; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionDeSer; + +import java.util.List; + +public class TypeVarSymbolDeSer extends TypeVarSymbolDeSerTOP { + + @Override + protected void serializeSuperTypes(List superTypes, + BasicSymbolsSymbols2Json s2j) { + SymTypeExpressionDeSer.serializeMember(s2j.getJsonPrinter(), "superTypes", superTypes); + } + + @Override + public List deserializeSuperTypes(JsonObject symbolJson) { + return SymTypeExpressionDeSer.deserializeListMember("superTypes", symbolJson); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/symbols/basicsymbols/_symboltable/VariableSymbol.java b/monticore-grammar/src/main/java/de/monticore/symbols/basicsymbols/_symboltable/VariableSymbol.java new file mode 100644 index 0000000000..df84990107 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/symbols/basicsymbols/_symboltable/VariableSymbol.java @@ -0,0 +1,60 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symbols.basicsymbols._symboltable; + +import de.monticore.symboltable.modifiers.AccessModifier; +import de.monticore.types.check.SymTypeExpression; +import de.se_rwth.commons.logging.Log; + +import java.util.Map; +import java.util.Optional; + +public class VariableSymbol extends VariableSymbolTOP { + + public VariableSymbol(String name){ + super(name); + } + + public VariableSymbol deepClone(){ + VariableSymbol clone = new VariableSymbol(name); + clone.setAccessModifier(this.accessModifier); + clone.setEnclosingScope(this.enclosingScope); + clone.setFullName(this.fullName); + if(isPresentAstNode()) { + clone.setAstNode(this.getAstNode()); + } + if(type!=null){ + clone.setType(type.deepClone()); + } + return clone; + } + + public void replaceTypeVariables(Map replaceMap){ + //return type + SymTypeExpression returnType = this.getType(); + TypeSymbol realTypeInfo; + TypeSymbol typeInfo = returnType.getTypeInfo(); + if(typeInfo instanceof TypeSymbolSurrogate){ + realTypeInfo = ((TypeSymbolSurrogate) returnType.getTypeInfo()).lazyLoadDelegate(); + }else{ + realTypeInfo = typeInfo; + } + if(returnType.isTypeVariable() && realTypeInfo instanceof TypeVarSymbol){ + Optional typeVar = replaceMap.keySet().stream().filter(t -> t.getName().equals(realTypeInfo.getName())).findAny(); + typeVar.ifPresent(typeVarSymbol -> this.setType(replaceMap.get(typeVarSymbol))); + }else{ + returnType.replaceTypeVariables(replaceMap); + } + } + + @Override + public AccessModifier getAccessModifier() { + // supporting legacy source code... + if(accessModifier == null) { + Log.trace("AccessModifier of variable '" + + getFullName() + "' was not set (null)", + "BasicSymbols"); + accessModifier = AccessModifier.ALL_INCLUSION; + } + return accessModifier; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/symbols/basicsymbols/_symboltable/VariableSymbolDeSer.java b/monticore-grammar/src/main/java/de/monticore/symbols/basicsymbols/_symboltable/VariableSymbolDeSer.java new file mode 100644 index 0000000000..e295b6dc20 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/symbols/basicsymbols/_symboltable/VariableSymbolDeSer.java @@ -0,0 +1,20 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symbols.basicsymbols._symboltable; + +import de.monticore.symboltable.serialization.json.JsonObject; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionDeSer; + +public class VariableSymbolDeSer extends VariableSymbolDeSerTOP { + + @Override + protected void serializeType(SymTypeExpression type, BasicSymbolsSymbols2Json s2j) { + SymTypeExpressionDeSer.serializeMember(s2j.getJsonPrinter(), "type", type); + } + + @Override + public SymTypeExpression deserializeType(JsonObject symbolJson) { + return SymTypeExpressionDeSer.deserializeMember("type", symbolJson); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/symbols/compsymbols/_symboltable/ComponentSymbol.java b/monticore-grammar/src/main/java/de/monticore/symbols/compsymbols/_symboltable/ComponentSymbol.java new file mode 100644 index 0000000000..4b83d9c24e --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/symbols/compsymbols/_symboltable/ComponentSymbol.java @@ -0,0 +1,295 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symbols.compsymbols._symboltable; + +import com.google.common.base.Preconditions; +import de.monticore.symbols.basicsymbols._symboltable.TypeVarSymbol; +import de.monticore.symbols.basicsymbols._symboltable.VariableSymbol; +import de.monticore.types.check.CompKindExpression; +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +public class ComponentSymbol extends ComponentSymbolTOP { + + protected List parameters; + + public ComponentSymbol(String name) { + super(name); + } + + public List getParameters() { + return this.parameters; + } + + public Optional getParameter(@NonNull String name) { + Preconditions.checkNotNull(name); + for (VariableSymbol parameter : this.getParameters()) { + if (parameter.getName().equals(name)) return Optional.of(parameter); + } + return Optional.empty(); + } + + public void addParameter(@NonNull VariableSymbol parameter) { + Preconditions.checkNotNull(parameter); + Preconditions.checkArgument(this.getSpannedScope().getLocalVariableSymbols().contains(parameter)); + this.parameters.add(parameter); + } + + public boolean hasParameters() { + return !this.getParameters().isEmpty(); + } + + public List getTypeParameters() { + return this.getSpannedScope().getLocalTypeVarSymbols(); + } + + public boolean hasTypeParameter() { + return !this.getTypeParameters().isEmpty(); + } + + public List getPorts() { + return this.getSpannedScope().getLocalPortSymbols(); + } + + /** + * Returns the port of this component that matches the given name, if it + * exists. Does not consider inherited ports. + * + * @param name the name of the port + * @return the port with the given name wrapped in an {@code Optional} or + * an empty {@code Optional} if no such port exists + */ + public Optional getPort(@NonNull String name) { + return this.getPort(name, false); + } + + /** + * Returns the port of this component that matches the given name, if it + * exists. Does consider inherited ports if {@code searchSuper} is set + * to true. + * + * @param name the name of the port + * @param searchSuper whether to consider ports of super components + * @return the port with the given name wrapped in an {@code Optional} or + * an empty {@code Optional} if no such port exists. + */ + public Optional getPort(@NonNull String name, boolean searchSuper) { + Preconditions.checkNotNull(name); + for (PortSymbol port : searchSuper ? this.getAllPorts() : this.getPorts()) { + if (port.getName().equals(name)) return Optional.of(port); + } + return Optional.empty(); + } + + /** + * Returns the incoming ports of this component. Does not include inherited ports. + * + * @return a {@code List} of incoming ports of this component + */ + public List getIncomingPorts() { + List result = new ArrayList<>(); + for (PortSymbol port : this.getPorts()) { + if (port.isIncoming()) { + result.add(port); + } + } + return result; + } + + /** + * Returns the incoming port of this component that matches the given name, + * if it exists. Does not consider inherited ports. + * + * @param name the name of the port + * @return the incoming port with the given name wrapped in an + * {@code Optional} or an empty {@code Optional} if no such port exists. + */ + public Optional getIncomingPort(@NonNull String name) { + Preconditions.checkNotNull(name); + return this.getIncomingPort(name, false); + } + + /** + * Returns the incoming port with matching name of this component, if it + * exists. Does consider inherited ports if {@code searchSuper} is set + * to true. + * + * @param name the name of the port + * @param searchSuper whether to consider ports of super components + * @return the incoming port with the given name wrapped in an + * {@code Optional} or an empty {@code Optional} if no such port exists + */ + public Optional getIncomingPort(@NonNull String name, boolean searchSuper) { + Preconditions.checkNotNull(name); + for (PortSymbol port : searchSuper ? this.getAllIncomingPorts() : this.getIncomingPorts()) { + if (port.getName().equals(name)) return Optional.of(port); + } + return Optional.empty(); + } + + /** + * Returns the outgoing ports of this component. Does not include inherited ports. + * + * @return a {@code List} of the outgoing ports of this component + */ + public List getOutgoingPorts() { + List result = new ArrayList<>(); + for (PortSymbol port : this.getPorts()) { + if (port.isOutgoing()) { + result.add(port); + } + } + return result; + } + + /** + * Returns the outgoing port of this component that matches the given name, + * if it exists. Does not consider inherited ports. + * + * @param name the name of the port + * @return the outgoing port with the given name wrapped in an + * {@code Optional} or an empty {@code Optional} if no such port exists. + */ + public Optional getOutgoingPort(@NonNull String name) { + Preconditions.checkNotNull(name); + return this.getOutgoingPort(name, false); + } + + /** + * Returns the outgoing port of this component that matches the given name, + * if it exists. Does consider inherited ports if {@code searchSuper} is set + * to true. + * + * @param name the name of the port + * @param searchSuper whether to consider ports of super components + * @return the outgoing port with the given name wrapped in an + * {@code Optional} or an empty {@code Optional} if no such port exists + */ + public Optional getOutgoingPort(@NonNull String name, boolean searchSuper) { + Preconditions.checkNotNull(name); + for (PortSymbol port : searchSuper ? this.getAllOutgoingPorts() : this.getOutgoingPorts()) { + if (port.getName().equals(name)) return Optional.of(port); + } + return Optional.empty(); + } + + /** + * Returns the ports of this component with matching direction. Does not + * included inherited ports. + * + * @param incoming whether to included incoming ports + * @param outgoing whether to included outgoing ports + * @return a {@code List} of all ports of this component the given direction + */ + public List getPorts(boolean incoming, boolean outgoing) { + List result = new ArrayList<>(); + for (PortSymbol port : this.getPorts()) { + if (port.isIncoming() == incoming && port.isOutgoing() == outgoing) { + result.add(port); + } + } + return result; + } + + /** + * Return all ports of this component, including inherited ports. + * + * @return a {@code Set} of all ports of this component + */ + public Set getAllPorts() { + Set result = new HashSet<>(this.getPorts()); + for (CompKindExpression superComponent : this.getSuperComponentsList()) { + result.addAll(superComponent.getTypeInfo().getAllPorts()); + } + return result; + } + + /** + * Returns all incoming ports of this component, including inherited ports. + * + * @return a {@code Set} of all incoming ports of this component + */ + public Set getAllIncomingPorts() { + Set result = new HashSet<>(); + for (PortSymbol port : this.getAllPorts()) { + if (port.isIncoming()) { + result.add(port); + } + } + return result; + } + + /** + * Returns all outgoing ports of this component, including inherited ports. + * + * @return a {@code Set} of all outgoing ports of this component + */ + public Set getAllOutgoingPorts() { + Set result = new HashSet<>(); + for (PortSymbol port : this.getAllPorts()) { + if (port.isOutgoing()) { + result.add(port); + } + } + return result; + } + + /** + * Returns the ports of this component with matching direction. Does included + * inherited ports. + * + * @param incoming whether to included incoming ports + * @param outgoing whether to included outgoing ports + * @return a {@code Set} of all ports of this component with the given direction + */ + public Set getAllPorts(boolean incoming, boolean outgoing) { + Set result = new HashSet<>(); + for (PortSymbol port : this.getAllPorts()) { + if (port.isIncoming() == incoming && port.isOutgoing() == outgoing) { + result.add(port); + } + } + return result; + } + + public boolean hasPorts() { + return !this.getPorts().isEmpty(); + } + + /** + * @return a {@code List} of the subcomponents of this component + */ + public List getSubcomponents() { + return this.getSpannedScope().getLocalSubcomponentSymbols(); + } + + /** + * Returns the subcomponent with matching name of this component, if it + * exists. + * + * @param name the name of the subcomponent + * @return the subcomponent with the given name wrapped in an + * {@code Optional} or an empty {@code Optional} if no such subcomponent + * exists. + */ + public Optional getSubcomponents(@NonNull String name) { + Preconditions.checkNotNull(name); + for (SubcomponentSymbol subcomponent : this.getSubcomponents()) { + if (subcomponent.getName().equals(name)) return Optional.of(subcomponent); + } + return Optional.empty(); + } + + public boolean isDecomposed() { + return !this.getSubcomponents().isEmpty(); + } + + public boolean isAtomic() { + return this.getSubcomponents().isEmpty(); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/symbols/compsymbols/_symboltable/ComponentSymbolDeSer.java b/monticore-grammar/src/main/java/de/monticore/symbols/compsymbols/_symboltable/ComponentSymbolDeSer.java new file mode 100644 index 0000000000..bca62d4795 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/symbols/compsymbols/_symboltable/ComponentSymbolDeSer.java @@ -0,0 +1,206 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symbols.compsymbols._symboltable; + +import com.google.common.base.Preconditions; +import de.monticore.symbols.basicsymbols._symboltable.TypeVarSymbol; +import de.monticore.symbols.basicsymbols._symboltable.VariableSymbol; +import de.monticore.symbols.compsymbols.CompSymbolsMill; +import de.monticore.symboltable.serialization.ISymbolDeSer; +import de.monticore.symboltable.serialization.JsonDeSers; +import de.monticore.symboltable.serialization.JsonPrinter; +import de.monticore.symboltable.serialization.json.JsonElement; +import de.monticore.symboltable.serialization.json.JsonElementFactory; +import de.monticore.symboltable.serialization.json.JsonObject; +import de.monticore.types.check.CompKindExpression; +import de.monticore.types.check.FullCompKindExprDeSer; +import de.se_rwth.commons.logging.Log; +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class ComponentSymbolDeSer extends ComponentSymbolDeSerTOP { + + public static final String PARAMETERS = "parameters"; + public static final String PORTS = "ports"; + public static final String TYPE_PARAMETERS = "typeParameters"; + public static final String SUPER = "super"; + + private FullCompKindExprDeSer compTypeExprDeSer; + + public ComponentSymbolDeSer() { + + } + + /** + * @param compTypeExprDeSer the DeSer to use for (de)serializing the super components + */ + public ComponentSymbolDeSer(@NonNull FullCompKindExprDeSer compTypeExprDeSer) { + this.compTypeExprDeSer = Preconditions.checkNotNull(compTypeExprDeSer); + } + + protected FullCompKindExprDeSer getCompTypeExprDeSer() { + return compTypeExprDeSer; + } + + @Override + public String serialize(@NonNull ComponentSymbol toSerialize, @NonNull CompSymbolsSymbols2Json s2j) { + JsonPrinter printer = s2j.getJsonPrinter(); + printer.beginObject(); + printer.member(JsonDeSers.KIND, getSerializedKind()); + printer.member(JsonDeSers.NAME, toSerialize.getName()); + + // serialize symbolrule attributes + serializeSuperComponents(toSerialize.getSuperComponentsList(), s2j); + + // Don't serialize the spanned scope (because it carries private information) + // Instead, serialize type parameters and normal parameters separately. + s2j.getTraverser().addTraversedElement(toSerialize.getSpannedScope()); // So the spanned scope is not visited + serializeParameters(toSerialize, s2j); + serializePorts(toSerialize, s2j); + serializeTypeParameters(toSerialize, s2j); + + serializeAddons(toSerialize, s2j); + printer.endObject(); + + return printer.toString(); + } + + @Override + protected void serializeSuperComponents(@NonNull List superComponents, + @NonNull CompSymbolsSymbols2Json s2j) { + s2j.getJsonPrinter().beginArray(SUPER); + for (CompKindExpression superComponent : superComponents) { + s2j.getJsonPrinter().addToArray(JsonElementFactory + .createJsonString(this.getCompTypeExprDeSer().serializeAsJson(superComponent))); + } + s2j.getJsonPrinter().endArray(); + } + + @Override + protected List deserializeSuperComponents(JsonObject symbolJson) { + + List superComponents = symbolJson.getArrayMemberOpt(SUPER).orElseGet(Collections::emptyList); + List result = new ArrayList<>(superComponents.size()); + + for (JsonElement superComponent : superComponents) { + result.add(this.getCompTypeExprDeSer().deserialize(superComponent)); + } + return result; + } + + @Override + protected void deserializeAddons(ComponentSymbol symbol, JsonObject symbolJson) { + deserializeParameters(symbol, symbolJson); + deserializePorts(symbol, symbolJson); + deserializeTypeParameters(symbol, symbolJson); + } + + protected void serializeParameters(@NonNull ComponentSymbol paramOwner, @NonNull CompSymbolsSymbols2Json s2j) { + JsonPrinter printer = s2j.getJsonPrinter(); + + printer.beginArray(PARAMETERS); + paramOwner.getParameters().forEach(p -> p.accept(s2j.getTraverser())); + printer.endArray(); + } + + protected void serializePorts(@NonNull ComponentSymbol portOwner, @NonNull CompSymbolsSymbols2Json s2j) { + JsonPrinter printer = s2j.getJsonPrinter(); + + printer.beginArray(PORTS); + portOwner.getPorts().forEach(p -> p.accept(s2j.getTraverser())); + printer.endArray(); + } + + /** + * @param paramOwner the component which owns the parameter. + * @param paramOwnerJson the component which owns the parameters, encoded as JSON. + */ + protected void deserializeParameters(@NonNull ComponentSymbol paramOwner, @NonNull JsonObject paramOwnerJson) { + final String varSerializeKind = VariableSymbol.class.getCanonicalName(); + + List params = paramOwnerJson.getArrayMemberOpt(PARAMETERS).orElseGet(Collections::emptyList); + + for (JsonElement param : params) { + String paramJsonKind = JsonDeSers.getKind(param.getAsJsonObject()); + if (paramJsonKind.equals(varSerializeKind)) { + ISymbolDeSer deSer = CompSymbolsMill.globalScope().getSymbolDeSer(varSerializeKind); + VariableSymbol paramSym = (VariableSymbol) deSer.deserialize(param.getAsJsonObject()); + + paramOwner.getSpannedScope().add(paramSym); + paramOwner.addParameter(paramSym); + + } else { + Log.error(String.format( + "0xD0101 Malformed json, parameter '%s' of unsupported kind '%s'", + param.getAsJsonObject().getStringMember(JsonDeSers.NAME), paramJsonKind + )); + } + } + } + + /** + * @param portOwner the component which owns the parameter. + * @param paramOwnerJson the component which owns the parameters, encoded as JSON. + */ + protected void deserializePorts(@NonNull ComponentSymbol portOwner, @NonNull JsonObject paramOwnerJson) { + final String portSerializeKind = PortSymbol.class.getCanonicalName(); + + List ports = paramOwnerJson.getArrayMemberOpt(PORTS).orElseGet(Collections::emptyList); + + for (JsonElement port : ports) { + String portJasonKind = JsonDeSers.getKind(port.getAsJsonObject()); + if (portJasonKind.equals(portSerializeKind)) { + ISymbolDeSer deSer = CompSymbolsMill.globalScope().getSymbolDeSer(portSerializeKind); + PortSymbol portSym = (PortSymbol) deSer.deserialize(port.getAsJsonObject()); + + portOwner.getSpannedScope().add(portSym); + + } else { + Log.error(String.format( + "0xD0102 Malformed json, port '%s' of unsupported kind '%s'", + port.getAsJsonObject().getStringMember(JsonDeSers.NAME), portJasonKind + )); + } + } + } + + protected void serializeTypeParameters(@NonNull ComponentSymbol typeParamOwner, CompSymbolsSymbols2Json s2j) { + JsonPrinter printer = s2j.getJsonPrinter(); + + printer.beginArray(TYPE_PARAMETERS); + typeParamOwner.getTypeParameters().forEach(tp -> tp.accept(s2j.getTraverser())); + printer.endArray(); + } + + /** + * @param typeParamOwner the component which owns the parameter. + * @param typeParamOwnerJson the component which owns the type parameters, encoded as JSON. + */ + protected void deserializeTypeParameters(@NonNull ComponentSymbol typeParamOwner, + @NonNull JsonObject typeParamOwnerJson) { + final String typeVarSerializedKind = "de.monticore.symbols.basicsymbols._symboltable.TypeVarSymbol"; + + List typeParams = + typeParamOwnerJson + .getArrayMemberOpt(TYPE_PARAMETERS) + .orElseGet(Collections::emptyList); + + for (JsonElement typeParam : typeParams) { + String typeParamJsonKind = JsonDeSers.getKind(typeParam.getAsJsonObject()); + if (typeParamJsonKind.equals(typeVarSerializedKind)) { + ISymbolDeSer deSer = CompSymbolsMill.globalScope().getSymbolDeSer(typeVarSerializedKind); + TypeVarSymbol typeParamSym = (TypeVarSymbol) deSer.deserialize(typeParam.getAsJsonObject()); + + typeParamOwner.getSpannedScope().add(typeParamSym); + } else { + Log.error(String.format( + "0xD0103 Malformed json, type parameter '%s' of unsupported kind '%s'", + typeParam.getAsJsonObject().getStringMember(JsonDeSers.NAME), typeParamJsonKind + )); + + } + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/symbols/compsymbols/_symboltable/PortSymbol.java b/monticore-grammar/src/main/java/de/monticore/symbols/compsymbols/_symboltable/PortSymbol.java new file mode 100644 index 0000000000..c37c9b090d --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/symbols/compsymbols/_symboltable/PortSymbol.java @@ -0,0 +1,83 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symbols.compsymbols._symboltable; + +import com.google.common.base.Preconditions; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbolSurrogate; +import de.monticore.types.check.SymTypeExpression; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + +public class PortSymbol extends PortSymbolTOP { + + protected Boolean delayed = null; + protected Boolean stronglyCausal = null; + + protected PortSymbol(String name) { + super(name); + } + + /** + * @param name the name of this port. + * @param incoming whether the port is incoming. + * @param outgoing whether the port is outgoing. + * @param type the type of this port. + * @param timing the timing of this port. + */ + protected PortSymbol(String name, + boolean incoming, + boolean outgoing, + SymTypeExpression type, + Timing timing) { + super(name); + this.type = type; + this.timing = timing; + this.incoming = incoming; + this.outgoing = outgoing; + } + + public boolean isTypePresent() { + return this.type != null; + } + + public SymTypeExpression getType() { + Preconditions.checkState(this.type != null); + return this.type; + } + + public void setType(@NonNull SymTypeExpression type) { + Preconditions.checkNotNull(type); + this.type = type; + } + + public TypeSymbol getTypeInfo() { + return this.getType().getTypeInfo() instanceof TypeSymbolSurrogate ? + ((TypeSymbolSurrogate) this.getType().getTypeInfo()).lazyLoadDelegate() : this.getType().getTypeInfo(); + } + + @Override + public Timing getTiming() { + return this.timing; + } + + @Override + public void setTiming(@Nullable Timing timing) { + this.timing = timing; + } + + public Boolean getDelayed() { + return this.isDelayed(); + } + + public boolean isDelayed() { + return this.delayed; + } + + public void setDelayed(@Nullable Boolean delayed) { + this.delayed = delayed; + } + + public boolean isStronglyCausal() { + return this.stronglyCausal; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/symbols/compsymbols/_symboltable/PortSymbolDeSer.java b/monticore-grammar/src/main/java/de/monticore/symbols/compsymbols/_symboltable/PortSymbolDeSer.java new file mode 100644 index 0000000000..d0e2cbf114 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/symbols/compsymbols/_symboltable/PortSymbolDeSer.java @@ -0,0 +1,41 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symbols.compsymbols._symboltable; + +import de.monticore.symboltable.serialization.json.JsonObject; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionDeSer; +import de.se_rwth.commons.logging.Log; +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.Optional; + +public class PortSymbolDeSer extends PortSymbolDeSerTOP { + + @Override + protected void serializeType(@NonNull SymTypeExpression type, @NonNull CompSymbolsSymbols2Json s2j) { + SymTypeExpressionDeSer.serializeMember(s2j.getJsonPrinter(), "type", type); + } + + @Override + protected void serializeTiming(@NonNull Timing timing, @NonNull CompSymbolsSymbols2Json s2j) { + s2j.getJsonPrinter().member("timing", timing.getName()); + } + + @Override + protected SymTypeExpression deserializeType(@NonNull JsonObject symbolJson) { + return SymTypeExpressionDeSer.deserializeMember("type", symbolJson); + } + + @Override + protected Timing deserializeTiming(@NonNull JsonObject symbolJson) { + String value = symbolJson.getStringMember("timing"); + Optional timing = Timing.of(value); + + if (timing.isPresent()) { + return timing.get(); + } else { + Log.error("0xD0100 Malformed json, unsupported timing '" + value + "'"); + return Timing.DEFAULT; + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/symbols/compsymbols/_symboltable/SubcomponentSymbolDeSer.java b/monticore-grammar/src/main/java/de/monticore/symbols/compsymbols/_symboltable/SubcomponentSymbolDeSer.java new file mode 100644 index 0000000000..a91a015d5c --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/symbols/compsymbols/_symboltable/SubcomponentSymbolDeSer.java @@ -0,0 +1,38 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symbols.compsymbols._symboltable; + +import com.google.common.base.Preconditions; +import de.monticore.symboltable.serialization.json.JsonObject; +import de.monticore.types.check.CompKindExpression; +import de.monticore.types.check.FullCompKindExprDeSer; +import org.checkerframework.checker.nullness.qual.NonNull; + +public class SubcomponentSymbolDeSer extends SubcomponentSymbolDeSerTOP { + + private FullCompKindExprDeSer compKindExprDeSer; + + public SubcomponentSymbolDeSer() { + + } + + protected FullCompKindExprDeSer getCompKindExprDeSer() { + return this.compKindExprDeSer; + } + + /** + * @param compKindExprDeSer the DeSer to use for (de)serializing the super components + */ + public SubcomponentSymbolDeSer(@NonNull FullCompKindExprDeSer compKindExprDeSer) { + this.compKindExprDeSer = Preconditions.checkNotNull(compKindExprDeSer); + } + + @Override + protected void serializeType(CompKindExpression type, CompSymbolsSymbols2Json s2j) { + s2j.getJsonPrinter().memberJson("type", this.getCompKindExprDeSer().serializeAsJson(type)); + } + + @Override + protected CompKindExpression deserializeType(JsonObject symbolJson) { + return null; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/symbols/compsymbols/_symboltable/Timing.java b/monticore-grammar/src/main/java/de/monticore/symbols/compsymbols/_symboltable/Timing.java new file mode 100644 index 0000000000..f07db14dec --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/symbols/compsymbols/_symboltable/Timing.java @@ -0,0 +1,43 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symbols.compsymbols._symboltable; + +import com.google.common.base.Preconditions; +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.Arrays; +import java.util.Optional; + +public enum Timing { + UNTIMED("untimed"), + TIMED("timed"), + TIMED_SYNC("sync"); + + final private String name; + + public String getName() { + return this.name; + } + + Timing(@NonNull String name) { + Preconditions.checkNotNull(name); + Preconditions.checkArgument(!name.isBlank()); + this.name = name; + } + + public static Timing DEFAULT = UNTIMED; + + public static boolean contains(String name) { + for (Timing t : values()) { + if (t.name.equals(name)) return true; + } + return false; + } + + public static Optional of(String name) { + return Arrays.stream(Timing.values()).filter(t -> t.getName().equals(name)).findFirst(); + } + + public boolean matches(Timing t) { + return this.equals(t); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/symbols/library/StreamType.java b/monticore-grammar/src/main/java/de/monticore/symbols/library/StreamType.java new file mode 100644 index 0000000000..078a52b288 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/symbols/library/StreamType.java @@ -0,0 +1,241 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.symbols.library; + +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.FunctionSymbol; +import de.monticore.symbols.basicsymbols._symboltable.IBasicSymbolsScope; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.symbols.basicsymbols._symboltable.TypeVarSymbol; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionFactory; + +/** + * Adds symbols for streams + */ +public class StreamType { + + protected static final String STREAM_TYPE_NAME = "Stream"; + + public void addStreamType() { + TypeVarSymbol typeVarSymbol = BasicSymbolsMill.typeVarSymbolBuilder().setName("T").build(); + IBasicSymbolsScope spannedScope = BasicSymbolsMill.scope(); + spannedScope.setName(""); + + TypeSymbol typeSymbol = BasicSymbolsMill.typeSymbolBuilder() + .setName(STREAM_TYPE_NAME) + .setEnclosingScope(BasicSymbolsMill.globalScope()) + .setSpannedScope(spannedScope) + .build(); + typeSymbol.addTypeVarSymbol(typeVarSymbol); + + BasicSymbolsMill.globalScope().add(typeSymbol); + BasicSymbolsMill.globalScope().addSubScope(spannedScope); + addStreamFunctions(typeSymbol, BasicSymbolsMill.globalScope()); + } + + protected void addStreamFunctions(TypeSymbol streamSymbol, IBasicSymbolsScope enclosingScope) { + addFunctionEmpty(streamSymbol, enclosingScope); + addFunctionAppendFirst(streamSymbol, enclosingScope); + addFunctionConc(streamSymbol, enclosingScope); + addFunctionLen(streamSymbol, enclosingScope); + addFunctionFirst(streamSymbol, enclosingScope); + addFunctionDropFirst(streamSymbol, enclosingScope); + addFunctionNth(streamSymbol, enclosingScope); + addFunctionTake(streamSymbol, enclosingScope); + addFunctionDrop(streamSymbol, enclosingScope); + addFunctionTimesElement(streamSymbol, enclosingScope); + addFunctionTimesStream(streamSymbol, enclosingScope); + addFunctionMap(streamSymbol, enclosingScope); + addFunctionIterate(streamSymbol, enclosingScope); + addFunctionFilter(streamSymbol, enclosingScope); + addFunctionTakeWhile(streamSymbol, enclosingScope); + addFunctionDropWhile(streamSymbol, enclosingScope); + addFunctionRcDups(streamSymbol, enclosingScope); + } + + // Functions + + protected void addFunctionEmpty(TypeSymbol streamType, IBasicSymbolsScope enclosingScope) { + FunctionSymbol function = createFunction("emptyStream", enclosingScope); + function.setType(createStreamTypeExpression(streamType, "T")); + function.getSpannedScope().add(BasicSymbolsMill.typeVarSymbolBuilder().setName("T").build()); + } + + protected void addFunctionAppendFirst(TypeSymbol streamType, IBasicSymbolsScope enclosingScope) { + FunctionSymbol function = createFunction("appendFirst", enclosingScope); + addParameter(function, "first", createTypeVarExpression("T")); + addParameter(function, "tail", createStreamTypeExpression(streamType, "T")); + function.setType(createStreamTypeExpression(streamType, "T")); + function.getSpannedScope().add(BasicSymbolsMill.typeVarSymbolBuilder().setName("T").build()); + } + + protected void addFunctionConc(TypeSymbol streamType, IBasicSymbolsScope enclosingScope) { + // to discuss: conc(streamOfInts, streamOfDoubles) allowed and returning a stream of doubles? + FunctionSymbol function = createFunction("conc", enclosingScope); + addParameter(function, "head", createStreamTypeExpression(streamType, "T")); + addParameter(function, "tail", createStreamTypeExpression(streamType, "T")); + function.setType(createStreamTypeExpression(streamType, "T")); + function.getSpannedScope().add(BasicSymbolsMill.typeVarSymbolBuilder().setName("T").build()); + } + + protected void addFunctionLen(TypeSymbol streamType, IBasicSymbolsScope enclosingScope) { + FunctionSymbol function = createFunction("len", enclosingScope); + // len must not require a type for T, such that "len(<>)" may be called + addParameter(function, "list", SymTypeExpressionFactory.createGenerics( + BasicSymbolsMill.typeSymbolBuilder().setName(STREAM_TYPE_NAME).build() + )); + function.setType(SymTypeExpressionFactory.createPrimitive(BasicSymbolsMill.LONG)); + } + + protected void addFunctionFirst(TypeSymbol streamType, IBasicSymbolsScope enclosingScope) { + FunctionSymbol function = createFunction("first", enclosingScope); + addParameter(function, "list", createStreamTypeExpression(streamType, "T")); + function.setType(createTypeVarExpression("T")); + function.getSpannedScope().add(BasicSymbolsMill.typeVarSymbolBuilder().setName("T").build()); + } + + protected void addFunctionDropFirst(TypeSymbol streamType, IBasicSymbolsScope enclosingScope) { + FunctionSymbol function = createFunction("dropFirst", enclosingScope); + addParameter(function, "list", createStreamTypeExpression(streamType, "T")); + function.setType(createStreamTypeExpression(streamType, "T")); + function.getSpannedScope().add(BasicSymbolsMill.typeVarSymbolBuilder().setName("T").build()); + } + + protected void addFunctionNth(TypeSymbol streamType, IBasicSymbolsScope enclosingScope) { + FunctionSymbol function = createFunction("nth", enclosingScope); + addParameter(function, "list", createStreamTypeExpression(streamType, "T")); + addParameter(function, "n", SymTypeExpressionFactory.createPrimitive(BasicSymbolsMill.LONG)); + function.setType(createTypeVarExpression("T")); + function.getSpannedScope().add(BasicSymbolsMill.typeVarSymbolBuilder().setName("T").build()); + } + + protected void addFunctionTake(TypeSymbol streamType, IBasicSymbolsScope enclosingScope) { + FunctionSymbol function = createFunction("take", enclosingScope); + addParameter(function, "list", createStreamTypeExpression(streamType, "T")); + addParameter(function, "n", SymTypeExpressionFactory.createPrimitive(BasicSymbolsMill.LONG)); + function.setType(createStreamTypeExpression(streamType, "T")); + function.getSpannedScope().add(BasicSymbolsMill.typeVarSymbolBuilder().setName("T").build()); + } + + protected void addFunctionDrop(TypeSymbol streamType, IBasicSymbolsScope enclosingScope) { + FunctionSymbol function = createFunction("drop", enclosingScope); + addParameter(function, "list", createStreamTypeExpression(streamType, "T")); + addParameter(function, "n", SymTypeExpressionFactory.createPrimitive(BasicSymbolsMill.LONG)); + function.setType(createStreamTypeExpression(streamType, "T")); + function.getSpannedScope().add(BasicSymbolsMill.typeVarSymbolBuilder().setName("T").build()); + } + + protected void addFunctionTimesElement(TypeSymbol streamType, IBasicSymbolsScope enclosingScope) { + FunctionSymbol function = createFunction("times", enclosingScope); + addParameter(function, "elem", createTypeVarExpression("T")); + addParameter(function, "n", SymTypeExpressionFactory.createPrimitive(BasicSymbolsMill.LONG)); + function.setType(createStreamTypeExpression(streamType, "T")); + function.getSpannedScope().add(BasicSymbolsMill.typeVarSymbolBuilder().setName("T").build()); + } + + protected void addFunctionTimesStream(TypeSymbol streamType, IBasicSymbolsScope enclosingScope) { + FunctionSymbol function = createFunction("times", enclosingScope); + addParameter(function, "list", createStreamTypeExpression(streamType, "T")); + addParameter(function, "n", SymTypeExpressionFactory.createPrimitive(BasicSymbolsMill.LONG)); + function.setType(createStreamTypeExpression(streamType, "T")); + function.getSpannedScope().add(BasicSymbolsMill.typeVarSymbolBuilder().setName("T").build()); + } + + protected void addFunctionMap(TypeSymbol streamType, IBasicSymbolsScope enclosingScope) { + FunctionSymbol function = createFunction("map", enclosingScope); + addParameter(function, "function", SymTypeExpressionFactory.createFunction( + createTypeVarExpression("U"), + createTypeVarExpression("T") + )); + addParameter(function, "list", createStreamTypeExpression(streamType, "T")); + function.setType(createStreamTypeExpression(streamType, "U")); + function.getSpannedScope().add(BasicSymbolsMill.typeVarSymbolBuilder().setName("T").build()); + function.getSpannedScope().add(BasicSymbolsMill.typeVarSymbolBuilder().setName("U").build()); + } + + protected void addFunctionIterate(TypeSymbol streamType, IBasicSymbolsScope enclosingScope) { + FunctionSymbol function = createFunction("iterate", enclosingScope); + addParameter(function, "function", SymTypeExpressionFactory.createFunction( + createTypeVarExpression("T"), + createTypeVarExpression("T") + )); + addParameter(function, "elem", createStreamTypeExpression(streamType, "T")); + function.setType(createStreamTypeExpression(streamType, "T")); + function.getSpannedScope().add(BasicSymbolsMill.typeVarSymbolBuilder().setName("T").build()); + } + + protected void addFunctionFilter(TypeSymbol streamType, IBasicSymbolsScope enclosingScope) { + FunctionSymbol function = createFunction("filter", enclosingScope); + addParameter(function, "predicate", SymTypeExpressionFactory.createFunction( + createTypeVarExpression("T"), + createTypeVarExpression("Bool") + )); + addParameter(function, "list", createStreamTypeExpression(streamType, "T")); + function.setType(createStreamTypeExpression(streamType, "T")); + function.getSpannedScope().add(BasicSymbolsMill.typeVarSymbolBuilder().setName("T").build()); + } + + protected void addFunctionTakeWhile(TypeSymbol streamType, IBasicSymbolsScope enclosingScope) { + FunctionSymbol function = createFunction("takeWhile", enclosingScope); + addParameter(function, "predicate", SymTypeExpressionFactory.createFunction( + createTypeVarExpression("T"), + createTypeVarExpression("Bool") + )); + addParameter(function, "list", createStreamTypeExpression(streamType, "T")); + function.setType(createStreamTypeExpression(streamType, "T")); + function.getSpannedScope().add(BasicSymbolsMill.typeVarSymbolBuilder().setName("T").build()); + } + + protected void addFunctionDropWhile(TypeSymbol streamType, IBasicSymbolsScope enclosingScope) { + FunctionSymbol function = createFunction("dropWhile", enclosingScope); + addParameter(function, "predicate", SymTypeExpressionFactory.createFunction( + createTypeVarExpression("T"), + createTypeVarExpression("Bool") + )); + addParameter(function, "list", createStreamTypeExpression(streamType, "T")); + function.setType(createStreamTypeExpression(streamType, "T")); + function.getSpannedScope().add(BasicSymbolsMill.typeVarSymbolBuilder().setName("T").build()); + } + + protected void addFunctionRcDups(TypeSymbol streamType, IBasicSymbolsScope enclosingScope) { + FunctionSymbol function = createFunction("rcDups", enclosingScope); + addParameter(function, "list", createStreamTypeExpression(streamType, "T")); + function.setType(createStreamTypeExpression(streamType, "T")); + function.getSpannedScope().add(BasicSymbolsMill.typeVarSymbolBuilder().setName("T").build()); + } + + // Helper + + protected SymTypeExpression createTypeVarExpression(String name) { + return SymTypeExpressionFactory.createTypeVariable( + BasicSymbolsMill.typeVarSymbolBuilder().setName(name).build()); + } + + protected SymTypeExpression createStreamTypeExpression(TypeSymbol streamType, String name) { + return SymTypeExpressionFactory.createGenerics( + streamType, + createTypeVarExpression(name) + ); + } + + protected FunctionSymbol createFunction(String name, IBasicSymbolsScope enclosingScope) { + FunctionSymbol function = BasicSymbolsMill.functionSymbolBuilder() + .setName(name) + .setEnclosingScope(enclosingScope) + .setSpannedScope(BasicSymbolsMill.scope()) + .build(); + enclosingScope.add(function); + return function; + } + + protected void addParameter(FunctionSymbol function, String name, SymTypeExpression type) { + function.getSpannedScope().add( + BasicSymbolsMill.variableSymbolBuilder() + .setName(name) + .setEnclosingScope(function.getSpannedScope()) + .setType(type) + .build() + ); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/symbols/oosymbols/_symboltable/FieldSymbol.java b/monticore-grammar/src/main/java/de/monticore/symbols/oosymbols/_symboltable/FieldSymbol.java new file mode 100644 index 0000000000..0c10aae3c5 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/symbols/oosymbols/_symboltable/FieldSymbol.java @@ -0,0 +1,66 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symbols.oosymbols._symboltable; + +import de.monticore.symboltable.modifiers.*; + +import java.util.ArrayList; +import java.util.List; + +public class FieldSymbol extends FieldSymbolTOP { + + public FieldSymbol(String name){ + super(name); + } + + /** + * returns a clone of this + * this is only required for the type check + * does not create a full deep clone + */ + public FieldSymbol deepClone(){ + FieldSymbol clone = new FieldSymbol(name); + clone.setAccessModifier(this.accessModifier); + clone.setEnclosingScope(this.enclosingScope); + clone.setFullName(this.fullName); + clone.setPackageName(this.packageName); + clone.setIsPrivate(this.isPrivate); + clone.setIsProtected(this.isProtected); + clone.setIsPublic(this.isPublic); + clone.setIsStatic(this.isStatic); + clone.setIsFinal(this.isFinal); + clone.setIsDerived(this.isDerived); + if(isPresentAstNode()) { + clone.setAstNode(this.getAstNode()); + } + if(getType()!=null){ + clone.setType(this.getType().deepClone()); + } + return clone; + } + + @Override + public AccessModifier getAccessModifier() { + List modifiers = new ArrayList<>(); + if(isIsPublic()){ + modifiers.add(BasicAccessModifier.PUBLIC); + }else if(isIsProtected()){ + modifiers.add(BasicAccessModifier.PROTECTED); + }else if(isIsPrivate()){ + modifiers.add(BasicAccessModifier.PRIVATE); + }else{ + modifiers.add(BasicAccessModifier.PACKAGE_LOCAL); + } + + if(isIsStatic()){ + modifiers.add(StaticAccessModifier.STATIC); + }else{ + modifiers.add(StaticAccessModifier.NON_STATIC); + } + if(isIsFinal()){ + modifiers.add(WritableAccessModifier.NON_WRITABLE); + }else{ + modifiers.add(WritableAccessModifier.WRITABLE); + } + return new CompoundAccessModifier(modifiers); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/symbols/oosymbols/_symboltable/FieldSymbolDeSer.java b/monticore-grammar/src/main/java/de/monticore/symbols/oosymbols/_symboltable/FieldSymbolDeSer.java new file mode 100644 index 0000000000..c9ef1db3f4 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/symbols/oosymbols/_symboltable/FieldSymbolDeSer.java @@ -0,0 +1,20 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symbols.oosymbols._symboltable; + +import de.monticore.symboltable.serialization.json.JsonObject; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionDeSer; + +public class FieldSymbolDeSer extends FieldSymbolDeSerTOP { + + @Override + protected void serializeType(SymTypeExpression type, OOSymbolsSymbols2Json s2j) { + SymTypeExpressionDeSer.serializeMember(s2j.getJsonPrinter(), "type", type); + } + + @Override + public SymTypeExpression deserializeType(JsonObject symbolJson) { + return SymTypeExpressionDeSer.deserializeMember("type", symbolJson); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/symbols/oosymbols/_symboltable/IOOSymbolsScope.java b/monticore-grammar/src/main/java/de/monticore/symbols/oosymbols/_symboltable/IOOSymbolsScope.java new file mode 100644 index 0000000000..0a336c1c66 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/symbols/oosymbols/_symboltable/IOOSymbolsScope.java @@ -0,0 +1,128 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symbols.oosymbols._symboltable; + +import de.monticore.symbols.basicsymbols._symboltable.FunctionSymbol; +import de.monticore.symbols.basicsymbols._symboltable.VariableSymbol; +import de.monticore.symboltable.IScopeSpanningSymbol; +import de.monticore.symboltable.modifiers.AccessModifier; +import de.monticore.symboltable.modifiers.StaticAccessModifier; +import de.monticore.types.check.SymTypeExpression; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.function.Predicate; + +public interface IOOSymbolsScope extends IOOSymbolsScopeTOP { + + /** + * override method from ExpressionBasisScope to resolve all methods correctly + * method needed to be overridden because of special cases: if the scope is spanned by a type symbol you have to look for fitting methods in its super types too because of inheritance + * the method resolves the methods like the overridden method and if the spanning symbol is a type symbol it additionally looks for methods in its super types + * it is used by the method getMethodList in SymTypeExpression + */ + @Override + default List resolveMethodLocallyMany(boolean foundSymbols, String name, AccessModifier modifier, + Predicate predicate) { + //resolve methods by using overridden method + List set = IOOSymbolsScopeTOP.super.resolveMethodLocallyMany(foundSymbols,name,modifier,predicate); + if(this.isPresentSpanningSymbol()){ + IScopeSpanningSymbol spanningSymbol = getSpanningSymbol(); + //if the methodsymbol is in the spanned scope of a typesymbol then look for method in super types too + if(spanningSymbol instanceof OOTypeSymbol){ + OOTypeSymbol typeSymbol = (OOTypeSymbol) spanningSymbol; + for(SymTypeExpression t : typeSymbol.getSuperTypesList()){ + t.getMethodList(name, false, modifier).stream(). + filter(m -> m instanceof MethodSymbol).forEach(m -> set.add((MethodSymbol) m)); + } + } + } + return set; + } + + /** + * override method from ExpressionBasisScope to resolve all fields correctly + * method needed to be overridden because of special cases: if the scope is spanned by a type symbol you have to look for fitting fields in its super types too because of inheritance + * the method resolves the fields like the overridden method and if the spanning symbol is a type symbol it additionally looks for fields in its super types + * it is used by the method getFieldList in SymTypeExpression + */ + @Override + default List resolveFieldLocallyMany(boolean foundSymbols, String name, AccessModifier modifier, Predicate predicate){ + //resolve methods by using overridden method + List result = IOOSymbolsScopeTOP.super.resolveFieldLocallyMany(foundSymbols,name,modifier,predicate); + if(this.isPresentSpanningSymbol() && modifier.includes(StaticAccessModifier.NON_STATIC)){ + IScopeSpanningSymbol spanningSymbol = getSpanningSymbol(); + //if the fieldsymbol is in the spanned scope of a typesymbol then look for method in super types too + if(spanningSymbol instanceof OOTypeSymbol){ + OOTypeSymbol typeSymbol = (OOTypeSymbol) spanningSymbol; + for(SymTypeExpression superType : typeSymbol.getSuperTypesList()){ + superType.getFieldList(name, false, modifier).stream(). + filter(f -> f instanceof FieldSymbol).forEach(f -> result.add((FieldSymbol) f)); + } + } + } + return result; + } + + /** + * override method from ExpressionBasisScope to resolve all fields correctly + * method needed to be overridden because of special cases: if the scope is spanned by a type symbol you have to look for fitting fields in its super types too because of inheritance + * the method resolves the fields like the overridden method and if the spanning symbol is a type symbol it additionally looks for fields in its super types + * it is used by the method getFieldList in SymTypeExpression + */ + @Override + default List resolveVariableLocallyMany(boolean foundSymbols, String name, AccessModifier modifier, Predicate predicate){ + final List resolvedSymbols = new ArrayList<>(); + + try { + Optional resolvedSymbol = filterVariable(name, getVariableSymbols()); + if (resolvedSymbol.isPresent()) { + resolvedSymbols.add(resolvedSymbol.get()); + } + } catch (de.monticore.symboltable.resolving.ResolvedSeveralEntriesForSymbolException e) { + resolvedSymbols.addAll(e.getSymbols()); + } + + // add all symbols of sub kinds of the current kind + resolvedSymbols.addAll(resolveVariableSubKinds(foundSymbols, name, modifier, predicate)); + + // filter out symbols that are not included within the access modifier + List filteredSymbols = filterSymbolsByAccessModifier(modifier, resolvedSymbols); + filteredSymbols = new ArrayList<>(filteredSymbols.stream().filter(predicate).collect(java.util.stream.Collectors.toList())); + + //try to find adapted one + filteredSymbols.addAll(resolveAdaptedVariableLocallyMany(foundSymbols, name, modifier, predicate)); + filteredSymbols = filterSymbolsByAccessModifier(modifier, filteredSymbols); + filteredSymbols = new ArrayList<>(filteredSymbols.stream().filter(predicate).collect(java.util.stream.Collectors.toList())); + + return filteredSymbols; + } + + @Override + default List resolveFunctionLocallyMany(boolean foundSymbols, String name, AccessModifier modifier, Predicate predicate) { + final List resolvedSymbols = new ArrayList<>(); + + try { + Optional resolvedSymbol = filterFunction(name, getFunctionSymbols()); + if (resolvedSymbol.isPresent()) { + resolvedSymbols.add(resolvedSymbol.get()); + } + } catch (de.monticore.symboltable.resolving.ResolvedSeveralEntriesForSymbolException e) { + resolvedSymbols.addAll(e.getSymbols()); + } + + // add all symbols of sub kinds of the current kind + resolvedSymbols.addAll(resolveFunctionSubKinds(foundSymbols, name, modifier, predicate)); + + // filter out symbols that are not included within the access modifier + List filteredSymbols = filterSymbolsByAccessModifier(modifier, resolvedSymbols); + filteredSymbols = new ArrayList<>(filteredSymbols.stream().filter(predicate).collect(java.util.stream.Collectors.toList())); + + //try to find adapted one + filteredSymbols.addAll(resolveAdaptedFunctionLocallyMany(foundSymbols, name, modifier, predicate)); + filteredSymbols = filterSymbolsByAccessModifier(modifier, filteredSymbols); + filteredSymbols = new ArrayList<>(filteredSymbols.stream().filter(predicate).collect(java.util.stream.Collectors.toList())); + + return filteredSymbols; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/symbols/oosymbols/_symboltable/MethodSymbol.java b/monticore-grammar/src/main/java/de/monticore/symbols/oosymbols/_symboltable/MethodSymbol.java new file mode 100644 index 0000000000..213c466585 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/symbols/oosymbols/_symboltable/MethodSymbol.java @@ -0,0 +1,69 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symbols.oosymbols._symboltable; + +import de.monticore.symbols.basicsymbols._symboltable.VariableSymbol; +import de.monticore.symboltable.modifiers.*; + +import java.util.ArrayList; +import java.util.List; + +public class MethodSymbol extends MethodSymbolTOP { + + public MethodSymbol(String name) { + super(name); + } + + /** + * returns a clone of this + */ + public MethodSymbol deepClone() { + MethodSymbol clone = new MethodSymbol(name); + clone.setType(this.type.deepClone()); + clone.setEnclosingScope(this.enclosingScope); + clone.setFullName(this.fullName); + clone.setIsConstructor(this.isConstructor); + clone.setIsMethod(this.isMethod); + clone.setIsElliptic(this.isElliptic); + clone.setIsStatic(this.isStatic); + clone.setIsFinal(this.isFinal); + clone.setIsPrivate(this.isPrivate); + clone.setIsProtected(this.isProtected); + clone.setIsPublic(this.isPublic); + if (isPresentAstNode()) { + clone.setAstNode(this.getAstNode()); + } + clone.setAccessModifier(this.accessModifier); + if (spannedScope != null) { + clone.setSpannedScope(this.spannedScope); + } + return clone; + } + + @Override + public List getParameterList() { + List params = super.getParameterList(); + params.addAll(getSpannedScope().getLocalFieldSymbols()); + return params; + } + + @Override + public AccessModifier getAccessModifier() { + List modifiers = new ArrayList<>(); + if(isIsPublic()){ + modifiers.add(BasicAccessModifier.PUBLIC); + }else if(isIsProtected()){ + modifiers.add(BasicAccessModifier.PROTECTED); + }else if(isIsPrivate()){ + modifiers.add(BasicAccessModifier.PRIVATE); + }else{ + modifiers.add(BasicAccessModifier.PACKAGE_LOCAL); + } + + if(isIsStatic()){ + modifiers.add(StaticAccessModifier.STATIC); + }else{ + modifiers.add(StaticAccessModifier.NON_STATIC); + } + return new CompoundAccessModifier(modifiers); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/symbols/oosymbols/_symboltable/MethodSymbolDeSer.java b/monticore-grammar/src/main/java/de/monticore/symbols/oosymbols/_symboltable/MethodSymbolDeSer.java new file mode 100644 index 0000000000..e4914307f8 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/symbols/oosymbols/_symboltable/MethodSymbolDeSer.java @@ -0,0 +1,20 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symbols.oosymbols._symboltable; + +import de.monticore.symboltable.serialization.json.JsonObject; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionDeSer; + +public class MethodSymbolDeSer extends MethodSymbolDeSerTOP { + + @Override + protected void serializeType(SymTypeExpression type, OOSymbolsSymbols2Json s2j) { + SymTypeExpressionDeSer.serializeMember(s2j.getJsonPrinter(), "type", type); + } + + @Override + public SymTypeExpression deserializeType(JsonObject symbolJson) { + return SymTypeExpressionDeSer.deserializeMember("type", symbolJson); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/symbols/oosymbols/_symboltable/OOTypeSymbol.java b/monticore-grammar/src/main/java/de/monticore/symbols/oosymbols/_symboltable/OOTypeSymbol.java new file mode 100644 index 0000000000..fbe573d0ed --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/symbols/oosymbols/_symboltable/OOTypeSymbol.java @@ -0,0 +1,139 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symbols.oosymbols._symboltable; + +import com.google.common.collect.Lists; +import de.monticore.symbols.basicsymbols._symboltable.FunctionSymbol; +import de.monticore.symbols.basicsymbols._symboltable.TypeVarSymbol; +import de.monticore.symbols.basicsymbols._symboltable.VariableSymbol; +import de.monticore.symboltable.modifiers.*; +import de.monticore.types.check.SymTypeExpression; +import de.se_rwth.commons.logging.Log; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class OOTypeSymbol extends OOTypeSymbolTOP { + + public OOTypeSymbol(String name) { + super(name); + } + + public void setMethodList(List methodList){ + for(MethodSymbol method: methodList){ + getSpannedScope().add(method); + getSpannedScope().add((FunctionSymbol) method); + } + } + + /** + * get a list of all the methods the type definition can access + */ + public List getMethodList() { + if (spannedScope == null) { + return Lists.newArrayList(); + } + return getSpannedScope().getLocalMethodSymbols(); + } + + /** + * search in the scope for methods with a specific name + */ + public List getMethodList(String methodname) { + return getSpannedScope().resolveMethodMany(methodname); + } + + /** + * get a list of all the fields the type definition can access + */ + public List getFieldList() { + if (spannedScope == null) { + return Lists.newArrayList(); + } + return getSpannedScope().getLocalFieldSymbols(); + } + + /** + * search in the scope for methods with a specific name + */ + public List getFieldList(String fieldname) { + return getSpannedScope().resolveFieldMany(fieldname); + } + + public List getTypeParameterList() { + if(spannedScope==null){ + return Lists.newArrayList(); + } + return getSpannedScope().getLocalTypeVarSymbols(); + } + + + public void addTypeVarSymbol(TypeVarSymbol t) { + getSpannedScope().add(t); + } + + public void addFieldSymbol(FieldSymbol f) { + getSpannedScope().add(f); + getSpannedScope().add((VariableSymbol) f); + } + + public void addMethodSymbol(MethodSymbol m) { + getSpannedScope().add(m); + getSpannedScope().add((FunctionSymbol) m); + } + + public boolean isPresentSuperClass() { + return !getSuperClassesOnly().isEmpty(); + } + + public SymTypeExpression getSuperClass() { + if (isPresentSuperClass()) { + return getSuperClassesOnly().get(0); + } + Log.error("0xA1067 SuperClass does not exist"); + // Normally this statement is not reachable + throw new IllegalStateException(); + } + + @Override + public List getSuperClassesOnly() { + List normalSuperTypes = super.getSuperClassesOnly().stream() + .filter(type -> !(type.getTypeInfo() instanceof OOTypeSymbol)).collect(Collectors.toList()); + List oOSuperTypes = superTypes.stream() + .filter(type -> type.getTypeInfo() instanceof OOTypeSymbol) + .filter(type -> ((OOTypeSymbol) type.getTypeInfo()).isIsClass()) + .collect(Collectors.toList()); + normalSuperTypes.addAll(oOSuperTypes); + return normalSuperTypes; + } + + @Override + public List getInterfaceList() { + return superTypes.stream() + .filter(type -> type.getTypeInfo() instanceof OOTypeSymbol) + .filter(type -> ((OOTypeSymbol) type.getTypeInfo()).isIsInterface()) + .collect(Collectors.toList()); + } + + @Override + public AccessModifier getAccessModifier() { + List modifiers = new ArrayList<>(); + if(isIsPublic()){ + modifiers.add(BasicAccessModifier.PUBLIC); + }else if(isIsProtected()){ + modifiers.add(BasicAccessModifier.PROTECTED); + }else if(isIsPrivate()){ + modifiers.add(BasicAccessModifier.PRIVATE); + }else{ + modifiers.add(BasicAccessModifier.PACKAGE_LOCAL); + } + + if(isIsStatic()){ + modifiers.add(StaticAccessModifier.STATIC); + }else{ + modifiers.add(StaticAccessModifier.NON_STATIC); + } + + return new CompoundAccessModifier(modifiers); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/symbols/oosymbols/_symboltable/OOTypeSymbolDeSer.java b/monticore-grammar/src/main/java/de/monticore/symbols/oosymbols/_symboltable/OOTypeSymbolDeSer.java new file mode 100644 index 0000000000..b027fc90a5 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/symbols/oosymbols/_symboltable/OOTypeSymbolDeSer.java @@ -0,0 +1,23 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symbols.oosymbols._symboltable; + +import de.monticore.symboltable.serialization.json.JsonObject; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionDeSer; + +import java.util.List; + +public class OOTypeSymbolDeSer extends OOTypeSymbolDeSerTOP { + + @Override + protected void serializeSuperTypes(List superTypes, + OOSymbolsSymbols2Json s2j) { + SymTypeExpressionDeSer.serializeMember(s2j.getJsonPrinter(), "superTypes", superTypes); + } + + @Override + public List deserializeSuperTypes(JsonObject symbolJson) { + return SymTypeExpressionDeSer.deserializeListMember("superTypes", symbolJson); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/symbols/oosymbols/_symboltable/OOTypeSymbolSurrogate.java b/monticore-grammar/src/main/java/de/monticore/symbols/oosymbols/_symboltable/OOTypeSymbolSurrogate.java new file mode 100644 index 0000000000..b39ad8cc5e --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/symbols/oosymbols/_symboltable/OOTypeSymbolSurrogate.java @@ -0,0 +1,31 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symbols.oosymbols._symboltable; + +import de.monticore.types.check.SymTypeExpression; +import de.se_rwth.commons.logging.Log; + +import java.util.List; + +public class OOTypeSymbolSurrogate extends OOTypeSymbolSurrogateTOP { + + public OOTypeSymbolSurrogate(String name){ + super(name); + } + + public IOOSymbolsScope getSpannedScope(){ + if (!checkLazyLoadDelegate()) { + if (spannedScope instanceof IOOSymbolsScope) { + return (IOOSymbolsScope) spannedScope; + } else { + Log.error("0xA7019 Could not cast the spanned scope to an IOOSymbolsScope."); + } + throw new IllegalStateException(); + } + return lazyLoadDelegate().getSpannedScope(); + } + + public List getSuperClassesOnly(){ + return lazyLoadDelegate().getSuperClassesOnly(); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/tf/grammartransformation/CollectCoCoInformationState.java b/monticore-grammar/src/main/java/de/monticore/tf/grammartransformation/CollectCoCoInformationState.java new file mode 100644 index 0000000000..fcbd859be0 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/tf/grammartransformation/CollectCoCoInformationState.java @@ -0,0 +1,75 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.grammartransformation; + +import com.google.common.collect.Sets; + +import java.util.HashSet; +import java.util.Set; + + +public class CollectCoCoInformationState { + + private int parentNest = 0; + private boolean rhs = false; + private Set varsOnLHS = new HashSet(); + private Set varsOnRHS = new HashSet(); + private int repElements = 0; + private int negElements = 0; + + public Set getRHSOnlyVars() { + Set result = Sets.newHashSet(varsOnRHS); + result.removeAll(varsOnLHS); + return result; + } + + public int getParentNest() { + return parentNest; + } + + public void incrementParentNest() { + this.parentNest += 1; + } + public void decrementParentNest() { + this.parentNest -= 1; + } + + public Set getVarsOnLHS() { + return varsOnLHS; + } + + public void addVarOnLHS(String var) { + varsOnLHS.add(var); + } + + public Set getVarsOnRHS() { + return varsOnRHS; + } + + public void addVarOnRHS(String var) { + varsOnRHS.add(var); + } + + public boolean getRHS() { + return rhs; + } + + public void setRHS(boolean rhs) { + this.rhs = rhs; + } + + public void incrementRepElements() { + repElements += 1; + } + + public int getRepElements() { + return repElements; + } + + public void incrementNegElements() { + negElements += 1; + } + + public int getNegElements() { + return negElements; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/tf/odrulegeneration/_ast/ASTDeleteOperation.java b/monticore-grammar/src/main/java/de/monticore/tf/odrulegeneration/_ast/ASTDeleteOperation.java new file mode 100644 index 0000000000..969ea23e89 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/tf/odrulegeneration/_ast/ASTDeleteOperation.java @@ -0,0 +1,63 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.odrulegeneration._ast; + +import java.util.HashMap; +import java.util.List; + +public class ASTDeleteOperation extends ASTDeleteOperationTOP { + + protected HashMap> possibleParents; + + protected ASTDeleteOperation() { + super(); + } + + protected ASTDeleteOperation( + String name, + String type, + String simpleType, + String grammarType, + String typepackage, + List parentsSets, + boolean list) { + super(); + setName(name); + setType(type); + setSimpleType(simpleType); + setGrammarType(grammarType); + setTypepackage(typepackage); + setParentsSetList(parentsSets); + setList(list); + //super(name, type, simpleType, grammarType, typepackage, parentsSets, list); + } + + protected ASTDeleteOperation(HashMap> possibleParents, + String name, + String type, + String simpleType, + String grammarType, + String typepackage, + List parentsSets, + boolean list) { + super(); + setName(name); + setType(type); + setSimpleType(simpleType); + setGrammarType(grammarType); + setTypepackage(typepackage); + setParentsSetList(parentsSets); + setList(list); + //super(name, type, simpleType, grammarType, typepackage, parentsSets, list); + setPossibleParents(possibleParents); + } + + + public HashMap> getPossibleParents () { + return this.possibleParents; + } + + public void setPossibleParents (HashMap> possibleParents) { + this.possibleParents = possibleParents; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/tf/odrulegeneration/_ast/ASTDeleteOperationBuilder.java b/monticore-grammar/src/main/java/de/monticore/tf/odrulegeneration/_ast/ASTDeleteOperationBuilder.java new file mode 100644 index 0000000000..09b56a34a6 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/tf/odrulegeneration/_ast/ASTDeleteOperationBuilder.java @@ -0,0 +1,19 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.odrulegeneration._ast; + +import java.util.HashMap; +import java.util.List; + +public class ASTDeleteOperationBuilder extends ASTDeleteOperationBuilderTOP { + + protected HashMap> possibleParents = new HashMap<>(); + + public ASTDeleteOperationBuilder setPossibleParents (HashMap> foldingHash) { + this.possibleParents = foldingHash; + return this.realBuilder; + } + + public HashMap> getPossibleParents() { + return this.possibleParents; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/tf/odrulegeneration/_ast/ASTTransformationStructure.java b/monticore-grammar/src/main/java/de/monticore/tf/odrulegeneration/_ast/ASTTransformationStructure.java new file mode 100644 index 0000000000..a5aab20a36 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/tf/odrulegeneration/_ast/ASTTransformationStructure.java @@ -0,0 +1,74 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.odrulegeneration._ast; + +import java.util.HashMap; +import java.util.List; + +public class ASTTransformationStructure extends ASTTransformationStructureTOP { + + protected HashMap> foldingHash = new HashMap<>(); + + protected ASTTransformationStructure() { + super(); + } + + protected ASTTransformationStructure( + String r__package, + List importss, + String classname, + ASTPattern pattern, + ASTReplacement replacement, + String constraintExpression, + String doStatement, + String undoStatement, + List assignmentss, + List variables) { + super(); + setPackage(r__package); + setImportsList(importss); + setClassname(classname); + setPattern(pattern); + setReplacement(replacement); + setConstraintExpression(constraintExpression); + setDoStatement(doStatement); + setUndoStatement(undoStatement); + setAssignmentsList(assignmentss); + setVariableList(variables); + //super(r__package, importss, classname, pattern, replacement, constraintExpression, doStatement, assignmentss, variables); + } + + protected ASTTransformationStructure(HashMap> foldingHash, + String r__package, + List importss, + String classname, + ASTPattern pattern, + ASTReplacement replacement, + String constraintExpression, + String doStatement, + String undoStatement, + List assignmentss, + List variables) { + super(); + setPackage(r__package); + setImportsList(importss); + setClassname(classname); + setPattern(pattern); + setReplacement(replacement); + setConstraintExpression(constraintExpression); + setDoStatement(doStatement); + setUndoStatement(doStatement); + setAssignmentsList(assignmentss); + setVariableList(variables); + //super(r__package, importss, classname, pattern, replacement, constraintExpression, doStatement, assignmentss, variables); + setFoldingHash(foldingHash); + } + + public HashMap> getFoldingHash () { + return this.foldingHash; + } + + public void setFoldingHash (HashMap> foldingHash) { + this.foldingHash = foldingHash; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/tf/odrulegeneration/_ast/ASTTransformationStructureBuilder.java b/monticore-grammar/src/main/java/de/monticore/tf/odrulegeneration/_ast/ASTTransformationStructureBuilder.java new file mode 100644 index 0000000000..910ae1a68a --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/tf/odrulegeneration/_ast/ASTTransformationStructureBuilder.java @@ -0,0 +1,78 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.odrulegeneration._ast; + +import de.se_rwth.commons.logging.Log; + +import java.util.HashMap; +import java.util.List; + +public class ASTTransformationStructureBuilder extends ASTTransformationStructureBuilderTOP { + + protected HashMap> foldingHash = new HashMap<>(); + + public ASTTransformationStructureBuilder setFoldingHash (HashMap> foldingHash) { + this.foldingHash = foldingHash; + return this.realBuilder; + } + + public HashMap> getFoldingHash() { + return this.foldingHash; + } + + @Override + public ASTTransformationStructure build () { + + if (!isValid()) { + if (r__package == null) { + Log.error("0xA7230 r__package of type String must not be null"); + } + if (classname == null) { + Log.error("0xA7231 classname of type String must not be null"); + } + if (pattern == null) { + Log.error("0xA7232 pattern of type de.monticore.tf.odrulegeneration._ast.ASTPattern must not be null"); + } + if (replacement == null) { + Log.error("0xA7233 replacement of type de.monticore.tf.odrulegeneration._ast.ASTReplacement must not be null"); + } + if (constraintExpression == null) { + Log.error("0xA7234 constraintExpression of type String must not be null"); + } + if (doStatement == null) { + Log.error("0xA7235 doStatement of type String must not be null"); + } + if (undoStatement == null) { + Log.error("0xA7222 undoStatement of type String must not be null"); + } + throw new IllegalStateException(); + } + ASTTransformationStructure value; + + value = new ASTTransformationStructure(); + value.setPackage(this.r__package); + value.setImportsList(this.imports); + value.setClassname(this.classname); + value.setPattern(this.pattern); + value.setReplacement(this.replacement); + value.setConstraintExpression(this.constraintExpression); + value.setDoStatement(this.doStatement); + value.setUndoStatement(this.undoStatement); + value.setAssignmentsList(this.assignments); + value.setVariableList(this.variables); + if (this.sourcePositionEnd.isPresent()) { + value.set_SourcePositionEnd(this.sourcePositionEnd.get()); + } else { + value.set_SourcePositionEndAbsent(); + } + if (this.sourcePositionStart.isPresent()) { + value.set_SourcePositionStart(this.sourcePositionStart.get()); + } else { + value.set_SourcePositionStartAbsent(); + } + value.set_PreCommentList(this.precomments); + value.set_PostCommentList(this.postcomments); + value.setFoldingHash(this.foldingHash); + + return value; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/tf/odrules/AddAffixesToAssignmentsVisitor.java b/monticore-grammar/src/main/java/de/monticore/tf/odrules/AddAffixesToAssignmentsVisitor.java new file mode 100644 index 0000000000..4ad5095df2 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/tf/odrules/AddAffixesToAssignmentsVisitor.java @@ -0,0 +1,149 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.odrules; + +import de.monticore.expressions.commonexpressions.CommonExpressionsMill; +import de.monticore.expressions.commonexpressions._ast.*; +import de.monticore.expressions.commonexpressions._visitor.CommonExpressionsVisitor2; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.expressionsbasis._ast.ASTNameExpression; +import de.monticore.expressions.expressionsbasis._visitor.ExpressionsBasisVisitor2; +import de.monticore.tf.odrulegeneration._ast.ASTMatchingObject; + +import java.util.List; + +/** + * Created by Alexander Wilts on 16.01.2017. + *

+ * This visitor adds prefix "m." to normal nodes in assignments. + */ +public class AddAffixesToAssignmentsVisitor implements + ExpressionsBasisVisitor2, CommonExpressionsVisitor2 { + + protected List lhsObjects; + protected HierarchyHelper hierarchyHelper; + public ASTExpression rootExp; + + public AddAffixesToAssignmentsVisitor(List lhsObjects, HierarchyHelper hierarchyHelper, ASTExpression rootExp) { + super(); + this.hierarchyHelper = hierarchyHelper; + this.lhsObjects = lhsObjects; + this.rootExp = rootExp; + } + + @Override + public void visit(ASTFieldAccessExpression node) { + if (node.getExpression() instanceof ASTNameExpression) { + node.setExpression(replaceNode((ASTNameExpression) node.getExpression())); + } + } + + @Override + public void visit(ASTBooleanNotExpression node) { + if (node.getExpression() instanceof ASTNameExpression) { + node.setExpression(replaceNode((ASTNameExpression) node.getExpression())); + } + } + + @Override + public void visit(ASTLogicalNotExpression node) { + if (node.getExpression() instanceof ASTNameExpression) { + node.setExpression(replaceNode((ASTNameExpression) node.getExpression())); + } + } + + @Override + public void visit(ASTEqualsExpression node) { + if (node.getLeft() instanceof ASTNameExpression) { + node.setLeft(replaceNode((ASTNameExpression) node.getLeft())); + } + if (node.getRight() instanceof ASTNameExpression) { + node.setRight(replaceNode((ASTNameExpression) node.getRight())); + } + } + + @Override + public void visit(ASTBooleanAndOpExpression node) { + if (node.getLeft() instanceof ASTNameExpression) { + node.setLeft(replaceNode((ASTNameExpression) node.getLeft())); + } + if (node.getRight() instanceof ASTNameExpression) { + node.setRight(replaceNode((ASTNameExpression) node.getRight())); + } + } + + @Override + public void visit(ASTBooleanOrOpExpression node) { + if (node.getLeft() instanceof ASTNameExpression) { + node.setLeft(replaceNode((ASTNameExpression) node.getLeft())); + } + if (node.getRight() instanceof ASTNameExpression) { + node.setRight(replaceNode((ASTNameExpression) node.getRight())); + } + } + + @Override + public void visit(ASTPlusExpression node) { + if (node.getLeft() instanceof ASTNameExpression) { + node.setLeft(replaceNode((ASTNameExpression) node.getLeft())); + } + if (node.getRight() instanceof ASTNameExpression) { + node.setRight(replaceNode((ASTNameExpression) node.getRight())); + } + } + + @Override + public void visit(ASTBracketExpression node) { + if (node.getExpression() instanceof ASTNameExpression) { + node.setExpression(replaceNode((ASTNameExpression) node.getExpression())); + } + } + + @Override + public void visit(ASTNameExpression node) { + if(node.equals(rootExp)) + //if(!node.getName().equals("m")) + rootExp = replaceNode(node); + } + + /** + * Prefix "m." is added for nodes. + * Suffix .get() for optional variables + */ + private ASTExpression replaceNode(ASTNameExpression node) { + for (ASTMatchingObject o : lhsObjects) { + if (node.getName().equals(o.getObjectName())) { + + ASTNameExpression nameExpr = CommonExpressionsMill.nameExpressionBuilder() + .setName("m") + .build(); + + ASTFieldAccessExpression resultExpr = CommonExpressionsMill.fieldAccessExpressionBuilder() + .setExpression(nameExpr) + .setName(node.getName()) + .build(); + + if(hierarchyHelper.isWithinOptionalStructure(o.getObjectName()) + || hierarchyHelper.isWithinNegativeStructure(o.getObjectName())) { + + ASTFieldAccessExpression getExpr = CommonExpressionsMill.fieldAccessExpressionBuilder() + .setExpression(resultExpr) + .setName("get") + .build(); + + ASTCallExpression callExpr = CommonExpressionsMill.callExpressionBuilder() + .setExpression(getExpr) + .setArguments(CommonExpressionsMill.argumentsBuilder().uncheckedBuild()) + .build(); + + return callExpr; + } + + return resultExpr; + } + } + + return node; + } + + +} diff --git a/monticore-grammar/src/main/java/de/monticore/tf/odrules/ChangeOperationFactory.java b/monticore-grammar/src/main/java/de/monticore/tf/odrules/ChangeOperationFactory.java new file mode 100644 index 0000000000..37b111cb39 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/tf/odrules/ChangeOperationFactory.java @@ -0,0 +1,626 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.odrules; + +import com.google.common.base.Splitter; +import com.google.common.collect.Lists; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.statements.mcarraystatements._ast.ASTArrayInit; +import de.monticore.statements.mcvardeclarationstatements._ast.ASTVariableInit; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.se_rwth.commons.Names; +import de.se_rwth.commons.Splitters; +import de.se_rwth.commons.StringTransformations; +import de.monticore.tf.odrulegeneration.ODRuleGenerationMill; +import de.monticore.tf.odrulegeneration._ast.*; +import de.monticore.tf.odrules._ast.*; +import de.monticore.tf.odrules.util.ODRuleStereotypes; +import de.monticore.tf.odrules.util.TFExpressionFullPrettyPrinter; +import de.monticore.tf.odrules.util.Util; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + + +public class ChangeOperationFactory { + + private ASTODRule rule; + private ASTODDefinition lhs; + private ASTODDefinition rhs; + private HierarchyHelper hierarchyHelper; + + /** + * @param from + * @param to + * @return returns a ChangeOperation, that contains all the steps, to convert + * one ASTODObject to another. + */ + + public ASTChangeOperation createChangeOperation(ASTODObject from, ASTODObject to) { + // this method does all the steps necessary to convert one Object to + // another + ASTChangeOperationBuilder builder = ODRuleGenerationMill.changeOperationBuilder(); + List setAttributeOperations = new ArrayList(); + + for (ASTODAttribute right : to.getAttributesList()) { + // search for matching types on the left + ASTODAttribute found = null; + for (ASTODAttribute left : from.getAttributesList()) { + if (left.getName().equals(right.getName())) { + found = left; + // if the data type mismatches, an exception is thrown, this + // may not happen + String leftType = left.getMCType().toString(); + String rightType = right.getMCType().toString(); + if (leftType.indexOf('@') >= 0) { + leftType = leftType.split("@")[0]; + } + if (rightType.indexOf('@') >= 0) { + rightType = rightType.split("@")[0]; + } + + if (!leftType.equals(rightType)){ + throw new IllegalArgumentException(leftType + " does not match " + rightType); + } + break; + } + } + if (found != null) { + // Compare the found attribute with the attribute on the right + // side + String l = getValueStringFromAttribute(from, found); + String r = getValueStringFromAttribute(to, right); + + if (!l.equals(r)){ + setAttributeOperations.add(createSetAttribute(to, right)); + } + } else { + // attribute is only on the right side, so set this attribute + setAttributeOperations.add(createSetAttribute(to, right)); + } + } + + builder.setSetAttributeOperationsList(setAttributeOperations); + return builder.build(); + } + + public ASTChangeOperation createDeleteOperation(ASTODObject obj) { + String variableName = obj.getName(); + ASTMCType type = obj.getType(); + + ASTChangeOperationBuilder builder = ODRuleGenerationMill.changeOperationBuilder(); + + List deleteOperations = new ArrayList(); + deleteOperations.add(createDelete(type, variableName, obj.hasStereotype(ODRuleStereotypes.LIST))); + + builder.setDeleteOperationsList(deleteOperations); + return builder.build(); + } + + public ASTChangeOperation createCreateOperation(ASTODObject obj) { + ASTMCType type = obj.getType(); + String objectName = obj.getName(); + + ASTChangeOperationBuilder builder = ODRuleGenerationMill.changeOperationBuilder(); + + List createOperations = new ArrayList<>(); + List setOperations = new ArrayList<>(); + createOperations.add(createCreate(type, objectName, !obj.getStereotypeValue("noBuilder").isEmpty())); + + List attributes = obj.getAttributesList(); + for (ASTODAttribute att : attributes) { + setOperations.add(createSetAttribute( obj, att)); + } + + builder.setSetAttributeOperationsList(setOperations); + builder.setCreateOperationsList(createOperations); + return builder.build(); + } + + public ASTChangeOperation createCreateOperation(ASTODLink link) { + ASTChangeOperationBuilder builder = ODRuleGenerationMill.changeOperationBuilder(); + + List setOperations = new ArrayList(); + setOperations.add(createLinkCreation(link)); + + builder.setSetAttributeOperationsList(setOperations); + return builder.build(); + } + + public ASTChangeOperation createCreateOperation(ASTODObject obj, List links) { + ASTChangeOperation changeOperation = createCreateOperation(obj); + + for(ASTODLink link : links) { + changeOperation.addSetAttributeOperations(createLinkCreation(link)); + } + + return changeOperation; + } + + public ASTChangeOperation createDeleteOperation(ASTODLink link) { + ASTChangeOperationBuilder builder = ODRuleGenerationMill.changeOperationBuilder(); + + List setOperations = new ArrayList(); + setOperations.add(createLinkDeletion(link)); + + builder.setSetAttributeOperationsList(setOperations); + return builder.build(); + } + + public ChangeOperationFactory(ASTODRule rule, ASTODDefinition lhs, ASTODDefinition rhs, HierarchyHelper hierarchyHelper) { + this.rule = rule; + this.lhs = lhs; + this.rhs = rhs; + this.hierarchyHelper = hierarchyHelper; + } + + protected String getValueStringFromAttribute(ASTODObject object, ASTODAttribute attribute) { + if (attribute.isPresentList() ) { + ASTArrayInit listValue = attribute.getList(); + StringBuilder valuesList = new StringBuilder(); + Iterator i = listValue.getVariableInitList().iterator(); + while (i.hasNext()) { + ASTVariableInit next = i.next(); + TFExpressionFullPrettyPrinter printer = new TFExpressionFullPrettyPrinter(new IndentPrinter()); + String n = printer.prettyprint(next); + if(n.contains(".")) { + List parts = Lists.newArrayList(Splitter.on(".").split(n)); + if(hierarchyHelper.isWithinOptionalStructure(object.getName())) { + valuesList.append("m." + parts.get(0) + ".isPresent()?"); + valuesList.append("m." + parts.get(0) + ".get()"); + valuesList.append(".get").append(StringTransformations.capitalize(parts.get(1))).append("()"); + if(parts.size()==3){ + valuesList.append(".").append(parts.get(2)); + } + valuesList.append(":\"undef\""); + } else { + valuesList.append("m." + parts.get(0)); + valuesList.append(".get").append(StringTransformations.capitalize(parts.get(1))).append("()"); + if(parts.size()==3){ + valuesList.append(".").append(parts.get(2)); + } + } + }else if(n.startsWith("\"$") && n.endsWith("\"")){ + valuesList.append(n.substring(1,n.length()-1)); + } else { + valuesList.append(n); + } + if (i.hasNext()) { + valuesList.append(", "); + } + } + + return "Lists.newArrayList(" + valuesList.toString() + ")"; + } else { + TFExpressionFullPrettyPrinter printer = new TFExpressionFullPrettyPrinter(new IndentPrinter()); + return printer.prettyprint(attribute.getSingleValue()); + } + } + + + private ASTChange createSetAttribute(ASTODObject object, ASTODAttribute attribute) { + ASTChangeBuilder builder = ODRuleGenerationMill.changeBuilder(); + + + + // composite + + builder.setObjectName(object.getName()); + builder.setAttributeName(attribute.getName()); + + builder.setObjectType(object.printType()); + String type = attribute.printType(); + builder.setType(type); + builder.setGenericType(type); + builder.setSimpleType(Names.getSimpleName(type)); + if (type.equals("int")) { + builder.setBoxingType("Integer"); + } + else if(type.equals("boolean")) { + builder.setBoxingType("Boolean"); + } + + String attributeName = attribute.getName(); + + boolean isAttributeOptional = attribute.isOptional(); + builder.setAttributeOptional(isAttributeOptional); + boolean isAttributeIterated = attribute.isIterated(); + // copy + builder.setObjectInList(object.hasStereotype(ODRuleStereotypes.LIST)); + // valueListObject + builder.setValueType(type); + + if (isAttributeIterated) { + builder.setSetter("get" + StringTransformations.capitalize(attributeName) + "List().addAll"); + builder.setUnsetter("get" + StringTransformations.capitalize(attributeName) + "List().remove"); + } else { + builder.setSetter("set" + StringTransformations.capitalize(attributeName)); + builder.setUnsetter("set" + StringTransformations.capitalize(attributeName)); + } + + if (type.equals("boolean")) { + builder.setGetter("is" + StringTransformations.capitalize(attributeName)); + } else if(isAttributeIterated) { + builder.setGetter("get" + StringTransformations.capitalize(attributeName) + "List"); + if(isAttributeOptional) { + builder.setGetIsPresent("isPresent" + Util.makeSingular(attributeName) + "List()"); + } + } else { + builder.setGetter("get" + StringTransformations.capitalize(attributeName)); + if(isAttributeOptional) { + builder.setGetIsPresent("isPresent" + StringTransformations.capitalize(attributeName) + "()"); + } + } + + builder.setValueStringList(attribute.isPresentList()); + builder.setPrimitiveType(Util.isBuiltInType(attribute)); + + builder.setValue(getValueStringFromAttribute(object, attribute)); + // oldValue + + boolean objectWithinOpt = hierarchyHelper.isWithinOptionalStructure(object.getName()); + boolean objectWithinList = hierarchyHelper.isWithinListStructure(object.getName()); + builder.setObjectWithinOpt(objectWithinOpt); + builder.setObjectWithinList(objectWithinList); + String objectGetter = ""; + if(objectWithinOpt && objectWithinList) { + objectGetter = "get_" + object.getName() + "().get()"; + } else if(objectWithinOpt) { + objectGetter = "m." + object.getName() + ".get()"; + } else if(objectWithinList) { + objectGetter = "get_" + object.getName() + "()"; + } else { + objectGetter = "m." + object.getName(); + } + builder.setObjectGetter(objectGetter); + + if(!builder.isPrimitiveType() && !builder.isValueStringList() && builder.isPresentValue()) { + boolean valueWithinOpt = hierarchyHelper.isWithinOptionalStructure(builder.getValue()); + boolean valueWithinList = hierarchyHelper.isWithinListStructure(builder.getValue()); + builder.setValueWithinOpt(valueWithinOpt); + builder.setValueWithinList(valueWithinList); + String valueGetter; + if (valueWithinOpt && valueWithinList) { + valueGetter = "get_" + builder.getValue() + "().get()"; + } else if (valueWithinOpt) { + valueGetter = "m." + builder.getValue() + ".get()"; + } else if (valueWithinList) { + valueGetter = "get_" + builder.getValue() + "()"; + } else { + valueGetter = "m." + builder.getValue(); + } + builder.setValueGetter(valueGetter); + } + if(!builder.isPrimitiveType() && !builder.isValueStringList() && builder.isPresentOldValue()) { + boolean oldValueWithinOpt = hierarchyHelper.isWithinOptionalStructure(builder.getOldValue()); + boolean oldValueWithinList = hierarchyHelper.isWithinListStructure(builder.getOldValue()); + builder.setOldValueWithinOpt(oldValueWithinOpt); + builder.setOldValueWithinList(oldValueWithinList); + String oldValueGetter; + if (oldValueWithinOpt && oldValueWithinList) { + oldValueGetter = "get_" + builder.getOldValue() + "().get()"; + } else if (oldValueWithinOpt) { + oldValueGetter = "m." + builder.getOldValue() + ".get()"; + } else if (oldValueWithinList) { + oldValueGetter = "get_" + builder.getOldValue() + "()"; + } else { + oldValueGetter = "m." + builder.getOldValue(); + } + builder.setOldValueGetter(oldValueGetter); + } + + return builder.build(); + } + + private ASTChange createLinkCreation(ASTODLink link) { + String targetName = Names.constructQualifiedName(link.getRightReferenceName(0).getPartsList()); + ASTODObject targetObject = Util.getODObject(rhs, targetName); + + + ASTChangeBuilder builder = ODRuleGenerationMill.changeBuilder(); + + builder.setComposite(!link.isLink()); + String objectName = Names.constructQualifiedName(link.getLeftReferenceName(0).getPartsList()); + builder.setObjectName(objectName); + String attributeName = link.getRightRole(); + builder.setAttributeName(attributeName); + + // objectType + String type = Util.getRightRoleType(link, rhs); + String objectType = Util.getLeftRoleType(link, lhs); + builder.setType(type); + builder.setObjectType(objectType); + builder.setSimpleType(Names.getSimpleName(type)); + if (type.equals("int")) { + builder.setBoxingType("Integer"); + } + else if(type.equals("boolean")) { + builder.setBoxingType("Boolean"); + } + if(!link.getStereotypeValue("genericType").isEmpty()) { + builder.setGenericType(link.getStereotypeValue("genericType")); + } else { + builder.setGenericType(type); + } + + boolean isAttributeIterated = link.isAttributeIterated(); + builder.setAttributeIterated(isAttributeIterated); + boolean isAttributeOptional = link.isAttributeOptional(); + builder.setAttributeOptional(isAttributeOptional); + builder.setValueListObject(targetObject.hasStereotype(ODRuleStereotypes.LIST)); + builder.setValueType(type); + builder.setCopy(link.hasStereotype("copy")); + + if (isAttributeIterated) { + builder.setSetter("get" + Util.makeSingular(attributeName) + "List().add"); + builder.setUnsetter("get" + Util.makeSingular(attributeName) + "List().remove"); + } else { + builder.setSetter("set" + StringTransformations.capitalize(attributeName)); + builder.setUnsetter("set" + StringTransformations.capitalize(attributeName)); + } + + if (type.equals("boolean")) { + builder.setGetter("is" + StringTransformations.capitalize(attributeName)); + } else if(isAttributeIterated) { + builder.setGetter("get" + Util.makeSingular(attributeName) + "List"); + if(isAttributeOptional) { + builder.setGetIsPresent("isPresent" + Util.makeSingular(attributeName) + "List()"); + } + } else { + builder.setGetter("get" + StringTransformations.capitalize(attributeName)); + if(isAttributeOptional) { + builder.setGetIsPresent("isPresent" + StringTransformations.capitalize(attributeName) + "()"); + } + } + + + // valueStringList + // primitiveType + + builder.setValue(targetName); + // oldValue + + + boolean objectWithinOpt = hierarchyHelper.isWithinOptionalStructure(objectName); + boolean objectWithinList = hierarchyHelper.isWithinListStructure(objectName); + builder.setObjectWithinOpt(objectWithinOpt); + builder.setObjectWithinList(objectWithinList); + String objectGetter = ""; + if(objectWithinOpt && objectWithinList) { + objectGetter = "get_" + objectName + "().get()"; + } else if(objectWithinOpt) { + objectGetter = "m." + objectName + ".get()"; + } else if(objectWithinList) { + objectGetter = "get_" + objectName + "()"; + } else { + objectGetter = "m." + objectName; + } + builder.setObjectGetter(objectGetter); + + if(!builder.isPrimitiveType() && !builder.isValueStringList() && builder.isPresentValue()) { + boolean valueWithinOpt = hierarchyHelper.isWithinOptionalStructure(builder.getValue()); + boolean valueWithinList = hierarchyHelper.isWithinListStructure(builder.getValue()); + builder.setValueWithinOpt(valueWithinOpt); + builder.setValueWithinList(valueWithinList); + String valueGetter; + if (valueWithinOpt && valueWithinList) { + valueGetter = "get_" + builder.getValue() + "().get()"; + } else if (valueWithinOpt) { + valueGetter = "m." + builder.getValue() + ".get()"; + } else if (valueWithinList) { + valueGetter = "get_" + builder.getValue() + "()"; + } else { + valueGetter = "m." + builder.getValue(); + } + builder.setValueGetter(valueGetter); + } + if(!builder.isPrimitiveType() && !builder.isValueStringList() && builder.isPresentOldValue()) { + boolean oldValueWithinOpt = hierarchyHelper.isWithinOptionalStructure(builder.getOldValue()); + boolean oldValueWithinList = hierarchyHelper.isWithinListStructure(builder.getOldValue()); + builder.setOldValueWithinOpt(oldValueWithinOpt); + builder.setOldValueWithinList(oldValueWithinList); + String oldValueGetter; + if (oldValueWithinOpt && oldValueWithinList) { + oldValueGetter = "get_" + builder.getOldValue() + "().get()"; + } else if (oldValueWithinOpt) { + oldValueGetter = "m." + builder.getOldValue() + ".get()"; + } else if (oldValueWithinList) { + oldValueGetter = "get_" + builder.getOldValue() + "()"; + } else { + oldValueGetter = "m." + builder.getOldValue(); + } + builder.setOldValueGetter(oldValueGetter); + } + + if(link.hasStereotype("insertType")) { + builder.setInsertPosition(createInsertPosition(link, builder)); + } + + return builder.build(); + } + + private String createInsertPosition(ASTODLink link, ASTChangeBuilder builder) { + String insertType = link.getStereotypeValue("insertType"); + String insertAt = link.getStereotypeValue("insertAt"); + String insertPos = ""; + + if(insertType.equals("first")) { + insertPos = "0"; + } else if(insertType.equals("last")) { + insertPos = "m."+ builder.getObjectName() + "." + builder.getGetter() + "().size()"; + } else if(insertType.equals("inplace")) { + insertPos = "m."+ builder.getObjectName() + "_" + insertAt + "_before_pos"; + } else if(insertType.equals("relative")) { + insertPos = "m."+ builder.getObjectName() + "." + builder.getGetter() + "().indexOf(" + "m." + insertAt + ") + 1"; + } + + return insertPos; + } + + private ASTChange createLinkDeletion(ASTODLink link) { + String attributeName = ""; + if(link.isPresentRightRole()) { + attributeName = link.getRightRole(); + } + String targetName = Names.constructQualifiedName(link.getRightReferenceName(0).getPartsList()); + String objectName = Names.constructQualifiedName(link.getLeftReferenceName(0).getPartsList()); + ASTODObject targetObject = Util.getODObject(lhs, targetName); + String objectType = Util.getLeftRoleType(link, lhs); + + ASTChangeBuilder builder = ODRuleGenerationMill.changeBuilder(); + + builder.setComposite(!link.isLink()); + + builder.setObjectName(objectName); + builder.setAttributeName(attributeName); + + builder.setObjectType(objectType); + String type = Util.getRightRoleType(link, lhs); + builder.setType(type); + builder.setSimpleType(Names.getSimpleName(type)); + if (type.equals("int")) { + builder.setBoxingType("Integer"); + } + else if(type.equals("boolean")) { + builder.setBoxingType("Boolean"); + } + if(!link.getStereotypeValue("genericType").isEmpty()) { + builder.setGenericType(link.getStereotypeValue("genericType")); + } else { + builder.setGenericType(type); + } + + + boolean isAttributeIterated = link.isAttributeIterated(); + builder.setAttributeIterated(isAttributeIterated); + boolean isAttributeOptional = link.isAttributeOptional(); + builder.setAttributeOptional(isAttributeOptional); + builder.setValueListObject(targetObject.hasStereotype(ODRuleStereotypes.LIST)); + builder.setValueType(type); + + if (isAttributeIterated) { + builder.setSetter("get" + Util.makeSingular(attributeName) + "List().add"); + builder.setUnsetter("get" + Util.makeSingular(attributeName) + "List().remove"); + } else { + builder.setSetter("set" + StringTransformations.capitalize(attributeName)); + builder.setUnsetter("set" + StringTransformations.capitalize(attributeName)); + } + + if (type.equals("boolean")) { + builder.setGetter("is" + StringTransformations.capitalize(attributeName)); + } else if(isAttributeIterated) { + builder.setGetter("get" + Util.makeSingular(attributeName) + "List"); + if(isAttributeOptional) { + builder.setGetIsPresent("isPresent" + Util.makeSingular(attributeName) + "List()"); + } + } else { + builder.setGetter("get" + StringTransformations.capitalize(attributeName)); + if(isAttributeOptional) { + builder.setGetIsPresent("isPresent" + StringTransformations.capitalize(attributeName) + "()"); + } + } + + // valueStringList + // primitiveType + + // value + builder.setOldValue(targetName); + + + boolean objectWithinOpt = hierarchyHelper.isWithinOptionalStructure(objectName); + boolean objectWithinList = hierarchyHelper.isWithinListStructure(objectName); + builder.setObjectWithinOpt(objectWithinOpt); + builder.setObjectWithinList(objectWithinList); + String objectGetter = ""; + if(objectWithinOpt && objectWithinList) { + objectGetter = "get_" + objectName + "().get()"; + } else if(objectWithinOpt) { + objectGetter = "m." + objectName + ".get()"; + } else if(objectWithinList) { + objectGetter = "get_" + objectName + "()"; + } else { + objectGetter = "m." + objectName; + } + builder.setObjectGetter(objectGetter); + + if(!builder.isPrimitiveType() && !builder.isValueStringList() && builder.isPresentValue()) { + boolean valueWithinOpt = hierarchyHelper.isWithinOptionalStructure(builder.getValue()); + boolean valueWithinList = hierarchyHelper.isWithinListStructure(builder.getValue()); + builder.setValueWithinOpt(valueWithinOpt); + builder.setValueWithinList(valueWithinList); + String valueGetter; + if (valueWithinOpt && valueWithinList) { + valueGetter = "get_" + builder.getValue() + "().get()"; + } else if (valueWithinOpt) { + valueGetter = "m." + builder.getValue() + ".get()"; + } else if (valueWithinList) { + valueGetter = "get_" + builder.getValue() + "()"; + } else { + valueGetter = "m." + builder.getValue(); + } + builder.setValueGetter(valueGetter); + } + if(!builder.isPrimitiveType() && !builder.isValueStringList() && builder.isPresentOldValue()) { + boolean oldValueWithinOpt = hierarchyHelper.isWithinOptionalStructure(builder.getOldValue()); + boolean oldValueWithinList = hierarchyHelper.isWithinListStructure(builder.getOldValue()); + builder.setOldValueWithinOpt(oldValueWithinOpt); + builder.setOldValueWithinList(oldValueWithinList); + String oldValueGetter; + if (oldValueWithinOpt && oldValueWithinList && objectWithinList) { + // Special case for nested Opt inside List + oldValueGetter = "get_" + builder.getOldValue() + "()"; + } else if (oldValueWithinOpt && oldValueWithinList) { + oldValueGetter = "get_" + builder.getOldValue() + "().get()"; + } else if (oldValueWithinOpt) { + oldValueGetter = "m." + builder.getOldValue() + ".get()"; + } else if (oldValueWithinList) { + oldValueGetter = "get_" + builder.getOldValue() + "()"; + } else { + oldValueGetter = "m." + builder.getOldValue(); + } + builder.setOldValueGetter(oldValueGetter); + } + + return builder.build(); + } + + private ASTDeleteOperation createDelete(ASTMCType type, String variableName, boolean isListObject) { + String typeName = Util.printType(type); + List complexname = Splitters.DOT.splitToList(typeName); + String simpleType = complexname.get(complexname.size()-1); + + ASTDeleteOperationBuilder builder = ODRuleGenerationMill.deleteOperationBuilder(); + builder.setName(variableName); + builder.setType(typeName); + builder.setList(isListObject); + builder.setSimpleType(simpleType); + builder.setGrammarType(simpleType.substring(3)); + + builder.setTypepackage(rule.getGrammarPackageName() + + "." + rule.getGrammarName().toLowerCase() + "._ast"); + + return builder.build(); + } + + private ASTCreateOperation createCreate(ASTMCType type, String variable, boolean isInterface) { + String typeName = Util.printType(type); + List complexname = Splitters.DOT.splitToList(typeName); + String simpleType = complexname.get(complexname.size()-1); + + ASTCreateOperationBuilder builder = ODRuleGenerationMill.createOperationBuilder(); + builder.setName(variable); + builder.setType(typeName); + builder.setSimpleType(simpleType); + + builder.setFactoryName(rule.getGrammarPackageName() + + "." + rule.getGrammarName().toLowerCase() + + "." + rule.getGrammarName() + "Mill"); + + + if (isInterface) + builder.setFactoryName("__missing"); + + return builder.build(); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/tf/odrules/DifferenceFinder.java b/monticore-grammar/src/main/java/de/monticore/tf/odrules/DifferenceFinder.java new file mode 100644 index 0000000000..f8d6893ea3 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/tf/odrules/DifferenceFinder.java @@ -0,0 +1,324 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.odrules; + +import de.monticore.ast.ASTNode; +import de.monticore.types.mcbasictypes._ast.ASTMCQualifiedName; +import de.monticore.umlstereotype._ast.ASTStereoValue; +import de.monticore.umlstereotype._ast.ASTStereotype; +import de.se_rwth.commons.Names; +import de.monticore.tf.odrulegeneration._ast.ASTChangeOperation; +import de.monticore.tf.odrules._ast.ASTODDefinition; +import de.monticore.tf.odrules._ast.ASTODLink; +import de.monticore.tf.odrules._ast.ASTODObject; +import de.monticore.tf.odrules._ast.ASTODRule; +import de.monticore.tf.odrules._parser.ODRulesParser; +import de.monticore.tf.odrules.util.ODRuleStereotypes; +import de.monticore.tf.odrules.util.Util; + +import java.io.IOException; +import java.util.*; + +public class DifferenceFinder { + + + private static class ChangePair { + + ChangePair(T from, T to) { + this.from = from; + this.to = to; + } + + private T from; + private T to; + } + + + private List toDeleteObjects = new ArrayList<>(); + private List toCreateObjects = new ArrayList<>(); + private List unchangedObjects = new ArrayList<>(); + private List> toChangeObjects = new LinkedList<>(); + + private List toDeleteLinks = new LinkedList<>(); + private List toCreateLinks = new LinkedList<>(); + private List unchangedLinks = new LinkedList<>(); + private ASTODDefinition lhs; + + private HashMap> toCreateObjectsAttr = new HashMap(); + + private HierarchyHelper hierarchyHelper; + + public DifferenceFinder(HierarchyHelper hierarchyHelper) { + this.hierarchyHelper = hierarchyHelper; + } + + + /** + * @param transformationRulesFilename Filename to the mtod File that contains + * both lhs and rhs of the Rules. + * @return + * + */ + public List getDifference(String transformationRulesFilename) throws IOException { + ODRulesParser parser = new ODRulesParser(); + Optional rule = parser.parse(transformationRulesFilename); + return getDifference(rule.get()); + } + + + /** + * @param rule the parsed ODRule + * @return returns the Composition of all change operations that have to be + * performed. Throws IllegalArgumentException, if the there are type + * mismatches in lhs rhs. + */ + public List getDifference(ASTODRule rule) { + this.lhs = rule.getLhs(); + //clear all lists for new calculation + toDeleteObjects = new ArrayList<>(); + toCreateObjects = new ArrayList<>(); + unchangedObjects = new ArrayList<>(); + toChangeObjects = new LinkedList<>(); + + + toDeleteLinks = new LinkedList<>(); + toCreateLinks = new LinkedList<>(); + unchangedLinks = new LinkedList<>(); + + List leftObjects = Util.getAllODObjects(lhs); + + if(rule.isPresentRhs()){ + List rightObjects = Util.getAllODObjects(rule.getRhs()); + + //calculate the list of deleted and the list of changed objects + calculateObjectsToChangeOrDelete(leftObjects, rightObjects); + + //calculate the list of created objects + calculateObjectsToCreate(leftObjects, rightObjects); + + List lhsLinks = lhs.getODLinkList(); + List rhsLinks = rule.getRhs().getODLinkList(); + + // calculate the list of deleted links + calculateLinksToDelete(lhsLinks, rhsLinks); + + //calculate the list of created links + calculateLinksToCreate(lhsLinks, rhsLinks); + + //calculate the build order of created objects + ODBuildOrder buildOrder = new ODBuildOrder(toCreateObjects, toCreateLinks); + toCreateObjects = buildOrder.getBuildOrder(); + toCreateObjectsAttr = buildOrder.getBuildAttrs(); + + //calculate and return the composition of all changes + return calculateChanges(rule, lhs, rule.getRhs()); + } + return new ArrayList(); + } + + + /** + * Calculates the change between the LHS and the RHS + * based on the fields toDeleteObjects, toCreateObjects, + * toChangeObjects, toDeleteLinks, toCreateLinks + * + * @param lhs left hand side of the Rules + * @param rhs right hand side of the Rules + * @return the changeComposition containing all changes. + */ + private List calculateChanges(ASTODRule rule, ASTODDefinition lhs, ASTODDefinition rhs) { + ChangeOperationFactory operationFactory = new ChangeOperationFactory(rule, lhs, rhs, hierarchyHelper); + + List changeOpList = new ArrayList(); + List toCreateLinksCopy = new LinkedList<>(toCreateLinks); + + for (ChangePair pair : toChangeObjects) { + changeOpList.add(operationFactory.createChangeOperation(pair.from, pair.to)); + } + for (ASTODObject obj : toCreateObjects) { + List relLinks = new LinkedList<>(); + for (ASTODLink link : toCreateObjectsAttr.get(obj)) { + toCreateLinksCopy.remove(link); + relLinks.add(link); + } + changeOpList.add(operationFactory.createCreateOperation(obj, relLinks)); + } + for (ASTODLink link : toDeleteLinks) { + String targetName = Names.constructQualifiedName(link.getRightReferenceName(0).getPartsList()); + ASTODObject targetObject = Util.getODObject(lhs, targetName); + if(!targetObject.hasStereotype(ODRuleStereotypes.NOT)){ + changeOpList.add(operationFactory.createDeleteOperation(link)); + } + } + for (ASTODLink link : toCreateLinksCopy) { + changeOpList.add(operationFactory.createCreateOperation(link)); + } + for (ASTODObject obj : toDeleteObjects) { + changeOpList.add(operationFactory.createDeleteOperation(obj)); + } + // unchanged objects do not need to be listed anywhere. + return changeOpList; + } + + /** + * Calculates the list of deleted objects stored in the field toDeleteObjects + * and the list of changed objects stored in the field toChangeObjects + * + * @param leftObjects List of objects from the LHS + * @param rightObjects List of objects from the RHS + */ + private void calculateObjectsToChangeOrDelete(List leftObjects, List rightObjects){ + for (ASTODObject left : leftObjects) { + // says, weather the variable from the left side is found on the + // right side. + boolean found = false; + // for all rules on the left side, find the matching rule on the + // right side. + if (!left.hasStereotype(ODRuleStereotypes.NOT)) { + for (ASTODObject right : rightObjects) { + if (left.getName().equals(right.getName())) { + // deepEquals is too strong but if there is nothing to + // change the factory doesn't add any changes + // needs to be replaced by another equals, if you need a + // correct list of unchanged Objects. + if (left.deepEquals(right)) { + unchangedObjects.add(left.deepClone()); + } else { + toChangeObjects.add(new ChangePair<>(left.deepClone(), right.deepClone())); + } + found = true; + } + } + // Objects only on left side should be deleted + if (!found) { + toDeleteObjects.add(left.deepClone()); + } + } + } + } + + /** + * Calculates the list of created objects stored in the field toCreateObjects + * + * @param leftObjects List of objects from the LHS + * @param rightObjects List of objects from the RHS + */ + private void calculateObjectsToCreate(List leftObjects, List rightObjects){ + for (ASTODObject right : rightObjects) { + // says, weather the variable from the right side is found on the + // left side. + boolean found = false; + // there must be a better way than iterating the whole object list + // on the right + for (ASTODObject left : leftObjects) { + if (left.getName().equals(right.getName())) { + found = true; + } + } + if (!found) { + toCreateObjects.add(right.deepClone()); + } + } + + } + + /** + * Calculates the list of deleted links stored in the field toDeleteLinks + * and the list of changed links stored in the field toChangeLinks + * + * @param lhsLinks List of links from the LHS + * @param rhsLinks List of links from the RHS + */ + private void calculateLinksToDelete(List lhsLinks, List rhsLinks) { + for (ASTODLink left : lhsLinks) { + // says, whether the variable from the left side is found on the + // right side. + boolean found = false; + boolean isSetValued = left.isAttributeIterated(); + + // for all rules on the left side, find the matching rule on the + // right side. If the multiplicity at the right end is larger than 1, + // then any link with the same source object and the same role is + // a match + for (ASTODLink right : rhsLinks) { + boolean match; + boolean unchanged; + if (isSetValued) { + match = isMatchForSetValuedLink(left, right); + unchanged = match; + } + else { + match = isMatchForLink(left, right); + unchanged = match && referencesAreEqual(left.getRightReferenceNameList(), + right.getRightReferenceNameList()); + } + if (match) { + if (unchanged) { + unchangedLinks.add(left); + } + found = true; + } + } + // Objects only on left side should be deleted + if (!found) { + toDeleteLinks.add(left); + } + + } + } + + private boolean referencesAreEqual(List rightReferenceNames, + List rightReferenceNames1) { + return rightReferenceNames.get(0).deepEquals(rightReferenceNames1.get(0)); + } + + private boolean isMatchForSetValuedLink(ASTODLink left, ASTODLink right){ + return (referencesAreEqual(left.getLeftReferenceNameList(),right.getLeftReferenceNameList()) + && referencesAreEqual(left.getRightReferenceNameList(),right.getRightReferenceNameList())) + && areRolesEqual(left, right); + } + + private boolean areRolesEqual(ASTODLink left, ASTODLink right){ + return ((left.getRightRole() == null && right.getRightRole() == null) + || (left.getRightRole() != null && left.getRightRole().equals(right.getRightRole()))); + } + + private boolean isMatchForLink(ASTODLink left, ASTODLink right){ + return referencesAreEqual(left.getLeftReferenceNameList(),right.getLeftReferenceNameList()) + && areRolesEqual(left, right); + } + + /** + * Calculates the list of created links stored in the field toCreateLinks + * + * @param lhsLinks List of links from the LHS + * @param rhsLinks List of links from the RHS + */ + private void calculateLinksToCreate(List lhsLinks, List rhsLinks){ + for (ASTODLink right : rhsLinks) { + // says, weather the variable from the right side is found on the + // left side. + boolean found = false; + // there must be a better way than iterating the whole object list + // on the right + for (ASTODLink left : lhsLinks) { + if (left.deepEquals(right)) { + found = true; + } + + } + + for(ASTODLink former : rhsLinks){ + if(!right.deepEquals(former) && referencesAreEqual(former.getRightReferenceNameList(),right.getRightReferenceNameList())){ + ASTStereotype stereotype = ODRulesMill.stereotypeBuilder().build(); + ASTStereoValue value = ODRulesMill.stereoValueBuilder().setName("copy").setContent("").build(); + stereotype.getValuesList().add(value); + right.setStereotype(stereotype); + } + } + if (!found) { + toCreateLinks.add(right); + } + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/tf/odrules/GenerateConditionsVisitor.java b/monticore-grammar/src/main/java/de/monticore/tf/odrules/GenerateConditionsVisitor.java new file mode 100644 index 0000000000..6b62704c0b --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/tf/odrules/GenerateConditionsVisitor.java @@ -0,0 +1,486 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.odrules; + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.statements.mcarraystatements._ast.ASTArrayInit; +import de.monticore.statements.mcvardeclarationstatements._ast.ASTVariableInit; +import de.se_rwth.commons.Joiners; +import de.se_rwth.commons.Names; +import de.se_rwth.commons.Splitters; +import de.se_rwth.commons.StringTransformations; +import de.se_rwth.commons.logging.Log; +import de.monticore.tf.odrulegeneration.ODRuleGenerationMill; +import de.monticore.tf.odrulegeneration._ast.ASTDependency; +import de.monticore.tf.odrulegeneration._ast.ASTObjectCondition; +import de.monticore.tf.odrules._ast.ASTODAttribute; +import de.monticore.tf.odrules._ast.ASTODDefinition; +import de.monticore.tf.odrules._ast.ASTODLink; +import de.monticore.tf.odrules._ast.ASTODObject; +import de.monticore.tf.odrules._visitor.ODRulesVisitor2; +import de.monticore.tf.odrules.util.ODRuleStereotypes; +import de.monticore.tf.odrules.util.TFExpressionFullPrettyPrinter; +import de.monticore.tf.odrules.util.Util; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import static de.se_rwth.commons.StringTransformations.capitalize; +import static de.se_rwth.commons.StringTransformations.uncapitalize; +import static java.lang.String.format; + +public class GenerateConditionsVisitor implements + ODRulesVisitor2 { + + private static final String REGEX = "\\."; + private static final String CAND = "cand"; + private static final String CAND_SUFFIX = "_cand"; + private static final String GET = ".get()"; + private static final String GET_ATTRIBUTE = ".get%s()"; + + private List objectConditions = new ArrayList<>(); + + private String objectName = ""; + private String attrValue = ""; + private boolean targetOptional = false; + + private ASTODObject object; + private List objectList; + + + public List getObjectConditions() { + return objectConditions; + } + + @Override + public void visit(ASTODObject node) { + object = node; + if (node.isPresentName()) { + objectName = node.getName(); + } else { + Log.error("0xF0001: Object name must be set."); + } + } + + @Override + public void visit(ASTODDefinition node) { + objectList = Util.getAllODObjects(node); + } + + + /** + * Visits the nodes of the given AST of the type ASTODAttribute and generates + * Strings for the conditions + * + * @param node the node to start visiting + */ + @Override + public void visit(ASTODAttribute node) { + if (node.isPresentSingleValue()) { + attrValue = Util.printExpression(node.getSingleValue()); + } else if (node.isPresentList()) { + attrValue = new TFExpressionFullPrettyPrinter(new IndentPrinter()).prettyprint(node.getList()); + } else { + Log.error("0xF0002: Object value must be set."); + } + + Optional targetObject = getODObject(createObjectString(attrValue)); + + + if ("boolean".equals(node.printType())) { + createConditionsForBoolean(node, targetObject); + } else if ("int".equals(node.printType())) { + createConditionsForInt(node); + } else if (node.isPresentList()) { + createConditionsForListWithoutTarget(node); + } else if (!targetObject.isPresent() || !targetObject.get().getName().equals(objectName)) { + createConditions(node, object, targetObject); + } + } + + @Override + public void endVisit(ASTODAttribute node) { + targetOptional = false; + } + + private void addConditions(ASTObjectCondition... objectCondition) { + for (ASTObjectCondition o : objectCondition) { + if (o.getObjectName() != null && !o.getObjectName().isEmpty()) { + objectConditions.add(o); + } + } + } + + private void createConditions(ASTODAttribute node, ASTODObject object, Optional targetObject) { + boolean isOptional = node.isOptional(); + boolean isIterated = node.isIterated(); + + ASTObjectCondition objectCondition = ODRuleGenerationMill.objectConditionBuilder().uncheckedBuild(); + objectCondition.setObjectName(objectName); + ASTObjectCondition secondObjectCondition = ODRuleGenerationMill.objectConditionBuilder().uncheckedBuild(); + String getter = createGetterStatement(node); + String condition; + if (isOptional) { + condition = format("(!cand.isPresent%s() || ! cand.get%s().equals(%s", capitalize(node.getName()), capitalize(node.getName()), getter); + } else if(isIterated) { + condition = format("(!cand.get%sList().equals(%s", Util.makeSingular(node.getName()), getter); + }else{ + condition = format("(!cand.get%s().equals(%s", capitalize(node.getName()), getter); + } + + condition += "))"; + if (targetOptional) { + String presentCheck = createPresentCheckStatement(getter); + objectCondition.setConditionString(format("%s && %s", presentCheck, condition)); + } else if (getter.endsWith(GET)) { + objectCondition.setConditionString(getter.replace(GET, ".isPresent()") + " && " + condition); + } else if (getter.startsWith("$")) { + if (!getter.contains(".")) { + objectCondition.setConditionString(format("%s_is_fix && %s", getter, condition)); + } else { + objectCondition.setConditionString(format("%s != null && %s", getter, condition)); + } + } else { + objectCondition.setConditionString(condition); + } + // only if value refers to a different object + if (targetObject.isPresent()) { + if (!createObjectString(attrValue).isEmpty()) { + ASTDependency dependency = ODRuleGenerationMill.dependencyBuilder().uncheckedBuild(); + dependency.setContent(createObjectString(attrValue)); + objectCondition.setDependency(dependency); + } + if (isObjectWithoutStereoType(object)) { + String second = format("!%s_cand", objectName); + if (isOptional) { + second += format(".get%s()", capitalize(node.getName())); + } else if(isIterated) { + second += format(".get%sList()", Util.makeSingular(node.getName())); + } else { + second += format(".get%s()", capitalize(node.getName())); + } + second = format("%s.equals(%s)", second, createGetterStatementForCand(attrValue)); + secondObjectCondition.setConditionString(second); + if (!objectName.isEmpty()) { + ASTDependency dependency = ODRuleGenerationMill.dependencyBuilder().uncheckedBuild(); + dependency.setContent(objectName); + secondObjectCondition.setDependency(dependency); + } + secondObjectCondition.setObjectName(createObjectString(attrValue)); + } + } + addConditions(objectCondition, secondObjectCondition); + } + + private void createConditionsForListWithoutTarget(ASTODAttribute node) { + ASTObjectCondition objectCondition = ODRuleGenerationMill.objectConditionBuilder().uncheckedBuild(); + objectCondition.setObjectName(objectName); + StringBuilder conditionString = new StringBuilder(format("(cand.get%sList() == null ", StringTransformations.capitalize(node.getName()))); + ASTArrayInit value = node.getList(); + int numberOfStrings = value.getVariableInitList().size(); + conditionString.append(format("|| cand.get%sList().size() != %s", StringTransformations.capitalize(node.getName()), numberOfStrings)); + for (int i = 0; i < numberOfStrings; i++) { + ASTVariableInit nextValue = value.getVariableInit(i); + TFExpressionFullPrettyPrinter printer = new TFExpressionFullPrettyPrinter(new IndentPrinter()); + String v = printer.prettyprint(nextValue); + conditionString.append("|| "); + String check = ""; + // value is a variable + if (v.startsWith("\"$")) { + v = v.substring(1, v.length() - 1); + conditionString.append(v).append("_is_fix && "); + // value is a reference to another object + } else if (v.contains(".")) { + List qName = Splitters.DOT.splitToList(v); + String cand = qName.get(0) + CAND_SUFFIX; + StringBuilder getter = new StringBuilder(); + getter.append(format(GET_ATTRIBUTE, capitalize(qName.get(1)))); + + if (qName.size() == 3) { + getter.append(".").append(qName.get(2)); + } + v = cand + getter; + check = cand + " != null &&"; + + } + conditionString.append(format("%s! cand.get%sList().get(%s).equals(%s) ", check, StringTransformations.capitalize(node.getName()), i, v)); + } + conditionString.append(")"); + objectCondition.setConditionString(conditionString.toString()); + + addConditions(objectCondition); + } + + + private void createConditionsForBoolean(ASTODAttribute node, Optional targetObject) { + ASTObjectCondition objectCondition = ODRuleGenerationMill.objectConditionBuilder().uncheckedBuild(); + objectCondition.setObjectName(objectName); + ASTObjectCondition secondObjectCondition = ODRuleGenerationMill.objectConditionBuilder().uncheckedBuild(); + if ("true".equals(attrValue)) { + objectCondition.setConditionString(format("!cand.is%s()", capitalize(node.getName()))); + } else if ("false".equals(attrValue)) { + objectCondition.setConditionString(format("cand.is%s()", capitalize(node.getName()))); + } else { + objectCondition.setConditionString(format("cand.is%s() != %s", capitalize(node.getName()), createIsStatement(attrValue, false))); + if (targetObject.isPresent() && attrValue.contains(".")) { + if (!createObjectString(attrValue).isEmpty()) { + ASTDependency dependency = ODRuleGenerationMill.dependencyBuilder().uncheckedBuild(); + dependency.setContent(createObjectString(attrValue)); + objectCondition.setDependency(dependency); + } + secondObjectCondition.setConditionString(format("%s_cand.is%s() != %s", objectName, capitalize(node.getName()), createIsStatement(attrValue, true))); + if (!objectName.isEmpty()) { + ASTDependency dependency = ODRuleGenerationMill.dependencyBuilder().uncheckedBuild(); + dependency.setContent(objectName); + secondObjectCondition.setDependency(dependency); + } + secondObjectCondition.setObjectName(createObjectString(attrValue)); + } + } + addConditions(objectCondition, secondObjectCondition); + } + + private void createConditionsForInt(ASTODAttribute node) { + ASTObjectCondition objectCondition = ODRuleGenerationMill.objectConditionBuilder().uncheckedBuild(); + objectCondition.setObjectName(objectName); + objectCondition.setConditionString(format("cand.get%s() != %s", capitalize(node.getName()), attrValue)); + addConditions(objectCondition); + + } + + /** + * Visits the nodes of the given AST of the type ASTODLink and generates + * Strings for the conditions + * + * @param node the node to start visiting + */ + @Override + public void visit(ASTODLink node) { + String leftObjectName = Names.constructQualifiedName(node.getLeftReferenceName(0).getPartsList()); + String rightObjectName = Names.constructQualifiedName(node.getRightReferenceName(0).getPartsList()); + Optional rightObject = getODObject(rightObjectName); + Optional leftObject = getODObject(leftObjectName); + boolean isIterated = node.isAttributeIterated(); + boolean isOptional = node.isAttributeOptional(); + + ASTObjectCondition objectCondition = ODRuleGenerationMill.objectConditionBuilder().uncheckedBuild(); + objectCondition.setObjectName(leftObjectName); + ASTObjectCondition secondObjectCondition = ODRuleGenerationMill.objectConditionBuilder().uncheckedBuild(); + secondObjectCondition.setObjectName(rightObjectName); + + if (!node.isLink()) { + // if it is a composition the left side is the parent of the right + objectCondition.setConditionString(format("t.getParent(%s_cand) != cand", rightObjectName)); + secondObjectCondition.setConditionString(format("t.getParent(cand) != %s_cand", leftObjectName)); + if (!rightObjectName.isEmpty()) { + ASTDependency dependency = ODRuleGenerationMill.dependencyBuilder().uncheckedBuild(); + dependency.setContent(rightObjectName); + objectCondition.setDependency(dependency); + } + if (!leftObjectName.isEmpty()) { + ASTDependency dependency = ODRuleGenerationMill.dependencyBuilder().uncheckedBuild(); + dependency.setContent(leftObjectName); + secondObjectCondition.setDependency(dependency); + } + if (node.isPresentRightRole()) { + // if a role name is given, then there is a second condition + // if an object is a list object or a negative node, it will be + // checked later + + + if (!rightObject.get().hasStereotype(ODRuleStereotypes.LIST) + && !(!leftObject.get().hasStereotype(ODRuleStereotypes.NOT) && rightObject.get().hasStereotype(ODRuleStereotypes.NOT)) + && !rightObject.get().hasStereotype(ODRuleStereotypes.OPTIONAL)) { + ASTObjectCondition roleCondition1 = ODRuleGenerationMill.objectConditionBuilder().uncheckedBuild(); + roleCondition1.setObjectName(leftObjectName); + roleCondition1.setConditionString(createSourceObjectCondition(node, rightObjectName, isIterated, isOptional)); + if (!rightObjectName.isEmpty()) { + ASTDependency dependency = ODRuleGenerationMill.dependencyBuilder().uncheckedBuild(); + dependency.setContent(rightObjectName); + roleCondition1.setDependency(dependency); + } + objectConditions.add(roleCondition1); + } + if (!leftObject.get().hasStereotype(ODRuleStereotypes.LIST) + && !leftObject.get().hasStereotype(ODRuleStereotypes.OPTIONAL)) { + ASTObjectCondition roleCondition2 = ODRuleGenerationMill.objectConditionBuilder().uncheckedBuild(); + roleCondition2.setObjectName(rightObjectName); + roleCondition2.setConditionString(createTargetObjectCondition(node, leftObjectName, isIterated, isOptional)); + if (!leftObjectName.isEmpty()) { + ASTDependency dependency = ODRuleGenerationMill.dependencyBuilder().uncheckedBuild(); + dependency.setContent(leftObjectName); + roleCondition2.setDependency(dependency); + } + objectConditions.add(roleCondition2); + } + } + } else { + // if its not a composition it should be a link + if (!node.getName().isEmpty()) { + // name must be given to create conditionString + objectCondition.setConditionString(format("%s.getRightFromLeft(cand) != null && !%s.getRightFromLeft(cand).contains(%s_cand)", + uncapitalize(node.getName()), + uncapitalize(node.getName()), + rightObjectName)); + if (!rightObjectName.isEmpty()) { + ASTDependency dependency = ODRuleGenerationMill.dependencyBuilder().uncheckedBuild(); + dependency.setContent(rightObjectName); + objectCondition.setDependency(dependency); + } + secondObjectCondition.setConditionString(format("%s.getRightFromLeft(%s_cand) != null && !%s.getRightFromLeft(%s_cand).contains(cand)", + uncapitalize(node.getName()), + leftObjectName, + uncapitalize(node.getName()), + leftObjectName)); + if (!leftObjectName.isEmpty()) { + ASTDependency dependency = ODRuleGenerationMill.dependencyBuilder().uncheckedBuild(); + dependency.setContent(leftObjectName); + secondObjectCondition.setDependency(dependency); + } + } + } + if (!leftObject.get().hasStereotype(ODRuleStereotypes.LIST) + && !leftObject.get().hasStereotype(ODRuleStereotypes.OPTIONAL)) { + objectConditions.add(secondObjectCondition); + } + if (!rightObject.get().hasStereotype(ODRuleStereotypes.LIST) + && !(!leftObject.get().hasStereotype(ODRuleStereotypes.NOT) && rightObject.get().hasStereotype(ODRuleStereotypes.NOT)) + && !rightObject.get().hasStereotype(ODRuleStereotypes.OPTIONAL)) { + objectConditions.add(objectCondition); + } + } + + private String createSourceObjectCondition(ASTODLink node, String rightObjectName, boolean isIterated, boolean isOptional) { + if (isIterated) { + return format("!cand.get%sList().contains(%s_cand)", + Util.makeSingular(node.getRightRole()), + rightObjectName); + } else if (isOptional) { + return format("((!cand.isPresent%s())||(cand.isPresent%s() && cand.get%s() != %s_cand))", + capitalize(node.getRightRole()), + capitalize(node.getRightRole()), + capitalize(node.getRightRole()), + rightObjectName); + } else { + return format("cand.get%s() != %s_cand", + capitalize(node.getRightRole()), + rightObjectName); + } + } + + private String createTargetObjectCondition(ASTODLink node, String leftObjectName, boolean isIterated, boolean isOptional) { + if (isIterated) { + return format("!%s_cand.get%sList().contains(cand)", + uncapitalize(leftObjectName), + Util.makeSingular(node.getRightRole())); + } else if (isOptional) { + return format("((!%s_cand.isPresent%s())||(%s_cand.isPresent%s() && %s_cand.get%s() != cand))", + leftObjectName, capitalize(node.getRightRole()), + leftObjectName, capitalize(node.getRightRole()), + leftObjectName, capitalize(node.getRightRole())); + } else { + return format("%s_cand.get%s() != cand", + leftObjectName, + capitalize(node.getRightRole())); + } + } + + private String createGetterStatement(ASTODAttribute attrWithValue) { + String value = Util.printExpression(attrWithValue.getSingleValue()); + String[] split = value.split(REGEX); + + if (split.length == 1) { + if (getODObject(split[0]).isPresent()) { + // object reference + return split[0] + CAND_SUFFIX; + } else if (split[0].startsWith("\"$")) { + // variable + return split[0].substring(1, split[0].length() - 1); + } else { + // fix value + return split[0]; + } + } else { + // attribute reference + StringBuilder result = new StringBuilder(split[0]).append(CAND_SUFFIX); + if (split.length > 1) { + result.append(format(GET_ATTRIBUTE, capitalize(split[1]))); + targetOptional = attrWithValue.isValueOptional(); + } + for (int i = 2; i < split.length; i++) { + if(!split[i].equals("get()")) { + result.append(".").append(split[i]); + targetOptional = attrWithValue.isValueOptional(); + } + } + return result.toString(); + } + + } + private String createPresentCheckStatement(String getter) { + List splits = new ArrayList<>(Splitters.DOT.splitToList(getter)); + splits.set(splits.size() - 1, splits.get(splits.size() - 1).replace("get", "isPresent")); + + return Joiners.DOT.join(splits); + } + + private String createGetterStatementForCand(String statement) { + String[] split = statement.split(REGEX); + if (split.length == 1) { + return CAND; + } else { + StringBuilder result = new StringBuilder(CAND); + if (split.length > 1) { + result.append(format(GET_ATTRIBUTE, capitalize(split[1]))); + } + for (int i = 2; i < split.length; i++) { + if(!split[i].equals("get()")) { + result.append(".").append(split[i]); + } + } + return result.toString(); + } + } + + private String createIsStatement(String attrValue, boolean forCand) { + String[] split = attrValue.split(REGEX); + StringBuilder result; + if (forCand) { + result = new StringBuilder(CAND); + } else { + result = new StringBuilder(split[0]).append(CAND_SUFFIX); + } + String getter = ".get"; + for (int i = 1; i < split.length; i++) { + if (i == split.length - 1) { + getter = ".is"; + } + result.append(getter).append(capitalize(split[i])).append("()"); + } + return result.toString(); + } + + private String createObjectString(String statement) { + String[] split = statement.split(REGEX); + if (split[0].startsWith("{")) { + return split[0].substring(1); + } + return split[0]; + } + + private Optional getODObject(String objName) { + for (ASTODObject o : objectList) { + if (objName.equals(o.getName())) { + return Optional.of(o); + } + } + return Optional.empty(); + } + + private boolean isObjectWithoutStereoType(ASTODObject object) { + return !object.hasStereotype(ODRuleStereotypes.LIST) + && !object.hasStereotype(ODRuleStereotypes.NOT) + && !object.hasStereotype(ODRuleStereotypes.OPTIONAL); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/tf/odrules/GenerateLinkConditionsVisitor.java b/monticore-grammar/src/main/java/de/monticore/tf/odrules/GenerateLinkConditionsVisitor.java new file mode 100644 index 0000000000..75a9027f17 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/tf/odrules/GenerateLinkConditionsVisitor.java @@ -0,0 +1,88 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.odrules; + +import de.se_rwth.commons.Names; +import de.se_rwth.commons.StringTransformations; +import de.monticore.tf.odrulegeneration.ODRuleGenerationMill; +import de.monticore.tf.odrulegeneration._ast.ASTDependency; +import de.monticore.tf.odrulegeneration._ast.ASTLinkCondition; +import de.monticore.tf.odrules._ast.ASTODDefinition; +import de.monticore.tf.odrules._ast.ASTODLink; +import de.monticore.tf.odrules._visitor.ODRulesVisitor2; +import de.monticore.tf.odrules.util.Util; + +import java.util.ArrayList; +import java.util.List; + +public class GenerateLinkConditionsVisitor implements + ODRulesVisitor2 { + + private List links = new ArrayList<>(); + private ASTODDefinition def; + + + @Override + public void visit(ASTODDefinition node){ + def = node; + } + + public List getLinkConditions() { + return links; + } + + @Override + public void visit(ASTODLink node) { + String leftObjectName = Names.constructQualifiedName(node.getLeftReferenceName(0).getPartsList()); + String rightObjectName = Names.constructQualifiedName(node.getRightReferenceName(0).getPartsList()); + ASTLinkCondition linkCondition = ODRuleGenerationMill.linkConditionBuilder().uncheckedBuild(); + linkCondition.setObjectName(leftObjectName); + ASTLinkCondition secondLinkCondition = ODRuleGenerationMill.linkConditionBuilder().uncheckedBuild(); + secondLinkCondition.setObjectName(rightObjectName); + // it's always a composition in the new grammar + //if (node.isComposition()) { + linkCondition.setLinktype("composition"); + secondLinkCondition.setLinktype("composition"); + // if it is a composition the left side is the parent of the right + if (node.isPresentRightRole()) { + + if (node.isAttributeIterated()) { + secondLinkCondition.setConditionString("return new ArrayList(" + leftObjectName + "_cand.get" + Util.makeSingular(node.getRightRole()) + "List());"); + } else if(node.isAttributeOptional()) { + secondLinkCondition.setConditionString("ArrayList list = new ArrayList();\n" + + "if (" + leftObjectName + "_cand.isPresent" + StringTransformations.capitalize(node.getRightRole()) + "()) {\n" + + " list.add(" + leftObjectName + "_cand.get" + StringTransformations.capitalize(node.getRightRole()) + "());\n" + + "}\n" + + " return list;"); + + } else { + secondLinkCondition.setConditionString("ArrayList list = new ArrayList();\n" + + "if (" + leftObjectName + "_cand.get" + StringTransformations.capitalize(node.getRightRole()) + "() != null) {\n" + + " list.add(" + leftObjectName + "_cand.get" + StringTransformations.capitalize(node.getRightRole()) + "());\n" + + "}\n" + + " return list;"); + } + ASTDependency dependency = ODRuleGenerationMill.dependencyBuilder().uncheckedBuild(); + dependency.setContent(leftObjectName); + secondLinkCondition.setDependency(dependency); + + } + /*} else { + if (!node.getName().get().isEmpty()) { + linkCondition.setLinktype("link"); + secondLinkCondition.setLinktype("link"); + linkCondition.setConditionString(StringTransformations.uncapitalize(node.getName().get()) + ".getLeftFromRight(" + rightObjectName + "_cand" + ")"); + linkCondition.setDependency(ODRuleGenerationNodeFactory.createASTDependency(rightObjectName)); + secondLinkCondition.setConditionString(StringTransformations.uncapitalize(node.getName().get()) + ".getRightFromLeft(" + leftObjectName + "_cand" + ")"); + secondLinkCondition.setDependency(ODRuleGenerationNodeFactory.createASTDependency(leftObjectName)); + } + }*/ + if (secondLinkCondition.getConditionString() != null && !secondLinkCondition.getConditionString().isEmpty()) { + links.add(secondLinkCondition); + } + + if (linkCondition.getConditionString() != null && !linkCondition.getConditionString().isEmpty()) { + links.add(linkCondition); + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/tf/odrules/HierarchyHelper.java b/monticore-grammar/src/main/java/de/monticore/tf/odrules/HierarchyHelper.java new file mode 100644 index 0000000000..329f7f8e24 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/tf/odrules/HierarchyHelper.java @@ -0,0 +1,745 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.odrules; + +import de.monticore.tf.odrulegeneration._ast.*; +import de.monticore.tf.odrules._ast.ASTODDefinition; +import de.monticore.tf.odrules._ast.ASTODInnerLink; +import de.monticore.tf.odrules._ast.ASTODObject; +import de.monticore.tf.odrules._ast.ASTODRule; +import de.monticore.tf.odrules.util.ODRuleStereotypes; +import de.monticore.tf.odrules.util.Util; + +import java.util.*; +import java.util.stream.Collectors; + +public final class HierarchyHelper { + private final static String optionalType = "de.monticore.tf.ast.IOptional"; + private final static String listType = "de.monticore.tf.ast.IList"; + + private ASTODRule rule; + private ASTODDefinition lhs; + private Optional rhs = Optional.empty(); + + private Map> listChildPairs = new HashMap<>(); + private Map> listChildPairsLhs = new HashMap<>(); + private Map> listChildPairsWithOptionals = new HashMap<>(); + + private List listChildNames = new ArrayList<>(); + private List listChildNamesLhs = new ArrayList<>(); + private List listChildNamesRhs = new ArrayList<>(); + + private String packageName = ""; + private final List customImports = new ArrayList<>(); + + public HierarchyHelper() { + rule = ODRulesMill.oDRuleBuilder().uncheckedBuild(); + lhs = ODRulesMill.oDDefinitionBuilder().uncheckedBuild(); + } + + public HierarchyHelper(ASTODRule astodRule) { + rule = astodRule; + lhs = rule.getLhs(); + rhs = rule.isPresentRhs() ? Optional.of(rule.getRhs()) : Optional.empty(); + listChildNames = new ArrayList<>(); + listChildNamesLhs = new ArrayList<>(); + listChildNamesRhs = new ArrayList<>(); + // Calculate ListChildPairs for the LHS + listChildPairs = getListChildPairs(lhs.getODObjectList()); + listChildPairsLhs = getListChildPairs(lhs.getODObjectList()); + for (String key : listChildPairs.keySet()) { + listChildNamesLhs.addAll(listChildPairs.get(key)); + } + + listChildPairsWithOptionals = getListChildPairsWithOptionals(lhs.getODObjectList()); + Map> rhsListChildPairs = rhs.isPresent() ? + getListChildPairs(rhs.get().getODObjectList()) : new HashMap<>(); + for (String key : rhsListChildPairs.keySet()) { + // Every list on the lhs is also on the rhs + // If there are objects to create in a list put them to the Map + if (!listChildPairs.get(key).containsAll(rhsListChildPairs.get(key))) { + List temporary = rhsListChildPairs.get(key); + // No duplicates + temporary.removeAll(listChildPairs.get(key)); + temporary.addAll(listChildPairs.get(key)); + listChildPairs.put(key, temporary); + } + listChildNamesRhs.addAll(rhsListChildPairs.get(key)); + } + // Fill names for quick check + for (String key : listChildPairs.keySet()) { + listChildNames.addAll(listChildPairs.get(key)); + } + } + + /** + * Checks whether the object with the given name is a list object. + * + * @param objectName the name of the object + * @return whether it is a list + */ + public boolean isListObject(String objectName) { + + // try to resolve it by the left-hand side first + ASTODObject obj = Util.getODObject(lhs, objectName); + if (obj == null) { + // and then by the right-hand side + if (rhs.isPresent()) { + obj = Util.getODObject(rhs.get(), objectName); + if (obj == null) { + return false; + } + } + } + return obj.hasStereotype(ODRuleStereotypes.LIST); + } + + /** + * Get package name for code generation + * + * @return + */ + public String getPackageName() { + return packageName; + } + + /** + * Set package name for code generation + * + * @param packageName + */ + public void setPackageName(String packageName) { + this.packageName = packageName; + } + + /** + * Check if package name for code generation is set + * + * @return + */ + public boolean packageisPresentName() { + return !"".equals(packageName); + } + + /** + * Get all custom imports for code generation + * + * @return + */ + public List getCustomImports() { + return customImports; + } + + /** + * Add custom import for code generation + * + * @param customImport + */ + public void addCustomImports(String customImport) { + customImports.add(customImport); + } + + /** + * Calculates all Lists and their children, saves also all children seperately + * + * @param objects List of objects to be checked for Lists + * @return A mapping for each list to their children + */ + private Map> getListChildPairs( + List objects) { + Map> result = new HashMap<>(); + List childs; + List innerObjects; + // Search for every List in the given Objects + for (ASTODObject object : objects) { + if (object.isPresentType() && + Util.printType(object.getType()) + .equals(listType)) { + childs = getListChilds(object); + // If there is a name for the list, save it and the childnames of the + // list + if (object.isPresentName()) { + result.put(object.getName(), childs); + } + } + innerObjects = new ArrayList<>(); + for (ASTODInnerLink link : object.getInnerLinksList()) { + innerObjects.add(link.getODObject()); + } + // Do it also for the InnerObjects of the current Object + result.putAll(getListChildPairs(innerObjects)); + } + return result; + } + + private Map> getListChildPairsWithOptionals( + List objects) { + Map> result = new HashMap<>(); + List childs; + List innerObjects; + // Search for every List in the given Objects + for (ASTODObject object : objects) { + if (object.isPresentType() && + Util.printType(object.getType()) + .equals(listType)) { + childs = getListChildsWithOptionals(object); + // If there is a name for the list, save it and the childnames of the + // list + if (object.isPresentName()) { + result.put(object.getName(), childs); + } + } + innerObjects = new ArrayList<>(); + for (ASTODInnerLink link : object.getInnerLinksList()) { + innerObjects.add(link.getODObject()); + } + // Do it also for the InnerObjects of the current Object + result.putAll(getListChildPairsWithOptionals(innerObjects)); + } + return result; + } + + /** + * Calculates all inner objects of a given object + * + * @param obj The list from which we want the childs. + * @param allMatches The list of all LHS match elements. + * @return a List of the names of all (direct) childs from the list. + */ + public List getInnerLinkObjectsLHS(List allMatches, + ASTMatchingObject obj) { + ArrayList innerObjects = new ArrayList<>(); + + for (String innerObjectName : obj.getInnerLinkObjectNamesList()) { + + Optional innerLinkObject = allMatches.stream() + .filter(m -> m.getObjectName().equals(innerObjectName)).findAny(); + if (innerLinkObject.isPresent()) { + innerObjects.add(innerLinkObject.get()); + } + } + + return innerObjects; + } + + /** + * Calculates all childs from the given list and regards an optional as + * another list + * + * @param list The list from which we want the childs. + * @return a List of the names of all (direct) childs from the list. + */ + private List getListChilds(ASTODObject list) { + List result = new ArrayList<>(); + // For every direct child calculate the name + for (ASTODInnerLink link : list.getInnerLinksList()) { + ASTODObject object = link.getODObject(); + // Do it recursively for each Optional or look if the name is present and + // save it + if (object.isPresentType() && + Util.printType(object.getType()) + .equals(optionalType)) { + result.addAll(getListChilds(object)); + } else if (object.isPresentName()) { + result.add(object.getName()); + } + } + return result; + } + + /** + * Calculates all childs from the given list and includes optionals + * + * @param list The list from which we want the childs. + * @return a List of the names of all (direct) childs from the list. + */ + private List getListChildsWithOptionals(ASTODObject list) { + List result = new ArrayList<>(); + // For every direct child calculate the name + for (ASTODInnerLink link : list.getInnerLinksList()) { + ASTODObject object = link.getODObject(); + // Do it recursively for each Optional or look if the name is present and + // save it + if (object.isPresentName()) { + result.add(object.getName()); + } + + } + return result; + } + + /** + * Removes every mandatory object. + * + * @param allObjects list of all objects + * @return a list of all found list objects + */ + public List getListObjects(List allObjects) { + ArrayList mandatoryObjects = allObjects.stream() + .filter(c -> c.isListObject()).collect(Collectors.toCollection(ArrayList::new)); + return mandatoryObjects; + } + + /** + * Checks if the given object is in a list. + * + * @param object The object to be checked + * @return If the object is in a list + */ + public boolean isListChild(ASTMatchingObject object) { + return listChildNames.contains(object.getObjectName()); + } + + public boolean isListChild(String objectname) { + return listChildNames.contains(objectname); + } + + public boolean isLhsListChild(String objectname) { + return listChildNamesLhs.contains(objectname); + } + + public boolean isLhsListChild(ASTMatchingObject object) { + return listChildNamesLhs.contains(object.getObjectName()); + } + + public boolean isRhsListChild(String objectname) { + return listChildNamesRhs.contains(objectname); + } + + public boolean isRhsListChild(ASTMatchingObject object) { + return listChildNamesRhs.contains(object.getObjectName()); + } + + /** + * Removes every optional and listChild match-object. + * + * @param allMatches list of all match-objects + * @return a list of match-objects without optional and listChild objects + */ + public List getMandatoryObjectsWithoutOptAndListChilds( + List allMatches) { + ArrayList mandatoryObjects = allMatches.stream() + .filter(c -> !c.isOptObject() && !isWithinListStructure(c.getObjectName())) + .collect(Collectors.toCollection(ArrayList::new)); + for (ASTMatchingObject object : allMatches) { + if (object.isListObject() || object.getType().endsWith("IList")) { + for (int i = 0; i <= object.getInnerLinkObjectNamesList().size(); i++) { + int index = allMatches.indexOf(object); + if (allMatches.get(index + i).isOptObject() || allMatches.get(index + i).getType().endsWith("IOptional")) { + for (String innerLinkName : allMatches.get(index + i).getInnerLinkObjectNamesList()) { + for (Iterator it = mandatoryObjects.iterator(); it.hasNext(); ) { + ASTMatchingObject mandatoryObject = it.next(); + if (mandatoryObject.getObjectName().equals(innerLinkName)) { + it.remove(); + } + } + } + } + } + } + if (object.isOptObject() || object.getType().endsWith("IOptional")) { + for (int i = 0; i <= object.getInnerLinkObjectNamesList().size(); i++) { + int index = allMatches.indexOf(object); + if (allMatches.get(index + i).isListObject() || allMatches.get(index + i).getType().endsWith("IList")) { + for (String innerLinkName : allMatches.get(index + i).getInnerLinkObjectNamesList()) { + for (Iterator it = mandatoryObjects.iterator(); it.hasNext(); ) { + ASTMatchingObject mandatoryObject = it.next(); + if (mandatoryObject.getObjectName().equals(innerLinkName)) { + it.remove(); + } + } + } + } + } + } + } + return mandatoryObjects; + } + + /** + * Removes every listChild match-object. + * + * @param allMatches list of all match-objects + * @return a list of match-objects without listChild objects + */ + public List getMandatoryObjectsWithoutListChilds( + List allMatches) { + ArrayList mandatoryObjects = allMatches.stream() + .filter(c -> !isWithinListStructure(c.getObjectName())) + .collect(Collectors.toCollection(ArrayList::new)); + return mandatoryObjects; + } + + /** + * Removes every optional and listChild match-object. + * + * @param allObjects list of all match-objects + * @return a list of match-objects without optional and listChild objects + */ + public List getListChilds(List allObjects, + ASTMatchingObject list) { + ArrayList mandatoryObjects = allObjects.stream() + .filter(c -> listChildPairsLhs.get(list.getObjectName()).contains(c.getObjectName())) + .collect(Collectors.toCollection(ArrayList::new)); + return mandatoryObjects; + } + + /** + * Gives all child objects from a given list of objects, including optionals + * + * @param allobjects the objects to search in + * @return all objects in lists in the given objects + */ + public List getListChildsWithOptionals(List allobjects, + ASTMatchingObject list) { + ArrayList mandatoryObjects = allobjects.stream() + .filter( + c -> listChildPairsWithOptionals.get(list.getObjectName()).contains(c.getObjectName())) + .collect(Collectors.toCollection(ArrayList::new)); + return mandatoryObjects; + } + + /** + * Gives all objects in a List in the given list of objects, besides optionals + * + * @param allobjects the objects to search in + * @return all objects in lists in the given objects + */ + public List getListChilds(List allobjects) { + ArrayList mandatoryObjects = allobjects.stream() + .filter(c -> isWithinListStructure(c.getObjectName()) && !c.getType().equals(optionalType) + && !c.getType().equals(listType)) + .collect(Collectors.toCollection(ArrayList::new)); + return mandatoryObjects; + } + + /** + * Gives all objects in a List in the given list of objects + * + * @param allobjects the objects to search in + * @return all objects in lists in the given objects + */ + public List getListChildsWithOptionals(List allobjects) { + ArrayList mandatoryObjects = allobjects.stream() + .filter(c -> isWithinListStructure(c.getObjectName()) && !c.getType().equals(listType)) + .collect(Collectors.toCollection(ArrayList::new)); + return mandatoryObjects; + } + + /** + * Gives the ListStructure Name the given objectName lies in + * + * @param object The name of the object + * @return The name of the list Structure which contains the object + */ + public String getListParent(String object) { + for (String key : listChildPairs.keySet()) { + if (listChildPairs.get(key).contains(object)) { + return key; + } + } + return null; + } + + /** + * Method to get the Treepath the object lies in. + * + * @param object The Object for which we want to find a path in the + * List-Structure Tree + * @return A List starting with the root-list and ending with the list + * containing the given Object. + */ + public List getListTree(String object) { + List result = new ArrayList<>(); + if (this.isListChild(object)) { + for (String key : listChildPairs.keySet()) { + if (listChildPairs.get(key).contains(object)) { + result.add(key); + result.addAll(getListTree(key)); + return result; + } + } + } + return result; + } + + /** + * Gives the Object to a Name in a given List of Objects + * + * @param allObjects The List of objects where the object should be found in. + * @param name The given objectname + * @return The object which has the given name, null if no object was found + */ + public ASTMatchingObject getObjectByName(List allObjects, String name) { + for (ASTMatchingObject obj : allObjects) { + if (obj.getObjectName().equals(name)) { + return obj; + } + } + return null; + } + + /** + * Removes every Change object with the Optional type. + * + * @param allChanges list of all Change objects + * @return a list of Changes without optional objects + */ + public List getMandatoryChangeObjects(List allChanges) { + ArrayList mandatoryObjects = allChanges.stream() + .filter(c -> !c.getType().equals(optionalType) && !c.getType().equals(listType)) + .collect(Collectors.toCollection(ArrayList::new)); + return mandatoryObjects; + } + + /** + * Removes every Change object with the Optional type and the list type. + * + * @param allChanges list of all Change objects + * @return a list of Changes without optional objects and list objects + */ + public List getMandatoryChangeObjectsNoList(List allChanges) { + ArrayList mandatoryObjects = allChanges.stream() + .filter(c -> !c.getType().equals(optionalType) && !c.getType().equals(listType)) + .collect(Collectors.toCollection(ArrayList::new)); + return mandatoryObjects.stream().filter(a -> !isWithinListStructure(a.getObjectName())).collect(Collectors.toCollection(ArrayList::new)); + } + + public List getChangeObjectsWhithoutCreate(ASTReplacement replacements) { + Set createStrings = replacements.getCreateObjectsList().stream().map(ASTCreateOperation::getName).collect(Collectors.toSet()); + return replacements.getChangesList().stream().filter(m -> !createStrings.contains(m.getObjectName())).collect(Collectors.toCollection(ArrayList::new)); + } + + /** + * Only change objects with the list type. + * + * @param allChanges list of all Change objects + * @return a list of Changes, which are list objects + */ + public List getMandatoryChangeObjectsOnlyList(List allChanges) { + ArrayList mandatoryObjects = allChanges.stream() + .filter(c -> !c.getType().equals(optionalType) && !c.getType().equals(listType)) + .filter(a -> isWithinListStructure(a.getObjectName())) + .collect(Collectors.toCollection(ArrayList::new)); + return mandatoryObjects; + } + + /** + * Removes every Delete object with the Optional type. + * + * @param allDeletes list of all Delete objects + * @return a list of Deletes without optional objects + */ + public List getMandatoryDeleteObjects(List allDeletes) { + ArrayList mandatoryObjects = allDeletes.stream() + .filter(d -> !d.getType().equals(optionalType) && !d.getType().equals(listType)) + .collect(Collectors.toCollection(ArrayList::new)); + return mandatoryObjects; + } + + /** + * Removes every optional Match object. + * + * @param allMatches list of all Match objects + * @return a list of Matches without optional objects + */ + public List getMandatoryMatchObjects(List allMatches) { + ArrayList mandatoryObjects = allMatches.stream() + .filter(c -> !c.isOptObject()).collect(Collectors.toCollection(ArrayList::new)); + return mandatoryObjects; + } + + /** + * Removes every optional and list match object. + * + * @param allMatches list of all Match objects + * @return a list of Matches without optional and list objects + */ + public List getMandatoryObjectsWithoutOptList( + List allMatches) { + ArrayList mandatoryObjects = allMatches.stream() + .filter(c -> !c.isListObject() && !c.isOptObject()) + .collect(Collectors.toCollection(ArrayList::new)); + return mandatoryObjects; + } + + /** + * Removes every mandatory Match object. + * + * @param allMatches list of all Match objects + * @return a list of optional Match objects + */ + public List getOptionalMatchObjects(List allMatches) { + ArrayList mandatoryObjects = + allMatches.stream().filter(c -> (c.isOptObject() || c.getType().endsWith("IOptional"))) + .collect(Collectors.toCollection(ArrayList::new)); + return mandatoryObjects; + } + + /** + * Checks if one of the InnerLinkObjects is an Optional object + * + * @param matches list of matches to check + * @param linkName the name of the object to look for + * @return true, if the object with the given name is no optional + */ + public boolean isNoOptionalName(List matches, String linkName) { + for (ASTMatchingObject linkObject : matches) { + if (linkObject.getObjectName().equals(linkName) && linkObject.isOptObject()) { + return false; + } + } + return true; + } + + /** + * Removes every normal object. + * + * @param allObjects list of all Match-objects + * @return a list of optional Match objects + */ + public List getOptListObjects(List allObjects) { + ArrayList mandatoryObjects = allObjects.stream() + .filter(c -> c.isOptObject() || c.isListObject()) + .collect(Collectors.toCollection(ArrayList::new)); + return mandatoryObjects; + } + + /** + * Checks if the object with the given name is within an optional structure. + * + * @param objectName the object name + * @return whether the object is within an optional structure + */ + public boolean isWithinOptionalStructure(String objectName) { + if (isWithinStructure(lhs, objectName, ODRuleStereotypes.OPTIONAL)) { + return true; + } else if (rhs.isPresent() && isWithinStructure(rhs.get(), objectName, ODRuleStereotypes.OPTIONAL)) { + return true; + } + return false; + } + + /** + * Checks if the object with the given name is within an negative structure + * (not[[..]]-element). + * + * @param objectName the object name + * @return whether the object is within a negative structure + */ + public boolean isWithinNegativeStructure(String objectName) { + if (isWithinNegativeStructure(lhs, objectName)) { + return true; + } else if (rhs.isPresent() && isWithinNegativeStructure(rhs.get(), objectName)) { + return true; + } + return false; + } + + public boolean isWithinListStructure(String objectName) { + return isWithinStructure(lhs, objectName, ODRuleStereotypes.LIST); + } + + /** + * Checks if the object with the given name is within an structure with the + * given stereotype. + * + * @param definition the ASTODDefinition (lhs or rhs) + * @param objectName the object name + * @param stereotype the structure type + * @return whether the object is within a structure + */ + private boolean isWithinStructure(ASTODDefinition definition, String objectName, + String stereotype) { + // check if the object is a direct child of the definition + // because that means it is not within any hierarchical structure + if (isDirectChild(definition, objectName)) { + return false; + } else { + // get every structure with the given stereotype + ArrayList structures = Util.getAllODObjects(definition).stream() + .filter(odObj -> odObj.hasStereotype(stereotype)) + .collect(Collectors.toCollection(ArrayList::new)); + // get every structure without the given stereotype + ArrayList notStructures = Util.getAllODObjects(definition).stream() + .filter(odObj -> !odObj.hasStereotype(stereotype)) + .collect(Collectors.toCollection(ArrayList::new)); + + // check if the object is a child of any relevant structure + for (ASTODObject structure : structures) { + if (isChild(structure, objectName)) { +/* if (stereotype.equals(ODRuleStereotypes.LIST)) { + for (ASTODObject notStructure : notStructures) { + if (isChild(notStructure, objectName)) { + return false; + } + } + }*/ + return true; + } + } + } + return false; + } + + /** + * Checks if the object with the given name is within a structure with the + * stereotype not. Implementation is slightly different than for other + * stereotypes as no abstract parent node is created for not. + * + * @param definition the ASTODDefinition (lhs or rhs) + * @param objectName the object name + * @return whether the object is within a structure + */ + private boolean isWithinNegativeStructure(ASTODDefinition definition, String objectName) { + // get every structure with the given stereotype + ArrayList structures = Util.getAllODObjects(definition).stream() + .filter(odObj -> odObj.hasStereotype(ODRuleStereotypes.NOT)) + .collect(Collectors.toCollection(ArrayList::new)); + + // check if the object is a child of any relevant structure + for (ASTODObject structure : structures) { + if (isChild(structure, objectName)) { + return true; + } else if (structure.isPresentName()) { + if (structure.getName().equals(objectName)) { + return true; + } + } + } + return false; + } + + /** + * Checks if the ODDefinition has a direct child with the given name. + * + * @param def the ODDefinition + * @param childName the ODObject name + * @return whether the object is a direct child of the definition + */ + private boolean isDirectChild(ASTODDefinition def, String childName) { + for (ASTODObject directChild : def.getODObjectList()) { + if (directChild.isPresentName() && directChild.getName().equals(childName)) { + return true; + } + } + return false; + } + + /** + * Checks if the given object is a child of the given parent object. + * + * @param parent the parent ODObject + * @param childName the child ODObject + * @return whether the object is a child of the definition + */ + private boolean isChild(ASTODObject parent, String childName) { + // getODObject performs a depth-first search if necessary + ASTODObject result = Util.getODObject(parent, childName); + return (result != null && !result.deepEquals(parent)); + } + + public static ASTMatchingObject getMatchingObject(List allObjects, String name) { + return allObjects.stream().filter(o -> o.getObjectName().equals(name)).findFirst().orElse(null); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/tf/odrules/ODBuildOrder.java b/monticore-grammar/src/main/java/de/monticore/tf/odrules/ODBuildOrder.java new file mode 100644 index 0000000000..71f317be50 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/tf/odrules/ODBuildOrder.java @@ -0,0 +1,103 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.odrules; + +import de.monticore.tf.odrules._ast.ASTODLink; +import de.monticore.tf.odrules._ast.ASTODObject; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; + +public class ODBuildOrder { + + public class ObjectTrees { + private HashMap nodes = new HashMap<>(); + + public class ObjectNode { + private ObjectNode parent; + private List childs = new LinkedList<>(); + private ASTODObject object; + private boolean visited = false; + private List attrs = new LinkedList<>(); + + public ObjectNode(ASTODObject object) { + this.object = object; + } + + } + + public void addObjects(List objects) { + for(ASTODObject o : objects) { + if(!nodes.containsKey(o.getName())) { + ObjectNode node = new ObjectNode(o); + nodes.put(o.getName(), node); + } + } + } + + public void addLinks(List links){ + for(ASTODLink l : links) { + String parent = l.getLeftReferenceName(0).toString(); + String child = l.getRightReferenceName(0).toString(); + ObjectNode parentNode = nodes.get(parent); + ObjectNode childNode = nodes.get(child); + if (parentNode != null) { + if(childNode != null) { + parentNode.childs.add(childNode); + } + parentNode.attrs.add(l); + } + if (childNode != null) { + childNode.parent = parentNode; + } + } + } + } + + private ObjectTrees trees = new ObjectTrees(); + private List buildOrder = new LinkedList<>(); + + + public ODBuildOrder(List objects, List links) { + trees.addObjects(objects); + trees.addLinks(links); + calculateBuildOrder(); + } + + public HashMap> getBuildAttrs() { + HashMap> buildAttrs = new HashMap<>(); + + for(ASTODObject o : buildOrder) { + buildAttrs.put(o, trees.nodes.get(o.getName()).attrs); + } + + return buildAttrs; + } + + public List getBuildOrder() { + return buildOrder; + } + + public void calculateBuildOrder() { + for(ObjectTrees.ObjectNode node : trees.nodes.values()) { + if(!node.visited) { + ObjectTrees.ObjectNode localRoot = getRoot(node); + calculateBuildOrder(localRoot); + } + } + } + + private void calculateBuildOrder(ObjectTrees.ObjectNode node) { + for(ObjectTrees.ObjectNode c : node.childs) { + calculateBuildOrder(c); + } + buildOrder.add(node.object); + node.visited = true; + } + + private ObjectTrees.ObjectNode getRoot(ObjectTrees.ObjectNode node) { + return node.parent == null ? node : getRoot(node.parent); + } + + +} diff --git a/monticore-grammar/src/main/java/de/monticore/tf/odrules/ODRuleCodeGenerator.java b/monticore-grammar/src/main/java/de/monticore/tf/odrules/ODRuleCodeGenerator.java new file mode 100644 index 0000000000..0dfba18124 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/tf/odrules/ODRuleCodeGenerator.java @@ -0,0 +1,770 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.odrules; + +import com.google.common.collect.Lists; +import de.monticore.expressions.commonexpressions._ast.ASTBooleanAndOpExpression; +import de.monticore.expressions.commonexpressions._ast.ASTBracketExpression; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.generating.GeneratorEngine; +import de.monticore.generating.GeneratorSetup; +import de.monticore.generating.templateengine.GlobalExtensionManagement; +import de.monticore.literals.mcliteralsbasis._ast.ASTLiteral; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.statements.mcarraystatements._ast.ASTArrayInit; +import de.monticore.statements.mccommonstatements._ast.ASTMCJavaBlock; +import de.monticore.statements.mcvardeclarationstatements._ast.ASTVariableInit; +import de.monticore.types.mcbasictypes._ast.ASTMCImportStatement; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.se_rwth.commons.Joiners; +import de.se_rwth.commons.Names; +import de.se_rwth.commons.StringTransformations; +import de.monticore.tf.odrulegeneration.ODRuleGenerationMill; +import de.monticore.tf.odrulegeneration._ast.*; +import de.monticore.tf.odrules._ast.*; +import de.monticore.tf.odrules._visitor.ODRulesTraverser; +import de.monticore.tf.odrules.subConstraints.*; +import de.monticore.tf.odrules.util.ODRuleStereotypes; +import de.monticore.tf.odrules.util.TFExpressionFullPrettyPrinter; +import de.monticore.tf.odrules.util.Util; +import de.monticore.tf.rule2od.Variable2AttributeMap; + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; +import java.util.stream.Collectors; + +public class ODRuleCodeGenerator { + // contains all the data for the code generator + private ASTTransformationStructure root; + private String filename; + private ASTODDefinition lhs; + private ASTODDefinition rhs; + private Variable2AttributeMap var2attr = new Variable2AttributeMap(); + private HierarchyHelper hierarchyHelper; + private List lhsObjects; + //Same assignments as in the parsedModel, but without "m." prefixes + private List modifiedAssignments; + + protected ODRuleCodeGenerator() { + } + + public static void generate(ASTODRule parsedModel, File targetDir) { + String packageName = "de.monticore.tf"; + if (!parsedModel.getPackageList().isEmpty()) { + packageName = Names.constructQualifiedName(parsedModel.getPackageList()); + } + generate(parsedModel, targetDir, packageName); + } + + public static void generate(ASTODRule parsedModel, File targetDir, String packageName) { + generate(parsedModel, new GlobalExtensionManagement(), targetDir, parsedModel.getName(), packageName); + } + + public static void generate(ASTODRule parsedModel, GlobalExtensionManagement glex, File targetDir, String fileName) { + generate(parsedModel, glex, targetDir, fileName, ""); + } + + public static void generate(ASTODRule parsedModel, GlobalExtensionManagement glex, File targetDir, String fileName, String packageName) { + List subConstraints = new ArrayList<>(); + ODRuleCodeGenerator odRuleCodeGenerator = initOdRuleCodeGenerator(parsedModel, fileName); + + if (parsedModel.isPresentConstraint()) { + ASTExpression constraint = parsedModel.getConstraint(); + subConstraints = odRuleCodeGenerator.findSubConstraints(constraint); + } + glex.setGlobalValue("subConstraints", subConstraints); + glex.setGlobalValue("dependVars", getAllDependVars(subConstraints)); + odRuleCodeGenerator.hierarchyHelper.setPackageName(packageName); + + for (ASTMCImportStatement d : parsedModel.getMCImportStatementList()) { + odRuleCodeGenerator.hierarchyHelper.addCustomImports(Util.printImportDeclaration(d)); + } + // Import the mill + List millImportList = new ArrayList<>(parsedModel.getGrammarPackageList()); + millImportList.add(parsedModel.getGrammarName().toLowerCase()); + millImportList.add(parsedModel.getGrammarName() + "Mill"); + String p = Joiners.DOT.join(millImportList); + if(p.startsWith(".")){ + p = p.substring(1); + } + odRuleCodeGenerator.hierarchyHelper.addCustomImports("import " + p + ";"); + glex.setGlobalValue("hierarchyHelper", odRuleCodeGenerator.hierarchyHelper); + glex.setGlobalValue("grammarName", parsedModel.getGrammarName()); + + + final GeneratorSetup setup = new GeneratorSetup(); + setup.setOutputDirectory(targetDir); + setup.setGlex(glex); + + final GeneratorEngine generator = new GeneratorEngine(setup); + ASTTransformationStructure tfStructure = odRuleCodeGenerator.generateASTTransformationStructure(parsedModel); + + final Path filePath = Paths.get(Names.getPathFromPackage(packageName), fileName + ".java"); + generator.generate("de.monticore.tf.odrules.TransformationUnit", filePath, tfStructure); + } + + private static ODRuleCodeGenerator initOdRuleCodeGenerator(ASTODRule parsedModel, String filename) { + final ODRuleCodeGenerator odRuleCodeGenerator = new ODRuleCodeGenerator(); + + odRuleCodeGenerator.filename = filename; + odRuleCodeGenerator.lhs = parsedModel.getLhs(); + odRuleCodeGenerator.rhs = parsedModel.isPresentRhs() ? parsedModel.getRhs() : ODRulesMill.oDDefinitionBuilder().uncheckedBuild(); + odRuleCodeGenerator.lhsObjects = odRuleCodeGenerator.generateLHSObjectList(parsedModel); + odRuleCodeGenerator.hierarchyHelper = new HierarchyHelper(parsedModel); + if (parsedModel.getVariables() != null) { + odRuleCodeGenerator.var2attr = parsedModel.getVariables(); + } + odRuleCodeGenerator.modifiedAssignments = odRuleCodeGenerator.fixAssignments(parsedModel); + + return odRuleCodeGenerator; + } + + public ASTTransformationStructure generateASTTransformationStructure(ASTODRule ast) { + hierarchyHelper = new HierarchyHelper(ast); + ASTTransformationStructureBuilder tsBuilder = ODRuleGenerationMill.transformationStructureBuilder(); + ASTPatternBuilder patternBuilder = ODRuleGenerationMill.patternBuilder(); + + if (!ast.getPackageList().isEmpty()) { + tsBuilder.setPackage(Names.getQualifiedName(ast.getPackageList())); + } else { + tsBuilder.setPackage("de.monticore.tf"); + } + tsBuilder.setClassname(getClassName()); + + patternBuilder.setMatchingObjectsList(generateObjectList(ast)); + patternBuilder.setObjectConditionsList(generateObjectConditions(ast)); + patternBuilder.setLinkConditionsList(generateLinkConditions(ast)); + patternBuilder.setAssocList(generateAssociations(ast)); + patternBuilder.setTypesList(generateTypesList(ast)); + patternBuilder.setLHSObjectsList(lhsObjects); + tsBuilder.setPattern(patternBuilder.build()); + List vars = generateVariables(ast); + tsBuilder.setConstraintExpression(generateConstraintExpression(ast)); + tsBuilder.setDoStatement(generateDoStatement(ast)); + tsBuilder.setUndoStatement(generateUndoStatement(ast)); + tsBuilder.setAssignmentsList(generateAssignments()); + + tsBuilder.setVariablesList(vars); + tsBuilder.setReplacement(generateReplacement(new DifferenceFinder(hierarchyHelper).getDifference(ast))); + tsBuilder.setFoldingHash(generateFoldingHash(ast)); + return tsBuilder.build(); + } + + protected String generateDoStatement(ASTODRule ast) { + if (ast.isPresentDoBlock()) { + ASTMCJavaBlock doblock = ast.getDoBlock(); + if (doblock == null) { + return ""; + } + return Util.print(doblock, lhsObjects, this.hierarchyHelper); + } else { + return ""; + } + } + + protected String generateUndoStatement(ASTODRule ast) { + if (ast.isPresentUndoBlock()) { + ASTMCJavaBlock undoblock = ast.getDoBlock(); + if (undoblock == null) { + return ""; + } + String undoStatement = Util.print(undoblock, lhsObjects, this.hierarchyHelper); + if(!ast.isPresentDoBlock()) + undoStatement += "\n Log.warn(\"UndoBlock was present and undoReplacement was executed, but there was no DoBlock\");"; + return undoStatement; + } else if(ast.isPresentDoBlock()) { + return "Log.warn(\"DoBlock was present and undoReplacement was executed, but there is no UndoBlock\");"; + } else { + return ""; + } + } + + protected List generateAssignments() { + + List result = new ArrayList<>(); + + for (ASTAssignment exp : modifiedAssignments) { + ODRulesTraverser traverser = ODRulesMill.inheritanceTraverser(); + + FindOptionalsVisitor findOptVisitor = new FindOptionalsVisitor(lhsObjects, hierarchyHelper); + traverser.add4ExpressionsBasis(findOptVisitor); + + exp.getRhs().accept(traverser); + + traverser = ODRulesMill.inheritanceTraverser(); + AddAffixesToAssignmentsVisitor addAffixVisitor = new AddAffixesToAssignmentsVisitor(lhsObjects, hierarchyHelper, exp.getRhs()); + traverser.add4ExpressionsBasis(addAffixVisitor); + traverser.add4CommonExpressions(addAffixVisitor); + + exp.getRhs().accept(traverser); + exp.setRhs(addAffixVisitor.rootExp); + + StringBuilder stringbuilder = new StringBuilder(); + IndentPrinter iPrinter = new IndentPrinter(stringbuilder); + TFExpressionFullPrettyPrinter p = new TFExpressionFullPrettyPrinter(iPrinter); + + p.prettyprint(exp.getRhs()); + iPrinter.flushBuffer(); + + + /** + * This code inserts isPresent()-checks into assignments that contain optional variables. + * + * Let $O be an optional variable in this assignment: $name = $O.getName(). + * In case that no value is present for $O, the evaluation of this assignment would produce a NoSuchElementException. + * In order to prevent this we add an isPresent()-check to the expression like this: $O.isPresent ? $O.getName() : "undef" + */ + if (!findOptVisitor.optVars.isEmpty()) { + + stringbuilder.insert(0, "()?"); + stringbuilder.insert(stringbuilder.length(), ":\"undef\""); + Iterator optVarsIterator = findOptVisitor.optVars.iterator(); + + while (optVarsIterator.hasNext()) { + ASTMatchingObject o = optVarsIterator.next(); + String condition = "m." + o.getObjectName() + ".isPresent()"; + if (optVarsIterator.hasNext()) { + condition = "&&" + condition; + } + stringbuilder.insert(1, condition); + } + } + + result.add(exp.getLhs() + " =" + stringbuilder.toString() + ";"); + } + return result; + + } + + /** + * Iterates over all conjugated boolean expressions and calculates ODSbuConstraints for each. + * + * @return the set of all ODSubConstraints calculated in this process.. + */ + private List findSubConstraints(ASTExpression constrExpr) { + List subConstraints = new ArrayList<>(); + + if (constrExpr instanceof ASTBooleanAndOpExpression) { + ASTBooleanAndOpExpression booleanAndOp = (ASTBooleanAndOpExpression) constrExpr; + subConstraints.addAll(findSubConstraints(booleanAndOp.getLeft())); + subConstraints.addAll(findSubConstraints(booleanAndOp.getRight())); + } else if (constrExpr instanceof ASTBracketExpression) { + ASTExpression subExpr = ((ASTBracketExpression)constrExpr).getExpression(); + subConstraints.addAll(findSubConstraints(subExpr)); + } else { + subConstraints.add(calculateSubConstraint(constrExpr)); + } + + return subConstraints; + } + + /** + * Transforms a given ASTExpression into a ODSubConstraint + * + * @return the resulting ODSubConstraint + */ + private ODSubConstraint calculateSubConstraint(ASTExpression constrExpr) { + ODRulesTraverser traverser = ODRulesMill + .inheritanceTraverser(); + + //Create copy of assignments to avoid side effects + List modifiedAssignmentsCopy = this.modifiedAssignments + .stream() + .map(ASTAssignment::deepClone) + .collect(Collectors.toList()); + + ReplaceIdentifierVisitor replaceIdentVisitor = new ReplaceIdentifierVisitor(modifiedAssignmentsCopy); + traverser.add4CommonExpressions(replaceIdentVisitor); + traverser.add4ExpressionsBasis(replaceIdentVisitor); + constrExpr.accept(traverser); + + traverser = ODRulesMill.inheritanceTraverser(); + FindDependVarsVisitor findDependVarsVisitor = new FindDependVarsVisitor(lhsObjects); + traverser.add4ExpressionsBasis(findDependVarsVisitor); + constrExpr.accept(traverser); + + traverser = ODRulesMill.inheritanceTraverser(); + InsertIsPresentChecksVisitor isPresentCheckVisitor = + new InsertIsPresentChecksVisitor(lhsObjects, hierarchyHelper, constrExpr); + traverser.add4CommonExpressions(isPresentCheckVisitor); + constrExpr.accept(traverser); + constrExpr = isPresentCheckVisitor.subConstraint; + + traverser = ODRulesMill.inheritanceTraverser(); + AddSuffixToOptionalsVisitor suffixVisitor = new AddSuffixToOptionalsVisitor(lhsObjects, hierarchyHelper); + traverser.add4CommonExpressions(suffixVisitor); + traverser.add4ExpressionsBasis(suffixVisitor); + constrExpr.accept(traverser); + + StringBuilder exprSB = new StringBuilder(); + IndentPrinter iPrinter = new IndentPrinter(exprSB); + TFExpressionFullPrettyPrinter p = new TFExpressionFullPrettyPrinter(iPrinter); + p.prettyprint(constrExpr); + + ODSubConstraint subConstraint = new ODSubConstraint(); + subConstraint.dependVars = findDependVarsVisitor.dependVars; + subConstraint.constrExpr = exprSB.toString(); + subConstraint.optionalInOrPresent = isPresentCheckVisitor.optionalInOrPresent; + + return subConstraint; + } + + /** + * Deletes "m." prefix from assignments if present. + */ + private List fixAssignments(ASTODRule parsedModel) { + List modifiedAssignments = new ArrayList<>(); + for (ASTAssignment assignment : parsedModel.getAssignmentList()) { + ASTAssignment clone = assignment.deepClone(); + ODRulesTraverser deleteMatchTraverser = ODRulesMill.inheritanceTraverser(); + deleteMatchTraverser.add4CommonExpressions(new DeleteMatchAccessVisitor()); + clone.getRhs().accept(deleteMatchTraverser); + modifiedAssignments.add(clone); + if (isObjectVariable(assignment.getLhs())) { + clone.setLhs("m." + assignment.getLhs()); + } + } + + return modifiedAssignments; + } + + private boolean isObjectVariable(String var) { + for (ASTODObject o : rhs.getODObjectList()) { + if (o.getName().equals(var)) { + return true; + } + } + for (ASTODObject o : lhs.getODObjectList()) { + if (o.getName().equals(var)) { + return true; + } + } + return false; + } + + /** + * Calculates a set of all variables that are used inside of constraints + */ + private static Map getAllDependVars(List subConstraints) { + + Map allDependVars = new HashMap<>(); + for (ODSubConstraint subConstraint : subConstraints) { + for (ASTMatchingObject object : subConstraint.dependVars) { + String objectName = object.getObjectName(); + if (allDependVars.containsKey(objectName)) { + allDependVars.put(objectName, allDependVars.get(objectName) + 1); + } else { + allDependVars.put(objectName, 1); + } + } + } + return allDependVars; + } + + + protected String getClassName() { + // compute the classname from the filename by taking the filename without + // extension and leading directories + String cnHelper = filename; + if (cnHelper.contains(".")) { + cnHelper = cnHelper.substring(0, cnHelper.lastIndexOf('.')); + } + if (cnHelper.contains(File.separator)) { + cnHelper = cnHelper.substring(cnHelper.lastIndexOf(File.separatorChar) + 1); + } + // consider forward slash separately, because it may also occur on Windows + // systems + if (cnHelper.contains("/")) { + cnHelper = cnHelper.substring(cnHelper.lastIndexOf('/') + 1); + } + return StringTransformations.capitalize(cnHelper); + } + + protected List generateLHSObjectList(ASTODRule dslRoot) { + List lhsObjects = Util.getAllODObjects(dslRoot.getLhs()); + List objects = new ArrayList<>(); + for (ASTODObject obj : lhsObjects) { + ASTMatchingObjectBuilder builder = ODRuleGenerationMill.matchingObjectBuilder(); + // getting the name is easy + if (obj.isPresentName()) { + builder.setObjectName(obj.getName()); + } + + // generates a String from a type + if (obj.isPresentType()) { + ASTMCType type = obj.getType(); + builder.setType(Util.printType(type)); + + if (obj.hasStereotype(ODRuleStereotypes.LIST)) { + builder.setListtype("List"); + builder.setListimpltype("ArrayList"); + } else { + builder.setListtype(""); + builder.setListimpltype("#"); + } + } else { + builder.setType(""); + } + + + // process stereotypes + builder.setLHSObject(true); + builder.setNotObject(obj.hasStereotype(ODRuleStereotypes.NOT)); + builder.setOptObject(obj.hasStereotype(ODRuleStereotypes.OPTIONAL)); + builder.setListObject(obj.hasStereotype(ODRuleStereotypes.LIST)); + + + // get the names of all inner links + List innerLinkObjectNamesList = new ArrayList(); + for (ASTODInnerLink link : obj.getInnerLinksList()) { + if (link.getODObject().isPresentName()) { + innerLinkObjectNamesList.add(link.getODObject().getName()); + } + } + builder.setInnerLinkObjectNamesList(innerLinkObjectNamesList); + + + objects.add(builder.build()); + } + return objects; + } + + protected List generateObjectList(ASTODRule ast) { + List objectNames = new LinkedList<>(); + List objects = generateLHSObjectList(ast); + for (ASTMatchingObject o : objects) { + objectNames.add(o.getObjectName()); + } + + // we still need the Objects from the right side + if (ast.isPresentRhs()) { + List rhsObjects = Util.getAllODObjects(ast.getRhs()); + for (ASTODObject obj : rhsObjects) { + // if it does not already exists + if (!objectNames.contains(obj.getName())) { + ASTMatchingObjectBuilder builder = ODRuleGenerationMill.matchingObjectBuilder(); + // getting the name is easy + if (obj.isPresentName()) { + builder.setObjectName(obj.getName()); + } + + // generates a String from a type + ASTMCType type = obj.getType(); + builder.setType(Util.printType(type)); + + // Right side objects can't be negated, optional or lists + builder.setLHSObject(false); + builder.setNotObject(false); + builder.setOptObject(false); + builder.setListObject(false); + + objects.add(builder.build()); + objectNames.add(obj.getName()); + } + } + } + + return objects; + } + + protected List generateObjectConditions(ASTODRule ast) { + ODRulesTraverser traverser = ODRulesMill.inheritanceTraverser(); + GenerateConditionsVisitor v = new GenerateConditionsVisitor(); + traverser.add4ODRules(v); + ast.getLhs().accept(traverser); + + return v.getObjectConditions(); + } + + protected List generateLinkConditions(ASTODRule ast) { + GenerateLinkConditionsVisitor v = new GenerateLinkConditionsVisitor(); + ODRulesTraverser t = ODRulesMill.inheritanceTraverser(); + t.add4ODRules(v); + ast.getLhs().accept(t); + return v.getLinkConditions(); + } + + protected List generateAssociations(ASTODRule dslRoot) { + List associations = new ArrayList<>(); + + List types = new ArrayList<>(); + + List astLinks = new LinkedList<>(); + astLinks.addAll(dslRoot.getLhs().getODLinkList()); + if (dslRoot.isPresentRhs()) { + astLinks.addAll(dslRoot.getRhs().getODLinkList()); + } + + for (ASTODLink link : astLinks) { + + // try to determine the type of this link + String grammarAssocName = link.getStereotypeValue("gAssoc"); + + if (link.isLink() && grammarAssocName != null && !types.contains(grammarAssocName)) { + String name = StringTransformations.uncapitalize(Names.getSimpleName(grammarAssocName)); + + types.add(grammarAssocName); + ASTAssociationBuilder builder = ODRuleGenerationMill.associationBuilder(); + builder.setName(name); + builder.setGname(grammarAssocName); + associations.add(builder.build()); + } + } + return associations; + + } + + protected String generateConstraintExpression(ASTODRule ast) { + return ast.isPresentConstraint() ? Util.printExpression(ast.getConstraint(), lhsObjects, + this.hierarchyHelper) : "true"; + } + + protected List generateVariables(ASTODRule ast) { + Collection collectedNames = new HashSet<>(); + + List variables = calculateVariablesFor(Util.getAllODObjects(ast.getLhs()), collectedNames); + if (ast.isPresentRhs()) { + variables.addAll(calculateVariablesFor(Util.getAllODObjects(ast.getRhs()), collectedNames)); + } + + variables.addAll(calculateVariablesFor(var2attr, collectedNames)); + + return variables; + } + + private List calculateVariablesFor(Variable2AttributeMap var2attr, + Collection collectedNames) { + List variables = new ArrayList<>(); + for (String key : var2attr.getV2a().keySet()) { + if (!collectedNames.contains(key) && !"$_".equals(key)) { + ASTVariableBuilder variable = ODRuleGenerationMill.variableBuilder(); + variable.setName(key); + variable.setType("String"); + variables.add(variable.build()); + collectedNames.add(key); + } + } + return variables; + } + + private List calculateVariablesFor(List ast, + Collection collectedNames) { + List variables = new ArrayList<>(); + for (ASTODObject obj : ast) { + for (ASTODAttribute attr : obj.getAttributesList()) { + if ((attr.isPresentSingleValue() && !(collectedNames.contains( + Util.printExpression(attr.getSingleValue()))))) { + ASTVariable variable = generateVariable(obj, attr); + + if (variable != null) { + if (attr.isPresentSingleValue()) { + collectedNames.add(Util.printExpression(attr.getSingleValue())); + } else { + collectedNames.add(Util.printFirstListValue(attr.getList())); + } + variables.add(variable); + } + } else if (attr.isPresentList()) { + variables.addAll(generateVariables(attr.getList(), collectedNames)); + } + } + } + return variables; + } + + private Collection generateVariables( + ASTArrayInit list, + Collection collectedNames) { + Collection result = new ArrayList<>(); + for (ASTVariableInit initializer : list.getVariableInitList()) { + String init = new TFExpressionFullPrettyPrinter(new IndentPrinter()).prettyprint(initializer); + if (init.startsWith("\"$")) { + init = init.substring(1, init.length() - 1); + if (!collectedNames.contains(init)) { + ASTVariable v = ODRuleGenerationMill.variableBuilder().uncheckedBuild(); + v.setName(init); + v.setType("String"); + result.add(v); + collectedNames.add(init); + } + } + } + return result; + } + + private ASTVariable generateVariable(ASTODObject obj, ASTODAttribute attr) { + + // The value is a literal + if (attr.isPresentSingleValue() && attr.getSingleValue() instanceof ASTLiteral) { + return null; + } + + if (attr.isPresentList() && attr.getList().getVariableInitList().size() > 1) { + return null; + } + + String attrValue = Util.printExpression(attr.getSingleValue()); + + // Qualified names are references to objects + if (attrValue.contains(".")) { + return null; + } + + if (!attrValue.startsWith("$")) { + return null; + } + + // Reference to an object + if (Util.getODObject(lhs, attrValue) != null || Util.getODObject(rhs, attrValue) != null) { + return null; + } + + // Create a new variable for the rule + ASTVariableBuilder variable = ODRuleGenerationMill.variableBuilder(); + variable.setName(attrValue); + variable.setType(attr.printType()); + + return variable.build(); + } + + protected HashMap> generateFoldingHash(ASTODRule ast) { + HashMap> result = new HashMap>(); + // create an (initially empty) sequence for all objects + for (ASTODObject odObject : Util.getAllODObjects(ast.getLhs())) { + result.put(odObject.getName(), new ArrayList()); + } + List f = ast.getFoldingSetList(); + for (ASTFoldingSet t : f) { + for (String o1 : t.getObjectNamesList()) { + for (String o2 : t.getObjectNamesList()) { + if (result.get(o1) != null) { + result.get(o1).add(o2); + } + } + + } + } + return result; + } + + protected ASTReplacement generateReplacement(List changes) { + ASTReplacementBuilder replacement = ODRuleGenerationMill.replacementBuilder(); + Map requirementNames = new HashMap(); + replacement.setRequirementsList(generateRequirements(changes, requirementNames)); + replacement.setChangesList(generateChanges(changes, requirementNames)); + replacement.setCreateObjectsList(generateCreateObjects(changes)); + replacement.setDeleteObjectsList(generateDeleteObjects(changes)); + return replacement.build(); + } + + protected List generateRequirements(List changesList, + Map requirementNames) { + List requirements = new ArrayList<>(); + + for (ASTChangeOperation co : changesList) { + + List setAttributeList = co.getSetAttributeOperationsList(); + + for (ASTChange set : setAttributeList) { + // if the value is collected from other objects + // save the Value before it might be changed + + if (set.isPresentValue()) { + String value = set.getValue(); + if (!set.isValueStringList() && value.contains(".") && !requirementNames.containsKey(value)) { + // create new name + StringBuilder sb = new StringBuilder("_"); + + sb.append(value.replace(".get()", "")); + for (int i = 0; i < sb.length(); i++) { + if (sb.charAt(i) == '.') { + sb.setCharAt(i, '_'); + } + } + + requirementNames.put(value, sb.toString()); + + // create Requirement + ASTRequirementBuilder requirement = ODRuleGenerationMill.requirementBuilder(); + + String type = set.getType(); + + String[] strings = value.split("\\."); + String attributeName = strings[1]; + String getterName = ("boolean".equals(type)) ? "is" + StringTransformations.capitalize( + attributeName) : "get" + StringTransformations.capitalize(attributeName); + String varName = strings[0]; + + + // fill Requirement with data + requirement.setType(type); + requirement.setAttribute(attributeName); + requirement.setGetter(getterName); + requirement.setObject(varName); + + requirements.add(requirement.build()); + } + } + + + } + } + + return requirements; + } + + protected List generateChanges(List changesList, + Map requirementNames) { + + List changesSequence = new ArrayList<>(); + + for (ASTChangeOperation co : changesList) { + List setAttributeList = co.getSetAttributeOperationsList(); + + for (ASTChange set : setAttributeList) { + if (set.isPresentValue() && requirementNames.get(set.getValue()) != null) { + set.setValue(requirementNames.get(set.getValue())); + } + changesSequence.add(set); + } + + } + return changesSequence; + } + + protected List generateCreateObjects(List changesList) { + List createSequence = new ArrayList<>(); + + for (ASTChangeOperation co : changesList) { + createSequence.addAll(co.getCreateOperationsList()); + } + return createSequence; + } + + protected List generateDeleteObjects(List changesList) { + + List deleteSequence = new ArrayList<>(); + + for (ASTChangeOperation co : changesList) { + deleteSequence.addAll(co.getDeleteOperationsList()); + } + return deleteSequence; + } + + private List generateTypesList(ASTODRule ast) { + Set types = new LinkedHashSet(); + + List astObjects = Util.getAllODObjects(ast.getLhs()); + for (ASTODObject obj : astObjects) { + + // generates a String from a type + + ASTMCType type = obj.getType(); + types.add(Util.printType(type)); + } + return Lists.newArrayList(types); + } + + +} diff --git a/monticore-grammar/src/main/java/de/monticore/tf/odrules/ODRulesCLI.java b/monticore-grammar/src/main/java/de/monticore/tf/odrules/ODRulesCLI.java new file mode 100644 index 0000000000..ac144a2fd0 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/tf/odrules/ODRulesCLI.java @@ -0,0 +1,144 @@ +/* (c) https://github.com/MontiCore/monticore */ +/* generated by template de.monticore.tf.translation.MainClass*/ + +package de.monticore.tf.odrules; + +import de.monticore.generating.templateengine.GlobalExtensionManagement; +import de.monticore.tf.odrules._ast.ASTODRule; +import de.monticore.tf.odrules._parser.ODRulesParser; +import de.monticore.tf.odrules._symboltable.ODRulesScopesGenitorDelegator; +import de.se_rwth.commons.logging.Log; +import org.apache.commons.cli.*; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Optional; + +public class ODRulesCLI { + + /** + * Main method. + * + * @param args the CLI arguments + */ + public static void main(String[] args) { + Log.init(); + new ODRulesCLI().run(args); + } + + protected void run(String[] args){ + + Options options = initOptions(); + + try { + // create CLI parser and parse input options from command line + CommandLineParser cliParser = new DefaultParser(); + CommandLine cmd = cliParser.parse(options, args); + + // help: when --help + if (cmd.hasOption("h")) { + printHelp(options); + // do not continue, when help is printed + return; + } + + if (!cmd.hasOption("i")) { + Log.error("0xA1000 There is no \".mtod\" file to parse. Please check the \"input\" option."); + // do not continue, when this error is logged + return; + } + + ODRulesMill.init(); + + Log.debug("----- Starting ODRules Generation -----", LOG_ID); + + Log.debug("--------------------------------", LOG_ID); + Log.debug("Input file : " + cmd.getOptionValue("i"), LOG_ID); + Log.debug("Output dir : " + cmd.getOptionValue("o"), LOG_ID); + + Path model = Paths.get(cmd.getOptionValue("i")); + + // Parse OD-Rules + Optional astRule = parseODRule(model); + + ODRulesScopesGenitorDelegator symbolTable = ODRulesMill.scopesGenitorDelegator(); + symbolTable.createFromAST(astRule.get()); + + GlobalExtensionManagement glex = new GlobalExtensionManagement(); + generate(astRule.get(), glex, Paths.get(cmd.getOptionValue("o")).toFile(), model.getFileName().toFile().getName().replace(".mtod", "")); + + }catch ( ParseException e) { + // an unexpected error from the Apache CLI parser: + Log.error("0xA6151 Could not process CLI parameters: " + e.getMessage()); + } + } + + public void generate(ASTODRule ast, GlobalExtensionManagement glex, File outputDirectory, String filename){ + Log.debug("Generate Transformation for " + filename, LOG_ID); + ast.setName(filename); + ODRuleCodeGenerator.generate(ast, outputDirectory); + } + + public Optional parseODRule(Path model) { + Log.debug("Start parsing of the model " + model, LOG_ID); + try { + ODRulesParser parser = new ODRulesParser(); + Optional ast = parser.parse(model.toString()); + if (!parser.hasErrors() && ast.isPresent()) { + Log.debug("Model " + model + " parsed successfully", LOG_ID); + } else { + Log.info( + "There are parsing errors while parsing of the model " + model, LOG_ID); + } + + return ast; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Initializes the available CLI options for the MontiCore tool. + * + * @return The CLI options with arguments. + */ + protected Options initOptions() { + Options options = new Options(); + + // parse input grammars + options.addOption(Option.builder("i") + .longOpt("input") + .argName("file") + .hasArg() + .desc("Processes the given model and triggers the transformation generation.") + .build()); + + // specify custom output directory + options.addOption(Option.builder("o") + .longOpt("out") + .argName("path") + .hasArg() + .desc("Output directory for all generated artifacts.") + .build()); + + + // help dialog + options.addOption(Option.builder("h") + .longOpt("help") + .desc("Prints this help dialog") + .build()); + + return options; + } + + protected void printHelp(Options options) { + HelpFormatter formatter = new HelpFormatter(); + formatter.setWidth(80); + formatter.printHelp("ODRulesCLI", options); + } + + static final String LOG_ID = "ODRules"; + +} diff --git a/monticore-grammar/src/main/java/de/monticore/tf/odrules/_ast/ASTODAttribute.java b/monticore-grammar/src/main/java/de/monticore/tf/odrules/_ast/ASTODAttribute.java new file mode 100644 index 0000000000..4b15a8d98a --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/tf/odrules/_ast/ASTODAttribute.java @@ -0,0 +1,99 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.odrules._ast; + + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.tf.odrules.util.TFExpressionFullPrettyPrinter; +import de.monticore.types.mcsimplegenerictypes.MCSimpleGenericTypesMill; + +public class ASTODAttribute extends ASTODAttributeTOP { + + protected ASTODAttribute (){ + + } + + protected ASTODAttribute (de.monticore.types.mcbasictypes._ast.ASTMCType mCType, + String name, + ASTCardinality attributeCardinality, + de.monticore.expressions.expressionsbasis._ast.ASTExpression singleValue, + de.monticore.statements.mcarraystatements._ast.ASTArrayInit list) + { + super(); + setMCType(mCType); + setName(name); + setAttributeCardinality(attributeCardinality); + setSingleValue(singleValue); + setList(list); + //super(mCType,name,singleValue,list); + } + + private String sType; + private String sValue; + + public String printType() { + // lazy calculation from ast + if (sType == null) { + if (isPresentMCType()) { + sType = MCSimpleGenericTypesMill.prettyPrint(getMCType(), false); + if(sType.endsWith("<>")) { + sType = sType.substring(0, sType.length()-2); + } + } + else { + sType = ""; + } + } + return sType; + } + + public String printName() { + // convenient method to get data in the same way in the templates + return name; + } + + public String printValue() { + // lazy calculation from ast + if (sValue == null) { + if (isPresentSingleValue()) { + + StringBuilder stringbuilder = new StringBuilder(); + IndentPrinter iPrinter = new IndentPrinter(stringbuilder); + TFExpressionFullPrettyPrinter p =new TFExpressionFullPrettyPrinter(iPrinter); + + p.prettyprint(getSingleValue()); + iPrinter.flushBuffer(); + sValue = stringbuilder.toString(); + } + else { + sValue = ""; + } + } + return sValue; + } + + public String printList() { + String ret; + if (isPresentList()) { + + StringBuilder stringbuilder = new StringBuilder(); + IndentPrinter iPrinter = new IndentPrinter(stringbuilder); + TFExpressionFullPrettyPrinter p = new TFExpressionFullPrettyPrinter(iPrinter); + + p.prettyprint(getList()); + iPrinter.flushBuffer(); + ret = stringbuilder.toString(); + } + else { + ret = ""; + } + return ret; + } + + public boolean isIterated() { + return getAttributeCardinality().isMany() || getAttributeCardinality().isOneToMany(); + } + + public boolean isOptional() { + return getAttributeCardinality().isOptional(); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/tf/odrules/_ast/ASTODLink.java b/monticore-grammar/src/main/java/de/monticore/tf/odrules/_ast/ASTODLink.java new file mode 100644 index 0000000000..0b4bba791e --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/tf/odrules/_ast/ASTODLink.java @@ -0,0 +1,145 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.odrules._ast; + + +import de.monticore.umlstereotype._ast.ASTStereotype; + +import java.util.NoSuchElementException; + +public class ASTODLink extends ASTODLinkTOP { + + protected ASTODLink (/* generated by template ast.ParametersDeclaration*/ + // Parameters declaration + ) + /* generated by template ast.EmptyMethodBody*/ + + { // empty body + } + + protected ASTODLink (ASTStereotype stereotype, + String name, + java.util.List leftReferenceNames, + String leftRole, + String rightRole, + ASTCardinality attributeCardinality, + java.util.List rightReferenceNames, + boolean link) { + /* generated by template data.ConstructorAttributesSetter*/ + super(); + setStereotype(stereotype); + setName(name); + setLeftReferenceNameList(leftReferenceNames); + setLeftRole(leftRole); + setRightRole(rightRole); + setAttributeCardinality(attributeCardinality); + setRightReferenceNameList(rightReferenceNames); +/* super( stereotype + , + name + , + leftReferenceNames + , + leftRole + , + rightRole + , + rightReferenceNames + , + link + + );*/ + } + + private String sLeftRole; + private String sRightRole; + + @Override + public boolean isLink() { + // ODLinks in ODRules are always compositions + return link; + } + + public boolean hasStereotype(String name) { + if (isPresentStereotype()) { + return getStereotype().contains(name.intern()); + } + return false; + } + + public boolean hasStereotype(String name, String value) { + if (isPresentStereotype()) { + return getStereotype().contains(name.intern(), value.intern()); + } + return false; + } + + public String getStereotypeValue(String name) { + if (isPresentStereotype()) { + try { + return getStereotype().getValue(name.intern()); + } catch (NoSuchElementException e) { + return ""; + } + } + return ""; + } + + public String printName() { + if (isPresentName()) { + return getName(); + } + return ""; + } + + + /** + * Returns the left role of the according association of this link. Default is + * the name of the association if leftRole is not set. In case the association + * is unnamed the type-name of the left reference (in lower-case) is used + * instead. + * + * @return left role as String + */ + public String printLeftRole() { + if (sLeftRole == null) { + if (isPresentLeftRole()) { + sLeftRole = getLeftRole(); + } + else if (isPresentName()) { + sLeftRole = getName(); + } + else { + sLeftRole = ""; + } + } + return sLeftRole; + } + + public String printRightRole() { + if (sRightRole == null) { + if (isPresentRightRole()) { + sRightRole = getRightRole(); + } + else if (isPresentName()) { + sRightRole = getName(); + } + else { + + sRightRole = ""; + } + } + return sRightRole; + } + + + + public boolean isAttributeIterated() { + return getAttributeCardinality().isMany() || getAttributeCardinality().isOneToMany(); + } + + public boolean isAttributeOptional() { + return getAttributeCardinality().isOptional(); + } + + +} diff --git a/monticore-grammar/src/main/java/de/monticore/tf/odrules/_ast/ASTODObject.java b/monticore-grammar/src/main/java/de/monticore/tf/odrules/_ast/ASTODObject.java new file mode 100644 index 0000000000..41604804a0 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/tf/odrules/_ast/ASTODObject.java @@ -0,0 +1,131 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.odrules._ast; + +import de.monticore.types.mcsimplegenerictypes.MCSimpleGenericTypesMill; +import de.monticore.umlstereotype._ast.ASTStereotype; +import de.se_rwth.commons.Names; + +import java.util.ArrayList; +import java.util.List; + +public class ASTODObject extends ASTODObjectTOP { + + protected ASTODObject (/* generated by template ast.ParametersDeclaration*/ + // Parameters declaration + ) + /* generated by template ast.EmptyMethodBody*/ + + { // empty body + } + + protected ASTODObject (ASTStereotype stereotype, + String name, + de.monticore.types.mcbasictypes._ast.ASTMCType type, + List attributes, + List innerLinks) + { + super(); + setStereotype(stereotype); + setName(name); + setType(type); + setAttributesList(attributes); + setInnerLinksList(innerLinks); + //super(stereotype, name, type, attributes, innerLinks); + } + + private String sName; + private String sType; + private String sQualifiedType; + private ArrayList linkList; + + @Override + public ASTODObject deepClone() { + return (ASTODObject) super.deepClone(); + } + + + public boolean hasStereotype(String name) { + if (isPresentStereotype()) { + return getStereotype().contains(name.intern()); + } + return false; + } + + public boolean hasStereotype(String name, String value) { + if (isPresentStereotype()) { + return getStereotype().contains(name.intern(), value.intern()); + } + return false; + } + + public String getStereotypeValue(String name) { + if (isPresentStereotype()) { + String sv = getStereotype().getValue(name.intern()); + if (sv != null) { + return sv; + } + } + return ""; + } + + public String printName() { + // lazy calculation from ast + if (sName == null) { + if (name.isPresent()) { + sName = name.get().intern(); + } + else { + // use type-name instead + sName = Names.getSimpleName(printType()); + // remove generic-part + if (sName.contains("<")) { + sName = sName.substring(0, sName.indexOf("<")); + } + sName = sName.toLowerCase().intern(); + } + } + return sName; + } + + public String printType() { + if (sType == null) { + if (type.isPresent()) { + // lazy calculation from ast + sType = MCSimpleGenericTypesMill.prettyPrint(type.get(), false).intern(); + if(sType.endsWith("<>")) { + sType = sType.substring(0, sType.length()-2); + } + } + else { + sType = ""; + } + } + return sType; + } + + public String printQualifiedType() { + if (sQualifiedType == null) { + if (type != null) { + sQualifiedType = printType(); + } + } + return sQualifiedType; + } + + /** + * Returns all ODObjects, including inner links + * and deeper hierarchies. + * @return a list of all ASTODObjects + */ + public List getAllODObjects() { + List allObjects = new ArrayList<>(); + + for (ASTODInnerLink link : getInnerLinksList()) { + ASTODObject innerObject = (ASTODObject) link.getODObject(); + allObjects.add(innerObject); + allObjects.addAll(innerObject.getAllODObjects()); + } + + return allObjects; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/tf/odrules/_symboltable/IODRulesScope.java b/monticore-grammar/src/main/java/de/monticore/tf/odrules/_symboltable/IODRulesScope.java new file mode 100644 index 0000000000..29f41d2fcb --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/tf/odrules/_symboltable/IODRulesScope.java @@ -0,0 +1,28 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.odrules._symboltable; + +import de.monticore.symboltable.modifiers.AccessModifier; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; + +public interface IODRulesScope extends IODRulesScopeTOP { + + + /* + * Ensure we are able to resolve properly in anonymous scopes + */ + default List continueAsODDefinitionSubScope( + boolean foundSymbols, String name, + AccessModifier modifier, + Predicate predicate) { + + List resultList = new ArrayList<>(); + setODDefinitionSymbolsAlreadyResolved(false); + for(String remainingSymbolName: getRemainingNameForResolveDown(name)) { + resultList.addAll(this.resolveODDefinitionDownMany(foundSymbols, remainingSymbolName, modifier, predicate)); + } + return resultList; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/tf/odrules/subConstraints/AddSuffixToOptionalsVisitor.java b/monticore-grammar/src/main/java/de/monticore/tf/odrules/subConstraints/AddSuffixToOptionalsVisitor.java new file mode 100644 index 0000000000..d5043e52b4 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/tf/odrules/subConstraints/AddSuffixToOptionalsVisitor.java @@ -0,0 +1,154 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.odrules.subConstraints; + +import de.monticore.expressions.commonexpressions.CommonExpressionsMill; +import de.monticore.expressions.commonexpressions._ast.*; +import de.monticore.expressions.commonexpressions._visitor.CommonExpressionsVisitor2; +import de.monticore.expressions.expressionsbasis.ExpressionsBasisMill; +import de.monticore.expressions.expressionsbasis._ast.ASTArguments; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.expressionsbasis._ast.ASTNameExpression; +import de.monticore.expressions.expressionsbasis._visitor.ExpressionsBasisVisitor2; +import de.monticore.tf.odrulegeneration._ast.ASTMatchingObject; +import de.monticore.tf.odrules.HierarchyHelper; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Created by Alexander Wilts on 16.01.2017. + *

+ * This visitor replaces elements with stereotype 'not' or 'optional' with a new variable with suffix '_candAsOptional'. + * Additionally the visitor adds a '.get()' call behind that variable. + */ +public class AddSuffixToOptionalsVisitor implements + ExpressionsBasisVisitor2, CommonExpressionsVisitor2 { + + protected List lhsObjects; + protected HierarchyHelper hierarchyHelper; + public Set handledNodes; + + public AddSuffixToOptionalsVisitor(List lhsObjects, HierarchyHelper hierarchyHelper) { + super(); + this.hierarchyHelper = hierarchyHelper; + this.lhsObjects = lhsObjects; + handledNodes = new HashSet<>(); + } + + @Override + public void visit(ASTArguments node) { + List newExpressions = new ArrayList<>(); + for(ASTExpression expr : node.getExpressionList()) { + if(expr instanceof ASTNameExpression) { + ASTNameExpression nameExpr = (ASTNameExpression) expr; + newExpressions.add(replaceNode(nameExpr)); + } else { + newExpressions.add(expr); + } + } + node.setExpressionList(newExpressions); + } + + @Override + public void visit(ASTFieldAccessExpression node) { + if (node.getExpression() instanceof ASTNameExpression) { + node.setExpression(replaceNode((ASTNameExpression) node.getExpression())); + } + } + + @Override + public void visit(ASTBooleanNotExpression node) { + if (node.getExpression() instanceof ASTNameExpression) { + node.setExpression(replaceNode((ASTNameExpression) node.getExpression())); + } + } + + @Override + public void visit(ASTLogicalNotExpression node) { + if (node.getExpression() instanceof ASTNameExpression) { + node.setExpression(replaceNode((ASTNameExpression) node.getExpression())); + } + } + + @Override + public void visit(ASTEqualsExpression node) { + if (node.getLeft() instanceof ASTNameExpression) { + node.setLeft(replaceNode((ASTNameExpression) node.getLeft())); + } + if (node.getRight() instanceof ASTNameExpression) { + node.setRight(replaceNode((ASTNameExpression) node.getRight())); + } + } + + @Override + public void visit(ASTBooleanAndOpExpression node) { + if (node.getLeft() instanceof ASTNameExpression) { + node.setLeft(replaceNode((ASTNameExpression) node.getLeft())); + } + if (node.getRight() instanceof ASTNameExpression) { + node.setRight(replaceNode((ASTNameExpression) node.getRight())); + } + } + + @Override + public void visit(ASTBooleanOrOpExpression node) { + if (node.getLeft() instanceof ASTNameExpression) { + node.setLeft(replaceNode((ASTNameExpression) node.getLeft())); + } + if (node.getRight() instanceof ASTNameExpression) { + node.setRight(replaceNode((ASTNameExpression) node.getRight())); + } + } + + @Override + public void visit(ASTPlusExpression node) { + if (node.getLeft() instanceof ASTNameExpression) { + node.setLeft(replaceNode((ASTNameExpression) node.getLeft())); + } + if (node.getRight() instanceof ASTNameExpression) { + node.setRight(replaceNode((ASTNameExpression) node.getRight())); + } + } + + @Override + public void visit(ASTBracketExpression node) { + if (node.getExpression() instanceof ASTNameExpression) { + node.setExpression(replaceNode((ASTNameExpression) node.getExpression())); + } + } + + private ASTExpression replaceNode(ASTNameExpression node) { + for (ASTMatchingObject o : lhsObjects) { + if (node.getName().equals(o.getObjectName())) { + if (hierarchyHelper.isWithinOptionalStructure(o.getObjectName()) + || hierarchyHelper.isWithinNegativeStructure(o.getObjectName())) { + return buildNode(node); + } + break; + } + } + + return node; + } + + private ASTExpression buildNode(ASTNameExpression node) { + + ASTNameExpression nameExpr = ExpressionsBasisMill.nameExpressionBuilder() + .setName(node.getName() + "_candAsOptional") + .build(); + + ASTFieldAccessExpression getExpr = CommonExpressionsMill.fieldAccessExpressionBuilder() + .setExpression(nameExpr) + .setName("get") + .build(); + + ASTCallExpression callExpr = CommonExpressionsMill.callExpressionBuilder() + .setExpression(getExpr) + .setArguments(CommonExpressionsMill.argumentsBuilder().build()) + .build(); + + return callExpr; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/tf/odrules/subConstraints/CalculateContextVisitor.java b/monticore-grammar/src/main/java/de/monticore/tf/odrules/subConstraints/CalculateContextVisitor.java new file mode 100644 index 0000000000..c15f5201ba --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/tf/odrules/subConstraints/CalculateContextVisitor.java @@ -0,0 +1,59 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.odrules.subConstraints; + +import de.monticore.expressions.commonexpressions.CommonExpressionsMill; +import de.monticore.expressions.commonexpressions._ast.ASTBooleanOrOpExpression; +import de.monticore.expressions.commonexpressions._visitor.CommonExpressionsTraverser; +import de.monticore.expressions.commonexpressions._visitor.CommonExpressionsVisitor2; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; + +/** + * Created by Alexander Wilts on 16.01.2017. + * + * Determines the context of an Optional. + * If it is used inside a OR-Expression it has to be handled differently from an AND-Expression. + */ +public class CalculateContextVisitor implements + CommonExpressionsVisitor2 { + + protected ASTExpression targetNode; + public boolean inOrContext = false; + + public CalculateContextVisitor(ASTExpression targetNode){ + super(); + this.targetNode = targetNode; + } + + @Override + public void visit(ASTBooleanOrOpExpression node) { + CommonExpressionsTraverser subExprVisitorTraverser = CommonExpressionsMill + .inheritanceTraverser(); + CommonExpressionsTraverser containsTargetVisitorTraverser = CommonExpressionsMill + .inheritanceTraverser(); + FindSubExpressionVisitor subExprVisitor = new FindSubExpressionVisitor(); + subExprVisitorTraverser.add4CommonExpressions(subExprVisitor); + CheckIfContainsTargetVisitor containsTargetVisitor = new CheckIfContainsTargetVisitor(targetNode); + containsTargetVisitorTraverser.add4IVisitor(containsTargetVisitor); + //check if lhs or rhs contains targetNode + node.getLeft().accept(containsTargetVisitorTraverser); + if(containsTargetVisitor.containsTarget){ + //check if that side is final (contains no further || or &&) + node.getLeft().accept(subExprVisitorTraverser); + if(!subExprVisitor.subExpressionIsPresent){ + inOrContext = true; + } + } + node.getRight().accept(containsTargetVisitorTraverser); + if(containsTargetVisitor.containsTarget){ + node.getRight().accept(subExprVisitorTraverser); + if(!subExprVisitor.subExpressionIsPresent){ + inOrContext = true; + } + } + } + + + + + +} diff --git a/monticore-grammar/src/main/java/de/monticore/tf/odrules/subConstraints/CheckIfContainsTargetVisitor.java b/monticore-grammar/src/main/java/de/monticore/tf/odrules/subConstraints/CheckIfContainsTargetVisitor.java new file mode 100644 index 0000000000..0ef0d675f6 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/tf/odrules/subConstraints/CheckIfContainsTargetVisitor.java @@ -0,0 +1,33 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.odrules.subConstraints; + +import de.monticore.ast.ASTNode; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.visitor.IVisitor; + +/** + * Created by Alexander Wilts on 16.01.2017. + * + * This visitor determines if if an Expression contains a given Expression targetNode. + */ +public class CheckIfContainsTargetVisitor implements + IVisitor +{ + + protected ASTExpression targetNode; + public boolean containsTarget = false; + + public CheckIfContainsTargetVisitor(ASTExpression targetNode){ + super(); + this.targetNode = targetNode; + containsTarget = false; + } + + @Override + public void visit(ASTNode node) { + + if(node.equals(targetNode)){ + containsTarget= true; + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/tf/odrules/subConstraints/DeleteMatchAccessVisitor.java b/monticore-grammar/src/main/java/de/monticore/tf/odrules/subConstraints/DeleteMatchAccessVisitor.java new file mode 100644 index 0000000000..f5cfc9bfab --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/tf/odrules/subConstraints/DeleteMatchAccessVisitor.java @@ -0,0 +1,115 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.odrules.subConstraints; + + +import de.monticore.expressions.commonexpressions.CommonExpressionsMill; +import de.monticore.expressions.commonexpressions._ast.*; +import de.monticore.expressions.commonexpressions._visitor.CommonExpressionsVisitor2; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.expressionsbasis._ast.ASTNameExpression; + +/** + * Created by Alexander Wilts on 16.01.2017. + * + * This visitor cuts off the 'm.' prefix used for accessing elements in a given Match m. + * In SubConstraints these elements are accessed by their original name. + */ +public class DeleteMatchAccessVisitor implements + CommonExpressionsVisitor2 { + + + public DeleteMatchAccessVisitor(){ + super(); + } + + @Override + public void visit(ASTFieldAccessExpression node) { + if (node.getExpression() instanceof ASTFieldAccessExpression) { + node.setExpression(replaceNode((ASTFieldAccessExpression) node.getExpression())); + } + } + + @Override + public void visit(ASTBooleanNotExpression node) { + if (node.getExpression() instanceof ASTFieldAccessExpression) { + node.setExpression(replaceNode((ASTFieldAccessExpression) node.getExpression())); + } + } + + @Override + public void visit(ASTLogicalNotExpression node) { + if (node.getExpression() instanceof ASTFieldAccessExpression) { + node.setExpression(replaceNode((ASTFieldAccessExpression) node.getExpression())); + } + } + + @Override + public void visit(ASTEqualsExpression node) { + if (node.getLeft() instanceof ASTFieldAccessExpression) { + node.setLeft(replaceNode((ASTFieldAccessExpression) node.getLeft())); + } + if (node.getRight() instanceof ASTFieldAccessExpression) { + node.setRight(replaceNode((ASTFieldAccessExpression) node.getRight())); + } + } + + @Override + public void visit(ASTBooleanAndOpExpression node) { + if (node.getLeft() instanceof ASTFieldAccessExpression) { + node.setLeft(replaceNode((ASTFieldAccessExpression) node.getLeft())); + } + if (node.getRight() instanceof ASTFieldAccessExpression) { + node.setRight(replaceNode((ASTFieldAccessExpression) node.getRight())); + } + } + + @Override + public void visit(ASTBooleanOrOpExpression node) { + if (node.getLeft() instanceof ASTFieldAccessExpression) { + node.setLeft(replaceNode((ASTFieldAccessExpression) node.getLeft())); + } + if (node.getRight() instanceof ASTFieldAccessExpression) { + node.setRight(replaceNode((ASTFieldAccessExpression) node.getRight())); + } + } + + @Override + public void visit(ASTBracketExpression node) { + if (node.getExpression() instanceof ASTFieldAccessExpression) { + node.setExpression(replaceNode((ASTFieldAccessExpression) node.getExpression())); + } + } + + @Override + public void visit(ASTPlusExpression node) { + if (node.getLeft() instanceof ASTFieldAccessExpression) { + node.setLeft(replaceNode((ASTFieldAccessExpression) node.getLeft())); + } + if (node.getRight() instanceof ASTFieldAccessExpression) { + node.setRight(replaceNode((ASTFieldAccessExpression) node.getRight())); + } + } + + private ASTExpression replaceNode(ASTFieldAccessExpression node) { + //Look for a child of a child that is a primary expression with name 'm' + if(node.getExpression() instanceof ASTNameExpression) { + ASTNameExpression nameExpression = (ASTNameExpression) node.getExpression(); + if(nameExpression.getName().equals("m")) { + return buildNode(node); + } + } + + return node; + } + + + private ASTNameExpression buildNode(ASTFieldAccessExpression node) { + //If found, remove this child and create a new primary node + ASTNameExpression primExpr = CommonExpressionsMill.nameExpressionBuilder() + .setName(node.getName()) + .build(); + + return primExpr; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/tf/odrules/subConstraints/FindDependVarsVisitor.java b/monticore-grammar/src/main/java/de/monticore/tf/odrules/subConstraints/FindDependVarsVisitor.java new file mode 100644 index 0000000000..e79c0d6c5e --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/tf/odrules/subConstraints/FindDependVarsVisitor.java @@ -0,0 +1,39 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.odrules.subConstraints; + +import de.monticore.expressions.expressionsbasis._ast.ASTNameExpression; +import de.monticore.expressions.expressionsbasis._visitor.ExpressionsBasisVisitor2; +import de.monticore.tf.odrulegeneration._ast.ASTMatchingObject; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Created by Alexander Wilts on 16.01.2017. + * + * This visitor calculates the variables contained in a given expression. + */ +public class FindDependVarsVisitor implements + ExpressionsBasisVisitor2 { + + protected List lhsObjects; + public Set dependVars; + + public FindDependVarsVisitor(List lhsObjects){ + super(); + this.lhsObjects = lhsObjects; + dependVars = new HashSet<>(); + } + + @Override + public void visit(ASTNameExpression node) { + for(ASTMatchingObject o : lhsObjects){ + if(node.getName().equals(o.getObjectName())){ + dependVars.add(o); + break; + } + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/tf/odrules/subConstraints/FindOptionalsVisitor.java b/monticore-grammar/src/main/java/de/monticore/tf/odrules/subConstraints/FindOptionalsVisitor.java new file mode 100644 index 0000000000..9e9a6b1a2e --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/tf/odrules/subConstraints/FindOptionalsVisitor.java @@ -0,0 +1,47 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.odrules.subConstraints; + +import de.monticore.expressions.expressionsbasis._ast.ASTNameExpression; +import de.monticore.expressions.expressionsbasis._visitor.ExpressionsBasisVisitor2; +import de.monticore.tf.odrulegeneration._ast.ASTMatchingObject; +import de.monticore.tf.odrules.HierarchyHelper; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Created by Alexander Wilts on 16.01.2017. + * + * This visitor calculates if there are any elements with stereotype 'optional' or 'null' contained in the given expression. + */ +public class FindOptionalsVisitor implements + ExpressionsBasisVisitor2 { + + protected List lhsObjects; + protected HierarchyHelper hierarchyHelper; + public Set optVars; + + public FindOptionalsVisitor(List lhsObjects, HierarchyHelper hierarchyHelper){ + super(); + this.hierarchyHelper = hierarchyHelper; + this.lhsObjects = lhsObjects; + optVars = new HashSet<>(); + } + + @Override + public void visit(ASTNameExpression node) { + + for(ASTMatchingObject o : lhsObjects){ + if(node.getName().equals(o.getObjectName())){ + if(hierarchyHelper.isWithinOptionalStructure(o.getObjectName()) + || hierarchyHelper.isWithinNegativeStructure(o.getObjectName())){ + optVars.add(o); + } + } + } + + } + + +} diff --git a/monticore-grammar/src/main/java/de/monticore/tf/odrules/subConstraints/FindSubExpressionVisitor.java b/monticore-grammar/src/main/java/de/monticore/tf/odrules/subConstraints/FindSubExpressionVisitor.java new file mode 100644 index 0000000000..f407331320 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/tf/odrules/subConstraints/FindSubExpressionVisitor.java @@ -0,0 +1,35 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.odrules.subConstraints; + + +import de.monticore.expressions.commonexpressions._ast.ASTBooleanAndOpExpression; +import de.monticore.expressions.commonexpressions._ast.ASTBooleanOrOpExpression; +import de.monticore.expressions.commonexpressions._visitor.CommonExpressionsVisitor2; + +/** + * Created by Alexander Wilts on 16.01.2017. + * + * This visitor calculates if the given expression contains any further subExpressions. + * A subExpression is every expression, that does not contain any further '&&' or '||' operators. + */ +public class FindSubExpressionVisitor implements + CommonExpressionsVisitor2 { + + public boolean subExpressionIsPresent; + + public FindSubExpressionVisitor(){ + super(); + subExpressionIsPresent = false; + } + + @Override + public void visit(ASTBooleanOrOpExpression node) { + subExpressionIsPresent = true; + } + + @Override + public void visit(ASTBooleanAndOpExpression node) { + subExpressionIsPresent = true; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/tf/odrules/subConstraints/InsertIsPresentChecksVisitor.java b/monticore-grammar/src/main/java/de/monticore/tf/odrules/subConstraints/InsertIsPresentChecksVisitor.java new file mode 100644 index 0000000000..eb510e92db --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/tf/odrules/subConstraints/InsertIsPresentChecksVisitor.java @@ -0,0 +1,255 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.odrules.subConstraints; + +import de.monticore.expressions.commonexpressions.CommonExpressionsMill; +import de.monticore.expressions.commonexpressions._ast.*; +import de.monticore.expressions.commonexpressions._visitor.CommonExpressionsTraverser; +import de.monticore.expressions.commonexpressions._visitor.CommonExpressionsVisitor2; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.expressionsbasis._ast.ASTNameExpression; +import de.monticore.tf.odrulegeneration._ast.ASTMatchingObject; +import de.monticore.tf.odrules.HierarchyHelper; +import de.monticore.tf.odrules.ODRulesMill; +import de.monticore.tf.odrules._visitor.ODRulesTraverser; + +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +/** + * Created by Alexander Wilts on 16.01.2017. + *

+ * This visitor inserts isPresent()-checks into expressions that contain optional variables. + *

+ * Let $O be an optional variable in this expression: isValid($O). + * In case that no value is present for $O, the evaluation of this expression would produce a NoSuchElementException. + * In order to prevent this we add an isPresent()-check to the expression like this: !$O.isPresent || isValid($O) + *

+ * Additionally we have to consider the context of the optional. + * The isPresent-Check has to be implemented differently depending on whether the optional is used inside an OR-Expression + * or an AND-Expression. + *

+ * Examples for normal variable $A and optional variable $O: + *

+ * $A && $O results in $A && (!$O.isPresent || isValid($O)) + * $A || $O results in $A || ($O.isPresent && isValid($O)) + */ +public class InsertIsPresentChecksVisitor implements CommonExpressionsVisitor2 { + + public boolean optionalInOrPresent; + + protected List lhsObjects; + protected HierarchyHelper hierarchyHelper; + public ASTExpression subConstraint; + + + public InsertIsPresentChecksVisitor(List lhsObjects, HierarchyHelper hierarchyHelper, ASTExpression subConstraint) { + super(); + this.hierarchyHelper = hierarchyHelper; + optionalInOrPresent = false; + this.lhsObjects = lhsObjects; + this.subConstraint = subConstraint; + } + + + + @Override + public void visit(ASTCallExpression node) { + if(node.equals(subConstraint) && checkNode(node)) { + subConstraint = replaceNode(node); + } + } + + @Override + public void visit(ASTFieldAccessExpression node) { + if(node.equals(subConstraint) && checkNode(node)) { + subConstraint = replaceNode(node); + } + } + + @Override + public void visit(ASTBooleanNotExpression node) { + if(node.equals(subConstraint) && checkNode(node)) { + subConstraint = replaceNode(node); + } + } + + @Override + public void visit(ASTLogicalNotExpression node) { + if(node.equals(subConstraint) && checkNode(node)) { + subConstraint = replaceNode(node); + } + } + + @Override + public void visit(ASTEqualsExpression node) { + if(node.equals(subConstraint) && checkNode(node)) { + subConstraint = replaceNode(node); + } + } + + @Override + public void visit(ASTPlusExpression node) { + if(node.equals(subConstraint) && checkNode(node)) { + subConstraint = replaceNode(node); + } + } + + @Override + public void visit(ASTBracketExpression node) { + if(node.equals(subConstraint) && checkNode(node)) { + subConstraint = replaceNode(node); + } + } + + @Override + public void visit(ASTBooleanAndOpExpression node) { + if(checkNode(node.getRight())) { + node.setRight(replaceNode(node.getRight())); + } + if(checkNode(node.getLeft())) { + node.setLeft(replaceNode(node.getLeft())); + } + } + + @Override + public void visit(ASTBooleanOrOpExpression node) { + if(checkNode(node.getRight())) { + node.setRight(replaceNode(node.getRight())); + } + if(checkNode(node.getLeft())) { + node.setLeft(replaceNode(node.getLeft())); + } + } + + + private boolean checkNode(ASTExpression node) { + FindSubExpressionVisitor subExprVisitor = new FindSubExpressionVisitor(); + CommonExpressionsTraverser t = CommonExpressionsMill.inheritanceTraverser(); + t.add4CommonExpressions(subExprVisitor); + node.accept(t); + + if(!subExprVisitor.subExpressionIsPresent) { + return true; + } + + return false; + } + + public ASTExpression replaceNode(ASTExpression node) { + CommonExpressionsTraverser findOptsVisitorTraverser = CommonExpressionsMill.inheritanceTraverser(); + FindOptionalsVisitor findOptsVisitor = new FindOptionalsVisitor(lhsObjects, hierarchyHelper); + findOptsVisitorTraverser.add4ExpressionsBasis(findOptsVisitor); + node.accept(findOptsVisitorTraverser); + if(!findOptsVisitor.optVars.isEmpty()) { + CommonExpressionsTraverser contextVisitorTraverser = CommonExpressionsMill.inheritanceTraverser(); + CalculateContextVisitor contextVisitor = new CalculateContextVisitor(node); + contextVisitorTraverser.add4CommonExpressions(contextVisitor); + subConstraint.accept(contextVisitorTraverser); + if (contextVisitor.inOrContext) { + optionalInOrPresent = true; + return insertIsPresentCheckForOr(node, findOptsVisitor.optVars); + } else { + return insertDefaultIsPresentCheck(node, findOptsVisitor.optVars); + } + } + + + return node; + } + + + private ASTBracketExpression insertDefaultIsPresentCheck(ASTExpression node, Set optVars) { + ODRulesTraverser t = ODRulesMill.inheritanceTraverser(); + AddSuffixToOptionalsVisitor suffixVisitor = new AddSuffixToOptionalsVisitor(lhsObjects, hierarchyHelper); + t.add4ExpressionsBasis(suffixVisitor); + t.add4CommonExpressions(suffixVisitor); + node.accept(t); + + ASTBooleanOrOpExpression mainOrExpr = CommonExpressionsMill.booleanOrOpExpressionBuilder().uncheckedBuild(); + mainOrExpr.setRight(node); + + ASTBracketExpression bracketExpr = CommonExpressionsMill.bracketExpressionBuilder() + .setExpression(mainOrExpr) + .build(); + + Iterator i = optVars.iterator(); + while (i.hasNext()) { + ASTMatchingObject o = i.next(); + + + ASTCallExpression callExpr = buildIsPresentCheck(o); + + + ASTLogicalNotExpression notExpr = CommonExpressionsMill.logicalNotExpressionBuilder() + .setExpression(callExpr) + .build(); + + + if (i.hasNext()) { + ASTBooleanOrOpExpression orExpr = CommonExpressionsMill.booleanOrOpExpressionBuilder().uncheckedBuild(); + orExpr.setRight(notExpr); + mainOrExpr.setLeft(orExpr); + mainOrExpr = orExpr; + } else { + mainOrExpr.setLeft(notExpr); + } + } + + return bracketExpr; + } + + + private ASTBracketExpression insertIsPresentCheckForOr(ASTExpression node, Set optVars) { + ODRulesTraverser t = ODRulesMill.inheritanceTraverser(); + AddSuffixToOptionalsVisitor suffixVisitor = new AddSuffixToOptionalsVisitor(lhsObjects, hierarchyHelper); + t.add4ExpressionsBasis(suffixVisitor); + t.add4CommonExpressions(suffixVisitor); + node.accept(t); + + ASTBooleanAndOpExpression mainAndExpr = CommonExpressionsMill.booleanAndOpExpressionBuilder().uncheckedBuild(); + mainAndExpr.setRight(node); + + ASTBracketExpression bracketExpr = CommonExpressionsMill.bracketExpressionBuilder() + .setExpression(mainAndExpr) + .build(); + + Iterator i = optVars.iterator(); + while (i.hasNext()) { + ASTMatchingObject o = i.next(); + + + ASTCallExpression callExpr = buildIsPresentCheck(o); + + + if (i.hasNext()) { + ASTBooleanAndOpExpression orExpr = CommonExpressionsMill.booleanAndOpExpressionBuilder() + .setRight(callExpr) + .build(); + mainAndExpr.setLeft(orExpr); + mainAndExpr = orExpr; + } else { + mainAndExpr.setLeft(callExpr); + } + } + + return bracketExpr; + } + + private ASTCallExpression buildIsPresentCheck(ASTMatchingObject o) { + ASTNameExpression optExpr = CommonExpressionsMill.nameExpressionBuilder() + .setName(o.getObjectName() + "_candAsOptional") + .build(); + + ASTFieldAccessExpression isPresentExpr = CommonExpressionsMill.fieldAccessExpressionBuilder() + .setName("isPresent") + .setExpression(optExpr) + .build(); + + return CommonExpressionsMill.callExpressionBuilder() + .setExpression(isPresentExpr) + .setArguments(CommonExpressionsMill.argumentsBuilder().uncheckedBuild()) + .build(); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/tf/odrules/subConstraints/ODSubConstraint.java b/monticore-grammar/src/main/java/de/monticore/tf/odrules/subConstraints/ODSubConstraint.java new file mode 100644 index 0000000000..7fe3f6a56e --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/tf/odrules/subConstraints/ODSubConstraint.java @@ -0,0 +1,55 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.odrules.subConstraints; + +import de.monticore.tf.odrulegeneration._ast.ASTMatchingObject; + +import java.util.HashSet; +import java.util.Set; + +/** + * Created by Alexander Wilts on 10.11.2016. + * + * Objects of type ODSubConstraint represent modules of constraints that can be checked independently. + */ +public class ODSubConstraint { + + public Set dependVars; + public String constrExpr; + + //Constraints with an Optional in an OR-Expression require special handling in templates + public boolean optionalInOrPresent = false; + + public boolean isOptionalInOrPresent() { + return optionalInOrPresent; + } + + public ODSubConstraint(Set dependVars, String constrExpr){ + this.dependVars = dependVars; + this.constrExpr = constrExpr; + } + + public Set getDependVars(){ + return dependVars; + } + + public boolean isDependendOn(ASTMatchingObject object){ + boolean dependsOnObject = dependVars.stream().anyMatch(astMatchingObject -> astMatchingObject.getObjectName() == object.getObjectName()); + if(dependsOnObject){ + return true; + }else { + return false; + } + } + + public String getConstrExpr(){ + return constrExpr; + } + + public String getConstrExprFor(String dependencyName){ + return constrExpr.replace(dependencyName+"_cand","cand"); + } + + public ODSubConstraint() { + dependVars = new HashSet<>(); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/tf/odrules/subConstraints/ReplaceIdentifierVisitor.java b/monticore-grammar/src/main/java/de/monticore/tf/odrules/subConstraints/ReplaceIdentifierVisitor.java new file mode 100644 index 0000000000..1816ef9d88 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/tf/odrules/subConstraints/ReplaceIdentifierVisitor.java @@ -0,0 +1,178 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.odrules.subConstraints; + +import de.monticore.expressions.commonexpressions._ast.*; +import de.monticore.expressions.commonexpressions._visitor.CommonExpressionsVisitor2; +import de.monticore.expressions.expressionsbasis._ast.ASTArguments; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.expressionsbasis._ast.ASTNameExpression; +import de.monticore.expressions.expressionsbasis._visitor.ExpressionsBasisVisitor2; +import de.monticore.tf.odrules._ast.ASTAssignment; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * Created by Alexander Wilts on 16.01.2017. + * + * This visitor replaces identifier variables with a call to their real variables. + * + * An identifier variable is a short way to reference the name of an element in a transformation. + * This identifier does not have any value before it is assigned in later stages of the transformation. + * In order to read the identifier variable early on in the transformation we have to replace it. + */ +public class ReplaceIdentifierVisitor implements + CommonExpressionsVisitor2, + ExpressionsBasisVisitor2 { + + protected Map assignments; + protected List assignmentsAsAST; + + public ReplaceIdentifierVisitor(List assignmentsAsAST){ + super(); + this.assignmentsAsAST = assignmentsAsAST; + } + + @Override + public void visit(ASTFieldAccessExpression node) { + if (node.getExpression() instanceof ASTNameExpression) { + Optional assignment = checkAssignment((ASTNameExpression)node.getExpression()); + if(assignment.isPresent()) { + node.setExpression(assignment.get().getRhs()); + } + } + } + + @Override + public void visit(ASTBooleanNotExpression node) { + if (node.getExpression() instanceof ASTNameExpression) { + Optional assignment = checkAssignment((ASTNameExpression)node.getExpression()); + if(assignment.isPresent()) { + node.setExpression(assignment.get().getRhs()); + } + } + } + + + @Override + public void visit(ASTLogicalNotExpression node) { + if (node.getExpression() instanceof ASTNameExpression) { + Optional assignment = checkAssignment((ASTNameExpression)node.getExpression()); + if(assignment.isPresent()) { + node.setExpression(assignment.get().getRhs()); + } + } + } + + @Override + public void visit(ASTEqualsExpression node) { + if (node.getLeft() instanceof ASTNameExpression) { + Optional assignment = checkAssignment((ASTNameExpression)node.getLeft()); + if(assignment.isPresent()) { + node.setLeft(assignment.get().getRhs()); + } + } + if (node.getRight() instanceof ASTNameExpression) { + Optional assignment = checkAssignment((ASTNameExpression)node.getRight()); + if(assignment.isPresent()) { + node.setRight(assignment.get().getRhs()); + } + } + } + + @Override + public void visit(ASTBooleanAndOpExpression node) { + if (node.getLeft() instanceof ASTNameExpression) { + Optional assignment = checkAssignment((ASTNameExpression)node.getLeft()); + if(assignment.isPresent()) { + node.setLeft(assignment.get().getRhs()); + } + } + if (node.getRight() instanceof ASTNameExpression) { + Optional assignment = checkAssignment((ASTNameExpression)node.getRight()); + if(assignment.isPresent()) { + node.setRight(assignment.get().getRhs()); + } + } + } + + @Override + public void visit(ASTBooleanOrOpExpression node) { + if (node.getLeft() instanceof ASTNameExpression) { + Optional assignment = checkAssignment((ASTNameExpression)node.getLeft()); + if(assignment.isPresent()) { + node.setLeft(assignment.get().getRhs()); + } + } + if (node.getRight() instanceof ASTNameExpression) { + Optional assignment = checkAssignment((ASTNameExpression)node.getRight()); + if(assignment.isPresent()) { + node.setRight(assignment.get().getRhs()); + } + } + } + + @Override + public void visit(ASTPlusExpression node) { + if (node.getLeft() instanceof ASTNameExpression) { + Optional assignment = checkAssignment((ASTNameExpression)node.getLeft()); + if(assignment.isPresent()) { + node.setLeft(assignment.get().getRhs()); + } + } + if (node.getRight() instanceof ASTNameExpression) { + Optional assignment = checkAssignment((ASTNameExpression)node.getRight()); + if(assignment.isPresent()) { + node.setRight(assignment.get().getRhs()); + } + } + } + + @Override + public void visit(ASTBracketExpression node) { + if (node.getExpression() instanceof ASTNameExpression) { + Optional assignment = checkAssignment((ASTNameExpression)node.getExpression()); + if(assignment.isPresent()) { + node.setExpression(assignment.get().getRhs()); + } + } + } + + @Override + public void visit(ASTCallExpression node) { + if (node.getExpression() instanceof ASTNameExpression) { + Optional assignment = checkAssignment((ASTNameExpression)node.getExpression()); + if(assignment.isPresent()) { + node.setExpression(assignment.get().getRhs()); + } + } + } + + @Override + public void visit(ASTArguments node) { + int index = 0; + for(ASTExpression expr : node.getExpressionList()){ + if (expr instanceof ASTNameExpression) { + Optional assignment = checkAssignment((ASTNameExpression)expr); + if(assignment.isPresent()) { + node.setExpression(index, assignment.get().getRhs()); + } + } + index++; + } + } + + + + private Optional checkAssignment(ASTNameExpression node) { + for (ASTAssignment a : assignmentsAsAST) { + if (a.getLhs().equals(node.getName())) { + return Optional.of(a); + } + } + return Optional.empty(); + } + + +} diff --git a/monticore-grammar/src/main/java/de/monticore/tf/odrules/util/NameASTPair.java b/monticore-grammar/src/main/java/de/monticore/tf/odrules/util/NameASTPair.java new file mode 100644 index 0000000000..7f04cff898 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/tf/odrules/util/NameASTPair.java @@ -0,0 +1,27 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.odrules.util; + +import de.monticore.tf.odrules._ast.ASTODRule; + +/** + * Created by + * + */ +public class NameASTPair { + private final String name; + private final ASTODRule ast; + + public NameASTPair(String name, ASTODRule ast){ + this.name = name; + this.ast = ast; + + } + + public String getName() { + return name; + } + + public ASTODRule getAst() { + return ast; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/tf/odrules/util/ODRuleStereotypes.java b/monticore-grammar/src/main/java/de/monticore/tf/odrules/util/ODRuleStereotypes.java new file mode 100644 index 0000000000..a644c057e6 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/tf/odrules/util/ODRuleStereotypes.java @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.odrules.util; + +public class ODRuleStereotypes { + + public static final String NOT = "not"; + public static final String OPTIONAL = "optional"; + public static final String LIST = "list"; + + +} diff --git a/monticore-grammar/src/main/java/de/monticore/tf/odrules/util/ODRulesPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/tf/odrules/util/ODRulesPrettyPrinter.java new file mode 100644 index 0000000000..4f7d130542 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/tf/odrules/util/ODRulesPrettyPrinter.java @@ -0,0 +1,337 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.odrules.util; + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.types.mcbasictypes._ast.ASTMCQualifiedName; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.mcbasictypes._visitor.MCBasicTypesVisitor2; +import de.monticore.types.mcbasictypes._prettyprint.MCBasicTypesFullPrettyPrinter; +import de.monticore.types.mcsimplegenerictypes.MCSimpleGenericTypesMill; +import de.monticore.types.mcsimplegenerictypes._prettyprint.MCSimpleGenericTypesFullPrettyPrinter; +import de.monticore.umlstereotype._ast.ASTStereoValue; +import de.monticore.umlstereotype._ast.ASTStereotype; +import de.monticore.umlstereotype._visitor.UMLStereotypeVisitor2; +import de.se_rwth.commons.Names; +import de.monticore.tf.odrules._ast.*; +import de.monticore.tf.odrules._visitor.ODRulesHandler; +import de.monticore.tf.odrules._visitor.ODRulesTraverser; +import de.monticore.tf.odrules._visitor.ODRulesVisitor2; + +import java.util.Iterator; + +/** + * Created by KH. + */ +public class ODRulesPrettyPrinter implements ODRulesVisitor2, + ODRulesHandler, UMLStereotypeVisitor2, + MCBasicTypesVisitor2 { + + private ODRulesTraverser traverser; + + @Override + public ODRulesTraverser getTraverser() { + return this.traverser; + } + + @Override + public void setTraverser(ODRulesTraverser traverser) { + this.traverser = traverser; + } + + public String getPrintedAST() { + return printer.toString(); + } + + StringBuilder printer = new StringBuilder(); + + + @Override + public void visit(ASTODRule node) { + printer.append("pattern "); + } + + @Override + public void traverse(ASTODRule node) { + node.getLhs().accept(getTraverser()); + if (node.isPresentRhs()){ + printer.append("replacement "); + node.getRhs().accept(getTraverser()); + } + if (node.isPresentConstraint()){ + printer.append("where { \n"); + printer.append(" " + Util.printExpression(node.getConstraint()) + "\n"); + printer.append("}"); + } + if(!node.getAssignmentList().isEmpty()){ + printer.append("assign { \n"); + for (ASTAssignment a : node.getAssignmentList()){ + a.accept(getTraverser()); + } + printer.append("}"); + + } + if (node.isPresentDoBlock()){ + printer.append("do " + Util.print(node.getDoBlock()) +"\n"); + } + if(!node.getFoldingSetList().isEmpty()){ + printer.append("folding {\n"); + for(ASTFoldingSet fs : node.getFoldingSetList()){ + fs.accept(getTraverser()); + } + printer.append("}\n"); + } + } + + @Override + public void endVisit(ASTODDefinition node) { + printer.append("}\n"); + } + + @Override + public void traverse(ASTODDefinition node) { + // print stereotype + // print object diagram name and parameters + printer.append("objectdiagram " + node.getName() + "{\n"); + // print body + for(ASTODObject o: node.getODObjectList()) { + o.accept(getTraverser()); + } + for(ASTODLink l : node.getODLinkList()) { + l.accept(getTraverser()); + } + } + + @Override + public void traverse(ASTODObject node) { + /*// print completeness + if (node.completenessIsPresent()) { + node.getCompleteness().get().accept(this); + } + // print object modifier + if (node.getModifier().isPresent()) + node.getModifier().get().accept(this);*/ + // print stereotype + if (node.isPresentStereotype()) { + node.getStereotype().accept(getTraverser()); + } + // print object name and type + if (node.isPresentName()) { + printer.append(" " + node.getName()); + } + if (node.isPresentType()) { + printer.append(":") + .append(MCSimpleGenericTypesMill.prettyPrint(node.getType(), false)); + } + // print object body + if (node.getAttributesList().isEmpty() && node.getInnerLinksList().isEmpty() ) { + printer.append(";\n\n"); + } + else { + printer.append("{\n"); + // print attributes + if (!node.getAttributesList().isEmpty()) { + for (ASTODAttribute a : node.getAttributesList()) { + a.accept(getTraverser()); + } + } + // print inner links + if (!node.getInnerLinksList().isEmpty()) { + for (ASTODInnerLink a : node.getInnerLinksList()) { + a.accept(getTraverser()); + } + } + printer.append(" }\n\n"); + } + /*if (!node.getAttributes().isEmpty()) { + printer.append("{\n"); + for (ASTODAttribute a : node.getAttributes()) { + a.accept(this); + } + printer.append(" }\n\n"); + } else { + printer.append(";\n\n"); + }*/ + } + + + + @Override + public void traverse(ASTODAttribute node) { + // print modifier + /*if(node.getModifier().isPresent()) + node.getModifier().get().accept(this);*/ + // print type + if (node.isPresentMCType()) { + printer.append(MCSimpleGenericTypesMill.prettyPrint(node.getMCType(), false)); + printer.append(" "); + } + // print name + printer.append(node.getName()); + // print value + if (node.isPresentSingleValue()) { + printer.append(" = "); + printer.append(node.printValue()); + } else if (node.isPresentList()) { + printer.append(" = "); + printer.append(node.printList()); + } + + } + + @Override + public void visit(ASTODAttribute node) { + printer.append(" "); + } + + @Override + public void endVisit(ASTODAttribute node) { + printer.append(";\n"); + } + + @Override + public void traverse(ASTODLink node) { + // print stereotype + if (node.isPresentStereotype()) { + node.getStereotype().accept(getTraverser()); + } + // print type of the link + /*if (node.isLink()) { + printer.append("link "); + } + else if (node.isComposition()) { + printer.append("composition "); + }*/ + // OD Rules are always compositions + printer.append("composition "); + // print name + /*if (node.isDerived()) { + printer.append("/"); + }*/ + if (node.isPresentName()) { + printer.append(node.getName() + " "); + } + // print left modifier + /*if(node.getLeftModifier().isPresent()) + node.getLeftModifier().get().accept(this);*/ + // print objects referenced on the left side of the link + printQualifiedNameList(node.getLeftReferenceNameList().iterator(), ", "); + printer.append(" "); + // print left qualifier + /*if (node.leftQualifierIsPresent()) { + printer.append("["); + node.getLeftQualifier().get().accept(this); + printer.append("] "); + }*/ + // print left role + if (node.isPresentLeftRole()) { + printer.append("("); + printer.append(node.getLeftRole()); + printer.append(") "); + } + // print arrow + printer.append(" -- "); + // print right role + if (node.isPresentRightRole()) { + printer.append(" ("); + printer.append(node.getRightRole()); + printer.append(")"); + } + // print right qualifier + /*if (node.rightQualifierIsPresent()) { + printer.append("["); + node.getRightQualifier().get().accept(this); + printer.append("] "); + }*/ + // print objects referenced on the right side of the link + printer.append(" "); + printQualifiedNameList(node.getRightReferenceNameList().iterator(), ", "); + printer.append(" "); + // print right modifier + /*if(node.getRightModifier().isPresent()) + node.getRightModifier().get().accept(this);*/ + } + + @Override + public void visit(ASTODLink node) { + printer.append(" "); + } + + @Override + public void endVisit(ASTODLink node) { + printer.append(";\n\n"); + } + + + + @Override + public void visit(ASTAssignment node) { + printer.append(" " + node.getLhs()); + printer.append(" = "); + printer.append(Util.printExpression(node.getRhs())); + printer.append(";\n"); + } + + + + @Override + public void visit(ASTFoldingSet node) { + printer.append(" ("); + Iterator objectNames = node.getObjectNamesList().iterator(); + while (objectNames.hasNext()) { + printer.append(objectNames.next()); + if (objectNames.hasNext()) { + printer.append(", "); + } + } + printer.append(")\n"); + } + + + @Override + public void visit(ASTMCType node) { + printer.append(node.printType()); + } + + /** + * Prints the start of stereotypes + * + * @param a stereotype + */ + @Override + public void visit(ASTStereotype a) { + printer.append("<<"); + String comma = ""; + for(ASTStereoValue v : a.getValuesList()){ + printer.append(comma).append(v.getName()); + comma = ","; + } + } + + /** + * Prints the end of stereotypes + * + * @param a stereotype + */ + @Override + public void endVisit(ASTStereotype a) { + printer.append(">>"); + } + + + /** + * Prints a list of ASTQualifiedNames in an ownVisit method + * + * @param iter iterator for the list of ASTQualifiedNames + * @param seperator string for seperating the ASTQualifiedNames + */ + protected void printQualifiedNameList(Iterator iter, String seperator) { + // print by iterate through all items + String sep = ""; + while (iter.hasNext()) { + printer.append(sep); + printer.append(Names.getQualifiedName(iter.next().getPartsList())); // visit + // item + sep = seperator; + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/tf/odrules/util/TFExpressionFullPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/tf/odrules/util/TFExpressionFullPrettyPrinter.java new file mode 100644 index 0000000000..40c209fcf4 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/tf/odrules/util/TFExpressionFullPrettyPrinter.java @@ -0,0 +1,148 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.odrules.util; + +import com.google.common.collect.Lists; +import de.monticore.expressions.assignmentexpressions._prettyprint.AssignmentExpressionsPrettyPrinter; +import de.monticore.expressions.commonexpressions._prettyprint.CommonExpressionsPrettyPrinter; +import de.monticore.expressions.expressionsbasis._ast.ASTExpressionsBasisNode; +import de.monticore.expressions.expressionsbasis._ast.ASTNameExpression; +import de.monticore.expressions.expressionsbasis._prettyprint.ExpressionsBasisPrettyPrinter; +import de.monticore.expressions.javaclassexpressions._prettyprint.JavaClassExpressionsPrettyPrinter; +import de.monticore.javalight._prettyprint.JavaLightPrettyPrinter; +import de.monticore.literals.mccommonliterals._prettyprint.MCCommonLiteralsPrettyPrinter; +import de.monticore.literals.mcjavaliterals._prettyprint.MCJavaLiteralsPrettyPrinter; +import de.monticore.mcbasics._prettyprint.MCBasicsPrettyPrinter; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.statements.mccommonstatements._ast.ASTMCCommonStatementsNode; +import de.monticore.statements.mccommonstatements._prettyprint.MCCommonStatementsPrettyPrinter; +import de.monticore.statements.mcvardeclarationstatements._ast.ASTMCVarDeclarationStatementsNode; +import de.monticore.statements.mcvardeclarationstatements._prettyprint.MCVarDeclarationStatementsPrettyPrinter; +import de.monticore.tf.odrulegeneration._ast.ASTMatchingObject; +import de.monticore.tf.odrules.HierarchyHelper; +import de.monticore.tf.odrules.ODRulesMill; +import de.monticore.tf.odrules._ast.ASTODRulesNode; +import de.monticore.tf.odrules._visitor.ODRulesTraverser; +import de.monticore.types.mcbasictypes._prettyprint.MCBasicTypesPrettyPrinter; +import de.monticore.types.mccollectiontypes._prettyprint.MCCollectionTypesPrettyPrinter; +import de.monticore.types.mcsimplegenerictypes._prettyprint.MCSimpleGenericTypesPrettyPrinter; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * Created by + * + */ +public class TFExpressionFullPrettyPrinter { + + protected List lhsObjects; + protected final HierarchyHelper helper; + protected final IndentPrinter printer; + protected final ODRulesTraverser traverser; + + public TFExpressionFullPrettyPrinter(IndentPrinter out) { + this(out, Lists.newArrayList(), new HierarchyHelper()); + } + + public TFExpressionFullPrettyPrinter(IndentPrinter out, List objects, HierarchyHelper helper) { + this.traverser = ODRulesMill.traverser(); + + MCBasicsPrettyPrinter mcBasicsPrettyPrinter = new MCBasicsPrettyPrinter(out, true); + traverser.add4MCBasics(mcBasicsPrettyPrinter); + + MCCommonLiteralsPrettyPrinter mcCommonLiteralsPrettyPrinter = new MCCommonLiteralsPrettyPrinter(out, true); + traverser.add4MCCommonLiterals(mcCommonLiteralsPrettyPrinter); + traverser.setMCCommonLiteralsHandler(mcCommonLiteralsPrettyPrinter); + de.monticore.literals.mcjavaliterals._prettyprint.MCJavaLiteralsPrettyPrinter mcJavaLiteralsPrettyPrinter = new MCJavaLiteralsPrettyPrinter(out, true); //b + traverser.add4MCJavaLiterals(mcJavaLiteralsPrettyPrinter); + traverser.setMCJavaLiteralsHandler(mcJavaLiteralsPrettyPrinter); + + de.monticore.types.mcbasictypes._prettyprint.MCBasicTypesPrettyPrinter mcBasicTypesPrettyPrinter = new MCBasicTypesPrettyPrinter(out, true); // a + traverser.add4MCBasicTypes(mcBasicTypesPrettyPrinter); + traverser.setMCBasicTypesHandler(mcBasicTypesPrettyPrinter); + MCCollectionTypesPrettyPrinter mcCollectionTypesPrettyPrinter = new MCCollectionTypesPrettyPrinter(out, true); + traverser.setMCCollectionTypesHandler(mcCollectionTypesPrettyPrinter); + traverser.add4MCCollectionTypes(mcCollectionTypesPrettyPrinter); + MCSimpleGenericTypesPrettyPrinter mcSimpleGenericTypesPrettyPrinter = new MCSimpleGenericTypesPrettyPrinter(out, true); + traverser.add4MCSimpleGenericTypes(mcSimpleGenericTypesPrettyPrinter); + traverser.setMCSimpleGenericTypesHandler(mcSimpleGenericTypesPrettyPrinter); + + MCCommonStatementsPrettyPrinter mcCommonStatementsPrettyPrinter = new MCCommonStatementsPrettyPrinter(out, true); + traverser.add4MCCommonStatements(mcCommonStatementsPrettyPrinter); + traverser.setMCCommonStatementsHandler(mcCommonStatementsPrettyPrinter); + MCVarDeclarationStatementsPrettyPrinter mcVarDeclarationStatementsPrettyPrinter = new MCVarDeclarationStatementsPrettyPrinter(out, true); + traverser.add4MCVarDeclarationStatements(mcVarDeclarationStatementsPrettyPrinter); + traverser.setMCVarDeclarationStatementsHandler(mcVarDeclarationStatementsPrettyPrinter); + + CommonExpressionsPrettyPrinter commonExpressionsPrettyPrinter = new CommonExpressionsPrettyPrinter(out, true); + traverser.add4CommonExpressions(commonExpressionsPrettyPrinter); + traverser.setCommonExpressionsHandler(commonExpressionsPrettyPrinter); + AssignmentExpressionsPrettyPrinter assignmentExpressionsPrettyPrinter = new AssignmentExpressionsPrettyPrinter(out, true); + traverser.add4AssignmentExpressions(assignmentExpressionsPrettyPrinter); + traverser.setAssignmentExpressionsHandler(assignmentExpressionsPrettyPrinter); + + JavaLightPrettyPrinter javaLightPrettyPrinter = new JavaLightPrettyPrinter(out, true); + traverser.setJavaLightHandler(javaLightPrettyPrinter); + traverser.add4JavaLight(javaLightPrettyPrinter); + + JavaClassExpressionsPrettyPrinter javaClassExpressionsPrettyPrinter = new JavaClassExpressionsPrettyPrinter(out, true); + traverser.setJavaClassExpressionsHandler(javaClassExpressionsPrettyPrinter); + traverser.add4JavaClassExpressions(javaClassExpressionsPrettyPrinter); + + + // Custom handling of the name expression + ExpressionsBasisPrettyPrinter expressionsBasisPrettyPrinter = + new ExpressionsBasisPrettyPrinter(out, true){ + @Override + public void handle(ASTNameExpression node) { + if(node.getName().startsWith("$") && lhsObjects.contains(node.getName()) + && !helper.isLhsListChild(node.getName()) && !helper.isRhsListChild(node.getName())){ + getPrinter().print("m."); + } + if(node.getName().startsWith("$") && lhsObjects.contains(node.getName()) + && (helper.isLhsListChild(node.getName()) || helper.isRhsListChild(node.getName()))){ + getPrinter().print("l."); + } + getPrinter().print(node.getName()); + } + }; + traverser.add4ExpressionsBasis(expressionsBasisPrettyPrinter); + traverser.setExpressionsBasisHandler(expressionsBasisPrettyPrinter); + + this.printer = out; + this.helper = helper; + this.lhsObjects = objects.stream().map(ASTMatchingObject::getObjectName).collect(Collectors.toList()); + } + + public IndentPrinter getPrinter() { + return this.printer; + } + + public String prettyprint(ASTODRulesNode a) { + getPrinter().clearBuffer(); + a.accept(getTraverser()); + return getPrinter().getContent().stripTrailing(); + } + + public String prettyprint(ASTMCCommonStatementsNode a) { + getPrinter().clearBuffer(); + a.accept(getTraverser()); + return getPrinter().getContent().stripTrailing(); + } + + public String prettyprint(ASTExpressionsBasisNode a) { + getPrinter().clearBuffer(); + a.accept(getTraverser()); + return getPrinter().getContent().stripTrailing(); + } + + public String prettyprint(ASTMCVarDeclarationStatementsNode a) { + getPrinter().clearBuffer(); + a.accept(getTraverser()); + return getPrinter().getContent().stripTrailing(); + } + + public ODRulesTraverser getTraverser() { + return traverser; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/tf/odrules/util/Util.java b/monticore-grammar/src/main/java/de/monticore/tf/odrules/util/Util.java new file mode 100644 index 0000000000..35f291d291 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/tf/odrules/util/Util.java @@ -0,0 +1,324 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.odrules.util; + + +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar._symboltable.ProdSymbol; +import de.monticore.grammar.grammar._symboltable.RuleComponentSymbol; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.statements.mcarraystatements._ast.ASTArrayInit; +import de.monticore.statements.mccommonstatements._ast.ASTMCJavaBlock; +import de.monticore.types.mcbasictypes.MCBasicTypesMill; +import de.monticore.types.mcbasictypes._ast.ASTMCImportStatement; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.mcsimplegenerictypes.MCSimpleGenericTypesMill; +import de.se_rwth.commons.Names; +import de.se_rwth.commons.StringTransformations; +import de.se_rwth.commons.logging.Log; +import de.monticore.tf.odrulegeneration._ast.ASTMatchingObject; +import de.monticore.tf.odrules.HierarchyHelper; +import de.monticore.tf.odrules._ast.*; +import de.monticore.tf.odrules._symboltable.ODDefinitionSymbol; +import de.monticore.tf.odrules._symboltable.ODObjectSymbol; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Optional; + +/** + * Created by + * + */ +public class Util { + private static final int PREFIX_LENGHT = 3; + + protected Util(){ + // no instances + } + + /** + * Resolves an ODObject by it's name. + * @param node the parent ODDefinition + * @param attrName the object's name + * @return the ASTODObject + */ + public static ASTODObject getODObject(ASTODDefinition node, String attrName) { + // first, try to resolve the object directly with the parent ODDefinition + ODDefinitionSymbol symb = node.getSymbol(); + Optional resultSymbol = getFirstSymbol(symb.getSpannedScope().resolveODObjectDownMany(attrName)); + + if(resultSymbol.isPresent() && resultSymbol.get().isPresentAstNode()) { + return (ASTODObject) resultSymbol.get().getAstNode(); + } + else { + // the object is not a direct child of the ODDefinition. + // it could still be within an inner link of some other ODObject, so check those, too + for (ASTODObject parent : node.getODObjectList()) { + ASTODObject obj = Util.getODObject(parent, attrName); + if (obj != null) { + return obj; + } + } + + // if we reach this then the ODObject really is not in the object diagram + return null; + } + } + + /** + * Resolves an ODObject by it's name. + * @param node the parent ODObject + * @param attrName the object's name + * @return the ASTODObject + */ + public static ASTODObject getODObject(ASTODObject node, String attrName) { + // first, try to resolve the object directly with the parent object + ODObjectSymbol symb = node.getSymbol(); + Optional resultSymbol = getFirstSymbol(symb.getSpannedScope().resolveODObjectDownMany(attrName)); + + if (resultSymbol.isPresent() && resultSymbol.get().isPresentAstNode()) { + return resultSymbol.get().getAstNode(); + } + else { + // the object is not a direct child of the parent object. + // it could still be within an inner link of some other inner link, so check those too + for (ASTODInnerLink link : node.getInnerLinksList()) { + ASTODObject obj = Util.getODObject(link.getODObject(), attrName); + if (obj != null) { + return obj; + } + } + + // if we reach this then the ODObject really is not a child of the parent object + return null; + } + } + + /** + * Returns the first symbol of the given collection. + * @param symbols the symbol collection + * @return optional of the first symbol or empty optional if collection is null or empty + */ + private static Optional getFirstSymbol(Collection symbols) { + if (symbols == null || symbols.size() == 0) { + return Optional.empty(); + } + else { + for (ODObjectSymbol symb : symbols) { + return Optional.of(symb); + } + return Optional.empty(); + } + } + + /** + * Returns all ODObjects in the given node. This includes objects in inner links + * of other objects. + * + * @param node the node + * @return all ASTODObjects + */ + public static List getAllODObjects(ASTODDefinition node) { + List result = new ArrayList<>(); + + for (ASTODObject obj : node.getODObjectList()) { + result.add(obj); + result.addAll(obj.getAllODObjects()); + } + + return result; + } + + public static String printImportDeclaration(ASTMCImportStatement importDeclaration){ + return MCBasicTypesMill.prettyPrint(importDeclaration, true); + } + + public static String printExpression(ASTExpression astExpression) { + TFExpressionFullPrettyPrinter p = new TFExpressionFullPrettyPrinter(new IndentPrinter()); + return p.prettyprint(astExpression); + } + + public static String printExpression(ASTExpression astExpression, List objects, HierarchyHelper helper) { + TFExpressionFullPrettyPrinter p = new TFExpressionFullPrettyPrinter(new IndentPrinter(),objects, helper); + return p.prettyprint(astExpression); + } + + public static String printListValue(ASTArrayInit initializer) { + TFExpressionFullPrettyPrinter p = new TFExpressionFullPrettyPrinter(new IndentPrinter()); + return p.prettyprint(initializer); + } + + public static String printFirstListValue(ASTArrayInit astArrayInit) { + TFExpressionFullPrettyPrinter p = new TFExpressionFullPrettyPrinter(new IndentPrinter()); + String value = p.prettyprint(astArrayInit.getVariableInit(0)); + if(value.startsWith("\"$") && value.endsWith("\"")){ + value = value.substring(1, value.length()-1); + } + return value; + } + + public static String print(ASTMCJavaBlock doblock) { + TFExpressionFullPrettyPrinter p = new TFExpressionFullPrettyPrinter(new IndentPrinter()); + return p.prettyprint(doblock); + } + + public static String print(ASTMCJavaBlock doblock, List objects, HierarchyHelper helper) { + TFExpressionFullPrettyPrinter p = new TFExpressionFullPrettyPrinter(new IndentPrinter(),objects, helper); + return p.prettyprint(doblock); + } + + public static String getRightRoleType(ASTODLink node, ASTODDefinition rhs) { + String attrName = Names.constructQualifiedName(node.getRightReferenceName(0).getPartsList()); + ASTODObject obj = getODObject(rhs, attrName); + return obj != null ? obj.printType() : null; + } + + public static String getLeftRoleType(ASTODLink node, ASTODDefinition lhs) { + String attrName = Names.constructQualifiedName(node.getLeftReferenceName(0).getPartsList()); + ASTODObject obj = getODObject(lhs, attrName); + return obj != null ? obj.printType() : null; + } + + public static boolean isBuiltInType(ASTODAttribute attribute) { + String typename = attribute.printType(); + if("boolean".equals(typename) + || "int".equals(typename) + || "float".equals(typename) + || "double".equals(typename) + || "char".equals(typename) + || "long".equals(typename) + || "byte".equals(typename) + || "short".equals(typename) + || "String".equals(typename)){ + return true; + + } + return false; + } + + @Deprecated + public static boolean isIterated(ASTODLink node, ASTODDefinition def, MCGrammarSymbol grammarSymbol) { + ASTODObject sourceObject = getODObject(def, + Names.getSimpleName(node.getLeftReferenceName(0).getPartsList())); + return isIterated(node, sourceObject, grammarSymbol); + } + @Deprecated + public static boolean isIterated(ASTODLink node, ASTODObject leftObject, MCGrammarSymbol grammarSymbol) { + if(grammarSymbol ==null){ + Log.warn("Grammar not specified. Cannot determine whether model elements are allowed multiple times within the grammar."); + return false; + } else if (leftObject.hasStereotype("list")){ + return true; + } else { + Optional attribute = getMCAttributeSymbol(node.getRightRole(), leftObject, + grammarSymbol); + + return attribute.isPresent() && attribute.get().isIsList(); + } + } + @Deprecated + public static boolean isOptional(ASTODLink node, ASTODDefinition def, MCGrammarSymbol grammarSymbol) { + ASTODObject sourceObject = getODObject(def, + Names.getSimpleName(node.getLeftReferenceName(0).getPartsList())); + return isOptional(node, sourceObject, grammarSymbol); + } + + @Deprecated + public static boolean isOptional(ASTODLink node, ASTODObject astodObject, + MCGrammarSymbol grammarSymbol) { + if(grammarSymbol == null){ + Log.warn("Grammar not specified. Cannot determine whether model elements are optional within the grammar."); + return false; + } else if(astodObject.hasStereotype("list")){ + return false; + } + Optional attribute = getMCAttributeSymbol(node.getRightRole(),astodObject, grammarSymbol); + + return attribute.isPresent() && attribute.get().isIsOptional() && !attribute.get().isIsList(); + } + @Deprecated + public static Optional getMCAttributeSymbol (String attributeName, ASTODObject leftObject, MCGrammarSymbol grammarSymbol){ + ASTMCType sourceType = leftObject.getType(); + String javaType = Names.getSimpleName(Util.printType(sourceType)); + // remove AST prefix + String grammarType = javaType.substring(PREFIX_LENGHT); + + + ProdSymbol prod = grammarSymbol.getProdWithInherited(grammarType).get(); + Optional attribute = getProdComponent(prod, attributeName); + + // This may be required depending on whether there is a usage name for this + // attribute + if (!attribute.isPresent()) { + attribute = getProdComponent(prod, StringTransformations.capitalize(attributeName)); + } + if (!attribute.isPresent()) { + attribute = getProdComponent(prod, attributeName.concat("s")); + } + if (!attribute.isPresent() && attributeName.endsWith("s")) { + attributeName = attributeName.substring(0, attributeName.length() - 1); + attribute = getProdComponent(prod, attributeName); + } + if (!attribute.isPresent() && attributeName.endsWith("e")) { + attributeName = attributeName.substring(0, attributeName.length() - 1); + attribute = getProdComponent(prod, attributeName); + } + return attribute; + } + @Deprecated + private static Optional getProdComponent(ProdSymbol prod, String attributeName) { + return prod.getProdComponents().stream().filter(c -> c.getName().equals(attributeName)).findFirst(); + } + @Deprecated + public static boolean isIterated(ASTODObject object, ASTODAttribute attribute, MCGrammarSymbol grammarSymbol) { + if(grammarSymbol == null){ + Log.warn("Grammar not specified. Cannot determine whether model elements are optional within the grammar."); + return false; + } + Optional attributeSymbol = getMCAttributeSymbol(attribute.getName(),object, grammarSymbol); + + if(attribute.printType().equals("boolean")) { + return false; + } + + return attributeSymbol.isPresent() && attributeSymbol.get().isIsList(); + } + @Deprecated + public static boolean isOptional(ASTODObject object, ASTODAttribute attribute, MCGrammarSymbol grammarSymbol) { + if("boolean".equals(attribute.printType())){ + return false; + } + if(grammarSymbol == null){ + Log.warn("Grammar not specified. Cannot determine whether model elements are optional within the grammar."); + return false; + } + Optional attributeSymbol = getMCAttributeSymbol(attribute.getName(),object, grammarSymbol); + return attributeSymbol.isPresent() && attributeSymbol.get().isIsOptional() && !attributeSymbol.get().isIsList(); + + } + @Deprecated + public static boolean isOptional(Optional target, String attrname, + MCGrammarSymbol grammarSymbol) { + if(target.isPresent()){ + Optional attributeSymbol = getMCAttributeSymbol(attrname,target.get(), grammarSymbol); + return attributeSymbol.isPresent() && attributeSymbol.get().isIsOptional()&& !attributeSymbol.get().isIsList(); + } + return false; + } + + public static String makeSingular(String name) { + if(name.endsWith("ses")) + name = name.substring(0, name.length() - 2); + else if(name.endsWith("ss") && !name.endsWith("class")) + name = name.substring(0, name.length() - 2); + else if(name.endsWith("s") && !name.endsWith("class")) + name = name.substring(0, name.length() - 1); + return StringTransformations.capitalize(name); + } + + public static String printType(ASTMCType type) { + return MCSimpleGenericTypesMill.prettyPrint(type, false).replace("<>", ""); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/tf/ruletranslation/ODRuleNameGenerator.java b/monticore-grammar/src/main/java/de/monticore/tf/ruletranslation/ODRuleNameGenerator.java new file mode 100644 index 0000000000..9b64904843 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/tf/ruletranslation/ODRuleNameGenerator.java @@ -0,0 +1,96 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.ruletranslation; + +import de.monticore.ast.ASTNode; +import de.se_rwth.commons.StringTransformations; +import de.monticore.tf.ast.*; + +import java.util.HashMap; +import java.util.Map; + +/** + * This class is used to create names for the objects in generated OD rules. + * + */ +public class ODRuleNameGenerator { + + private Map generatedNames = new HashMap<>(); + private Map, Integer> numberOfElements = new HashMap<>(); + + public String getNameForElement(ITFObject element, Map parents) { + ASTNode parent = parents.get(element); + if(parent instanceof IList || parent instanceof IOptional){ + return getNameForElement(element); + } + // return the name chosen by the user if available + if (parent instanceof ITFObject && !(parent instanceof IPattern) + && ((ITFObject) parents.get(element)).isPresentSchemaVarName()) { + return ((ITFObject) parents.get(element)).getSchemaVarName(); + } + + return getNameForElement(element); + } + + public String getNameForElement(ITFObject element, ITFObject parent) { + + // return the name chosen by the user if available + if (parent.isPresentSchemaVarName()) { + return parent.getSchemaVarName(); + } + + return getNameForElement(element); + } + + public String getNameForElement(ITFObject element) { + // use the schema variable name, if present + if (element.isPresentSchemaVarName() && !element.getSchemaVarName().equals("$_")) { + // we cannot use schema variable names for optional objects, as they do not represent + // any real model object. they just group other objects together. the schema variable name + // will be used as a name for the actual object within the optional object + if(! (element instanceof IOptional)) { + return element.getSchemaVarName(); + } + } + // for negation + // we can safely use the LHS because this is no replacement + if (element instanceof INegation) { + element = (ITFObject) element.getTFElement(); + } + if (!generatedNames.containsKey(element)) { + int nextNumber; + Class elementType; + // optional nodes always have the type de.monticore.tf.ast.IOptional + // regardless of the element they contain + if (element instanceof IOptional) { + elementType = IOptional.class; + } + else if (element instanceof IList) { + elementType = IList.class; + } + else { + elementType = element._getTFElementType(); + } + if (numberOfElements.containsKey(elementType)) { + nextNumber = numberOfElements.get(elementType) + 1; + numberOfElements.put(elementType, nextNumber); + } else { + nextNumber = 1; + numberOfElements.put(elementType, 1); + } + // remove AST prefix, convert first letter to lower case and append number + // for complete object name + String prefix; + if (element instanceof IOptional || element instanceof IList) { + // remove the 'I' from 'IOptional' or 'IList' + prefix = StringTransformations.uncapitalize(elementType.getSimpleName().substring(1)); + } + else { + prefix = StringTransformations.uncapitalize(elementType.getSimpleName().substring(3)); + } + String name = StringTransformations.uncapitalize(prefix + "_" + Integer.toString(nextNumber)); + generatedNames.put(element, name); + } + return generatedNames.get(element); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/tf/ruletranslation/Position.java b/monticore-grammar/src/main/java/de/monticore/tf/ruletranslation/Position.java new file mode 100644 index 0000000000..99d7ebefae --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/tf/ruletranslation/Position.java @@ -0,0 +1,7 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.ruletranslation; + + +public enum Position { + LHS, RHS, BOTH +} diff --git a/monticore-grammar/src/main/java/de/monticore/tf/ruletranslation/Rule2ODState.java b/monticore-grammar/src/main/java/de/monticore/tf/ruletranslation/Rule2ODState.java new file mode 100644 index 0000000000..9dd9691895 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/tf/ruletranslation/Rule2ODState.java @@ -0,0 +1,132 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.ruletranslation; + +import de.monticore.ast.ASTNode; +import de.monticore.tf.odrules.ODRulesMill; +import de.monticore.tf.odrules._ast.ASTODDefinition; +import de.monticore.tf.odrules._ast.ASTODObject; +import de.monticore.tf.odrules._ast.ASTODRule; +import de.monticore.tf.rule2od.Variable2AttributeMap; + +import java.util.Map; +import java.util.Stack; + +public class Rule2ODState { + + public Rule2ODState(Variable2AttributeMap variable2Attributes, Map parents){ + genRule = ODRulesMill.oDRuleBuilder().uncheckedBuild(); + ASTODDefinition lhs = ODRulesMill.oDDefinitionBuilder().uncheckedBuild(); + this.lhs = lhs; + lhs.setName("lhs"); + genRule.setLhs(lhs); + ASTODDefinition rhs = ODRulesMill.oDDefinitionBuilder().uncheckedBuild(); + this.rhs = rhs; + rhs.setName("rhs"); + genRule.setRhs(rhs); + this.variable2Attributes = variable2Attributes; + this.parents = parents; + } + + private ASTODRule genRule; + + private ASTODDefinition lhs; + + private ASTODDefinition rhs; + + private ODRuleNameGenerator nameGen = new ODRuleNameGenerator(); + + private Variable2AttributeMap variable2Attributes; + + private Map parents; + + private boolean isNegative = false; + + private Stack hierarchyLHS = new Stack<>(); + private Stack hierarchyRHS = new Stack<>(); + + private Position position = Position.BOTH; + + public ASTODRule getGenRule() { + return genRule; + } + + public void setGenRule(ASTODRule genRule) { + this.genRule = genRule; + } + + public ASTODDefinition getLhs() { + return lhs; + } + + public void setLhs(ASTODDefinition lhs) { + this.lhs = lhs; + } + + public ASTODDefinition getRhs() { + return rhs; + } + + public void setRhs(ASTODDefinition rhs) { + this.rhs = rhs; + } + + public ODRuleNameGenerator getNameGen() { + return nameGen; + } + + public void setNameGen(ODRuleNameGenerator nameGen) { + this.nameGen = nameGen; + } + + public Variable2AttributeMap getVariable2Attributes() { + return variable2Attributes; + } + + public void setVariable2Attributes(Variable2AttributeMap variable2Attributes) { + this.variable2Attributes = variable2Attributes; + } + + public Map getParents() { + return parents; + } + + public void setParents(Map parents) { + this.parents = parents; + } + + public boolean isNegative() { + return isNegative; + } + + public void setNegative(boolean negative) { + isNegative = negative; + } + + public Stack getHierarchyLHS() { + return hierarchyLHS; + } + + public void setHierarchyLHS(Stack hierarchyLHS) { + this.hierarchyLHS = hierarchyLHS; + } + + public Stack getHierarchyRHS() { + return hierarchyRHS; + } + + public void setHierarchyRHS(Stack hierarchyRHS) { + this.hierarchyRHS = hierarchyRHS; + } + + public Position getPosition() { + return position; + } + + public void setPosition(Position position) { + this.position = position; + } + + + + +} diff --git a/monticore-grammar/src/main/java/de/monticore/tf/ruletranslation/Rule2ODVisitor.java b/monticore-grammar/src/main/java/de/monticore/tf/ruletranslation/Rule2ODVisitor.java new file mode 100644 index 0000000000..69c190ee0d --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/tf/ruletranslation/Rule2ODVisitor.java @@ -0,0 +1,446 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.ruletranslation; + +import com.google.common.collect.Lists; +import de.monticore.ast.ASTNode; +import de.monticore.expressions.expressionsbasis.ExpressionsBasisMill; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.literals.mccommonliterals._ast.ASTBooleanLiteral; +import de.monticore.literals.mccommonliterals._ast.ASTStringLiteral; +import de.monticore.literals.mcjavaliterals._ast.ASTIntLiteral; +import de.monticore.types.mcbasictypes._ast.ASTConstantsMCBasicTypes; +import de.monticore.types.mcbasictypes._ast.ASTMCPrimitiveType; +import de.monticore.types.mcbasictypes._ast.ASTMCQualifiedName; +import de.monticore.types.mcsimplegenerictypes._ast.ASTMCBasicGenericType; +import de.monticore.umlstereotype._ast.ASTStereoValue; +import de.monticore.umlstereotype._ast.ASTStereotype; +import de.se_rwth.commons.Names; +import de.se_rwth.commons.Splitters; +import de.se_rwth.commons.logging.Log; +import de.monticore.tf.odrules.ODRulesMill; +import de.monticore.tf.odrules._ast.*; +import de.monticore.tf.odrules._parser.ODRulesParser; +import de.monticore.tf.odrules.util.ODRuleStereotypes; +import de.monticore.tf.odrules.util.Util; +import de.monticore.tf.rule2od.Variable2AttributeMap; +import de.monticore.tf.tfcommons._ast.*; +import de.monticore.tf.tfcommons._visitor.TFCommonsVisitor2; +import de.monticore.tf.ast.*; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import static de.se_rwth.commons.StringTransformations.capitalize; +import static de.monticore.tf.ruletranslation.Position.*; + +@SuppressWarnings("unused") +public abstract class Rule2ODVisitor implements TFCommonsVisitor2 { + + // The transformation rule in object diagram syntax generated by this visitor + protected Rule2ODState state; + + + public Rule2ODVisitor(Variable2AttributeMap variable2Attributes, Map parents) { + super(); + this.state = new Rule2ODState(variable2Attributes,parents); + } + + public Rule2ODVisitor(Rule2ODState state){ + this.state = state; + } + + + /** + * @param name the name of an object that has been created by this visitor + * @return the object with this name if existent, null otherwise + */ + protected ASTODObject getLhsObjectByName(String name) { + for (ASTODObject o : Util.getAllODObjects(state.getLhs())) { + if (name.equals(o.getName())) { + return o; + } + } + return null; + } + + /** + * @param name the name of an object that has been created by this visitor + * @return the object with this name if existent, null otherwise + */ + protected ASTODObject getRhsObjectByName(String name) { + for (ASTODObject o : Util.getAllODObjects(state.getRhs())) { + if (name.equals(o.getName())) { + return o; + } + } + return null; + } + + /** + * This method will add an ASTODObject to the LHS of the rule. + * If the hierarchyStack is empty, it will just add it to the + * lhs definition. If the hierarchyStack contains an element, + * it will add the object as an inner link of the object in the stack. + * @param object the object + */ + protected void addObjectToLHS(ASTODObject object) { + if (state.getHierarchyLHS().isEmpty()) { + state.getLhs().addODObject(object); + } + else { + ASTODInnerLinkBuilder builder = ODRulesMill.oDInnerLinkBuilder(); + builder.setODObject(object); + state.getHierarchyLHS().peek().addInnerLinks(builder.build()); + } + } + + protected void addObjectToRHS(ASTODObject object) { + if (state.getHierarchyRHS().isEmpty()) { + state.getRhs().addODObject(object); + } + else { + ASTODInnerLinkBuilder builder = ODRulesMill.oDInnerLinkBuilder(); + builder.setODObject(object); + state.getHierarchyRHS().peek().addInnerLinks(builder.build()); + } + } + + @Override + public void visit(ASTTFWhere node) { + state.getGenRule().setConstraint(node.getConstraint().deepClone()); + } + + @Override + public void visit(ASTTFAssignments node) { + for (ASTAssign assign : node.getAssignList()) { + ASTAssignment assignment = ODRulesMill.assignmentBuilder().uncheckedBuild(); + assignment.setLhs(assign.getVariable()); + assignment.setRhs(assign.getValue().deepClone()); + state.getGenRule().addAssignment(assignment); + } + } + + @Override + public void visit(ASTTFFolding node) { + for (de.monticore.tf.tfcommons._ast.ASTFoldingSet f : node.getFoldingSetList()) { + state.getGenRule().addFoldingSet(transfromFoldingSet(f)); + } + } + + @Override + public void endVisit(ASTTFRule node){ + for(String key : state.getVariable2Attributes().getV2a().keySet()){ + String value = state.getNameGen().getNameForElement(state.getVariable2Attributes().getObject(key)); + String attr_value = state.getVariable2Attributes().getAttributeName(key); + if (state.getVariable2Attributes().isList(key)) { + attr_value = attr_value.replace("s.", "sList."); + } + value += ".get" + capitalize(attr_value).replace(".","()."); + if(!value.endsWith(")")) { + value += "()"; + } + if(!value.startsWith("$")){ + value = "m." + value; + } + try { + ASTAssignment assignment = ODRulesMill.assignmentBuilder().uncheckedBuild(); + assignment.setLhs(key); + assignment.setRhs(new ODRulesParser().parse_StringExpression(value).get()); + state.getGenRule().getAssignmentList().add(0,assignment); } + catch (IOException e) { + Log.error("0xF0901: Invalid Java Expression used in Transformation Rule", e); + } + } + } + + @Override + public void visit(ASTTFDo node) { + state.getGenRule().setDoBlock(node.getMCJavaBlock().deepClone()); + } + + private de.monticore.tf.odrules._ast.ASTFoldingSet transfromFoldingSet(de.monticore.tf.tfcommons._ast.ASTFoldingSet f) { + de.monticore.tf.odrules._ast.ASTFoldingSet s = ODRulesMill.foldingSetBuilder().uncheckedBuild(); + s.setObjectNamesList(new ArrayList<>()); + s.getObjectNamesList().addAll(f.getObjectNamesList()); + return s; + } + + protected void handleLHS(IPattern node, ASTODObject o_lhs) { + doHandle(node, o_lhs); + if (state.isNegative()) { + ASTStereotype not = ODRulesMill.stereotypeBuilder().uncheckedBuild(); + ASTStereoValue value = ODRulesMill.stereoValueBuilder().uncheckedBuild(); + value.setName(ODRuleStereotypes.NOT); + not.addValues(value); + + o_lhs.setStereotype(not); + } + } + + protected void handleRHS(IPattern node, ASTODObject o_rhs) { + doHandle(node, o_rhs); + } + + private void doHandle(IPattern node, ASTODObject obj) { + obj.setName(state.getNameGen().getNameForElement(node, state.getParents())); + List qType = + Splitters.DOT.splitToList(node._getTFElementType().getName()); + ASTMCBasicGenericType type = ODRulesMill.mCBasicGenericTypeBuilder().uncheckedBuild(); + type.setNameList(qType); + obj.setType(type); + } + + protected void handleListLHS(IList node, ITFObject listElement) { + String objectName = state.getNameGen().getNameForElement(listElement, node); + ASTODObject object = getLhsObjectByName(objectName); + doHandleList(node, object); + } + + protected void handleListRHS(IList node, ITFObject listElement) { + String objectName = state.getNameGen().getNameForElement(listElement, node); + ASTODObject object = getRhsObjectByName(objectName); + doHandleList(node, object); + } + + private void doHandleList(IList node, ASTODObject object) { + if (node.isPresentSchemaVarName()) { + object.setName(node.getSchemaVarName()); + } + ASTStereotype list = ODRulesMill.stereotypeBuilder().uncheckedBuild(); + ASTStereoValue value = ODRulesMill.stereoValueBuilder().uncheckedBuild(); + value.setName(ODRuleStereotypes.LIST); + list.addValues(value); + + object.setStereotype(list); + } + + /** + * Creates an ASTODObject for the given list node + * and puts it on the hierarchy stack. + * @param node the list node + */ + protected void handleList(IList node) { + ASTODObject objectLhs = ODRulesMill.oDObjectBuilder().uncheckedBuild(); + ASTODObject objectRhs = ODRulesMill.oDObjectBuilder().uncheckedBuild(); + + String objectName = state.getNameGen().getNameForElement(node); + objectLhs.setName(objectName); + objectRhs.setName(objectName); + + // set stereotype to <> + ASTStereotype list = ODRulesMill.stereotypeBuilder().uncheckedBuild(); + ASTStereoValue value = ODRulesMill.stereoValueBuilder().uncheckedBuild(); + value.setName(ODRuleStereotypes.LIST); + list.addValues(value); + objectLhs.setStereotype(list); + objectRhs.setStereotype(list); + + // set type to IList + List qType = Splitters.DOT.splitToList("de.monticore.tf.ast.IList"); + ASTMCBasicGenericType type = ODRulesMill.mCBasicGenericTypeBuilder().uncheckedBuild(); + type.setNameList(qType); + objectLhs.setType(type); + objectRhs.setType(type); + + addObjectToLHS(objectLhs); + state.getHierarchyLHS().push(objectLhs); + addObjectToRHS(objectRhs); + state.getHierarchyRHS().push(objectRhs); + } + + /** + * Creates an ASTODObject for the given optional node + * and puts it on the hierarchy stack. + * @param node the optional node + */ + protected void handleOpt(IOptional node) { + ASTODObject objectLhs = ODRulesMill.oDObjectBuilder().uncheckedBuild(); + ASTODObject objectRhs = ODRulesMill.oDObjectBuilder().uncheckedBuild(); + + String objectName = state.getNameGen().getNameForElement(node); + objectLhs.setName(objectName); + objectRhs.setName(objectName); + + // set stereotype to <> + ASTStereotype opt = ODRulesMill.stereotypeBuilder().uncheckedBuild(); + ASTStereoValue value = ODRulesMill.stereoValueBuilder().uncheckedBuild(); + value.setName(ODRuleStereotypes.OPTIONAL); + opt.addValues(value); + objectLhs.setStereotype(opt); + objectRhs.setStereotype(opt); + + // set type to IOptional + List qType = Splitters.DOT.splitToList("de.monticore.tf.ast.IOptional"); + ASTMCBasicGenericType type = ODRulesMill.mCBasicGenericTypeBuilder().uncheckedBuild(); + type.setNameList(qType); + objectLhs.setType(type); + objectRhs.setType(type); + + addObjectToLHS(objectLhs); + state.getHierarchyLHS().push(objectLhs); + addObjectToRHS(objectRhs); + state.getHierarchyRHS().push(objectRhs); + } + + protected boolean isOnLHS() { + return (state.getPosition().equals(LHS) || state.getPosition().equals(BOTH)); + } + + protected boolean isOnRHS() { + return (state.getPosition().equals(RHS) || state.getPosition().equals(BOTH)); + } + + protected ASTExpression createQualifiedNameExpression(List parts) { + try { + Optional exp = new ODRulesParser() + .parse_StringExpression( + Names.getQualifiedName(parts)); + if (exp.isPresent()) { + return exp.get(); + } + else { + Log.error( + "0xF0006: " + Names.getQualifiedName(parts) + " cannot be treated as an expression"); + return ODRulesMill.nameExpressionBuilder().uncheckedBuild(); + } + } + catch (IOException e) { + Log.error( + "0xF0004: " + Names.getQualifiedName(parts) + " cannot be treated as an expression"); + return ODRulesMill.nameExpressionBuilder().uncheckedBuild(); + } + } + + protected ASTODLink createLHSComposition(ITFObject parent, ITFElement child, String roleName, String genericType, boolean attrIsIterated, boolean attrIsOptional) { + ITFObject component = child instanceof IReplacement ? + (ITFObject) ((IReplacement) child).getLhs() : + (ITFObject) child.getTFElement(); + // optionals & lists only group elements, they do not represent elements themselves. + // in order to create links, we need to get the actual element of the optional + component = getActualElement(component); + return createLink(parent, roleName, genericType, component, attrIsIterated, attrIsOptional); + } + + protected ASTODLink createRHSComposition(ITFObject parent, ITFElement child, String roleName, String genericType, + boolean attrIsIterated, boolean attrIsOptional) { + ITFObject component = child instanceof IReplacement ? + (ITFObject) ((IReplacement) child).getRhs() : + (ITFObject) child.getTFElement(); + // optionals & lists only group elements, they do not represent elements themselves. + // in order to create links, we need to get the actual element of the optional + component = getActualElement(component); + return createLink(parent, roleName, genericType, component, attrIsIterated, attrIsOptional); + } + + private ITFObject getActualElement(ITFObject component) { + if (component instanceof IOptional || component instanceof IList) { + component = (ITFObject) component.getTFElement(); + return getActualElement(component); + } else { + return component; + } + } + + protected ASTODLink createLinkInsertAt(ITFObject parent, String roleName, String genericType, ITFObject component, String insertType, boolean attrIsIterated, boolean attrIsOptional) { + ASTODLink composition = createLink(parent, roleName, genericType, component, attrIsIterated, attrIsOptional); + ASTStereotype stereotype = composition.getStereotype(); + ASTStereoValue stereoValueType = ODRulesMill.stereoValueBuilder().uncheckedBuild(); + stereoValueType.setName("insertType"); + stereoValueType.setContent(insertType); + stereotype.addValues(stereoValueType); + return composition; + } + protected ASTODLink createLinkInsertAt(ITFObject parent, String roleName, String genericType, ITFObject component, String insertType, + ITFObject insertAt, boolean attrIsIterated, boolean attrIsOptional) { + ASTODLink composition = createLinkInsertAt(parent, roleName, genericType, component, insertType, attrIsIterated, attrIsOptional); + ASTStereoValue stereoValueAt = ODRulesMill.stereoValueBuilder().uncheckedBuild(); + stereoValueAt.setName("insertAt"); + stereoValueAt.setContent(state.getNameGen().getNameForElement(getActualElement(insertAt))); + composition.getStereotype().addValues(stereoValueAt); + return composition; + } + + protected ASTODLink createLink(ITFObject parent, String roleName, String genericType, + ITFObject component, boolean attrIsIterated, boolean attrIsOptional){ + ASTODLink composition = ODRulesMill.oDLinkBuilder().uncheckedBuild(); + composition.setRightRole(roleName); + List lhs_compositeName = Splitters.DOT.splitToList(state.getNameGen().getNameForElement(parent, state.getParents())); + ASTMCQualifiedName lhs_compositeQName = ODRulesMill.mCQualifiedNameBuilder().uncheckedBuild(); + lhs_compositeQName.setPartsList(lhs_compositeName); + composition.addLeftReferenceName(lhs_compositeQName); + List lhs_componentName = Splitters.DOT.splitToList(state.getNameGen().getNameForElement(component,state.getParents())); + ASTMCQualifiedName lhs_componentQName = ODRulesMill.mCQualifiedNameBuilder().uncheckedBuild(); + lhs_componentQName.setPartsList(lhs_componentName); + composition.addRightReferenceName(lhs_componentQName); + ASTCardinality cardinality = ODRulesMill.cardinalityBuilder().uncheckedBuild(); + cardinality.setOptional(attrIsOptional && !attrIsIterated); + cardinality.setOne(!attrIsOptional && !attrIsIterated); + cardinality.setOneToMany(!attrIsOptional && attrIsIterated); + cardinality.setMany(attrIsOptional && attrIsIterated); + composition.setAttributeCardinality(cardinality); + if(!genericType.isEmpty()) { + ASTStereotype stereotype = ODRulesMill.stereotypeBuilder().uncheckedBuild(); + ASTStereoValue stereoValueType = ODRulesMill.stereoValueBuilder().uncheckedBuild(); + stereoValueType.setName("genericType"); + stereoValueType.setContent(genericType); + stereotype.addValues(stereoValueType); + composition.setStereotype(stereotype); + } + return composition; + } + + protected ASTODAttribute createAttributeForBoolean(String name, ASTExpression value, boolean isOptional) { + ASTODAttribute attribute = ODRulesMill.oDAttributeBuilder().uncheckedBuild(); + attribute.setName(name); + ASTMCPrimitiveType primitiveType = ODRulesMill.mCPrimitiveTypeBuilder().uncheckedBuild(); + primitiveType.setPrimitive(ASTConstantsMCBasicTypes.BOOLEAN); + attribute.setMCType(primitiveType); + attribute.setSingleValue(value); + attribute.setAttributeCardinality(ODRulesMill.cardinalityBuilder().uncheckedBuild()); + attribute.getAttributeCardinality().setOptional(isOptional); + attribute.getAttributeCardinality().setOne(!isOptional); + return attribute; + } + + protected ASTExpression createExpressionForBoolean(int astConstant) { + ASTBooleanLiteral booleanLiteral = ODRulesMill.booleanLiteralBuilder().uncheckedBuild(); + booleanLiteral.setSource(astConstant); + return ExpressionsBasisMill.literalExpressionBuilder().setLiteral(booleanLiteral).build(); + } + + protected ASTExpression createExpressionForInt(int astConstant) { + ASTIntLiteral intLiteral = ODRulesMill.intLiteralBuilder().uncheckedBuild(); + intLiteral.setSource(""+astConstant); + return ExpressionsBasisMill.literalExpressionBuilder().setLiteral(intLiteral).build(); + } + + protected ASTODAttribute createAttributeForString(String name, boolean isOptional) { + ASTODAttribute attribute = ODRulesMill.oDAttributeBuilder().uncheckedBuild(); + ASTMCBasicGenericType type = ODRulesMill.mCBasicGenericTypeBuilder().uncheckedBuild(); + type.setNameList(Splitters.DOT.splitToList("String")); + attribute.setMCType(type); + attribute.setName(name); + attribute.setAttributeCardinality(ODRulesMill.cardinalityBuilder().uncheckedBuild()); + attribute.getAttributeCardinality().setOptional(isOptional); + attribute.getAttributeCardinality().setOne(!isOptional); + return attribute; + } + + protected ASTExpression createPrimaryExpressionForString(String variableName) { + ASTStringLiteral stringLiteral = ODRulesMill.stringLiteralBuilder().uncheckedBuild(); + stringLiteral.setSource(variableName); + return ExpressionsBasisMill.literalExpressionBuilder().setLiteral(stringLiteral).build(); + } + + protected void createQualifiedNameExpressionAsAttributeValue(ASTODAttribute attribute, + String... parts) { + List stringList = Lists.newArrayList(parts); + ASTExpression qualifiedNameExpression = createQualifiedNameExpression(stringList); + attribute.setSingleValue(qualifiedNameExpression); + } + + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/MCArrayTypesNodeIdentHelper.java b/monticore-grammar/src/main/java/de/monticore/types/MCArrayTypesNodeIdentHelper.java new file mode 100644 index 0000000000..f4cf14a4e5 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/MCArrayTypesNodeIdentHelper.java @@ -0,0 +1,16 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types; + +import de.monticore.generating.templateengine.reporting.commons.Layouter; +import de.monticore.types.mcarraytypes._ast.ASTMCArrayType; + +/** + * NodeIdentHelper for MCArrayTypes, mainly used for Reporting + */ +public class MCArrayTypesNodeIdentHelper extends MCBasicTypesNodeIdentHelper { + + public String getIdent(ASTMCArrayType a){ + return format(a.printTypeWithoutBrackets(), Layouter.nodeName(a)); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/MCBasicTypesHelper.java b/monticore-grammar/src/main/java/de/monticore/types/MCBasicTypesHelper.java new file mode 100644 index 0000000000..dd214ddb5f --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/MCBasicTypesHelper.java @@ -0,0 +1,72 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.types; + +import de.monticore.types.mcbasictypes._ast.ASTConstantsMCBasicTypes; + +public class MCBasicTypesHelper { + + /** + * Map the String with a primitive type, e.g. "int" to its + * enumerative Number, e.g. ASTConstantsMCBasicTypes.INT + * Returns -1 if illegal name; no error message + * @param typeName + * @return + */ + public static int primitiveName2Const(String typeName) { + if (null == typeName || typeName.isEmpty()) { + return -1; + } + switch (typeName) { + case "boolean": + return ASTConstantsMCBasicTypes.BOOLEAN; + case "float": + return ASTConstantsMCBasicTypes.FLOAT; + case "byte": + return ASTConstantsMCBasicTypes.BYTE; + case "char": + return ASTConstantsMCBasicTypes.CHAR; + case "double": + return ASTConstantsMCBasicTypes.DOUBLE; + case "int": + return ASTConstantsMCBasicTypes.INT; + case "short": + return ASTConstantsMCBasicTypes.SHORT; + case "long": + return ASTConstantsMCBasicTypes.LONG; + default: + return -1; + } + } + + /** + * Map the integer e.g. ASTConstantsMCBasicTypes.INT + * to the respective String with a primitive type, e.g. "int" + * Returns "unknownType" if illegal number; no error message + * @param typeConstant + * @return + */ + public static String primitiveConst2Name(int typeConstant) { + switch (typeConstant) { + case ASTConstantsMCBasicTypes.BOOLEAN: + return "boolean"; + case ASTConstantsMCBasicTypes.BYTE: + return "byte"; + case ASTConstantsMCBasicTypes.CHAR: + return "char"; + case ASTConstantsMCBasicTypes.SHORT: + return "short"; + case ASTConstantsMCBasicTypes.INT: + return "int"; + case ASTConstantsMCBasicTypes.FLOAT: + return "float"; + case ASTConstantsMCBasicTypes.LONG: + return "long"; + case ASTConstantsMCBasicTypes.DOUBLE: + return "double"; + default: + return "unknownType"; + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/MCBasicTypesNodeIdentHelper.java b/monticore-grammar/src/main/java/de/monticore/types/MCBasicTypesNodeIdentHelper.java new file mode 100644 index 0000000000..ab9913d403 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/MCBasicTypesNodeIdentHelper.java @@ -0,0 +1,43 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types; + +import de.monticore.generating.templateengine.reporting.commons.ASTNodeIdentHelper; +import de.monticore.generating.templateengine.reporting.commons.Layouter; +import de.monticore.types.mcbasictypes.MCBasicTypesMill; +import de.monticore.types.mcbasictypes._ast.*; + +/** + * NodeIdentHelper for MCBasicTypes, mainly used for Reporting + */ +public class MCBasicTypesNodeIdentHelper extends ASTNodeIdentHelper { + + public String getIdent(ASTMCPrimitiveType a) { + return format(a.printType(), Layouter.nodeName(a)); + } + + public String getIdent(ASTMCType a) { + return format(a.printType(), Layouter.nodeName(a)); + } + + public String getIdent(ASTMCQualifiedName a) { + return format(a.getQName(), Layouter.nodeName(a)); + } + + public String getIdent(ASTMCQualifiedType a) { + return format(a.getMCQualifiedName().getQName(), Layouter.nodeName(a)); + } + + public String getIdent(ASTMCReturnType a) { + if (a.isPresentMCType()) { + return getIdent(a.getMCType()); + } + else if (a.isPresentMCVoidType()) { + return getIdent(a.getMCVoidType()); + } + return ""; + } + + public String getIdent(ASTMCVoidType a) { + return format("void", Layouter.nodeName(a)); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/MCCollectionTypesNodeIdentHelper.java b/monticore-grammar/src/main/java/de/monticore/types/MCCollectionTypesNodeIdentHelper.java new file mode 100644 index 0000000000..58c84a01e1 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/MCCollectionTypesNodeIdentHelper.java @@ -0,0 +1,61 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types; + +import de.monticore.generating.templateengine.reporting.commons.Layouter; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.mccollectiontypes._ast.*; + +import java.util.Arrays; +import java.util.List; + +/** + * NodeIdentHelper for MCCollectionTypes, mainly used for Reporting + */ +public class MCCollectionTypesNodeIdentHelper extends MCBasicTypesNodeIdentHelper { + + public String getIdent(ASTMCGenericType a) { + StringBuilder name = new StringBuilder(); + List nameList = Arrays.asList(a.printWithoutTypeArguments().split("\\.")); + int nameListSize = nameList.size(); + for (int i = 0; i < nameListSize; i++) { + name.append(nameList.get(i)); + if (i != nameListSize - 1) { + name.append("."); + } + } + return format(name.toString(), Layouter.nodeName(a)); + } + + public String getIdent(ASTMCType a) { + if (a instanceof ASTMCGenericType) { + return format(((ASTMCGenericType) a).printWithoutTypeArguments(), Layouter.nodeName(a)); + } else { + return format(a.printType(), Layouter.nodeName(a)); + } + } + + public String getIdent(ASTMCListType a){ + return format("List",Layouter.nodeName(a)); + } + + public String getIdent(ASTMCSetType a){ + return format("Set",Layouter.nodeName(a)); + } + + public String getIdent(ASTMCMapType a){ + return format("Map", Layouter.nodeName(a)); + } + + public String getIdent(ASTMCOptionalType a){ + return format("Optional", Layouter.nodeName(a)); + } + + public String getIdent(ASTMCBasicTypeArgument a){ + return format(a.getMCQualifiedType().getMCQualifiedName().getQName(), Layouter.nodeName(a)); + } + + public String getIdent(ASTMCPrimitiveTypeArgument a){ + return format(a.getMCPrimitiveType().printType(),Layouter.nodeName(a)); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/MCFullGenericTypesNodeIdentHelper.java b/monticore-grammar/src/main/java/de/monticore/types/MCFullGenericTypesNodeIdentHelper.java new file mode 100644 index 0000000000..15cced8cb1 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/MCFullGenericTypesNodeIdentHelper.java @@ -0,0 +1,16 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types; + +import de.monticore.generating.templateengine.reporting.commons.Layouter; +import de.monticore.types.mcfullgenerictypes._ast.ASTMCMultipleGenericType; + +/** + * NodeIdentHelper for MCFullGenericTypes, mainly used for Reporting + */ +public class MCFullGenericTypesNodeIdentHelper extends MCSimpleGenericTypesNodeIdentHelper { + + public String getIdent(ASTMCMultipleGenericType a){ + return format(a.printWithoutTypeArguments(), Layouter.nodeName(a)); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/MCSimpleGenericTypesNodeIdentHelper.java b/monticore-grammar/src/main/java/de/monticore/types/MCSimpleGenericTypesNodeIdentHelper.java new file mode 100644 index 0000000000..897cd7bc02 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/MCSimpleGenericTypesNodeIdentHelper.java @@ -0,0 +1,28 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types; + +import de.monticore.generating.templateengine.reporting.commons.Layouter; +import de.monticore.types.mccollectiontypes._ast.ASTMCGenericType; +import de.monticore.types.mcsimplegenerictypes._ast.ASTMCBasicGenericType; +import de.monticore.types.mcsimplegenerictypes._ast.ASTMCCustomTypeArgument; +import de.monticore.types.mcsimplegenerictypes.MCSimpleGenericTypesMill; + +/** + * NodeIdentHelper for MCSimpleGenericTypes, mainly used for Reporting + */ +public class MCSimpleGenericTypesNodeIdentHelper extends MCCollectionTypesNodeIdentHelper { + public String getIdent(ASTMCBasicGenericType type){ + return format(type.printWithoutTypeArguments(), Layouter.nodeName(type)); + } + + @Override + public String getIdent(ASTMCGenericType a){ + return format(a.printWithoutTypeArguments(), Layouter.nodeName(a)); + } + + public String getIdent(ASTMCCustomTypeArgument a){ + return format(a.getMCType().printType(),Layouter.nodeName(a)); + } + + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/MCTypeFacade.java b/monticore-grammar/src/main/java/de/monticore/types/MCTypeFacade.java new file mode 100644 index 0000000000..f8496516a5 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/MCTypeFacade.java @@ -0,0 +1,277 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types; + +import com.google.common.collect.Lists; +import de.monticore.types.mcarraytypes.MCArrayTypesMill; +import de.monticore.types.mcarraytypes._ast.ASTMCArrayType; +import de.monticore.types.mcbasictypes.MCBasicTypesMill; +import de.monticore.types.mcbasictypes._ast.*; +import de.monticore.types.mccollectiontypes._ast.*; +import de.monticore.types.mcfullgenerictypes.MCFullGenericTypesMill; +import de.monticore.types.mcfullgenerictypes._ast.ASTMCWildcardTypeArgument; +import de.monticore.types.mcsimplegenerictypes._ast.ASTMCBasicGenericType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * facade for creation of ASTMCTypes + */ +public class MCTypeFacade { + + protected static final String PACKAGE_SEPARATOR = "\\."; + + private static MCTypeFacade MCTypeFacade; + + private MCTypeFacade() { + } + + public static MCTypeFacade getInstance() { + if (MCTypeFacade == null) { + MCTypeFacade = new MCTypeFacade(); + } + return MCTypeFacade; + } + + /** + * qualified type creation methods + */ + + public ASTMCQualifiedName createQualifiedName(final String name) { + return MCBasicTypesMill.mCQualifiedNameBuilder().setPartsList(Arrays.asList(name.split(PACKAGE_SEPARATOR))).build(); + } + + public ASTMCQualifiedType createQualifiedType(final Class clazz) { + return createQualifiedType(clazz.getSimpleName()); + } + + public ASTMCQualifiedType createQualifiedType(final String name) { + ASTMCQualifiedName qualName = MCBasicTypesMill.mCQualifiedNameBuilder().setPartsList(Arrays.asList(name.split(PACKAGE_SEPARATOR))).build(); + return MCBasicTypesMill.mCQualifiedTypeBuilder().setMCQualifiedName(qualName).build(); + } + + /** + * typeArgument creation methods + */ + + public ASTMCBasicTypeArgument createBasicTypeArgumentOf(String name) { + return MCFullGenericTypesMill.mCBasicTypeArgumentBuilder() + .setMCQualifiedType(createQualifiedType(name)) + .build(); + } + + public ASTMCWildcardTypeArgument createWildCardWithNoBounds() { + return MCFullGenericTypesMill.mCWildcardTypeArgumentBuilder().build(); + } + + public ASTMCWildcardTypeArgument createWildCardWithUpperBoundType(final Class upperBound) { + return createWildCardWithUpperBoundType(this.createQualifiedType(upperBound)); + } + + public ASTMCWildcardTypeArgument createWildCardWithUpperBoundType(final String upperBound) { + return createWildCardWithUpperBoundType(this.createQualifiedType(upperBound)); + } + + public ASTMCWildcardTypeArgument createWildCardWithUpperBoundType(final ASTMCType upperBound) { + return MCFullGenericTypesMill.mCWildcardTypeArgumentBuilder() + .setUpperBound(upperBound) + .build(); + } + + /** + * optional type of ASTMCBasicTypeArgument + */ + + public ASTMCOptionalType createOptionalTypeOf(final String name) { + return MCFullGenericTypesMill.mCOptionalTypeBuilder() + .setMCTypeArgument(createBasicTypeArgumentOf(name)) + .build(); + } + + public ASTMCOptionalType createOptionalTypeOf(final Class clazz) { + return createOptionalTypeOf(clazz.getSimpleName()); + } + + public ASTMCOptionalType createOptionalTypeOf(final ASTMCType type) { + return createOptionalTypeOf(type.printType()); + } + + public ASTMCOptionalType createOptionalTypeOf(final ASTMCTypeArgument type) { + if (!type.getMCTypeOpt().isPresent()) { + return createOptionalTypeOf("?"); + } + return createOptionalTypeOf(type.getMCTypeOpt().get().printType()); + } + + /** + * list types of ASTMCBasicTypeArgument + */ + + public ASTMCListType createListTypeOf(final String name) { + return MCFullGenericTypesMill.mCListTypeBuilder() + .setMCTypeArgument(createBasicTypeArgumentOf(name)) + .build(); + } + + public ASTMCListType createListTypeOf(final Class clazz) { + return createListTypeOf(clazz.getSimpleName()); + } + + public ASTMCListType createListTypeOf(final ASTMCType type) { + return createListTypeOf(type.printType()); + } + + public ASTMCListType createListTypeOf(final ASTMCTypeArgument type) { + if (!type.getMCTypeOpt().isPresent()) { + return createListTypeOf("?"); + } + return createListTypeOf(type.getMCTypeOpt().get()); + } + + /** + * set types of ASTMCBasicTypeArgument + */ + + public ASTMCSetType createSetTypeOf(final String name) { + return MCFullGenericTypesMill.mCSetTypeBuilder() + .setMCTypeArgument(createBasicTypeArgumentOf(name)) + .build(); + } + + public ASTMCSetType createSetTypeOf(final Class clazz) { + return createSetTypeOf(clazz.getSimpleName()); + } + + public ASTMCSetType createSetTypeOf(final ASTMCType type) { + return createSetTypeOf(type.printType()); + } + + public ASTMCSetType createSetTypeOf(final ASTMCTypeArgument type) { + if (!type.getMCTypeOpt().isPresent()) { + return createSetTypeOf("?"); + } + return createSetTypeOf(type.getMCTypeOpt().get().printType()); + } + + /** + * collection types of ASTMCBasicTypeArgument + */ + + public ASTMCGenericType createCollectionTypeOf(final String name) { + return MCFullGenericTypesMill.mCBasicGenericTypeBuilder() + .setNamesList(Lists.newArrayList("Collection")) + .setMCTypeArgumentsList(Lists.newArrayList(createBasicTypeArgumentOf(name))) + .build(); + } + + public ASTMCGenericType createCollectionTypeOf(final Class clazz) { + return createCollectionTypeOf(clazz.getSimpleName()); + } + + public ASTMCGenericType createCollectionTypeOf(final ASTMCType type) { + return createCollectionTypeOf(type.printType()); + } + + /** + * map types of ASTMCBasicTypeArgument + */ + + public ASTMCMapType createMapTypeOf(final String firstType, final String secondType) { + return MCFullGenericTypesMill.mCMapTypeBuilder() + .setKey(createBasicTypeArgumentOf(firstType)) + .setValue(createBasicTypeArgumentOf(secondType)) + .build(); + } + + public ASTMCMapType createMapTypeOf(final Class firstType, final Class secondType) { + return createMapTypeOf(firstType.getSimpleName(), secondType.getSimpleName()); + } + + public ASTMCMapType createMapTypeOf(final ASTMCType firstType, final ASTMCType secondType) { + return createMapTypeOf(firstType.printType(), secondType.printType()); + } + + public ASTMCMapType createMapTypeOf(final ASTMCTypeArgument firstType, final ASTMCTypeArgument secondType) { + String first = firstType.getMCTypeOpt().isPresent()?firstType.getMCTypeOpt().get().printType():"?"; + String second = secondType.getMCTypeOpt().isPresent()?secondType.getMCTypeOpt().get().printType():"?"; + return createMapTypeOf(first, second); + } + + /** + * create ASTMCBasicGenericType + */ + public ASTMCBasicGenericType createBasicGenericTypeOf(final List nameList, List typeArguments) { + return MCFullGenericTypesMill.mCBasicGenericTypeBuilder() + .setNamesList(nameList) + .setMCTypeArgumentsList(typeArguments) + .build(); + } + + public ASTMCBasicGenericType createBasicGenericTypeOf(final String name, List typeArguments) { + return createBasicGenericTypeOf(new ArrayList<>(Arrays.asList(name.split("\\."))), typeArguments); + } + + public ASTMCBasicGenericType createBasicGenericTypeOf(final String name, ASTMCTypeArgument... typeArguments) { + return createBasicGenericTypeOf(name, new ArrayList<>(Arrays.asList(typeArguments))); + } + + public ASTMCBasicGenericType createBasicGenericTypeOf(final String name, String... typeArgumentStrings) { + List typeArgumentList = Arrays.stream(typeArgumentStrings) + .map(this::createBasicTypeArgumentOf) + .collect(Collectors.toList()); + return createBasicGenericTypeOf(new ArrayList<>(Arrays.asList(name.split("\\."))), + typeArgumentList); + } + + /** + * array types + */ + + public ASTMCArrayType createArrayType(final ASTMCType type, int dimension) { + return MCArrayTypesMill.mCArrayTypeBuilder() + .setMCType(type) + .setDimensions(dimension) + .build(); + } + + public ASTMCArrayType createArrayType(final Class clazz, int dimension) { + return createArrayType(clazz.getSimpleName(), dimension); + } + + public ASTMCArrayType createArrayType(final String name, int dimension) { + return createArrayType(this.createQualifiedType(name), dimension); + } + + /** + * primitive types + */ + + public ASTMCVoidType createVoidType() { + return MCBasicTypesMill.mCVoidTypeBuilder() + .build(); + } + + public ASTMCPrimitiveType createBooleanType() { + return createPrimitiveType(ASTConstantsMCBasicTypes.BOOLEAN); + } + + public boolean isBooleanType(ASTMCType type) { + return type instanceof ASTMCPrimitiveType && ((ASTMCPrimitiveType) type).isBoolean(); + } + + public ASTMCPrimitiveType createIntType() { + return createPrimitiveType(ASTConstantsMCBasicTypes.INT); + } + + protected ASTMCPrimitiveType createPrimitiveType(int constantsType) { + return MCBasicTypesMill.mCPrimitiveTypeBuilder() + .setPrimitive(constantsType) + .build(); + } + + public ASTMCType createStringType() { + return createQualifiedType("String"); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/AbstractDerive.java b/monticore-grammar/src/main/java/de/monticore/types/check/AbstractDerive.java new file mode 100644 index 0000000000..36b2cefed0 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/AbstractDerive.java @@ -0,0 +1,39 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.literals.mcliteralsbasis._ast.ASTLiteral; +import de.monticore.visitor.ITraverser; + +public abstract class AbstractDerive implements IDerive { + + protected ITraverser traverser; + protected TypeCheckResult typeCheckResult; + + protected ITraverser getTraverser() { + return traverser; + } + + protected TypeCheckResult getTypeCheckResult() { + return typeCheckResult; + } + + public AbstractDerive(ITraverser traverser) { + this.typeCheckResult = new TypeCheckResult(); + this.traverser = traverser; + } + + @Override + public TypeCheckResult deriveType(ASTExpression expr) { + this.getTypeCheckResult().reset(); + expr.accept(this.getTraverser()); + return this.getTypeCheckResult().copy(); + } + + @Override + public TypeCheckResult deriveType(ASTLiteral lit) { + this.getTypeCheckResult().reset(); + lit.accept(this.getTraverser()); + return this.getTypeCheckResult().copy(); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/AbstractDeriveFromExpression.java b/monticore-grammar/src/main/java/de/monticore/types/check/AbstractDeriveFromExpression.java new file mode 100644 index 0000000000..cc8bd2c672 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/AbstractDeriveFromExpression.java @@ -0,0 +1,135 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.expressionsbasis._symboltable.IExpressionsBasisScope; +import de.monticore.expressions.expressionsbasis._visitor.ExpressionsBasisTraverser; +import de.monticore.literals.mcliteralsbasis._ast.ASTLiteral; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.IBasicSymbolsScope; +import de.se_rwth.commons.SourcePosition; +import de.se_rwth.commons.logging.Log; + +import java.util.ArrayList; +import java.util.List; + +import static de.monticore.types.check.SymTypePrimitive.unbox; +import static de.monticore.types.check.TypeCheck.isFloat; +import static de.monticore.types.check.TypeCheck.isDouble; +import static de.monticore.types.check.TypeCheck.isLong; + +public abstract class AbstractDeriveFromExpression { + + public abstract ExpressionsBasisTraverser getTraverser(); + + public IBasicSymbolsScope getScope (IExpressionsBasisScope expressionsBasisScope){ + // is accepted only here, decided on 07.04.2020 + if(!(expressionsBasisScope instanceof IBasicSymbolsScope)){ + Log.error("0xA2307 the enclosing scope of the expression does not implement the interface IBasicSymbolsScope"); + } + // is accepted only here, decided on 07.04.2020 + return (IBasicSymbolsScope) expressionsBasisScope; + } + + protected TypeCheckResult typeCheckResult; + + protected static final String ERROR_MSG = " The expression at source position %s cannot be calculated."; + + public void setTypeCheckResult(TypeCheckResult typeCheckResult) { + this.typeCheckResult = typeCheckResult; + } + + public TypeCheckResult getTypeCheckResult() { + return typeCheckResult; + } + + protected void logError(String errorCode, SourcePosition start){ + Log.error(errorCode+String.format(ERROR_MSG, start)); + } + + /** + * Helper method to store the calculated result or log an error if it is not present + * @param result the calculated result + * @param expression the expression the SymTypeExpressions is calculated for + * @param errorCode the code which is logged in case of an error + */ + protected void storeResultOrLogError(SymTypeExpression result, ASTExpression expression, String errorCode){ + getTypeCheckResult().reset(); + getTypeCheckResult().setResult(result); + if(result.isObscureType()){ + logError(errorCode, expression.get_SourcePositionStart()); + } + } + + /** + * Helper method to calculate the SymTypeExpression of a subexpression in a traverse method + * @param expression the expression the SymTypeExpressions is calculated for + * @return the SymTypeExpression of the expression + */ + protected SymTypeExpression acceptThisAndReturnSymTypeExpression(ASTExpression expression) { + // calculate the type + this.getTypeCheckResult().reset(); + expression.accept(this.getTraverser()); + TypeCheckResult res = this.getTypeCheckResult(); + + // result should be present + if (!res.isPresentResult()) { + // should never happen, we expect results to be present + // indicates that the underlying type resolver is erroneous + this.logError("0xA0169", expression.get_SourcePositionStart()); + return SymTypeExpressionFactory.createObscureType(); + } else { + // else return sym-type + return res.getResult(); + } + } + + /** + * Helper method to calculate the SymTypeExpression of a subliteral in a traverse method + * @param literal the literal the SymTypeExpressions is calculated for + * @return the SymTypeExpression of the literal + */ + protected SymTypeExpression acceptThisAndReturnSymTypeExpression(ASTLiteral literal){ + SymTypeExpression result = SymTypeExpressionFactory.createObscureType(); + literal.accept(getTraverser()); + if(getTypeCheckResult().isPresentResult()){ + result = getTypeCheckResult().getResult(); + } + return result; + } + + /** + * test if the expression is of numeric type (double, float, long, int, char, short, byte) + */ + public boolean isNumericType(SymTypeExpression type) { + return (TypeCheck.isDouble(type) || isFloat(type) || + isIntegralType(type)); + } + + /** + * test if the expression is of integral type (long, int, char, short, byte) + */ + public boolean isIntegralType(SymTypeExpression type) { + return (TypeCheck.isLong(type) || TypeCheck.isInt(type) || + TypeCheck.isChar(type) || TypeCheck.isShort(type) || + TypeCheck.isByte(type)); + } + + protected List calculateInnerTypes(ASTExpression... expressions){ + List result = new ArrayList<>(); + for(ASTExpression expr : expressions){ + result.add(acceptThisAndReturnSymTypeExpression(expr)); + } + return result; + } + + protected boolean checkNotObscure(List typesOfInnerExpressions) { + for(SymTypeExpression expr : typesOfInnerExpressions){ + if(expr.isObscureType()){ + return false; + } + } + return true; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/AbstractSynthesize.java b/monticore-grammar/src/main/java/de/monticore/types/check/AbstractSynthesize.java new file mode 100644 index 0000000000..525f8caeb6 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/AbstractSynthesize.java @@ -0,0 +1,49 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.types.mcbasictypes._ast.ASTMCQualifiedName; +import de.monticore.types.mcbasictypes._ast.ASTMCReturnType; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.visitor.ITraverser; + +public abstract class AbstractSynthesize implements ISynthesize { + + protected ITraverser traverser; + protected TypeCheckResult typeCheckResult; + + protected ITraverser getTraverser() { + return traverser; + } + + protected TypeCheckResult getTypeCheckResult() { + return typeCheckResult; + } + + public AbstractSynthesize(ITraverser traverser) { + this.typeCheckResult = new TypeCheckResult(); + this.traverser = traverser; + } + + @Override + public TypeCheckResult synthesizeType(ASTMCType type) { + this.getTypeCheckResult().reset(); + type.accept(this.getTraverser()); + return this.getTypeCheckResult().copy(); + } + + @Override + public TypeCheckResult synthesizeType(ASTMCReturnType type) { + this.getTypeCheckResult().reset(); + type.accept(this.getTraverser()); + return this.getTypeCheckResult().copy(); + } + + @Override + public TypeCheckResult synthesizeType(ASTMCQualifiedName qName) { + this.getTypeCheckResult().reset(); + qName.accept(this.getTraverser()); + return this.getTypeCheckResult().copy(); + } + + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/AbstractSynthesizeFromType.java b/monticore-grammar/src/main/java/de/monticore/types/check/AbstractSynthesizeFromType.java new file mode 100644 index 0000000000..cf517bd9ed --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/AbstractSynthesizeFromType.java @@ -0,0 +1,55 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.symbols.basicsymbols._symboltable.IBasicSymbolsScope; +import de.monticore.types.mcbasictypes._symboltable.IMCBasicTypesScope; +import de.monticore.types.mcbasictypes._visitor.MCBasicTypesTraverser; +import de.se_rwth.commons.logging.Log; + +import java.util.List; +import java.util.Optional; + +public abstract class AbstractSynthesizeFromType { + + public abstract MCBasicTypesTraverser getTraverser(); + + public IBasicSymbolsScope getScope (IMCBasicTypesScope mcBasicTypesScope){ + // is accepted only here, decided on 07.04.2020 + if(!(mcBasicTypesScope instanceof IBasicSymbolsScope)){ + Log.error("0xA1308 the enclosing scope of the type does not implement the interface IBasicSymbolsScope"); + } + // is accepted only here, decided on 07.04.2020 + return (IBasicSymbolsScope) mcBasicTypesScope; + } + + /** + * Storage in the Visitor: result of the last endVisit. + * This attribute is synthesized upward. + */ + public TypeCheckResult typeCheckResult = new TypeCheckResult(); + + public Optional getResult() { + return Optional.of(getTypeCheckResult().getResult()); + } + + public void init() { + typeCheckResult = new TypeCheckResult(); + } + + public void setTypeCheckResult(TypeCheckResult typeCheckResult){ + this.typeCheckResult = typeCheckResult; + } + + public TypeCheckResult getTypeCheckResult() { + return typeCheckResult; + } + + protected boolean checkNotObscure(List typesOfInnerExpressions) { + for(SymTypeExpression expr : typesOfInnerExpressions){ + if(expr.isObscureType()){ + return false; + } + } + return true; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/CompKindExprDeSer.java b/monticore-grammar/src/main/java/de/monticore/types/check/CompKindExprDeSer.java new file mode 100644 index 0000000000..3658d56ac7 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/CompKindExprDeSer.java @@ -0,0 +1,21 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.symboltable.serialization.json.JsonObject; +import de.monticore.types.check.CompKindExpression; +import de.monticore.types.check.FullCompKindExprDeSer; +import org.checkerframework.checker.nullness.qual.NonNull; + +/** + * Serializes and deserializes {@link CompKindExpression}s to and from their Json encoding. + * This interface should be implemented separately for the different subtypes of {@link CompKindExpression}. + * These separate implementations should be composed into an implementation of {@link FullCompKindExprDeSer}. + * + * @param the {@link CompKindExpression} that this class (de-)serializes. + */ +public interface CompKindExprDeSer { + + String serializeAsJson(@NonNull T toSerialize); + + T deserialize(@NonNull JsonObject serialized); +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/CompKindExpression.java b/monticore-grammar/src/main/java/de/monticore/types/check/CompKindExpression.java new file mode 100644 index 0000000000..59496cec3e --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/CompKindExpression.java @@ -0,0 +1,158 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import com.google.common.base.Preconditions; +import de.monticore.expressions.assignmentexpressions._ast.ASTAssignmentExpression; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.expressionsbasis._ast.ASTNameExpression; +import de.monticore.symbols.basicsymbols._symboltable.VariableSymbol; +import de.monticore.symbols.compsymbols._symboltable.ComponentSymbol; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * Represents all sorts of component kinds. E.g., a {@code ComponentExpression} can represent a generic component with + * bound type arguments {@code MyComp}. This is not representable by Symbols alone, as generic + * components only have unspecific type parameters ({@code MyComp}. + */ +public abstract class CompKindExpression { + + protected final ComponentSymbol component; + protected LinkedHashMap parameterBindings; + protected List arguments; + + /** + * @return a {@code List} of the configuration arguments of this component. + */ + public List getArguments() { + return this.arguments; + } + + /** + * @param argument the configuration argument to add to this component. + */ + public void addArgument(ASTExpression argument) { + Preconditions.checkNotNull(argument); + this.arguments.add(argument); + } + + /** + * @param arguments the configuration arguments to add to this component. + * @see this#addArgument(ASTExpression) + */ + public void addArgument(List arguments) { + Preconditions.checkNotNull(arguments); + Preconditions.checkArgument(!arguments.contains(null)); + for (ASTExpression argument : arguments) { + this.addArgument(argument); + } + } + + public Optional getParamBindingFor(VariableSymbol var) { + Preconditions.checkNotNull(var); + return Optional.ofNullable(this.getParamBindings().get(var)); + } + + public Map getParamBindings() { + return Collections.unmodifiableMap(this.parameterBindings); + } + + public List getParamBindingsAsList() { + return new ArrayList<>(this.getParamBindings().values()); + } + + public void bindParams() { + List parameterArguments = this.getArguments(); + + int firstKeywordArgument = 0; + LinkedHashMap keywordExpressionMap = new LinkedHashMap<>(); + LinkedHashMap parameterBindings = new LinkedHashMap<>(); + // We know LinkedHashMaps are ordered by insertion time. As we rely on the fact that the ordering of the + // arguments is consistent with the ordering in the map, the following iteration ensures it: + for (int i = 0; i < this.getTypeInfo().getParameters().size(); i++) { + if (i < parameterArguments.size()) // Deal with wrong number of parameters through cocos + if (parameterArguments.get(i) instanceof ASTAssignmentExpression + && ((ASTAssignmentExpression) parameterArguments.get(i)).getLeft() instanceof ASTNameExpression) { + keywordExpressionMap.put(((ASTNameExpression) ((ASTAssignmentExpression) parameterArguments.get(i)) + .getLeft()).getName(), parameterArguments.get(i)); + } else { + parameterBindings.put(this.getTypeInfo().getParameters().get(i), parameterArguments.get(i)); + firstKeywordArgument++; + } + } + + // iterate over keyword-based arguments (CoCo assures that no position-based argument occurs + // after the first keyword-based argument) + for (int j = firstKeywordArgument; j < this.getTypeInfo().getParameters().size(); j++) { + if (keywordExpressionMap.containsKey(this.getTypeInfo().getParameters().get(j).getName()) && + !parameterBindings.containsKey(this.getTypeInfo().getParameters().get(j))) { + parameterBindings.put(this.getTypeInfo().getParameters().get(j), + keywordExpressionMap.get(this.getTypeInfo().getParameters().get(j).getName())); + } + } + + this.parameterBindings = parameterBindings; + } + protected CompKindExpression(ComponentSymbol component) { + Preconditions.checkNotNull(component); + this.component = component; + this.arguments = new ArrayList<>(); + this.parameterBindings = new LinkedHashMap<>(); + } + + public ComponentSymbol getTypeInfo() { + return this.component; + } + + public abstract String printName(); + + public abstract String printFullName(); + + /** + * @return The {@link CompKindExpression} that represents this component's super components. E.g., given + * {@code Comp extends Parent>}, the returned list for component expression {@code Comp} + * contains a single entry representing {@code Parent>}. The List is empty if the component has + * no super components. + */ + public abstract List getSuperComponents(); + + /** + * Returns the SymTypeExpression of the type of the port specified by {@code portName}. If the port's type depends on + * type parameters which are assigned by this CompTypeExpression, they are resolved in the returned + * SymTypeExpression. E.g., let assume this component's type expression is {@code Comp} and Comp is defined by + * {@code Comp}, having a port of type {@code T}. Then, as the type argument for {@code T} is {@code Person}, the + * SymTypeExpression returned by this method will be {@code Person} for that port. + * + * @param portName The name of the port for whom the type is requested. + * @return The {@code SymTypeExpressions} of the port's type enclosed in an {@code Optional}. An empty {@code + * Optional} if the component has no such port. + */ + public abstract Optional getTypeOfPort(String portName); + + /** + * Returns the SymTypeExpression of the type of the parameter specified by {@code parameterName}. If the parameter's + * type depends on type parameters which are assigned by this CompTypeExpression, they are resolved in the returned + * SymTypeExpression. E.g., let assume this component's type expression is {@code Comp} and Comp is defined by + * {@code Comp}, having a parameter of type {@code T}. Then, as the type argument for {@code T} is {@code Person}, + * the SymTypeExpression returned by this method will be {@code Person} for that parameter. + * + * @param parameterName The name of the parameter for whom the type is requested. + * @return The {@code SymTypeExpressions} of the parameter's type enclosed in an {@code Optional}. An empty {@code + * Optional} if the component has no such parameter. + */ + public abstract Optional getTypeOfParameter(String parameterName); + + public CompKindExpression deepClone() { + return deepClone(getTypeInfo()); + } + + public abstract CompKindExpression deepClone(ComponentSymbol component); + + public abstract boolean deepEquals(CompKindExpression compSymType); +} + diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/DeriveSymTypeOfAssignmentExpressions.java b/monticore-grammar/src/main/java/de/monticore/types/check/DeriveSymTypeOfAssignmentExpressions.java new file mode 100644 index 0000000000..8e416379e6 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/DeriveSymTypeOfAssignmentExpressions.java @@ -0,0 +1,312 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import com.google.common.base.Preconditions; +import de.monticore.expressions.assignmentexpressions._ast.ASTAssignmentExpression; +import de.monticore.expressions.assignmentexpressions._ast.ASTConstantsAssignmentExpressions; +import de.monticore.expressions.assignmentexpressions._ast.ASTDecPrefixExpression; +import de.monticore.expressions.assignmentexpressions._ast.ASTDecSuffixExpression; +import de.monticore.expressions.assignmentexpressions._ast.ASTIncPrefixExpression; +import de.monticore.expressions.assignmentexpressions._ast.ASTIncSuffixExpression; +import de.monticore.expressions.assignmentexpressions._visitor.AssignmentExpressionsHandler; +import de.monticore.expressions.assignmentexpressions._visitor.AssignmentExpressionsTraverser; +import de.monticore.expressions.assignmentexpressions._visitor.AssignmentExpressionsVisitor2; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.se_rwth.commons.SourcePosition; +import de.se_rwth.commons.logging.Log; + +import static de.monticore.types.check.SymTypePrimitive.unbox; +import static de.monticore.types.check.TypeCheck.compatible; +import static de.monticore.types.check.TypeCheck.isBoolean; +import static de.monticore.types.check.TypeCheck.isString; + +/** + * This Visitor can calculate a SymTypeExpression (type) for the expressions in AssignmentExpressions + * It can be combined with other expressions in your language by creating a DelegatorVisitor + */ +public class DeriveSymTypeOfAssignmentExpressions extends AbstractDeriveFromExpression + implements AssignmentExpressionsVisitor2, AssignmentExpressionsHandler { + + protected AssignmentExpressionsTraverser traverser; + + @Override + public void setTraverser(AssignmentExpressionsTraverser traverser) { + this.traverser = traverser; + } + + @Override + public AssignmentExpressionsTraverser getTraverser() { + return traverser; + } + + @Override + public void traverse(ASTIncSuffixExpression expr) { + Preconditions.checkNotNull(expr); + SymTypeExpression symType = this.affix(expr.getExpression(), "++", expr.get_SourcePositionStart()); + + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(symType); + + } + + @Override + public void traverse(ASTDecSuffixExpression expr) { + Preconditions.checkNotNull(expr); + SymTypeExpression symType = this.affix(expr.getExpression(), "--", expr.get_SourcePositionStart()); + + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(symType); + } + + @Override + public void traverse(ASTIncPrefixExpression expr) { + Preconditions.checkNotNull(expr); + SymTypeExpression symType = this.affix(expr.getExpression(), "++", expr.get_SourcePositionStart()); + + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(symType); + } + + @Override + public void traverse(ASTDecPrefixExpression expr) { + Preconditions.checkNotNull(expr); + SymTypeExpression symType = this.affix(expr.getExpression(), "--", expr.get_SourcePositionStart()); + + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(symType); + } + + protected SymTypeExpression affix(ASTExpression expr, String op, SourcePosition pos) { + // calculate the type of the inner expressions + this.getTypeCheckResult().reset(); + expr.accept(this.getTraverser()); + TypeCheckResult inner = this.getTypeCheckResult().copy(); + + // result of inner type computation should be present + if (!inner.isPresentResult()) { + // should never happen, we expect results to be present + // indicates that the underlying type resolver is erroneous + this.logError("0xA0182", expr.get_SourcePositionStart()); + return SymTypeExpressionFactory.createObscureType(); + } else if (inner.getResult().isObscureType()) { + // if inner obscure then error already logged + return SymTypeExpressionFactory.createObscureType(); + } else if (!inner.isField()) { + // inner should be a field + Log.error("0xA0183 Variable expected.", expr.get_SourcePositionStart()); + return SymTypeExpressionFactory.createObscureType(); + } else { + // else check with signature + return affix(inner.getResult(), op, pos); + } + } + + protected SymTypeExpression affix(SymTypeExpression inner, String op, SourcePosition pos) { + if (isNumericType(inner)) { + return SymTypeExpressionFactory.createPrimitive(unbox(inner.print())); + } else { + Log.error("0xA0184 Operator '" + op + "' not applicable to " + "'" + inner.print() + "'", pos); + return SymTypeExpressionFactory.createObscureType(); + } + } + + + @Override + public void traverse(ASTAssignmentExpression expr) { + Preconditions.checkNotNull(expr); + SymTypeExpression symType = this.derive(expr); + + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(symType); + } + + protected SymTypeExpression derive(ASTAssignmentExpression expr) { + // calculate the type of inner expressions + this.getTypeCheckResult().reset(); + expr.getLeft().accept(this.getTraverser()); + TypeCheckResult left = this.getTypeCheckResult().copy(); + this.getTypeCheckResult().reset(); + expr.getRight().accept(this.getTraverser()); + TypeCheckResult right = this.getTypeCheckResult().copy(); + + // result of inner type computation should be present + if (!left.isPresentResult() || !right.isPresentResult()) { + // should never happen, we expect results to be present + // indicates that the underlying type resolver is erroneous + this.logError("0xA0180", expr.get_SourcePositionStart()); + return SymTypeExpressionFactory.createObscureType(); + } else if (left.getResult().isObscureType() || right.getResult().isObscureType()) { + // if left or right obscure then error already logged + return SymTypeExpressionFactory.createObscureType(); + } else if (!left.isField()) { + // left should be a field + Log.error("0xA0181 Variable expected.", expr.getLeft().get_SourcePositionStart()); + return SymTypeExpressionFactory.createObscureType(); + } else { + // else compare the inner results + return assignment(left.getResult(), right.getResult(), expr.getOperator(), expr.get_SourcePositionStart()); + } + } + + + protected SymTypeExpression assignment(SymTypeExpression left, + SymTypeExpression right, + int op, SourcePosition pos) { + if (op == ASTConstantsAssignmentExpressions.PLUSEQUALS) { + return this.addAssignment(left, right, pos); // a += b + } else if (op == ASTConstantsAssignmentExpressions.MINUSEQUALS) { + return this.subtractAssignment(left, right, pos); // a -= b + } else if (op == ASTConstantsAssignmentExpressions.STAREQUALS) { + return this.multiplyAssignment(left, right, pos); // a *= b + } else if (op == ASTConstantsAssignmentExpressions.SLASHEQUALS) { + return this.divideAssignment(left, right, pos); // a /= b + } else if (op == ASTConstantsAssignmentExpressions.PERCENTEQUALS) { + return this.moduloAssignment(left, right, pos); // a %= b + } else if (op == ASTConstantsAssignmentExpressions.AND_EQUALS) { + return this.andAssignment(left, right, pos); // a %= b + } else if (op == ASTConstantsAssignmentExpressions.PIPEEQUALS) { + return this.orAssignment(left, right, pos); // a |= b + } else if (op == ASTConstantsAssignmentExpressions.ROOFEQUALS) { + return this.xorAssignment(left, right, pos); // a ^= b + } else if (op == ASTConstantsAssignmentExpressions.GTGTEQUALS) { + return this.rightShiftAssignment(left, right, pos); // a >>= b + } else if (op == ASTConstantsAssignmentExpressions.LTLTEQUALS) { + return this.leftShiftAssignment(left, right, pos); // a <<= b + } else if (op == ASTConstantsAssignmentExpressions.GTGTGTEQUALS) { + return this.unsignedRightShiftAssignment(left, right, pos); // a >>>= b + } else { + return this.assignment(left, right, pos); // a = b + } + } + + protected SymTypeExpression addAssignment(SymTypeExpression left, + SymTypeExpression right, + SourcePosition pos) { + // anything on the rhs be converted to a String + if (isString(left)) { + return left; + } else { + return arithmeticAssignment(left, right, "+=", pos); + } + } + + protected SymTypeExpression subtractAssignment(SymTypeExpression left, + SymTypeExpression right, + SourcePosition pos) { + return arithmeticAssignment(left, right, "-=", pos); + } + + protected SymTypeExpression multiplyAssignment(SymTypeExpression left, + SymTypeExpression right, + SourcePosition pos) { + return arithmeticAssignment(left, right, "*=", pos); + } + + protected SymTypeExpression moduloAssignment(SymTypeExpression left, + SymTypeExpression right, + SourcePosition pos) { + return arithmeticAssignment(left, right, "%=", pos); + } + + protected SymTypeExpression divideAssignment(SymTypeExpression left, + SymTypeExpression right, + SourcePosition pos) { + return arithmeticAssignment(left, right, "/=", pos); + } + + protected SymTypeExpression andAssignment(SymTypeExpression left, + SymTypeExpression right, + SourcePosition pos) { + return binaryAssignment(left, right, "&=", pos); + } + + protected SymTypeExpression orAssignment(SymTypeExpression left, + SymTypeExpression right, + SourcePosition pos) { + return binaryAssignment(left, right, "|=", pos); + } + + protected SymTypeExpression xorAssignment(SymTypeExpression left, + SymTypeExpression right, + SourcePosition pos) { + return binaryAssignment(left, right, "^=", pos); + } + + + protected SymTypeExpression rightShiftAssignment(SymTypeExpression left, + SymTypeExpression right, + SourcePosition pos) { + return bitAssignment(left, right, ">>=", pos); + } + + protected SymTypeExpression leftShiftAssignment(SymTypeExpression left, + SymTypeExpression right, + SourcePosition pos) { + return bitAssignment(left, right, "<<=", pos); + } + + protected SymTypeExpression unsignedRightShiftAssignment(SymTypeExpression left, + SymTypeExpression right, + SourcePosition pos) { + return bitAssignment(left, right, ">>>=", pos); + } + + protected SymTypeExpression binaryAssignment(SymTypeExpression left, + SymTypeExpression right, + String op, SourcePosition pos) { + // both must be of integral or both must be of boolean type + if ((isIntegralType(left) && isIntegralType(right)) + || (isBoolean(left) && isBoolean(right))) { + return left; + } else { + // else operator not applicable + Log.error("0xA0176 Operator '" + op + "' not applicable to " + + "'" + left.print() + "', '" + right.print() + "'", pos); + return SymTypeExpressionFactory.createObscureType(); + } + } + + protected SymTypeExpression bitAssignment(SymTypeExpression left, + SymTypeExpression right, + String op, + SourcePosition src) { + // both must be of integral type + if (isIntegralType(left) && isIntegralType(right)) { + return left; + } else { + // else operator not applicable + Log.error("0xA0177 Operator '" + op + "' not applicable to " + + "'" + left.print() + "', '" + right.print() + "'", src); + return SymTypeExpressionFactory.createObscureType(); + } + } + + protected SymTypeExpression arithmeticAssignment(SymTypeExpression left, + SymTypeExpression right, + String op, + SourcePosition src) { + // both must be of numeric type + if (isNumericType(left) && isNumericType(right)) { + return left; + } else { + // else operator not applicable + Log.error("0xA0178 Operator '" + op + "' not applicable to " + + "'" + left.print() + "', '" + right.print() + "'", src); + return SymTypeExpressionFactory.createObscureType(); + } + } + + protected SymTypeExpression assignment(SymTypeExpression left, + SymTypeExpression right, + SourcePosition src) { + // types must be compatible + if (compatible(left, right)) { + return left; + } else { + // else type mismatch + Log.error("0xA0179 Incompatible types, required '" + left.print() + + "' but provided '" + right.print() + "'", src); + return SymTypeExpressionFactory.createObscureType(); + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/DeriveSymTypeOfBSCommonExpressions.java b/monticore-grammar/src/main/java/de/monticore/types/check/DeriveSymTypeOfBSCommonExpressions.java new file mode 100644 index 0000000000..b0f27b60dc --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/DeriveSymTypeOfBSCommonExpressions.java @@ -0,0 +1,1252 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import de.monticore.expressions.commonexpressions._ast.*; +import de.monticore.expressions.commonexpressions._visitor.CommonExpressionsHandler; +import de.monticore.expressions.commonexpressions._visitor.CommonExpressionsTraverser; +import de.monticore.expressions.commonexpressions._visitor.CommonExpressionsVisitor2; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.expressionsbasis._ast.ASTNameExpression; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.*; +import de.monticore.symboltable.modifiers.AccessModifier; +import de.monticore.types.check.helpers.*; +import de.se_rwth.commons.SourcePosition; +import de.se_rwth.commons.logging.Log; + +import java.util.*; +import java.util.stream.Collectors; + +import static de.monticore.types.check.TypeCheck.*; + +/** + * This Visitor can calculate a SymTypeExpression (type) for the expressions in CommonExpressions + * The BS stands for BasicSymbols. Therefore, the class should not be used in an OO-context as it does not know + * OO concepts such as the modifiers static, public or private. + * For an OO language, use {@link de.monticore.types.check.DeriveSymTypeOfCommonExpressions} instead, which + * extends the functionality of this class so that it may be used in an OO-context as well. + * It can be combined with other expressions in your language by creating a DelegatorVisitor + */ +public class DeriveSymTypeOfBSCommonExpressions extends AbstractDeriveFromExpression implements CommonExpressionsVisitor2, CommonExpressionsHandler { + + protected CommonExpressionsTraverser traverser; + + protected SubExprNameExtractor subExprNameExtractor; + + protected DefiningSymbolSetter definingSymbolSetter; + + public DeriveSymTypeOfBSCommonExpressions() { + this(new SubExprNameExtractor4CommonExpressions(), new DefiningSymbolSetter4CommonExpressions()); + } + + public DeriveSymTypeOfBSCommonExpressions(SubExprNameExtractor subExprNameExtractor, + DefiningSymbolSetter definingSymbolSetter) { + this.subExprNameExtractor = subExprNameExtractor; + this.definingSymbolSetter = definingSymbolSetter; + } + + @Override + public CommonExpressionsTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(CommonExpressionsTraverser traverser) { + this.traverser = traverser; + } + + public SubExprNameExtractor getSubExprNameExtractor() { + return subExprNameExtractor; + } + + public void setSubExprNameExtractor(SubExprNameExtractor subExprNameExtractor) { + this.subExprNameExtractor = subExprNameExtractor; + } + + public DefiningSymbolSetter getDefiningSymbolSetter() { + return definingSymbolSetter; + } + + public void setDefiningSymbolSetter(DefiningSymbolSetter definingSymboLSetter) { + this.definingSymbolSetter = definingSymboLSetter; + } + + @Override + public void traverse(ASTPlusPrefixExpression expr) { + Preconditions.checkNotNull(expr); + SymTypeExpression symType = numericPrefix(expr.getExpression(), "+", expr.get_SourcePositionStart()); + + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(symType); + } + + @Override + public void traverse(ASTMinusPrefixExpression expr) { + Preconditions.checkNotNull(expr); + SymTypeExpression symType = numericPrefix(expr.getExpression(), "-", expr.get_SourcePositionStart()); + + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(symType); + } + + protected SymTypeExpression numericPrefix(ASTExpression expr, String op, SourcePosition pos) { + this.getTypeCheckResult().reset(); + expr.accept(this.getTraverser()); + TypeCheckResult inner = this.getTypeCheckResult().copy(); + + // result of inner type computation should be present + if (!inner.isPresentResult()) { + // should never happen, we expect results to be present + // indicates that the underlying type resolver is erroneous + this.logError("0xA0174", expr.get_SourcePositionStart()); + return SymTypeExpressionFactory.createObscureType(); + } else if (inner.getResult().isObscureType()) { + // if inner obscure then error already logged + return SymTypeExpressionFactory.createObscureType(); + } else { + return numericPrefix(inner.getResult(), op, pos); + } + } + + protected SymTypeExpression numericPrefix(SymTypeExpression inner, String op, SourcePosition pos) { + if (isDouble(inner)) { + return SymTypeExpressionFactory.createPrimitive(BasicSymbolsMill.DOUBLE); + } else if (isFloat(inner)) { + return SymTypeExpressionFactory.createPrimitive(BasicSymbolsMill.FLOAT); + } else if (isLong(inner)) { + return SymTypeExpressionFactory.createPrimitive(BasicSymbolsMill.LONG); + } else if (isIntegralType(inner)) { + return SymTypeExpressionFactory.createPrimitive(BasicSymbolsMill.INT); + } else { + Log.error("0xA0175 Operator '" + op + "' not applicable to " + "'" + inner.print() + "'", pos); + return SymTypeExpressionFactory.createObscureType(); + } + } + + /** + * We use traverse to collect the results of the two parts of the expression and calculate the result for the whole expression + */ + @Override + public void traverse(ASTPlusExpression expr) { + SymTypeExpression left = acceptThisAndReturnSymTypeExpression(expr.getLeft()); + SymTypeExpression right = acceptThisAndReturnSymTypeExpression(expr.getRight()); + + if (left.isObscureType() || right.isObscureType()) { + // if any inner obscure then error already logged + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + } else { + // else calculate result + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(this.calculatePlusExpression(left, right, expr.get_SourcePositionStart())); + } + } + + protected SymTypeExpression calculatePlusExpression(SymTypeExpression left, SymTypeExpression right, SourcePosition pos) { + // if one part of the expression is a String then the whole expression is a String + if(isString(left)) { + return SymTypeExpressionFactory.createTypeObject(left.getTypeInfo()); + } else if (isString(right)) { + return SymTypeExpressionFactory.createTypeObject(right.getTypeInfo()); + } else { + // no String in the expression -> use the normal calculation for the basic arithmetic operators + return calculateArithmeticExpression(left, right, "+", pos); + } + } + + /** + * We use traverse to collect the results of the two parts of the expression and calculate the result for the whole expression + */ + @Override + public void traverse(ASTMultExpression expr) { + SymTypeExpression left = acceptThisAndReturnSymTypeExpression(expr.getLeft()); + SymTypeExpression right = acceptThisAndReturnSymTypeExpression(expr.getRight()); + + if (left.isObscureType() || right.isObscureType()) { + // if any inner obscure then error already logged + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + } else { + // else calculate result + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(this.calculateMultExpression(left, right, expr.get_SourcePositionStart())); + } + } + + protected SymTypeExpression calculateMultExpression(SymTypeExpression left, SymTypeExpression right, SourcePosition pos) { + return calculateArithmeticExpression(left, right, "*", pos); + } + + /** + * We use traverse to collect the results of the two parts of the expression and calculate the result for the whole expression + */ + @Override + public void traverse(ASTDivideExpression expr) { + SymTypeExpression left = acceptThisAndReturnSymTypeExpression(expr.getLeft()); + SymTypeExpression right = acceptThisAndReturnSymTypeExpression(expr.getRight()); + + if (left.isObscureType() || right.isObscureType()) { + // if any inner obscure then error already logged + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + } else { + // else calculate result + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(this.calculateDivideExpression(left, right, expr.get_SourcePositionStart())); + } + } + + protected SymTypeExpression calculateDivideExpression(SymTypeExpression left, SymTypeExpression right, SourcePosition pos) { + return calculateArithmeticExpression(left, right, "/", pos); + } + + /** + * We use traverse to collect the results of the two parts of the expression and calculate the result for the whole expression + */ + @Override + public void traverse(ASTMinusExpression expr) { + SymTypeExpression left = acceptThisAndReturnSymTypeExpression(expr.getLeft()); + SymTypeExpression right = acceptThisAndReturnSymTypeExpression(expr.getRight()); + + if (left.isObscureType() || right.isObscureType()) { + // if any inner obscure then error already logged + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + } else { + // else calculate result + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(this.calculateMinusExpression(left, right, expr.get_SourcePositionStart())); + } + } + + protected SymTypeExpression calculateMinusExpression(SymTypeExpression left, SymTypeExpression right, SourcePosition pos) { + return calculateArithmeticExpression(left, right, "-", pos); + } + + /** + * We use traverse to collect the results of the two parts of the expression and calculate the result for the whole expression + */ + @Override + public void traverse(ASTModuloExpression expr) { + SymTypeExpression left = acceptThisAndReturnSymTypeExpression(expr.getLeft()); + SymTypeExpression right = acceptThisAndReturnSymTypeExpression(expr.getRight()); + + if (left.isObscureType() || right.isObscureType()) { + // if any inner obscure then error already logged + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + } else { + // else calculate result + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(this.calculateModuloExpression(left, right, expr.get_SourcePositionStart())); + } + } + + protected SymTypeExpression calculateModuloExpression(SymTypeExpression left, SymTypeExpression right, SourcePosition pos) { + return calculateArithmeticExpression(left, right, "%", pos); + } + + + /** + * We use traverse to collect the results of the two parts of the expression and calculate the result for the whole expression + */ + @Override + public void traverse(ASTLessEqualExpression expr) { + SymTypeExpression left = acceptThisAndReturnSymTypeExpression(expr.getLeft()); + SymTypeExpression right = acceptThisAndReturnSymTypeExpression(expr.getRight()); + + if (left.isObscureType() || right.isObscureType()) { + // if any inner obscure then error already logged + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + } else { + // else calculate result + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(this.lessEqual(left, right, expr.get_SourcePositionStart())); + } + } + + protected SymTypeExpression lessEqual(SymTypeExpression left, SymTypeExpression right, SourcePosition pos) { + return calculateTypeCompare(left, right, "<=", pos); + } + + /** + * We use traverse to collect the results of the two parts of the expression and calculate the result for the whole expression + */ + @Override + public void traverse(ASTGreaterEqualExpression expr) { + SymTypeExpression left = acceptThisAndReturnSymTypeExpression(expr.getLeft()); + SymTypeExpression right = acceptThisAndReturnSymTypeExpression(expr.getRight()); + + if (left.isObscureType() || right.isObscureType()) { + // if any inner obscure then error already logged + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + } else { + // else calculate result + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(this.greaterEqual(left, right, expr.get_SourcePositionStart())); + } + } + + protected SymTypeExpression greaterEqual(SymTypeExpression left, SymTypeExpression right, SourcePosition pos) { + return calculateTypeCompare(left, right, ">=", pos); + } + + /** + * We use traverse to collect the results of the two parts of the expression and calculate the result for the whole expression + */ + @Override + public void traverse(ASTLessThanExpression expr) { + SymTypeExpression left = acceptThisAndReturnSymTypeExpression(expr.getLeft()); + SymTypeExpression right = acceptThisAndReturnSymTypeExpression(expr.getRight()); + + if (left.isObscureType() || right.isObscureType()) { + // if any inner obscure then error already logged + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + } else { + // else calculate result + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(this.less(left, right, expr.get_SourcePositionStart())); + } + } + + protected SymTypeExpression less(SymTypeExpression left, SymTypeExpression right, SourcePosition pos) { + return calculateTypeCompare(left, right, "<", pos); + } + + /** + * We use traverse to collect the results of the two parts of the expression and calculate the result for the whole expression + */ + @Override + public void traverse(ASTGreaterThanExpression expr) { + SymTypeExpression left = acceptThisAndReturnSymTypeExpression(expr.getLeft()); + SymTypeExpression right = acceptThisAndReturnSymTypeExpression(expr.getRight()); + + if (left.isObscureType() || right.isObscureType()) { + // if any inner obscure then error already logged + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + } else { + // else calculate result + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(this.calculateGreaterThanExpression(left, right, expr.get_SourcePositionStart())); + } + } + + protected SymTypeExpression calculateGreaterThanExpression(SymTypeExpression left, SymTypeExpression right, SourcePosition pos) { + return calculateTypeCompare(left, right, ">", pos); + } + + /** + * We use traverse to collect the results of the two parts of the expression and calculate the result for the whole expression + */ + @Override + public void traverse(ASTEqualsExpression expr) { + SymTypeExpression left = acceptThisAndReturnSymTypeExpression(expr.getLeft()); + SymTypeExpression right = acceptThisAndReturnSymTypeExpression(expr.getRight()); + + if (left.isObscureType() || right.isObscureType()) { + // if any inner obscure then error already logged + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + } else { + // else calculate result + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(this.calculateEqualsExpression(left, right, expr.get_SourcePositionStart())); + } + } + + protected SymTypeExpression calculateEqualsExpression(SymTypeExpression left, SymTypeExpression right, SourcePosition pos) { + return calculateTypeLogical(left, right, "==", pos); + } + + /** + * We use traverse to collect the results of the two parts of the expression and calculate the result for the whole expression + */ + @Override + public void traverse(ASTNotEqualsExpression expr) { + SymTypeExpression left = acceptThisAndReturnSymTypeExpression(expr.getLeft()); + SymTypeExpression right = acceptThisAndReturnSymTypeExpression(expr.getRight()); + + if (left.isObscureType() || right.isObscureType()) { + // if any inner obscure then error already logged + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + } else { + // else calculate result + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(this.calculateNotEqualsExpression(left, right, expr.get_SourcePositionStart())); + } + } + + protected SymTypeExpression calculateNotEqualsExpression(SymTypeExpression left, SymTypeExpression right, SourcePosition pos) { + return calculateTypeLogical(left, right, "!=", pos); + } + + /** + * We use traverse to collect the results of the two parts of the expression and calculate the result for the whole expression + */ + @Override + public void traverse(ASTBooleanAndOpExpression expr) { + SymTypeExpression left = acceptThisAndReturnSymTypeExpression(expr.getLeft()); + SymTypeExpression right = acceptThisAndReturnSymTypeExpression(expr.getRight()); + + if (left.isObscureType() || right.isObscureType()) { + // if any inner obscure then error already logged + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + } else { + // else calculate result + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(this.calculateBooleanAndOpExpression(left, right, expr.get_SourcePositionStart())); + } + } + + protected SymTypeExpression calculateBooleanAndOpExpression(SymTypeExpression left, SymTypeExpression right, SourcePosition pos) { + return calculateLogicalOrOpAndOp(left, right, "&&", pos); + } + + @Override + public void traverse(ASTBooleanOrOpExpression expr) { + SymTypeExpression left = acceptThisAndReturnSymTypeExpression(expr.getLeft()); + SymTypeExpression right = acceptThisAndReturnSymTypeExpression(expr.getRight()); + + if (left.isObscureType() || right.isObscureType()) { + // if any inner obscure then error already logged + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + } else { + // else calculate result + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(this.calculateBooleanOrOpExpression(left, right, expr.get_SourcePositionStart())); + } + } + + protected SymTypeExpression calculateBooleanOrOpExpression(SymTypeExpression left, SymTypeExpression right, SourcePosition pos) { + return calculateLogicalOrOpAndOp(left, right, "||", pos); + } + + protected SymTypeExpression calculateLogicalOrOpAndOp(SymTypeExpression left, SymTypeExpression right, String op, SourcePosition pos) { + if (isBoolean(left) && isBoolean(right)) { + return SymTypeExpressionFactory.createPrimitive(BasicSymbolsMill.BOOLEAN); + } else { + // else operator not applicable + Log.error("0xA0167 Operator '" + op + "' not applicable to " + "'" + left.print() + "', '" + right.print() + "'", pos); + return SymTypeExpressionFactory.createObscureType(); + } + } + + /** + * We use traverse to collect the result of the inner part of the expression and calculate the result for the whole expression + */ + @Override + public void traverse(ASTLogicalNotExpression expr) { + Preconditions.checkNotNull(expr); + SymTypeExpression symType = this.derive(expr); + + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(symType); + } + + protected SymTypeExpression derive(ASTLogicalNotExpression expr) { + // calculate the type of the inner expressions + this.getTypeCheckResult().reset(); + expr.getExpression().accept(this.getTraverser()); + TypeCheckResult inner = this.getTypeCheckResult().copy(); + + // result of inner type computation should be present + if (!inner.isPresentResult()) { + // should never happen, we expect results to be present + // indicates that the underlying type resolver is erroneous + this.logError("0xA0170", expr.getExpression().get_SourcePositionStart()); + return SymTypeExpressionFactory.createObscureType(); + } else if (inner.getResult().isObscureType()) { + // if inner obscure then error already logged + return SymTypeExpressionFactory.createObscureType(); + } else { + // else check with signature + return logicalNot(inner.getResult(), expr.get_SourcePositionStart()); + } + } + + protected SymTypeExpression logicalNot(SymTypeExpression inner, SourcePosition pos) { + if (isBoolean(inner)) { + return SymTypeExpressionFactory.createPrimitive(BasicSymbolsMill.BOOLEAN); + } else { + Log.error("0xA0171 Operator '!' not applicable to " + "'" + inner.print() + "'", pos); + return SymTypeExpressionFactory.createObscureType(); + } + } + + /** + * We use traverse to collect the results of the three parts of the expression and calculate the result for the whole expression + */ + @Override + public void traverse(ASTConditionalExpression expr) { + Preconditions.checkNotNull(expr); + SymTypeExpression condition = this.acceptThisAndReturnSymTypeExpression(expr.getCondition()); + SymTypeExpression first = this.acceptThisAndReturnSymTypeExpression(expr.getTrueExpression()); + SymTypeExpression second = this.acceptThisAndReturnSymTypeExpression(expr.getFalseExpression()); + + getTypeCheckResult().reset(); + getTypeCheckResult().setResult(this.calculateConditionalExpressionType(expr, condition, first, second)); + } + + protected SymTypeExpression calculateConditionalExpressionType(ASTConditionalExpression expr, + SymTypeExpression conditionResult, + SymTypeExpression trueResult, + SymTypeExpression falseResult) { + if (!conditionResult.isObscureType() && !isBoolean(conditionResult)) { + // if obscure then error already logged + // else condition must be boolean + Log.error("0xA0165 Expected '" + BasicSymbolsMill.BOOLEAN + "' but provided '" + conditionResult.print() + "'", + expr.getCondition().get_SourcePositionStart(), expr.getCondition().get_SourcePositionEnd()); + } + + if (compatible(trueResult, falseResult)) { + return trueResult; + } else if (compatible(falseResult, trueResult)) { + return falseResult; + } else { + SymTypeExpression inner = getBinaryNumericPromotion(trueResult, falseResult); + if (inner.isObscureType()) { + // binary numeric promotion does not log error + Log.error("0xA0164 Resulting types '" + trueResult + "' and '" + falseResult + "' of operator ? are incompatible", + expr.getTrueExpression().get_SourcePositionStart(), expr.getFalseExpression().get_SourcePositionEnd()); + } + return inner; + } + } + + /** + * We use traverse to collect the result of the inner part of the expression and calculate the result for the whole expression + */ + @Override + public void traverse(ASTBooleanNotExpression expr) { + Preconditions.checkNotNull(expr); + SymTypeExpression symType = this.derive(expr); + + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(symType); + } + + protected SymTypeExpression derive(ASTBooleanNotExpression expr) { + // calculate the type of the inner expressions + this.getTypeCheckResult().reset(); + expr.getExpression().accept(this.getTraverser()); + TypeCheckResult inner = this.getTypeCheckResult().copy(); + + // result of inner type computation should be present + if (!inner.isPresentResult()) { + // should never happen, we expect results to be present + // indicates that the underlying type resolver is erroneous + this.logError("0xA0172", expr.getExpression().get_SourcePositionStart()); + return SymTypeExpressionFactory.createObscureType(); + } else if (inner.getResult().isObscureType()) { + // if inner obscure then error already logged + return SymTypeExpressionFactory.createObscureType(); + } else { + // else check with signature + return booleanNot(inner.getResult(), expr.get_SourcePositionStart()); + } + } + + protected SymTypeExpression booleanNot(SymTypeExpression inner, SourcePosition pos) { + if (isIntegralType(inner)) { + if (isLong(inner)) { + return SymTypeExpressionFactory.createPrimitive(BasicSymbolsMill.LONG); + } else { + return SymTypeExpressionFactory.createPrimitive(BasicSymbolsMill.INT); + } + } else { + Log.error("0xA0173 Operator '~' not applicable to " + "'" + inner.print() + "'", pos); + return SymTypeExpressionFactory.createObscureType(); + } + } + + /** + * We use traverse to collect the result of the inner part of the expression and calculate the result for the whole expression + */ + @Override + public void traverse(ASTFieldAccessExpression expr) { + SubExprNameExtractionResult extractedNames = getSubExprNameExtractor().calculateNameParts(expr); + + if(extractedNames.resultIsValidName() && expr.getEnclosingScope() instanceof IBasicSymbolsScope) { + List nameParts = extractedNames.getNamePartsIfValid().get(); + calculateNamingChainFieldAccess(expr, nameParts); + } else { + calculateArithmeticFieldAccessExpression(expr); + } + } + + /** + * Calculate the type result of FieldAccessExpressions that represent qualified names and cascading field accesses. + * E.g., pac.kage.Type.staticMember, or, localField.innerField.furtherNestedField. + * But not: pac.kage.Type.staticMethod().innerField, as here innerField is not only qualified by names, but + * it is based on the access of a value returned by a CallExpression. + * @param expr The only valid sub expressions of the FieldAccessExpression are other FieldAccessExpressions, and + * a {@link ASTNameExpression} that is the end of the field access chain. + */ + protected void calculateNamingChainFieldAccess(ASTFieldAccessExpression expr, + List nameParts) { + + + // We will incrementally try to build our result: + // We will start with the first name part and check whether it resolves to an entity. + // Then we will check whether further field accesses are (nested) accesses on that entity's members. + // When there is no such entity, or it does not have a member we were looking for, then we try to resolve the + // qualified name up to the part of the name where we currently are. + + getTypeCheckResult().reset(); + for(int i = 0; i < nameParts.size(); i++) { + if(getTypeCheckResult().isPresentResult() && !getTypeCheckResult().getResult().isObscureType()) { + calculateFieldAccess((ASTFieldAccessExpression) nameParts.get(i).getExpression(), true); + } else { + calculatedQualifiedEntity(nameParts.subList(0, i + 1)); + } + } + + if(!getTypeCheckResult().isPresentResult() || getTypeCheckResult().getResult().isObscureType()) { + String qualName = nameParts.stream().map(ExprToNamePair::getName).collect(Collectors.joining(".")); + Log.error("0xA0241 No SymTypeExpression could be derived for the FieldAccessExpression " + qualName, expr.get_SourcePositionStart()); + } + } + + /** + * Calculate the type result of FieldAccessExpressions that do not represent qualified names. (E.g. `new Foo().bar`) + */ + protected void calculateArithmeticFieldAccessExpression(ASTFieldAccessExpression expr) { + expr.getExpression().accept(getTraverser()); + if (getTypeCheckResult().isPresentResult() && !getTypeCheckResult().getResult().isObscureType()) { + calculateFieldAccess(expr, false); + } // else do nothing (as the type check result is already absent or obscure). + } + + /** + * Calculates the type result of the field access expression, given that the type result of the accessed entity's + * owner has already been computed (and is accessible via getTypeCheckResult()). + * @param quiet Prevents the logging of errors if no entity is found that could be accessed, i.e., if the field access + * is invalid and the calculation of a result is not possible. + */ + protected void calculateFieldAccess(ASTFieldAccessExpression expr, + boolean quiet) { + TypeCheckResult fieldOwner = getTypeCheckResult().copy(); + SymTypeExpression fieldOwnerExpr = fieldOwner.getResult(); + TypeSymbol fieldOwnerSymbol = fieldOwnerExpr.getTypeInfo(); + if (fieldOwnerSymbol instanceof TypeVarSymbol && !quiet) { + Log.error("0xA0321 The type " + fieldOwnerSymbol.getName() + " is a type variable and cannot have methods and attributes", expr.get_SourcePositionStart()); + } + //search for a method, field or type in the scope of the type of the inner expression + List fieldSymbols = getCorrectFieldsFromInnerType(fieldOwnerExpr, expr); + Optional typeSymbolOpt = fieldOwnerSymbol.getSpannedScope().resolveType(expr.getName()); + Optional typeVarOpt = fieldOwnerSymbol.getSpannedScope().resolveTypeVar(expr.getName()); + String qualName = fieldOwnerSymbol.getName() + "." + expr.getName(); + + if (!fieldSymbols.isEmpty()) { + //cannot be a method, test variable first + //durch AST-Umbau kann ASTFieldAccessExpression keine Methode sein + //if the last result is a type then filter for static field symbols + if (fieldOwner.isType()) { + fieldSymbols = filterModifiersVariables(fieldSymbols); + } + if (fieldSymbols.size() != 1) { + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + if(!quiet) { + Log.error("0xA1236 Ambiguous: Found " + fieldSymbols.size() + " symbols for " + qualName, expr.get_SourcePositionStart()); + } + } + if (!fieldSymbols.isEmpty()) { + VariableSymbol var = fieldSymbols.get(0); + expr.setDefiningSymbol(var); + SymTypeExpression type = var.getType(); + getTypeCheckResult().setField(); + getTypeCheckResult().setResult(type); + } + } else if (typeVarOpt.isPresent()) { + //test for type var first + TypeVarSymbol typeVar = typeVarOpt.get(); + if(checkModifierType(typeVar)){ + SymTypeExpression wholeResult = SymTypeExpressionFactory.createTypeVariable(typeVar); + expr.setDefiningSymbol(typeVar); + getTypeCheckResult().setType(); + getTypeCheckResult().setResult(wholeResult); + } else{ + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + if(!quiet) { + Log.error("0xA1306 The referenced type variable " + typeVar.getName() + " is not accessible.", expr.get_SourcePositionStart()); + } + } + } else if (typeSymbolOpt.isPresent()) { + //no variable found, test type + TypeSymbol typeSymbol = typeSymbolOpt.get(); + if (checkModifierType(typeSymbol)) { + SymTypeExpression wholeResult = SymTypeExpressionFactory.createTypeExpression(typeSymbol); + expr.setDefiningSymbol(typeSymbol); + getTypeCheckResult().setType(); + getTypeCheckResult().setResult(wholeResult); + } else { + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + if(!quiet) { + Log.error("0xA1303 The referenced type " + typeSymbol.getName() + " is not accessible.", expr.get_SourcePositionStart()); + } + } + } else { + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + if(!quiet) { + Log.error("0xA1317 Cannot find symbol " + qualName, expr.get_SourcePositionStart()); + } + } + } + + /** + * Hookpoint for object oriented languages to get the correct variables/fields from a type based on their modifiers + */ + protected List getCorrectFieldsFromInnerType(SymTypeExpression innerResult, ASTFieldAccessExpression expr) { + return innerResult.getFieldList(expr.getName(), getTypeCheckResult().isType(), true, AccessModifier.ALL_INCLUSION); + } + + /** + * Hookpoint for object oriented languages that offer modifiers like static, public, private, ... + */ + protected boolean checkModifierType(TypeSymbol typeSymbol){ + return true; + } + + /** + * Hookpoint for object oriented languages that offer modifiers like static, public, private, ... + */ + protected List filterModifiersVariables(List variableSymbols) { + return variableSymbols; + } + + /** + * Tries to resolve the given name parts to a variable, type variable, or type and if a symbol is found, then + * it(s type) is set as the current type check result. + * If no symbol is found, then nothing happens (no error logged, no altering of the type check result). + * If multiple fields are found, then the result is set to obscure, and an error is logged. + * Variables take precedence over types variables that take precedence over + * types. + * @param nameParts Expressions that represent a qualified identification of a {@link VariableSymbol}, + * {@link TypeVarSymbol}, or {@link TypeSymbol}. Therefore, the list that must contain a + * {@code NameExpression} at the beginning, followed only by {@code FieldAccessExpression}s. + */ + protected void calculatedQualifiedEntity(List nameParts) { + List namePartStrings = nameParts.stream().map(ExprToNamePair::getName).collect(Collectors.toList()); + String qualName = String.join(".", namePartStrings); + ASTExpression lastExpr = nameParts.get(nameParts.size() - 1).getExpression(); + + List fieldSymbols = getScope(lastExpr.getEnclosingScope()).resolveVariableMany(qualName); + Optional typeSymbolOpt = getScope(lastExpr.getEnclosingScope()).resolveType(qualName); + Optional typeVarOpt = getScope(lastExpr.getEnclosingScope()).resolveTypeVar(qualName); + + if (!fieldSymbols.isEmpty()) { + if (fieldSymbols.size() != 1) { + Log.error("0xA1237 Ambiguous: Found " + fieldSymbols.size() + " symbols for " + qualName, lastExpr.get_SourcePositionStart()); + getTypeCheckResult().reset(); + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + } else { + VariableSymbol var = fieldSymbols.get(0); + definingSymbolSetter.setDefiningSymbol(lastExpr, var); + SymTypeExpression type = var.getType(); + getTypeCheckResult().setField(); + getTypeCheckResult().setResult(type); + } + + } else if (typeVarOpt.isPresent()) { + TypeVarSymbol typeVar = typeVarOpt.get(); + SymTypeExpression type = SymTypeExpressionFactory.createTypeVariable(typeVar); + definingSymbolSetter.setDefiningSymbol(lastExpr, typeVar); + getTypeCheckResult().setType(); + getTypeCheckResult().setResult(type); + + } else if (typeSymbolOpt.isPresent()) { + TypeSymbol typeSymbol = typeSymbolOpt.get(); + SymTypeExpression type = SymTypeExpressionFactory.createTypeExpression(typeSymbol); + definingSymbolSetter.setDefiningSymbol(lastExpr, typeSymbol); + getTypeCheckResult().setType(); + getTypeCheckResult().setResult(type); + } + } + + /** + * We use traverse to collect the result of the inner part of the expression and calculate the result for the whole expression + */ + @Override + public void traverse(ASTCallExpression expr) { + SubExprNameExtractionResult extractedNames = getSubExprNameExtractor().calculateNameParts(expr.getExpression()); + + Optional methodName = extractedNames.getLastName(); + + List arguments = calculateArguments(expr); + + if (extractedNames.resultIsValidName() && expr.getEnclosingScope() instanceof IBasicSymbolsScope) { + calculateNamingChainCallExpression(expr, extractedNames.getNamePartsIfValid().get(), arguments); + } else { + calculateArithmeticCallExpression(expr, methodName.orElse(""), extractedNames.getNamePartsRaw(), arguments); + } + } + + /** + * Calculate the type result of call expressions that represent fully qualified method calls (like when calling a + * static method: {@code pac.kage.Type.staticMethod()}) and methodCalls on cascading field accesses (e.g., + * {@code localField.innerField.instanceMethod()}). But not: + * {@code pac.kage.Type.staticMethod().innerField.instanceMethod()}, as here instanceMethod is not only + * qualified by names, but it is based on the access of a value returned by a CallExpression. + * + * @param expr The call expression itself + * @param nameParts The name parts of the method in their order of appearance. Provide both their AST version, and + * their String version! + * @param argTypes the types of the arguments of the method + */ + protected void calculateNamingChainCallExpression(ASTCallExpression expr, + List nameParts, + List argTypes) { + List astNameParts = nameParts.stream().map(ExprToNamePair::getExpression).collect(Collectors.toList()); + List stringNameParts = nameParts.stream().map(ExprToNamePair::getName).collect(Collectors.toList()); + + String methodName = stringNameParts.get(stringNameParts.size() - 1); + + // We will incrementally try to build our result: + // We will start with the first name part and check whether it resolves to an entity. + // Then we will check whether further field accesses are (nested) accesses on that entity's members. + // When there is no such entity, or it does not have a member we were looking for, then we try to resolve the + // qualified name up to the part of the name where we currently are. + + // We terminate at the last name part, as the last part may resolve to a field of a function type + getTypeCheckResult().reset(); + for(int i = 0; i < astNameParts.size(); i++) { + if(getTypeCheckResult().isPresentResult() && !getTypeCheckResult().getResult().isObscureType()) { + calculateFieldAccess((ASTFieldAccessExpression) astNameParts.get(i), true); + } else { + calculatedQualifiedEntity(nameParts.subList(0, i + 1)); + } + } + + if(getTypeCheckResult().isPresentResult() && getTypeCheckResult().getResult().isFunctionType()) { + calculateFunctionReturnTypeBasedOnSignature( + Collections.singletonList((SymTypeOfFunction) getTypeCheckResult().getResult()), expr, argTypes); + } + else { + // We terminate *before* the last name part, as the last name part must resolve to a method and not a type or field. + getTypeCheckResult().reset(); + for(int i = 0; i < astNameParts.size() - 1; i++) { + if(getTypeCheckResult().isPresentResult() && !getTypeCheckResult().getResult().isObscureType()) { + calculateFieldAccess((ASTFieldAccessExpression) astNameParts.get(i), true); + } else { + calculatedQualifiedEntity(nameParts.subList(0, i + 1)); + } + } + + if(getTypeCheckResult().isPresentResult() && !getTypeCheckResult().getResult().isObscureType()) { + calculateOwnedCallExpression(expr, methodName, argTypes); + } else { + getTypeCheckResult().reset(); + // Check whether we have a fully qualified method. Local method calls will also end in this program branch. + String qualName = String.join(".", stringNameParts); + calculateQualifiedMethod(qualName, expr, argTypes); + } + } + } + + /** + * Calculates the type result of the expression, given that it is not a simple, or qualified method access. E.g., + * this method calculates the result of {@code "FooBar".substring(0, 3)}, or + * {@code ( foo ? new LinkedList() : new ArrayList() ).add("bar")}. On the other hand, this method is + * not suited for {@code pac.kage.Owner.staticMethod()}, or {@code isInt()} (a local Method name). + * Use {@link #calculateNamingChainCallExpression(ASTCallExpression, List, List)} in these cases. + */ + protected void calculateArithmeticCallExpression(ASTCallExpression expr, + String methodName, + List methodCallParts, + List argTypes) { + if(methodCallParts.size() < 2) { + Log.error("0xA1240 (Internal error) call expression just consists of a method name, but the program " + + "flow ended in the branch that calculates call expressions on method chains and similar."); + getTypeCheckResult().reset(); + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + return; + } + getTypeCheckResult().reset(); + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + + ASTExpression methodOwner = methodCallParts.get(methodCallParts.size() - 2).getExpression(); + methodOwner.accept(getTraverser()); + calculateOwnedCallExpression(expr, methodName, argTypes); + + + ASTExpression methodNameExpr = expr.getExpression(); + if (methodNameExpr instanceof ASTFieldAccessExpression) { + getTypeCheckResult().reset(); + ((ASTFieldAccessExpression) methodNameExpr).getExpression().accept(getTraverser()); + calculateFieldAccess((ASTFieldAccessExpression) methodNameExpr, true); + if(getTypeCheckResult().isPresentResult() && getTypeCheckResult().getResult().isFunctionType()) { + // Expression has the form ... .owner.func -> func is a field that has a function type + calculateFunctionReturnTypeBasedOnSignature( + Collections.singletonList((SymTypeOfFunction) getTypeCheckResult().getResult()), expr, argTypes); + } + else { + // MethodName has the form `... .owner.methodName -> calc the type of the owner and then calculate its method type + ((ASTFieldAccessExpression) methodNameExpr).getExpression().accept(getTraverser()); + calculateOwnedCallExpression(expr, methodName, argTypes); + } + } else { + expr.getExpression().accept(getTraverser()); + if(getTypeCheckResult().isPresentResult() && getTypeCheckResult().getResult().isFunctionType()) { + calculateFunctionReturnTypeBasedOnSignature( + Collections.singletonList((SymTypeOfFunction) getTypeCheckResult().getResult()), expr, argTypes); + } + else { + if(!getTypeCheckResult().isPresentResult()) { + Log.debug("Unexpectedly, the call expression was not made up of a field access expression " + + "and its expressions type was not a function.", + "DeriveSTOfBSCommonExpressions#calculateArithmeticCallExpression"); + } + calculateOwnedCallExpression(expr, methodName, argTypes); + } + } + } + + /** + * Calculates the type result of the call expression, given that the type result of the method owner has already been + * computed (and is accessible via getTypeCheckResult()), and that the type of its arguments has already been + * computed. + */ + protected void calculateOwnedCallExpression(ASTCallExpression expr, String methodName, List args) { + if(!getTypeCheckResult().isPresentResult() + || getTypeCheckResult().getResult().isObscureType()) { + return; + } + + SymTypeExpression methodOwnerExpr = getTypeCheckResult().getResult(); + // Filter based on the method modifiers + List methodList = getCorrectMethodsFromInnerType(methodOwnerExpr, expr, methodName); + + if(getTypeCheckResult().isType()) { + methodList = filterModifiersFunctions(methodList); + } + + if(methodList.isEmpty()) { + getTypeCheckResult().reset(); + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + String qualName = methodOwnerExpr.getTypeInfo().getName() + "." + methodName; + Log.error("0xA2239 No matching method " + qualName + " found.", expr.get_SourcePositionStart()); + } else { + calculateMethodReturnTypeBasedOnSignature(methodList, expr, args); + } + } + + + + /** + * Checks whether there exists a method symbol with the name given by {@code qualName} with matching argument types. + * If such a method exists, it's return type is set as the type check result. + * @param qualName Qualified name of the method symbol to look for. Can also be a simple name without qualification. + * @param callExpr The call expression of the method call. Needed as entry point to the symbol table to resolve + * method symbols. + * @param argTypes The types of the method arguments. Are allowed to be obscure, but in this case the method will also + * always set *obscure* to be the method's type. Nevertheless, this method may print errors, if there + * is no method symbol of the given name at all, regardless of matching argument types. + */ + protected void calculateQualifiedMethod(String qualName, + ASTCallExpression callExpr, + List argTypes) { + List funcSymbols = getScope(callExpr.getEnclosingScope()).resolveFunctionMany(qualName); + List methodList = new ArrayList<>(funcSymbols); + + calculateMethodReturnTypeBasedOnSignature(methodList, callExpr, argTypes); + } + + + protected void calculateFunctionReturnTypeBasedOnSignature(List candidates, + ASTCallExpression callExpr, + List argTypes) { + calculateFunctionReturnTypeBasedOnSignature(candidates, callExpr, argTypes, Collections.emptyMap()); + } + + /** + * Checks whether any of the {@code candidates} functions that the {@code callExpr} may represent match the given + * {@code argTypes} signature. If there are multiple candidates remaining and the callExpr has bounds on the types + * that it may represent, then the candidate with the matching return type is chosen. + * The chosen candidate's return type is then set as the current type check result. + * @param argTypes The types of the method arguments. Are allowed to be obscure, but in this case the function will also + * always set *obscure* to be the method's type. Nevertheless, this method may print errors, if there + * is no method symbol of the given name at all, regardless of matching argument types. + * @param definingSymbols if one candidate is found the corresponding symbol + * is set as the defining symbol, if applicable. + */ + protected void calculateFunctionReturnTypeBasedOnSignature(List candidates, + ASTCallExpression callExpr, + List argTypes, + Map definingSymbols) { + if(candidates.isEmpty()) { + Log.error("0xA1242 No matching function found.", callExpr.get_SourcePositionStart()); + getTypeCheckResult().reset(); + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + } + + // From now on we need the argument types of the method call. Hence, if the arguments are malformed we can not + // proceed and exit early + if(!checkNotObscure(argTypes)) { + getTypeCheckResult().reset(); + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + return; + } + + // Filter based on a compatible signature + List fittingFunctions = getFittingFunctions(candidates, callExpr, argTypes); + List mostSpecific = chooseMostSpecificFunction(fittingFunctions, argTypes, callExpr); + // There can only be one method with the correct arguments and return type + if (!mostSpecific.isEmpty()) { + if (mostSpecific.size() > 1) { + checkForReturnType(mostSpecific, callExpr); + } + if(definingSymbols.containsKey(mostSpecific.get(0))){ + callExpr.setDefiningSymbol(definingSymbols.get(mostSpecific.get(0))); + } + SymTypeExpression wholeResult = mostSpecific.get(0).getType(); + getTypeCheckResult().setMethod(); + getTypeCheckResult().setResult(wholeResult); + } else { + if(fittingFunctions.isEmpty()) { + getTypeCheckResult().reset(); + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + Log.error("0xA1241 Could not resolve method", callExpr.get_SourcePositionStart()); + } + // else an error was already logged because of ambiguity + } + } + + /** + * Checks whether any of the {@code candidates} methods that the {@code callExpr} may represent match the given + * {@code argTypes} signature. If there are multiple candidates remaining and the callExpr has bounds on the types + * that it may represent, then the candidate with the matching return type is chosen. + * The chosen candidate's return type is then set as the current type check result. + * @param argTypes The types of the method arguments. Are allowed to be obscure, but in this case the method will also + * always set *obscure* to be the method's type. Nevertheless, this method may print errors, if there + * is no method symbol of the given name at all, regardless of matching argument types. + */ + protected void calculateMethodReturnTypeBasedOnSignature(List candidates, + ASTCallExpression callExpr, + List argTypes) { + List functions = new ArrayList<>(); + Map symTypeToSymbol = new HashMap<>(); + for(FunctionSymbol functionSymbol : candidates) { + SymTypeOfFunction function = functionSymbol.getFunctionType(); + functions.add(function); + symTypeToSymbol.put(function, functionSymbol); + } + calculateFunctionReturnTypeBasedOnSignature(functions, callExpr, argTypes, symTypeToSymbol); + } + + protected List calculateArguments(ASTCallExpression expr){ + List returnList = new ArrayList<>(); + for(int i = 0; i < expr.getArguments().sizeExpressions(); i++){ + getTypeCheckResult().reset(); + expr.getArguments().getExpression(i).accept(getTraverser()); + if(getTypeCheckResult().isPresentResult() && !getTypeCheckResult().isType()){ + returnList.add(getTypeCheckResult().getResult()); + }else{ + //Placeholder as no function can have a parameter of type void and so that the correct number of + //SymTypeExpressions is in the list + returnList.add(SymTypeExpressionFactory.createObscureType()); + } + } + return returnList; + } + + protected void checkForReturnType(ASTCallExpression expr, List fittingMethods){ + checkForReturnType( + fittingMethods.stream().map(FunctionSymbol::getFunctionType).collect(Collectors.toList()), + expr + ); + } + + protected void checkForReturnType(List fittingFunctions, ASTCallExpression expr) { + SymTypeExpression returnType = fittingFunctions.get(0).getType(); + for (SymTypeOfFunction function: fittingFunctions) { + if (!returnType.deepEquals(function.getType())) { + getTypeCheckResult().reset(); + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + Log.error("0xA1239 Ambiguous method call, multiple matching functions with different return types", expr.get_SourcePositionStart()); + } + } + } + + /** + * Hookpoint for object oriented languages to get the correct functions/methods from a type based on their modifiers + */ + protected List getCorrectMethodsFromInnerType(SymTypeExpression innerResult, ASTCallExpression expr, String name) { + return innerResult.getMethodList(name, getTypeCheckResult().isType(), true, AccessModifier.ALL_INCLUSION); + } + + protected List getFittingFunctions(List candidates, + ASTCallExpression expr, + List args) { + List fittingFunctions = new ArrayList<>(); + for (SymTypeOfFunction function : candidates) { + // for every function check if the arguments are correct + if ((!function.isElliptic() && + args.size() == function.getArgumentTypeList().size()) + || (function.isElliptic() && + args.size() >= function.getArgumentTypeList().size() - 1)) { + boolean success = true; + for (int i = 0; i < args.size(); i++) { + // test if every single argument is correct + // if an argument is void type then it could not be calculated correctly -> see calculateArguments + SymTypeExpression paramType = function.getArgumentTypeList() + .get(Math.min(i, function.getArgumentTypeList().size() - 1)); + if (!paramType.deepEquals(args.get(i)) && + !compatible(paramType, args.get(i)) || + args.get(i).isVoidType()) { + success = false; + } + } + if (success) { + // function has the correct arguments and return type + fittingFunctions.add(function); + } + } + } + return fittingFunctions; + } + + protected List chooseMostSpecificFunction(List candidates, List args, ASTCallExpression expr) { + if(candidates.size() <= 1){ + return candidates; + } + boolean ambiguous = false; + Map specificityMap = new HashMap<>(); + List mostSpecific = Lists.newArrayList(candidates.get(0)); + for(SymTypeOfFunction function: candidates) { + int[] specificity = new int[args.size()]; + for(int i = 0; i spec2[i] && spec2[i] != -1){ + return fun2; + } + } + return fun1; + } + + /** + * Hookpoint for object oriented languages that offer modifiers like static, public, private, ... + */ + protected List filterModifiersFunctions(List functionSymbols) { + return functionSymbols; + } + + /** + * helper method for <=, >=, <, > -> calculates the result of these expressions + */ + protected SymTypeExpression calculateTypeCompare(SymTypeExpression left, SymTypeExpression right, String op, SourcePosition pos) { + // if the left and the right part of the expression are numerics, + // then the whole expression is a boolean + if (isNumericType(left) && isNumericType(right)) { + return SymTypeExpressionFactory.createPrimitive(BasicSymbolsMill.BOOLEAN); + } else { + // else operator not applicable + Log.error("0xA0167 Operator '" + op + "' not applicable to " + "'" + left.print() + "', '" + right.print() + "'", pos); + return SymTypeExpressionFactory.createObscureType(); + } + } + + /** + * helper method for ==, != calculates the result of these expressions + */ + protected SymTypeExpression calculateTypeLogical(SymTypeExpression left, SymTypeExpression right, String op, SourcePosition pos) { + //Option one: they are both numeric types + if (isNumericType(left) && isNumericType(right) || isBoolean(left) && isBoolean(right)) { + return SymTypeExpressionFactory.createPrimitive(BasicSymbolsMill.BOOLEAN); + } else if (compatible(left, right) || compatible(right, left)) { + return SymTypeExpressionFactory.createPrimitive(BasicSymbolsMill.BOOLEAN); + } else { + // else operator not applicable + Log.error("0xA0166 Operator '" + op + "' not applicable to " + "'" + left.print() + "', '" + right.print() + "'", pos); + return SymTypeExpressionFactory.createObscureType(); + } + } + + /** + * return the result for the five basic arithmetic operations (+,-,*,/,%) + */ + protected SymTypeExpression getBinaryNumericPromotion(SymTypeExpression leftResult, SymTypeExpression rightResult) { + //if one part of the expression is a double and the other is another numeric type then the result is a double + if ((isDouble(leftResult) && isNumericType(rightResult)) || + (isDouble(rightResult) && isNumericType(leftResult))) { + return SymTypeExpressionFactory.createPrimitive("double"); + //no part of the expression is a double -> try again with float + } else if ((isFloat(leftResult) && isNumericType(rightResult)) || + (isFloat(rightResult) && isNumericType(leftResult))) { + return SymTypeExpressionFactory.createPrimitive("float"); + //no part of the expression is a float -> try again with long + } else if ((isLong(leftResult) && isNumericType(rightResult)) || + (isLong(rightResult) && isNumericType(leftResult))) { + return SymTypeExpressionFactory.createPrimitive("long"); + //no part of the expression is a long -> if both parts are numeric types then the result is a int + } else if (isIntegralType(leftResult) && isIntegralType(rightResult) + ) { + return SymTypeExpressionFactory.createPrimitive("int"); + } + //should never happen, no valid result, error will be handled in traverse + return SymTypeExpressionFactory.createObscureType(); + } + + protected SymTypeExpression calculateArithmeticExpression(SymTypeExpression left, SymTypeExpression right, String op, SourcePosition pos) { + // if one part of the expression is a double and the other is another numeric type then the result is a double + if ((isDouble(left) && isNumericType(right)) || (isDouble(right) && isNumericType(left))) { + return SymTypeExpressionFactory.createPrimitive(BasicSymbolsMill.DOUBLE); + // no part of the expression is a double -> try again with float + } else if ((isFloat(left) && isNumericType(right)) || (isFloat(right) && isNumericType(left))) { + return SymTypeExpressionFactory.createPrimitive(BasicSymbolsMill.FLOAT); + // no part of the expression is a float -> try again with long + } else if ((isLong(left) && isNumericType(right)) || (isLong(right) && isNumericType(left))) { + return SymTypeExpressionFactory.createPrimitive(BasicSymbolsMill.LONG); + // no part of the expression is a long -> if both parts are numeric types then the result is an int + } else if (isIntegralType(left) && isIntegralType(right)) { + return SymTypeExpressionFactory.createPrimitive(BasicSymbolsMill.INT); + } else { + // else operator not applicable + Log.error("0xA0168 Operator '" + op + "' not applicable to " + "'" + left.print() + "', '" + right.print() + "'", pos); + } + //should never happen, no valid result, error will be handled in traverse + return SymTypeExpressionFactory.createObscureType(); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/DeriveSymTypeOfBitExpressions.java b/monticore-grammar/src/main/java/de/monticore/types/check/DeriveSymTypeOfBitExpressions.java new file mode 100644 index 0000000000..f98a86fc61 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/DeriveSymTypeOfBitExpressions.java @@ -0,0 +1,216 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import com.google.common.base.Preconditions; +import de.monticore.expressions.bitexpressions._ast.*; +import de.monticore.expressions.bitexpressions._visitor.BitExpressionsHandler; +import de.monticore.expressions.bitexpressions._visitor.BitExpressionsTraverser; +import de.monticore.expressions.bitexpressions._visitor.BitExpressionsVisitor2; +import de.se_rwth.commons.SourcePosition; +import de.se_rwth.commons.logging.Log; + +/** + * This Visitor can calculate a SymTypeExpression (type) for the expressions in BitExpressions + * It can be combined with other expressions in your language by creating a DelegatorVisitor + */ +public class DeriveSymTypeOfBitExpressions extends AbstractDeriveFromExpression implements BitExpressionsVisitor2, BitExpressionsHandler { + + protected BitExpressionsTraverser traverser; + + @Override + public void setTraverser(BitExpressionsTraverser traverser) { + this.traverser = traverser; + } + + @Override + public BitExpressionsTraverser getTraverser() { + return traverser; + } + + @Override + public void traverse(ASTLeftShiftExpression expr) { + Preconditions.checkNotNull(expr); + SymTypeExpression symType = this.deriveShift(expr, "<<"); + + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(symType); + } + + @Override + public void traverse(ASTRightShiftExpression expr) { + Preconditions.checkNotNull(expr); + SymTypeExpression symType = this.deriveShift(expr, ">>"); + + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(symType); + } + + @Override + public void traverse(ASTLogicalRightShiftExpression expr) { + Preconditions.checkNotNull(expr); + SymTypeExpression symType = this.deriveShift(expr, ">>>"); + + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(symType); + } + + public SymTypeExpression deriveShift(ASTShiftExpression expr, String op) { + // calculate the type of inner expressions + this.getTypeCheckResult().reset(); + expr.getLeft().accept(this.getTraverser()); + TypeCheckResult left = this.getTypeCheckResult().copy(); + this.getTypeCheckResult().reset(); + expr.getRight().accept(this.getTraverser()); + TypeCheckResult right = this.getTypeCheckResult().copy(); + + // result of inner type computation should be present + if (!left.isPresentResult() || !right.isPresentResult()) { + // should never happen, we expect results to be present + // indicates that the underlying type resolver is erroneous + this.logError("0xA0200", expr.get_SourcePositionStart()); + return SymTypeExpressionFactory.createObscureType(); + } else if (left.getResult().isObscureType() || right.getResult().isObscureType()) { + // if left or right obscure then error already logged + return SymTypeExpressionFactory.createObscureType(); + } else { + return calculateTypeShift(left.getResult(), right.getResult(), op, expr.get_SourcePositionStart()); + } + } + + protected SymTypeExpression calculateTypeShift(SymTypeExpression leftResult, SymTypeExpression rightResult, String op, SourcePosition pos) { + if (leftResult.isPrimitive() && rightResult.isPrimitive()) { + SymTypePrimitive leftEx = (SymTypePrimitive) leftResult; + SymTypePrimitive rightEx = (SymTypePrimitive) rightResult; + + //only defined on integral type - integral type + if (isIntegralType(leftEx) && isIntegralType(rightEx)) { + return shiftCalculator(leftResult, rightResult, op, pos); + } + } + //should not happen, will be handled in traverse + Log.error("0xA0201 Operator " + op + " not applicable to the types" + + "'" + leftResult.print() + "', '" + rightResult.print() + "'"); + return SymTypeExpressionFactory.createObscureType(); + } + + /** + * helper method to calculate the type of the ShiftExpressions + * cannot be linked with the BinaryExpressions because they are not calculated the same way + */ + protected SymTypeExpression shiftCalculator(SymTypeExpression left, SymTypeExpression right, String op, SourcePosition pos) { + if (!left.isPrimitive() || !right.isPrimitive()){ + Log.error("0xA0204 The operator " + op + " is only applicable to primitive types.", pos); + return SymTypeExpressionFactory.createObscureType(); + } + SymTypePrimitive leftResult = (SymTypePrimitive) left; + SymTypePrimitive rightResult = (SymTypePrimitive) right; + + //only defined on integral type - integral type + if (isIntegralType(leftResult) && isIntegralType(rightResult)) { + if (TypeCheck.isLong(rightResult)) { + if (TypeCheck.isLong(leftResult)) { + return SymTypeExpressionFactory.createPrimitive("long"); + } else { + return SymTypeExpressionFactory.createPrimitive("int"); + } + } else { + return SymTypeExpressionFactory.createPrimitive("int"); + } + } + //should never happen + Log.error("0xA0205 The operator " + op + " is only applicable to integral types.", pos); + return SymTypeExpressionFactory.createObscureType(); + } + + @Override + public void traverse(ASTBinaryAndExpression expr) { + Preconditions.checkNotNull(expr); + SymTypeExpression symType = this.deriveBinary(expr, expr.getOperator()); + + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(symType); + } + + @Override + public void traverse(ASTBinaryOrOpExpression expr) { + Preconditions.checkNotNull(expr); + SymTypeExpression symType = this.deriveBinary(expr, expr.getOperator()); + + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(symType); + } + + @Override + public void traverse(ASTBinaryXorExpression expr) { + Preconditions.checkNotNull(expr); + SymTypeExpression symType = this.deriveBinary(expr, expr.getOperator()); + + this.getTypeCheckResult().reset(); + this.getTypeCheckResult().setResult(symType); + } + + protected SymTypeExpression deriveBinary(ASTBinaryExpression expr, String operator) { + // calculate the type of inner expressions + this.getTypeCheckResult().reset(); + expr.getLeft().accept(this.getTraverser()); + TypeCheckResult leftRes = this.getTypeCheckResult().copy(); + this.getTypeCheckResult().reset(); + expr.getRight().accept(this.getTraverser()); + TypeCheckResult rightRes = this.getTypeCheckResult().copy(); + + // result of inner type computation should be present + if (!leftRes.isPresentResult() || !rightRes.isPresentResult()) { + // should never happen, we expect results to be present + // indicates that the underlying type resolver is erroneous + this.logError("0xA0202", expr.get_SourcePositionStart()); + return SymTypeExpressionFactory.createObscureType(); + } else if (leftRes.getResult().isObscureType() || rightRes.getResult().isObscureType()) { + // if left or right obscure then error already logged + return SymTypeExpressionFactory.createObscureType(); + } else { + return calculateTypeBinary(leftRes.getResult(), rightRes.getResult(), operator, expr.get_SourcePositionStart()); + } + } + + protected SymTypeExpression calculateTypeBinary(SymTypeExpression leftResult, SymTypeExpression rightResult, String operator, SourcePosition pos) { + if (leftResult.isPrimitive() && rightResult.isPrimitive()) { + SymTypePrimitive leftEx = (SymTypePrimitive) leftResult; + SymTypePrimitive rightEx = (SymTypePrimitive) rightResult; + + //only defined on boolean - boolean and integral type - integral type + if (TypeCheck.isBoolean(leftResult) && TypeCheck.isBoolean(rightResult)) { + return SymTypeExpressionFactory.createPrimitive("boolean"); + } else if (isIntegralType(leftEx) && isIntegralType(rightEx)) { + return getBinaryNumericPromotion(leftResult, rightResult, operator, pos); + } + } + //should not happen, no valid result, error will be handled in traverse + Log.error("0xA0203 The operator " + operator + " is not applicable to the types" + + "'" + leftResult + "', '" + rightResult +"'", pos); + return SymTypeExpressionFactory.createObscureType(); + } + + /** + * helper method to calculate the type of the BinaryExpressions + */ + protected SymTypeExpression getBinaryNumericPromotion(SymTypeExpression left, SymTypeExpression right, String op, SourcePosition pos){ + //only integral type - integral type + if(left.isPrimitive() && right.isPrimitive()) { + SymTypePrimitive leftResult = (SymTypePrimitive) left; + SymTypePrimitive rightResult = (SymTypePrimitive) right; + if ((TypeCheck.isLong(leftResult) && rightResult.isIntegralType()) || + (TypeCheck.isLong(rightResult) && leftResult.isIntegralType())) { + return SymTypeExpressionFactory.createPrimitive("long"); + //no part of the expression is a long -> if both parts are integral types then the result is a int + }else{ + if (leftResult.isIntegralType()&&rightResult.isIntegralType()) { + return SymTypeExpressionFactory.createPrimitive("int"); + } + } + } + //should not happen, no valid result, error will be handled in traverse + Log.error("0xA0206 The types " + left.print() + " and " + right.print() + + " could not be combined in terms of the operator " + op, pos); + return SymTypeExpressionFactory.createObscureType(); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/DeriveSymTypeOfCommonExpressions.java b/monticore-grammar/src/main/java/de/monticore/types/check/DeriveSymTypeOfCommonExpressions.java new file mode 100644 index 0000000000..2378faf5eb --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/DeriveSymTypeOfCommonExpressions.java @@ -0,0 +1,52 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.expressions.commonexpressions._ast.*; +import de.monticore.symbols.basicsymbols._symboltable.FunctionSymbol; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.symbols.basicsymbols._symboltable.VariableSymbol; +import de.monticore.symbols.oosymbols._symboltable.FieldSymbol; +import de.monticore.symbols.oosymbols._symboltable.MethodSymbol; +import de.monticore.symbols.oosymbols._symboltable.OOTypeSymbol; +import de.monticore.symboltable.modifiers.AccessModifier; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * This Visitor can calculate a SymTypeExpression (type) for the expressions in CommonExpressions. + * It is the OO extension of the class {@link de.monticore.types.check.DeriveSymTypeOfBSCommonExpressions} and adds OO + * functionalities like modifiers to the derivation of a SymTypeExpression. + * It can be combined with other expressions in your language by creating a DelegatorVisitor + */ +public class DeriveSymTypeOfCommonExpressions extends DeriveSymTypeOfBSCommonExpressions { + + @Override + protected List filterModifiersVariables(List variableSymbols) { + return variableSymbols.stream().filter(f -> f instanceof FieldSymbol ? ((FieldSymbol) f).isIsStatic() : false).collect(Collectors.toList()); + } + + @Override + protected List filterModifiersFunctions(List functionSymbols) { + return functionSymbols.stream().filter(m -> m instanceof MethodSymbol ? ((MethodSymbol) m).isIsStatic() : true).collect(Collectors.toList()); + } + + @Override + protected boolean checkModifierType(TypeSymbol typeSymbol) { + //if the last result is a type and the type is not static then it is not accessible + if (getTypeCheckResult().isType()) { + return typeSymbol instanceof OOTypeSymbol ? ((OOTypeSymbol) typeSymbol).isIsStatic() : true; + } + return true; + } + + @Override + protected List getCorrectMethodsFromInnerType(SymTypeExpression innerResult, ASTCallExpression expr, String name) { + return innerResult.getMethodList(name, getTypeCheckResult().isType(), false, AccessModifier.ALL_INCLUSION); + } + + @Override + protected List getCorrectFieldsFromInnerType(SymTypeExpression innerResult, ASTFieldAccessExpression expr) { + return innerResult.getFieldList(expr.getName(), getTypeCheckResult().isType(), false, AccessModifier.ALL_INCLUSION); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/DeriveSymTypeOfExpression.java b/monticore-grammar/src/main/java/de/monticore/types/check/DeriveSymTypeOfExpression.java new file mode 100644 index 0000000000..796e593a5b --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/DeriveSymTypeOfExpression.java @@ -0,0 +1,91 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.expressions.expressionsbasis._ast.*; +import de.monticore.expressions.expressionsbasis._symboltable.*; +import de.monticore.expressions.expressionsbasis._visitor.ExpressionsBasisHandler; +import de.monticore.expressions.expressionsbasis._visitor.ExpressionsBasisTraverser; +import de.monticore.expressions.expressionsbasis._visitor.ExpressionsBasisVisitor2; +import de.monticore.symbols.basicsymbols._symboltable.IBasicSymbolsScope; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.symbols.basicsymbols._symboltable.TypeVarSymbol; +import de.monticore.symbols.basicsymbols._symboltable.VariableSymbol; +import de.se_rwth.commons.logging.Log; + +import java.util.*; + +import static de.monticore.types.check.SymTypeExpressionFactory.*; + +/** + * This Visitor can calculate a SymTypeExpression (type) for the expressions in ExpressionsBasis + * It can be combined with other expressions in your language by creating a DelegatorVisitor + */ +public class DeriveSymTypeOfExpression extends AbstractDeriveFromExpression implements ExpressionsBasisVisitor2, ExpressionsBasisHandler { + + public IBasicSymbolsScope getScope (IExpressionsBasisScope expressionsBasisScope){ + // is accepted only here, decided on 07.04.2020 + if(!(expressionsBasisScope instanceof IBasicSymbolsScope)){ + Log.error("0xA2308 the enclosing scope of the expression does not implement the interface IBasicSymbolsScope"); + } + // is accepted only here, decided on 07.04.2020 + return (IBasicSymbolsScope) expressionsBasisScope; + } + + protected ExpressionsBasisTraverser traverser; + + @Override + public ExpressionsBasisTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(ExpressionsBasisTraverser traverser) { + this.traverser = traverser; + } + + @Override + public void traverse(ASTNameExpression expr) { + Optional wholeResult = calculateNameExpression(expr); + if(wholeResult.isPresent()){ + getTypeCheckResult().setResult(wholeResult.get()); + }else{ + getTypeCheckResult().reset(); + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + Log.error("0xA0240 Cannot find symbol " + expr.getName(), expr.get_SourcePositionStart()); + } + } + + protected Optional calculateNameExpression(ASTNameExpression expr){ + Optional optVar = getScope(expr.getEnclosingScope()).resolveVariable(expr.getName()); + Optional optTypeVar = getScope(expr.getEnclosingScope()).resolveTypeVar(expr.getName()); + Optional optType = getScope(expr.getEnclosingScope()).resolveType(expr.getName()); + if("null".equals(expr.getName())){ + SymTypeExpression res = createTypeOfNull(); + expr.setDefiningSymbol(res.getTypeInfo()); + return Optional.of(res); + }else if (optVar.isPresent()) { + //no method here, test variable first + // durch AST-Umbau kann ASTNameExpression keine Methode sein + VariableSymbol var = optVar.get(); + expr.setDefiningSymbol(var); + SymTypeExpression res = var.getType().deepClone(); + getTypeCheckResult().setField(); + return Optional.of(res); + } else if(optTypeVar.isPresent()) { + TypeVarSymbol typeVar = optTypeVar.get(); + expr.setDefiningSymbol(typeVar); + SymTypeExpression res = createTypeVariable(typeVar); + getTypeCheckResult().setType(); + return Optional.of(res); + } else if (optType.isPresent()) { + //no variable found, test if name is type + TypeSymbol type = optType.get(); + expr.setDefiningSymbol(type); + SymTypeExpression res = createTypeExpression(type); + getTypeCheckResult().setType(); + return Optional.of(res); + } + return Optional.empty(); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/DeriveSymTypeOfJavaClassExpressions.java b/monticore-grammar/src/main/java/de/monticore/types/check/DeriveSymTypeOfJavaClassExpressions.java new file mode 100644 index 0000000000..018d51b750 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/DeriveSymTypeOfJavaClassExpressions.java @@ -0,0 +1,728 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import de.monticore.expressions.expressionsbasis._ast.ASTArguments; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.expressionsbasis._symboltable.IExpressionsBasisScope; +import de.monticore.expressions.javaclassexpressions._ast.*; +import de.monticore.expressions.javaclassexpressions._visitor.JavaClassExpressionsHandler; +import de.monticore.expressions.javaclassexpressions._visitor.JavaClassExpressionsTraverser; +import de.monticore.expressions.javaclassexpressions._visitor.JavaClassExpressionsVisitor2; +import de.monticore.symbols.basicsymbols._symboltable.*; +import de.monticore.symbols.oosymbols._symboltable.MethodSymbol; +import de.monticore.symbols.oosymbols._symboltable.OOTypeSymbol; +import de.monticore.symboltable.modifiers.AccessModifier; +import de.se_rwth.commons.logging.Log; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +import static de.monticore.types.check.TypeCheck.compatible; + +/** + * This Visitor can calculate a SymTypeExpression (type) for the expressions in JavaClassExpressions + * It can be combined with other expressions in your language by creating a DelegatorVisitor + */ +public class DeriveSymTypeOfJavaClassExpressions extends AbstractDeriveFromExpression implements JavaClassExpressionsVisitor2, JavaClassExpressionsHandler { + + protected JavaClassExpressionsTraverser traverser; + + @Override + public JavaClassExpressionsTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(JavaClassExpressionsTraverser traverser) { + this.traverser = traverser; + } + + @Override + public void traverse(ASTThisExpression node) { + SymTypeExpression innerResult = acceptThisAndReturnSymTypeExpression(node.getExpression()); + if(!innerResult.isObscureType()) { + SymTypeExpression wholeResult = calculateThisExpression(node, innerResult); + + getTypeCheckResult().reset(); + getTypeCheckResult().setResult(wholeResult); + if (wholeResult.isObscureType()) { + Log.error("0xA0252 Could not derive the type that 'this' refers to", node.get_SourcePositionStart()); + } + } + } + + protected SymTypeExpression calculateThisExpression(ASTThisExpression expr, SymTypeExpression innerResult){ + //no primitive type and only type allowed --> check that Expression is no field or method + //JAVA: can only be used in nested classes to get an instance of the enclosing class + //traverse the inner expression, check that it is a type; this type is the current class and is a nested class + //can be calculated + SymTypeExpression wholeResult = SymTypeExpressionFactory.createObscureType(); + //check recursively until there is no enclosing scope or the spanningsymbol of the scope is a type + //while the enclosing scope is not null, it is possible that the expression can be calculated + int count = 0; + if(getTypeCheckResult().isType() && getScope(expr.getEnclosingScope()).getEnclosingScope()!=null) { + IBasicSymbolsScope testScope = getScope(expr.getEnclosingScope()); + while (testScope!=null) { + if(testScope.isPresentSpanningSymbol()&&testScope.getSpanningSymbol() instanceof OOTypeSymbol) { + count++; + OOTypeSymbol sym = (OOTypeSymbol) testScope.getSpanningSymbol(); + if (sym.getName().equals(innerResult.getTypeInfo().getName())&&count>1) { + wholeResult = innerResult; + break; + } + } + testScope = testScope.getEnclosingScope(); + } + } + return wholeResult; + } + + @Override + public void traverse(ASTArrayExpression node) { + SymTypeExpression indexResult = acceptThisAndReturnSymTypeExpression(node.getIndexExpression()); + if(getTypeCheckResult().isType()){ + getTypeCheckResult().reset(); + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + Log.error("0xA0253 the expression at source position "+node.getIndexExpression().get_SourcePositionStart()+" cannot be a type"); + return; + } + SymTypeExpression arrayTypeResult = acceptThisAndReturnSymTypeExpression(node.getExpression()); + if(getTypeCheckResult().isType()){ + getTypeCheckResult().reset(); + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + Log.error("0xA0255 the expression at source position "+node.getExpression().get_SourcePositionStart()+" cannot be a type"); + return; + } + + if(!indexResult.isObscureType() && !arrayTypeResult.isObscureType()){ + SymTypeExpression wholeResult = calculateArrayExpression(node, arrayTypeResult, indexResult); + storeResultOrLogError(wholeResult, node, "0xA0257"); + }else{ + getTypeCheckResult().reset(); + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + } + } + + protected SymTypeExpression calculateArrayExpression(ASTArrayExpression node, SymTypeExpression arrayTypeResult, SymTypeExpression indexResult) { + SymTypeExpression wholeResult = SymTypeExpressionFactory.createObscureType(); + //the type of the index has to be an integral type + if(indexResult.isPrimitive() && ((SymTypePrimitive)indexResult).isIntegralType() && arrayTypeResult instanceof SymTypeArray){ + SymTypeArray arrayResult = (SymTypeArray) arrayTypeResult; + wholeResult = getCorrectResultArrayExpression(node.getEnclosingScope(), indexResult, arrayTypeResult, arrayResult); + } + return wholeResult; + } + + protected SymTypeExpression getCorrectResultArrayExpression(IExpressionsBasisScope scope, SymTypeExpression indexResult, SymTypeExpression arrayTypeResult, SymTypeArray arrayResult) { + SymTypeExpression wholeResult; + if(arrayResult.getDim()>1){ + //case 1: A[][] bar -> bar[3] returns the type A[] -> decrease the dimension of the array by 1 + wholeResult = SymTypeExpressionFactory.createTypeArray(arrayTypeResult.getTypeInfo().getName(),getScope(scope),arrayResult.getDim()-1,indexResult); + }else { + //case 2: A[] bar -> bar[3] returns the type A + //determine whether the result has to be a constant, generic or object + if(arrayResult.getTypeInfo().getTypeParameterList().isEmpty()){ + //if the return type is a primitive + if(SymTypePrimitive.boxMap.containsKey(arrayResult.getTypeInfo().getName())){ + wholeResult = SymTypeExpressionFactory.createPrimitive(arrayResult.getTypeInfo().getName()); + }else { + //if the return type is an object + wholeResult = SymTypeExpressionFactory.createTypeObject(arrayResult.getTypeInfo().getName(), getScope(scope)); + } + }else { + //the return type must be a generic + List typeArgs = Lists.newArrayList(); + for(TypeVarSymbol s : arrayResult.getTypeInfo().getTypeParameterList()){ + typeArgs.add(SymTypeExpressionFactory.createTypeVariable(s.getName(),getScope(scope))); + } + wholeResult = SymTypeExpressionFactory.createGenerics(arrayResult.getTypeInfo().getName(), getScope(scope), typeArgs); + wholeResult = replaceTypeVariables(wholeResult,typeArgs,((SymTypeOfGenerics)arrayResult.getArgument()).getArgumentList()); + } + } + return wholeResult; + } + + protected SymTypeExpression replaceTypeVariables(SymTypeExpression wholeResult, List typeArgs, List argumentList) { + Map map = Maps.newHashMap(); + if(typeArgs.size()!=argumentList.size()){ + getTypeCheckResult().reset(); + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + Log.error("0xA2297 Different amount of type variables and type arguments"); + }else{ + for(int i = 0;i oldArgs = ((SymTypeOfGenerics) wholeResult).getArgumentList(); + List newArgs = Lists.newArrayList(); + for (SymTypeExpression oldArg : oldArgs) { + newArgs.add(map.getOrDefault(oldArg, oldArg)); + } + ((SymTypeOfGenerics) wholeResult).setArgumentList(newArgs); + } + return wholeResult; + } + + @Override + public void traverse(ASTClassExpression node) { + //only type allowed --> check that Expression is no field or method + //traverse the inner expression, check that it is a type (how?); the result is the type "Class" + //can be calculated + SymTypeExpression wholeResult = SymTypeExpressionFactory.createObscureType(); + SymTypeExpression innerResult = SymTypeExpressionFactory.createObscureType(); + + node.getExtReturnType().accept(getTraverser()); + if(getTypeCheckResult().isPresentResult()){ + innerResult = getTypeCheckResult().getResult(); + wholeResult = SymTypeExpressionFactory.createGenerics("Class", getScope(node.getEnclosingScope()), innerResult); + } + if(!innerResult.isObscureType()) { + storeResultOrLogError(wholeResult, node, "0xA0258"); + } + } + + @Override + public void traverse(ASTSuperExpression node) { + //the expression before the super has to be a nested type + //search for the enclosing type, get its super class and execute the supersuffix + SymTypeExpression beforeSuperType = acceptThisAndReturnSymTypeExpression(node.getExpression()); + if(!getTypeCheckResult().isType()){ + getTypeCheckResult().reset(); + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + Log.error("0xA0259 the expression at source position "+node.getExpression().get_SourcePositionStart()+" has to be a type"); + return; + } + + if(!beforeSuperType.isObscureType()) { + SymTypeExpression wholeResult = calculateSuperExpression(node, beforeSuperType); + storeResultOrLogError(wholeResult, node, "0xA0261"); + } + } + + protected SymTypeExpression calculateSuperExpression(ASTSuperExpression node, SymTypeExpression beforeSuperType) { + SymTypeExpression wholeResult = SymTypeExpressionFactory.createObscureType(); + int count = 0; + boolean isOuterType = false; + IBasicSymbolsScope testScope = getScope(node.getEnclosingScope()); + while (testScope!=null) { + if(testScope.isPresentSpanningSymbol()&&testScope.getSpanningSymbol() instanceof TypeSymbol) { + count++; + TypeSymbol sym = (TypeSymbol) testScope.getSpanningSymbol(); + if (sym.getName().equals(beforeSuperType.getTypeInfo().getName())&&count>1) { + isOuterType = true; + break; + } + } + testScope = testScope.getEnclosingScope(); + } + + if(isOuterType) { + List superClasses = beforeSuperType.getTypeInfo().getSuperClassesOnly(); + if (superClasses.size() == 1) { + SymTypeExpression superClass = superClasses.get(0); + if (null != node.getSuperSuffix().getName() || !"".equals(node.getSuperSuffix().getName())) { + ASTSuperSuffix superSuffix = node.getSuperSuffix(); + wholeResult = handleSuperSuffix(superSuffix, superClass); + } + } + } + return wholeResult; + } + + protected SymTypeExpression handleSuperSuffix(ASTSuperSuffix superSuffix, SymTypeExpression superClass){ + if (superSuffix.isPresentArguments()) { + //case 1 -> Expression.super.Method(Args) + List typeArgsList = calculateTypeArguments(superSuffix.getExtTypeArgumentList()); + List methods = superClass.getMethodList(superSuffix.getName(), false, AccessModifier.ALL_INCLUSION); + if (!methods.isEmpty() && null != superSuffix.getArguments()) { + //check if the methods fit and return the right returntype + ASTArguments args = superSuffix.getArguments(); + return checkMethodsAndReplaceTypeVariables(methods, args, typeArgsList); + } + } + else { + //case 2 -> Expression.super.Field + List fields = superClass.getFieldList(superSuffix.getName(), false, AccessModifier.ALL_INCLUSION); + if (fields.size()==1) { + return fields.get(0).getType(); + }else{ + getTypeCheckResult().reset(); + Log.error("0xA1305 There cannot be more than one field with the same name"); + return SymTypeExpressionFactory.createObscureType(); + } + } + return SymTypeExpressionFactory.createObscureType(); + } + + @Override + public void traverse(ASTTypeCastExpression node) { + //innerResult is the SymTypeExpression of the type that will be casted into another type + SymTypeExpression innerResult = acceptThisAndReturnSymTypeExpression(node.getExpression()); + if(getTypeCheckResult().isType()){ + getTypeCheckResult().reset(); + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + Log.error("0xA0262 the expression at source position "+node.getExpression().get_SourcePositionStart()+" cannot be a type"); + return; + } + //castResult is the SymTypeExpression of the type the innerResult will be casted to + SymTypeExpression castResult; + + //castResult is the type in the brackets -> (ArrayList) list + node.getExtType().accept(getTraverser()); + if(getTypeCheckResult().isPresentResult()){ + castResult = getTypeCheckResult().getResult(); + }else{ + getTypeCheckResult().reset(); + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + Log.error("0xA0265 the type at source position "+node.getExtType().get_SourcePositionStart()+" cannot be calculated"); + return; + } + + if(!innerResult.isObscureType() && !castResult.isObscureType()) { + //wholeResult will be the result of the whole expression + SymTypeExpression wholeResult = calculateTypeCastExpression(node, castResult, innerResult); + + storeResultOrLogError(wholeResult, node, "0xA0266"); + }else{ + getTypeCheckResult().reset(); + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + } + } + + protected SymTypeExpression calculateTypeCastExpression(ASTTypeCastExpression node, SymTypeExpression castResult, SymTypeExpression innerResult) { + if(compatible(castResult,innerResult)|| compatible(innerResult,castResult)){ + return castResult; + } + return SymTypeExpressionFactory.createObscureType(); + } + + @Override + public void traverse(ASTInstanceofExpression node) { + SymTypeExpression expressionResult = acceptThisAndReturnSymTypeExpression(node.getExpression()); + if(getTypeCheckResult().isType()) { + getTypeCheckResult().reset(); + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + Log.error("0xA0267 the expression at source position " + node.getExpression().get_SourcePositionStart() + " cannot be a type"); + return; + } + + SymTypeExpression typeResult = SymTypeExpressionFactory.createObscureType(); + + //calculate right type: type that the expression should be an instance of + node.getExtType().accept(getTraverser()); + if(getTypeCheckResult().isPresentResult()){ + if(!getTypeCheckResult().isType()) { + if(!getTypeCheckResult().getResult().isObscureType()) { + getTypeCheckResult().reset(); + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + Log.error("0xA0269 the expression at source position " + node.getExtType().get_SourcePositionStart() + " must be a type"); + return; + } + }else{ + typeResult = getTypeCheckResult().getResult(); + } + }else{ + getTypeCheckResult().reset(); + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + logError("0xA0270",node.getExpression().get_SourcePositionStart()); + return; + } + + if(!expressionResult.isObscureType() && !typeResult.isObscureType()){ + //the method was not finished yet (either with Log.error or return) -> both types are present and thus the result is boolean + SymTypeExpression wholeResult = SymTypeExpressionFactory.createPrimitive("boolean"); + + getTypeCheckResult().setResult(wholeResult); + }else{ + getTypeCheckResult().reset(); + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + } + } + + @Override + public void traverse(ASTPrimaryThisExpression node) { + //search for the nearest TypeSymbol and return its Type + SymTypeExpression wholeResult = SymTypeExpressionFactory.createObscureType(); + Optional typeSymbol=searchForTypeSymbolSpanningEnclosingScope(getScope(node.getEnclosingScope())); + if(typeSymbol.isPresent()) { + wholeResult = getResultOfPrimaryThisExpression(getScope(node.getEnclosingScope()), typeSymbol.get()); + } + storeResultOrLogError(wholeResult, node, "0xA0272"); + } + + protected SymTypeExpression getResultOfPrimaryThisExpression(IBasicSymbolsScope scope, TypeSymbol typeSymbol) { + SymTypeExpression wholeResult; + if(typeSymbol.getTypeParameterList().isEmpty()){ + //if the return type is a primitive + if(SymTypePrimitive.unboxMap.containsKey(typeSymbol.getName())){ + wholeResult = SymTypeExpressionFactory.createPrimitive(typeSymbol.getName()); + }else { + //the return type is an object + wholeResult = SymTypeExpressionFactory.createTypeObject(typeSymbol); + } + }else { + //the return type must be a generic + List typeArgs = Lists.newArrayList(); + for(TypeVarSymbol s : typeSymbol.getTypeParameterList()){ + typeArgs.add(SymTypeExpressionFactory.createTypeVariable(s)); + } + wholeResult = SymTypeExpressionFactory.createGenerics(typeSymbol, typeArgs); + } + return wholeResult; + } + + @Override + public void traverse(ASTPrimarySuperExpression node) { + SymTypeExpression wholeResult = SymTypeExpressionFactory.createObscureType(); + + Optional typeSymbol = searchForTypeSymbolSpanningEnclosingScope(getScope(node.getEnclosingScope())); + if(typeSymbol.isPresent()) { + List superClasses = typeSymbol.get().getSuperClassesOnly(); + if (superClasses.size() == 1) { + wholeResult = superClasses.get(0); + } + else { + getTypeCheckResult().reset(); + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + Log.error("0xA0273 for super to work there has to be exactly one superclass"); + return; + } + } + storeResultOrLogError(wholeResult, node, "0xA0280"); + } + + @Override + public void traverse(ASTGenericInvocationExpression node) { + //expressions of type A.B.c() or A.B.super.c() plus Arguments in the brackets + SymTypeExpression expressionResult = acceptThisAndReturnSymTypeExpression(node.getExpression()); + boolean isType = getTypeCheckResult().isType(); + + if(!expressionResult.isObscureType()) { + SymTypeExpression wholeResult = calculateGenericInvocationExpression(node, expressionResult, isType); + storeResultOrLogError(wholeResult, node, "0xA0282"); + } + } + + protected SymTypeExpression calculateGenericInvocationExpression(ASTGenericInvocationExpression node, SymTypeExpression expressionResult, boolean isType) { + SymTypeExpression wholeResult = SymTypeExpressionFactory.createObscureType(); + //the only case where you can calculate a result is Expression.method() + //because the other cases of the GenericInvocationSuffix can only be calculated if the expression + //is a PrimaryGenericInvocationExpression + List typeArgsList = calculateTypeArguments(node.getPrimaryGenericInvocationExpression().getExtTypeArgumentList()); + + //search in the scope of the type that before the "." for a method that has the right name + if(node.getPrimaryGenericInvocationExpression().getGenericInvocationSuffix().isPresentName()) { + List methods = expressionResult.getMethodList(node.getPrimaryGenericInvocationExpression().getGenericInvocationSuffix().getName(),isType,false, AccessModifier.ALL_INCLUSION); + //if the last result is a type then the method has to be static to be accessible + if(isType){ + methods = filterStaticMethodSymbols(methods); + } + if (!methods.isEmpty() && null != node.getPrimaryGenericInvocationExpression().getGenericInvocationSuffix().getArguments()) { + //check if the methods fit and return the right returntype + ASTArguments args = node.getPrimaryGenericInvocationExpression().getGenericInvocationSuffix().getArguments(); + wholeResult = checkMethodsAndReplaceTypeVariables(methods, args, typeArgsList); + } + } + return wholeResult; + } + + protected List filterStaticMethodSymbols(List fittingMethods) { + return fittingMethods.stream().filter(m -> m instanceof MethodSymbol).filter(m -> ((MethodSymbol) m).isIsStatic()).collect(Collectors.toList()); + } + + protected List calculateTypeArguments(List extTypeArgumentList) { + //calculate each TypeArgument and return the results in a list + List typeArgsList = Lists.newArrayList(); + for (ASTExtTypeArgumentExt astExtTypeArgumentExt : extTypeArgumentList) { + astExtTypeArgumentExt.accept(getTraverser()); + if (getTypeCheckResult().isPresentResult()) { + typeArgsList.add(getTypeCheckResult().getResult()); + } else { + getTypeCheckResult().reset(); + typeArgsList.add(SymTypeExpressionFactory.createObscureType()); + Log.error("0xA0283 the type argument at source position " + astExtTypeArgumentExt.get_SourcePositionStart() + " cannot be calculated"); + } + } + return typeArgsList; + } + + protected SymTypeExpression checkMethodsAndReplaceTypeVariables(List methods, ASTArguments args, List typeArgsList) { + outer: + for (FunctionSymbol method : methods) { + if (method.getParameterList().size() != args.getExpressionList().size()) { + //wrong method + continue; + } + if (method.getTypeVariableList().size() != typeArgsList.size()) { + //wrong method + continue; + } + + List argsList = calculateArguments(args); + + //method has the correct name, the correct number of type arguments and the correct amount of parameters + //search for the right method by searching for the TypeVariables in the parameters and the return type of the methodsymbol + //and if there is anything wrong jump to the next method -> do not change the methodsymbol + //if everything is okay, return the return type of the method -> if this return type is a type variable return the typeArgument + //that replaces this type variable + Map transformMap = Maps.newHashMap(); + for (int j = 0; j < method.getTypeVariableList().size(); j++) { + transformMap.put(method.getTypeVariableList().get(j).getName(), typeArgsList.get(j)); + } + + for (int j = 0; j < method.getParameterList().size(); j++) { + VariableSymbol param = method.getParameterList().get(j); + if (param.getType().isTypeVariable()) { + if (!transformMap.containsKey(param.getType().print())) { + //there is a typevariable that cannot be resolved to the correct type -> wrong method + continue outer; + } + if (!argsList.get(j).deepEquals(transformMap.get(param.getType().print())) && !compatible(transformMap.get(param.getType().print()), argsList.get(j))) { + continue outer; + } + } else { + if (!argsList.get(j).deepEquals(param.getType()) && !compatible(param.getType(), argsList.get(j))) { + continue outer; + } + } + } + if (method.getType().isTypeVariable()) { + if (transformMap.containsKey(method.getType().print())) { + return transformMap.get(method.getType().print()); + } + } else { + return method.getType(); + } + } + //there cannot be found a fitting method + return SymTypeExpressionFactory.createObscureType(); + } + + protected List calculateArguments(ASTArguments args) { + List argList = Lists.newArrayList(); + for(int i = 0;ic() or super.c() plus Arguments in the brackets + SymTypeExpression wholeResult = SymTypeExpressionFactory.createObscureType(); + + if(!node.getGenericInvocationSuffix().isPresentSuperSuffix()){ + if(node.getGenericInvocationSuffix().isPresentName()){ + //case 1: method(Args) -> similar to GenericInvocationExpression + //can be accessed solely or after another expression -> check if lastResult is present + IBasicSymbolsScope testScope; + if(getTypeCheckResult().isPresentResult() && !getTypeCheckResult().getResult().isObscureType()){ + testScope = getTypeCheckResult().getResult().getTypeInfo().getSpannedScope(); + }else{ + testScope = getScope(node.getEnclosingScope()); + } + //resolve for fitting methods + List methods = testScope.resolveFunctionMany(node.getGenericInvocationSuffix().getName()); + if(!methods.isEmpty() && node.getGenericInvocationSuffix().isPresentArguments()){ + //check if the methods fit and return the right returntype + ASTArguments args = node.getGenericInvocationSuffix().getArguments(); + List typeArgsList = calculateTypeArguments(node.getExtTypeArgumentList()); + if(!typeArgsList.isEmpty()){ + getTypeCheckResult().unsetType(); + } + wholeResult = checkMethodsAndReplaceTypeVariables(methods,args,typeArgsList); + } + }else{ + //case 2: this(Args) -> similar to PrimaryThisExpression, use method checkMethodsAndReplaceTypeVariables + //can only be accessed solely -> there cannot be a lastresult + //search for the nearest enclosingscope spanned by a typesymbol + Optional typeSymbol = searchForTypeSymbolSpanningEnclosingScope(getScope(node.getEnclosingScope())); + if(typeSymbol.isPresent()) { + //get the constructors of the typesymbol + List methods = typeSymbol.get().getSpannedScope().resolveFunctionMany(typeSymbol.get().getName()); + if (!methods.isEmpty() && null != node.getGenericInvocationSuffix().getArguments()) { + //check if the constructors fit and return the right returntype + ASTArguments args = node.getGenericInvocationSuffix().getArguments(); + List typeArgsList = calculateTypeArguments(node.getExtTypeArgumentList()); + wholeResult = checkMethodsAndReplaceTypeVariables(methods, args, typeArgsList); + } + } + } + }else{ + ASTSuperSuffix superSuffix = node.getGenericInvocationSuffix().getSuperSuffix(); + if(!superSuffix.isPresentName()){ + //case 3: super(Args) -> find the constructor of the super class, use method checkMethodsAndReplaceTypeVariables + //search for the nearest enclosingscope spanned by a typesymbol + Optional subType = searchForTypeSymbolSpanningEnclosingScope(getScope(node.getEnclosingScope())); + //get the superclass of this typesymbol and search for its fitting constructor + if(subType.isPresent() &&subType.get().getSuperClassesOnly().size()==1){ + SymTypeExpression superClass = subType.get().getSuperClassesOnly().get(0); + List methods = superClass.getMethodList(superClass.getTypeInfo().getName(), false, AccessModifier.ALL_INCLUSION); + if(!methods.isEmpty() && superSuffix.isPresentArguments()){ + //check if the constructors fit and return the right returntype + ASTArguments args = superSuffix.getArguments(); + List typeArgsList = calculateTypeArguments(node.getExtTypeArgumentList()); + wholeResult = checkMethodsAndReplaceTypeVariables(methods,args,typeArgsList); + } + } + } + } + + storeResultOrLogError(wholeResult, node, "0xA0285"); + } + + protected Optional searchForTypeSymbolSpanningEnclosingScope(IBasicSymbolsScope scope) { + //search for the nearest type symbol in the enclosing scopes -> for this and super to get the + //current object + while(scope!=null){ + if(scope.isPresentSpanningSymbol()&&scope.getSpanningSymbol() instanceof OOTypeSymbol){ + return Optional.of((OOTypeSymbol)scope.getSpanningSymbol()); + } + scope = scope.getEnclosingScope(); + } + //no typesymbol found + return Optional.empty(); + } + + @Override + public void traverse(ASTAnonymousClass creator){ + SymTypeExpression extType; + SymTypeExpression wholeResult = SymTypeExpressionFactory.createObscureType(); + creator.getExtType().accept(getTraverser()); + if(getTypeCheckResult().isPresentResult()){ + extType = getTypeCheckResult().getResult(); + }else{ + getTypeCheckResult().reset(); + getTypeCheckResult().setResult(wholeResult); + logError("0xA1311",creator.getExtType().get_SourcePositionStart()); + return; + } + + if(!extType.isObscureType()) { + + if (!extType.isPrimitive()) { + //see if there is a constructor fitting for the arguments + List constructors = extType.getMethodList(extType.getTypeInfo().getName(), false, AccessModifier.ALL_INCLUSION); + if (!constructors.isEmpty()) { + if (testForCorrectArguments(constructors, creator.getArguments())) { + wholeResult = extType; + } + } else if (creator.getArguments().isEmptyExpressions()) { + //no constructor in this class -> default constructor without arguments, only possible if arguments in creator are empty + wholeResult = extType; + } + } + + getTypeCheckResult().reset(); + getTypeCheckResult().setResult(wholeResult); + if (wholeResult.isObscureType()) { + logError("0xA1312", creator.get_SourcePositionStart()); + } + } + } + + @Override + public void traverse(ASTArrayCreator creator){ + SymTypeExpression extTypeResult; + SymTypeExpression wholeResult = SymTypeExpressionFactory.createObscureType(); + + creator.getExtType().accept(getTraverser()); + if(getTypeCheckResult().isPresentResult()){ + extTypeResult = getTypeCheckResult().getResult(); + }else{ + getTypeCheckResult().reset(); + getTypeCheckResult().setResult(wholeResult); + logError("0xA0314", creator.getExtType().get_SourcePositionStart()); + return; + } + + if(!extTypeResult.isObscureType()) { + //the definition of the Arrays are based on the assumption that ExtType is not an array + if (!extTypeResult.isArrayType()) { + if (creator.getArrayDimensionSpecifier() instanceof ASTArrayDimensionByExpression) { + ASTArrayDimensionByExpression arrayInitializer = (ASTArrayDimensionByExpression) creator.getArrayDimensionSpecifier(); + int dim = arrayInitializer.getDimList().size() + arrayInitializer.getExpressionList().size(); + //teste dass alle Expressions integer-zahl sind + for (ASTExpression expr : arrayInitializer.getExpressionList()) { + expr.accept(getTraverser()); + if (getTypeCheckResult().isPresentResult()) { + SymTypeExpression result = getTypeCheckResult().getResult(); + if (result.isPrimitive()) { + if (!((SymTypePrimitive) result).isIntegralType()) { + getTypeCheckResult().reset(); + getTypeCheckResult().setResult(wholeResult); + logError("0xA0315", expr.get_SourcePositionStart()); + return; + } + } else { + getTypeCheckResult().reset(); + getTypeCheckResult().setResult(wholeResult); + logError("0xA0316", expr.get_SourcePositionStart()); + return; + } + } else { + getTypeCheckResult().reset(); + getTypeCheckResult().setResult(wholeResult); + logError("0xA0317", expr.get_SourcePositionStart()); + return; + } + } + wholeResult = SymTypeExpressionFactory.createTypeArray(extTypeResult.getTypeInfo(), dim, extTypeResult); + } + } + + getTypeCheckResult().reset(); + getTypeCheckResult().setResult(wholeResult); + if (wholeResult.isObscureType()) { + logError("0xA0318", creator.get_SourcePositionStart()); + } + } + } + + protected List calculateCorrectArguments(ASTArguments args) { + List argList = Lists.newArrayList(); + for(int i = 0;i constructors, ASTArguments arguments) { + List symTypeOfArguments = calculateCorrectArguments(arguments); + outer: for(FunctionSymbol constructor: constructors){ + if(constructor.getParameterList().size() == symTypeOfArguments.size()){ + //get the types of the constructor arguments + List constructorArguments = constructor.getParameterList().stream().map(VariableSymbol::getType).collect(Collectors.toList()); + for(int i = 0;i return true + return true; + } + } + return false; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/DeriveSymTypeOfLambdaExpressions.java b/monticore-grammar/src/main/java/de/monticore/types/check/DeriveSymTypeOfLambdaExpressions.java new file mode 100644 index 0000000000..e2cb920ec2 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/DeriveSymTypeOfLambdaExpressions.java @@ -0,0 +1,83 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.expressions.lambdaexpressions._ast.ASTLambdaExpression; +import de.monticore.expressions.lambdaexpressions._ast.ASTLambdaExpressionBody; +import de.monticore.expressions.lambdaexpressions._ast.ASTLambdaParameter; +import de.monticore.expressions.lambdaexpressions._visitor.LambdaExpressionsHandler; +import de.monticore.expressions.lambdaexpressions._visitor.LambdaExpressionsTraverser; +import de.monticore.expressions.lambdaexpressions._visitor.LambdaExpressionsVisitor2; +import de.se_rwth.commons.logging.Log; + +import java.util.LinkedList; +import java.util.List; + +/** + * This Visitor can calculate a SymTypeExpression (type) for the expressions in LambdaExpressions + * It can be combined with other expressions in your language by creating a DelegatorVisitor + */ +public class DeriveSymTypeOfLambdaExpressions extends AbstractDeriveFromExpression + implements LambdaExpressionsVisitor2, LambdaExpressionsHandler { + + protected LambdaExpressionsTraverser traverser; + + protected AbstractSynthesize synthesize; + + @Override + public void setTraverser(LambdaExpressionsTraverser traverser) { + this.traverser = traverser; + } + + @Override + public LambdaExpressionsTraverser getTraverser() { + return traverser; + } + + public ISynthesize getSynthesize() { + return synthesize; + } + + public void setSynthesize(AbstractSynthesize synthesize) { + this.synthesize = synthesize; + } + + @Override + public void traverse(ASTLambdaExpression exp) { + getTypeCheckResult().reset(); + exp.getLambdaBody().accept(getTraverser()); + SymTypeExpression returnType = getTypeCheckResult().getResult(); + + List parameters = new LinkedList<>(); + for (ASTLambdaParameter parameter : exp.getLambdaParameters().getLambdaParameterList()) { + parameters.add(calculateTypeOfLambdaParameter(parameter)); + } + + if(!returnType.isObscureType() && checkNotObscure(parameters)) { + SymTypeExpression wholeResult = + SymTypeExpressionFactory.createFunction(returnType, parameters); + storeResultOrLogError(wholeResult, exp, "0xA0470"); + } else { + getTypeCheckResult().reset(); + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + } + } + + @Override + public void traverse(ASTLambdaExpressionBody node) { + node.getExpression().accept(getTraverser()); + } + + public SymTypeExpression calculateTypeOfLambdaParameter(ASTLambdaParameter parameter) { + if (parameter.isPresentMCType()) { + synthesize.getTypeCheckResult().reset(); + parameter.getMCType().accept(synthesize.getTraverser()); + if (synthesize.getTypeCheckResult().isPresentResult()) { + return synthesize.getTypeCheckResult().getResult(); + } + } + Log.error("0xBC373 unable to calculate type of lambda parameter", + parameter.get_SourcePositionStart(), parameter.get_SourcePositionEnd()); + return SymTypeExpressionFactory.createObscureType(); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/DeriveSymTypeOfLiterals.java b/monticore-grammar/src/main/java/de/monticore/types/check/DeriveSymTypeOfLiterals.java new file mode 100644 index 0000000000..43b553dedc --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/DeriveSymTypeOfLiterals.java @@ -0,0 +1,60 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.literals.mcliteralsbasis._ast.ASTLiteral; +import de.monticore.literals.mcliteralsbasis._symboltable.IMCLiteralsBasisScope; +import de.monticore.literals.mcliteralsbasis._visitor.MCLiteralsBasisVisitor2; +import de.monticore.symbols.basicsymbols._symboltable.IBasicSymbolsScope; +import de.monticore.symbols.oosymbols._symboltable.IOOSymbolsScope; +import de.se_rwth.commons.logging.Log; + +/** + * Visitor for Derivation of SymType from Literals + * (Function 2b) + * i.e. for + * literals/MCLiteralsBasis.mc4 + */ +public class DeriveSymTypeOfLiterals implements MCLiteralsBasisVisitor2 { + + public IBasicSymbolsScope getScope (IMCLiteralsBasisScope mcLiteralsBasisScope){ + // is accepted only here, decided on 07.04.2020 + if(!(mcLiteralsBasisScope instanceof IBasicSymbolsScope)){ + Log.error("0xA1309 the enclosing scope of the literal does not implement the interface IBasicSymbolsScope"); + } + // is accepted only here, decided on 07.04.2020 + return (IBasicSymbolsScope) mcLiteralsBasisScope; + } + + + /** + * Storage in the Visitor: result of the last endVisit. + * This attribute is synthesized upward. + */ + protected TypeCheckResult typeCheckResult; + + public TypeCheckResult getTypeCheckResult() { + return typeCheckResult; + } + + public void init() { + typeCheckResult = new TypeCheckResult(); + } + + + // ---------------------------------------------------------- Visting Methods + + // This section is deliberately empty: + // MCLiteralsBasis has no implementing Nonterminals + + @Override + public void visit(ASTLiteral lit){ + // This general method is only called, if no specific exists,: + // Should not happen. + Log.error("0xED671 Internal Error: No Type for Literal " + lit + + " Probably TypeCheck mis-configured."); + } + + public void setTypeCheckResult(TypeCheckResult result) { + this.typeCheckResult = result; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/DeriveSymTypeOfMCCommonLiterals.java b/monticore-grammar/src/main/java/de/monticore/types/check/DeriveSymTypeOfMCCommonLiterals.java new file mode 100644 index 0000000000..98f76742b8 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/DeriveSymTypeOfMCCommonLiterals.java @@ -0,0 +1,121 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.literals.mccommonliterals._ast.*; +import de.monticore.literals.mccommonliterals._visitor.MCCommonLiteralsVisitor2; +import de.monticore.literals.mcliteralsbasis._ast.ASTLiteral; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.se_rwth.commons.SourcePosition; +import de.se_rwth.commons.logging.Log; + +import java.util.Optional; + +/** + * Visitor for Derivation of SymType from Literals + * (Function 2b) + * i.e. for + * literals/MCLiteralsBasis.mc4 + */ +public class DeriveSymTypeOfMCCommonLiterals extends DeriveSymTypeOfLiterals implements MCCommonLiteralsVisitor2 { + + protected TypeCheckResult typeCheckResult; + + public void setTypeCheckResult(TypeCheckResult result) { + this.typeCheckResult = result; + } + + public TypeCheckResult getTypeCheckResult() { + return typeCheckResult; + } + + protected SymTypeExpression getSymType(String type, SourcePosition pos) { + Optional primitive = BasicSymbolsMill.globalScope().resolveType(type); + if(primitive.isPresent()){ + return SymTypeExpressionFactory.createPrimitive(primitive.get()); + }else{ + Log.error("0xA0207 The primitive type " + type + " could not be resolved." + + "Did you add primitive types to your language?", pos); + return SymTypeExpressionFactory.createObscureType(); + } + } + + @Override + public void visit(ASTNatLiteral lit){ + derivePrimitive(lit, BasicSymbolsMill.INT); + } + + @Override + public void visit(ASTCharLiteral lit){ + derivePrimitive((ASTLiteral) lit, BasicSymbolsMill.CHAR); + } + + @Override + public void visit(ASTBooleanLiteral lit){ + derivePrimitive((ASTLiteral) lit, BasicSymbolsMill.BOOLEAN); + } + + @Override + public void visit(ASTBasicDoubleLiteral lit){ + derivePrimitive(lit, BasicSymbolsMill.DOUBLE); + } + + @Override + public void visit(ASTBasicFloatLiteral lit){ + derivePrimitive(lit, BasicSymbolsMill.FLOAT); + } + + @Override + public void visit(ASTBasicLongLiteral lit){ + derivePrimitive(lit, BasicSymbolsMill.LONG); + } + + @Override + public void visit(ASTStringLiteral lit){ + Optional stringType = getScope(lit.getEnclosingScope()).resolveType("String"); + if(stringType.isPresent()){ + getTypeCheckResult().setResult(SymTypeExpressionFactory.createTypeObject(stringType.get())); + } else { + getTypeCheckResult().reset(); + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + Log.error("0xA0238 The type String could not be resolved."); + } + } + + @Override + public void visit(ASTSignedNatLiteral lit) { + derivePrimitive(lit, BasicSymbolsMill.INT); + } + + @Override + public void visit(ASTSignedBasicDoubleLiteral lit) { + derivePrimitive(lit, BasicSymbolsMill.DOUBLE); + } + + @Override + public void visit(ASTSignedBasicFloatLiteral lit) { + derivePrimitive(lit, BasicSymbolsMill.FLOAT); + } + + @Override + public void visit(ASTSignedBasicLongLiteral lit) { + derivePrimitive(lit, BasicSymbolsMill.LONG); + } + + protected void derivePrimitive(ASTLiteral lit, String primitive){ + getTypeCheckResult().setResult(getSymType(primitive, lit.get_SourcePositionStart())); + } + + protected void derivePrimitive(ASTSignedLiteral lit, String primitive) { + getTypeCheckResult().setResult(getSymType(primitive, lit.get_SourcePositionStart())); + } + + /** + * Literal "null" gets marked with implicit SymType _null + */ + @Override + public void visit(ASTNullLiteral lit){ + getTypeCheckResult().setResult(new SymTypeOfNull()); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/DeriveSymTypeOfMCJavaLiterals.java b/monticore-grammar/src/main/java/de/monticore/types/check/DeriveSymTypeOfMCJavaLiterals.java new file mode 100644 index 0000000000..6da2fb2a85 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/DeriveSymTypeOfMCJavaLiterals.java @@ -0,0 +1,43 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.literals.mcjavaliterals._ast.ASTDoubleLiteral; +import de.monticore.literals.mcjavaliterals._ast.ASTFloatLiteral; +import de.monticore.literals.mcjavaliterals._ast.ASTIntLiteral; +import de.monticore.literals.mcjavaliterals._ast.ASTLongLiteral; +import de.monticore.literals.mcjavaliterals._visitor.MCJavaLiteralsVisitor2; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; + +public class DeriveSymTypeOfMCJavaLiterals extends DeriveSymTypeOfMCCommonLiterals implements MCJavaLiteralsVisitor2 { + + protected TypeCheckResult typeCheckResult; + + public void setTypeCheckResult(TypeCheckResult typeCheckResult) { + this.typeCheckResult = typeCheckResult; + } + + public TypeCheckResult getTypeCheckResult() { + return typeCheckResult; + } + + @Override + public void visit(ASTIntLiteral lit){ + derivePrimitive(lit, BasicSymbolsMill.INT); + } + + @Override + public void visit(ASTLongLiteral lit){ + derivePrimitive(lit, BasicSymbolsMill.LONG); + } + + @Override + public void visit(ASTFloatLiteral lit){ + derivePrimitive(lit, BasicSymbolsMill.FLOAT); + } + + @Override + public void visit(ASTDoubleLiteral lit){ + derivePrimitive(lit, BasicSymbolsMill.DOUBLE); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/FullCompKindExprDeSer.java b/monticore-grammar/src/main/java/de/monticore/types/check/FullCompKindExprDeSer.java new file mode 100644 index 0000000000..5ee49d2a53 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/FullCompKindExprDeSer.java @@ -0,0 +1,73 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import com.google.common.base.Preconditions; +import de.monticore.symboltable.serialization.JsonDeSers; +import de.monticore.symboltable.serialization.JsonParser; +import de.monticore.symboltable.serialization.json.JsonElement; +import de.monticore.symboltable.serialization.json.JsonObject; +import org.checkerframework.checker.nullness.qual.NonNull; + +/** + * Serializes and deserializes {@link CompKindExpression}s from and to their Json encoding. + * Implementations of this interface should compose different {@link CompKindExprDeSer}s and delegate to them + * for the real (de-)serialization. + */ +public interface FullCompKindExprDeSer { + + /** + * @param toSerialize {@link CompKindExpression} to serialize as Json + * @return Json encoded version of the {@link CompKindExpression}. + */ + String serializeAsJson(@NonNull CompKindExpression toSerialize); + + /** + * Deserialize a {@link CompKindExpression} from its Json encoding. + */ + default CompKindExpression deserializeFromJsonString(@NonNull String serializedInJson) { + JsonObject compExpr = JsonParser.parseJsonObject(serializedInJson); + return deserialize(compExpr); + } + + /** + * Deserialize a {@link CompKindExpression} from its Json representation. + */ + CompKindExpression deserialize(@NonNull JsonElement serialized); + + default IllegalStateException missingDeSerException(@NonNull JsonObject unloadableElement) { + Preconditions.checkNotNull(unloadableElement); + + String typeExprKind = JsonDeSers.getKind(unloadableElement); + String deSerAggregatorName = this.getClass().getName(); + + return new IllegalStateException( + String.format("No DeSer available for CompKindExpressionKind '%s' in '%s'. Therefore, the " + + "deserialization of '%s' is impossible.", + typeExprKind, deSerAggregatorName, unloadableElement + )); + } + + default IllegalStateException missingDeSerException(@NonNull CompKindExpression unsaveableElement) { + Preconditions.checkNotNull(unsaveableElement); + + String typeExpressionKind = unsaveableElement.getClass().getName(); + String deSerAggregatorName = this.getClass().getName(); + + return new IllegalStateException( + String.format("No DeSer available for CompKindExpressionKind '%s' in '%s'. Therefore, the " + + "serialization of '%s' is impossible.", + typeExpressionKind, deSerAggregatorName, unsaveableElement.printName() + )); + } + + default IllegalStateException missingDeSerException(@NonNull String CompKindExpressionKind) { + Preconditions.checkNotNull(CompKindExpressionKind); + + String deSerAggregatorName = this.getClass().getName(); + + return new IllegalStateException( + String.format("No DeSer available for CompKindExpressionKind '%s' in '%s'.", + CompKindExpressionKind, deSerAggregatorName + )); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/FullSynthesizeFromMCArrayTypes.java b/monticore-grammar/src/main/java/de/monticore/types/check/FullSynthesizeFromMCArrayTypes.java new file mode 100644 index 0000000000..ac4fdb5ab1 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/FullSynthesizeFromMCArrayTypes.java @@ -0,0 +1,34 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.types.mcarraytypes.MCArrayTypesMill; +import de.monticore.types.mcarraytypes._visitor.MCArrayTypesTraverser; +import de.monticore.types.mcbasictypes._ast.ASTMCQualifiedName; +import de.monticore.types.mcbasictypes._ast.ASTMCReturnType; +import de.monticore.types.mcbasictypes._ast.ASTMCType; + +import java.util.Optional; + +public class FullSynthesizeFromMCArrayTypes extends AbstractSynthesize { + + public FullSynthesizeFromMCArrayTypes(){ + this(MCArrayTypesMill.traverser()); + } + + public FullSynthesizeFromMCArrayTypes(MCArrayTypesTraverser traverser){ + super(traverser); + init(traverser); + } + + public void init(MCArrayTypesTraverser traverser) { + SynthesizeSymTypeFromMCArrayTypes synFromArray = new SynthesizeSymTypeFromMCArrayTypes(); + synFromArray.setTypeCheckResult(typeCheckResult); + SynthesizeSymTypeFromMCBasicTypes synFromBasic = new SynthesizeSymTypeFromMCBasicTypes(); + synFromBasic.setTypeCheckResult(typeCheckResult); + + traverser.add4MCArrayTypes(synFromArray); + traverser.setMCArrayTypesHandler(synFromArray); + traverser.add4MCBasicTypes(synFromBasic); + traverser.setMCBasicTypesHandler(synFromBasic); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/FullSynthesizeFromMCBasicTypes.java b/monticore-grammar/src/main/java/de/monticore/types/check/FullSynthesizeFromMCBasicTypes.java new file mode 100644 index 0000000000..5fcc2bab4c --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/FullSynthesizeFromMCBasicTypes.java @@ -0,0 +1,30 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.types.mcbasictypes.MCBasicTypesMill; +import de.monticore.types.mcbasictypes._ast.ASTMCQualifiedName; +import de.monticore.types.mcbasictypes._ast.ASTMCReturnType; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.mcbasictypes._visitor.MCBasicTypesTraverser; + +import java.util.Optional; + +public class FullSynthesizeFromMCBasicTypes extends AbstractSynthesize { + + public FullSynthesizeFromMCBasicTypes(){ + this(MCBasicTypesMill.traverser()); + } + + public FullSynthesizeFromMCBasicTypes(MCBasicTypesTraverser traverser){ + super(traverser); + init(traverser); + } + + public void init(MCBasicTypesTraverser traverser) { + SynthesizeSymTypeFromMCBasicTypes synFromBasic = new SynthesizeSymTypeFromMCBasicTypes(); + synFromBasic.setTypeCheckResult(typeCheckResult); + + traverser.add4MCBasicTypes(synFromBasic); + traverser.setMCBasicTypesHandler(synFromBasic); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/FullSynthesizeFromMCCollectionTypes.java b/monticore-grammar/src/main/java/de/monticore/types/check/FullSynthesizeFromMCCollectionTypes.java new file mode 100644 index 0000000000..c463165537 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/FullSynthesizeFromMCCollectionTypes.java @@ -0,0 +1,31 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + + +import de.monticore.types.mccollectiontypes.MCCollectionTypesMill; +import de.monticore.types.mccollectiontypes._visitor.MCCollectionTypesTraverser; + +public class FullSynthesizeFromMCCollectionTypes extends AbstractSynthesize { + + public FullSynthesizeFromMCCollectionTypes(){ + this(MCCollectionTypesMill.traverser()); + } + + public FullSynthesizeFromMCCollectionTypes(MCCollectionTypesTraverser traverser){ + super(traverser); + init(traverser); + } + + public void init(MCCollectionTypesTraverser traverser) { + SynthesizeSymTypeFromMCCollectionTypes synFromCollection = new SynthesizeSymTypeFromMCCollectionTypes(); + synFromCollection.setTypeCheckResult(typeCheckResult); + SynthesizeSymTypeFromMCBasicTypes synFromBasic = new SynthesizeSymTypeFromMCBasicTypes(); + synFromBasic.setTypeCheckResult(typeCheckResult); + + traverser.add4MCCollectionTypes(synFromCollection); + traverser.setMCCollectionTypesHandler(synFromCollection); + traverser.add4MCBasicTypes(synFromBasic); + traverser.setMCBasicTypesHandler(synFromBasic); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/FullSynthesizeFromMCFullGenericTypes.java b/monticore-grammar/src/main/java/de/monticore/types/check/FullSynthesizeFromMCFullGenericTypes.java new file mode 100644 index 0000000000..474cc2d436 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/FullSynthesizeFromMCFullGenericTypes.java @@ -0,0 +1,41 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.types.mcfullgenerictypes.MCFullGenericTypesMill; +import de.monticore.types.mcfullgenerictypes._visitor.MCFullGenericTypesTraverser; + +public class FullSynthesizeFromMCFullGenericTypes extends AbstractSynthesize { + + public FullSynthesizeFromMCFullGenericTypes(){ + this(MCFullGenericTypesMill.traverser()); + } + + public FullSynthesizeFromMCFullGenericTypes(MCFullGenericTypesTraverser traverser){ + super(traverser); + init(traverser); + } + + public void init(MCFullGenericTypesTraverser traverser) { + SynthesizeSymTypeFromMCFullGenericTypes synFromFull = new SynthesizeSymTypeFromMCFullGenericTypes(); + synFromFull.setTypeCheckResult(getTypeCheckResult()); + SynthesizeSymTypeFromMCSimpleGenericTypes synFromSimple = new SynthesizeSymTypeFromMCSimpleGenericTypes(); + synFromSimple.setTypeCheckResult(getTypeCheckResult()); + SynthesizeSymTypeFromMCCollectionTypes synFromCollection = new SynthesizeSymTypeFromMCCollectionTypes(); + synFromCollection.setTypeCheckResult(getTypeCheckResult()); + SynthesizeSymTypeFromMCBasicTypes synFromBasic = new SynthesizeSymTypeFromMCBasicTypes(); + synFromBasic.setTypeCheckResult(getTypeCheckResult()); + + traverser.add4MCFullGenericTypes(synFromFull); + traverser.setMCFullGenericTypesHandler(synFromFull); + traverser.add4MCSimpleGenericTypes(synFromSimple); + traverser.setMCSimpleGenericTypesHandler(synFromSimple); + traverser.add4MCCollectionTypes(synFromCollection); + traverser.setMCCollectionTypesHandler(synFromCollection); + traverser.add4MCBasicTypes(synFromBasic); + traverser.setMCBasicTypesHandler(synFromBasic); + } + + public void setTraverser(MCFullGenericTypesTraverser traverser) { + this.traverser = traverser; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/FullSynthesizeFromMCFunctionTypes.java b/monticore-grammar/src/main/java/de/monticore/types/check/FullSynthesizeFromMCFunctionTypes.java new file mode 100644 index 0000000000..e5449fce72 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/FullSynthesizeFromMCFunctionTypes.java @@ -0,0 +1,29 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.types.mcfunctiontypes.MCFunctionTypesMill; +import de.monticore.types.mcfunctiontypes._visitor.MCFunctionTypesTraverser; + +public class FullSynthesizeFromMCFunctionTypes extends AbstractSynthesize { + + public FullSynthesizeFromMCFunctionTypes() { + this(MCFunctionTypesMill.traverser()); + } + + public FullSynthesizeFromMCFunctionTypes(MCFunctionTypesTraverser traverser) { + super(traverser); + init(traverser); + } + + public void init(MCFunctionTypesTraverser traverser) { + SynthesizeSymTypeFromMCFunctionTypes synFromFunction = new SynthesizeSymTypeFromMCFunctionTypes(); + synFromFunction.setTypeCheckResult(typeCheckResult); + SynthesizeSymTypeFromMCBasicTypes synFromBasic = new SynthesizeSymTypeFromMCBasicTypes(); + synFromBasic.setTypeCheckResult(typeCheckResult); + + traverser.add4MCFunctionTypes(synFromFunction); + traverser.setMCFunctionTypesHandler(synFromFunction); + traverser.add4MCBasicTypes(synFromBasic); + traverser.setMCBasicTypesHandler(synFromBasic); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/FullSynthesizeFromMCSimpleGenericTypes.java b/monticore-grammar/src/main/java/de/monticore/types/check/FullSynthesizeFromMCSimpleGenericTypes.java new file mode 100644 index 0000000000..bfbaa6b6b5 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/FullSynthesizeFromMCSimpleGenericTypes.java @@ -0,0 +1,38 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.types.mcbasictypes._ast.ASTMCQualifiedName; +import de.monticore.types.mcbasictypes._ast.ASTMCReturnType; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.mcsimplegenerictypes.MCSimpleGenericTypesMill; +import de.monticore.types.mcsimplegenerictypes._visitor.MCSimpleGenericTypesTraverser; + +import java.util.Optional; + +public class FullSynthesizeFromMCSimpleGenericTypes extends AbstractSynthesize { + + public FullSynthesizeFromMCSimpleGenericTypes(){ + this(MCSimpleGenericTypesMill.traverser()); + } + + public FullSynthesizeFromMCSimpleGenericTypes(MCSimpleGenericTypesTraverser traverser){ + super(traverser); + init(traverser); + } + + public void init(MCSimpleGenericTypesTraverser traverser) { + SynthesizeSymTypeFromMCSimpleGenericTypes synFromSimple = new SynthesizeSymTypeFromMCSimpleGenericTypes(); + synFromSimple.setTypeCheckResult(typeCheckResult); + SynthesizeSymTypeFromMCCollectionTypes synFromCollection = new SynthesizeSymTypeFromMCCollectionTypes(); + synFromCollection.setTypeCheckResult(typeCheckResult); + SynthesizeSymTypeFromMCBasicTypes synFromBasic = new SynthesizeSymTypeFromMCBasicTypes(); + synFromBasic.setTypeCheckResult(typeCheckResult); + + traverser.add4MCSimpleGenericTypes(synFromSimple); + traverser.setMCSimpleGenericTypesHandler(synFromSimple); + traverser.add4MCCollectionTypes(synFromCollection); + traverser.setMCCollectionTypesHandler(synFromCollection); + traverser.add4MCBasicTypes(synFromBasic); + traverser.setMCBasicTypesHandler(synFromBasic); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/IDerive.java b/monticore-grammar/src/main/java/de/monticore/types/check/IDerive.java new file mode 100644 index 0000000000..779c154dff --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/IDerive.java @@ -0,0 +1,16 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.literals.mcliteralsbasis._ast.ASTLiteral; + +/** + * A common interface that can be used to derive SymTypeExpressions from expressions and literals + */ +public interface IDerive { + + TypeCheckResult deriveType(ASTExpression expr); + + TypeCheckResult deriveType(ASTLiteral lit); + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/ISynthesize.java b/monticore-grammar/src/main/java/de/monticore/types/check/ISynthesize.java new file mode 100644 index 0000000000..862d877f9c --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/ISynthesize.java @@ -0,0 +1,19 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.types.mcbasictypes._ast.ASTMCQualifiedName; +import de.monticore.types.mcbasictypes._ast.ASTMCReturnType; +import de.monticore.types.mcbasictypes._ast.ASTMCType; + +/** + * A common interface that can be used to synthesize SymTypeExpressions from MCTypes + */ +public interface ISynthesize { + + TypeCheckResult synthesizeType(ASTMCType type); + + TypeCheckResult synthesizeType(ASTMCReturnType type); + + TypeCheckResult synthesizeType(ASTMCQualifiedName qName); + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/ISynthesizeComponent.java b/monticore-grammar/src/main/java/de/monticore/types/check/ISynthesizeComponent.java new file mode 100644 index 0000000000..79aa56e0a9 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/ISynthesizeComponent.java @@ -0,0 +1,18 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.Optional; + +/** + * Interface for synthesizing a {@link CompKindExpression} from an {@link ASTMCType}. + */ +public interface ISynthesizeComponent { + + /** + * Synthesizes a {@link CompKindExpression} from a {@link ASTMCType} + */ + Optional synthesize(@NonNull ASTMCType mcType); +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/ITypeRelations.java b/monticore-grammar/src/main/java/de/monticore/types/check/ITypeRelations.java new file mode 100644 index 0000000000..110340f597 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/ITypeRelations.java @@ -0,0 +1,82 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +/** + * This class is intended to provide typeChecking functionality + * with regard to relations of types + */ +public interface ITypeRelations { + + /** + * Function 3: + * Given two SymTypeExpressions super, sub: + * This function answers, whether the right type is a subtype of the left type in an assignment. + * (This allows to store/use values of type "sub" at all positions of type "super". + * Compatibility examples: + * compatible("int", "long") (in all directions) + * compatible("long", "int") (in all directions) + * compatible("double", "float") (in all directions) + * compatible("Person", "Student") (uni-directional) + *

+ * Incompatible: + * !compatible("double", "int") (in all directions) + *

+ * The concrete Typechecker has to decide on further issues, like + * !compatible("List", "List") + * where e.g. Java and OCL/P differ in their answers + * + * @param left Super-Type + * @param right Sub-Type (assignment-compatible to supertype?) + *

+ * TODO: Probably needs to be extended for free type-variable assignments + * (because it may be that they get unified over time: e.g. Map> and Map + * are compatible, by refining the assignments a-> long, b->List + */ + boolean compatible(SymTypeExpression left, SymTypeExpression right); + + /** + * determines if one SymTypeExpression is a subtype of another SymTypeExpression + * + * @param subType the SymTypeExpression that could be a subtype of the other SymTypeExpression + * @param superType the SymTypeExpression that could be a supertype of the other SymTypeExpression + */ + boolean isSubtypeOf(SymTypeExpression subType, SymTypeExpression superType); + + /** + * calculate the minimum inheritance distance from the specific type to the general type + * e.g. C extends B extends A => object of type C has distance of 2 to object of type A + * object of type B has distance of 1 to object of type A + * object of type A has distance of 0 to object of type A + * + * @param specific the specific type + * @param general the general type + * @return 0 if they are the same type, else their minimum inheritance distance + */ + int calculateInheritanceDistance(SymTypeExpression specific, SymTypeExpression general); + + int calculateInheritanceDistance(SymTypePrimitive specific, SymTypePrimitive general); + + boolean isBoolean(SymTypeExpression type); + + boolean isInt(SymTypeExpression type); + + boolean isDouble(SymTypeExpression type); + + boolean isFloat(SymTypeExpression type); + + boolean isLong(SymTypeExpression type); + + boolean isChar(SymTypeExpression type); + + boolean isShort(SymTypeExpression type); + + boolean isByte(SymTypeExpression type); + + boolean isVoid(SymTypeExpression type); + + boolean isString(SymTypeExpression type); + + boolean isNumericType(SymTypeExpression type); + + boolean isIntegralType(SymTypeExpression type); +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/KindOfComponent.java b/monticore-grammar/src/main/java/de/monticore/types/check/KindOfComponent.java new file mode 100644 index 0000000000..9ca638fa18 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/KindOfComponent.java @@ -0,0 +1,59 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import com.google.common.base.Preconditions; +import de.monticore.symbols.basicsymbols._symboltable.VariableSymbolTOP; +import de.monticore.symbols.compsymbols._symboltable.ComponentSymbol; +import de.monticore.symbols.compsymbols._symboltable.PortSymbol; +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.List; +import java.util.Optional; + +/** + * Represents a component expression that is solely defined by the component symbol. + */ +public class KindOfComponent extends CompKindExpression { + + public KindOfComponent(@NonNull ComponentSymbol component) { + super(component); + } + + @Override + public String printName() { + return this.getTypeInfo().getName(); + } + + @Override + public String printFullName() { + return this.getTypeInfo().getFullName(); + } + + @Override + public List getSuperComponents() { + return this.getTypeInfo().getSuperComponentsList(); + } + + @Override + public Optional getTypeOfPort(@NonNull String portName) { + Preconditions.checkNotNull(portName); + return this.getTypeInfo().getPort(portName, true).map(PortSymbol::getType); + } + + @Override + public Optional getTypeOfParameter(@NonNull String name) { + Preconditions.checkNotNull(name); + return this.getTypeInfo().getParameter(name).map(VariableSymbolTOP::getType); + } + + @Override + public KindOfComponent deepClone(@NonNull ComponentSymbol component) { + return new KindOfComponent(component); + } + + @Override + public boolean deepEquals(@NonNull CompKindExpression component) { + Preconditions.checkNotNull(component); + return this.getTypeInfo().equals(component.getTypeInfo()); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/KindOfComponentDeSer.java b/monticore-grammar/src/main/java/de/monticore/types/check/KindOfComponentDeSer.java new file mode 100644 index 0000000000..a690ed6870 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/KindOfComponentDeSer.java @@ -0,0 +1,53 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import com.google.common.base.Preconditions; +import de.monticore.symbols.compsymbols.CompSymbolsMill; +import de.monticore.symbols.compsymbols._symboltable.ComponentSymbolSurrogate; +import de.monticore.symboltable.serialization.JsonDeSers; +import de.monticore.symboltable.serialization.JsonPrinter; +import de.monticore.symboltable.serialization.json.JsonObject; +import org.checkerframework.checker.nullness.qual.NonNull; + +/** + * (De-)serializes {@link KindOfComponent}s. + */ +public class KindOfComponentDeSer implements CompKindExprDeSer { + + public static final String SERIALIZED_KIND = "de.monticore.types.check.KindOfComponent"; + public static final String COMP_NAME = "componentName"; + + @Override + public String serializeAsJson(@NonNull KindOfComponent toSerialize) { + Preconditions.checkNotNull(toSerialize); + + JsonPrinter printer = new JsonPrinter(); + + printer.beginObject(); + printer.member(JsonDeSers.KIND, SERIALIZED_KIND); + printer.member(COMP_NAME, toSerialize.getTypeInfo().getFullName()); + printer.endObject(); + + return printer.getContent(); + } + + @Override + public KindOfComponent deserialize(@NonNull JsonObject serialized) { + Preconditions.checkNotNull(serialized); + Preconditions.checkArgument( + JsonDeSers.getKind(serialized).equals(SERIALIZED_KIND), + "Kind must be %s, but is %s.", + SERIALIZED_KIND, JsonDeSers.getKind(serialized) + ); + + String compTypeName = serialized.getMember(COMP_NAME).getAsJsonString().getValue(); + + ComponentSymbolSurrogate compType = CompSymbolsMill + .componentSymbolSurrogateBuilder() + .setName(compTypeName) + .setEnclosingScope(CompSymbolsMill.globalScope()) + .build(); + + return new KindOfComponent(compType); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/KindOfGenericComponent.java b/monticore-grammar/src/main/java/de/monticore/types/check/KindOfGenericComponent.java new file mode 100644 index 0000000000..bfde3b8ea6 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/KindOfGenericComponent.java @@ -0,0 +1,201 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import de.monticore.symbols.basicsymbols._symboltable.TypeVarSymbol; +import de.monticore.symbols.basicsymbols._symboltable.VariableSymbolTOP; +import de.monticore.symbols.compsymbols._symboltable.ComponentSymbol; +import de.monticore.symbols.compsymbols._symboltable.PortSymbol; +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * Represents generic component with filled type parameters. E.g., a {@code TypeExprOfGenericComponent} can + * represent generic component usages, such as, {@code MyComp} and {@code OtherComp>}. + */ +public class KindOfGenericComponent extends CompKindExpression { + + protected final ImmutableList typeArguments; + + private ImmutableMap typeVarBindingsAsMap; + + public KindOfGenericComponent(@NonNull ComponentSymbol component, + @NonNull List typeArguments) { + super(component); + + this.typeArguments = ImmutableList.copyOf(Preconditions.checkNotNull(typeArguments)); + } + + public ImmutableMap getTypeVarBindings() { + if (typeVarBindingsAsMap == null) { + this.typeVarBindingsAsMap = calcTypeVarBindingsAsMap(); + } + + return this.typeVarBindingsAsMap; + } + + private ImmutableMap calcTypeVarBindingsAsMap() { + ImmutableMap.Builder typeVarBindingBuilder = ImmutableMap.builder(); + + for (int i = 0; i < typeArguments.size(); i++) { + // We deal with the wrong number of parameters through cocos + List typeParams = this.getTypeInfo().getTypeParameters(); + if (i < typeParams.size() && typeArguments.get(i) != null) { + TypeVarSymbol typeParam = this.getTypeInfo().getTypeParameters().get(i); + typeVarBindingBuilder.put(typeParam, typeArguments.get(i)); + } + } + + return typeVarBindingBuilder.build(); + } + + @Override + public String printName() { + StringBuilder builder = new StringBuilder(this.getTypeInfo().getName()).append('<'); + for (int i = 0; i < this.getTypeBindingsAsList().size(); i++) { + builder.append(this.getTypeBindingsAsList().get(i).print()); + if (i < this.getTypeBindingsAsList().size() - 1) { + builder.append(','); + } + } + return builder.append('>').toString(); + } + + @Override + public String printFullName() { + StringBuilder builder = new StringBuilder(this.getTypeInfo().getFullName()).append('<'); + for (int i = 0; i < this.getTypeBindingsAsList().size(); i++) { + builder.append(this.getTypeBindingsAsList().get(i).printFullName()); + if (i < this.getTypeBindingsAsList().size() - 1) { + builder.append(','); + } + } + return builder.append('>').toString(); + } + + @Override + public List getSuperComponents() { + return this.getTypeInfo().getSuperComponentsList(); + } + + @Override + public Optional getTypeOfPort(@NonNull String name) { + Preconditions.checkNotNull(name); + + Optional port = this.getTypeInfo().getPort(name, false); + + if (port.isPresent()) { + Optional unboundPortType + = port.filter(PortSymbol::isTypePresent).map(PortSymbol::getType); + if (unboundPortType.isEmpty()) return Optional.empty(); + return this.createBoundTypeExpression(unboundPortType.get()); + } else { + for (CompKindExpression superComponent : this.getSuperComponents()) { + Optional portOfSuper = superComponent.getTypeOfPort(name); + if (portOfSuper.isPresent()) return portOfSuper; + } + return Optional.empty(); + } + } + + @Override + public Optional getTypeOfParameter(@NonNull String parameterName) { + Preconditions.checkNotNull(parameterName); + + SymTypeExpression unboundParamType = this.getTypeInfo() + .getParameter(parameterName) + .map(VariableSymbolTOP::getType) + .orElseThrow(NoSuchElementException::new); + + return this.createBoundTypeExpression(unboundParamType); + } + + public Optional getTypeBindingFor(@NonNull TypeVarSymbol typeVar) { + Preconditions.checkNotNull(typeVar); + return Optional.ofNullable(this.getTypeVarBindings().get(typeVar)); + } + + public Optional getTypeBindingFor(@NonNull String typeVarName) { + Preconditions.checkNotNull(typeVarName); + Optional searchedTypeVar = this.getTypeVarBindings().keySet().stream() + .filter(tvar -> tvar.getName().equals(typeVarName)) + .findFirst(); + return searchedTypeVar.map(typeVarSymbol -> this.getTypeVarBindings().get(typeVarSymbol)); + } + + public ImmutableList getTypeBindingsAsList() { + return this.typeArguments; + } + + /** + * If this {@code TypeExprOfGenericComponent} references type variables (e.g. the type {@code Comp>}) and you + * provide a {@link SymTypeExpression} mapping for that type variable, this method returns a {@code + * TypeExprOfGenericComponent} where that type variable has been reset by the SymTypeExpression you provided. E.g., if + * you provide the mapping {@code T -> Person} for the above given example component, then this method would return + * {@code Comp>}. If you provide mappings for type variables that do not appear in the component type + * expression, then these will be ignored. + */ + public KindOfGenericComponent bindTypeParameter( + @NonNull Map newTypeVarBindings) { + Preconditions.checkNotNull(newTypeVarBindings); + + List newBindings = new ArrayList<>(); + for(SymTypeExpression typeArg : this.typeArguments) { + SymTypeExpression newTypeArg; + if(typeArg.isTypeVariable() && newTypeVarBindings.containsKey(((TypeVarSymbol) typeArg.getTypeInfo()))) { + newTypeArg = newTypeVarBindings.get((TypeVarSymbol) typeArg.getTypeInfo()); + } else { + newTypeArg = typeArg.deepClone(); + newTypeArg.replaceTypeVariables(newTypeVarBindings); + } + newBindings.add(newTypeArg); + } + return new KindOfGenericComponent(this.getTypeInfo(), newBindings); + } + + @Override + public KindOfGenericComponent deepClone(@NonNull ComponentSymbol compTypeSymbol) { + List clonedBindings = this.getTypeBindingsAsList().stream() + .map(SymTypeExpression::deepClone) + .collect(Collectors.toList()); + return new KindOfGenericComponent(compTypeSymbol, clonedBindings); + } + + @Override + public boolean deepEquals(@NonNull CompKindExpression compSymType) { + Preconditions.checkNotNull(compSymType); + + if(!(compSymType instanceof KindOfGenericComponent)) { + return false; + } + KindOfGenericComponent otherCompExpr = (KindOfGenericComponent) compSymType; + + boolean equal = this.getTypeInfo().equals(compSymType.getTypeInfo()); + equal &= this.getTypeBindingsAsList().size() == otherCompExpr.getTypeBindingsAsList().size(); + for(int i = 0; i < this.getTypeBindingsAsList().size(); i++) { + equal &= this.getTypeBindingsAsList().get(i).deepEquals(otherCompExpr.getTypeBindingsAsList().get(i)); + } + + return equal; + } + + protected Optional createBoundTypeExpression(@NonNull SymTypeExpression unboundTypeExpr) { + if (unboundTypeExpr.isPrimitive() || unboundTypeExpr.isObjectType() || unboundTypeExpr.isNullType()) { + return Optional.of(unboundTypeExpr); + } else if (unboundTypeExpr.isTypeVariable()) { + return this.getTypeBindingFor(unboundTypeExpr.getTypeInfo().getName()); + } else { + SymTypeExpression boundSymType = unboundTypeExpr.deepClone(); + boundSymType.replaceTypeVariables(this.getTypeVarBindings()); + return Optional.of(boundSymType); + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/KindOfGenericComponentDeSer.java b/monticore-grammar/src/main/java/de/monticore/types/check/KindOfGenericComponentDeSer.java new file mode 100644 index 0000000000..f4fe3777d7 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/KindOfGenericComponentDeSer.java @@ -0,0 +1,60 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import com.google.common.base.Preconditions; +import de.monticore.symbols.compsymbols.CompSymbolsMill; +import de.monticore.symbols.compsymbols._symboltable.ComponentSymbolSurrogate; +import de.monticore.symboltable.serialization.JsonDeSers; +import de.monticore.symboltable.serialization.JsonPrinter; +import de.monticore.symboltable.serialization.json.JsonObject; +import de.se_rwth.commons.logging.Log; +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.List; + +public class KindOfGenericComponentDeSer implements CompKindExprDeSer { + + public static final String SERIALIZED_KIND = "genericarc.check.TypeExprOfGenericComponent"; + public static final String TYPE_VAR_BINDINGS = "typeVarBindings"; + + @Override + public String serializeAsJson(@NonNull KindOfGenericComponent toSerialize) { + Preconditions.checkNotNull(toSerialize); + + JsonPrinter printer = new JsonPrinter(); + + printer.beginObject(); + printer.member(JsonDeSers.KIND, SERIALIZED_KIND); + printer.member(KindOfComponentDeSer.COMP_NAME, toSerialize.getTypeInfo().getFullName()); + SymTypeExpressionDeSer.serializeMember(printer, TYPE_VAR_BINDINGS, toSerialize.getTypeBindingsAsList()); + printer.endObject(); + + return printer.getContent(); + } + + @Override + public KindOfGenericComponent deserialize(JsonObject serialized) { + Preconditions.checkNotNull(serialized); + Preconditions.checkArgument( + JsonDeSers.getKind(serialized).equals(SERIALIZED_KIND), + "Kind must be %s, but is %s.", + SERIALIZED_KIND, JsonDeSers.getKind(serialized) + ); + + Log.warn("Deserializing TypeExprOfGenericComponents is buggy currently!"); + + String compTypeName = serialized.getMember(KindOfComponentDeSer.COMP_NAME) + .getAsJsonString() + .getValue(); + + ComponentSymbolSurrogate compType = CompSymbolsMill + .componentSymbolSurrogateBuilder() + .setName(compTypeName) + .setEnclosingScope(CompSymbolsMill.globalScope()) + .build(); + + List paramBindings = SymTypeExpressionDeSer.deserializeListMember(TYPE_VAR_BINDINGS, serialized); + + return new KindOfGenericComponent(compType, paramBindings); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeArray.java b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeArray.java new file mode 100644 index 0000000000..bd376d4d24 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeArray.java @@ -0,0 +1,165 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbolSurrogate; +import de.monticore.types3.ISymTypeVisitor; +import de.se_rwth.commons.logging.Log; + +/** + * Arrays of a certain dimension (>= 1) + */ +public class SymTypeArray extends SymTypeExpression { + + /** + * An arrayType has a dimension (>= 1) + */ + protected int dim; + + /** + * An Array has an argument Type + */ + protected SymTypeExpression argument; + + /** + * Constructor + * + * @deprecated TypeSymbul UND Expression: da ist was doppelt? + * + * @param dim dimension + * @param argument Argument Type + * @param typeSymbol loader for the Type-Symbol that defines this type + */ + @Deprecated + public SymTypeArray(TypeSymbol typeSymbol, int dim, SymTypeExpression argument) { + this.typeSymbol = typeSymbol; + this.dim = dim; + this.argument = argument; + } + + public SymTypeArray(SymTypeExpression argument, int dim) { + this.dim = dim; + this.argument = argument; + } + + // ------------------------------------------------------------------ Functions + + + @Override + public boolean isArrayType() { + return true; + } + + public int getDim() { + return dim; + } + + public void setDim(int dim) { + this.dim = dim; + } + + /** + * returns a clone, but with n dimensions less + */ + public SymTypeExpression cloneWithLessDim(int n) { + if (n > getDim()) { + Log.error("0xFDCCE tried removing " + n + + "dimensions from an " + getDim() + "dimensional array"); + return SymTypeExpressionFactory.createObscureType(); + } + else if (n == getDim()) { + return getArgument().deepClone(); + } + else { + SymTypeArray clone = this.deepClone(); + clone.setDim(getDim() - n); + return clone; + } + } + + public SymTypeExpression getArgument() { + return argument; + } + + public void setArgument(SymTypeExpression argument) { + this.argument = argument; + } + + /** + * print: Umwandlung in einen kompakten String + */ + @Override + public String print() { + StringBuffer r = new StringBuffer(getArgument().print()); + for (int i = 1; i <= dim; i++) { + r.append("[]"); + } + return r.toString(); + } + + @Override + public String printFullName(){ + StringBuffer r = new StringBuffer(getArgument().printFullName()); + for (int i = 1; i <= dim; i++) { + r.append("[]"); + } + return r.toString(); + } + + @Override + public SymTypeArray deepClone() { + //to support deprecated code: + if(typeSymbol != null) { + TypeSymbol typeSymbol = new TypeSymbolSurrogate(this.typeSymbol.getName()); + typeSymbol.setEnclosingScope(this.typeSymbol.getEnclosingScope()); + return new SymTypeArray(typeSymbol, + this.dim, this.argument.deepClone()); + } + else { + return new SymTypeArray(getArgument().deepClone(), getDim()); + } + } + + @Override + public boolean deepEquals(SymTypeExpression sym){ + //to support deprecated code: + if(typeSymbol != null) { + if (!(sym instanceof SymTypeArray)) { + return false; + } + SymTypeArray symArr = (SymTypeArray) sym; + if(this.dim!=symArr.dim){ + return false; + } + if(this.typeSymbol == null ||symArr.typeSymbol ==null){ + return false; + } + if(!this.typeSymbol.getEnclosingScope().equals(symArr.typeSymbol.getEnclosingScope())){ + return false; + } + if(!this.typeSymbol.getName().equals(symArr.typeSymbol.getName())){ + return false; + } + if(!this.getArgument().deepEquals(symArr.getArgument())){ + return false; + } + return this.print().equals(symArr.print()); + } + if (!sym.isArrayType()) { + return false; + } + SymTypeArray symArr = (SymTypeArray) sym; + if (getDim() != symArr.getDim()) { + return false; + } + if (!getArgument().deepEquals(symArr.getArgument())) { + return false; + } + return true; + } + + @Override + public void accept(ISymTypeVisitor visitor) { + visitor.visit(this); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeArrayDeSer.java b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeArrayDeSer.java new file mode 100644 index 0000000000..3cf175cd01 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeArrayDeSer.java @@ -0,0 +1,48 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.IBasicSymbolsGlobalScope; +import de.monticore.symboltable.serialization.JsonDeSers; +import de.monticore.symboltable.serialization.JsonParser; +import de.monticore.symboltable.serialization.JsonPrinter; +import de.monticore.symboltable.serialization.json.JsonElement; +import de.monticore.symboltable.serialization.json.JsonObject; +import de.se_rwth.commons.logging.Log; + +public class SymTypeArrayDeSer { + + // Care: the following String needs to be adapted if the package was renamed + public static final String SERIALIZED_KIND = "de.monticore.types.check.SymTypeArray"; + public static final String SERIALIZED_ARGUMENT = "argument"; + public static final String SERIALIZED_DIM = "dim"; + + public String serialize(SymTypeArray toSerialize) { + JsonPrinter jp = new JsonPrinter(); + jp.beginObject(); + jp.member(JsonDeSers.KIND, SERIALIZED_KIND); + jp.memberJson(SERIALIZED_ARGUMENT, + SymTypeExpressionDeSer.getInstance().serialize(toSerialize.argument)); + jp.member(SERIALIZED_DIM, toSerialize.getDim()); + jp.endObject(); + return jp.getContent(); + } + + public SymTypeArray deserialize(String serialized) { + return deserialize(JsonParser.parseJsonObject(serialized)); + } + + public SymTypeArray deserialize(JsonObject serialized) { + if (serialized.hasIntegerMember(SERIALIZED_DIM) && serialized.hasMember(SERIALIZED_ARGUMENT)) { + int dim = serialized.getIntegerMember(SERIALIZED_DIM); + JsonElement argumentJson = serialized.getMember(SERIALIZED_ARGUMENT); + SymTypeExpression arg = SymTypeExpressionDeSer.getInstance().deserialize(argumentJson); + IBasicSymbolsGlobalScope gs = BasicSymbolsMill.globalScope(); + return SymTypeExpressionFactory.createTypeArray(arg.print(), gs, dim, arg); + } + Log.error( + "0x823F2 Internal error: Cannot deserialize \"" + serialized + "\" as SymTypeArray!"); + return null; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeExpression.java b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeExpression.java new file mode 100644 index 0000000000..f8096d06c3 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeExpression.java @@ -0,0 +1,513 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import com.google.common.collect.Lists; +import de.monticore.ast.ASTNode; +import de.monticore.symbols.basicsymbols._symboltable.FunctionSymbol; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.symbols.basicsymbols._symboltable.TypeVarSymbol; +import de.monticore.symbols.basicsymbols._symboltable.VariableSymbol; +import de.monticore.symbols.oosymbols.OOSymbolsMill; +import de.monticore.symbols.oosymbols._symboltable.*; +import de.monticore.symboltable.modifiers.AccessModifier; +import de.monticore.types3.ISymTypeVisitor; +import de.monticore.types3.util.SymTypeDeepCloneVisitor; +import de.se_rwth.commons.logging.Log; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * SymTypeExpression is the superclass for all typeexpressions, such as + * TypeConstants, TypeVariables and applications of Type-Constructors. + * It shares common functionality + * (such as comparison, printing) + */ +public abstract class SymTypeExpression { + + /** + * print: Conversion to a compact string, such as "int", "Person", "List< A >" + */ + public abstract String print(); + + /** + * printFullName: prints the full name of the symbol, such as "java.util.List" + * @return + */ + public abstract String printFullName(); + + /** + * printAsJson: Umwandlung in einen kompakten Json String + */ + protected String printAsJson() { + return SymTypeExpressionDeSer.getInstance().serialize(this); + } + + /** + * Am I a not valid type? + * e.g. unknown type, + * not all type variables set, + * pseudo types like typeVariables + */ + public boolean isValidType() { + return true; + } + + /** + * Am I primitive? (such as "int") + * (default: no) + */ + public boolean isPrimitive() { + return false; + } + + /** + * Am I a generic type? (such as "List") + */ + public boolean isGenericType() { + return false; + } + + /** + * Am I a type variable? + */ + public boolean isTypeVariable(){ + return false; + } + + /** + * Am I an array? + */ + public boolean isArrayType(){ + return false; + } + + /** + * Am I of void type? + */ + public boolean isVoidType(){ + return false; + } + + /** + * Am I of null type? + */ + public boolean isNullType(){ + return false; + } + + /** + * Am I an object type? (e.g. "String", "Person") + */ + public boolean isObjectType(){ + return false; + } + + /** + * Am I a function type (e.g. "String -> Integer") + */ + public boolean isFunctionType(){ + return false; + } + + /** + * Am I an union type (e.g. "(A|B)")? + */ + public boolean isUnionType() { + return false; + } + + /** + * Am I an intersection type (e.g. "(A&B)") + */ + public boolean isIntersectionType() { + return false; + } + + /** + * Can I not have a type derived from (e.g. "1 - student")? + */ + public boolean isObscureType(){ + return false; + } + + /** + * Am I a wildcard (s. generics)? + */ + public boolean isWildcard() { + return false; + } + + public SymTypeExpression deepClone() { + return new SymTypeDeepCloneVisitor().calculate(this); + } + + public abstract boolean deepEquals(SymTypeExpression sym); + + @Deprecated + protected List functionList = new ArrayList<>(); + +@Deprecated +public List getMethodList(String methodName, boolean abstractTc) { + return getMethodList(methodName, abstractTc, AccessModifier.ALL_INCLUSION); +} + + /** + * returns the list of methods the SymTypeExpression can access and + * filters these for a method with specific name + * the last calculated type in the type check was no type + */ + public List getMethodList(String methodname, boolean abstractTc, AccessModifier modifier){ + functionList.clear(); + //get methods from the typesymbol + List methods = getCorrectMethods(methodname,false, abstractTc, modifier); + return transformMethodList(methodname,methods); + } + +@Deprecated +public List getCorrectMethods(String methodName, boolean outerIsType, boolean abstractTc) { + return getCorrectMethods(methodName, outerIsType, abstractTc, AccessModifier.ALL_INCLUSION); +} + + /** + * return the correct methods for the two situations: + * 1) the last calculated type in the type check was a type, + * then filter for non-static methods and + * add the static methods of this type + * 2) the last calculated type in the type check was an instance, + * then just resolve for methods + * @param methodName name of the method we search for + * @param outerIsType true if last result of type check was type, + * false if it was an instance + * @param abstractTc true if the tc is not used for object-oriented languages + * @return the correct methods for the specific case + */ + protected List getCorrectMethods(String methodName, + boolean outerIsType, boolean abstractTc, AccessModifier modifier){ + if(!abstractTc) { + List functions = getTypeInfo().getSpannedScope() + .resolveFunctionMany(methodName, modifier).stream() + .filter(f -> !(f instanceof MethodSymbol)) + .collect(Collectors.toList()); + List methods = Lists.newArrayList(); + if (getTypeInfo().getSpannedScope() instanceof IOOSymbolsScope) { + methods.addAll(((IOOSymbolsScope) getTypeInfo() + .getSpannedScope()).resolveFunctionMany(methodName, modifier) + .stream().filter(f -> f instanceof MethodSymbol) + .collect(Collectors.toList())); + } + if (outerIsType) { + List methodsWithoutStatic = + methods.stream().filter(Objects::nonNull) + .map(m -> (MethodSymbol) m) + .filter(m -> !m.isIsStatic()) + .collect(Collectors.toList()); + methodsWithoutStatic.addAll(functions); + if (getTypeInfo().getSpannedScope() instanceof IOOSymbolsScope) { + List localStaticMethods = + ((IOOSymbolsScope) getTypeInfo().getSpannedScope()) + .getLocalMethodSymbols().stream() + .filter(MethodSymbol::isIsStatic) + .collect(Collectors.toList()); + methodsWithoutStatic.addAll(localStaticMethods); + } + return methodsWithoutStatic; + } else { + functions.addAll(methods); + return functions; + } + }else{ + return getTypeInfo().getSpannedScope().resolveFunctionMany(methodName, modifier); + } + } + + /** + * transforms the methods by replacing their type variables with + * the actual type arguments + * @param methodName name of the method we search for + * @param functions methods that need to be transformed + * @return transformed methods + */ + protected List transformMethodList(String methodName, List functions){ + List matchingMethods = new ArrayList<>(); + for(FunctionSymbol method: functions){ + List fieldSymbols = new ArrayList<>(); + for(VariableSymbol parameter: method.getParameterList()){ + fieldSymbols.add(parameter.deepClone()); + } + FunctionSymbol copiedMethodSymbol = method.deepClone(); + IOOSymbolsScope scope = OOSymbolsMill.scope(); + for(VariableSymbol parameter: fieldSymbols){ + scope.add(parameter); + } + for(TypeVarSymbol typeVar: method.getTypeVariableList()){ + scope.add(typeVar); + } + copiedMethodSymbol.setSpannedScope(scope); + this.functionList.add(copiedMethodSymbol); + } + //filter methods + for(FunctionSymbol method: functionList){ + if(method.getName().equals(methodName)){ + matchingMethods.add(method.deepClone()); + } + } + if(isGenericType()){ + // compare type arguments of SymTypeExpression(actual type) + // and its TypeVarSymbol(type definition) + List arguments = + ((SymTypeOfGenerics)this.deepClone()).getArgumentList(); + List typeVariableArguments = + getTypeInfo().getTypeParameterList(); + Map map = new HashMap<>(); + if(arguments.size()!=typeVariableArguments.size()){ + Log.error("0xA1300 Different number of type arguments in TypeSymbol and SymTypeExpression"); + } + for(int i=0;i SymTypeExpression + map.put(typeVariableArguments.get(i),arguments.get(i)); + } + //every method in methodList: replace typevariables in + // parameters or return type with its actual symtypeexpression + for(FunctionSymbol method: matchingMethods) { + //return type + method.replaceTypeVariables(map); + //type parameters + for (VariableSymbol parameter : method.getParameterList()) { + parameter.replaceTypeVariables(map); + } + } + // if there are two methods with the same parameters and return + // type remove the second method + // in the list because it is a method from a super type and is + // overridden by the first method + for(int i = 0;i replaceMap){ + //empty so it only needs to be overridden by some SymTypeExpressions + } + +@Deprecated +public List getMethodList(String methodName, boolean outerIsType, boolean abstractTc) { + return getMethodList(methodName, outerIsType, abstractTc, AccessModifier.ALL_INCLUSION); +} + + /** + * returns the correct methods in both cases: + * 1) the last result was a type, + * 2) the last result was an instance + * @param methodName name of the method we search for + * @param outerIsType true if the last result was a type, false + * if it was an instance + * @return the correct methods for the specific case + */ + public List getMethodList(String methodName, + boolean outerIsType, boolean abstractTc, AccessModifier modifier) { + functionList.clear(); + List methods = + getCorrectMethods(methodName,outerIsType, abstractTc, modifier); + return transformMethodList(methodName,methods); + } + +@Deprecated +public List getFieldList(String fieldName, boolean abstractTc){ + return getFieldList(fieldName, abstractTc, AccessModifier.ALL_INCLUSION); +} + + /** + * returns the list of fields the SymTypeExpression can access + * and filters these for a field with specific name + */ + public List getFieldList(String fieldName, boolean abstractTc, AccessModifier modifier){ + //get methods from the typesymbol + List fields = getCorrectFields(fieldName,false, abstractTc, modifier); + return transformFieldList(fieldName,fields); + } + +@Deprecated +public List getFieldList(String fieldName, boolean outerIsType, boolean abstractTc){ + return getFieldList(fieldName, outerIsType, abstractTc, AccessModifier.ALL_INCLUSION); +} + + /** + * returns the correct fields in both cases: + * 1) the last result was a type, + * 2) the last result was an instance + * @param fieldName name of the field we search for + * @param outerIsType true if the last result was a type, + * false if it was an instance + * @return the correct fields for the specific case + */ + public List getFieldList(String fieldName, + boolean outerIsType, boolean abstractTc, AccessModifier modifier) { + List fields = getCorrectFields(fieldName, + outerIsType, abstractTc, modifier); + return transformFieldList(fieldName,fields); + } + + @Deprecated +public List getCorrectFields(String fieldName, boolean outerIsType, boolean abstractTc){ + return getCorrectFields(fieldName, outerIsType, abstractTc, AccessModifier.ALL_INCLUSION); +} + + /** + * return the correct fields for the two situations: + * 1) the last calculated type in the type check was a type, + * then filter for non-static fields and + * add the static fields of this type + * 2) the last calculated type in the type check was an instance, + * then just resolve for fields + * @param fieldName name of the field we search for + * @param outerIsType true if last result of type check was type, + * false if it was an instance + * @return the correct fields for the specific case + */ + protected List getCorrectFields(String fieldName, + boolean outerIsType, boolean abstractTc, AccessModifier modifier) { + if(!abstractTc) { + List variables = getTypeInfo().getSpannedScope() + .resolveVariableMany(fieldName, modifier).stream() + .filter(v -> !(v instanceof FieldSymbol)) + .collect(Collectors.toList()); + List fields = Lists.newArrayList(); + if (getTypeInfo().getSpannedScope() instanceof IOOSymbolsScope) { + fields.addAll((getTypeInfo().getSpannedScope()) + .resolveVariableMany(fieldName, modifier).stream() + .filter(v -> v instanceof FieldSymbol) + .collect(Collectors.toList())); + } + if (outerIsType) { + List fieldsWithoutStatic = + fields.stream().map(f -> (FieldSymbol) f) + .filter(f -> !f.isIsStatic()) + .collect(Collectors.toList()); + fieldsWithoutStatic.addAll(variables); + if (getTypeInfo().getSpannedScope() instanceof IOOSymbolsScope) { + List localStaticFields = + ((IOOSymbolsScope) getTypeInfo().getSpannedScope()) + .getLocalFieldSymbols().stream() + .filter(FieldSymbol::isIsStatic) + .collect(Collectors.toList()); + fieldsWithoutStatic.addAll(localStaticFields); + } + return fieldsWithoutStatic; + } else { + variables.addAll(fields); + return variables; + } + } else { + return getTypeInfo().getSpannedScope().resolveVariableMany(fieldName, modifier); + } + } + + /** + * transforms the fields by replacing their type variables with + * the actual type arguments + * @param fieldName name of the field we search for + * @param fields fields that need to be transformed + * @return transformed fields + */ + protected List transformFieldList(String fieldName, + List fields) { + List fieldList = new ArrayList<>(); + //filter fields + for(VariableSymbol field: fields){ + if(field.getName().equals(fieldName)){ + fieldList.add(field.deepClone()); + } + } + if(!isGenericType()){ + return fieldList; + }else{ + // compare type arguments of SymTypeExpression(actual type) + // and its TypeSymbol(type definition) + List arguments = + ((SymTypeOfGenerics)this.deepClone()).getArgumentList(); + List typeVariableArguments = + getTypeInfo().getTypeParameterList(); + Map map = new HashMap<>(); + if(arguments.size()!=typeVariableArguments.size()){ + Log.error("0xA1301 Different number of type arguments in TypeSymbol and SymTypeExpression"); + } + for(int i=0;i SymTypeExpression + map.put(typeVariableArguments.get(i),arguments.get(i)); + } + // every field in fieldList: replace typevariables in + // type with its actual symtypeexpression + for(VariableSymbol field: fieldList){ + field.replaceTypeVariables(map); + } + } + // if there are two fields with the same type remove the + // second field in the list because it is a + // field from a super type and is overridden by the first field + for(int i = 0;i member) { + if (member.isPresent()) { + printer.memberJson(memberName, member.get().printAsJson()); + } + } + + public static void serializeMember(JsonPrinter printer, String memberName, + List member) { + printer.array(memberName, member, SymTypeExpression::printAsJson); + } + + public static SymTypeExpression deserializeMember(String memberName, JsonObject json) { + return getInstance().deserialize(json.getMember(memberName)); + } + + public static Optional deserializeOptionalMember(String memberName, + JsonObject json) { + if (json.hasMember(memberName)) { + return Optional.of(getInstance().deserialize(json.getMember(memberName))); + } + else { + return Optional.empty(); + } + } + + public static List deserializeListMember(String memberName, JsonObject json) { + List result = new ArrayList<>(); + if (json.hasMember(memberName)) { + for (JsonElement e : json.getArrayMember(memberName)) { + result.add(getInstance().deserialize(e)); + } + } + return result; + } + + public static SymTypeExpressionDeSer getInstance() { + if (null == instance) { + instance = new SymTypeExpressionDeSer(); + } + return instance; + } + + /** + * This method can be used to set the instance of the SymTypeExpressionDeSer to a custom suptype + * + * @param theInstance + */ + public static void setInstance(SymTypeExpressionDeSer theInstance) { + if (null == theInstance) { //in this case, "reset" to default type + instance = new SymTypeExpressionDeSer(); + } + else { + instance = theInstance; + } + } + + public String serialize(SymTypeExpression toSerialize) { + // this may not be the most optimal implementation, + // however, we currently do not need more + // void and null are stored as strings + if(toSerialize.isNullType()) { + return "\""+BasicSymbolsMill.NULL +"\""; + } + if(toSerialize.isVoidType()) { + return "\""+BasicSymbolsMill.VOID +"\""; + } + if(toSerialize.isArrayType()) { + return symTypeArrayDeSer.serialize((SymTypeArray)toSerialize); + } + if(toSerialize.isFunctionType()) { + return symTypeOfFunctionDeSer.serialize((SymTypeOfFunction) toSerialize); + } + if(toSerialize.isGenericType()) { + return symTypeOfGenericsDeSer.serialize((SymTypeOfGenerics) toSerialize); + } + if(toSerialize.isIntersectionType()) { + return symTypeOfIntersectionDeSer.serialize((SymTypeOfIntersection) toSerialize); + } + if(toSerialize.isObjectType()) { + return symTypeOfObjectDeSer.serialize((SymTypeOfObject) toSerialize); + } + if(toSerialize.isUnionType()) { + return symTypeOfUnionDeSer.serialize((SymTypeOfUnion)toSerialize); + } + if(toSerialize.isPrimitive()) { + return symTypePrimitiveDeSer.serialize((SymTypePrimitive)toSerialize); + } + if(toSerialize.isTypeVariable()) { + return symTypeVariableDeSer.serialize((SymTypeVariable) toSerialize); + } + if(toSerialize.isWildcard()) { + return symTypeOfWildcardDeSer.serialize((SymTypeOfWildcard) toSerialize); + } + Log.error("0x823FD Internal error: Loading ill-structured SymTab: No way to serialize SymType;"); + return null; + } + + /** + * This method is a shortcut, as there are many symbolrules indicating that a symbol has a + * a List of SymTypeExpressions as member. + * + * @param serializedMember + * @return + */ + public List deserializeList(JsonElement serializedMember) { + List result = new ArrayList<>(); + for (JsonElement e : serializedMember.getAsJsonArray().getValues()) { + result.add(deserialize(e)); + } + return result; + } + + public SymTypeExpression deserialize(String serialized) { + return deserialize(JsonParser.parse(serialized)); + } + + public SymTypeExpression deserialize(JsonElement serialized) { + + // void and null are stored as strings + if (serialized.isJsonString()) { + switch(serialized.getAsJsonString().getValue()){ + case BasicSymbolsMill.NULL: + return SymTypeExpressionFactory.createTypeOfNull(); + case BasicSymbolsMill.VOID: + return SymTypeExpressionFactory.createTypeVoid(); + } + } + + // all other serialized SymTypeExrpressions are json objects with a kind + if (serialized.isJsonObject()) { + JsonObject o = serialized.getAsJsonObject(); + switch (JsonDeSers.getKind(o)) { + case SymTypeArrayDeSer.SERIALIZED_KIND: + return symTypeArrayDeSer.deserialize(o); + case SymTypePrimitiveDeSer.SERIALIZED_KIND: + return symTypePrimitiveDeSer.deserialize(o); + case SymTypeOfGenericsDeSer.SERIALIZED_KIND: + return symTypeOfGenericsDeSer.deserialize(o); + case SymTypeOfIntersectionDeSer.SERIALIZED_KIND: + return symTypeOfIntersectionDeSer.deserialize(o); + case SymTypeOfObjectDeSer.SERIALIZED_KIND: + return symTypeOfObjectDeSer.deserialize(o); + case SymTypeOfUnionDeSer.SERIALIZED_KIND: + return symTypeOfUnionDeSer.deserialize(o); + case SymTypeVariableDeSer.SERIALIZED_KIND: + return symTypeVariableDeSer.deserialize(o); + case SymTypeOfWildcardDeSer.SERIALIZED_KIND: + return symTypeOfWildcardDeSer.deserialize(o); + case SymTypeOfFunctionDeSer.SERIALIZED_KIND: + return symTypeOfFunctionDeSer.deserialize(o); + } + } + + Log.error( + "0x823FE Internal error: Loading ill-structured SymTab: Unknown serialization of SymTypeExpression: " + + serialized); + return null; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeExpressionFactory.java b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeExpressionFactory.java new file mode 100644 index 0000000000..68465722cb --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeExpressionFactory.java @@ -0,0 +1,415 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import com.google.common.collect.Lists; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.FunctionSymbol; +import de.monticore.symbols.basicsymbols._symboltable.IBasicSymbolsScope; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbolSurrogate; +import de.monticore.symbols.basicsymbols._symboltable.TypeVarSymbol; +import de.monticore.symbols.basicsymbols._util.BasicSymbolsTypeDispatcher; +import de.se_rwth.commons.logging.Log; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import static de.monticore.symbols.basicsymbols.BasicSymbolsMill.PRIMITIVE_LIST; + +/** + * SymTypeExpressionFactory contains static functions that create + * the various forms of TypeExpressions used for Sym-Types. + *

+ * This factory therefore should be the only source to create SymTypeExpressions. + * No other source is needed. + * (That is ok, as the set of SymTypeExpressions is rather fixed and we do not expect + * many modular extensions that would be needed. Saying this, we know that + * potentially union types (A|B) might still be added in the future.) + */ +public class SymTypeExpressionFactory { + + /** + * createTypeVariable vor Variables + */ + public static SymTypeVariable createTypeVariable(String name, IBasicSymbolsScope scope) { + TypeVarSymbol typeSymbol = new TypeVarSymbol(name); + typeSymbol.setEnclosingScope(scope); + return createTypeVariable(typeSymbol); + } + + /** + * used to create a (optionally bounded) FREE type variable. + * These are created internally while inferring types. + * + * @param lowerBound is required to be a sub-type. + * For no bounds use {@link #createBottomType()}. + * @param upperBound is required to be a super-type, + * e.g., {@code T extends Person & Iterable}. + * For no bounds use {@link #createTopType()}. + */ + public static SymTypeVariable createTypeVariable( + SymTypeExpression lowerBound, SymTypeExpression upperBound) { + return createTypeVariable(null, lowerBound, upperBound); + } + + public static SymTypeVariable createTypeVariable(TypeVarSymbol typeVarSymbol) { + // the SymTypeVariable extracts the upper bound from the type itself, + // as such we do not set it here + SymTypeExpression upperBound = createTopType(); + // our Symbols have no notion of lower bound, + // as such we use the bottom type + SymTypeExpression lowerBound = createBottomType(); + return createTypeVariable(typeVarSymbol, lowerBound, upperBound); + } + + public static SymTypeVariable createTypeVariable( + TypeVarSymbol typeVarSymbol, + SymTypeExpression lowerBound, + SymTypeExpression upperBound + ) { + return new SymTypeVariable(typeVarSymbol, lowerBound, upperBound); + } + + @Deprecated + public static SymTypeVariable createTypeVariable(TypeSymbol typeSymbol) { + return new SymTypeVariable(typeSymbol); + } + + /** + * for primitives, such as "int" (and no other kinds). + * TypeInfo is not needed (as the Objects are predefined singletons) + */ + public static SymTypePrimitive createPrimitive(String name) { + Optional type = BasicSymbolsMill.globalScope().resolveTypeLocally(name); + if (!type.isPresent()) { + Log.error("0x893F62 Internal Error: Non primitive type " + name + " stored as constant."); + } + return createPrimitive(type.get()); + } + + public static SymTypePrimitive createPrimitive(TypeSymbol type) { + return new SymTypePrimitive(type); + } + + /** + * for ObjectTypes, as e.g. "Person" + */ + public static SymTypeOfObject createTypeObject(TypeSymbol typeSymbol) { + return new SymTypeOfObject(typeSymbol); + } + + /** + * for ObjectTypes, as e.g. "Person" + */ + public static SymTypeOfObject createTypeObject(String name, IBasicSymbolsScope enclosingScope) { + TypeSymbol typeSymbol = new TypeSymbolSurrogate(name); + typeSymbol.setEnclosingScope(enclosingScope); + return new SymTypeOfObject(typeSymbol); + } + + /** + * creates the "Void"-type, i.e. a pseudotype that represents the absence of a real type + * + * @return + */ + public static SymTypeVoid createTypeVoid() { + return new SymTypeVoid(); + } + + public static SymTypeObscure createObscureType() { + return new SymTypeObscure(); + } + + /** + * That is the pseudo-type of "null" + */ + public static SymTypeOfNull createTypeOfNull() { + return new SymTypeOfNull(); + } + + /** + * creates an array-Type Expression + * @deprecated arrays do not have an type symbol + * @param typeSymbol + * @param dim the dimension of the array + * @param argument the argument type (of the elements) + * @return + */ + @Deprecated + public static SymTypeArray createTypeArray(TypeSymbol typeSymbol, int dim, + SymTypeExpression argument) { + return new SymTypeArray(typeSymbol, dim, argument); + } + + /** + * @deprecated arrays do not have a name + */ + @Deprecated + public static SymTypeArray createTypeArray(String name, IBasicSymbolsScope typeSymbolsScope, + int dim, SymTypeExpression argument) { + TypeSymbol typeSymbol = new TypeSymbolSurrogate(name); + typeSymbol.setEnclosingScope(typeSymbolsScope); + return new SymTypeArray(typeSymbol, dim, argument); + } + + public static SymTypeArray createTypeArray(SymTypeExpression argument, int dim) { + return new SymTypeArray(argument, dim); + } + + public static SymTypeExpression createTypeExpression(TypeSymbol typeSymbol) { + SymTypeExpression o; + if(PRIMITIVE_LIST.contains(typeSymbol.getName())){ + o = createPrimitive(typeSymbol.getName()); + } else if ("void".equals(typeSymbol.getName())) { + o = createTypeVoid(); + } else if ("null".equals(typeSymbol.getName())) { + o = createTypeOfNull(); + } else if (typeSymbol.getTypeParameterList().isEmpty()) { + o = createTypeObject(typeSymbol); + } else { + o = createGenerics(typeSymbol); + } + return o; + } + + /** + * creates a TypeExpression for primitives, such as "int", for "null", "void" and + * also for object types, such as "Person" from a given symbol + * Primitives don't need a type symbol, object types need both. + * + * @param name + * @param scope + * @return + */ + public static SymTypeExpression createTypeExpression(String name, IBasicSymbolsScope scope) { + SymTypeExpression o; + if ("void".equals(name)) { + o = createTypeVoid(); + } else if (PRIMITIVE_LIST.contains(name)) { + o = createPrimitive(name); + } else if ("null".equals(name)) { + o = createTypeOfNull(); + }else if(name.endsWith("[]")) { + o = createArrayFromString(name, scope); + } else if (name.contains("<")) { + o = createGenericsFromString(name, scope); + } else if (scope!=null && scope.resolveTypeVar(name).isPresent()){ + o = createTypeVariable(name, scope); + }else { + o = createTypeObject(name, scope); + } + return o; + } + + protected static SymTypeExpression createArrayFromString(String type, IBasicSymbolsScope scope){ + //berechne Typen vom Array per createTypeExpression, berechne Dimension von Array durch Anzahl der Klammerpaare am Ende + int countDim = 0; + int start = type.indexOf("["); + String typeWithoutBraces = type; + while(start!=-1){ + countDim++; + typeWithoutBraces = typeWithoutBraces.substring(0, start); + int lastGeneric = type.lastIndexOf(">"); + start = typeWithoutBraces.indexOf("["); + if(lastGeneric!=-1 && lastGeneric > start){ + break; + } + } + int generic = typeWithoutBraces.indexOf("<"); + String typeWithoutGenericsAndBraces = generic==-1? typeWithoutBraces:typeWithoutBraces.substring(0, generic); + return createTypeArray(typeWithoutGenericsAndBraces, scope, countDim, createTypeExpression(typeWithoutBraces, scope)); + } + + protected static SymTypeExpression createGenericsFromString(String type, IBasicSymbolsScope scope) { + int start = type.indexOf("<"); + if (start == -1) { + return SymTypeExpressionFactory.createTypeExpression(type, scope); + } + assert type.length() - start > 1; + List betweenBrackets = iterateBrackets(type, start); + String beforeBrackets = type.substring(0, start); + return SymTypeExpressionFactory.createGenerics(beforeBrackets, scope, getSubGenerics(betweenBrackets, scope)); + } + + /** + * recursively calls {@link #createGenericsFromString(String, IBasicSymbolsScope)} (String, IBasicSymbolsScope)}. + * If that method for example received {@code Map>} this Method should get {@code [String, List]} as parameter + * @param inBrackets list of generics nested one level + */ + protected static List getSubGenerics(List inBrackets, IBasicSymbolsScope scope){ + return inBrackets.stream() + .map(String::trim) + .map(name -> createGenericsFromString(name, scope)) + .collect(Collectors.toList()); + } + + /** + * splits the type-string along commas, but only on such that are on the first depth of generics + * @param type type-string, like {@code Map>} + * @param start first occurrence of an opening generic + * @return all first sub-generics as a list, like {@code [Double; HashMap]} + */ + protected static List iterateBrackets(String type, int start){ + List list = new ArrayList<>(); + int depth = 0; + for(int i = 0; i < type.toCharArray().length; i++) { + char c = type.toCharArray()[i]; + if(c == '<'){ + depth++; + } + if(depth == 1 && (c == ',' || c == '>')){ + list.add(type.substring(start+1, i)); + start = i; + } + if(c == '>'){ + depth--; + } + } + return list; + } + + + + /** + * createGenerics: for a generic Type + * + * @return + */ + public static SymTypeOfGenerics createGenerics(TypeSymbol typeSymbol) { + List parameters = + typeSymbol.getTypeParameterList().stream() + .map(tp -> createFromSymbol(tp)) + .collect(Collectors.toList()); + return createGenerics(typeSymbol, parameters); + } + + public static SymTypeOfGenerics createGenerics(TypeSymbol typeSymbol, + List arguments) { + return new SymTypeOfGenerics(typeSymbol, arguments); + } + + public static SymTypeOfGenerics createGenerics(TypeSymbol typeSymbol, + SymTypeExpression... arguments) { + return createGenerics(typeSymbol, Arrays.asList(arguments)); + } + + /** + * createGenerics: is created using the enclosing Scope to ask for the appropriate symbol. + */ + public static SymTypeOfGenerics createGenerics(String name, IBasicSymbolsScope enclosingScope) { + return createGenerics(name, enclosingScope, Lists.newArrayList()); + } + + public static SymTypeOfGenerics createGenerics(String name, IBasicSymbolsScope enclosingScope, + List arguments) { + TypeSymbol typeSymbol = new TypeSymbolSurrogate(name); + typeSymbol.setEnclosingScope(enclosingScope); + return createGenerics(typeSymbol, arguments); + } + + public static SymTypeOfGenerics createGenerics(String name, IBasicSymbolsScope enclosingScope, + SymTypeExpression... arguments) { + return createGenerics(name, enclosingScope, Arrays.asList(arguments)); + } + + public static SymTypeExpression createFromSymbol(TypeSymbol typeSymbol) { + BasicSymbolsTypeDispatcher typeDispatcher = + BasicSymbolsMill.typeDispatcher(); + if(typeDispatcher.isTypeVar(typeSymbol)) { + return createTypeVariable((TypeVarSymbol) typeSymbol); + } + if(typeSymbol.getSpannedScope().getLocalTypeVarSymbols().isEmpty()) { + return createTypeObject(typeSymbol); + } + else { + return createGenerics(typeSymbol); + } + } + + public static SymTypeOfWildcard createWildcard(boolean isUpper, SymTypeExpression bound) { + return new SymTypeOfWildcard(bound, isUpper); + } + + public static SymTypeOfWildcard createWildcard() { + return createWildcard(false, null); + } + + public static SymTypeOfFunction createFunction(FunctionSymbol symbol) { + return symbol.getFunctionType(); + } + + public static SymTypeOfFunction createFunction(SymTypeExpression returnType) { + return createFunction(returnType, Lists.newArrayList()); + } + + public static SymTypeOfFunction createFunction(SymTypeExpression returnType, + List argumentTypes) { + return createFunction(returnType, argumentTypes, false); + } + + public static SymTypeOfFunction createFunction(SymTypeExpression returnType, + SymTypeExpression... argumentTypes) { + return createFunction(returnType, Arrays.asList(argumentTypes)); + } + + public static SymTypeOfFunction createFunction(SymTypeExpression returnType, + List argumentTypes, boolean elliptic) { + return createFunction(null, returnType, argumentTypes, elliptic); + } + + public static SymTypeOfFunction createFunction( + FunctionSymbol symbol, + SymTypeExpression returnType, + List argumentTypes, + boolean elliptic) { + return new SymTypeOfFunction(symbol, returnType, argumentTypes, elliptic); + } + + public static SymTypeOfUnion createUnion(Set unionizedTypes) { + return new SymTypeOfUnion(unionizedTypes); + } + + public static SymTypeOfUnion createUnion(SymTypeExpression... unionizedTypes) { + return createUnion(Arrays.stream(unionizedTypes).collect(Collectors.toSet())); + } + + public static SymTypeOfIntersection createIntersection(Set intersectedTypes) { + return new SymTypeOfIntersection(intersectedTypes); + } + + public static SymTypeOfIntersection createIntersection(SymTypeExpression... intersectedTypes) { + return createIntersection(Set.of(intersectedTypes)); + } + + /** + * @return a type which is the superType of ALL other types, + * NO further guarantees are made. + * Note, this is not the case for, + * e.g., Java's Object class as it is not a superType of primitives + * This is used internally in the TypeCheck. + */ + public static SymTypeExpression createTopType() { + // This is technically dependent on the type system in use. + // Here, use the fact that we support + // intersection types in our implementations + return createIntersection(); + } + + /** + * @return a type which is the subType of ALL other types, + * NO further guarantees are made. + * Note, this is not the case for, + * e.g., Java's null type as it is not a subType of primitives + * This is used internally in the TypeCheck. + */ + public static SymTypeExpression createBottomType() { + // This is technically dependent on the type system in use. + // Here, use the fact that we support + // intersection types in our implementations + return createUnion(); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeObscure.java b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeObscure.java new file mode 100644 index 0000000000..64caaadc49 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeObscure.java @@ -0,0 +1,86 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.symbols.basicsymbols._symboltable.FunctionSymbol; +import de.monticore.symbols.basicsymbols._symboltable.VariableSymbol; +import de.monticore.symboltable.modifiers.AccessModifier; +import de.monticore.types3.ISymTypeVisitor; + +import java.util.Collections; +import java.util.List; + +public class SymTypeObscure extends SymTypeExpression { + + @Override + public String print() { + return "Obscure"; + } + + @Override + public String printFullName() { + return "Obscure"; + } + + @Override + public SymTypeExpression deepClone() { + return new SymTypeObscure(); + } + + @Override + public boolean deepEquals(SymTypeExpression sym) { + return sym.isObscureType(); + } + + @Override + public boolean isValidType() { + return false; + } + + @Override + public boolean isObscureType() { + return true; + } + + @Override + public List getMethodList(String methodname, + boolean abstractTc, + AccessModifier modifier) { + return Collections.emptyList(); + } + + @Override + protected List getCorrectMethods(String methodName, + boolean outerIsType, + boolean abstractTc, + AccessModifier modifier) { + return Collections.emptyList(); + } + + @Override + public List getFieldList(String fieldName, + boolean abstractTc, + AccessModifier modifier) { + return Collections.emptyList(); + } + + @Override + public List getFieldList(String fieldName, + boolean outerIsType, + boolean abstractTc, + AccessModifier modifier) { + return Collections.emptyList(); + } + + @Override + protected List getCorrectFields(String fieldName, + boolean outerIsType, + boolean abstractTc, + AccessModifier modifier) { + return Collections.emptyList(); + } + + @Override + public void accept(ISymTypeVisitor visitor) { + visitor.visit(this); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfFunction.java b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfFunction.java new file mode 100644 index 0000000000..57c4f9b0ee --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfFunction.java @@ -0,0 +1,413 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.FunctionSymbol; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.symbols.basicsymbols._symboltable.TypeVarSymbol; +import de.monticore.types3.ISymTypeVisitor; +import de.se_rwth.commons.logging.Log; + +import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Optional; +import java.util.Spliterator; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; +import java.util.stream.Stream; + +/** + * SymTypeOfFunction stores any kind of Function, + * such as List::get, obj::getX, i -> i + 2 + */ +public class SymTypeOfFunction extends SymTypeExpression { + + /** + * @deprecated only required for the deprecated type symbol + */ + @Deprecated + public static final String TYPESYMBOL_NAME = "function"; + + /** + * Symbol of the function + * may not be present (e.g., for lambdas) + */ + protected Optional functionSymbol; + + /** + * Type of return value + * returned when the function is called + */ + protected SymTypeExpression returnType; + + /** + * List of argument types of the function + * e.g. "Integer f(Float t)" has "Float" as its argument type + * a this-pointer is the first argument + */ + protected List argumentTypes; + + /** + * Whether the function supports varargs + * e.g. {@code Integer f(Float... t)} + */ + protected boolean elliptic; + + /** + * Constructor with all parameters that are stored: + * FunctionSymbol can be null + */ + public SymTypeOfFunction( + FunctionSymbol functionSymbol, + SymTypeExpression returnType, + List argumentTypes, + boolean elliptic) { + super.typeSymbol = new TypeSymbol(TYPESYMBOL_NAME); + super.typeSymbol.setEnclosingScope(BasicSymbolsMill.scope()); + super.typeSymbol.setSpannedScope(BasicSymbolsMill.scope()); + this.functionSymbol = Optional.ofNullable(functionSymbol); + this.returnType = returnType; + this.argumentTypes = argumentTypes; + this.elliptic = elliptic; + } + + /** + * @deprecated the other constructor is to be used + */ + @Deprecated + public SymTypeOfFunction(SymTypeExpression returnType, List argumentTypes, + boolean elliptic) { + this(null, returnType, argumentTypes, elliptic); + } + + /** + * print: Umwandlung in einen kompakten String + */ + @Override + public String print() { + final StringBuilder r = new StringBuilder(); + r.append("("); + for (int i = 0; i < argumentTypes.size(); i++) { + r.append(argumentTypes.get(i).print()); + if (i < argumentTypes.size() - 1) { + r.append(", "); + } + else if (isElliptic()) { + r.append("..."); + } + } + r.append(")"); + r.append(" -> "); + r.append(returnType.print()); + return r.toString(); + } + + @Override + public String printFullName() { + final StringBuilder r = new StringBuilder(); + r.append("("); + for (int i = 0; i < argumentTypes.size(); i++) { + r.append(argumentTypes.get(i).printFullName()); + if (i < argumentTypes.size() - 1) { + r.append(", "); + } + else if (isElliptic()) { + r.append("..."); + } + } + r.append(")"); + r.append(" -> "); + r.append(returnType.printFullName()); + return r.toString(); + } + + @Override + public boolean isFunctionType() { + return true; + } + + @Override + public boolean deepEquals(SymTypeExpression sym) { + if (!deepEqualsSignature(sym)) { + return false; + } + SymTypeOfFunction symFun = (SymTypeOfFunction) sym; + if (!getType().deepEquals(symFun.getType())) { + return false; + } + return true; + } + + /** + * deepEquals, but ignoring the return type + * s.a. Java Spec 20 8.4.2 + */ + public boolean deepEqualsSignature(SymTypeExpression other) { + if (!other.isFunctionType()) { + return false; + } + SymTypeOfFunction symFun = (SymTypeOfFunction) other; + if (this.sizeArgumentTypes() != symFun.sizeArgumentTypes()) { + return false; + } + for (int i = 0; i < this.sizeArgumentTypes(); i++) { + if (!this.getArgumentType(i).deepEquals(symFun.getArgumentType(i))) { + return false; + } + } + if (isElliptic() != symFun.isElliptic()) { + return false; + } + return true; + } + + /** + * @return the return type of the function + * NOT the actual type of the function itself + */ + public SymTypeExpression getType() { + return returnType; + } + + /** + * iff true, the last argument type is accepted any amount of times + */ + public boolean isElliptic() { + return elliptic; + } + + public void setElliptic(boolean elliptic) { + this.elliptic = elliptic; + } + + public boolean hasSymbol() { + return functionSymbol.isPresent(); + } + + public FunctionSymbol getSymbol() { + if (!hasSymbol()) { + Log.error("0xFD712 internal error: " + + "tried to get non existing function symbol" + ); + } + return functionSymbol.get(); + } + + /** + * returns whether the specified amount of arguments can be accepted + * E.g., (P, int...) -> int can accept 1,2,3,... arguments, but not 0 + */ + protected boolean canHaveArity(int arity) { + return ((isElliptic() && sizeArgumentTypes() - 1 <= arity) + || sizeArgumentTypes() == arity); + } + + /** + * returns a clone, with the arity fixed to the specified number + * E.g., (P, int...) -> int with arity of 3 is (P, int, int) -> int + */ + public SymTypeOfFunction getWithFixedArity(int arity) { + SymTypeOfFunction clone = (SymTypeOfFunction) deepClone(); + if (canHaveArity(arity)) { + for (int i = sizeArgumentTypes(); i < arity; i++) { + clone.addArgumentType( + getArgumentType(sizeArgumentTypes() - 1).deepClone() + ); + } + clone.setElliptic(false); + } + else { + Log.error("0xFD2A1 internal error: " + + "the arity of a function of type " + + clone.printFullName() + + " cannot be fixed to " + arity + ); + } + return clone; + } + + public Map getTypeVariableReplaceMap() { + Map replaceMap = new HashMap<>(); + if (hasSymbol()) { + Map symbol2instance + = new HashMap<>(); + symbol2instance.put(getSymbol().getType(), getType()); + for (int i = 0; i < getSymbol().getFunctionType().sizeArgumentTypes() + && i < sizeArgumentTypes(); i++) { + symbol2instance.put( + getSymbol().getFunctionType().getArgumentType(i), + getArgumentType(i) + ); + } + for (SymTypeExpression symbolExpr : symbol2instance.keySet()) { + if (symbolExpr.isTypeVariable() + && !symbolExpr.deepEquals(symbol2instance.get(symbolExpr)) + ) { + replaceMap.put( + ((SymTypeVariable) symbolExpr).getTypeVarSymbol(), + symbol2instance.get(symbolExpr) + ); + } + } + } + return replaceMap; + } + + @Override + public void accept(ISymTypeVisitor visitor) { + visitor.visit(this); + } + + // -------------------------------------------------------------------------- + // From here on: Standard functionality to access the list of arguments; + // (was copied from a created class) + // (and demonstrates that we still can optimize our generators & build processes) + // -------------------------------------------------------------------------- + + public boolean containsArgumentType(Object element) { + return this.getArgumentTypeList().contains(element); + } + + public boolean containsAllArgumentTypes(Collection collection) { + return this.getArgumentTypeList().containsAll(collection); + } + + public boolean isEmptyArgumentTypes() { + return this.getArgumentTypeList().isEmpty(); + } + + public Iterator iteratorArgumentTypes() { + return this.getArgumentTypeList().iterator(); + } + + public int sizeArgumentTypes() { + return this.getArgumentTypeList().size(); + } + + public SymTypeExpression[] toArrayArgumentTypes(SymTypeExpression[] array) { + return this.getArgumentTypeList().toArray(array); + } + + public Object[] toArrayArgumentTypes() { + return this.getArgumentTypeList().toArray(); + } + + public Spliterator spliteratorArgumentTypes() { + return this.getArgumentTypeList().spliterator(); + } + + public Stream streamArgumentTypes() { + return this.getArgumentTypeList().stream(); + } + + public Stream parallelStreamArgumentTypes() { + return this.getArgumentTypeList().parallelStream(); + } + + public SymTypeExpression getArgumentType(int index) { + if (this.isElliptic() && index >= getArgumentTypeList().size()) { + return this.getArgumentTypeList().get(getArgumentTypeList().size() - 1); + } + return this.getArgumentTypeList().get(index); + } + + public int indexOfArgumentType(Object element) { + return this.getArgumentTypeList().indexOf(element); + } + + public int lastIndexOfArgumentType(Object element) { + return this.getArgumentTypeList().lastIndexOf(element); + } + + public boolean equalsArgumentTypeTypes(Object o) { + return this.getArgumentTypeList().equals(o); + } + + public int hashCodeArgumentTypes() { + return this.getArgumentTypeList().hashCode(); + } + + public ListIterator listIteratorArgumentTypes() { + return this.getArgumentTypeList().listIterator(); + } + + public ListIterator listIteratorArgumentTypes(int index) { + return this.getArgumentTypeList().listIterator(index); + } + + public List subListArgumentTypes(int start, int end) { + return this.getArgumentTypeList().subList(start, end); + } + + public List getArgumentTypeList() { + return this.argumentTypes; + } + + public void clearArgumentTypes() { + this.getArgumentTypeList().clear(); + } + + public boolean addArgumentType(SymTypeExpression element) { + return this.getArgumentTypeList().add(element); + } + + public boolean addAllArgumentTypes(Collection collection) { + return this.getArgumentTypeList().addAll(collection); + } + + public boolean removeArgumentType(Object element) { + return this.getArgumentTypeList().remove(element); + } + + public boolean removeAllArgumentTypes(Collection collection) { + return this.getArgumentTypeList().removeAll(collection); + } + + public boolean retainAllArgumentTypes(Collection collection) { + return this.getArgumentTypeList().retainAll(collection); + } + + public boolean removeIfArgumentType(Predicate filter) { + return this.getArgumentTypeList().removeIf(filter); + } + + public void forEachArgumentTypes(Consumer action) { + this.getArgumentTypeList().forEach(action); + } + + public void addArgumentType(int index, SymTypeExpression element) { + this.getArgumentTypeList().add(index, element); + } + + public boolean addAllArgumentTypes(int index, + Collection collection) { + return this.getArgumentTypeList().addAll(index, collection); + } + + public SymTypeExpression removeArgumentType(int index) { + return this.getArgumentTypeList().remove(index); + } + + public SymTypeExpression setArgumentType(int index, SymTypeExpression element) { + return this.getArgumentTypeList().set(index, element); + } + + public void replaceAllArgumentTypes(UnaryOperator operator) { + this.getArgumentTypeList().replaceAll(operator); + } + + public void sortArgumentTypes(Comparator comparator) { + this.getArgumentTypeList().sort(comparator); + } + + public void setArgumentTypeList(List argumentTypes) { + this.argumentTypes = argumentTypes; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfFunctionDeSer.java b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfFunctionDeSer.java new file mode 100644 index 0000000000..005c2648b2 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfFunctionDeSer.java @@ -0,0 +1,54 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.symboltable.serialization.JsonDeSers; +import de.monticore.symboltable.serialization.JsonParser; +import de.monticore.symboltable.serialization.JsonPrinter; +import de.monticore.symboltable.serialization.json.JsonObject; +import de.se_rwth.commons.logging.Log; + +import java.util.List; + +public class SymTypeOfFunctionDeSer { + + // Care: the following String needs to be adapted if the package was renamed + public static final String SERIALIZED_KIND = "de.monticore.types.check.SymTypeOfFunction"; + protected static final String SERIALIZED_RETURNTYPE = "returnType"; + protected static final String SERIALIZED_ARGUMENTTYPES = "argumentTypes"; + protected static final String SERIALIZED_ELLIPTIC = "elliptic"; + + public String serialize(SymTypeOfFunction toSerialize) { + JsonPrinter jp = new JsonPrinter(); + jp.beginObject(); + jp.member(JsonDeSers.KIND, SERIALIZED_KIND); + SymTypeExpressionDeSer.serializeMember(jp, SERIALIZED_RETURNTYPE, toSerialize.getType()); + SymTypeExpressionDeSer.serializeMember(jp, SERIALIZED_ARGUMENTTYPES, + toSerialize.getArgumentTypeList()); + jp.member(SERIALIZED_ELLIPTIC, toSerialize.elliptic); + jp.endObject(); + return jp.getContent(); + } + + public SymTypeOfFunction deserialize(String serialized) { + return deserialize(JsonParser.parseJsonObject(serialized)); + } + + public SymTypeOfFunction deserialize(JsonObject serialized) { + if (serialized.hasMember(SERIALIZED_RETURNTYPE)) { + SymTypeExpression returnType = SymTypeExpressionDeSer + .deserializeMember(SERIALIZED_RETURNTYPE, serialized); + List arguments = + SymTypeExpressionDeSer.deserializeListMember(SERIALIZED_ARGUMENTTYPES, serialized); + boolean isElliptic = serialized + .getBooleanMemberOpt(SERIALIZED_ELLIPTIC) + .orElse(false); + return SymTypeExpressionFactory.createFunction(returnType, arguments, isElliptic); + } + Log.error( + "0x9E2F6 Internal error: Loading ill-structured SymTab: missing " + + SERIALIZED_RETURNTYPE + + "of SymTypeOfFunction " + + serialized); + return null; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfGenerics.java b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfGenerics.java new file mode 100644 index 0000000000..9c1dc60e63 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfGenerics.java @@ -0,0 +1,425 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbolSurrogate; +import de.monticore.symbols.basicsymbols._symboltable.TypeVarSymbol; +import de.monticore.types3.ISymTypeVisitor; +import de.se_rwth.commons.logging.Log; + +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; +import java.util.stream.Stream; + +/** + * SymTypeOfGenerics stores any kind of TypeConstructor applied + * to Arguments, such as Map< int,Person > + * List, List< Set< List< a >>>. + * This subsumes all kinds of generic Types from several of the + * MC-Type grammars. + */ +public class SymTypeOfGenerics extends SymTypeExpression { + + /** + * Map for unboxing generic types (e.g. "java.util.Collection" -> "Collection") + */ + @Deprecated + public static final Map unboxMap; + + /** + * Map for boxing generic types (e.g. "Collection" -> "java.util.Collection") + * Results are fully qualified. + */ + @Deprecated + public static final Map boxMap; + + /** + * initializing the maps + */ + static { + Map unboxMap_temp = new HashMap(); + unboxMap_temp.put("java.util.Optional", "Optional"); + unboxMap_temp.put("java.util.Set", "Set"); + unboxMap_temp.put("java.util.List", "List"); + unboxMap_temp.put("java.util.Map","Map"); + unboxMap = Collections.unmodifiableMap(unboxMap_temp); + + Map boxMap_temp = new HashMap(); + boxMap_temp.put("Optional", "java.util.Optional"); + boxMap_temp.put("Set", "java.util.Set"); + boxMap_temp.put("List", "java.util.List"); + boxMap_temp.put("Map","java.util.Map"); + boxMap = Collections.unmodifiableMap(boxMap_temp); + } + + /** + * unboxing generic types (e.g. "java.util.Collection" -> "Collection"). + * otherwise return is unchanged + * @deprecated use SymTypeUnboxingVisitor + * @param type + * @return + */ + @Deprecated + public static String unbox(SymTypeOfGenerics type){ + List arguments = type.getArgumentList(); + StringBuilder r = new StringBuilder().append('<'); + for (int i = 0; i < arguments.size(); i++) { + if (arguments.get(i).isGenericType()) { + r.append(unbox((SymTypeOfGenerics) arguments.get(i))); + } + else { + r.append(SymTypePrimitive.unbox(arguments.get(i).print())); + } + if (i < arguments.size() - 1) { + r.append(','); + } + } + r.append(">"); + if (unboxMap.containsKey(type.printTypeWithoutTypeArgument())) { + return unboxMap.get(type.printTypeWithoutTypeArgument()) + r.toString(); + } + else { + return type.printTypeWithoutTypeArgument() + r.toString(); + } + } + + /** + * Boxing generic types (e.g. "Collection" -> "java.util.Collection") + * Results are fully qualified. + * Otherwise return is unchanged + * @deprecated use SymtypeBoxingVisitor + * @param type + * @return + */ + @Deprecated + public static String box(SymTypeOfGenerics type){ + List arguments = type.getArgumentList(); + StringBuilder r = new StringBuilder().append('<'); + for (int i = 0; i < arguments.size(); i++) { + if (arguments.get(i).isGenericType()) { + r.append(box((SymTypeOfGenerics) arguments.get(i))); + } + else { + r.append(SymTypePrimitive.box(arguments.get(i).print())); + } + if (i < arguments.size() - 1) { + r.append(','); + } + } + r.append(">"); + if (boxMap.containsKey(type.printTypeWithoutTypeArgument())) { + return boxMap.get(type.printTypeWithoutTypeArgument()) + r.toString(); + } + else { + return type.printTypeWithoutTypeArgument() + r.toString(); + } + } + + protected TypeSymbol typeSymbol; + + /** + * List of arguments of a type constructor + */ + protected List arguments; + + /** + * @deprecated use SymTypeExpressionFactory + * The Factory then uses the constructor below + */ + @Deprecated + public SymTypeOfGenerics(TypeSymbol typeSymbol) { + this(typeSymbol, new LinkedList<>()); + } + + /** + * Constructor with all parameters that are stored: + */ + public SymTypeOfGenerics(TypeSymbol typeSymbol, List arguments) { + this.typeSymbol = typeSymbol; + this.arguments = arguments; + } + + @Override + public boolean hasTypeInfo() { + return typeSymbol != null; + } + + @Override + public TypeSymbol getTypeInfo() { + return typeSymbol; + } + + @Override + public String print() { + StringBuffer r = new StringBuffer(typeSymbol.getName()).append('<'); + for(int i = 0; i').toString(); + } + + @Override + public String printFullName() { + StringBuffer r = new StringBuffer(getTypeConstructorFullName()).append('<'); + for(int i = 0; i').toString(); + } + + public String getTypeConstructorFullName() { + return getTypeInfo().getFullName(); + } + + /** + * @deprecated same as the the other 2 methods even in spec? + */ + @Deprecated + public String printTypeWithoutTypeArgument(){ + return this.getFullName(); + } + + /** + * @deprecated same as the the other 2 methods even in spec? + */ + @Deprecated + public String getFullName() { + return getTypeConstructorFullName(); + } + + /** + * getBaseName: get the unqualified Name (no ., no Package) + * @deprecated unused outside of tests, but not required for tests + */ + @Deprecated + public String getBaseName() { + String[] parts = getTypeConstructorFullName().split("\\."); + return parts[parts.length - 1]; + } + + @Override + public boolean isGenericType(){ + return true; + } + + @Override + public boolean deepEquals(SymTypeExpression sym){ + if(!sym.isGenericType()){ + return false; + } + SymTypeOfGenerics symGen = (SymTypeOfGenerics) sym; + if(!this.typeSymbol.getEnclosingScope().equals(symGen.typeSymbol.getEnclosingScope())){ + return false; + } + if(!this.typeSymbol.getName().equals(symGen.typeSymbol.getName())){ + return false; + } + if(this.sizeArguments()!=symGen.sizeArguments()){ + return false; + } + for(int i = 0;i getTypeVariableReplaceMap() { + List typeVars = getTypeInfo().getTypeParameterList(); + List arguments = getArgumentList(); + Map replaceMap = new HashMap<>(); + // empty List, e.g. new HashMap<>(); + if (arguments.size() == 0) { + // no-op + } + // otherwise, we expect the same amount of parameters as there are variables + // Java (and we) currently (Java Spec 20) do not support varargs type variables + else if (arguments.size() != typeVars.size()) { + Log.error("0xFD672 expected " + typeVars.size() + " parameters " + + "for " + getTypeInfo().getFullName() + + ", but got " + arguments.size() + + ": " + printFullName() + ); + }else { + for(int i = 0; i < typeVars.size(); i++) { + replaceMap.put(typeVars.get(i), arguments.get(i)); + } + } + return replaceMap; + } + + @Override + public void replaceTypeVariables(Map replaceMap) { + for(int i = 0; i typeVar = replaceMap.keySet().stream().filter(t -> t.getName().equals(realTypeInfo.getName())).findAny(); + if(typeVar.isPresent()){ + List args = new ArrayList<>(getArgumentList()); + args.remove(type); + args.add(i, replaceMap.get(typeVar.get())); + this.setArgumentList(args); + } + }else{ + type.replaceTypeVariables(replaceMap); + } + } + } + + @Override + public void accept(ISymTypeVisitor visitor) { + visitor.visit(this); + } + + // -------------------------------------------------------------------------- + // From here on: Standard functionality to access the list of arguments + // (was copied from a created class) + // (and demonstrates that we still can optimize our generators & build processes) + // -------------------------------------------------------------------------- + + public boolean containsArgument (Object element) { + return this.getArgumentList().contains(element); + } + + public boolean containsAllArguments (Collection collection) { + return this.getArgumentList().containsAll(collection); + } + + public boolean isEmptyArguments () { + return this.getArgumentList().isEmpty(); + } + + public Iterator iteratorArguments () { + return this.getArgumentList().iterator(); + } + + public int sizeArguments () { + return this.getArgumentList().size(); + } + + public de.monticore.types.check.SymTypeExpression[] toArrayArguments (de.monticore.types.check.SymTypeExpression[] array) { + return this.getArgumentList().toArray(array); + } + + public Object[] toArrayArguments () { + return this.getArgumentList().toArray(); + } + + public Spliterator spliteratorArguments () { + return this.getArgumentList().spliterator(); + } + + public Stream streamArguments () { + return this.getArgumentList().stream(); + } + + public Stream parallelStreamArguments () { + return this.getArgumentList().parallelStream(); + } + + public de.monticore.types.check.SymTypeExpression getArgument (int index) { + return this.getArgumentList().get(index); + } + + public int indexOfArgument (Object element) { + return this.getArgumentList().indexOf(element); + } + + public int lastIndexOfArgument (Object element) { + return this.getArgumentList().lastIndexOf(element); + } + + public boolean equalsArguments (Object o) { + return this.getArgumentList().equals(o); + } + + public int hashCodeArguments () { + return this.getArgumentList().hashCode(); + } + + public ListIterator listIteratorArguments () { + return this.getArgumentList().listIterator(); + } + + public ListIterator listIteratorArguments (int index) { + return this.getArgumentList().listIterator(index); + } + + public List subListArguments (int start,int end) { + return this.getArgumentList().subList(start, end); + } + + public List getArgumentList () { + return this.arguments; + } + + public void clearArguments () { + this.getArgumentList().clear(); + } + + public boolean addArgument (de.monticore.types.check.SymTypeExpression element) { + return this.getArgumentList().add(element); + } + + public boolean addAllArguments (Collection collection) { + return this.getArgumentList().addAll(collection); + } + public boolean removeArgument (Object element) { + return this.getArgumentList().remove(element); + } + + public boolean removeAllArguments (Collection collection) { + return this.getArgumentList().removeAll(collection); + } + public boolean retainAllArguments (Collection collection) { + return this.getArgumentList().retainAll(collection); + } + + public boolean removeIfArgument (Predicate filter) { + return this.getArgumentList().removeIf(filter); + } + + public void forEachArguments (Consumer action) { + this.getArgumentList().forEach(action); + } + + public void addArgument (int index,de.monticore.types.check.SymTypeExpression element) { + this.getArgumentList().add(index, element); + } + + public boolean addAllArguments (int index,Collection collection) { + return this.getArgumentList().addAll(index, collection); + } + + public de.monticore.types.check.SymTypeExpression removeArgument (int index) { + return this.getArgumentList().remove(index); + } + + public de.monticore.types.check.SymTypeExpression setArgument (int index,de.monticore.types.check.SymTypeExpression element) { + return this.getArgumentList().set(index, element); + } + + public void replaceAllArguments (UnaryOperator operator) { + this.getArgumentList().replaceAll(operator); + } + + public void sortArguments (Comparator comparator) { + this.getArgumentList().sort(comparator); + } + + public void setArgumentList (List arguments) { + this.arguments = arguments; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfGenericsDeSer.java b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfGenericsDeSer.java new file mode 100644 index 0000000000..d73f142714 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfGenericsDeSer.java @@ -0,0 +1,52 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.IBasicSymbolsGlobalScope; +import de.monticore.symboltable.serialization.JsonDeSers; +import de.monticore.symboltable.serialization.JsonParser; +import de.monticore.symboltable.serialization.JsonPrinter; +import de.monticore.symboltable.serialization.json.JsonObject; +import de.se_rwth.commons.logging.Log; + +import java.util.List; + +public class SymTypeOfGenericsDeSer { + + // Care: the following String needs to be adapted if the package was renamed + public static final String SERIALIZED_KIND = "de.monticore.types.check.SymTypeOfGenerics"; + protected static final String SERIALIZED_TYPE_CONSTRUCTOR = "typeConstructorFullName"; + protected static final String SERIALIZED_ARGUMENTS = "arguments"; + + public String serialize(SymTypeOfGenerics toSerialize) { + JsonPrinter jp = new JsonPrinter(); + jp.beginObject(); + jp.member(JsonDeSers.KIND, SERIALIZED_KIND); + jp.member(SERIALIZED_TYPE_CONSTRUCTOR, toSerialize.getTypeConstructorFullName()); + SymTypeExpressionDeSer.serializeMember(jp, SERIALIZED_ARGUMENTS, toSerialize.getArgumentList()); + jp.endObject(); + return jp.getContent(); + } + + public SymTypeOfGenerics deserialize(String serialized) { + return deserialize(JsonParser.parseJsonObject(serialized)); + } + + public SymTypeOfGenerics deserialize(JsonObject serialized) { + if (serialized.hasStringMember(SERIALIZED_TYPE_CONSTRUCTOR) && + serialized.hasArrayMember(SERIALIZED_ARGUMENTS)) { + String typeConstructorFullName = serialized.getStringMember(SERIALIZED_TYPE_CONSTRUCTOR); + IBasicSymbolsGlobalScope gs = BasicSymbolsMill.globalScope(); + + List arguments = SymTypeExpressionDeSer + .deserializeListMember(SERIALIZED_ARGUMENTS, serialized); + + return SymTypeExpressionFactory + .createGenerics(typeConstructorFullName, gs, arguments); + } + Log.error( + "0x823F6 Internal error: Loading ill-structured SymTab: missing typeConstructorFullName of SymTypeOfGenerics " + + serialized); + return null; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfIntersection.java b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfIntersection.java new file mode 100644 index 0000000000..5498ede1e8 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfIntersection.java @@ -0,0 +1,179 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.types3.ISymTypeVisitor; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import java.util.Spliterator; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class SymTypeOfIntersection extends SymTypeExpression { + + /** + * Set of types within the intersection + */ + protected Set intersectedTypes; + + public SymTypeOfIntersection(Set types) { + this.intersectedTypes = new HashSet<>(types); + } + + @Override + public boolean isValidType() { + return streamIntersectedTypes().allMatch(SymTypeExpression::isValidType); + } + + public boolean isIntersectionType() { + return true; + } + + @Override + public String print() { + final StringBuilder r = new StringBuilder(); + r.append("("); + r.append(getIntersectedTypeSet().stream() + .map(SymTypeExpression::print) + // sorted to be deterministic + .sorted() + .collect(Collectors.joining(" & ")) + ); + r.append(")"); + return r.toString(); + } + + @Override + public String printFullName() { + final StringBuilder r = new StringBuilder(); + r.append("("); + r.append(getIntersectedTypeSet().stream() + .map(SymTypeExpression::printFullName) + // sorted to be deterministic + .sorted() + .collect(Collectors.joining(" & ")) + ); + r.append(")"); + return r.toString(); + } + + @Override + public boolean deepEquals(SymTypeExpression sym) { + if (!sym.isIntersectionType()) { + return false; + } + SymTypeOfIntersection other = (SymTypeOfIntersection) sym; + if (other.sizeIntersectedTypes() != this.sizeIntersectedTypes()) { + return false; + } + for (SymTypeExpression ownExpr : this.getIntersectedTypeSet()) { + if (!other.parallelStreamIntersectedTypes().anyMatch(ownExpr::deepEquals)) { + return false; + } + } + return true; + } + + @Override + public void accept(ISymTypeVisitor visitor) { + visitor.visit(this); + } + + // -------------------------------------------------------------------------- + // From here on: Functionality to access the set of types; + // -------------------------------------------------------------------------- + + public boolean containsIntersectedType(Object element) { + return this.getIntersectedTypeSet().contains(element); + } + + public boolean containsAllIntersectedTypes(Collection collection) { + return this.getIntersectedTypeSet().containsAll(collection); + } + + public boolean isEmptyIntersectedTypes() { + return this.getIntersectedTypeSet().isEmpty(); + } + + public Iterator iteratorIntersectedTypes() { + return this.getIntersectedTypeSet().iterator(); + } + + public int sizeIntersectedTypes() { + return this.getIntersectedTypeSet().size(); + } + + public SymTypeExpression[] toArrayIntersectedTypes( + SymTypeExpression[] array) { + return this.getIntersectedTypeSet().toArray(array); + } + + public Object[] toArrayIntersectedTypes() { + return this.getIntersectedTypeSet().toArray(); + } + + public Spliterator spliteratorIntersectedTypes() { + return this.getIntersectedTypeSet().spliterator(); + } + + public Stream streamIntersectedTypes() { + return this.getIntersectedTypeSet().stream(); + } + + public Stream parallelStreamIntersectedTypes() { + return this.getIntersectedTypeSet().parallelStream(); + } + + public boolean equalsIntersectedTypeTypes(Object o) { + return this.getIntersectedTypeSet().equals(o); + } + + public int hashCodeIntersectedTypes() { + return this.getIntersectedTypeSet().hashCode(); + } + + public Set getIntersectedTypeSet() { + return this.intersectedTypes; + } + + public void clearIntersectedTypes() { + this.getIntersectedTypeSet().clear(); + } + + public boolean addIntersectedType(SymTypeExpression element) { + return this.getIntersectedTypeSet().add(element); + } + + public boolean addAllIntersectedTypes(Collection collection) { + return this.getIntersectedTypeSet().addAll(collection); + } + + public boolean removeIntersectedType(Object element) { + return this.getIntersectedTypeSet().remove(element); + } + + public boolean removeAllIntersectedTypes(Collection collection) { + return this.getIntersectedTypeSet().removeAll(collection); + } + + public boolean retainAllIntersectedTypes(Collection collection) { + return this.getIntersectedTypeSet().retainAll(collection); + } + + public boolean removeIfIntersectedType(Predicate filter) { + return this.getIntersectedTypeSet().removeIf(filter); + } + + public void forEachIntersectedTypes(Consumer action) { + this.getIntersectedTypeSet().forEach(action); + } + + public void setIntersectedTypeSet(Set intersectedTypes) { + this.intersectedTypes = intersectedTypes; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfIntersectionDeSer.java b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfIntersectionDeSer.java new file mode 100644 index 0000000000..88354a43c7 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfIntersectionDeSer.java @@ -0,0 +1,52 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.symboltable.serialization.JsonDeSers; +import de.monticore.symboltable.serialization.JsonParser; +import de.monticore.symboltable.serialization.JsonPrinter; +import de.monticore.symboltable.serialization.json.JsonObject; +import de.se_rwth.commons.logging.Log; + +import java.util.HashSet; +import java.util.List; + +public class SymTypeOfIntersectionDeSer { + + // Care: the following String needs to be adapted if the package was renamed + public static final String SERIALIZED_KIND = "de.monticore.types.check.SymTypeOfIntersection"; + protected static final String SERIALIZED_TYPES = "intersectedTypes"; + + public String serialize(SymTypeOfIntersection toSerialize) { + JsonPrinter jp = new JsonPrinter(); + jp.beginObject(); + jp.member(JsonDeSers.KIND, SERIALIZED_KIND); + + jp.beginArray(SERIALIZED_TYPES); + toSerialize.getIntersectedTypeSet().stream() + .map(SymTypeExpression::printAsJson) + .sorted() + .forEach(jp::valueJson); + jp.endArray(); + + jp.endObject(); + return jp.getContent(); + } + + public SymTypeOfIntersection deserialize(String serialized) { + return deserialize(JsonParser.parseJsonObject(serialized)); + } + + public SymTypeOfIntersection deserialize(JsonObject serialized) { + if (serialized.hasMember(SERIALIZED_TYPES)) { + List intersectedTypesList = + SymTypeExpressionDeSer.deserializeListMember(SERIALIZED_TYPES, serialized); + return SymTypeExpressionFactory.createIntersection(new HashSet<>(intersectedTypesList)); + } + Log.error( + "0x9E2F7 Internal error: Loading ill-structured SymTab: missing " + + SERIALIZED_TYPES + + "of SymTypeOfIntersection " + + serialized); + return null; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfNull.java b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfNull.java new file mode 100644 index 0000000000..4d2cba14d3 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfNull.java @@ -0,0 +1,55 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbolSurrogate; +import de.monticore.types3.ISymTypeVisitor; + +public class SymTypeOfNull extends SymTypeExpression { + + /** + * This Class represents the type of the value "null". + * This type doesn't really exist (hence the print method delivers "nullType", i.e. _nullTypeString), + * but the object is used to attach "null" a proper type, + * which is then compatible to e.g. to TypeConstant or TypeArray, + * int[] j = null; ok + * Integer i2 = null; ok + * but not e.g. to int + * int i = null; illegal + * @deprecated contains no non-deprecated program logic + */ + @Deprecated + public SymTypeOfNull() { + typeSymbol = new TypeSymbolSurrogate(BasicSymbolsMill.NULL); + typeSymbol.setEnclosingScope(BasicSymbolsMill.scope()); + } + + /** + * print: Umwandlung in einen kompakten String + */ + @Override + public String print() { + return BasicSymbolsMill.NULL; + } + + @Override + public String printFullName(){ + return print(); + } + + @Override + public boolean isNullType() { + return true; + } + + @Override + public boolean deepEquals(SymTypeExpression sym){ + return sym.isNullType(); + } + + @Override + public void accept(ISymTypeVisitor visitor) { + visitor.visit(this); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfObject.java b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfObject.java new file mode 100644 index 0000000000..6565d02781 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfObject.java @@ -0,0 +1,102 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.types3.ISymTypeVisitor; + +/** + * An objectType is a full qualified class name. + * Therefore, we have the fullName, the baseName and the + * Symbol behind that full qualified class name to retrieve + */ +public class SymTypeOfObject extends SymTypeExpression { + + protected TypeSymbol typeSymbol; + + /** + * Constructor: with a TypeSymbolSurrogate that contains the name and enclosingScope + */ + public SymTypeOfObject(TypeSymbol typeSymbol) + { + this.typeSymbol = typeSymbol; + } + + @Override + public boolean hasTypeInfo() { + return typeSymbol != null; + } + + @Override + public TypeSymbol getTypeInfo() { + return typeSymbol; + } + + /** + * @deprecated questionable name: getter and setter do different things. + * one may add a getObjFullName() or similar if required + * also, seems unused in our main projects + */ + @Deprecated + public String getObjName() { + return typeSymbol.getFullName(); + } + + /** + * @deprecated unused in main projects + */ + @Deprecated + public void setObjName(String objname) { + this.typeSymbol.setName(objname); + } + + /** + * print: Umwandlung in einen kompakten String + */ + @Override + public String printFullName() { + return typeSymbol.getFullName(); + } + + @Override + public String print(){ + return typeSymbol.getName(); + } + + /** + * getBaseName: get the unqualified Name (no ., no Package) + * @deprecated unused outside of tests, but not required for tests + */ + @Deprecated + public String getBaseName() { + String[] parts = getObjName().split("\\."); + return parts[parts.length - 1]; + } + + @Override + public boolean isObjectType() { + return true; + } + + @Override + public boolean deepEquals(SymTypeExpression sym){ + if(!sym.isObjectType()){ + return false; + } + SymTypeOfObject symCon = (SymTypeOfObject) sym; + if(this.typeSymbol == null ||symCon.typeSymbol ==null){ + return false; + } + if(!this.typeSymbol.getEnclosingScope().equals(symCon.typeSymbol.getEnclosingScope())){ + return false; + } + if(!this.typeSymbol.getName().equals(symCon.typeSymbol.getName())){ + return false; + } + return true; + } + + @Override + public void accept(ISymTypeVisitor visitor) { + visitor.visit(this); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfObjectDeSer.java b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfObjectDeSer.java new file mode 100644 index 0000000000..4393772dcf --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfObjectDeSer.java @@ -0,0 +1,39 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symboltable.serialization.JsonDeSers; +import de.monticore.symboltable.serialization.JsonParser; +import de.monticore.symboltable.serialization.JsonPrinter; +import de.monticore.symboltable.serialization.json.JsonObject; +import de.se_rwth.commons.logging.Log; + +public class SymTypeOfObjectDeSer { + + // Care: the following String needs to be adapted if the package was renamed + public static final String SERIALIZED_KIND = "de.monticore.types.check.SymTypeOfObject"; + protected static final String SERIALIZED_OBJNAME = "objName"; + + public String serialize(SymTypeOfObject toSerialize) { + JsonPrinter jp = new JsonPrinter(); + jp.beginObject(); + jp.member(JsonDeSers.KIND, SERIALIZED_KIND); + jp.member(SERIALIZED_OBJNAME, toSerialize.getObjName()); + jp.endObject(); + return jp.getContent(); + } + + public SymTypeOfObject deserialize(String serialized) { + return deserialize(JsonParser.parseJsonObject(serialized)); + } + + public SymTypeOfObject deserialize(JsonObject serialized) { + if (serialized.hasStringMember(SERIALIZED_OBJNAME)) { + String objName = serialized.getStringMember(SERIALIZED_OBJNAME); + return SymTypeExpressionFactory.createTypeObject(objName, BasicSymbolsMill.globalScope()); + } + Log.error("0x823F4 Internal error: Cannot load \"" + + serialized + "\" as SymTypeOfObject!"); + return null; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfUnion.java b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfUnion.java new file mode 100644 index 0000000000..4a0f6f7246 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfUnion.java @@ -0,0 +1,187 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.types3.ISymTypeVisitor; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import java.util.Spliterator; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class SymTypeOfUnion extends SymTypeExpression { + + public static final String DEFAULT_TYPESYMBOL_NAME = "union"; + + /** + * Set of types within the union + */ + protected Set unionizedTypes; + + public SymTypeOfUnion(Set types) { + super.typeSymbol = new TypeSymbol(DEFAULT_TYPESYMBOL_NAME); + super.typeSymbol.setEnclosingScope(BasicSymbolsMill.globalScope()); + super.typeSymbol.setSpannedScope(BasicSymbolsMill.scope()); + this.unionizedTypes = new HashSet<>(types); + } + + @Override + public boolean isValidType() { + return streamUnionizedTypes().allMatch(SymTypeExpression::isValidType); + } + + @Override + public boolean isUnionType() { + return true; + } + + @Override + public String print() { + final StringBuilder r = new StringBuilder(); + r.append("("); + r.append(getUnionizedTypeSet().stream() + .map(SymTypeExpression::print) + // sorted to be deterministic + .sorted() + .collect(Collectors.joining(" | ")) + ); + r.append(")"); + return r.toString(); + } + + @Override + public String printFullName() { + final StringBuilder r = new StringBuilder(); + r.append("("); + r.append(getUnionizedTypeSet().stream() + .map(SymTypeExpression::printFullName) + // sorted to be deterministic + .sorted() + .collect(Collectors.joining(" | ")) + ); + r.append(")"); + return r.toString(); + } + + @Override + public boolean deepEquals(SymTypeExpression sym) { + if (!sym.isUnionType()) { + return false; + } + SymTypeOfUnion other = (SymTypeOfUnion) sym; + if (other.sizeUnionizedTypes() != this.sizeUnionizedTypes()) { + return false; + } + for (SymTypeExpression ownExpr : this.getUnionizedTypeSet()) { + if (!other.parallelStreamUnionizedTypes().anyMatch(ownExpr::deepEquals)) { + return false; + } + } + return true; + } + + @Override + public void accept(ISymTypeVisitor visitor) { + visitor.visit(this); + } + + // -------------------------------------------------------------------------- + // From here on: Functionality to access the set of types; + // -------------------------------------------------------------------------- + + public boolean containsUnionizedType(Object element) { + return this.getUnionizedTypeSet().contains(element); + } + + public boolean containsAllUnionizedTypes(Collection collection) { + return this.getUnionizedTypeSet().containsAll(collection); + } + + public boolean isEmptyUnionizedTypes() { + return this.getUnionizedTypeSet().isEmpty(); + } + + public Iterator iteratorUnionizedTypes() { + return this.getUnionizedTypeSet().iterator(); + } + + public int sizeUnionizedTypes() { + return this.getUnionizedTypeSet().size(); + } + + public SymTypeExpression[] toArrayUnionizedTypes( + SymTypeExpression[] array) { + return this.getUnionizedTypeSet().toArray(array); + } + + public Object[] toArrayUnionizedTypes() { + return this.getUnionizedTypeSet().toArray(); + } + + public Spliterator spliteratorUnionizedTypes() { + return this.getUnionizedTypeSet().spliterator(); + } + + public Stream streamUnionizedTypes() { + return this.getUnionizedTypeSet().stream(); + } + + public Stream parallelStreamUnionizedTypes() { + return this.getUnionizedTypeSet().parallelStream(); + } + + public boolean equalsUnionizedTypeTypes(Object o) { + return this.getUnionizedTypeSet().equals(o); + } + + public int hashCodeUnionizedTypes() { + return this.getUnionizedTypeSet().hashCode(); + } + + public Set getUnionizedTypeSet() { + return this.unionizedTypes; + } + + public void clearUnionizedTypes() { + this.getUnionizedTypeSet().clear(); + } + + public boolean addUnionizedType(SymTypeExpression element) { + return this.getUnionizedTypeSet().add(element); + } + + public boolean addAllUnionizedTypes(Collection collection) { + return this.getUnionizedTypeSet().addAll(collection); + } + + public boolean removeUnionizedType(Object element) { + return this.getUnionizedTypeSet().remove(element); + } + + public boolean removeAllUnionizedTypes(Collection collection) { + return this.getUnionizedTypeSet().removeAll(collection); + } + + public boolean retainAllUnionizedTypes(Collection collection) { + return this.getUnionizedTypeSet().retainAll(collection); + } + + public boolean removeIfUnionizedType(Predicate filter) { + return this.getUnionizedTypeSet().removeIf(filter); + } + + public void forEachUnionizedTypes(Consumer action) { + this.getUnionizedTypeSet().forEach(action); + } + + public void setUnionizedTypeSet(Set unionizedTypes) { + this.unionizedTypes = unionizedTypes; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfUnionDeSer.java b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfUnionDeSer.java new file mode 100644 index 0000000000..07188e2320 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfUnionDeSer.java @@ -0,0 +1,52 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.symboltable.serialization.JsonDeSers; +import de.monticore.symboltable.serialization.JsonParser; +import de.monticore.symboltable.serialization.JsonPrinter; +import de.monticore.symboltable.serialization.json.JsonObject; +import de.se_rwth.commons.logging.Log; + +import java.util.HashSet; +import java.util.List; + +public class SymTypeOfUnionDeSer { + + // Care: the following String needs to be adapted if the package was renamed + public static final String SERIALIZED_KIND = "de.monticore.types.check.SymTypeOfUnion"; + protected static final String SERIALIZED_TYPES = "unionizedTypes"; + + public String serialize(SymTypeOfUnion toSerialize) { + JsonPrinter jp = new JsonPrinter(); + jp.beginObject(); + jp.member(JsonDeSers.KIND, SERIALIZED_KIND); + + jp.beginArray(SERIALIZED_TYPES); + toSerialize.getUnionizedTypeSet().stream() + .map(SymTypeExpression::printAsJson) + .sorted() + .forEach(jp::valueJson); + jp.endArray(); + + jp.endObject(); + return jp.getContent(); + } + + public SymTypeOfUnion deserialize(String serialized) { + return deserialize(JsonParser.parseJsonObject(serialized)); + } + + public SymTypeOfUnion deserialize(JsonObject serialized) { + if (serialized.hasMember(SERIALIZED_TYPES)) { + List unionizedTypesList = + SymTypeExpressionDeSer.deserializeListMember(SERIALIZED_TYPES, serialized); + return SymTypeExpressionFactory.createUnion(new HashSet<>(unionizedTypesList)); + } + Log.error( + "0x9E2F7 Internal error: Loading ill-structured SymTab: missing " + + SERIALIZED_TYPES + + "of SymTypeOfUnion " + + serialized); + return null; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfWildcard.java b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfWildcard.java new file mode 100644 index 0000000000..47a8c686ff --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfWildcard.java @@ -0,0 +1,141 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.types3.ISymTypeVisitor; + +import java.util.Optional; + +public class SymTypeOfWildcard extends SymTypeExpression { + + protected Optional bound; + protected boolean isUpper; + + /** + * @deprecated use the Factory, + * the Factory uses the constructor below + */ + @Deprecated + public SymTypeOfWildcard(){ + } + + /** + * To be used by the SymTypeExpressionFactory + */ + public SymTypeOfWildcard(SymTypeExpression bound, boolean isUpper){ + this.bound = Optional.ofNullable(bound); + this.isUpper = isUpper; + } + + public boolean hasBound() { + return !bound.isEmpty(); + } + + public SymTypeExpression getBound() { + if(hasBound()){ + return bound.get(); + } + else { + // deprecated behaviour -> reduce null-checks in code + // this behaviour seems unused in our main projects (except tests) + // later: replace with log.error + return null; + } + } + + public boolean isUpper() { + return isUpper; + } + + @Override + public String print() { + if (!hasBound()) { + return "?"; + } + else if (isUpper()) { + return "? extends " + getBound().print(); + } + else { + return "? super " + getBound().print(); + } + } + + @Override + public String printFullName() { + if (!hasBound()) { + return "?"; + } + else if (isUpper()) { + return "? extends " + getBound().printFullName(); + } + else { + return "? super " + getBound().printFullName(); + } + } + + @Override + public SymTypeOfWildcard deepClone() { + SymTypeOfWildcard clone; + if (hasBound()) { + clone = new SymTypeOfWildcard(getBound().deepClone(), isUpper()); + } + else { + clone = new SymTypeOfWildcard(null, false); + } + clone.typeSymbol = this.typeSymbol; + clone.functionList = this.functionList; + return clone; + } + + @Override + public boolean isValidType() { + return false; + } + + @Override + public boolean isWildcard() { + return true; + } + + @Override + public boolean deepEquals(SymTypeExpression sym){ + //supporting deprecated code: + if(typeSymbol != null) { + if(!(sym instanceof SymTypeOfWildcard)){ + return false; + } + SymTypeOfWildcard symWil = (SymTypeOfWildcard) sym; + if(this.isUpper()!=symWil.isUpper()){ + return false; + } + if((this.getBound()==null && symWil.getBound()!=null) || (this.getBound()!=null && symWil.getBound()==null)){ + return false; + } + if(this.getBound()!=null && symWil.getBound()!=null && !this.getBound().deepEquals(symWil.getBound())){ + return false; + } + return this.print().equals(symWil.print()); + } + if (!sym.isWildcard()) { + return false; + } + SymTypeOfWildcard symWil = (SymTypeOfWildcard) sym; + if (hasBound() != symWil.hasBound()) { + return false; + } + if (hasBound()) { + if (!getBound().deepEquals(symWil.getBound())) { + return false; + } + if (isUpper() != symWil.isUpper()) { + return false; + } + } + return true; + } + + @Override + public void accept(ISymTypeVisitor visitor) { + visitor.visit(this); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfWildcardDeSer.java b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfWildcardDeSer.java new file mode 100644 index 0000000000..ec5b6d2e31 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeOfWildcardDeSer.java @@ -0,0 +1,43 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.symboltable.serialization.JsonDeSers; +import de.monticore.symboltable.serialization.JsonParser; +import de.monticore.symboltable.serialization.JsonPrinter; +import de.monticore.symboltable.serialization.json.JsonObject; + +public class SymTypeOfWildcardDeSer { + + // Care: the following String needs to be adapted if the package was renamed + public static final String SERIALIZED_KIND = "de.monticore.types.check.SymTypeOfWildcard"; + protected static final String SERIALIZED_ISUPPER = "isUpper"; + protected static final String SERIALIZED_BOUND = "bound"; + + public String serialize(SymTypeOfWildcard toSerialize) { + JsonPrinter jp = new JsonPrinter(); + jp.beginObject(); + jp.member(JsonDeSers.KIND, SERIALIZED_KIND); + if(toSerialize.hasBound()) { + jp.member(SERIALIZED_ISUPPER, toSerialize.isUpper()); + SymTypeExpressionDeSer.serializeMember(jp, SERIALIZED_BOUND, toSerialize.getBound()); + } + jp.endObject(); + return jp.getContent(); + } + + public SymTypeOfWildcard deserialize(String serialized) { + return deserialize(JsonParser.parseJsonObject(serialized)); + } + + public SymTypeOfWildcard deserialize(JsonObject serialized) { + if (serialized.hasMember(SERIALIZED_BOUND)) { + // isUpper == false iff. value is not serialized + boolean isUpper = serialized.hasBooleanMember(SERIALIZED_ISUPPER); + SymTypeExpression bound = SymTypeExpressionDeSer + .deserializeMember(SERIALIZED_BOUND, serialized); + return SymTypeExpressionFactory.createWildcard(isUpper, bound); + } + return SymTypeExpressionFactory.createWildcard(); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/SymTypePrimitive.java b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypePrimitive.java new file mode 100644 index 0000000000..897243fb93 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypePrimitive.java @@ -0,0 +1,216 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.types3.ISymTypeVisitor; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Collections; + +public class SymTypePrimitive extends SymTypeExpression { + + protected TypeSymbol typeSymbol; + + public SymTypePrimitive(TypeSymbol typeSymbol) { + this.typeSymbol = typeSymbol; + } + + @Override + public boolean hasTypeInfo() { + return typeSymbol != null; + } + + @Override + public TypeSymbol getTypeInfo() { + return typeSymbol; + } + + public String getPrimitiveName() { + return typeSymbol.getName(); + } + + public String getBoxedPrimitiveName() { + return box(typeSymbol.getName()); + } + + /** + * @deprecated only used in 1 test ONCE... in our main projects + */ + @Deprecated + public String getBaseOfBoxedName() { + String[] parts = box(typeSymbol.getName()).split("\\."); + return parts[parts.length - 1]; + } + + /** + * @deprecated only used in tests in our main projects + */ + @Deprecated + public void setPrimitiveName(String constName){ + typeSymbol.setName(constName); + } + + @Override + public String print() { + return getPrimitiveName(); + } + + @Override + public String printFullName(){ + return print(); + } + + /** + * List of potential constants + * (on purpose not implemented as enum) + */ + public static final List primitiveTypes = + Collections.unmodifiableList(Arrays.asList( + BasicSymbolsMill.BOOLEAN, + BasicSymbolsMill.BYTE, + BasicSymbolsMill.CHAR, + BasicSymbolsMill.SHORT, + BasicSymbolsMill.INT, + BasicSymbolsMill.LONG, + BasicSymbolsMill.FLOAT, + BasicSymbolsMill.DOUBLE, + //deprecated: use SymTypeOfVoid + BasicSymbolsMill.VOID + )); + + /** + * Map for unboxing const types (e.g. "java.lang.Boolean" -> "boolean") + */ + @Deprecated + public static final Map unboxMap; + + /** + * Map for boxing const types (e.g. "boolean" -> "java.lang.Boolean") + * Results are fully qualified. + */ + @Deprecated + public static final Map boxMap; + + /** + * initializing the maps + */ + static { + Map unboxMap_temp = new HashMap(); + unboxMap_temp.put("java.lang.Boolean", "boolean"); + unboxMap_temp.put("java.lang.Byte", "byte"); + unboxMap_temp.put("java.lang.Character", "char"); + unboxMap_temp.put("java.lang.Short", "short"); + unboxMap_temp.put("java.lang.Integer", "int"); + unboxMap_temp.put("java.lang.Long", "long"); + unboxMap_temp.put("java.lang.Float", "float"); + unboxMap_temp.put("java.lang.Double", "double"); + //deprecated: String is not expected + unboxMap_temp.put("java.lang.String", "String"); + unboxMap_temp.put("Boolean", "boolean"); + unboxMap_temp.put("Byte", "byte"); + unboxMap_temp.put("Character", "char"); + unboxMap_temp.put("Short", "short"); + unboxMap_temp.put("Integer", "int"); + unboxMap_temp.put("Long", "long"); + unboxMap_temp.put("Float", "float"); + unboxMap_temp.put("Double", "double"); + unboxMap = Collections.unmodifiableMap(unboxMap_temp); + + Map boxMap_temp = new HashMap(); + boxMap_temp.put("boolean", "java.lang.Boolean"); + boxMap_temp.put("byte", "java.lang.Byte"); + boxMap_temp.put("char", "java.lang.Character"); + boxMap_temp.put("double", "java.lang.Double"); + boxMap_temp.put("float", "java.lang.Float"); + boxMap_temp.put("int", "java.lang.Integer"); + boxMap_temp.put("long", "java.lang.Long"); + boxMap_temp.put("short", "java.lang.Short"); + //deprecated: String is not expected + boxMap_temp.put("String", "java.lang.String"); + boxMap = Collections.unmodifiableMap(boxMap_temp); + } + + /** + * unboxing const types (e.g. "java.lang.Boolean" -> "boolean"). + * otherwise return is unchanged + * @deprecated use SymTypeUnboxingVisitor + * @param boxedName + * @return + */ + @Deprecated + public static String unbox(String boxedName) { + if (unboxMap.containsKey(boxedName)) + return unboxMap.get(boxedName); + else + return boxedName; + } + + /** + * Boxing const types (e.g. "boolean" -> "java.lang.Boolean") + * Results are fully qualified. + * Otherwise return is unchanged + * @deprecated use SymTypeBoxingVisitor + * @param unboxedName + * @return + */ + @Deprecated + public static String box(String unboxedName) { + if (boxMap.containsKey(unboxedName)) + return boxMap.get(unboxedName); + else + return unboxedName; + } + + /** + * Checks whether it is an integer type (incl. byte, long, char) + * + * @return true if the given type is an integral type + */ + public boolean isIntegralType() { + return BasicSymbolsMill.INT.equals(getPrimitiveName()) || + BasicSymbolsMill.BYTE.equals(getPrimitiveName()) || + BasicSymbolsMill.SHORT.equals(getPrimitiveName()) || + BasicSymbolsMill.LONG.equals(getPrimitiveName()) || + BasicSymbolsMill.CHAR.equals(getPrimitiveName()); + } + + /** + * Checks whether it is a numeric type (incl. byte, long, char, float) + * + * @return true if the given type is a numeric type + */ + public boolean isNumericType() { + return BasicSymbolsMill.FLOAT.equals(getPrimitiveName()) || + BasicSymbolsMill.DOUBLE.equals(getPrimitiveName()) || + isIntegralType(); + } + + /** + * Am I primitive? (such as "int") + */ + public boolean isPrimitive() { + return true; + } + + @Override + public boolean deepEquals(SymTypeExpression sym){ + if(!sym.isPrimitive()){ + return false; + } + SymTypePrimitive symPrim = (SymTypePrimitive) sym; + if(!this.typeSymbol.getName().equals(symPrim.typeSymbol.getName())){ + return false; + } + return this.print().equals(symPrim.print()); + } + + @Override + public void accept(ISymTypeVisitor visitor) { + visitor.visit(this); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/SymTypePrimitiveDeSer.java b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypePrimitiveDeSer.java new file mode 100644 index 0000000000..8485d55c08 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypePrimitiveDeSer.java @@ -0,0 +1,37 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.symboltable.serialization.JsonDeSers; +import de.monticore.symboltable.serialization.JsonParser; +import de.monticore.symboltable.serialization.JsonPrinter; +import de.monticore.symboltable.serialization.json.JsonObject; +import de.se_rwth.commons.logging.Log; + +public class SymTypePrimitiveDeSer { + + // Care: the following String needs to be adapted if the package was renamed + public static final String SERIALIZED_KIND = "de.monticore.types.check.SymTypePrimitive"; + protected static final String SERIALIZED_NAME = "primitiveName"; + + public String serialize(SymTypePrimitive toSerialize) { + JsonPrinter jp = new JsonPrinter(); + jp.beginObject(); + jp.member(JsonDeSers.KIND, SERIALIZED_KIND); + jp.member(SERIALIZED_NAME, toSerialize.getPrimitiveName()); + jp.endObject(); + return jp.getContent(); + } + + public SymTypePrimitive deserialize(String serialized) { + return deserialize(JsonParser.parseJsonObject(serialized)); + } + + public SymTypePrimitive deserialize(JsonObject serialized) { + if (serialized.hasStringMember(SERIALIZED_NAME)) { + String constName = serialized.getStringMember(SERIALIZED_NAME); + return SymTypeExpressionFactory.createPrimitive(constName); + } + Log.error("0x823F1 Internal error: Cannot load \"" + serialized + "\" as SymTypePrimitive!"); + return null; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeVariable.java b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeVariable.java new file mode 100644 index 0000000000..e9d661279d --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeVariable.java @@ -0,0 +1,250 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.symbols.basicsymbols._symboltable.TypeVarSymbol; +import de.monticore.types3.ISymTypeVisitor; +import de.se_rwth.commons.logging.Log; + +import java.util.HashSet; +import java.util.Set; + +public class SymTypeVariable extends SymTypeExpression { + + protected static final String FREE_VARIABLE_NAME = "__INTERNAL_TYPEVARIABLE"; + + /** + * may be null, as some type variables are created + * DURING the type checking process and thus have no symbols + */ + protected TypeVarSymbol typeVarSymbol; + + protected SymTypeExpression lowerBound; + + /** + * this is NOT the full upper bound, + * given a TypeVarSymbol, it's supertypes are added to this upperBound + */ + protected SymTypeExpression upperBound; + + /** + * @param typeVarSymbol is allowed to be null + */ + public SymTypeVariable( + TypeVarSymbol typeVarSymbol, + SymTypeExpression lowerBound, + SymTypeExpression upperBound + ) { + this.typeVarSymbol = typeVarSymbol; + this.lowerBound = lowerBound; + this.upperBound = upperBound; + } + + @Deprecated + public SymTypeVariable(TypeVarSymbol typeSymbol) { + this.typeVarSymbol = typeSymbol; + } + + @Deprecated + public SymTypeVariable(TypeSymbol typeSymbol) { + this.typeSymbol = typeSymbol; + if(typeSymbol instanceof TypeVarSymbol) { + this.typeVarSymbol = (TypeVarSymbol) typeSymbol; + } + } + + public boolean hasTypeVarSymbol() { + return typeVarSymbol != null; + } + + public TypeVarSymbol getTypeVarSymbol() { + if (hasTypeVarSymbol()) { + return typeVarSymbol; + } + Log.error("0xFDFDD internal error: getTypeVarSymbol called, " + + "but no TypeVarSymbol available"); + return null; + } + + @Override + public boolean hasTypeInfo() { + // support deprecated behavior + return typeVarSymbol != null || typeSymbol != null; + } + + @Override + @Deprecated + public TypeSymbol getTypeInfo() { + //support deprecated behavior + if(typeSymbol != null) { + return typeSymbol; + } + return getTypeVarSymbol(); + } + + /** + * a type variable only allows super types of its lower bound + */ + protected SymTypeExpression getStoredLowerBound() { + return lowerBound; + } + + public SymTypeExpression getLowerBound() { + return getStoredLowerBound(); + } + + protected SymTypeExpression getStoredUpperBound() { + return upperBound; + } + + /** + * a type variable only allows sub-types of its upper bound, + * e.g., T extends Number + */ + public SymTypeExpression getUpperBound() { + // add upper bound given by symbol if applicable + SymTypeExpression result; + if (hasTypeVarSymbol() && !getTypeVarSymbol().isEmptySuperTypes()) { + Set intersectedTypes = + new HashSet<>(getTypeVarSymbol().getSuperTypesList()); + intersectedTypes.add(getStoredUpperBound()); + result = SymTypeExpressionFactory.createIntersection(intersectedTypes); + } + else { + result = getStoredUpperBound(); + } + return result; + } + + /** + * @deprecated unused in main projects + * also: getter and setter do something different, questionable + */ + @Deprecated + public String getVarName() { + return getTypeInfo().getFullName(); + } + + /** + * @deprecated unused in main projects + */ + @Deprecated + public void setVarName(String name) { + typeSymbol.setName(name); + } + + @Override + public String print() { + //support deprecated code: + if(typeSymbol != null) { + return typeSymbol.getName(); + } + if (!hasTypeVarSymbol()) { + return FREE_VARIABLE_NAME; + } + return getTypeVarSymbol().getName(); + } + + @Override + public String printFullName() { + //support deprecated code: + if(typeSymbol != null) { + return getVarName(); + } + if (!hasTypeVarSymbol()) { + return FREE_VARIABLE_NAME; + } + return getTypeVarSymbol().getFullName(); + } + + public boolean isPrimitive() { + return false; + /** + * Please note that the var itself is not a primitive type, but it might + * be instantiated into a primitive type + * unless we always assume boxed implementations then return false would be correct + * according to the W algorithm of Hindley-Milner, we regard a variable + * a monomorphic type on its own and do hence not regard it as primitive type + */ + } + + @Override + public boolean isValidType() { + return false; + /** + * Please note that the var itself is not a type, + * but it might be instantiated into a type + */ + } + + @Override + public boolean isTypeVariable() { + return true; + } + + @Override + public SymTypeVariable deepClone() { + //support deprecated code: + if(typeSymbol != null) { + return new SymTypeVariable(this.typeSymbol); + } + if (hasTypeVarSymbol()) { + return new SymTypeVariable( + getTypeVarSymbol(), + getLowerBound(), + getUpperBound() + ); + } + else { + return new SymTypeVariable(null, getLowerBound(), getUpperBound()); + } + } + + @Override + public boolean deepEquals(SymTypeExpression sym){ + //support deprecated code: + if(typeSymbol != null) { + if(!(sym instanceof SymTypeVariable)){ + return false; + } + SymTypeVariable symVar = (SymTypeVariable) sym; + if(this.typeSymbol == null ||symVar.typeSymbol ==null){ + return false; + } + if(!this.typeSymbol.getEnclosingScope().equals(symVar.typeSymbol.getEnclosingScope())){ + return false; + } + if(!this.typeSymbol.getName().equals(symVar.typeSymbol.getName())){ + return false; + } + return this.print().equals(symVar.print()); + } + if (!sym.isTypeVariable()) { + return false; + } + if(sym == this) { + return true; + } + SymTypeVariable symVar = (SymTypeVariable) sym; + if (!getUpperBound().deepEquals(symVar.getUpperBound())) { + return false; + } + else if (!getLowerBound().deepEquals(symVar.getLowerBound())) { + return false; + } + // cannot identify without a name at this point + else if (!hasTypeVarSymbol() || !symVar.hasTypeVarSymbol()) { + return false; + } + else if (!getTypeVarSymbol().deepEquals(symVar.getTypeVarSymbol())) { + return false; + } + return true; + } + + @Override + public void accept(ISymTypeVisitor visitor) { + visitor.visit(this); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeVariableDeSer.java b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeVariableDeSer.java new file mode 100644 index 0000000000..6623caf923 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeVariableDeSer.java @@ -0,0 +1,40 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.IBasicSymbolsGlobalScope; +import de.monticore.symboltable.serialization.JsonDeSers; +import de.monticore.symboltable.serialization.JsonParser; +import de.monticore.symboltable.serialization.JsonPrinter; +import de.monticore.symboltable.serialization.json.JsonObject; +import de.se_rwth.commons.logging.Log; + +public class SymTypeVariableDeSer { + + // Care: the following String needs to be adapted if the package was renamed + public static final String SERIALIZED_KIND = "de.monticore.types.check.SymTypeVariable"; + protected static final String SERIALIZED_NAME = "varName"; + + public String serialize(SymTypeVariable toSerialize) { + JsonPrinter jp = new JsonPrinter(); + jp.beginObject(); + jp.member(JsonDeSers.KIND, SERIALIZED_KIND); + jp.member(SERIALIZED_NAME, toSerialize.getVarName()); + jp.endObject(); + return jp.getContent(); + } + + public SymTypeVariable deserialize(String serialized) { + return deserialize(JsonParser.parseJsonObject(serialized)); + } + + public SymTypeVariable deserialize(JsonObject serialized) { + if (serialized.hasStringMember(SERIALIZED_NAME)) { + String varName = serialized.getStringMember(SERIALIZED_NAME); + IBasicSymbolsGlobalScope gs = BasicSymbolsMill.globalScope(); + return SymTypeExpressionFactory.createTypeVariable(varName, gs); + } + Log.error("0x823F5 Internal error: Cannot load \"" + serialized + "\" as SymTypeVariable!"); + return null; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeVoid.java b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeVoid.java new file mode 100644 index 0000000000..32295c431d --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/SymTypeVoid.java @@ -0,0 +1,45 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + + +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbolSurrogate; +import de.monticore.types3.ISymTypeVisitor; + +public class SymTypeVoid extends SymTypeExpression { + + /** + * @deprecated no logic that is not deprecated + */ + @Deprecated + public SymTypeVoid() { + typeSymbol = new TypeSymbolSurrogate(BasicSymbolsMill.VOID); + typeSymbol.setEnclosingScope(BasicSymbolsMill.scope()); + } + + @Override + public String print() { + return BasicSymbolsMill.VOID; + } + + @Override + public String printFullName() { + return print(); + } + + @Override + public boolean isVoidType() { + return true; + } + + @Override + public boolean deepEquals(SymTypeExpression sym){ + return sym.isVoidType(); + } + + @Override + public void accept(ISymTypeVisitor visitor) { + visitor.visit(this); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/SynthComp4MCBasicTypes.java b/monticore-grammar/src/main/java/de/monticore/types/check/SynthComp4MCBasicTypes.java new file mode 100644 index 0000000000..cbac938253 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/SynthComp4MCBasicTypes.java @@ -0,0 +1,70 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import com.google.common.base.Preconditions; +import de.monticore.symbols.compsymbols._symboltable.ComponentSymbol; +import de.monticore.symbols.compsymbols._symboltable.ICompSymbolsScope; +import de.monticore.types.mcbasictypes._ast.ASTMCQualifiedType; +import de.monticore.types.mcbasictypes._visitor.MCBasicTypesHandler; +import de.monticore.types.mcbasictypes._visitor.MCBasicTypesTraverser; +import de.se_rwth.commons.logging.Log; +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.List; + +/** + * A visitor (a handler indeed) that creates a {@link KindOfComponent} from an + * {@link ASTMCQualifiedType}, given that there is matching, resolvable + * component symbol. + */ +public class SynthComp4MCBasicTypes implements MCBasicTypesHandler { + + protected MCBasicTypesTraverser traverser; + + /** + * Common state with other visitors, if this visitor is part of a visitor composition. + */ + protected SynthCompResult resultWrapper; + + public SynthComp4MCBasicTypes(@NonNull SynthCompResult resultWrapper) { + this.resultWrapper = Preconditions.checkNotNull(resultWrapper); + } + + @Override + public MCBasicTypesTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(@NonNull MCBasicTypesTraverser traverser) { + this.traverser = Preconditions.checkNotNull(traverser); + } + + @Override + public void handle(@NonNull ASTMCQualifiedType node) { + Preconditions.checkNotNull(node); + Preconditions.checkNotNull(node.getEnclosingScope()); + Preconditions.checkArgument(node.getEnclosingScope() instanceof ICompSymbolsScope); + + ICompSymbolsScope enclScope = ((ICompSymbolsScope) node.getEnclosingScope()); + List comp = enclScope.resolveComponentMany(node.getMCQualifiedName().getQName()); + + if (comp.isEmpty()) { + Log.error(String.format("0xD0104 Cannot resolve component '%s'", node.getMCQualifiedName().getQName()), + node.get_SourcePositionStart(), node.get_SourcePositionEnd() + ); + this.resultWrapper.setResultAbsent(); + } else if (comp.size() > 1) { + Log.error( + String.format( + "0xD0105 Ambiguous reference, both '%s' and '%s' match'", + comp.get(0).getFullName(), comp.get(1).getFullName() + ), + node.get_SourcePositionStart(), node.get_SourcePositionEnd() + ); + this.resultWrapper.setResult(new KindOfComponent(comp.get(0))); + } else { + this.resultWrapper.setResult(new KindOfComponent(comp.get(0))); + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/SynthComp4MCSimpleGenericTypes.java b/monticore-grammar/src/main/java/de/monticore/types/check/SynthComp4MCSimpleGenericTypes.java new file mode 100644 index 0000000000..995092e899 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/SynthComp4MCSimpleGenericTypes.java @@ -0,0 +1,104 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import com.google.common.base.Preconditions; +import de.monticore.symbols.compsymbols._symboltable.ComponentSymbol; +import de.monticore.symbols.compsymbols._symboltable.ICompSymbolsScope; +import de.monticore.symboltable.resolving.ResolvedSeveralEntriesForSymbolException; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.mccollectiontypes._ast.ASTMCBasicTypeArgument; +import de.monticore.types.mccollectiontypes._ast.ASTMCPrimitiveTypeArgument; +import de.monticore.types.mccollectiontypes._ast.ASTMCTypeArgument; +import de.monticore.types.mcsimplegenerictypes._ast.ASTMCBasicGenericType; +import de.monticore.types.mcsimplegenerictypes._ast.ASTMCCustomTypeArgument; +import de.monticore.types.mcsimplegenerictypes._visitor.MCSimpleGenericTypesHandler; +import de.monticore.types.mcsimplegenerictypes._visitor.MCSimpleGenericTypesTraverser; +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * A visitor (a handler indeed) that creates a {@link KindOfComponent} from an + * {@link ASTMCBasicGenericType}, given that there is a matching resolvable + * component symbol. + */ +public class SynthComp4MCSimpleGenericTypes implements MCSimpleGenericTypesHandler { + + protected MCSimpleGenericTypesTraverser traverser; + + /** + * Common state with other visitors, if this visitor is part of a visitor composition. + */ + protected SynthCompResult resultWrapper; + + /** + * Used to create {@link SymTypeExpression}s for the ast-representation of the generic component type's type. + */ + protected ISynthesize typeSynth; + + public SynthComp4MCSimpleGenericTypes(@NonNull SynthCompResult result, + @NonNull ISynthesize typeSynth) { + this.resultWrapper = result; + this.typeSynth = typeSynth; + } + + @Override + public MCSimpleGenericTypesTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(@NonNull MCSimpleGenericTypesTraverser traverser) { + this.traverser = Preconditions.checkNotNull(traverser); + } + + @Override + public void handle(@NonNull ASTMCBasicGenericType mcType) { + Preconditions.checkNotNull(mcType); + Preconditions.checkNotNull(mcType.getEnclosingScope()); + Preconditions.checkArgument(mcType.getEnclosingScope() instanceof ICompSymbolsScope); + + ICompSymbolsScope enclScope = (ICompSymbolsScope) mcType.getEnclosingScope(); + String compName = String.join(".", mcType.getNameList()); + List compSym = enclScope.resolveComponentMany(compName); + + if (compSym.isEmpty()) { + this.resultWrapper.setResultAbsent(); + } else { + List typeArgExpressions = typeArgumentsToTypes(mcType.getMCTypeArgumentList()).stream() + .map(typeArg -> { + TypeCheckResult typeResult = null; + try { + typeResult = typeSynth.synthesizeType(typeArg); + } catch (ResolvedSeveralEntriesForSymbolException ignored) { } + return typeResult != null && typeResult.isPresentResult() ? typeResult.getResult() : null; + }) + .collect(Collectors.toList()); + this.resultWrapper.setResult(new KindOfGenericComponent(compSym.get(0), typeArgExpressions)); + } + } + + /** + * Given that all {@link ASTMCTypeArgument}s in {@code typeArgs} are {@link ASTMCType}s, this method returns a list + * with these {@code ASTMCType}s in the same order. Else, an exception is thrown. + */ + protected List typeArgumentsToTypes(@NonNull List typeArgs) { + Preconditions.checkNotNull(typeArgs); + + List types = new ArrayList<>(typeArgs.size()); + for (ASTMCTypeArgument typeArg : typeArgs) { + if (typeArg instanceof ASTMCBasicTypeArgument) { + types.add(((ASTMCBasicTypeArgument) typeArg).getMCQualifiedType()); + } else if (typeArg instanceof ASTMCPrimitiveTypeArgument) { + types.add(((ASTMCPrimitiveTypeArgument) typeArg).getMCPrimitiveType()); + } else if (typeArg instanceof ASTMCCustomTypeArgument) { + types.add(((ASTMCCustomTypeArgument) typeArg).getMCType()); + } else { + throw new IllegalStateException(); + } + } + return types; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/SynthCompResult.java b/monticore-grammar/src/main/java/de/monticore/types/check/SynthCompResult.java new file mode 100644 index 0000000000..2fb0ef747f --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/SynthCompResult.java @@ -0,0 +1,32 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import com.google.common.base.Preconditions; +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.Optional; + +/** + * Wraps a {@link CompKindExpression} (if present). This class is used as a common state for composed visitors that + * implement {@link ISynthesizeComponent}. + */ +public class SynthCompResult { + + protected CompKindExpression result = null; + + public void reset() { + this.setResultAbsent(); + } + + public void setResultAbsent() { + this.result = null; + } + + public void setResult(@NonNull CompKindExpression result) { + this.result = Preconditions.checkNotNull(result); + } + + public Optional getResult() { + return Optional.ofNullable(result); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/SynthesizeSymTypeFromMCArrayTypes.java b/monticore-grammar/src/main/java/de/monticore/types/check/SynthesizeSymTypeFromMCArrayTypes.java new file mode 100644 index 0000000000..1f8b42c523 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/SynthesizeSymTypeFromMCArrayTypes.java @@ -0,0 +1,61 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.types.check; + +import de.monticore.types.mcarraytypes._ast.ASTMCArrayType; +import de.monticore.types.mcarraytypes._visitor.MCArrayTypesHandler; +import de.monticore.types.mcarraytypes._visitor.MCArrayTypesTraverser; +import de.monticore.types.mcarraytypes._visitor.MCArrayTypesVisitor2; +import de.se_rwth.commons.logging.Log; + +/** + * Visitor for Derivation of SymType from MCArrayTypes + * i.e. for + * types/MCArrayTypes.mc4 + */ +public class SynthesizeSymTypeFromMCArrayTypes extends AbstractSynthesizeFromType + implements MCArrayTypesVisitor2, MCArrayTypesHandler { + + protected MCArrayTypesTraverser traverser; + + @Override + public void setTraverser(MCArrayTypesTraverser traverser) { + this.traverser = traverser; + } + + @Override + public MCArrayTypesTraverser getTraverser() { + return traverser; + } + + /** + * Storage in the Visitor: result of the last endVisit + * is inherited + * This attribute is synthesized upward. + */ + + /** + * We use mainly endVisit, because the result is synthesized along the + * tree, when walking upwards + */ + @Override + public void traverse(ASTMCArrayType arrayType) { + arrayType.getMCType().accept(getTraverser()); + if (!getTypeCheckResult().isPresentResult()) { + // should never happen, we expect results to be present + // indicates that the underlying type resolver is erroneous + Log.error("0xE9CDC The type of the array could not be synthesized" , arrayType.get_SourcePositionStart()); + getTypeCheckResult().reset(); + return; + } + if(!getTypeCheckResult().getResult().isObscureType()){ + SymTypeExpression tex = SymTypeExpressionFactory.createTypeArray( + arrayType.printTypeWithoutBrackets(), getScope(arrayType.getEnclosingScope()), + arrayType.getDimensions(), + getTypeCheckResult().getResult()); + getTypeCheckResult().setResult(tex); + arrayType.setDefiningSymbol(tex.getTypeInfo()); + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/SynthesizeSymTypeFromMCBasicTypes.java b/monticore-grammar/src/main/java/de/monticore/types/check/SynthesizeSymTypeFromMCBasicTypes.java new file mode 100644 index 0000000000..58db5a3626 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/SynthesizeSymTypeFromMCBasicTypes.java @@ -0,0 +1,163 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import com.google.common.base.Preconditions; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.symbols.basicsymbols._symboltable.TypeVarSymbol; +import de.monticore.types.mcbasictypes.MCBasicTypesMill; +import de.monticore.types.mcbasictypes._ast.*; +import de.monticore.types.mcbasictypes._visitor.MCBasicTypesHandler; +import de.monticore.types.mcbasictypes._visitor.MCBasicTypesTraverser; +import de.monticore.types.mcbasictypes._visitor.MCBasicTypesVisitor2; +import de.se_rwth.commons.logging.Log; +import org.apache.commons.lang3.StringUtils; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +/** + * Visitor for Derivation of SymType from MCBasicTypes + * i.e. for + * types/MCBasicTypes.mc4 + */ +public class SynthesizeSymTypeFromMCBasicTypes extends AbstractSynthesizeFromType implements MCBasicTypesVisitor2, MCBasicTypesHandler { + + protected MCBasicTypesTraverser traverser; + + // ---------------------------------------------------------- Visting Methods + + /** + * We use mainly endVisit, because the result is synthesized along the + * tree, when walking upwards + */ + @Override + public void endVisit(ASTMCPrimitiveType primitiveType) { + String primName = primitiveType.printType(); + Optional prim = getScope(primitiveType.getEnclosingScope()).resolveType(primName); + if(prim.isPresent()){ + SymTypePrimitive typeConstant = + SymTypeExpressionFactory.createPrimitive(prim.get()); + getTypeCheckResult().setResult(typeConstant); + primitiveType.setDefiningSymbol(typeConstant.getTypeInfo()); + }else{ + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + Log.error("0xA0111 Cannot find symbol " + primName, primitiveType.get_SourcePositionStart()); + } + } + + @Override + public void endVisit(ASTMCVoidType voidType) { + getTypeCheckResult().setResult(SymTypeExpressionFactory.createTypeVoid()); + } + + @Override + public void endVisit(ASTMCReturnType rType) { + // result is pushed upward (no change) + } + + @Override + public MCBasicTypesTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(MCBasicTypesTraverser traverser) { + this.traverser = traverser; + } + + @Override + public void endVisit(ASTMCQualifiedName qName) { + // Search for matching type variables + Optional symType = createTypeVariable(qName); + + // If no matching type variables, search for matching types + if (symType.isEmpty()) { + symType = createTypeObject(qName); + } + + // Update result, if still no matching symbol then create sym-type obscure + this.getTypeCheckResult().setResult( + symType.isPresent() ? symType.get() : createObscure(qName) + ); + } + + @Override + public void endVisit(ASTMCQualifiedType node) { + if(getTypeCheckResult().isPresentResult() && !getTypeCheckResult().getResult().isObscureType()){ + node.setDefiningSymbol(getTypeCheckResult().getResult().getTypeInfo()); + } + } + + /** + * This method creates a sym-type expression for the first resolved type + * variable symbol matching the provided qualified name. It logs an error if + * multiple matching symbols are found. The resulting sym-type expression is + * encapsulated in an optional. + * @param qName the qualified name + * @return an optional of the created sym-type expression, empty if no match + * type variable symbol is found + */ + protected Optional createTypeVariable(ASTMCQualifiedName qName) { + Preconditions.checkNotNull(qName); + + List matches = getScope(qName.getEnclosingScope()) + .resolveTypeVarMany(qName.getQName()); + if (matches.isEmpty()) { + return Optional.empty(); + } else { + SymTypeVariable symType = SymTypeExpressionFactory.createTypeVariable(matches.get(0)); + /* TODO: Enable once CD4A resolving is fixed + if (matches.size() > 1) { + Log.error("0xA0325 Reference " + qName.getQName() + + " matches multiple type variables: " + + StringUtils.join(matches, ", "), + qName.get_SourcePositionStart() + ); + }*/ + return Optional.of(symType); + } + } + + /** + * This method creates a sym-type expression for the first resolved type + * symbol matching the provided qualified name. It logs an error if + * multiple matching symbols are found. The resulting sym-type expression is + * encapsulated in an optional. + * @param qName the qualified name + * @return an optional of the created sym-type expression, empty if no match + * type symbol is found + */ + protected Optional createTypeObject(ASTMCQualifiedName qName) { + Preconditions.checkNotNull(qName); + + List matches = getScope(qName.getEnclosingScope()) + .resolveTypeMany(qName.getQName()); + if (matches.isEmpty()) { + return Optional.empty(); + } else { + SymTypeOfObject symType = SymTypeExpressionFactory.createTypeObject(matches.get(0)); + /* TODO: Enable once CD4A resolving is fixes + if (matches.size() > 1) { + Log.error("0xA0326 Reference " + qName.getQName() + + " matches multiple types: " + + StringUtils.join(matches, ", "), + qName.get_SourcePositionStart() + ); + }*/ + return Optional.of(symType); + } + } + + /** + * This method creates a sym-type obscure and logs an error that no symbol + * matching the provided qualified name can be found. + * @param qName the qualified name + * @return a sym-type obscure + */ + protected SymTypeObscure createObscure(ASTMCQualifiedName qName) { + Preconditions.checkNotNull(qName); + Log.error("0xA0324 Cannot find symbol " + qName.getQName(), qName.get_SourcePositionStart()); + return SymTypeExpressionFactory.createObscureType(); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/SynthesizeSymTypeFromMCCollectionTypes.java b/monticore-grammar/src/main/java/de/monticore/types/check/SynthesizeSymTypeFromMCCollectionTypes.java new file mode 100644 index 0000000000..507eb54170 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/SynthesizeSymTypeFromMCCollectionTypes.java @@ -0,0 +1,150 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.mccollectiontypes._ast.*; +import de.monticore.types.mccollectiontypes._visitor.MCCollectionTypesHandler; +import de.monticore.types.mccollectiontypes._visitor.MCCollectionTypesTraverser; +import de.monticore.types.mccollectiontypes._visitor.MCCollectionTypesVisitor2; +import de.se_rwth.commons.logging.Log; + +import java.util.List; + +/** + * Visitor for Derivation of SymType from MCBasicTypes + * i.e. for + * types/MCBasicTypes.mc4 + */ +public class SynthesizeSymTypeFromMCCollectionTypes extends AbstractSynthesizeFromType implements MCCollectionTypesVisitor2, MCCollectionTypesHandler { + + + protected MCCollectionTypesTraverser traverser; + + @Override + public void setTraverser(MCCollectionTypesTraverser traverser) { + this.traverser = traverser; + } + + @Override + public MCCollectionTypesTraverser getTraverser() { + return traverser; + } + + /** + * Storage in the Visitor: result of the last endVisit + * is inherited + * This attribute is synthesized upward. + */ + + /** + * We use mainly endVisit, because the result is synthesized along the + * tree, when walking upwards + */ + + @Override + public void endVisit(ASTMCListType t) { + synthesizeTypeWithOneArgument(t, "List"); + } + + @Override + public void endVisit(ASTMCSetType t) { + synthesizeTypeWithOneArgument(t, "Set"); + } + + @Override + public void endVisit(ASTMCOptionalType t) { + synthesizeTypeWithOneArgument(t, "Optional"); + } + + public void synthesizeTypeWithOneArgument(ASTMCType type, String name){ + List symbols = getScope(type.getEnclosingScope()).resolveTypeMany(name); + if(getTypeCheckResult().isPresentResult()) { + if (!getTypeCheckResult().getResult().isObscureType()) { + + // Argument type has been processed and stored in result + if (symbols.size() == 1) { + SymTypeExpression typeArg = getTypeCheckResult().getResult(); + SymTypeExpression typeExpression = SymTypeExpressionFactory.createGenerics(symbols.get(0), typeArg); + getTypeCheckResult().setResult(typeExpression); + type.setDefiningSymbol(typeExpression.getTypeInfo()); + } else { + if(symbols.size() > 1) { + Log.error(String.format("0xE9FD6 %s matching types were found for %s.", symbols.size(), name), type.get_SourcePositionStart()); + }else{ + Log.error(String.format("0xE9FD0 Cannot find symbol %s", name), type.get_SourcePositionStart()); + } + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + } + } + } else { + // should never happen, we expect results to be present + // indicates that the underlying type resolver is erroneous + Log.error("0xE9FD1 One of the type arguments of '" + name + "' could not be resolved", type.get_SourcePositionStart()); + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + } + } + + /** + * Map has two arguments: to retrieve them properly, the traverse method + * is adapted (looking deeper into the visitor), instead of the endVisit method: + */ + @Override + public void traverse(ASTMCMapType node) { + List mapSyms = getScope(node.getEnclosingScope()).resolveTypeMany("Map"); + + // Argument 1: + if (null != node.getKey()) { + node.getKey().accept(getTraverser()); + } + TypeCheckResult keyTypeResult = getTypeCheckResult().copy(); + + // Argument 2: + if (null != node.getValue()) { + node.getValue().accept(getTraverser()); + } + TypeCheckResult valueTypeResult = getTypeCheckResult().copy(); + + if(!keyTypeResult.isPresentResult()){ + Log.error("0xE9FDD Could not synthesize the key type argument " + + "of the Map type.", node.get_SourcePositionStart()); + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + }else if(!valueTypeResult.isPresentResult()){ + Log.error("0xE9FDE Could not synthesize the value type argument " + + "of the Map type.", node.get_SourcePositionStart()); + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + }else{ + if(!keyTypeResult.getResult().isObscureType() && !valueTypeResult.getResult().isObscureType()){ + if(mapSyms.size() == 1) { + SymTypeExpression keyTypeExpr = keyTypeResult.getResult(); + SymTypeExpression valueTypeExpr = valueTypeResult.getResult(); + SymTypeExpression typeExpression = + SymTypeExpressionFactory.createGenerics(mapSyms.get(0), keyTypeExpr, valueTypeExpr); + getTypeCheckResult().setResult(typeExpression); + node.setDefiningSymbol(typeExpression.getTypeInfo()); + } else { + if(mapSyms.size() > 1) { + Log.error(String.format("0xE9FDC %s matching types were found for Map.", mapSyms.size()), node.get_SourcePositionStart()); + }else{ + Log.error("0xE9FD7 Cannot find symbol Map", node.get_SourcePositionStart()); + } + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + } + }else{ + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + } + } + } + + // ASTMCTypeArgument, ASTMCBasicTypeArgument and MCPrimitiveTypeArgument: + // Do nothing, because result already contains the argument + // (because: MCG contains: + // interface MCTypeArgument; + // MCBasicTypeArgument implements MCTypeArgument <200> = + // MCQualifiedType; + // + // MCPrimitiveTypeArgument implements MCTypeArgument <190> = + // MCPrimitiveType; + + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/SynthesizeSymTypeFromMCFullGenericTypes.java b/monticore-grammar/src/main/java/de/monticore/types/check/SynthesizeSymTypeFromMCFullGenericTypes.java new file mode 100644 index 0000000000..c416a95eed --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/SynthesizeSymTypeFromMCFullGenericTypes.java @@ -0,0 +1,75 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.types.check; + +import de.monticore.types.mcfullgenerictypes._ast.ASTMCWildcardTypeArgument; +import de.monticore.types.mcfullgenerictypes._visitor.MCFullGenericTypesHandler; +import de.monticore.types.mcfullgenerictypes._visitor.MCFullGenericTypesTraverser; +import de.monticore.types.mcfullgenerictypes._visitor.MCFullGenericTypesVisitor2; +import de.se_rwth.commons.logging.Log; + +/** + * Visitor for Derivation of SymType from MCFullGenericTypes + * i.e. for + * types/MCFullGenericTypes.mc4 + */ +public class SynthesizeSymTypeFromMCFullGenericTypes extends AbstractSynthesizeFromType + implements MCFullGenericTypesVisitor2, MCFullGenericTypesHandler { + + protected MCFullGenericTypesTraverser traverser; + + @Override + public MCFullGenericTypesTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(MCFullGenericTypesTraverser traverser) { + this.traverser = traverser; + } + + /** + * Storage in the Visitor: result of the last endVisit + * is inherited + * This attribute is synthesized upward. + */ + + /** + * We use mainly endVisit, because the result is synthesized along the + * tree, when walking upwards + */ + @Override + public void traverse(ASTMCWildcardTypeArgument wildcardType) { + SymTypeExpression tex; + if (wildcardType.isPresentLowerBound()) { + wildcardType.getLowerBound().accept(getTraverser()); + if (!getTypeCheckResult().isPresentResult()) { + Log.error("0xE9CDD The lower bound type of the wildcard type " + + "could not be synthesized.", wildcardType.get_SourcePositionStart()); + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + return; + } + if(!getTypeCheckResult().getResult().isObscureType()) { + tex = SymTypeExpressionFactory.createWildcard(false, getTypeCheckResult().getResult()); + } else { + tex = SymTypeExpressionFactory.createObscureType(); + } + } else if (wildcardType.isPresentUpperBound()) { + wildcardType.getUpperBound().accept(getTraverser()); + if (!getTypeCheckResult().isPresentResult()) { + Log.error("0xE9CDA The upper bound type of the wildcard type " + + "could not be synthesized.", wildcardType.get_SourcePositionStart()); + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + return; + } + if(!getTypeCheckResult().getResult().isObscureType()) { + tex = SymTypeExpressionFactory.createWildcard(true, getTypeCheckResult().getResult()); + } else { + tex = SymTypeExpressionFactory.createObscureType(); + } + } else { + tex = SymTypeExpressionFactory.createWildcard(); + } + getTypeCheckResult().setResult(tex); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/SynthesizeSymTypeFromMCFunctionTypes.java b/monticore-grammar/src/main/java/de/monticore/types/check/SynthesizeSymTypeFromMCFunctionTypes.java new file mode 100644 index 0000000000..81d67e541b --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/SynthesizeSymTypeFromMCFunctionTypes.java @@ -0,0 +1,79 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.types.check; + +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.mcfunctiontypes._ast.ASTMCFunctionType; +import de.monticore.types.mcfunctiontypes._visitor.MCFunctionTypesHandler; +import de.monticore.types.mcfunctiontypes._visitor.MCFunctionTypesTraverser; +import de.monticore.types.mcfunctiontypes._visitor.MCFunctionTypesVisitor2; +import de.se_rwth.commons.logging.Log; + +import java.util.LinkedList; +import java.util.List; + +/** + * Visitor for Derivation of SymType from MCFunctionTypes + * i.e. for + * types/MCFunctionTypes.mc4 + */ +public class SynthesizeSymTypeFromMCFunctionTypes extends AbstractSynthesizeFromType + implements MCFunctionTypesVisitor2, MCFunctionTypesHandler { + + protected MCFunctionTypesTraverser traverser; + + @Override + public MCFunctionTypesTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(MCFunctionTypesTraverser traverser) { + this.traverser = traverser; + } + + @Override + public void handle(ASTMCFunctionType functionType) { + SymTypeExpression symType; + + List arguments = new LinkedList(); + for (int i = 0; i arguments = new LinkedList(); + for (int i = 0; i typeVar = getScope(genericType.getEnclosingScope()).resolveTypeVar(genericType.printWithoutTypeArguments()); + if (typeVar.isPresent()) { + Log.error("0xA0320 The generic type cannot have a generic parameter because it is a type variable", + genericType.get_SourcePositionStart()); + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + } else { + Optional type = getScope(genericType.getEnclosingScope()).resolveType(genericType.printWithoutTypeArguments()); + if (type.isPresent()) { + symType = SymTypeExpressionFactory.createGenerics(type.get(), arguments); + } else { + symType = handleIfNotFound(genericType, arguments); + } + } + if (null != symType) { + getTypeCheckResult().setResult(symType); + genericType.setDefiningSymbol(symType.getTypeInfo()); + } + }else{ + // one of the type arguments could not be synthesized => the generic type itself cannot be synthesized correctly + getTypeCheckResult().setResult(SymTypeExpressionFactory.createObscureType()); + } + } + + /** + * extension method for error handling + */ + protected SymTypeExpression handleIfNotFound(ASTMCGenericType type, List arguments){ + Log.error("0xA0323 Cannot find symbol " + type.printWithoutTypeArguments(), type.get_SourcePositionStart()); + return SymTypeExpressionFactory.createObscureType(); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/TypeCalculator.java b/monticore-grammar/src/main/java/de/monticore/types/check/TypeCalculator.java new file mode 100644 index 0000000000..aa25761907 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/TypeCalculator.java @@ -0,0 +1,266 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.literals.mcliteralsbasis._ast.ASTLiteral; +import de.monticore.types.mcbasictypes._ast.ASTMCQualifiedName; +import de.monticore.types.mcbasictypes._ast.ASTMCReturnType; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.mcbasictypes._ast.ASTMCVoidType; +import de.monticore.types.mcfullgenerictypes.MCFullGenericTypesMill; +import de.se_rwth.commons.logging.Log; + +public class TypeCalculator implements ITypeRelations { + + /** + * Configuration: Visitor for Function 1: + * Synthesizing the SymTypeExpression from an AST Type. + * May also be of a subclass; + */ + protected ISynthesize iSynthesize; + + /** + * Configuration: Visitor for Function 2b: + * Deriving the SymTypeExpression from an AST Value - Literal. + * May also be of a subclass; + */ + protected IDerive iDerive; + + /** + * Configuration: Implementation for Function 4: + * Delegatee for ITypeRelations + * Calculate relations of types. + */ + protected ITypeRelations iTypeRelations; + + + /** + * Configuration as state: + * @param synthesizeSymType defines, which AST Types are mapped (and how) + * @param iDerive defines, which AST Literals are handled + * through the Expression type recognition + * @param iTypeRelations defines, what the relations of types are, + * this is delegated to, to provide the {@link ITypeRelations} + */ + public TypeCalculator(ISynthesize synthesizeSymType, + IDerive iDerive, + ITypeRelations iTypeRelations) { + this.iSynthesize = synthesizeSymType; + this.iDerive = iDerive; + this.iTypeRelations = iTypeRelations; + } + + // only providing the old constructor to not break downstream languages + // it is recommended to always provide the ITypeRelations implementation + @Deprecated + public TypeCalculator(ISynthesize iSynthesize, IDerive iDerive) { + this(iSynthesize, iDerive, new TypeRelations()); + } + + /*************************************************************************/ + + + /** + * Function 1: extracting the SymTypeExpression from an AST Type + * The SymTypeExpression is independent of the AST and can be stored in the SymTab etc. + * + * Tests for this Function are combined in the Visitor tests + * (SynthesizeSymType.*Types.*Test) + */ + public SymTypeExpression symTypeFromAST(ASTMCType ast) + { + TypeCheckResult result = iSynthesize.synthesizeType(ast); + if(!result.isPresentResult()) { + Log.error("0xE9FD4 Internal Error: No SymType for: " + + ast.printType() + ". Probably TypeCheck mis-configured."); + } + return result.getResult(); + } + + + /** + * Function 1b: extracting the SymTypeExpression from the AST Type "void" + * ("void" is not in the ASTMCType hierarchy, while it is included in the SymTypeExpressions) + */ + public SymTypeExpression symTypeFromAST(ASTMCVoidType ast) + { + return SymTypeExpressionFactory.createTypeVoid(); + } + + + /** + * Function 1c: extracting the SymTypeExpression from the AST MCReturnType + * (MCReturnType is not in the ASTMCType hierarchy, while it is included in the SymTypeExpressions) + * + * Tests for this Function are combined in the Visitor tests + * (SynthesizeSymType.*Types.*Test) + */ + public SymTypeExpression symTypeFromAST(ASTMCReturnType ast) + { + TypeCheckResult result = iSynthesize.synthesizeType(ast); + if(!result.isPresentResult()) { + Log.error("0xE9FD9 Internal Error: No SymType for return type: " + + ast.printType() + + ". Probably TypeCheck mis-configured."); + } + return result.getResult(); + } + + + /** + * Function 1d: extracting the SymTypeExpression from the AST MCQualifiedName + * + * Tests for this Function are combined in the Visitor tests + * (SynthesizeSymType.*Types.*Test) + */ + public SymTypeExpression symTypeFromAST(ASTMCQualifiedName ast) + { + TypeCheckResult result = iSynthesize.synthesizeType(ast); + if(!result.isPresentResult()) { + Log.error("0xE9FD5 Internal Error: No SymType for MCQualifiedName: " + + ast.getBaseName() + + ". Probably TypeCheck mis-configured."); + } + return result.getResult(); + } + + + /*************************************************************************/ + + + /** + * Function 2: Derive the SymTypeExpression from an Expression AST + * This defines the Type that an Expression has. + * Precondition: + * Free Variables in the AST are being looked u through the Symbol Table that + * needs to be in place; same for method calls etc. + */ + public SymTypeExpression typeOf(ASTExpression expr) + { + TypeCheckResult result = iDerive.deriveType(expr); + if(!result.isPresentResult()) { + Log.error("0xED680 Internal Error: No Type for Expression " + expr + + " Probably TypeCheck mis-configured."); + } + return result.getResult(); + } + + + /** + * Function 2b: Derive the SymTypeExpression of a Literal + * This defines the Type that a Literal has and will be used to + * determine the Type of Expressions. + * + * Tests for this Function are combined in the Visitor tests + * (DeriveSymType.*Literals.*Test) + */ + public SymTypeExpression typeOf(ASTLiteral lit) + { + TypeCheckResult result = iDerive.deriveType(lit); + if(!result.isPresentResult()) { + Log.error("0xED670 Internal Error: No Type for Literal " + lit + + " Probably TypeCheck mis-configured."); + } + return result.getResult(); + } + + /*************************************************************************/ + + + /** + * Function 4: + * Checks whether the ASTExpression exp will result in a value that is of type, and + * thus can be e.g. stored, sent, etc. Essentially exp needs to be of a subtype to + * be assignment compatible. + * (as it is combined from other functions, it need not be overwritten) + * @param exp the Expression that shall be checked for a given type + * @param type the Type it needs to have (e.g. the Type of a variable used for assignment, or the + * type of a channel where to send a value) + */ + public boolean isOfTypeForAssign(SymTypeExpression type, + ASTExpression exp) + { + return compatible( type, typeOf(exp)); + // that is all what is needed + } + + @Override + public boolean compatible(SymTypeExpression left, SymTypeExpression right) { + return iTypeRelations.compatible(left, right); + } + + @Override + public boolean isSubtypeOf(SymTypeExpression subType, SymTypeExpression superType) { + return iTypeRelations.isSubtypeOf(subType, superType); + } + + @Override + public int calculateInheritanceDistance(SymTypeExpression specific, SymTypeExpression general) { + return iTypeRelations.calculateInheritanceDistance(specific, general); + } + + @Override + public int calculateInheritanceDistance(SymTypePrimitive specific, SymTypePrimitive general) { + return iTypeRelations.calculateInheritanceDistance(specific, general); + } + + @Override + public boolean isBoolean(SymTypeExpression type) { + return iTypeRelations.isBoolean(type); + } + + @Override + public boolean isInt(SymTypeExpression type) { + return iTypeRelations.isInt(type); + } + + @Override + public boolean isDouble(SymTypeExpression type) { + return iTypeRelations.isDouble(type); + } + + @Override + public boolean isFloat(SymTypeExpression type) { + return iTypeRelations.isFloat(type); + } + + @Override + public boolean isLong(SymTypeExpression type) { + return iTypeRelations.isLong(type); + } + + @Override + public boolean isChar(SymTypeExpression type) { + return iTypeRelations.isChar(type); + } + + @Override + public boolean isShort(SymTypeExpression type) { + return iTypeRelations.isShort(type); + } + + @Override + public boolean isByte(SymTypeExpression type) { + return iTypeRelations.isByte(type); + } + + @Override + public boolean isVoid(SymTypeExpression type) { + return iTypeRelations.isVoid(type); + } + + @Override + public boolean isString(SymTypeExpression type) { + return iTypeRelations.isString(type); + } + + @Override + public boolean isIntegralType(SymTypeExpression type) { + return iTypeRelations.isIntegralType(type); + } + + @Override + public boolean isNumericType(SymTypeExpression type) { + return iTypeRelations.isNumericType(type); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/TypeCheck.java b/monticore-grammar/src/main/java/de/monticore/types/check/TypeCheck.java new file mode 100644 index 0000000000..7b2a710386 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/TypeCheck.java @@ -0,0 +1,88 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +/** + * Deprecated due to static methods + * use {@link ITypeRelations} instead, + * preferably provided by {@link TypeCalculator} + */ +@Deprecated +public class TypeCheck { + + /** + * only used to provide deprecated static methods + */ + protected static TypeRelations typeRelations = new TypeRelations(); + + @Deprecated + public static boolean compatible(SymTypeExpression left, + SymTypeExpression right) { + return typeRelations.compatible(left, right); + } + + @Deprecated + public static boolean isSubtypeOf(SymTypeExpression subType, SymTypeExpression superType){ + return typeRelations.isSubtypeOf(subType, superType); + } + + @Deprecated + protected static int calculateInheritanceDistance(SymTypeExpression specific, SymTypeExpression general){ + return typeRelations.calculateInheritanceDistance(specific, general); + } + + @Deprecated + public static int calculateInheritanceDistance(SymTypePrimitive specific, SymTypePrimitive general) { + return typeRelations.calculateInheritanceDistance(specific, general); + } + + @Deprecated + public static boolean isBoolean(SymTypeExpression type) { + return typeRelations.isBoolean(type); + } + + @Deprecated + public static boolean isInt(SymTypeExpression type) { + return typeRelations.isInt(type); + } + + @Deprecated + public static boolean isDouble(SymTypeExpression type) { + return typeRelations.isDouble(type); + } + + @Deprecated + public static boolean isFloat(SymTypeExpression type) { + return typeRelations.isFloat(type); + } + + @Deprecated + public static boolean isLong(SymTypeExpression type) { + return typeRelations.isLong(type); + } + + @Deprecated + public static boolean isChar(SymTypeExpression type) { + return typeRelations.isChar(type); + } + + @Deprecated + public static boolean isShort(SymTypeExpression type) { + return typeRelations.isShort(type); + } + + @Deprecated + public static boolean isByte(SymTypeExpression type) { + return typeRelations.isByte(type); + } + + @Deprecated + public static boolean isVoid(SymTypeExpression type) { + return typeRelations.isVoid(type); + } + + @Deprecated + public static boolean isString(SymTypeExpression type) { + return typeRelations.isString(type); + } +} + diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/TypeCheck/index.html b/monticore-grammar/src/main/java/de/monticore/types/check/TypeCheck/index.html new file mode 100644 index 0000000000..864dc70bcf --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/TypeCheck/index.html @@ -0,0 +1,826 @@ + + + + + + + + + + + + + + + + + + + + + TypeCheck - MontiCore + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + +

TypeCheck

+ + + + + +

In MontiCore, the TypeCheck is used to calculate the SymTypeExpression of a set of expressions, types and literals. +This is made possible by traversing the AST of an expression, type or literal, calculating the SymTypeExpression of its +subexpressions, -types or -literals and combining them to the SymTypeExpression of the main expression, type or literal.

+

Given infrastructure in MontiCore

+ +

What is the difference between TypeSymbols and SymTypeExpressions?

+

The TypeCheck uses the TypeSymbols of the BasicSymbols grammar and the handwritten SymTypeExpressions. +While they are very similar, there is a big difference between them and when to use them. +The TypeSymbols represent a type definition (example in Java: class List) while the SymTypeExpressions +represent a type usage (example in Java: List). There is only one type definition, but there can +be many type usages. The SymTypeExpression knows its corresponding +TypeSymbol (like the type usage knows its definition) and can refer to its methods and fields.

+

So when talking about a type definition, a type symbol, which can be stored and is +present only once in the symboltable, has to be used. A SymTypeExpression is not stored in the symboltable, +but refers to the definition of its type (its corresponding TypeSymbol) in the symboltable. Thus, a +SymTypeExpression can be used multiple times and represents the usage of a type.

+

The TypeCheck facade

+

The TypeCheck class of MontiCore is used as a facade to make TypeChecking easier for the user. +It needs a Derive-Class and/or a Synthesize-Class to be instantiated. +Here is a list of the methods and functions of the TypeCheck. +* SymTypeExpression typeOf(ASTExpression expr) (uses the Derive-Class to derive a SymTypeExpression from an ASTExpression) +* SymTypeExpression typeOf(ASTLiteral lit) (uses the Derive-Class to derive a SymTypeExpression from an ASTLiteral) +* SymTypeExpression symTypeFromAST(ASTType type) (uses the Synthesize-Class to derive a SymTypeExpression from an ASTType) +* static boolean compatible(SymTypeExpression left, SymTypeExpression right) (checks if the type of the right SymTypeExpression would be assignable to a variable with the type of the left SymTypeExpression) +* static boolean isSubTypeOf(SymTypeExpression sub, SymTypeExpression sup) (checks if the type of sub is a subtype of the type of sup) +* static boolean isInt(SymTypeExpression sym) (there are methods like this for all primitive types, checks if the SymTypeExpression represents the type 'int')

+

How does the TypeCheck work?

+

The TypeCheck can be used to derive a SymTypeExpression from an expression or type. +For example, when using an ASTPlusExpression '3+4', the TypeCheck returns a SymTypeExpression +representing the type 'int'. You can use the ASTPlusExpression as a parameter for the +method typeOf of the TypeCheck facade. This method delegates the calculation to your Derive-Class. +First it derives the SymTypeExpression of the subexpressions '3' and '4' (the LiteralExpressions +used in the PlusExpression) and then it calculates the SymTypeExpression of the whole PlusExpression +by combining the SymTypeExpressions of its subexpressions in the context of the '+' operator.

+

In general, the derivation of SymTypeExpressions for expressions first calculates the +SymTypeExpressions of its subexpressions and (depending on the results) then combines these +SymTypeExpression adequately to one SymTypeExpression for the whole expression.

+

Deriving the SymTypeExpression of a type is often easier than deriving the SymTypeExpression +of an expression. The ASTMCPrimitiveType 'int' will result in a SymTypeExpression 'int', the +ASTMCBasicGenericType 'java.util.Map' will result in a SymTypeExpression +'java.util.Map'.

+

The derivation of types is very similar to the derivation of expressions with regard to +subtypes and subexpressions. If a type has subtypes (like type arguments), then the SymTypeExpressions representing +these subtypes will be calculated first. They can be used to put together the SymTypeExpression +of the whole type.

+

Because both types and expressions are converted into SymTypeExpressions, you can compare them. +This is useful in the next example:

+
int a = "Hello";
+
+

The SymTypeExpression derived from the ASTPrimitiveType 'int' will be 'int', the SymTypeExpression +derived from the ASTLiteralExpression "Hello" will be 'String'. In a CoCo, you can check if they are compatible by +using the function 'compatible' of the TypeCheck facade.

+

I want to write a CoCo for my language that uses the TypeCheck - How?

+

You can use the TypeCheck facade. The facade needs a Derive-Class (for expressions +and literals) and/or a Synthesize-Class (for types) and calculates the SymTypeExpression of your +given expressions/literals/types. +

+Create a DelegatorVisitor which combines all expression grammars and literal grammars +used by your language. The DelegatorVisitor needs to implement the Interface +IDerive. Use this Delegator as Derive-Class in the TypeCheck facade. The +Synthesize-Class depends on the types grammar you use (see above-mentioned classes). +For an example of the Delegator-Visitor see +here. +

+If you want to create a Derive-Class for your expression/literal grammar, you have to +extend the Derive-Class of the supergrammar and implement the standard visitor of +your language. There you can override the traverse methods for your expressions/literals +so that it calculates the SymTypeExpression of the expression/literal (see implementation in one of the +above-mentioned classes). You can add your visitor to the DelegatorVisitor later on. +For an example of the Derive-Class for one language see here. +For an example of the Synthesize-Class for one language see here +

+Writing a CoCo to check the correctness of your type/expression/literal should be easy now that you +have the TypeCheck facade to use. Just use the correct Derive-Class and/or the correct +Synthesize-Class as parameters and check if the SymTypeExpression of your expression or type is +correctly calculated.

+Example for a CoCo: +

@Override
+public void check(ASTExpression expr){
+    YourDeriveClass deriveClass = new YourDeriveClass(...); //instance of your Derive-Class
+    YourSynthesizeClass synthesizeClass = new YourSynthesizeClass(...); //instance of your Synthesize-Class
+    TypeCheck check = new TypeCheck(synthesizeClass,deriveClass); //instance of the TypeCheck-facade, parameters are your Synthesize-Class and your Derive-Class
+    if(!"double".equals(check.typeOf(expr).print())){ //test if your expression is of the correct type (here: double)
+        Log.error(...); //your specified error message
+    }
+}
+

+

An example for the case that a plus expression needs to return 'int' can be found +here.

+

Further Information

+ + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/TypeCheckResult.java b/monticore-grammar/src/main/java/de/monticore/types/check/TypeCheckResult.java new file mode 100644 index 0000000000..b87c0151c5 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/TypeCheckResult.java @@ -0,0 +1,91 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import java.util.Optional; + +/** + * Wrapper class for the last result in a type check + */ +public class TypeCheckResult { + + protected Optional result; + + protected boolean type; + + protected boolean method; + + protected boolean field; + + public TypeCheckResult(){ + this.result = Optional.empty(); + this.field = false; + this.method = false; + this.type = false; + } + + public SymTypeExpression getResult() { + return result.get(); + } + + public boolean isPresentResult() { + return result.isPresent(); + } + + public void setResult(SymTypeExpression result){ + this.result = Optional.ofNullable(result); + } + + public void setResultAbsent() { + this.result = Optional.empty(); + } + + public void reset(){ + setResultAbsent(); + field = false; + method = false; + type = false; + } + + public void setType() { + this.type = true; + this.field = false; + this.method = false; + } + + public void setMethod() { + this.method = true; + this.type=false; + this.field=false; + } + + public void setField() { + this.field = true; + this.type=false; + this.method=false; + } + + public void unsetType(){ + this.type=false; + } + + public boolean isField() { + return field; + } + + public boolean isMethod() { + return method; + } + + public boolean isType() { + return type; + } + + public TypeCheckResult copy(){ + TypeCheckResult result = new TypeCheckResult(); + result.result = this.result; + result.type = type; + result.field = field; + result.method = method; + return result; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/TypeRelations.java b/monticore-grammar/src/main/java/de/monticore/types/check/TypeRelations.java new file mode 100644 index 0000000000..260252b215 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/TypeRelations.java @@ -0,0 +1,300 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; + +import java.util.List; +import java.util.stream.Collectors; + +import static de.monticore.types.check.SymTypePrimitive.unbox; + +/** + * This Class provides the default implementation of {@link ITypeRelations} + */ +public class TypeRelations implements ITypeRelations { + + @Override + public boolean compatible(SymTypeExpression left, SymTypeExpression right) { + if (left.isObscureType() || right.isObscureType()) { + return true; + } + if (left.isPrimitive() && right.isPrimitive()) { + SymTypePrimitive leftType = (SymTypePrimitive) left; + SymTypePrimitive rightType = (SymTypePrimitive) right; + if (isBoolean(leftType) && isBoolean(rightType)) { + return true; + } + if (isDouble(leftType) && rightType.isNumericType()) { + return true; + } + if (isFloat(leftType) && ((rightType.isIntegralType()) || isFloat(right))) { + return true; + } + if (isLong(leftType) && rightType.isIntegralType()) { + return true; + } + if (isInt(leftType) && rightType.isIntegralType() && !isLong(right)) { + return true; + } + if (isChar(leftType) && isChar(right)) { + return true; + } + if (isShort(leftType) && (isByte(right) || isShort(right))) { + return true; + } + if (isByte(leftType) && isByte(right)) { + return true; + } + return false; + } + else if (!left.isPrimitive() && right.isNullType()) { + return true; + } + else if (unbox(left.printFullName()).equals(unbox(right.printFullName()))) { + return true; + } + else if (isSubtypeOf(right, left)) { + return true; + } + else if (right.printFullName().equals(left.printFullName())) { + return true; + } + else if (left.deepEquals(right) || right.deepEquals(left)) { + return true; + } + return false; + } + + @Override + public boolean isSubtypeOf(SymTypeExpression subType, SymTypeExpression superType) { + if (subType.isObscureType() || superType.isObscureType()) { + return true; + } + if (unbox(subType.printFullName()).equals(unbox(superType.printFullName()))) { + return true; + } + if (subType.isPrimitive() && superType.isPrimitive()) { + SymTypePrimitive sub = (SymTypePrimitive) subType; + SymTypePrimitive supert = (SymTypePrimitive) superType; + if (isDouble(supert) && sub.isNumericType() && !isDouble(sub)) { + return true; + } + if (isFloat(supert) && sub.isIntegralType()) { + return true; + } + if (isLong(supert) && sub.isIntegralType() && !isLong(subType)) { + return true; + } + if (isInt(supert) && sub.isIntegralType() && !isLong(subType) && !isInt(subType)) { + return true; + } + return false; + } + else if ((subType.isPrimitive() && !superType.isPrimitive()) || + (superType.isPrimitive() && !subType.isPrimitive())) { + return false; + } + return isSubtypeOfRec(subType, superType); + } + + protected boolean isSubtypeOfRec(SymTypeExpression subType, SymTypeExpression superType) { + if (!subType.getTypeInfo().getSuperTypesList().isEmpty()) { + for (SymTypeExpression type : subType.getTypeInfo().getSuperTypesList()) { + if (type.print().equals(superType.print())) { + return true; + } + if (type.isGenericType() && superType.isGenericType()) { + //TODO check recursively, this is only a hotfix, see #2977 + SymTypeOfGenerics typeGen = (SymTypeOfGenerics) type; + SymTypeOfGenerics supTypeGen = (SymTypeOfGenerics) superType; + if (typeGen.printTypeWithoutTypeArgument() + .equals(supTypeGen.printTypeWithoutTypeArgument()) + && typeGen.sizeArguments() == supTypeGen.sizeArguments()) { + boolean success = true; + for (int i = 0; i < typeGen.sizeArguments(); i++) { + if (!typeGen.getArgument(i).isTypeVariable()) { + if (!typeGen.getArgument(i).print().equals(supTypeGen.getArgument(i).print())) { + success = false; + } + } + } + if (success) { + return true; + } + } + } + } + } + boolean subtype = false; + for (int i = 0; i < subType.getTypeInfo().getSuperTypesList().size(); i++) { + if (isSubtypeOf(subType.getTypeInfo().getSuperTypesList().get(i), superType)) { + subtype = true; + break; + } + } + return subtype; + } + + @Override + public int calculateInheritanceDistance(SymTypeExpression specific, + SymTypeExpression general) { + if (specific.isPrimitive() && general.isPrimitive()) { + return calculateInheritanceDistance((SymTypePrimitive) specific, (SymTypePrimitive) general); + } + else if (specific.deepEquals(general)) { + return 0; + } + else if (!isSubtypeOf(specific, general)) { + return -1; + } + else { + List superTypes = specific.getTypeInfo().getSuperTypesList(); + List superTypesSpecificity = superTypes.stream() + .map(s -> calculateInheritanceDistance(s, general)).collect(Collectors.toList()); + int min = -1; + for (int specificity : superTypesSpecificity) { + if (min != -1 && specificity != -1 && specificity < min) { + min = specificity; + } + else if (min == -1 && specificity != -1) { + min = specificity; + } + } + if (min == -1) { + return -1; + } + else { + return min + 1; + } + } + } + + @Override + public int calculateInheritanceDistance(SymTypePrimitive specific, SymTypePrimitive general) { + if (unbox(specific.print()).equals(unbox(general.print()))) { + return 0; + } + if (specific.isNumericType() && general.isNumericType()) { + if (isByte(specific)) { + if (isShort(general)) { + return 1; + } + else if (isInt(general)) { + return 2; + } + else if (isLong(general)) { + return 3; + } + else if (isFloat(general)) { + return 4; + } + else if (isDouble(general)) { + return 5; + } + } + else if (isChar(specific) || isShort(specific)) { + if (isInt(general)) { + return 1; + } + else if (isLong(general)) { + return 2; + } + else if (isFloat(general)) { + return 3; + } + else if (isDouble(general)) { + return 4; + } + } + else if (isInt(specific)) { + if (isLong(general)) { + return 1; + } + else if (isFloat(general)) { + return 2; + } + else if (isDouble(general)) { + return 3; + } + } + else if (isLong(specific)) { + if (isFloat(general)) { + return 1; + } + else if (isDouble(general)) { + return 2; + } + } + else if (isFloat(specific)) { + if (isDouble(general)) { + return 1; + } + } + return -1; + } + else { + return -1; + } + } + + @Override + public boolean isBoolean(SymTypeExpression type) { + return BasicSymbolsMill.BOOLEAN.equals(unbox(type.print())); + } + + @Override + public boolean isInt(SymTypeExpression type) { + return BasicSymbolsMill.INT.equals(unbox(type.print())); + } + + @Override + public boolean isDouble(SymTypeExpression type) { + return BasicSymbolsMill.DOUBLE.equals(unbox(type.print())); + } + + @Override + public boolean isFloat(SymTypeExpression type) { + return BasicSymbolsMill.FLOAT.equals(unbox(type.print())); + } + + @Override + public boolean isLong(SymTypeExpression type) { + return BasicSymbolsMill.LONG.equals(unbox(type.print())); + } + + @Override + public boolean isChar(SymTypeExpression type) { + return BasicSymbolsMill.CHAR.equals(unbox(type.print())); + } + + @Override + public boolean isShort(SymTypeExpression type) { + return BasicSymbolsMill.SHORT.equals(unbox(type.print())); + } + + @Override + public boolean isByte(SymTypeExpression type) { + return BasicSymbolsMill.BYTE.equals(unbox(type.print())); + } + + @Override + public boolean isVoid(SymTypeExpression type) { + return BasicSymbolsMill.VOID.equals(unbox(type.print())); + } + + @Override + public boolean isString(SymTypeExpression type) { + return "String".equals(type.print()); + } + + @Override + public boolean isNumericType(SymTypeExpression type) { + return isIntegralType(type) || isFloat(type) || isDouble(type); + } + + @Override + public boolean isIntegralType(SymTypeExpression type) { + return isLong(type) || isInt(type) || isChar(type) || isByte(type) || isShort(type); + } +} + diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/helpers/DefiningSymbolHandler.java b/monticore-grammar/src/main/java/de/monticore/types/check/helpers/DefiningSymbolHandler.java new file mode 100644 index 0000000000..caf9b2795f --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/helpers/DefiningSymbolHandler.java @@ -0,0 +1,85 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check.helpers; + +import de.monticore.expressions.commonexpressions._ast.ASTCallExpression; +import de.monticore.expressions.commonexpressions._ast.ASTFieldAccessExpression; +import de.monticore.expressions.commonexpressions._visitor.CommonExpressionsHandler; +import de.monticore.expressions.commonexpressions._visitor.CommonExpressionsTraverser; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.expressionsbasis._ast.ASTNameExpression; +import de.monticore.expressions.expressionsbasis._visitor.ExpressionsBasisHandler; +import de.monticore.expressions.expressionsbasis._visitor.ExpressionsBasisTraverser; +import de.monticore.symboltable.ISymbol; + +/** + * Provides a handlers for CommonExpressions and ExpressionBasis that implement part of the logic of + * {@link DefiningSymbolSetter}. Compose this handler with a traverser and potentially handlers for other languages to + * get a full implementation. The traverser/handling pattern is used to downcast expressions to their real types. + * Thereby we can easily set {@link ASTNameExpression#setDefiningSymbol(ISymbol)} or similar methods while maintaining + * flexibility for language composition. + */ +public class DefiningSymbolHandler implements ExpressionsBasisHandler, CommonExpressionsHandler { + + protected CommonExpressionsTraverser traverser; + + protected ASTExpression expressionToLink; + protected ISymbol symbolToLink; + + @Override + public CommonExpressionsTraverser getTraverser() { + return this.traverser; + } + + @Override + public void setTraverser(CommonExpressionsTraverser traverser) { + this.traverser = traverser; + } + + /** @param traverser must be of type {@link CommonExpressionsTraverser} */ + @Override + public void setTraverser(ExpressionsBasisTraverser traverser) { + if(traverser instanceof CommonExpressionsTraverser) { + this.traverser = (CommonExpressionsTraverser) traverser; + } else { + throw new UnsupportedOperationException("Traverser for " + this.getClass().getName() + + " must be a CommonExpressionsTraverser"); + } + } + + public ASTExpression getExpressionToLink() { + return expressionToLink; + } + + public void setExpressionToLink(ASTExpression expressionToLink) { + this.expressionToLink = expressionToLink; + } + + public ISymbol getSymbolToLink() { + return symbolToLink; + } + + public void setSymbolToLink(ISymbol symbolToLink) { + this.symbolToLink = symbolToLink; + } + + @Override + public void handle(ASTNameExpression nameExpr) { + if(nameExpr == this.expressionToLink) { + nameExpr.setDefiningSymbol(this.symbolToLink); + } + } + + @Override + public void handle(ASTFieldAccessExpression fieldAccessExpr) { + if (fieldAccessExpr == this.expressionToLink) { + fieldAccessExpr.setDefiningSymbol(this.symbolToLink); + } + } + + @Override + public void handle(ASTCallExpression callExpr) { + if (callExpr == this.expressionToLink) { + callExpr.setDefiningSymbol(this.symbolToLink); + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/helpers/DefiningSymbolSetter.java b/monticore-grammar/src/main/java/de/monticore/types/check/helpers/DefiningSymbolSetter.java new file mode 100644 index 0000000000..de056762eb --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/helpers/DefiningSymbolSetter.java @@ -0,0 +1,14 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check.helpers; + +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.symboltable.ISymbol; + +/** Provides an easy fassade to set the defining symbol for name, field access, and call expressions. */ +public interface DefiningSymbolSetter { + /** + * Sets the symbol as the {@code definingSymbol} of the expression, given that the expression provides this + * possibility. + */ + void setDefiningSymbol(ASTExpression expressionToLink, ISymbol symbolToLink); +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/helpers/DefiningSymbolSetter4CommonExpressions.java b/monticore-grammar/src/main/java/de/monticore/types/check/helpers/DefiningSymbolSetter4CommonExpressions.java new file mode 100644 index 0000000000..08f990ce87 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/helpers/DefiningSymbolSetter4CommonExpressions.java @@ -0,0 +1,36 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check.helpers; + +import de.monticore.expressions.commonexpressions.CommonExpressionsMill; +import de.monticore.expressions.commonexpressions._visitor.CommonExpressionsTraverser; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.symboltable.ISymbol; + +/** + * Implements {@link DefiningSymbolSetter} for common expressions by using traversers and handlers to + * automatically down cast expressions to their concrete type. + */ +public class DefiningSymbolSetter4CommonExpressions implements DefiningSymbolSetter { + + protected CommonExpressionsTraverser traverser; + + DefiningSymbolHandler handler4commonAndBasicExpressions; + + public DefiningSymbolSetter4CommonExpressions() { + this.traverser = CommonExpressionsMill.traverser(); + init(); + } + + protected void init() { + this.handler4commonAndBasicExpressions = new DefiningSymbolHandler(); + this.traverser.setCommonExpressionsHandler(handler4commonAndBasicExpressions); + this.traverser.setExpressionsBasisHandler(handler4commonAndBasicExpressions); + } + + @Override + public void setDefiningSymbol(ASTExpression expressionToLink, ISymbol symbolToLink) { + this.handler4commonAndBasicExpressions.setExpressionToLink(expressionToLink); + this.handler4commonAndBasicExpressions.setSymbolToLink(symbolToLink); + expressionToLink.accept(this.traverser); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/helpers/ExprToNamePair.java b/monticore-grammar/src/main/java/de/monticore/types/check/helpers/ExprToNamePair.java new file mode 100644 index 0000000000..a6b4d30a67 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/helpers/ExprToNamePair.java @@ -0,0 +1,28 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check.helpers; + +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; + +/** + * Abstraction over the pair of an expression that is linked to a name. + */ +public final class ExprToNamePair { + private final ASTExpression expression; + private final String name; + private ExprToNamePair(ASTExpression expression, String name) { + this.expression = expression; + this.name = name; + } + + public static ExprToNamePair of(ASTExpression expression, String name) { + return new ExprToNamePair(expression, name); + } + + public ASTExpression getExpression() { + return expression; + } + + public String getName() { + return name; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/helpers/ExprToOptNamePair.java b/monticore-grammar/src/main/java/de/monticore/types/check/helpers/ExprToOptNamePair.java new file mode 100644 index 0000000000..560076d2f9 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/helpers/ExprToOptNamePair.java @@ -0,0 +1,30 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check.helpers; + +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; + +import java.util.Optional; + +/** + * Abstraction over the pair of an expression that is optionally linked to a name. + */ +public final class ExprToOptNamePair { + private final ASTExpression expression; + private final Optional name; + private ExprToOptNamePair(ASTExpression expression, Optional name) { + this.expression = expression; + this.name = name; + } + + public static ExprToOptNamePair of(ASTExpression expression, Optional name) { + return new ExprToOptNamePair(expression, name); + } + + public ASTExpression getExpression() { + return expression; + } + + public Optional getName() { + return name; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/helpers/SubExprNameExtractionResult.java b/monticore-grammar/src/main/java/de/monticore/types/check/helpers/SubExprNameExtractionResult.java new file mode 100644 index 0000000000..2030d1a434 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/helpers/SubExprNameExtractionResult.java @@ -0,0 +1,105 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check.helpers; + +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; + +import java.util.*; +import java.util.stream.Collectors; + +public class SubExprNameExtractionResult { + + /** + * Data structure that contains the subexpressions (usually these are the subexpressions of a single composed + * expression). Each subexpression that represents a name is saved associated with that name. Other expressions are + * associated with an empty optional. + */ + protected List subExpressions = new LinkedList<>(); + + public void reset() { + this.subExpressions = new LinkedList<>(); + } + + public void setSubExpressions(List subExpressions) { + this.subExpressions = subExpressions; + } + + /** + * Only adds the given expression to the start of the {@link #subExpressions} list, if it does not exist there yet. + * The expression is added associated with an empty optional, indicating that the expression is not a valid name. + */ + public void maybeAppendInvalidExprAtStart(ASTExpression expression) { + ExprToOptNamePair toBeAdded = ExprToOptNamePair.of(expression, Optional.empty()); + + if (subExpressions.isEmpty() || !subExpressions.get(0).getExpression().equals(expression)) { + subExpressions.add(0, toBeAdded); + } + } + + /** + * If the given expression is already at the start of the {@link #subExpressions} list, then the associated name is + * set to be {@code name}. Else, the given expression is inserted at the start of the list, associated with + * {@code name}. + */ + protected void putNameAtStart(ASTExpression expression, String name) { + ExprToOptNamePair toBeAdded = ExprToOptNamePair.of(expression, Optional.of(name)); + + if(subExpressions.isEmpty() || !subExpressions.get(0).getExpression().equals(expression)) { + subExpressions.add(0, toBeAdded); + } else { + subExpressions.set(0, toBeAdded); + } + } + + /** + * @return If all recorded {@link #subExpressions}s represent name parts, then these name parts are returned. Every + * name part (represented as a String) is associated with the expression that defines it. On the other side, if any + * part of the expression is not representing a name, an empty optional is returned by this method. + */ + public Optional> getNamePartsIfValid() { + if (resultIsValidName()) { + return Optional.of( + subExpressions.stream() + .map(oldPair -> ExprToNamePair.of(oldPair.getExpression(), oldPair.getName().get())) + .collect(Collectors.toList()) + ); + } else { + return Optional.empty(); + } + } + + /** @return An unmodifiable list of {@link #subExpressions}. Expressions that represent names are associated with + * their corresponding name. Others are associated with an empty optional. + */ + public List getNamePartsRaw() { + return Collections.unmodifiableList(this.subExpressions); + } + + /** + * @return If the last part of {@link #subExpressions} represents a name, then this method returns this name. Else, an + * empty optional is returned. + */ + public Optional getLastName() { + if(subExpressions.isEmpty()) { + return Optional.empty(); + } else { + return subExpressions.get(subExpressions.size() - 1).getName(); + } + } + + /** + * @return Whether all {@link #subExpressions} represent names. + */ + public boolean resultIsValidName() { + return !subExpressions.isEmpty() && subExpressions.stream().allMatch(p -> p.getName().isPresent()); + } + + /** + * @return A new instance with a unmodifiable {@link #subExpressions} list. + */ + public SubExprNameExtractionResult copy() { + SubExprNameExtractionResult copy = new SubExprNameExtractionResult(); + copy.setSubExpressions(new ArrayList<>(this.getNamePartsRaw())); + + return copy; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/helpers/SubExprNameExtractionVisitor.java b/monticore-grammar/src/main/java/de/monticore/types/check/helpers/SubExprNameExtractionVisitor.java new file mode 100644 index 0000000000..7f61f75808 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/helpers/SubExprNameExtractionVisitor.java @@ -0,0 +1,68 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check.helpers; + +import de.monticore.expressions.commonexpressions._ast.ASTFieldAccessExpression; +import de.monticore.expressions.commonexpressions._visitor.CommonExpressionsVisitor2; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.expressionsbasis._ast.ASTNameExpression; +import de.monticore.expressions.expressionsbasis._visitor.ExpressionsBasisVisitor2; +import de.monticore.types.check.DeriveSymTypeOfCommonExpressions; + +/** + * The usage of this class is to collect information about a call expression + * so that it is easier to resolve in the TypeCheck + * + * We take the call expression, and split it into a qualified name and an expression having a type + * such that type.qualifiedName(args) has the type of the call expression + * if no type can be found, the expression is set to the inner expression for compatibility reasons + * This makes it far easier to calculate the type of call expressions. + * + * To use this class: + *
    + *
  1. Create an inheritance traverser
  2. + *
  3. Create an instance of this class and set {@link #subExpressions}. This is a shared state object that should be + * synchronized among the all name extracting visitors of the different languages
  4. + *
  5. Register the instance of this class to be a visitor for ExpressionsBasis and CommonExpressions in the + * traverser.
  6. + *
, + * + * This class is used in {@link DeriveSymTypeOfCommonExpressions}. + */ + +public class SubExprNameExtractionVisitor implements CommonExpressionsVisitor2, ExpressionsBasisVisitor2 { + + /** @see #subExpressions */ + public SubExprNameExtractionResult getSubExpressions() { + return subExpressions; + } + + /** + * Collects the name parts of the expression that the visitor is applied to. + */ + protected SubExprNameExtractionResult subExpressions; + + public SubExprNameExtractionVisitor(SubExprNameExtractionResult commonNamePartsRecord) { + this.subExpressions = commonNamePartsRecord; + } + + /** + * Default implementation that marks an expression as *not* being a name part. This marking will be ignored if the + * expression indeed represents a valid name. Use + * {@link SubExprNameExtractionResult#putNameAtStart(ASTExpression, String)} on {@link #subExpressions} in the visit + * implementations of the expressions representing names to overwrite this "invalidity" marking. + */ + @Override + public void visit(ASTExpression expr) { + subExpressions.maybeAppendInvalidExprAtStart(expr); + } + + @Override + public void visit(ASTFieldAccessExpression expr){ + subExpressions.putNameAtStart(expr, expr.getName()); + } + + @Override + public void visit(ASTNameExpression expr){ + subExpressions.putNameAtStart(expr, expr.getName()); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/helpers/SubExprNameExtractor.java b/monticore-grammar/src/main/java/de/monticore/types/check/helpers/SubExprNameExtractor.java new file mode 100644 index 0000000000..2c265ad2cc --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/helpers/SubExprNameExtractor.java @@ -0,0 +1,21 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check.helpers; + +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.types.check.DeriveSymTypeOfCommonExpressions; + +/** + * Collects information about potential naming parts of a call expression, field access expressions, or other composed + * expressions so that it is easier to resolve fields / types / methods in the TypeCheck. E.g., this interface is used + * in {@link DeriveSymTypeOfCommonExpressions}. + */ +public interface SubExprNameExtractor { + + /** + * Collect the subexpressions that the expression call is composed of and if a subexpression represents a name, + * associate this name with that subexpression. + * @return the {@link SubExprNameExtractionResult} that contains the information about the subexpressions and + * potentially represented names. + */ + SubExprNameExtractionResult calculateNameParts(ASTExpression expression); +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/helpers/SubExprNameExtractor4CommonExpressions.java b/monticore-grammar/src/main/java/de/monticore/types/check/helpers/SubExprNameExtractor4CommonExpressions.java new file mode 100644 index 0000000000..c917a063ac --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/helpers/SubExprNameExtractor4CommonExpressions.java @@ -0,0 +1,49 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check.helpers; + +import de.monticore.expressions.commonexpressions.CommonExpressionsMill; +import de.monticore.expressions.commonexpressions._visitor.CommonExpressionsTraverser; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; + +/** + * Provides a complete traverser for CommonExpressions and ExpressionBasis, using + * {@link SubExprNameExtractionVisitor} as a visitor. + */ +public class SubExprNameExtractor4CommonExpressions implements SubExprNameExtractor { + + /** + * Collects the name parts of the expression that the visitor is applied to. + */ + protected SubExprNameExtractionResult subExpressions; + + protected final CommonExpressionsTraverser traverser; + + protected final SubExprNameExtractionVisitor nameCalculator; + + public SubExprNameExtractor4CommonExpressions() { + this(CommonExpressionsMill.inheritanceTraverser()); + } + + public SubExprNameExtractor4CommonExpressions(CommonExpressionsTraverser traverser) { + this.subExpressions = new SubExprNameExtractionResult(); + this.traverser = traverser; + this.nameCalculator = new SubExprNameExtractionVisitor(subExpressions); + init(); + } + + public CommonExpressionsTraverser getTraverser() { + return traverser; + } + + protected void init() { + this.traverser.add4ExpressionsBasis(this.nameCalculator); + this.traverser.add4CommonExpressions(this.nameCalculator); + } + + @Override + public SubExprNameExtractionResult calculateNameParts(ASTExpression expression) { + this.subExpressions.reset(); + expression.accept(this.getTraverser()); + return subExpressions.copy(); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/types3wrapper/TypeCheck3AsIDerive.java b/monticore-grammar/src/main/java/de/monticore/types/check/types3wrapper/TypeCheck3AsIDerive.java new file mode 100644 index 0000000000..edd527fbb3 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/types3wrapper/TypeCheck3AsIDerive.java @@ -0,0 +1,63 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.types.check.types3wrapper; + +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.expressionsbasis.types3.util.ILValueRelations; +import de.monticore.literals.mcliteralsbasis._ast.ASTLiteral; +import de.monticore.types.check.IDerive; +import de.monticore.types.check.TypeCheckResult; +import de.monticore.types3.Type4Ast; +import de.monticore.visitor.ITraverser; + +/** + * IDerive using the TypeCheck3. + * Does not set isMethod of TypeCheckResult. + * While the TypeCheck3 should be used directly, + * this can be used to try the TypeCheck without major rewrites. + */ +public class TypeCheck3AsIDerive implements IDerive { + + protected Type4Ast type4Ast; + + protected ITraverser typeTraverser; + + protected ILValueRelations lValueRelations; + + /** + * type4Ast should be filled by the typeTraverser, + * thus, this and TypeCheck3AsISynthesize should have the same type4Ast. + */ + public TypeCheck3AsIDerive( + ITraverser typeTraverser, + Type4Ast type4Ast, + ILValueRelations lValueRelations + ) { + this.typeTraverser = typeTraverser; + this.type4Ast = type4Ast; + this.lValueRelations = lValueRelations; + } + + @Override + public TypeCheckResult deriveType(ASTExpression expr) { + TypeCheckResult result = new TypeCheckResult(); + if (!type4Ast.hasTypeOfExpression(expr)) { + expr.accept(typeTraverser); + } + result.setResult(type4Ast.getTypeOfExpression(expr)); + if (lValueRelations.isLValue(expr)) { + result.setField(); + } + return result; + } + + @Override + public TypeCheckResult deriveType(ASTLiteral lit) { + TypeCheckResult result = new TypeCheckResult(); + if (!type4Ast.hasTypeOfExpression(lit)) { + lit.accept(typeTraverser); + } + result.setResult(type4Ast.getTypeOfExpression(lit)); + return result; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/types3wrapper/TypeCheck3AsISynthesize.java b/monticore-grammar/src/main/java/de/monticore/types/check/types3wrapper/TypeCheck3AsISynthesize.java new file mode 100644 index 0000000000..942e5627b8 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/types3wrapper/TypeCheck3AsISynthesize.java @@ -0,0 +1,65 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.types.check.types3wrapper; + +import de.monticore.types.check.ISynthesize; +import de.monticore.types.check.TypeCheckResult; +import de.monticore.types.mcbasictypes._ast.ASTMCQualifiedName; +import de.monticore.types.mcbasictypes._ast.ASTMCReturnType; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types3.Type4Ast; +import de.monticore.visitor.ITraverser; + +/** + * ISynthesize using the TypeCheck3. + * While the TypeCheck3 should be used directly, + * this can be used to try the TypeCheck without major rewrites. + */ +public class TypeCheck3AsISynthesize implements ISynthesize { + + protected Type4Ast type4Ast; + + protected ITraverser typeTraverser; + + /** + * type4Ast should be filled by the typeTraverser + */ + public TypeCheck3AsISynthesize( + ITraverser typeTraverser, + Type4Ast type4Ast) { + this.typeTraverser = typeTraverser; + this.type4Ast = type4Ast; + } + + @Override + public TypeCheckResult synthesizeType(ASTMCType type) { + TypeCheckResult result = new TypeCheckResult(); + if (!type4Ast.hasTypeOfTypeIdentifier(type)) { + type.accept(typeTraverser); + } + result.setResult(type4Ast.getTypeOfTypeIdentifier(type)); + result.setType(); + return result; + } + + @Override + public TypeCheckResult synthesizeType(ASTMCReturnType type) { + TypeCheckResult result = new TypeCheckResult(); + if (!type4Ast.hasTypeOfTypeIdentifier(type)) { + type.accept(typeTraverser); + } + result.setResult(type4Ast.getTypeOfTypeIdentifier(type)); + result.setType(); + return result; + } + + @Override + public TypeCheckResult synthesizeType(ASTMCQualifiedName qName) { + TypeCheckResult result = new TypeCheckResult(); + if (!type4Ast.hasTypeOfTypeIdentifier(qName)) { + qName.accept(typeTraverser); + } + result.setResult(type4Ast.getTypeOfTypeIdentifier(qName)); + result.setType(); + return result; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/check/types3wrapper/TypeCheck3AsTypeCalculator.java b/monticore-grammar/src/main/java/de/monticore/types/check/types3wrapper/TypeCheck3AsTypeCalculator.java new file mode 100644 index 0000000000..a9317ebca9 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/check/types3wrapper/TypeCheck3AsTypeCalculator.java @@ -0,0 +1,54 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.types.check.types3wrapper; + +import de.monticore.expressions.expressionsbasis.types3.util.ILValueRelations; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.TypeCalculator; +import de.monticore.types3.ISymTypeRelations; +import de.monticore.types3.Type4Ast; +import de.monticore.visitor.ITraverser; + +/** + * the TypeCalculator class using the TypeCheck3. + * While the TypeCheck3 should be used directly, + * this can be used to try the TypeCheck without major rewrites. + */ +public class TypeCheck3AsTypeCalculator extends TypeCalculator { + + protected ISymTypeRelations symTypeRelations; + + protected Type4Ast type4Ast; + + protected ITraverser typeTraverser; + + /** + * @param typeTraverser traverser filling type4Ast, language specific + * @param type4Ast a map of types to be filled + * @param symTypeRelations relations on SymTypes, language specific + * @param lValueRelations is expression a variable?, language specific + */ + public TypeCheck3AsTypeCalculator( + ITraverser typeTraverser, + Type4Ast type4Ast, + ISymTypeRelations symTypeRelations, + ILValueRelations lValueRelations + ) { + super( + new TypeCheck3AsISynthesize(typeTraverser, type4Ast), + new TypeCheck3AsIDerive(typeTraverser, type4Ast, lValueRelations) + ); + this.typeTraverser = typeTraverser; + this.type4Ast = type4Ast; + this.symTypeRelations = symTypeRelations; + } + + @Override + public boolean compatible(SymTypeExpression left, SymTypeExpression right) { + return symTypeRelations.isCompatible(left, right); + } + + @Override + public boolean isSubtypeOf(SymTypeExpression subType, SymTypeExpression superType) { + return symTypeRelations.isSubTypeOf(subType, superType); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/mcarraytypes/MCArrayTypesMill.java b/monticore-grammar/src/main/java/de/monticore/types/mcarraytypes/MCArrayTypesMill.java new file mode 100644 index 0000000000..2995720269 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/mcarraytypes/MCArrayTypesMill.java @@ -0,0 +1,48 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.mcarraytypes; + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.types.prettyprint.MCArrayTypesFullPrettyPrinter; + +public class MCArrayTypesMill extends MCArrayTypesMillTOP { + + protected static MCArrayTypesMill mcArrayTypesPrettyPrinter; + + protected static MCArrayTypesFullPrettyPrinter prettyPrinter; + + /** + * Static getter for the pretty printer that delegates to the non static implementation. + * Only one pretty printer object is created and reused. + * @return the pretty printer instance + */ + @Deprecated(forRemoval = true) + public static MCArrayTypesFullPrettyPrinter mcArrayTypesPrettyPrinter () { + + if (mcArrayTypesPrettyPrinter == null) { + mcArrayTypesPrettyPrinter = getMill(); + } + if (prettyPrinter == null) { + prettyPrinter = getPrettyPrinter(); + } + return mcArrayTypesPrettyPrinter._mcArrayTypesPrettyPrinter(); + + } + + protected MCArrayTypesFullPrettyPrinter _mcArrayTypesPrettyPrinter () { + + if (mcArrayTypesPrettyPrinter == null) { + mcArrayTypesPrettyPrinter = getMill(); + } + if (prettyPrinter == null) { + prettyPrinter = getPrettyPrinter(); + } + // as pretty printer are stateful, it needs to be cleared before it is provided + prettyPrinter.getPrinter().clearBuffer(); + return prettyPrinter; + } + + protected static MCArrayTypesFullPrettyPrinter getPrettyPrinter() { + return new MCArrayTypesFullPrettyPrinter(new IndentPrinter()); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/mcarraytypes/_ast/ASTMCArrayType.java b/monticore-grammar/src/main/java/de/monticore/types/mcarraytypes/_ast/ASTMCArrayType.java new file mode 100644 index 0000000000..75462a6e8f --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/mcarraytypes/_ast/ASTMCArrayType.java @@ -0,0 +1,25 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.mcarraytypes._ast; + +import de.monticore.symboltable.ISymbol; + +import java.util.Optional; + +public class ASTMCArrayType extends ASTMCArrayTypeTOP { + + protected ISymbol definingSymbol; + + @Override + public Optional getDefiningSymbol() { + return Optional.ofNullable(this.definingSymbol); + } + + @Override + public void setDefiningSymbol(ISymbol symbol) { + this.definingSymbol = symbol; + } + + public String printTypeWithoutBrackets() { + return this.getMCType().printType(); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/mcarraytypes/_prettyprint/MCArrayTypesPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/types/mcarraytypes/_prettyprint/MCArrayTypesPrettyPrinter.java new file mode 100644 index 0000000000..cf20bb031c --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/mcarraytypes/_prettyprint/MCArrayTypesPrettyPrinter.java @@ -0,0 +1,29 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.mcarraytypes._prettyprint; + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.types.mcarraytypes._ast.ASTMCArrayType; + +public class MCArrayTypesPrettyPrinter extends MCArrayTypesPrettyPrinterTOP { + + public MCArrayTypesPrettyPrinter(IndentPrinter printer, boolean printComments) { + super(printer, printComments); + } + + @Override + public void handle(ASTMCArrayType node) { + if (this.isPrintComments()) { + de.monticore.prettyprint.CommentPrettyPrinter.printPreComments(node, getPrinter()); + } + + // The action + astrule combination of dimension requires manual work + node.getMCType().accept(getTraverser()); + for (int i = 0; i < node.getDimensions(); i++) { + getPrinter().print("[]"); + } + + if (this.isPrintComments()) { + de.monticore.prettyprint.CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/mcarraytypes/types3/MCArrayTypesTypeVisitor.java b/monticore-grammar/src/main/java/de/monticore/types/mcarraytypes/types3/MCArrayTypesTypeVisitor.java new file mode 100644 index 0000000000..539d5627b4 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/mcarraytypes/types3/MCArrayTypesTypeVisitor.java @@ -0,0 +1,32 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.mcarraytypes.types3; + +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types.mcarraytypes._ast.ASTMCArrayType; +import de.monticore.types.mcarraytypes._visitor.MCArrayTypesVisitor2; +import de.monticore.types3.AbstractTypeVisitor; +import de.se_rwth.commons.logging.Log; + +public class MCArrayTypesTypeVisitor extends AbstractTypeVisitor + implements MCArrayTypesVisitor2 { + + @Override + public void endVisit(ASTMCArrayType arrayType) { + SymTypeExpression type; + SymTypeExpression innerType = + getType4Ast().getPartialTypeOfTypeId(arrayType.getMCType()); + if (innerType.isObscureType()) { + Log.error("0xE9CDC The type of the array could not be synthesized", + arrayType.get_SourcePositionStart(), + arrayType.get_SourcePositionEnd() + ); + type = SymTypeExpressionFactory.createObscureType(); + } + else { + type = SymTypeExpressionFactory + .createTypeArray(innerType, arrayType.getDimensions()); + } + getType4Ast().setTypeOfTypeIdentifier(arrayType, type); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/mcbasictypes/CalculatePotentialQNames.java b/monticore-grammar/src/main/java/de/monticore/types/mcbasictypes/CalculatePotentialQNames.java new file mode 100644 index 0000000000..5288fc060b --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/mcbasictypes/CalculatePotentialQNames.java @@ -0,0 +1,38 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.mcbasictypes; + +import com.google.common.collect.Lists; +import de.monticore.types.mcbasictypes._ast.ASTMCImportStatement; +import de.monticore.types.mcbasictypes._ast.ASTMCPackageDeclaration; + +import java.util.List; + +/** + * This class can be used to calculate potential qualified names for a symbol. + * It needs the packageDeclaration of the model, the import statments specified in the model and the simple name of the symbol. + */ +public class CalculatePotentialQNames { + + public static List calculateQualifiedNames(ASTMCPackageDeclaration packageDeclaration, List importStatements, String simpleName){ + List potentialQNames = Lists.newArrayList(); + //if in a spanned scope + potentialQNames.add(simpleName); + //if in the same package + if(packageDeclaration!=null && !packageDeclaration.getMCQualifiedName().getQName().isEmpty()) { + potentialQNames.add(packageDeclaration.getMCQualifiedName().getQName() + "." + simpleName); + } + //import statements + for(ASTMCImportStatement importStatement: importStatements){ + if(importStatement.getMCQualifiedName() != null && !importStatement.getMCQualifiedName().getQName().isEmpty()) { + if (importStatement.isStar()) { + potentialQNames.add(importStatement.getQName() + "." + simpleName); + } else { + if (importStatement.getMCQualifiedName().getBaseName().equals(simpleName)) { + potentialQNames.add(importStatement.getQName()); + } + } + } + } + return potentialQNames; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/mcbasictypes/MCBasicTypesMill.java b/monticore-grammar/src/main/java/de/monticore/types/mcbasictypes/MCBasicTypesMill.java new file mode 100644 index 0000000000..c38f6f1614 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/mcbasictypes/MCBasicTypesMill.java @@ -0,0 +1,53 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.types.mcbasictypes; + + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.types.prettyprint.MCBasicTypesFullPrettyPrinter; + +/** + * Extension of the generated Mill to provide a (currently handcrafted) + * prettyprinter for this language + */ +public class MCBasicTypesMill extends MCBasicTypesMillTOP { + + protected static MCBasicTypesMill mcBasicTypesPrettyPrinter; + + protected static MCBasicTypesFullPrettyPrinter prettyPrinter; + + /** + * Static getter for the pretty printer that delegates to the non static implementation. + * Only one pretty printer object is created and reused. + * @return the pretty printer instance + */ + @Deprecated(forRemoval = true) + public static MCBasicTypesFullPrettyPrinter mcBasicTypesPrettyPrinter () { + + if (mcBasicTypesPrettyPrinter == null) { + mcBasicTypesPrettyPrinter = getMill(); + } + if (prettyPrinter == null) { + prettyPrinter = getPrettyPrinter(); + } + return mcBasicTypesPrettyPrinter._mcBasicTypesPrettyPrinter(); + + } + + protected MCBasicTypesFullPrettyPrinter _mcBasicTypesPrettyPrinter () { + + if (mcBasicTypesPrettyPrinter == null) { + mcBasicTypesPrettyPrinter = getMill(); + } + if (prettyPrinter == null) { + prettyPrinter = getPrettyPrinter(); + } + // as pretty printer are stateful, it needs to be cleared before it is provided + prettyPrinter.getPrinter().clearBuffer(); + return prettyPrinter; + } + + protected static MCBasicTypesFullPrettyPrinter getPrettyPrinter() { + return new MCBasicTypesFullPrettyPrinter(new IndentPrinter()); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/mcbasictypes/_ast/ASTMCImportStatement.java b/monticore-grammar/src/main/java/de/monticore/types/mcbasictypes/_ast/ASTMCImportStatement.java new file mode 100644 index 0000000000..a5ceeda8ec --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/mcbasictypes/_ast/ASTMCImportStatement.java @@ -0,0 +1,24 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.types.mcbasictypes._ast; + +import de.monticore.types.mcbasictypes.MCBasicTypesMill; + +public class ASTMCImportStatement extends ASTMCImportStatementTOP { + + protected ASTMCImportStatement() {} + + protected ASTMCImportStatement (ASTMCQualifiedName qualifiedName, boolean star) { + setMCQualifiedName(qualifiedName); + setStar(star); + } + + public String getQName(){ + return getMCQualifiedName().toString(); + } + + public String printType() { + return MCBasicTypesMill.prettyPrint(this, false); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/mcbasictypes/_ast/ASTMCPrimitiveType.java b/monticore-grammar/src/main/java/de/monticore/types/mcbasictypes/_ast/ASTMCPrimitiveType.java new file mode 100644 index 0000000000..d64d4f472a --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/mcbasictypes/_ast/ASTMCPrimitiveType.java @@ -0,0 +1,57 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.types.mcbasictypes._ast; + + +import de.monticore.symboltable.ISymbol; + +import java.util.Optional; + +public class ASTMCPrimitiveType extends ASTMCPrimitiveTypeTOP { + + protected ISymbol definingSymbol; + + @Override + public Optional getDefiningSymbol() { + return Optional.ofNullable(this.definingSymbol); + } + + @Override + public void setDefiningSymbol(ISymbol symbol) { + this.definingSymbol = symbol; + } + + public boolean isBoolean(){ + return this.getPrimitive()==ASTConstantsMCBasicTypes.BOOLEAN; + } + + public boolean isByte(){ + return this.getPrimitive()==ASTConstantsMCBasicTypes.BYTE; + } + public boolean isChar(){ + return this.getPrimitive()==ASTConstantsMCBasicTypes.CHAR; + } + public boolean isShort(){ + return this.getPrimitive()==ASTConstantsMCBasicTypes.SHORT; + } + public boolean isInt(){ + return this.getPrimitive()==ASTConstantsMCBasicTypes.INT; + } + public boolean isFloat(){ + return this.getPrimitive()==ASTConstantsMCBasicTypes.FLOAT; + } + public boolean isLong(){ + return this.getPrimitive()==ASTConstantsMCBasicTypes.LONG; + } + public boolean isDouble(){ + return this.getPrimitive()==ASTConstantsMCBasicTypes.DOUBLE; + } + + /** + * toString delivers a short name like "int" for the primitive Types + * @return + */ + public String toString(){ + return printType(); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/mcbasictypes/_ast/ASTMCQualifiedName.java b/monticore-grammar/src/main/java/de/monticore/types/mcbasictypes/_ast/ASTMCQualifiedName.java new file mode 100644 index 0000000000..5749a55d9b --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/mcbasictypes/_ast/ASTMCQualifiedName.java @@ -0,0 +1,27 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.mcbasictypes._ast; + +import de.se_rwth.commons.Names; + +public class ASTMCQualifiedName extends ASTMCQualifiedNameTOP { + + public ASTMCQualifiedName() { + } + + public Boolean isQualified() { + return parts.size() >=2 ; + } + + public String getBaseName() { + return parts.get(parts.size()-1); + } + + public String getQName() { + return Names.constructQualifiedName( + this.getPartsList()); + } + + public String toString(){ + return getQName(); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/mcbasictypes/_ast/ASTMCQualifiedType.java b/monticore-grammar/src/main/java/de/monticore/types/mcbasictypes/_ast/ASTMCQualifiedType.java new file mode 100644 index 0000000000..1864e10897 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/mcbasictypes/_ast/ASTMCQualifiedType.java @@ -0,0 +1,26 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.mcbasictypes._ast; + +import de.monticore.symboltable.ISymbol; + +import java.util.List; +import java.util.Optional; + +public class ASTMCQualifiedType extends ASTMCQualifiedTypeTOP { + + protected ISymbol definingSymbol; + + @Override + public Optional getDefiningSymbol() { + return Optional.ofNullable(this.definingSymbol); + } + + @Override + public void setDefiningSymbol(ISymbol symbol) { + this.definingSymbol = symbol; + } + + public List getNameList() { + return this.getMCQualifiedName().getPartsList(); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/mcbasictypes/_ast/ASTMCReturnType.java b/monticore-grammar/src/main/java/de/monticore/types/mcbasictypes/_ast/ASTMCReturnType.java new file mode 100644 index 0000000000..e10a03508a --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/mcbasictypes/_ast/ASTMCReturnType.java @@ -0,0 +1,26 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.mcbasictypes._ast; + +import de.monticore.types.mcbasictypes.MCBasicTypesMill; +import de.monticore.types.prettyprint.MCBasicTypesFullPrettyPrinter; + +public class ASTMCReturnType extends ASTMCReturnTypeTOP { + public ASTMCReturnType() { + } + + /** + * Conversion to a compact string, such as "int", "Person", "List< A >" + */ + @Deprecated(forRemoval = true) + public String printType(MCBasicTypesFullPrettyPrinter pp) { + return printType(); + } + + /** + * Conversion to a compact string, such as "int", "Person", "List< A >" + */ + public String printType() { + return MCBasicTypesMill.prettyPrint(this, false); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/mcbasictypes/_ast/ASTMCType.java b/monticore-grammar/src/main/java/de/monticore/types/mcbasictypes/_ast/ASTMCType.java new file mode 100644 index 0000000000..7b570235f5 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/mcbasictypes/_ast/ASTMCType.java @@ -0,0 +1,31 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.mcbasictypes._ast; + +import de.monticore.symboltable.ISymbol; +import de.monticore.types.mcbasictypes.MCBasicTypesMill; +import de.monticore.types.prettyprint.MCBasicTypesFullPrettyPrinter; + +import java.util.Optional; + +public interface ASTMCType extends ASTMCTypeTOP { + + /** + * Conversion to a compact string, such as "int", "Person", "List< A >" + */ + @Deprecated(forRemoval = true) + default String printType(MCBasicTypesFullPrettyPrinter pp) { + return printType(); + } + + /** + * Conversion to a compact string, such as "int", "Person", "List< A >" + */ + default String printType() { + return MCBasicTypesMill.prettyPrint(this, false); + } + + Optional getDefiningSymbol(); + + void setDefiningSymbol(ISymbol symbol); + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/mcbasictypes/types3/MCBasicTypesTypeVisitor.java b/monticore-grammar/src/main/java/de/monticore/types/mcbasictypes/types3/MCBasicTypesTypeVisitor.java new file mode 100644 index 0000000000..4bb8d1c5dc --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/mcbasictypes/types3/MCBasicTypesTypeVisitor.java @@ -0,0 +1,190 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.mcbasictypes.types3; + +import de.monticore.symbols.basicsymbols._symboltable.IBasicSymbolsScope; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types.check.SymTypePrimitive; +import de.monticore.types.mcbasictypes._ast.ASTMCPrimitiveType; +import de.monticore.types.mcbasictypes._ast.ASTMCQualifiedName; +import de.monticore.types.mcbasictypes._ast.ASTMCQualifiedType; +import de.monticore.types.mcbasictypes._ast.ASTMCReturnType; +import de.monticore.types.mcbasictypes._visitor.MCBasicTypesVisitor2; +import de.monticore.types3.AbstractTypeVisitor; +import de.monticore.types3.util.NameExpressionTypeCalculator; +import de.monticore.types3.util.TypeContextCalculator; +import de.monticore.types3.util.WithinTypeBasicSymbolsResolver; +import de.se_rwth.commons.logging.Log; + +import java.util.Optional; +import java.util.stream.Collectors; + +public class MCBasicTypesTypeVisitor extends AbstractTypeVisitor + implements MCBasicTypesVisitor2 { + + protected NameExpressionTypeCalculator nameExprTypeCalc; + + protected WithinTypeBasicSymbolsResolver withinTypeResolver; + + protected TypeContextCalculator typeCtxCalc; + + public MCBasicTypesTypeVisitor() { + // defaultValues + nameExprTypeCalc = new NameExpressionTypeCalculator(); + withinTypeResolver = new WithinTypeBasicSymbolsResolver(); + typeCtxCalc = new TypeContextCalculator(); + } + + protected NameExpressionTypeCalculator getNameExprTypeCalc() { + return nameExprTypeCalc; + } + + public void setNameExpressionTypeCalculator( + NameExpressionTypeCalculator nameExprTypeCalc) { + this.nameExprTypeCalc = nameExprTypeCalc; + } + + protected WithinTypeBasicSymbolsResolver getWithinTypeResolver() { + return withinTypeResolver; + } + + public void setWithinTypeResolver( + WithinTypeBasicSymbolsResolver withinTypeResolver) { + this.withinTypeResolver = withinTypeResolver; + } + + protected TypeContextCalculator getTypeCtxCalc() { + return typeCtxCalc; + } + + public void setTypeContextCalculator(TypeContextCalculator typeCtxCalc) { + this.typeCtxCalc = typeCtxCalc; + } + + @Override + public void endVisit(ASTMCPrimitiveType primitiveType) { + String primName = primitiveType.printType(); + Optional prim = + getAsBasicSymbolsScope(primitiveType.getEnclosingScope()) + .resolveType(primName); + if (prim.isPresent()) { + SymTypePrimitive typeConstant = + SymTypeExpressionFactory.createPrimitive(prim.get()); + getType4Ast().setTypeOfTypeIdentifier(primitiveType, typeConstant); + primitiveType.setDefiningSymbol(typeConstant.getTypeInfo()); + } + else { + getType4Ast().setTypeOfTypeIdentifier(primitiveType, + SymTypeExpressionFactory.createObscureType()); + Log.error("0xA0111 Cannot find symbol " + primName, + primitiveType.get_SourcePositionStart(), + primitiveType.get_SourcePositionEnd() + ); + } + } + + // Note: ASTMCVoidType does not get its type stored in type4ast, + // as the type is known to be void. + // However, it is stored for the ASTMCReturnType + + @Override + public void endVisit(ASTMCReturnType rType) { + if (rType.isPresentMCType()) { + getType4Ast().setTypeOfTypeIdentifier( + rType, + getType4Ast().getTypeOfTypeIdentifier(rType.getMCType()) + ); + } + else if (rType.isPresentMCVoidType()) { + getType4Ast().setTypeOfTypeIdentifier( + rType, + SymTypeExpressionFactory.createTypeVoid() + ); + } + } + + @Override + public void endVisit(ASTMCQualifiedName qName) { + IBasicSymbolsScope enclosingScope = + getAsBasicSymbolsScope(qName.getEnclosingScope()); + // Note: As the qualified name is a List of Names, + // There is no ASTNode representing each prefix with a type, + // e.g., Assume a.b.c with a being a qualifier, a.b being a type + // and c being an inner type in a.b + // then there is no AST node representing a.b. + // As types are usually stored in Type4AST, + // this cannot be done for a.b in this case. + // The result from a.b will be discarded after this method + + // find the type with the smallest prefix, + // e.g., for a.b.c.d it is a.b, if a.b and a.b.c are types, + // with a and a.b being their qualifiers respectively. + // Afterwards, use the prefix to search for inner types + int numberOfPartsUsedForFirstType = 0; + Optional type = Optional.empty(); + do { + numberOfPartsUsedForFirstType++; + if (type.isEmpty()) { + String prefix = qName.getPartsList().stream() + .limit(numberOfPartsUsedForFirstType) + .collect(Collectors.joining(".")); + type = getNameExprTypeCalc() + .typeOfNameAsTypeId(enclosingScope, prefix); + } + else { + SymTypeExpression prefixType = type.get(); + String name = qName.getParts(numberOfPartsUsedForFirstType - 1); + if (prefixType.isObjectType() || prefixType.isGenericType()) { + type = getWithinTypeResolver().resolveType( + prefixType, + name, + getTypeCtxCalc().getAccessModifier( + prefixType.getTypeInfo(), + enclosingScope, + true + ), + t -> true + ); + if (type.isEmpty()) { + Log.error("0xFDAE3 unable to find type " + + name + + " within type " + + prefixType.printFullName(), + qName.get_SourcePositionStart(), + qName.get_SourcePositionEnd() + ); + } + } + else { + Log.error("0xFDA3E unexpected access \"." + + name + + "\" for type " + + prefixType.printFullName(), + qName.get_SourcePositionStart(), + qName.get_SourcePositionEnd() + ); + } + } + + } while (numberOfPartsUsedForFirstType < qName.sizeParts()); + + if (type.isEmpty()) { + Log.error("0xA0324 Cannot find symbol " + qName.getQName(), + qName.get_SourcePositionStart(), + qName.get_SourcePositionEnd() + ); + type = Optional.of(SymTypeExpressionFactory.createObscureType()); + } + + getType4Ast().setTypeOfTypeIdentifier(qName, type.get()); + } + + @Override + public void endVisit(ASTMCQualifiedType mcQType) { + getType4Ast().setTypeOfTypeIdentifier( + mcQType, + getType4Ast().getPartialTypeOfTypeId(mcQType.getMCQualifiedName()) + ); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/MCCollectionTypesMill.java b/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/MCCollectionTypesMill.java new file mode 100644 index 0000000000..279bd792df --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/MCCollectionTypesMill.java @@ -0,0 +1,53 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.types.mccollectiontypes; + + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.types.prettyprint.MCCollectionTypesFullPrettyPrinter; + +/** + * Extension of the generated Mill to provide a (currently handcrafted) + * prettyprinter for this language + */ +public class MCCollectionTypesMill extends MCCollectionTypesMillTOP { + + protected static MCCollectionTypesMill mcCollectionTypesPrettyPrinter; + + protected static MCCollectionTypesFullPrettyPrinter prettyPrinter; + + /** + * Static getter for the pretty printer that delegates to the non static implementation. + * Only one pretty printer object is created and reused. + * @return the pretty printer instance + */ + @Deprecated(forRemoval = true) + public static MCCollectionTypesFullPrettyPrinter mcCollectionTypesPrettyPrinter () { + + if (mcCollectionTypesPrettyPrinter == null) { + mcCollectionTypesPrettyPrinter = getMill(); + } + if (prettyPrinter == null) { + prettyPrinter = getPrettyPrinter(); + } + return mcCollectionTypesPrettyPrinter._mcCollectionTypesPrettyPrinter(); + + } + + protected MCCollectionTypesFullPrettyPrinter _mcCollectionTypesPrettyPrinter () { + + if (mcCollectionTypesPrettyPrinter == null) { + mcCollectionTypesPrettyPrinter = getMill(); + } + if (prettyPrinter == null) { + prettyPrinter = getPrettyPrinter(); + } + // as pretty printer are stateful, it needs to be cleared before it is provided + prettyPrinter.getPrinter().clearBuffer(); + return prettyPrinter; + } + + protected static MCCollectionTypesFullPrettyPrinter getPrettyPrinter() { + return new MCCollectionTypesFullPrettyPrinter(new IndentPrinter()); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/_ast/ASTMCBasicTypeArgument.java b/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/_ast/ASTMCBasicTypeArgument.java new file mode 100644 index 0000000000..c2dc5dd4d3 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/_ast/ASTMCBasicTypeArgument.java @@ -0,0 +1,22 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.mccollectiontypes._ast; + +import de.monticore.types.mcbasictypes._ast.ASTMCType; + +import java.util.Optional; + +public class ASTMCBasicTypeArgument extends ASTMCBasicTypeArgumentTOP { + + public ASTMCBasicTypeArgument(){ + super(); + } + + // TODO RE: Kann doch gar nicht optional sein: Methode & Klasse entfernen! + // TODO BR: Optional ergibt sich aus dem ASTMCTypeArgument Interface, dass + // wiederrum ist notwendig weil die größten Types die Implementierung + // ASTMCWildcardTypeArgument liefern, und hier ist der Typ tatsächlich optional + // sähe dann im Java Code so aus: List + public Optional getMCTypeOpt(){ + return Optional.ofNullable(getMCQualifiedType()); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/_ast/ASTMCGenericType.java b/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/_ast/ASTMCGenericType.java new file mode 100644 index 0000000000..600e63857d --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/_ast/ASTMCGenericType.java @@ -0,0 +1,161 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.mccollectiontypes._ast; + +import java.util.*; +import java.util.stream.Stream; + +public interface ASTMCGenericType extends ASTMCGenericTypeTOP { + + default String printWithoutTypeArguments() { + return String.join(".", getNameList()); + } + + /* + Getter for MCTypeArgumentList + */ + + List getMCTypeArgumentList(); + + default boolean containsMCTypeArgument(Object element) { + return getMCTypeArgumentList().contains(element); + } + + default boolean containsAllMCTypeArguments(Collection collection) { + return getMCTypeArgumentList().containsAll(collection); + } + + default boolean isEmptyMCTypeArguments() { + return getMCTypeArgumentList().isEmpty(); + } + + default Iterator iteratorMCTypeArguments() { + return getMCTypeArgumentList().iterator(); + } + + default int sizeMCTypeArguments() { + return getMCTypeArgumentList().size(); + } + + default ASTMCTypeArgument[] toArrayMCTypeArguments(ASTMCTypeArgument[] array) { + return getMCTypeArgumentList().toArray(array); + } + + default Object[] toArrayMCTypeArguments() { + return getMCTypeArgumentList().toArray(); + } + + default Spliterator spliteratorMCTypeArguments() { + return getMCTypeArgumentList().spliterator(); + } + + default Stream streamMCTypeArguments() { + return getMCTypeArgumentList().stream(); + } + + default Stream parallelStreamMCTypeArguments() { + return getMCTypeArgumentList().parallelStream(); + } + + default ASTMCTypeArgument getMCTypeArgument(int index) { + return getMCTypeArgumentList().get(index); + } + + default int indexOfMCTypeArgument(Object element) { + return getMCTypeArgumentList().indexOf(element); + } + + default int lastIndexOfMCTypeArgument(Object element) { + return getMCTypeArgumentList().lastIndexOf(element); + } + + default int hashCodeMCTypeArguments() { + return getMCTypeArgumentList().hashCode(); + } + + default ListIterator listIteratorMCTypeArguments() { + return getMCTypeArgumentList().listIterator(); + } + + default ListIterator listIteratorMCTypeArguments(int index) { + return getMCTypeArgumentList().listIterator(index); + } + + default List subListMCTypeArguments(int start, int end) { + return getMCTypeArgumentList().subList(start, end); + } + + /* + Getter for NameList + */ + + List getNameList(); + + default boolean containsName(Object element) { + return getNameList().contains(element); + } + + default boolean containsAllNames(Collection collection) { + return getNameList().containsAll(collection); + } + + default boolean isEmptyNames() { + return getNameList().isEmpty(); + } + + default Iterator iteratorNames() { + return getNameList().iterator(); + } + + default int sizeNames() { + return getNameList().size(); + } + + default String[] toArrayNames(String[] array) { + return getNameList().toArray(array); + } + + default Object[] toArrayNames() { + return getNameList().toArray(); + } + + default Spliterator spliteratorNames() { + return getNameList().spliterator(); + } + + default Stream streamNames() { + return getNameList().stream(); + } + + default Stream parallelStreamNames() { + return getNameList().parallelStream(); + } + + default String getName(int index) { + return getNameList().get(index); + } + + default int indexOfName(Object element) { + return getNameList().indexOf(element); + } + + default int lastIndexOfName(Object element) { + return getNameList().lastIndexOf(element); + } + + default int hashCodeNames() { + return getNameList().hashCode(); + } + + default ListIterator listIteratorNames() { + return getNameList().listIterator(); + } + + default ListIterator listIteratorNames(int index) { + return getNameList().listIterator(index); + } + + default List subListNames(int start, int end) { + return getNameList().subList(start, end); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/_ast/ASTMCListType.java b/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/_ast/ASTMCListType.java new file mode 100644 index 0000000000..554b12bf3a --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/_ast/ASTMCListType.java @@ -0,0 +1,43 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.mccollectiontypes._ast; + +import com.google.common.collect.Lists; +import de.monticore.symboltable.ISymbol; + +import java.util.List; +import java.util.Optional; + +public class ASTMCListType extends ASTMCListTypeTOP { + + protected ISymbol definingSymbol; + + @Override + public Optional getDefiningSymbol() { + return Optional.ofNullable(this.definingSymbol); + } + + @Override + public void setDefiningSymbol(ISymbol symbol) { + this.definingSymbol = symbol; + } + + protected List names = Lists.newArrayList("List"); + + protected List typeArguments = Lists.newArrayList(); + + @Override + public void setMCTypeArgument(ASTMCTypeArgument mCTypeArgument) { + super.setMCTypeArgument(mCTypeArgument); + typeArguments = Lists.newArrayList(mCTypeArgument); + } + + @Override + public List getMCTypeArgumentList() { + return Lists.newArrayList(typeArguments); + } + + @Override + public List getNameList() { + return Lists.newArrayList(names); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/_ast/ASTMCMapType.java b/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/_ast/ASTMCMapType.java new file mode 100644 index 0000000000..c198ee0fe3 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/_ast/ASTMCMapType.java @@ -0,0 +1,50 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.mccollectiontypes._ast; + + +import com.google.common.collect.Lists; +import de.monticore.symboltable.ISymbol; + +import java.util.List; +import java.util.Optional; + +public class ASTMCMapType extends ASTMCMapTypeTOP { + + protected ISymbol definingSymbol; + + @Override + public Optional getDefiningSymbol() { + return Optional.ofNullable(this.definingSymbol); + } + + @Override + public void setDefiningSymbol(ISymbol symbol) { + this.definingSymbol = symbol; + } + + protected List names = Lists.newArrayList("Map"); + + protected List typeArguments = Lists.newArrayList(); + + @Override + public void setKey(ASTMCTypeArgument key) { + super.setKey(key); + typeArguments = Lists.newArrayList(getKey(), getValue()); + } + + @Override + public void setValue(ASTMCTypeArgument value) { + super.setValue(value); + typeArguments = Lists.newArrayList(getKey(), getValue()); + } + + @Override + public List getMCTypeArgumentList() { + return Lists.newArrayList(typeArguments); + } + + @Override + public List getNameList() { + return Lists.newArrayList(names); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/_ast/ASTMCMapTypeBuilder.java b/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/_ast/ASTMCMapTypeBuilder.java new file mode 100644 index 0000000000..ed7c2b9553 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/_ast/ASTMCMapTypeBuilder.java @@ -0,0 +1,38 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.mccollectiontypes._ast; + +public class ASTMCMapTypeBuilder extends ASTMCMapTypeBuilderTOP { + + @Override + public ASTMCMapType build() { + if (!isValid()) { + throw new IllegalStateException(); + } + ASTMCMapType value; + + value = new ASTMCMapType(); + if (this.key != null && this.value != null) { + value.setKey(this.key); + value.setValue(this.value); + } + if (this.isPresent_SourcePositionEnd()) { + value.set_SourcePositionEnd(this.get_SourcePositionEnd()); + } else { + value.set_SourcePositionEndAbsent(); + } + if (this.isPresent_SourcePositionStart()) { + value.set_SourcePositionStart(this.get_SourcePositionStart()); + } else { + value.set_SourcePositionStartAbsent(); + } + value.set_PreCommentList(this.precomments); + value.set_PostCommentList(this.postcomments); + + return value; + } + + @Override + public boolean isValid() { + return !(key == null && value == null); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/_ast/ASTMCOptionalType.java b/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/_ast/ASTMCOptionalType.java new file mode 100644 index 0000000000..9578f9e9bd --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/_ast/ASTMCOptionalType.java @@ -0,0 +1,41 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.mccollectiontypes._ast; + +import com.google.common.collect.Lists; +import de.monticore.symboltable.ISymbol; + +import java.util.List; +import java.util.Optional; + +public class ASTMCOptionalType extends ASTMCOptionalTypeTOP { + + protected ISymbol definingSymbol; + + public Optional getDefiningSymbol() { + return Optional.ofNullable(this.definingSymbol); + } + + public void setDefiningSymbol(ISymbol symbol) { + this.definingSymbol = symbol; + } + + protected List names = Lists.newArrayList("Optional"); + + protected List typeArguments = Lists.newArrayList(); + + @Override + public void setMCTypeArgument(ASTMCTypeArgument mCTypeArgument) { + super.setMCTypeArgument(mCTypeArgument); + typeArguments = Lists.newArrayList(mCTypeArgument); + } + + @Override + public List getMCTypeArgumentList() { + return Lists.newArrayList(typeArguments); + } + + @Override + public List getNameList() { + return Lists.newArrayList(names); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/_ast/ASTMCPrimitiveTypeArgument.java b/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/_ast/ASTMCPrimitiveTypeArgument.java new file mode 100644 index 0000000000..bddcf28784 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/_ast/ASTMCPrimitiveTypeArgument.java @@ -0,0 +1,17 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.mccollectiontypes._ast; + +import de.monticore.types.mcbasictypes._ast.ASTMCType; + +import java.util.Optional; + +public class ASTMCPrimitiveTypeArgument extends ASTMCPrimitiveTypeArgumentTOP { + public ASTMCPrimitiveTypeArgument(){ + super(); + } + + // TODO RE: Kann doch gar nicht optional sein: Methode & Klasse entfernen! + public Optional getMCTypeOpt(){ + return Optional.ofNullable(getMCPrimitiveType()); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/_ast/ASTMCSetType.java b/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/_ast/ASTMCSetType.java new file mode 100644 index 0000000000..830cae5357 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/_ast/ASTMCSetType.java @@ -0,0 +1,44 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.mccollectiontypes._ast; + +import com.google.common.collect.Lists; +import de.monticore.symboltable.ISymbol; + +import java.util.List; +import java.util.Optional; + +public class ASTMCSetType extends ASTMCSetTypeTOP { + + protected ISymbol definingSymbol; + + @Override + public Optional getDefiningSymbol() { + return Optional.ofNullable(this.definingSymbol); + } + + @Override + public void setDefiningSymbol(ISymbol symbol) { + this.definingSymbol = symbol; + } + + protected List names = Lists.newArrayList("Set"); + + protected List typeArguments = Lists.newArrayList(); + + + @Override + public void setMCTypeArgument(ASTMCTypeArgument mCTypeArgument) { + super.setMCTypeArgument(mCTypeArgument); + typeArguments = Lists.newArrayList(mCTypeArgument); + } + + @Override + public List getMCTypeArgumentList() { + return Lists.newArrayList(typeArguments); + } + + @Override + public List getNameList() { + return Lists.newArrayList(names); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/_ast/ASTMCTypeArgument.java b/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/_ast/ASTMCTypeArgument.java new file mode 100644 index 0000000000..dd0efe0926 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/_ast/ASTMCTypeArgument.java @@ -0,0 +1,28 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.mccollectiontypes._ast; + +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.mccollectiontypes.MCCollectionTypesMill; +import de.monticore.types.prettyprint.MCCollectionTypesFullPrettyPrinter; + +import java.util.Optional; + +public interface ASTMCTypeArgument extends ASTMCTypeArgumentTOP { + public Optional getMCTypeOpt(); + + /** + * Conversion to a compact string, such as "int", "Person", "List< A >" + */ + @Deprecated(forRemoval = true) + default String printType(MCCollectionTypesFullPrettyPrinter pp) { + return this.printType(); + } + + /** + * Conversion to a compact string, such as "int", "Person", "List< A >" + */ + default String printType() { + return MCCollectionTypesMill.prettyPrint(this, false); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/types3/IMCCollectionTypeRelations.java b/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/types3/IMCCollectionTypeRelations.java new file mode 100644 index 0000000000..a8d8c8cc32 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/types3/IMCCollectionTypeRelations.java @@ -0,0 +1,33 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.types.mccollectiontypes.types3; + +import de.monticore.types.check.SymTypeExpression; + +public interface IMCCollectionTypeRelations { + + /** + * whether the type is one of the four collection types + * from MCCollectionTypes; List, Set, Optional, Map. + * Can be extended (e.g., in OCL) + */ + default boolean isCollection(SymTypeExpression type) { + return isList(type) || isSet(type) || isOptional(type) || isMap(type); + } + + boolean isList(SymTypeExpression type); + + boolean isSet(SymTypeExpression type); + + boolean isOptional(SymTypeExpression type); + + boolean isMap(SymTypeExpression type); + + /** + * @return the Element type of a collection. + * In case of a Map, this is the value type. + */ + SymTypeExpression getCollectionElementType(SymTypeExpression type); + + SymTypeExpression getMapKeyType(SymTypeExpression type); + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/types3/MCCollectionTypesTypeVisitor.java b/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/types3/MCCollectionTypesTypeVisitor.java new file mode 100644 index 0000000000..c00a8f142b --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/types3/MCCollectionTypesTypeVisitor.java @@ -0,0 +1,95 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.mccollectiontypes.types3; + +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.mccollectiontypes._ast.ASTMCBasicTypeArgument; +import de.monticore.types.mccollectiontypes._ast.ASTMCListType; +import de.monticore.types.mccollectiontypes._ast.ASTMCMapType; +import de.monticore.types.mccollectiontypes._ast.ASTMCOptionalType; +import de.monticore.types.mccollectiontypes._ast.ASTMCPrimitiveTypeArgument; +import de.monticore.types.mccollectiontypes._ast.ASTMCSetType; +import de.monticore.types.mccollectiontypes._ast.ASTMCTypeArgument; +import de.monticore.types.mccollectiontypes._visitor.MCCollectionTypesVisitor2; +import de.monticore.types3.AbstractTypeVisitor; +import de.se_rwth.commons.logging.Log; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class MCCollectionTypesTypeVisitor extends AbstractTypeVisitor + implements MCCollectionTypesVisitor2 { + + @Override + public void endVisit(ASTMCListType t) { + synthesizeCollectionType(t, List.of(t.getMCTypeArgument()), "List"); + } + + @Override + public void endVisit(ASTMCSetType t) { + synthesizeCollectionType(t, List.of(t.getMCTypeArgument()), "Set"); + } + + @Override + public void endVisit(ASTMCOptionalType t) { + synthesizeCollectionType(t, List.of(t.getMCTypeArgument()), "Optional"); + } + + @Override + public void endVisit(ASTMCMapType t) { + synthesizeCollectionType(t, List.of(t.getKey(), t.getValue()), "Map"); + } + + protected void synthesizeCollectionType( + ASTMCType mcType, + List args, + String name + ) { + SymTypeExpression type; + Optional symbolOpt = + getAsBasicSymbolsScope(mcType.getEnclosingScope()).resolveType(name); + if (symbolOpt.isEmpty()) { + Log.error(String.format("0xE9FD0 Cannot find symbol %s", name), + mcType.get_SourcePositionStart(), + mcType.get_SourcePositionEnd() + ); + type = SymTypeExpressionFactory.createObscureType(); + } + else { + List argTypes = new ArrayList<>(); + for (ASTMCTypeArgument arg : args) { + SymTypeExpression argType = getType4Ast().getPartialTypeOfTypeId(arg); + if (argType.isObscureType()) { + Log.error("0xE9FD1 type argument '" + arg.printType() + + "' could not be calculated", + mcType.get_SourcePositionStart(), + mcType.get_SourcePositionEnd() + ); + } + argTypes.add(argType); + } + type = SymTypeExpressionFactory.createGenerics(symbolOpt.get(), argTypes); + } + getType4Ast().setTypeOfTypeIdentifier(mcType, type); + } + + @Override + public void endVisit(ASTMCBasicTypeArgument basicArg) { + getType4Ast().setTypeOfTypeIdentifier( + basicArg, + getType4Ast().getPartialTypeOfTypeId(basicArg.getMCQualifiedType()) + ); + } + + @Override + public void endVisit(ASTMCPrimitiveTypeArgument primitiveArg) { + getType4Ast().setTypeOfTypeIdentifier( + primitiveArg, + getType4Ast().getPartialTypeOfTypeId(primitiveArg.getMCPrimitiveType()) + ); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/types3/util/MCCollectionSymTypeFactory.java b/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/types3/util/MCCollectionSymTypeFactory.java new file mode 100644 index 0000000000..9bd091f0a9 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/types3/util/MCCollectionSymTypeFactory.java @@ -0,0 +1,62 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.types.mccollectiontypes.types3.util; + +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.IBasicSymbolsGlobalScope; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types.check.SymTypeOfGenerics; +import de.se_rwth.commons.logging.Log; + +import java.util.Optional; + +/** + * Factory for MCCollectionTypes + * (ONLY) convenience methods for + * {@link de.monticore.types.check.SymTypeExpressionFactory} + */ +public class MCCollectionSymTypeFactory { + + public static SymTypeOfGenerics createList(SymTypeExpression innerType) { + return createCollectionType("List", "java.util.List", innerType); + } + + public static SymTypeOfGenerics createSet(SymTypeExpression innerType) { + return createCollectionType("Set", "java.util.Set", innerType); + } + + public static SymTypeOfGenerics createOptional(SymTypeExpression innerType) { + return createCollectionType("Optional", "java.util.Optional", innerType); + } + + public static SymTypeOfGenerics createMap( + SymTypeExpression keyType, + SymTypeExpression valueType + ) { + return createCollectionType("Map", "java.util.Map", keyType, valueType); + } + + // Helper + + protected static SymTypeOfGenerics createCollectionType( + String unboxedName, + String boxedName, + SymTypeExpression... innerTypes + ) { + IBasicSymbolsGlobalScope gs = BasicSymbolsMill.globalScope(); + Optional typeSymbol = gs.resolveType(unboxedName); + if (typeSymbol.isEmpty()) { + typeSymbol = gs.resolveType(boxedName); + } + if (typeSymbol.isPresent()) { + return SymTypeExpressionFactory.createGenerics(typeSymbol.get(), innerTypes); + } + else { + Log.error("0xFD299 unable to resolve type " + + unboxedName + ", unable to create the collection type."); + return null; + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/types3/util/MCCollectionTypeRelations.java b/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/types3/util/MCCollectionTypeRelations.java new file mode 100644 index 0000000000..e4f0accf68 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/mccollectiontypes/types3/util/MCCollectionTypeRelations.java @@ -0,0 +1,92 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.types.mccollectiontypes.types3.util; + +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types.check.SymTypeOfGenerics; +import de.monticore.types.mccollectiontypes.types3.IMCCollectionTypeRelations; +import de.se_rwth.commons.logging.Log; + +/** + * relations for built-in Collection SymTypes of MCCollectionTypes + * these are List, Set, Optional, Map + * This does NOT include types that inherit from collection types + */ +public class MCCollectionTypeRelations implements IMCCollectionTypeRelations { + + /** + * whether the type is one of the four collection types + * from MCCollectionTypes; List, Set, Optional, Map + */ + public boolean isCollection(SymTypeExpression type) { + return isList(type) || isSet(type) || isOptional(type) || isMap(type); + } + + public boolean isList(SymTypeExpression type) { + return isSpecificCollection(type, "List", "java.util.List", 1); + } + + public boolean isSet(SymTypeExpression type) { + return isSpecificCollection(type, "Set", "java.util.Set", 1); + } + + public boolean isOptional(SymTypeExpression type) { + return isSpecificCollection(type, "Optional", "java.util.Optional", 1); + } + + public boolean isMap(SymTypeExpression type) { + return isSpecificCollection(type, "Map", "java.util.Map", 2); + } + + /** + * @return the Element type of a collection. + * In case of a Map, this is the value type. + */ + public SymTypeExpression getCollectionElementType(SymTypeExpression type) { + if (!isCollection(type)) { + Log.error("0xFD1C7 internal error: tried to get the type " + + "of an collection's element of a non collection type"); + return SymTypeExpressionFactory.createObscureType(); + } + // in case of List, Set, Optional we only have one type parameter, + // in case of Map the second type parameter is the value type. + SymTypeOfGenerics collectionType = (SymTypeOfGenerics) type; + return collectionType.getArgument(collectionType.sizeArguments() - 1); + } + + public SymTypeExpression getMapKeyType(SymTypeExpression type) { + if (!isMap(type)) { + Log.error("0xFD1C8 internal error: tried to get a map's key type " + + "of a non map type"); + return SymTypeExpressionFactory.createObscureType(); + } + return ((SymTypeOfGenerics) type).getArgument(0); + } + + // Helper + + protected boolean isSpecificCollection( + SymTypeExpression type, + String unboxedName, + String boxedName, + int numberOfArgs + ) { + if (!type.isGenericType()) { + return false; + } + SymTypeOfGenerics generic = (SymTypeOfGenerics) type; + String name = generic.getTypeConstructorFullName(); + if (!name.equals(unboxedName) && !name.equals(boxedName)) { + return false; + } + if (generic.sizeArguments() != numberOfArgs) { + Log.warn("0xFD1C4 encountered generic called " + + generic.getTypeConstructorFullName() + " with " + + generic.sizeArguments() + " type arguments, " + + "but expected " + numberOfArgs); + return false; + } + return true; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/mcfullgenerictypes/MCFullGenericTypesMill.java b/monticore-grammar/src/main/java/de/monticore/types/mcfullgenerictypes/MCFullGenericTypesMill.java new file mode 100644 index 0000000000..b9ad4b2988 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/mcfullgenerictypes/MCFullGenericTypesMill.java @@ -0,0 +1,53 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.types.mcfullgenerictypes; + + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.types.prettyprint.MCFullGenericTypesFullPrettyPrinter; + +/** + * Extension of the generated Mill to provide a (currently handcrafted) + * prettyprinter for this language + */ +public class MCFullGenericTypesMill extends MCFullGenericTypesMillTOP { + + protected static MCFullGenericTypesMill mcFullGenericTypesPrettyPrinter; + + protected static MCFullGenericTypesFullPrettyPrinter prettyPrinter; + + /** + * Static getter for the pretty printer that delegates to the non static implementation. + * Only one pretty printer object is created and reused. + * @return the pretty printer instance + */ + @Deprecated(forRemoval = true) + public static MCFullGenericTypesFullPrettyPrinter mcFullGenericTypesPrettyPrinter () { + + if (mcFullGenericTypesPrettyPrinter == null) { + mcFullGenericTypesPrettyPrinter = getMill(); + } + if (prettyPrinter == null) { + prettyPrinter = getPrettyPrinter(); + } + return mcFullGenericTypesPrettyPrinter._mcFullGenericTypesPrettyPrinter(); + + } + + protected MCFullGenericTypesFullPrettyPrinter _mcFullGenericTypesPrettyPrinter () { + + if (mcFullGenericTypesPrettyPrinter == null) { + mcFullGenericTypesPrettyPrinter = getMill(); + } + if (prettyPrinter == null) { + prettyPrinter = getPrettyPrinter(); + } + // as pretty printer are stateful, it needs to be cleared before it is provided + prettyPrinter.getPrinter().clearBuffer(); + return prettyPrinter; + } + + protected static MCFullGenericTypesFullPrettyPrinter getPrettyPrinter() { + return new MCFullGenericTypesFullPrettyPrinter(new IndentPrinter()); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/mcfullgenerictypes/_ast/ASTMCMultipleGenericType.java b/monticore-grammar/src/main/java/de/monticore/types/mcfullgenerictypes/_ast/ASTMCMultipleGenericType.java new file mode 100644 index 0000000000..9b75843007 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/mcfullgenerictypes/_ast/ASTMCMultipleGenericType.java @@ -0,0 +1,45 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.types.mcfullgenerictypes._ast; + +import com.google.common.collect.Lists; +import de.monticore.symboltable.ISymbol; +import de.monticore.types.mccollectiontypes._ast.ASTMCTypeArgument; + +import java.util.List; +import java.util.Optional; + +public class ASTMCMultipleGenericType extends ASTMCMultipleGenericTypeTOP { + + protected ISymbol definingSymbol; + + @Override + public Optional getDefiningSymbol() { + return Optional.ofNullable(this.definingSymbol); + } + + @Override + public void setDefiningSymbol(ISymbol symbol) { + this.definingSymbol = symbol; + } + + public String printWithoutTypeArguments() { + // from a.BD.E..G ist will return a.B.D.E.G + String firstGenericType = getMCBasicGenericType().printWithoutTypeArguments(); + String innerTypes = getMCInnerTypeList() + .stream() + .map(ASTMCInnerType::getName) + .reduce((a, b) -> a + "." + b).get(); + return firstGenericType + "." + innerTypes; + } + + @Override + public List getMCTypeArgumentList() { + return getMCInnerType(sizeMCInnerTypes()-1).getMCTypeArgumentList(); + } + + @Override + public List getNameList() { + return Lists.newArrayList(printWithoutTypeArguments().split(".")); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/mcfullgenerictypes/_ast/ASTMCWildcardTypeArgument.java b/monticore-grammar/src/main/java/de/monticore/types/mcfullgenerictypes/_ast/ASTMCWildcardTypeArgument.java new file mode 100644 index 0000000000..ae5719249a --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/mcfullgenerictypes/_ast/ASTMCWildcardTypeArgument.java @@ -0,0 +1,21 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.mcfullgenerictypes._ast; + +import de.monticore.types.mcbasictypes._ast.ASTMCType; + +import java.util.Optional; + +public class ASTMCWildcardTypeArgument extends ASTMCWildcardTypeArgumentTOP { + public ASTMCWildcardTypeArgument(){ + super(); + } + + // TODO RE: Mir ist nicht klar wozu diese Methode dient, aber auch hier + // wäre eigentlich manchmal ein Type vorhanden.- + // Warum da "empty" zurück kommt ist mir schleierhaft. + // entfernbar? + // (dann könte man sich nämlich viele der TOP Klassen sparen) + public Optional getMCTypeOpt() { + return Optional.empty(); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/mcfullgenerictypes/types3/MCFullGenericTypesTypeVisitor.java b/monticore-grammar/src/main/java/de/monticore/types/mcfullgenerictypes/types3/MCFullGenericTypesTypeVisitor.java new file mode 100644 index 0000000000..03e65f16c1 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/mcfullgenerictypes/types3/MCFullGenericTypesTypeVisitor.java @@ -0,0 +1,67 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.mcfullgenerictypes.types3; + +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types.mcfullgenerictypes._ast.ASTMCWildcardTypeArgument; +import de.monticore.types.mcfullgenerictypes._visitor.MCFullGenericTypesVisitor2; +import de.monticore.types3.AbstractTypeVisitor; +import de.se_rwth.commons.logging.Log; + +public class MCFullGenericTypesTypeVisitor extends AbstractTypeVisitor + implements MCFullGenericTypesVisitor2 { + + @Override + public void endVisit(ASTMCWildcardTypeArgument wildcardType) { + SymTypeExpression tex; + if (wildcardType.isPresentLowerBound()) { + if (getType4Ast().getPartialTypeOfTypeId((wildcardType.getLowerBound())) + .isObscureType()) { + Log.error("0xE9CDD The lower bound type of the wildcard type " + + "could not be synthesized.", + wildcardType.get_SourcePositionStart(), + wildcardType.get_SourcePositionEnd() + ); + getType4Ast().setTypeOfTypeIdentifier(wildcardType, + SymTypeExpressionFactory.createObscureType()); + return; + } + if (!getType4Ast() + .getPartialTypeOfTypeId(wildcardType.getLowerBound()) + .isObscureType()) { + tex = SymTypeExpressionFactory.createWildcard(false, + getType4Ast().getPartialTypeOfTypeId(wildcardType.getLowerBound())); + } + else { + tex = SymTypeExpressionFactory.createObscureType(); + } + } + else if (wildcardType.isPresentUpperBound()) { + if (getType4Ast() + .getPartialTypeOfTypeId((wildcardType.getUpperBound())) + .isObscureType()) { + Log.error("0xE9CDA The upper bound type of the wildcard type " + + "could not be synthesized.", + wildcardType.get_SourcePositionStart(), + wildcardType.get_SourcePositionEnd() + ); + getType4Ast().setTypeOfTypeIdentifier(wildcardType, + SymTypeExpressionFactory.createObscureType()); + return; + } + if (!getType4Ast() + .getPartialTypeOfTypeId(wildcardType.getUpperBound()) + .isObscureType()) { + tex = SymTypeExpressionFactory.createWildcard(true, + getType4Ast().getPartialTypeOfTypeId(wildcardType.getUpperBound())); + } + else { + tex = SymTypeExpressionFactory.createObscureType(); + } + } + else { + tex = SymTypeExpressionFactory.createWildcard(); + } + getType4Ast().setTypeOfTypeIdentifier(wildcardType, tex); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/mcfunctiontypes/_ast/ASTMCFunctionType.java b/monticore-grammar/src/main/java/de/monticore/types/mcfunctiontypes/_ast/ASTMCFunctionType.java new file mode 100644 index 0000000000..24a45ec651 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/mcfunctiontypes/_ast/ASTMCFunctionType.java @@ -0,0 +1,21 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.mcfunctiontypes._ast; + +import de.monticore.symboltable.ISymbol; + +import java.util.Optional; + +public class ASTMCFunctionType extends ASTMCFunctionTypeTOP { + + protected ISymbol definingSymbol; + + @Override + public Optional getDefiningSymbol() { + return Optional.ofNullable(this.definingSymbol); + } + + @Override + public void setDefiningSymbol(ISymbol symbol) { + this.definingSymbol = symbol; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/mcfunctiontypes/types3/MCFunctionTypesTypeVisitor.java b/monticore-grammar/src/main/java/de/monticore/types/mcfunctiontypes/types3/MCFunctionTypesTypeVisitor.java new file mode 100644 index 0000000000..52e4b69303 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/mcfunctiontypes/types3/MCFunctionTypesTypeVisitor.java @@ -0,0 +1,59 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.mcfunctiontypes.types3; + +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.mcfunctiontypes._ast.ASTMCFunctionType; +import de.monticore.types.mcfunctiontypes._visitor.MCFunctionTypesVisitor2; +import de.monticore.types3.AbstractTypeVisitor; +import de.se_rwth.commons.logging.Log; + +import java.util.LinkedList; +import java.util.List; + +public class MCFunctionTypesTypeVisitor extends AbstractTypeVisitor + implements MCFunctionTypesVisitor2 { + + @Override + public void endVisit(ASTMCFunctionType functionType) { + SymTypeExpression symType; + + List arguments = new LinkedList(); + for (int i = 0; i < functionType.getMCFunctionParTypes().sizeMCTypes(); i++) { + ASTMCType par = functionType.getMCFunctionParTypes().getMCType(i); + + if (getType4Ast().getPartialTypeOfTypeId(par).isObscureType()) { + Log.error("0xE9BDC Type of argument " + i + 1 + + " of the function type could not be synthesized.", + par.get_SourcePositionStart(), + par.get_SourcePositionEnd() + ); + getType4Ast().setTypeOfTypeIdentifier(functionType, + SymTypeExpressionFactory.createObscureType()); + return; + } + arguments.add(getType4Ast().getPartialTypeOfTypeId(par)); + } + + if (!getType4Ast().hasTypeOfTypeIdentifier(functionType.getMCReturnType())) { + Log.error("0xE9BDD The return type of the function type" + + " could not be synthesized.", + functionType.getMCReturnType().get_SourcePositionStart(), + functionType.getMCReturnType().get_SourcePositionEnd() + ); + getType4Ast().setTypeOfTypeIdentifier(functionType, + SymTypeExpressionFactory.createObscureType()); + return; + } + SymTypeExpression returnArgument = getType4Ast() + .getPartialTypeOfTypeId(functionType.getMCReturnType()); + + symType = SymTypeExpressionFactory.createFunction( + returnArgument, + arguments, + functionType.getMCFunctionParTypes().isPresentIsElliptic() + ); + getType4Ast().setTypeOfTypeIdentifier(functionType, symType); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/mcsimplegenerictypes/MCSimpleGenericTypesMill.java b/monticore-grammar/src/main/java/de/monticore/types/mcsimplegenerictypes/MCSimpleGenericTypesMill.java new file mode 100644 index 0000000000..58165806ae --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/mcsimplegenerictypes/MCSimpleGenericTypesMill.java @@ -0,0 +1,54 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.types.mcsimplegenerictypes; + + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.types.prettyprint.MCSimpleGenericTypesFullPrettyPrinter; + +/** + * Extension of the generated Mill to provide a (currently handcrafted) + * prettyprinter for this language + */ +public class MCSimpleGenericTypesMill extends MCSimpleGenericTypesMillTOP { + + protected static MCSimpleGenericTypesMill mcSimpleGenericTypesPrettyPrinter; + + protected static MCSimpleGenericTypesFullPrettyPrinter prettyPrinter; + + /** + * Static getter for the pretty printer that delegates to the non static implementation. + * Only one pretty printer object is created and reused. + * @return the pretty printer instance + */ + @Deprecated(forRemoval = true) + public static MCSimpleGenericTypesFullPrettyPrinter mcSimpleGenericTypesPrettyPrinter () { + + if (mcSimpleGenericTypesPrettyPrinter == null) { + mcSimpleGenericTypesPrettyPrinter = getMill(); + } + if (prettyPrinter == null) { + prettyPrinter = getPrettyPrinter(); + } + return mcSimpleGenericTypesPrettyPrinter._mcSimpleGenericTypesPrettyPrinter(); + + } + + + protected MCSimpleGenericTypesFullPrettyPrinter _mcSimpleGenericTypesPrettyPrinter () { + + if (mcSimpleGenericTypesPrettyPrinter == null) { + mcSimpleGenericTypesPrettyPrinter = getMill(); + } + if (prettyPrinter == null) { + prettyPrinter = getPrettyPrinter(); + } + // as pretty printer are stateful, it needs to be cleared before it is provided + prettyPrinter.getPrinter().clearBuffer(); + return prettyPrinter; + } + + protected static MCSimpleGenericTypesFullPrettyPrinter getPrettyPrinter() { + return new MCSimpleGenericTypesFullPrettyPrinter(new IndentPrinter()); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/mcsimplegenerictypes/_ast/ASTMCBasicGenericType.java b/monticore-grammar/src/main/java/de/monticore/types/mcsimplegenerictypes/_ast/ASTMCBasicGenericType.java new file mode 100644 index 0000000000..82b3a0e7b0 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/mcsimplegenerictypes/_ast/ASTMCBasicGenericType.java @@ -0,0 +1,21 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.mcsimplegenerictypes._ast; + +import de.monticore.symboltable.ISymbol; + +import java.util.Optional; + +public class ASTMCBasicGenericType extends ASTMCBasicGenericTypeTOP { + + protected ISymbol definingSymbol; + + @Override + public Optional getDefiningSymbol() { + return Optional.ofNullable(this.definingSymbol); + } + + @Override + public void setDefiningSymbol(ISymbol symbol) { + this.definingSymbol = symbol; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/mcsimplegenerictypes/_ast/ASTMCCustomTypeArgument.java b/monticore-grammar/src/main/java/de/monticore/types/mcsimplegenerictypes/_ast/ASTMCCustomTypeArgument.java new file mode 100644 index 0000000000..c137d6caf2 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/mcsimplegenerictypes/_ast/ASTMCCustomTypeArgument.java @@ -0,0 +1,22 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.mcsimplegenerictypes._ast; + +import de.monticore.types.mcbasictypes._ast.ASTMCType; + +import java.util.Optional; + +public class ASTMCCustomTypeArgument extends ASTMCCustomTypeArgumentTOP { + + public ASTMCCustomTypeArgument(){ + super(); + } + + // TODO RE: das hier tut das falsche: Der Typ eines Arrays unterscheidet sich vom Typ seines arguments + @Deprecated + public Optional getMCTypeOpt(){ + return Optional.ofNullable(getMCType()); + } + + // TODO: Klasse kann entfernt werden + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/mcsimplegenerictypes/types3/MCSimpleGenericTypesTypeVisitor.java b/monticore-grammar/src/main/java/de/monticore/types/mcsimplegenerictypes/types3/MCSimpleGenericTypesTypeVisitor.java new file mode 100644 index 0000000000..e7c2677969 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/mcsimplegenerictypes/types3/MCSimpleGenericTypesTypeVisitor.java @@ -0,0 +1,126 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.mcsimplegenerictypes.types3; + +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.symbols.basicsymbols._symboltable.TypeVarSymbol; +import de.monticore.symboltable.modifiers.AccessModifier; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types.mccollectiontypes._ast.ASTMCGenericType; +import de.monticore.types.mccollectiontypes._ast.ASTMCTypeArgument; +import de.monticore.types.mcsimplegenerictypes._ast.ASTMCBasicGenericType; +import de.monticore.types.mcsimplegenerictypes._ast.ASTMCCustomTypeArgument; +import de.monticore.types.mcsimplegenerictypes._visitor.MCSimpleGenericTypesVisitor2; +import de.monticore.types3.AbstractTypeVisitor; +import de.se_rwth.commons.logging.Log; + +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; + +public class MCSimpleGenericTypesTypeVisitor extends AbstractTypeVisitor + implements MCSimpleGenericTypesVisitor2 { + + @Override + public void endVisit(ASTMCBasicGenericType genericType) { + + SymTypeExpression symType = null; + List arguments = new LinkedList<>(); + boolean obscure = false; + for (int i = 0; i < genericType.sizeMCTypeArguments(); i++) { + ASTMCTypeArgument arg = genericType.getMCTypeArgument(i); + if (!getType4Ast().hasTypeOfTypeIdentifier(arg)) { + Log.error("0xE9CDB The type argument " + i + 1 + + " of the generic type " + + genericType.printWithoutTypeArguments() + + "could not be synthesized.", + genericType.get_SourcePositionStart(), + genericType.get_SourcePositionEnd() + ); + return; + } + SymTypeExpression argSymType = getType4Ast().getPartialTypeOfTypeId(arg); + arguments.add(argSymType); + if (argSymType.isObscureType()) { + obscure = true; + } + } + + if (!obscure) { + Optional typeVar = + getAsBasicSymbolsScope(genericType.getEnclosingScope()) + .resolveTypeVar(genericType.printWithoutTypeArguments()); + if (typeVar.isPresent()) { + Log.error( + "0xA0320 The generic type cannot have a generic parameter" + + "because it is a type variable", + genericType.get_SourcePositionStart(), + genericType.get_SourcePositionEnd() + ); + getType4Ast().setTypeOfTypeIdentifier(genericType, + SymTypeExpressionFactory.createObscureType()); + } + else { + Optional type = + getAsBasicSymbolsScope(genericType.getEnclosingScope()) + .resolveType(genericType.printWithoutTypeArguments()); + if (type.isPresent()) { + if (type.get().getTypeParameterList().size() == 0) { + Log.error("0xB5487 expected a generic type, but \"" + + type.get().getFullName() + + "\" contains no type variables", + genericType.get_SourcePositionStart(), + genericType.get_SourcePositionEnd() + ); + } + if (type.get().getTypeParameterList().size() != arguments.size() + && arguments.size() != 0) { + // we only allow 0 arguments for, e.g., "new ArrayList<>()" + // in most cases we expect the arguments to be set + Log.error("0xB5864 The generic type \"" + + type.get().getFullName() + + "\" expects " + + type.get().getTypeParameterList().size() + + " arguments instead of " + + arguments.size(), + genericType.get_SourcePositionStart(), + genericType.get_SourcePositionEnd() + ); + } + symType = SymTypeExpressionFactory.createGenerics(type.get(), arguments); + } + else { + symType = handleIfNotFound(genericType, arguments); + } + } + if (null != symType) { + getType4Ast().setTypeOfTypeIdentifier(genericType, symType); + genericType.setDefiningSymbol(symType.getTypeInfo()); + } + } + else { + // one of the type arguments could not be synthesized + // => the generic type itself cannot be synthesized correctly + getType4Ast().setTypeOfTypeIdentifier(genericType, + SymTypeExpressionFactory.createObscureType()); + } + } + + @Override + public void endVisit(ASTMCCustomTypeArgument typeArgument) { + getType4Ast().setTypeOfTypeIdentifier( + typeArgument, + getType4Ast().getPartialTypeOfTypeId(typeArgument.getMCType()) + ); + } + + /** + * extension method for error handling + */ + protected SymTypeExpression handleIfNotFound(ASTMCGenericType type, + List arguments) { + Log.error("0xA0323 Cannot find symbol " + type.printWithoutTypeArguments(), + type.get_SourcePositionStart()); + return SymTypeExpressionFactory.createObscureType(); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/prettyprint/MCArrayTypesFullPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/types/prettyprint/MCArrayTypesFullPrettyPrinter.java new file mode 100644 index 0000000000..0e6904a748 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/prettyprint/MCArrayTypesFullPrettyPrinter.java @@ -0,0 +1,37 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.prettyprint; + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.prettyprint.MCBasicsPrettyPrinter; +import de.monticore.types.mcarraytypes.MCArrayTypesMill; +import de.monticore.types.mcarraytypes._visitor.MCArrayTypesTraverser; + +@Deprecated(forRemoval = true) +public class MCArrayTypesFullPrettyPrinter extends MCBasicTypesFullPrettyPrinter { + + protected MCArrayTypesTraverser traverser; + + public MCArrayTypesFullPrettyPrinter(IndentPrinter printer) { + super(printer); + this.traverser = MCArrayTypesMill.traverser(); + + MCArrayTypesPrettyPrinter arrayTypes = new MCArrayTypesPrettyPrinter(printer); + traverser.setMCArrayTypesHandler(arrayTypes); + traverser.add4MCArrayTypes(arrayTypes); + + MCBasicTypesPrettyPrinter basicTypes = new MCBasicTypesPrettyPrinter(printer); + traverser.setMCBasicTypesHandler(basicTypes); + traverser.add4MCBasicTypes(basicTypes); + + MCBasicsPrettyPrinter basics = new MCBasicsPrettyPrinter(printer); + traverser.add4MCBasics(basics); + } + + public MCArrayTypesTraverser getTraverser() { + return traverser; + } + + public void setTraverser(MCArrayTypesTraverser traverser) { + this.traverser = traverser; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/prettyprint/MCArrayTypesPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/types/prettyprint/MCArrayTypesPrettyPrinter.java new file mode 100644 index 0000000000..de5eb55cc5 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/prettyprint/MCArrayTypesPrettyPrinter.java @@ -0,0 +1,47 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.prettyprint; + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.types.mcarraytypes._ast.ASTMCArrayType; +import de.monticore.types.mcarraytypes._visitor.MCArrayTypesHandler; +import de.monticore.types.mcarraytypes._visitor.MCArrayTypesTraverser; +import de.monticore.types.mcarraytypes._visitor.MCArrayTypesVisitor2; + +@Deprecated(forRemoval = true) +public class MCArrayTypesPrettyPrinter implements MCArrayTypesVisitor2, MCArrayTypesHandler { + + protected MCArrayTypesTraverser traverser; + + protected IndentPrinter printer; + + public MCArrayTypesPrettyPrinter(IndentPrinter printer) { + this.printer = printer; + } + + public IndentPrinter getPrinter() { + return printer; + } + + public void setPrinter(IndentPrinter printer) { + this.printer = printer; + } + + @Override + public MCArrayTypesTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(MCArrayTypesTraverser traverser) { + this.traverser = traverser; + } + + @Override + public void handle(ASTMCArrayType node) { + node.getMCType().accept(getTraverser()); + for (int i = 0; i < node.getDimensions(); i++) { + getPrinter().print("[]"); + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/prettyprint/MCBasicTypesFullPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/types/prettyprint/MCBasicTypesFullPrettyPrinter.java new file mode 100644 index 0000000000..cf42045dd4 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/prettyprint/MCBasicTypesFullPrettyPrinter.java @@ -0,0 +1,57 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.prettyprint; + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.prettyprint.MCBasicsPrettyPrinter; +import de.monticore.types.mcbasictypes.MCBasicTypesMill; +import de.monticore.types.mcbasictypes._ast.ASTMCBasicTypesNode; +import de.monticore.types.mcbasictypes._visitor.MCBasicTypesTraverser; + +@Deprecated(forRemoval = true) +public class MCBasicTypesFullPrettyPrinter { + + protected MCBasicTypesTraverser traverser; + + protected IndentPrinter printer; + + public MCBasicTypesFullPrettyPrinter(IndentPrinter printer){ + this.printer = printer; + this.traverser = MCBasicTypesMill.traverser(); + + MCBasicTypesPrettyPrinter basicTypes = new MCBasicTypesPrettyPrinter(printer); + traverser.setMCBasicTypesHandler(basicTypes); + traverser.add4MCBasicTypes(basicTypes); + + MCBasicsPrettyPrinter basics = new MCBasicsPrettyPrinter(printer); + traverser.add4MCBasics(basics); + } + + public IndentPrinter getPrinter() { + return printer; + } + + public void setPrinter(IndentPrinter printer) { + this.printer = printer; + } + + public MCBasicTypesTraverser getTraverser() { + return traverser; + } + + public void setTraverser(MCBasicTypesTraverser traverser) { + this.traverser = traverser; + } + + /** + * This method prettyprints a given node from type grammar. + * + * @param a A node from type grammar. + * @return String representation. + */ + public String prettyprint(ASTMCBasicTypesNode a) { + getPrinter().clearBuffer(); + a.accept(getTraverser()); + return getPrinter().getContent(); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/prettyprint/MCBasicTypesPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/types/prettyprint/MCBasicTypesPrettyPrinter.java new file mode 100644 index 0000000000..13294a4068 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/prettyprint/MCBasicTypesPrettyPrinter.java @@ -0,0 +1,107 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.types.prettyprint; + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.types.MCBasicTypesHelper; +import de.monticore.types.mcbasictypes._ast.*; +import de.monticore.types.mcbasictypes._visitor.MCBasicTypesHandler; +import de.monticore.types.mcbasictypes._visitor.MCBasicTypesTraverser; +import de.monticore.types.mcbasictypes._visitor.MCBasicTypesVisitor2; +import de.se_rwth.commons.Names; + +import java.util.Iterator; + +@Deprecated(forRemoval = true) +public class MCBasicTypesPrettyPrinter implements MCBasicTypesVisitor2, MCBasicTypesHandler { + + protected MCBasicTypesTraverser traverser; + + @Override + public MCBasicTypesTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(MCBasicTypesTraverser traverser) { + this.traverser = traverser; + } + + public IndentPrinter getPrinter() { + return printer; + } + + public MCBasicTypesPrettyPrinter(IndentPrinter printer) { + this.printer = printer; + } + + public void setPrinter(IndentPrinter printer) { + this.printer = printer; + } + + // printer to use + protected IndentPrinter printer; + + + /** + * Prints qualified names + * + * @param a qualified name + */ + @Override + public void handle(ASTMCQualifiedName a) { + getPrinter().print(Names.getQualifiedName(a.getPartsList())); + } + + + /** + * Prints a void type. + * + * @param a void type + */ + @Override + public void handle(ASTMCVoidType a) { + getPrinter().print("void"); + } + + /** + * Prints a primitive type. + * + * @param a primitive type + */ + @Override + public void handle(ASTMCPrimitiveType a) { + getPrinter().print(MCBasicTypesHelper.primitiveConst2Name(a.getPrimitive())); + } + + @Override + public void handle(ASTMCImportStatement a){ + getPrinter().print("import " + a.getMCQualifiedName().toString()); + if(a.isStar()){ + getPrinter().print(".*"); + } + getPrinter().print(";"); + } + + @Override + public void handle(ASTMCPackageDeclaration a){ + getPrinter().print("package " + a.getMCQualifiedName().toString() + ";"); + } + + /** + * Prints a list + * + * @param iter iterator for the list + * @param separator string for separating list + */ + protected void printList(Iterator iter, String separator) { + // print by iterate through all items + String sep = ""; + while (iter.hasNext()) { + getPrinter().print(sep); + iter.next().accept(getTraverser()); + sep = separator; + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/prettyprint/MCCollectionTypesFullPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/types/prettyprint/MCCollectionTypesFullPrettyPrinter.java new file mode 100644 index 0000000000..9f4a962fa2 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/prettyprint/MCCollectionTypesFullPrettyPrinter.java @@ -0,0 +1,51 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.prettyprint; + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.prettyprint.MCBasicsPrettyPrinter; +import de.monticore.types.mccollectiontypes.MCCollectionTypesMill; +import de.monticore.types.mccollectiontypes._ast.ASTMCTypeArgument; +import de.monticore.types.mccollectiontypes._visitor.MCCollectionTypesTraverser; + +@Deprecated(forRemoval = true) +public class MCCollectionTypesFullPrettyPrinter extends MCBasicTypesFullPrettyPrinter { + + protected MCCollectionTypesTraverser traverser; + + public MCCollectionTypesFullPrettyPrinter(IndentPrinter printer){ + super(printer); + this.traverser = MCCollectionTypesMill.traverser(); + + MCCollectionTypesPrettyPrinter collectionTypes = new MCCollectionTypesPrettyPrinter(printer); + traverser.setMCCollectionTypesHandler(collectionTypes); + traverser.add4MCCollectionTypes(collectionTypes); + + MCBasicTypesPrettyPrinter basicTypes = new MCBasicTypesPrettyPrinter(printer); + traverser.setMCBasicTypesHandler(basicTypes); + traverser.add4MCBasicTypes(basicTypes); + + MCBasicsPrettyPrinter basics = new MCBasicsPrettyPrinter(printer); + traverser.add4MCBasics(basics); + } + + public MCCollectionTypesTraverser getTraverser() { + return traverser; + } + + public void setTraverser(MCCollectionTypesTraverser traverser) { + this.traverser = traverser; + } + + /** + * This method prettyprints a given node from type grammar. + * + * @param a A node from type grammar. + * @return String representation. + */ + public String prettyprint(ASTMCTypeArgument a) { + getPrinter().clearBuffer(); + a.accept(getTraverser()); + return getPrinter().getContent(); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/prettyprint/MCCollectionTypesPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/types/prettyprint/MCCollectionTypesPrettyPrinter.java new file mode 100644 index 0000000000..98ca6821f8 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/prettyprint/MCCollectionTypesPrettyPrinter.java @@ -0,0 +1,67 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.prettyprint; + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.types.mccollectiontypes._ast.*; +import de.monticore.types.mccollectiontypes._visitor.MCCollectionTypesHandler; +import de.monticore.types.mccollectiontypes._visitor.MCCollectionTypesTraverser; +import de.monticore.types.mccollectiontypes._visitor.MCCollectionTypesVisitor2; + +@Deprecated(forRemoval = true) +public class MCCollectionTypesPrettyPrinter implements MCCollectionTypesVisitor2, MCCollectionTypesHandler { + + protected IndentPrinter printer; + + protected MCCollectionTypesTraverser traverser; + + public MCCollectionTypesPrettyPrinter(IndentPrinter printer) { + this.printer = printer; + } + + public MCCollectionTypesTraverser getTraverser() { + return traverser; + } + + public void setTraverser(MCCollectionTypesTraverser traverser) { + this.traverser = traverser; + } + + public IndentPrinter getPrinter() { + return printer; + } + + public void setPrinter(IndentPrinter printer) { + this.printer = printer; + } + + @Override + public void handle(ASTMCListType a) { + getPrinter().print("List<"); + a.getMCTypeArgument().accept(getTraverser()); + getPrinter().print(">"); + } + + @Override + public void handle(ASTMCOptionalType a) { + getPrinter().print("Optional<"); + a.getMCTypeArgument().accept(getTraverser()); + getPrinter().print(">"); + } + + @Override + public void handle(ASTMCSetType a) { + getPrinter().print("Set<"); + a.getMCTypeArgument().accept(getTraverser()); + getPrinter().print(">"); + } + + @Override + public void handle(ASTMCMapType a) { + getPrinter().print("Map<"); + a.getKey().accept(getTraverser()); + getPrinter().print(","); + a.getValue().accept(getTraverser()); + getPrinter().print(">"); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/prettyprint/MCFullGenericTypesFullPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/types/prettyprint/MCFullGenericTypesFullPrettyPrinter.java new file mode 100644 index 0000000000..8854c66c5c --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/prettyprint/MCFullGenericTypesFullPrettyPrinter.java @@ -0,0 +1,47 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.prettyprint; + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.prettyprint.MCBasicsPrettyPrinter; +import de.monticore.types.mcfullgenerictypes.MCFullGenericTypesMill; +import de.monticore.types.mcfullgenerictypes._visitor.MCFullGenericTypesTraverser; + +@Deprecated(forRemoval = true) +public class MCFullGenericTypesFullPrettyPrinter extends MCSimpleGenericTypesFullPrettyPrinter { + + protected MCFullGenericTypesTraverser traverser; + + public MCFullGenericTypesFullPrettyPrinter(IndentPrinter printer) { + super(printer); + this.traverser = MCFullGenericTypesMill.traverser(); + + MCFullGenericTypesPrettyPrinter fullGenericTypes = new MCFullGenericTypesPrettyPrinter(printer); + traverser.setMCFullGenericTypesHandler(fullGenericTypes); + traverser.add4MCFullGenericTypes(fullGenericTypes); + + MCSimpleGenericTypesPrettyPrinter simpleGenericTypes = new MCSimpleGenericTypesPrettyPrinter(printer); + traverser.setMCSimpleGenericTypesHandler(simpleGenericTypes); + traverser.add4MCSimpleGenericTypes(simpleGenericTypes); + + MCCollectionTypesPrettyPrinter collectionTypes = new MCCollectionTypesPrettyPrinter(printer); + traverser.setMCCollectionTypesHandler(collectionTypes); + traverser.add4MCCollectionTypes(collectionTypes); + + MCBasicTypesPrettyPrinter basicTypes = new MCBasicTypesPrettyPrinter(printer); + traverser.setMCBasicTypesHandler(basicTypes); + traverser.add4MCBasicTypes(basicTypes); + + MCBasicsPrettyPrinter basics = new MCBasicsPrettyPrinter(printer); + traverser.add4MCBasics(basics); + } + + @Override + public MCFullGenericTypesTraverser getTraverser() { + return traverser; + } + + public void setTraverser(MCFullGenericTypesTraverser traverser) { + this.traverser = traverser; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/prettyprint/MCFullGenericTypesPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/types/prettyprint/MCFullGenericTypesPrettyPrinter.java new file mode 100644 index 0000000000..7768cd3ed8 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/prettyprint/MCFullGenericTypesPrettyPrinter.java @@ -0,0 +1,80 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.prettyprint; + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.types.mccollectiontypes._ast.ASTMCTypeArgument; +import de.monticore.types.mcfullgenerictypes._ast.ASTMCInnerType; +import de.monticore.types.mcfullgenerictypes._ast.ASTMCMultipleGenericType; +import de.monticore.types.mcfullgenerictypes._ast.ASTMCWildcardTypeArgument; +import de.monticore.types.mcfullgenerictypes._visitor.MCFullGenericTypesHandler; +import de.monticore.types.mcfullgenerictypes._visitor.MCFullGenericTypesTraverser; +import de.monticore.types.mcfullgenerictypes._visitor.MCFullGenericTypesVisitor2; + +@Deprecated(forRemoval = true) +public class MCFullGenericTypesPrettyPrinter implements MCFullGenericTypesVisitor2, MCFullGenericTypesHandler { + + protected MCFullGenericTypesTraverser traverser; + + protected IndentPrinter printer; + + public MCFullGenericTypesPrettyPrinter(IndentPrinter printer) { + this.printer = printer; + } + + public IndentPrinter getPrinter() { + return printer; + } + + public void setPrinter(IndentPrinter printer) { + this.printer = printer; + } + + @Override + public MCFullGenericTypesTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(MCFullGenericTypesTraverser traverser) { + this.traverser = traverser; + } + + @Override + public void handle(ASTMCWildcardTypeArgument node) { + getPrinter().print("?"); + if (node.isPresentUpperBound()) { + getPrinter().print(" extends "); + node.getUpperBound().accept(getTraverser()); + } else if (node.isPresentLowerBound()) { + getPrinter().print(" super "); + node.getLowerBound().accept(getTraverser()); + } + } + + @Override + public void handle(ASTMCInnerType innerType) { + getPrinter().print(innerType.getName()); + if(!innerType.getMCTypeArgumentList().isEmpty()) { + getPrinter().print("<"); + String komma = ""; + for (ASTMCTypeArgument arg : innerType.getMCTypeArgumentList()) { + getPrinter().print(komma); + arg.accept(getTraverser()); + komma = ","; + } + getPrinter().print(">"); + } + + } + + @Override + public void handle(ASTMCMultipleGenericType node) { + // prints first part a.b.C.E + node.getMCBasicGenericType().accept(getTraverser()); + + for(ASTMCInnerType innerType : node.getMCInnerTypeList()) { + getPrinter().print("."); + innerType.accept(getTraverser()); + } + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/prettyprint/MCFunctionTypesFullPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/types/prettyprint/MCFunctionTypesFullPrettyPrinter.java new file mode 100644 index 0000000000..3180e7d246 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/prettyprint/MCFunctionTypesFullPrettyPrinter.java @@ -0,0 +1,38 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.prettyprint; + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.prettyprint.MCBasicsPrettyPrinter; +import de.monticore.types.mcfunctiontypes.MCFunctionTypesMill; +import de.monticore.types.mcfunctiontypes._visitor.MCFunctionTypesTraverser; + +@Deprecated(forRemoval = true) +public class MCFunctionTypesFullPrettyPrinter extends MCBasicTypesFullPrettyPrinter { + + protected MCFunctionTypesTraverser traverser; + + public MCFunctionTypesFullPrettyPrinter(IndentPrinter printer) { + super(printer); + traverser = MCFunctionTypesMill.traverser(); + + MCFunctionTypesPrettyPrinter functionTypes = new MCFunctionTypesPrettyPrinter(printer); + traverser.setMCFunctionTypesHandler(functionTypes); + traverser.add4MCFunctionTypes(functionTypes); + + MCBasicTypesPrettyPrinter basicTypes = new MCBasicTypesPrettyPrinter(printer); + traverser.setMCBasicTypesHandler(basicTypes); + traverser.add4MCBasicTypes(basicTypes); + + MCBasicsPrettyPrinter basics = new MCBasicsPrettyPrinter(printer); + traverser.add4MCBasics(basics); + } + + public void setTraverser(MCFunctionTypesTraverser traverser) { + this.traverser = traverser; + } + + @Override + public MCFunctionTypesTraverser getTraverser() { + return traverser; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/prettyprint/MCFunctionTypesPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/types/prettyprint/MCFunctionTypesPrettyPrinter.java new file mode 100644 index 0000000000..7d3692a544 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/prettyprint/MCFunctionTypesPrettyPrinter.java @@ -0,0 +1,66 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.prettyprint; + +import de.monticore.prettyprint.CommentPrettyPrinter; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.types.mcfunctiontypes._ast.ASTMCFunctionParTypes; +import de.monticore.types.mcfunctiontypes._ast.ASTMCFunctionType; +import de.monticore.types.mcfunctiontypes._visitor.MCFunctionTypesHandler; +import de.monticore.types.mcfunctiontypes._visitor.MCFunctionTypesTraverser; +import de.monticore.types.mcfunctiontypes._visitor.MCFunctionTypesVisitor2; + +@Deprecated(forRemoval = true) +public class MCFunctionTypesPrettyPrinter + implements MCFunctionTypesVisitor2, MCFunctionTypesHandler { + + protected MCFunctionTypesTraverser traverser; + + protected IndentPrinter printer; + + public MCFunctionTypesPrettyPrinter(IndentPrinter printer) { + this.printer = printer; + } + + public IndentPrinter getPrinter() { + return printer; + } + + public void setPrinter(IndentPrinter printer) { + this.printer = printer; + } + + @Override + public MCFunctionTypesTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(MCFunctionTypesTraverser traverser) { + this.traverser = traverser; + } + + public void handle(ASTMCFunctionParTypes node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + getPrinter().print("("); + for (int i = 0; i < node.getMCTypeList().size(); i++) { + node.getMCType(i).accept(getTraverser()); + if (i < node.getMCTypeList().size() - 1) { + getPrinter().print(", "); + } + else if (node.isPresentIsElliptic()) { + getPrinter().print("..."); + } + } + getPrinter().print(")"); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + public void handle(ASTMCFunctionType node) { + CommentPrettyPrinter.printPreComments(node, getPrinter()); + node.getMCFunctionParTypes().accept(getTraverser()); + getPrinter().print(" -> "); + node.getMCReturnType().accept(getTraverser()); + CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/prettyprint/MCSimpleGenericTypesFullPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/types/prettyprint/MCSimpleGenericTypesFullPrettyPrinter.java new file mode 100644 index 0000000000..ac49ac7db3 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/prettyprint/MCSimpleGenericTypesFullPrettyPrinter.java @@ -0,0 +1,42 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.prettyprint; + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.prettyprint.MCBasicsPrettyPrinter; +import de.monticore.types.mcsimplegenerictypes.MCSimpleGenericTypesMill; +import de.monticore.types.mcsimplegenerictypes._visitor.MCSimpleGenericTypesTraverser; + +@Deprecated(forRemoval = true) +public class MCSimpleGenericTypesFullPrettyPrinter extends MCCollectionTypesFullPrettyPrinter { + + protected MCSimpleGenericTypesTraverser traverser; + + public MCSimpleGenericTypesFullPrettyPrinter(IndentPrinter printer) { + super(printer); + traverser = MCSimpleGenericTypesMill.traverser(); + + MCSimpleGenericTypesPrettyPrinter simpleGenericTypes = new MCSimpleGenericTypesPrettyPrinter(printer); + traverser.setMCSimpleGenericTypesHandler(simpleGenericTypes); + traverser.add4MCSimpleGenericTypes(simpleGenericTypes); + + MCCollectionTypesPrettyPrinter collectionTypes = new MCCollectionTypesPrettyPrinter(printer); + traverser.setMCCollectionTypesHandler(collectionTypes); + traverser.add4MCCollectionTypes(collectionTypes); + + MCBasicTypesPrettyPrinter basicTypes = new MCBasicTypesPrettyPrinter(printer); + traverser.setMCBasicTypesHandler(basicTypes); + traverser.add4MCBasicTypes(basicTypes); + + MCBasicsPrettyPrinter basics = new MCBasicsPrettyPrinter(printer); + traverser.add4MCBasics(basics); + } + + public void setTraverser(MCSimpleGenericTypesTraverser traverser) { + this.traverser = traverser; + } + + @Override + public MCSimpleGenericTypesTraverser getTraverser() { + return traverser; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types/prettyprint/MCSimpleGenericTypesPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/types/prettyprint/MCSimpleGenericTypesPrettyPrinter.java new file mode 100644 index 0000000000..7fda0b8b1d --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types/prettyprint/MCSimpleGenericTypesPrettyPrinter.java @@ -0,0 +1,80 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.prettyprint; + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.types.mccollectiontypes._ast.ASTMCTypeArgument; +import de.monticore.types.mcsimplegenerictypes._ast.ASTMCBasicGenericType; +import de.monticore.types.mcsimplegenerictypes._ast.ASTMCCustomTypeArgument; +import de.monticore.types.mcsimplegenerictypes._ast.ASTMCSimpleGenericTypesNode; +import de.monticore.types.mcsimplegenerictypes._visitor.MCSimpleGenericTypesHandler; +import de.monticore.types.mcsimplegenerictypes._visitor.MCSimpleGenericTypesTraverser; +import de.monticore.types.mcsimplegenerictypes._visitor.MCSimpleGenericTypesVisitor2; + +import java.util.Iterator; + +@Deprecated(forRemoval = true) +public class MCSimpleGenericTypesPrettyPrinter implements MCSimpleGenericTypesVisitor2, MCSimpleGenericTypesHandler { + + protected MCSimpleGenericTypesTraverser traverser; + + protected IndentPrinter printer; + + public MCSimpleGenericTypesPrettyPrinter(IndentPrinter printer){ + this.printer = printer; + } + + public IndentPrinter getPrinter() { + return printer; + } + + public void setPrinter(IndentPrinter printer) { + this.printer = printer; + } + + @Override + public MCSimpleGenericTypesTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(MCSimpleGenericTypesTraverser traverser) { + this.traverser = traverser; + } + + public void handle(ASTMCBasicGenericType node) { + getPrinter().print(String.join(".",node.getNameList())+"<"); + Iterator a = node.getMCTypeArgumentList().iterator(); + // printListSimpleGenericTypes(a,","); + String seperator = ","; + String sepTemp = ""; + + for(ASTMCTypeArgument t:node.getMCTypeArgumentList()) { + getPrinter().print(sepTemp); + t.accept(getTraverser()); + sepTemp = seperator; + } + getPrinter().print(">"); + } + + public void handle(ASTMCCustomTypeArgument node) { + node.getMCType().accept(getTraverser()); + } + + + /** + * Prints a list + * + * @param iter iterator for the list + * @param seperator string for seperating list + */ + protected void printListSimpleGenericTypes(Iterator iter, String seperator) { + // print by iterate through all items + String sep = ""; + while (iter.hasNext()) { + getPrinter().print(sep); + iter.next().accept(getTraverser()); + sep = seperator; + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types3/AbstractTypeVisitor.java b/monticore-grammar/src/main/java/de/monticore/types3/AbstractTypeVisitor.java new file mode 100644 index 0000000000..2636437660 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types3/AbstractTypeVisitor.java @@ -0,0 +1,53 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.types3; + +import de.monticore.symbols.basicsymbols._symboltable.IBasicSymbolsScope; +import de.monticore.symboltable.IScope; +import de.se_rwth.commons.logging.Log; + +/** + * extended by the main type system visitors + * gives access to common functionality, e.g., the map from expression to type + */ +public abstract class AbstractTypeVisitor { + + /** + * the name to be used for Log.info, etc. + */ + protected static final String LOG_NAME = "TypeVisitor"; + + //to be moved to the mill after specifics are discussed, DO NOT USE + public static Type4Ast tmpMap = new Type4Ast(); + + /** + * the map to be filled with type information + * In most cases, this is the global map in the mill. + * However, if a AstNode kann have multiple Types + * (e.g., variants within the same model), + * one may set a different map to be filled. + */ + protected Type4Ast type4Ast = tmpMap; + + public void setType4Ast(Type4Ast type4Ast) { + this.type4Ast = type4Ast; + } + + /** + * the map to be filled with type information. + * Deriving classes should always use this method to get the map. + */ + protected Type4Ast getType4Ast() { + return type4Ast; + } + + protected IBasicSymbolsScope getAsBasicSymbolsScope(IScope scope) { + // is accepted only here, decided on 07.04.2020 + if (!(scope instanceof IBasicSymbolsScope)) { + Log.error("0xA2307 the enclosing scope of the expression" + + " does not implement the interface IBasicSymbolsScope"); + } + // is accepted only here, decided on 07.04.2020 + return (IBasicSymbolsScope) scope; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types3/ISymTypeRelations.java b/monticore-grammar/src/main/java/de/monticore/types3/ISymTypeRelations.java new file mode 100644 index 0000000000..4d59248279 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types3/ISymTypeRelations.java @@ -0,0 +1,139 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types3; + +import de.monticore.types.check.SymTypeExpression; + +import java.util.Collection; +import java.util.List; +import java.util.Optional; + +/** + * Relations of SymTypeExpressions + *

+ * some are dependent on the specific type system + * and as such not hardcoded in the SymTypeExpressions themselves + */ +public interface ISymTypeRelations { + + /** + * whether the assignee can be assigned to by the assigner, + * e.g., assignment operator: x = 2, + * -> type of x and type of 2 need to be compatible, + * e.g., functions call: (float -> void)(2), + * -> float and type of 2 need to be compatible + */ + boolean isCompatible(SymTypeExpression assignee, SymTypeExpression assigner); + + /** + * whether subType is the sub-type of superType, + * Examples: + * isSubType(Person, Person) + * isSubType(Student, Person) + * !isSubType(Person, Student) + * isSubType(int, float) + * !isSubType(float, int) + */ + boolean isSubTypeOf(SymTypeExpression subType, SymTypeExpression superType); + + /** + * returns nominal supertypes. + * Nominal supertypes are those that are explicitly listed as super types, + * e.g., in Java those specified using "extends" or "implements". + * The return value is neither the reflexive nor the transitive closure, + * i.e., only the direct supertypes are included (s. Java spec 20 4.10). + * Note that the "direct" supertype-relation is deliberately underspecified, + * such that it can be refined according to the specific type system's needs. + */ + List getNominalSuperTypes(SymTypeExpression thisType); + + /** + * Boxes SymTypeExpressions, + * including, but not limited to, Java primitive boxing + * e.g., int -> java.lang.Integer + * e.g., List -> java.util.List + */ + SymTypeExpression box(SymTypeExpression unboxed); + + /** + * Unboxes SymTypeExpressions, + * including, but not limited to, Java primitive unboxing + * e.g., java.lang.Integer -> int + * e.g., java.util.List -> List + */ + SymTypeExpression unbox(SymTypeExpression boxed); + + /** + * normalizes the SymTypeExpression, + * e.g., (A & B[])[] -> (A[] & B[][]) + *

+ * Within our type systems, each type has ONE normalized representation. + * This can be used to, e.g., compare SymTypeExpressions + */ + SymTypeExpression normalize(SymTypeExpression type); + + /** + * least upper bound for a set of types + * for e.g. union types + * unlike the Java counterpart, + * we specify it for non-reference types as well, + * making it more akin to Java conditional expressions, + * where "a?b:c" has type leastUpperBound(b,c) + *

+ * empty represents the universal type (aka the lack of a bound) + * Obscure is returned, if no lub could be calculated, e.g. lub(int, Person) + */ + Optional leastUpperBound( + Collection types); + + default Optional leastUpperBound( + SymTypeExpression... types) { + return leastUpperBound(List.of(types)); + } + + // primitives + + /** + * calculates the one promoted numeric type, + * ignoring the specifics of the context + * s. Java spec. 20 5.6 + * e.g., short -> int + * e.g., byte, float -> float + */ + SymTypeExpression numericPromotion(List types); + + default SymTypeExpression numericPromotion(SymTypeExpression... types) { + return numericPromotion(List.of(types)); + } + + /** + * test if the expression is of numeric type, + * e.g., in Java: (double, float, long, int, char, short, byte) + */ + boolean isNumericType(SymTypeExpression type); + + /** + * test if the expression is of integral type, + * e.g., in Java: (long, int, char, short, byte) + */ + boolean isIntegralType(SymTypeExpression type); + + boolean isBoolean(SymTypeExpression type); + + boolean isInt(SymTypeExpression type); + + boolean isDouble(SymTypeExpression type); + + boolean isFloat(SymTypeExpression type); + + boolean isLong(SymTypeExpression type); + + boolean isChar(SymTypeExpression type); + + boolean isShort(SymTypeExpression type); + + boolean isByte(SymTypeExpression type); + + boolean isString(SymTypeExpression type); + +} + diff --git a/monticore-grammar/src/main/java/de/monticore/types3/ISymTypeVisitor.java b/monticore-grammar/src/main/java/de/monticore/types3/ISymTypeVisitor.java new file mode 100644 index 0000000000..77562c4efb --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types3/ISymTypeVisitor.java @@ -0,0 +1,55 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.types3; + +import de.monticore.types.check.SymTypeArray; +import de.monticore.types.check.SymTypeObscure; +import de.monticore.types.check.SymTypeOfFunction; +import de.monticore.types.check.SymTypeOfGenerics; +import de.monticore.types.check.SymTypeOfIntersection; +import de.monticore.types.check.SymTypeOfNull; +import de.monticore.types.check.SymTypeOfObject; +import de.monticore.types.check.SymTypeOfUnion; +import de.monticore.types.check.SymTypeOfWildcard; +import de.monticore.types.check.SymTypePrimitive; +import de.monticore.types.check.SymTypeVariable; +import de.monticore.types.check.SymTypeVoid; + +public interface ISymTypeVisitor { + + default void visit(SymTypeArray array) { + } + + default void visit(SymTypeObscure obscure) { + } + + default void visit(SymTypeOfFunction function) { + } + + default void visit(SymTypeOfGenerics generic) { + } + + default void visit(SymTypeOfIntersection intersection) { + } + + default void visit(SymTypeOfNull nullSymType) { + } + + default void visit(SymTypeOfObject object) { + } + + default void visit(SymTypeOfUnion union) { + } + + default void visit(SymTypePrimitive primitive) { + } + + default void visit(SymTypeVariable typeVar) { + } + + default void visit(SymTypeVoid voidSymType) { + } + + default void visit(SymTypeOfWildcard wildcard) { + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types3/SymTypeRelations.java b/monticore-grammar/src/main/java/de/monticore/types3/SymTypeRelations.java new file mode 100644 index 0000000000..bb78915cf8 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types3/SymTypeRelations.java @@ -0,0 +1,16 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types3; + +/** + * Relations of SymTypeExpressions + *

+ * some are dependent on the specific type system + * and as such not hardcoded in the SymTypeExpressions themselves + * + * @deprecated moved to types3.util, use ISymTypeRelations instead + */ +@Deprecated(forRemoval = true) +public class SymTypeRelations extends de.monticore.types3.util.SymTypeRelations { + +} + diff --git a/monticore-grammar/src/main/java/de/monticore/types3/Type4Ast.java b/monticore-grammar/src/main/java/de/monticore/types3/Type4Ast.java new file mode 100644 index 0000000000..098fbb38f7 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types3/Type4Ast.java @@ -0,0 +1,480 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.types3; + +import de.monticore.ast.ASTNode; +import de.monticore.expressions.commonexpressions.CommonExpressionsMill; +import de.monticore.expressions.commonexpressions._ast.ASTFieldAccessExpression; +import de.monticore.expressions.commonexpressions._util.CommonExpressionsTypeDispatcher; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.expressionsbasis._ast.ASTNameExpression; +import de.monticore.literals.mccommonliterals._ast.ASTSignedLiteral; +import de.monticore.literals.mcliteralsbasis._ast.ASTLiteral; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types.mcbasictypes._ast.ASTMCQualifiedName; +import de.monticore.types.mcbasictypes._ast.ASTMCReturnType; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.mccollectiontypes._ast.ASTMCTypeArgument; +import de.se_rwth.commons.SourcePosition; +import de.se_rwth.commons.logging.Log; +import org.apache.commons.io.FilenameUtils; + +import java.util.HashMap; +import java.util.Map; + +/** + * Stores the type of expressions and type identifiers, + * as such they do not need to be calculated again. + * get the type of an expression (e.g., "1+2") using + * {@link Type4Ast#getTypeOfExpression}. + * get the type of a type identifier (e.g., "int") using + * {@link Type4Ast#getTypeOfTypeIdentifier}. + * The getters marked with "Partial" and the setters + * are used by the type traverser filling the map. + */ +public class Type4Ast { + + protected static final String LOG_NAME = "Type4Ast"; + + /** + * the actual map from expression to types, + * strictly seperated from the map for type identifiers + * we use ASTNode to support non-ASTExpression Nodes (e.g., literals) + * however, we do NOT support non-expression ASTNodes, + * e.g. in MyClass.myMethod() -> the "MyClass" is not an expression by itself + */ + protected Map expr2type; + + protected Map getExpression2Type() { + return expr2type; + } + + /** + * the actual map from type identifier to types, + * strictly seperated from the map for expressions + * we use ASTNode to support non-ASTMCType Nodes (e.g. qualified Names) + * however, we do NOT support expression ASTNodes, + * e.g. other qualified names that represent a variable, rather than a type + */ + protected Map typeID2type; + + protected Map getTypeIdentifier2Type() { + return typeID2type; + } + + public Type4Ast() { + reset(); + } + + public void reset() { + expr2type = new HashMap<>(); + typeID2type = new HashMap<>(); + } + + /** + * whether a type has been calculated for the expression + */ + public boolean hasTypeOfExpression(ASTExpression astExpr) { + return internal_hasTypeOfExpression((ASTNode) astExpr); + } + + /** + * whether a type has been calculated for the literal + */ + public boolean hasTypeOfExpression(ASTLiteral astLit) { + return internal_hasTypeOfExpression((ASTNode) astLit); + } + + /** + * whether a type has been calculated for the signed literal + */ + public boolean hasTypeOfExpression(ASTSignedLiteral astSignLit) { + return internal_hasTypeOfExpression((ASTNode) astSignLit); + } + + protected boolean internal_hasTypeOfExpression(ASTNode node) { + if (!getExpression2Type().containsKey(node)) { + return false; + } + return !getExpression2Type().get(node).isObscureType(); + } + + public boolean hasTypeOfTypeIdentifier(ASTMCType mcType) { + return internal_hasTypeOfTypeIdentifier((ASTNode) mcType); + } + + public boolean hasTypeOfTypeIdentifier(ASTMCReturnType mcReturnType) { + return internal_hasTypeOfTypeIdentifier((ASTNode) mcReturnType); + } + + public boolean hasTypeOfTypeIdentifier(ASTMCQualifiedName mcQName) { + return internal_hasTypeOfTypeIdentifier((ASTNode) mcQName); + } + + public boolean hasTypeOfTypeIdentifier(ASTMCTypeArgument mcTypeArg) { + return internal_hasTypeOfTypeIdentifier((ASTNode) mcTypeArg); + } + + public boolean hasTypeOfTypeIdentifierForName(ASTExpression nameExpr) { + if (!isQNameExpr(nameExpr)) { + Log.error("0xFD4B4 internal error: " + + "expected a qualified name, " + + "this is not an issue with the model, " + + "the wrong internal method was called.", + nameExpr.get_SourcePositionStart(), + nameExpr.get_SourcePositionEnd() + ); + } + return internal_hasTypeOfTypeIdentifier((ASTNode) nameExpr); + } + + /** + * whether a type has been calculated for the type identifier + */ + protected boolean internal_hasTypeOfTypeIdentifier(ASTNode node) { + if (!getTypeIdentifier2Type().containsKey(node)) { + return false; + } + return !getTypeIdentifier2Type().get(node).isObscureType(); + } + + /** + * gets the type information of the expression + */ + public SymTypeExpression getTypeOfExpression(ASTExpression astExpr) { + return internal_getTypeOfExpression((ASTNode) astExpr); + } + + /** + * gets the type information of the literal + */ + public SymTypeExpression getTypeOfExpression(ASTLiteral astLit) { + return internal_getTypeOfExpression((ASTNode) astLit); + } + + /** + * gets the type information of the signed literal + */ + public SymTypeExpression getTypeOfExpression(ASTSignedLiteral astSignLit) { + return internal_getTypeOfExpression((ASTNode) astSignLit); + } + + protected SymTypeExpression internal_getTypeOfExpression(ASTNode node) { + if (internal_hasTypeOfExpression(node)) { + return getExpression2Type().get(node); + } + Log.error("0xFD791 type of expression unknown but requested", + node.get_SourcePositionStart(), + node.get_SourcePositionEnd() + ); + return SymTypeExpressionFactory.createObscureType(); + } + + public SymTypeExpression getPartialTypeOfExpr(ASTExpression astExpr) { + return internal_getPartialTypeOfExpr((ASTNode) astExpr); + } + + public SymTypeExpression getPartialTypeOfExpr(ASTLiteral astLit) { + return internal_getPartialTypeOfExpr((ASTNode) astLit); + } + + public SymTypeExpression getPartialTypeOfExpr(ASTSignedLiteral astSignLit) { + return internal_getPartialTypeOfExpr((ASTNode) astSignLit); + } + + /** + * returns potentially partial type information of the expression + * internally used by type deriver + */ + protected SymTypeExpression internal_getPartialTypeOfExpr(ASTNode node) { + if (!getExpression2Type().containsKey(node)) { + Log.error("0x7C001 internal error: type information expected" + + " but not present.", + node.get_SourcePositionStart(), + node.get_SourcePositionEnd() + ); + return SymTypeExpressionFactory.createObscureType(); + } + return getExpression2Type().get(node); + } + + public SymTypeExpression getTypeOfTypeIdentifier(ASTMCType mcType) { + return internal_getTypeOfTypeIdentifier((ASTNode) mcType); + } + + public SymTypeExpression getTypeOfTypeIdentifier(ASTMCReturnType mcReturnType) { + return internal_getTypeOfTypeIdentifier((ASTNode) mcReturnType); + } + + public SymTypeExpression getTypeOfTypeIdentifier(ASTMCQualifiedName qName) { + return internal_getTypeOfTypeIdentifier((ASTNode) qName); + } + + public SymTypeExpression getTypeOfTypeIdentifier(ASTMCTypeArgument typeArg) { + return internal_getTypeOfTypeIdentifier((ASTNode) typeArg); + } + + /** + * gets the type of the type identifier if it has been calculated + */ + protected SymTypeExpression internal_getTypeOfTypeIdentifier(ASTNode node) { + if (internal_hasTypeOfTypeIdentifier(node)) { + return getTypeIdentifier2Type().get(node); + } + Log.error("0xFD792 type of type identifier unknown but requested", + node.get_SourcePositionStart(), + node.get_SourcePositionEnd() + ); + return SymTypeExpressionFactory.createObscureType(); + } + + public SymTypeExpression getPartialTypeOfTypeId(ASTMCType mcType) { + return internal_getPartialTypeOfTypeId((ASTNode) mcType); + } + + public SymTypeExpression getPartialTypeOfTypeId(ASTMCReturnType mcReturnType) { + return internal_getPartialTypeOfTypeId((ASTNode) mcReturnType); + } + + public SymTypeExpression getPartialTypeOfTypeId(ASTMCQualifiedName qName) { + return internal_getPartialTypeOfTypeId((ASTNode) qName); + } + + public SymTypeExpression getPartialTypeOfTypeId(ASTMCTypeArgument typeArg) { + return internal_getPartialTypeOfTypeId((ASTNode) typeArg); + } + + /** + * A special case for specific MCQualifiedNames + * s. {@link Type4Ast#setTypeOfTypeIdentifierForName( + *ASTFieldAccessExpression, SymTypeExpression)} + */ + public SymTypeExpression getPartialTypeOfTypeIdForName(ASTExpression nameExpr) { + if (!isQNameExpr(nameExpr)) { + Log.error("0xFD4B5 internal error: " + + "expected a qualified name, " + + "this is not an issue with the model, " + + "the wrong internal method was called", + nameExpr.get_SourcePositionStart(), + nameExpr.get_SourcePositionEnd() + ); + } + return internal_getPartialTypeOfTypeId((ASTNode) nameExpr); + } + + /** + * @deprecated do not use, only here until fix in grammar + */ + @Deprecated + public SymTypeExpression internal_getPartialTypeOfTypeId2(ASTNode node) { + return internal_getPartialTypeOfTypeId(node); + } + + /** + * returns potentially partial type information of the type identifier + * used by type deriver + */ + protected SymTypeExpression internal_getPartialTypeOfTypeId(ASTNode node) { + if (!getTypeIdentifier2Type().containsKey(node)) { + Log.error("0xFD799 internal error: type information expected" + + " but not present.", + node.get_SourcePositionStart(), + node.get_SourcePositionEnd() + ); + return SymTypeExpressionFactory.createObscureType(); + } + return getTypeIdentifier2Type().get(node); + } + + /** + * sets the type information of the expression, + * information may be partial + */ + public void setTypeOfExpression( + ASTExpression astExpr, + SymTypeExpression typeExpr + ) { + internal_setTypeOfExpression((ASTNode) astExpr, typeExpr); + } + + /** + * sets the type information of the literal, + * information may be partial + */ + public void setTypeOfExpression( + ASTLiteral astLit, + SymTypeExpression typeExpr + ) { + internal_setTypeOfExpression((ASTNode) astLit, typeExpr); + } + + public void setTypeOfExpression( + ASTSignedLiteral astSignLit, + SymTypeExpression typeExpr + ) { + internal_setTypeOfExpression((ASTNode) astSignLit, typeExpr); + } + + protected void internal_setTypeOfExpression( + ASTNode node, + SymTypeExpression typeExpr + ) { + if (internal_hasTypeOfExpression(node)) { + Log.trace(node2InfoString(node) + + ": had the expression type " + + internal_getTypeOfExpression(node).printFullName(), + LOG_NAME + ); + } + Log.trace(node2InfoString(node) + + ": expression type is " + + typeExpr.printFullName(), + LOG_NAME + ); + getExpression2Type().put(node, typeExpr); + } + + public void setTypeOfTypeIdentifier( + ASTMCType mcType, + SymTypeExpression type + ) { + internal_setTypeOfTypeIdentifier((ASTNode) mcType, type); + } + + public void setTypeOfTypeIdentifier( + ASTMCReturnType mcReturnType, + SymTypeExpression type + ) { + internal_setTypeOfTypeIdentifier((ASTNode) mcReturnType, type); + } + + public void setTypeOfTypeIdentifier( + ASTMCQualifiedName qName, + SymTypeExpression type + ) { + internal_setTypeOfTypeIdentifier((ASTNode) qName, type); + } + + /** + * a special case of the MCQualifiedName + * s. {@link Type4Ast#setTypeOfTypeIdentifierForName( + *ASTNameExpression, SymTypeExpression)} + */ + public void setTypeOfTypeIdentifierForName( + ASTFieldAccessExpression qName, + SymTypeExpression type) { + internal_setTypeOfTypeIdentifier((ASTNode) qName, type); + } + + /** + * a special case of the MCQualifiedName + *

+ * this is only used in cases there the type identifier + * 1. was parsed as an expression (instead of a MCQualifiedName) and + * 2. the AST can not be transformed to accommodate without + * non-conservative grammar changes + *

+ * In our Java-esque languages, this usually happens before ".", + * e.g., C in C.staticVar, C.staticMethod() C.this.var, C.super.method(), ... + * As the type id has been parsed as an expression, + * and nothing further is known without reflection, + * getting the type information cannot be type safe + * ("type safe" as in (partially) ensuring correct usage by the type checker), + * thus the uniqueness of the methods' names. + * note that (technically) not even ASTExpression applicable enough, + * due to some grammar extension points + * The getter is {@link Type4Ast#getPartialTypeOfTypeIdForName(ASTExpression)} + */ + public void setTypeOfTypeIdentifierForName( + ASTNameExpression name, + SymTypeExpression type) { + internal_setTypeOfTypeIdentifier((ASTNode) name, type); + } + + public void setTypeOfTypeIdentifier( + ASTMCTypeArgument typeArg, + SymTypeExpression type) { + internal_setTypeOfTypeIdentifier((ASTNode) typeArg, type); + } + + /** + * @deprecated do not use, remove after fix of grammars + */ + @Deprecated + public void internal_setTypeOfTypeIdentifier2( + ASTNode node, + SymTypeExpression type) { + internal_setTypeOfTypeIdentifier(node, type); + } + + /** + * sets the type information of the type identifier, + * information may be partial + */ + protected void internal_setTypeOfTypeIdentifier( + ASTNode node, + SymTypeExpression typeExpr) { + if (internal_hasTypeOfTypeIdentifier(node)) { + Log.trace(node2InfoString(node) + + ": had the type id " + + internal_getTypeOfTypeIdentifier(node).printFullName(), + LOG_NAME + ); + } + Log.trace(node2InfoString(node) + + ": type id is " + + typeExpr.printFullName(), + LOG_NAME + ); + getTypeIdentifier2Type().put(node, typeExpr); + } + + // Helper + + /** + * whether the expression represents a qualified name + */ + protected boolean isQNameExpr(ASTExpression expr) { + CommonExpressionsTypeDispatcher typeDispatcher = + CommonExpressionsMill.typeDispatcher(); + if (typeDispatcher.isASTNameExpression(expr)) { + return true; + } + else if (typeDispatcher.isASTFieldAccessExpression(expr)) { + return isQNameExpr( + typeDispatcher.asASTFieldAccessExpression(expr).getExpression() + ); + } + else { + return false; + } + } + + /** + * helps with logging. + */ + protected String node2InfoString(ASTNode node) { + // may be moved from here, if required somewhere else as well + // as multiple expressions can start at the same position, + // we need to know the end position as well + // even better would be access to the model source the positions refer to... + // based on SourcePosition::toString + StringBuilder result = new StringBuilder(); + if (node.isPresent_SourcePositionStart() && + node.isPresent_SourcePositionEnd()) { + SourcePosition startPos = node.get_SourcePositionStart(); + SourcePosition endPos = node.get_SourcePositionEnd(); + if (startPos.getFileName().isPresent()) { + result.append(FilenameUtils.getName(startPos.getFileName().get())); + result.append(":"); + } + result.append("<" + startPos.getLine() + "," + startPos.getColumn() + ">"); + result.append("-"); + result.append("<" + endPos.getLine() + "," + endPos.getColumn() + ">"); + } + else { + result.append("unknown position"); + } + return result.toString(); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types3/util/BuiltInTypeRelations.java b/monticore-grammar/src/main/java/de/monticore/types3/util/BuiltInTypeRelations.java new file mode 100644 index 0000000000..9f728e05a8 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types3/util/BuiltInTypeRelations.java @@ -0,0 +1,168 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.types3.util; + +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.se_rwth.commons.logging.Log; + +import java.util.List; + +/** + * relations for built-in SymTypes + * these are the primitives, boxed primitives and String + * delegate of SymTypeRelations + */ +public class BuiltInTypeRelations { + + public BuiltInTypeRelations() { + } + + /** + * @deprecated use constructor {@link #BuiltInTypeRelations()} + */ + @Deprecated + public BuiltInTypeRelations(SymTypeRelations symTypeRelations) { + } + + public SymTypeExpression numericPromotion(List types) { + // according to the Java Specification (20) + + for (SymTypeExpression type : types) { + if (!isNumericType(type)) { + Log.error("0xFD285 internal error: tried to get" + + "numeric promotion of non-numerics"); + } + } + + for (SymTypeExpression type : types) { + if (isDouble(type)) { + return SymTypeExpressionFactory + .createPrimitive(BasicSymbolsMill.DOUBLE); + } + } + for (SymTypeExpression type : types) { + if (isFloat(type)) { + return SymTypeExpressionFactory + .createPrimitive(BasicSymbolsMill.FLOAT); + } + } + for (SymTypeExpression type : types) { + if (isLong(type)) { + return SymTypeExpressionFactory + .createPrimitive(BasicSymbolsMill.LONG); + } + } + + // in arithmetic and array contexts, the promoted type is int + // in a numeric choice context (e.g. a?2:4), + // the result would not always be int + // this has currently no relevance + return SymTypeExpressionFactory + .createPrimitive(BasicSymbolsMill.INT); + } + + public boolean isNumericType(SymTypeExpression type) { + return (isDouble(type) || isFloat(type) || isIntegralType(type)); + } + + public boolean isIntegralType(SymTypeExpression type) { + return (isLong(type) || isInt(type) || isChar(type) || + isShort(type) || isByte(type) + ); + } + + public boolean isBoolean(SymTypeExpression type) { + if (type.isPrimitive()) { + return type.printFullName().equals(BasicSymbolsMill.BOOLEAN); + } + if (type.isObjectType()) { + return type.printFullName().equals("java.lang.Boolean"); + } + return false; + } + + public boolean isInt(SymTypeExpression type) { + if (type.isPrimitive()) { + return type.printFullName().equals(BasicSymbolsMill.INT); + } + if (type.isObjectType()) { + return type.printFullName().equals("java.lang.Integer"); + } + return false; + } + + public boolean isDouble(SymTypeExpression type) { + if (type.isPrimitive()) { + return type.printFullName().equals(BasicSymbolsMill.DOUBLE); + } + if (type.isObjectType()) { + return type.printFullName().equals("java.lang.Double"); + } + return false; + } + + public boolean isFloat(SymTypeExpression type) { + if (type.isPrimitive()) { + return type.printFullName().equals(BasicSymbolsMill.FLOAT); + } + if (type.isObjectType()) { + return type.printFullName().equals("java.lang.Float"); + } + return false; + } + + public boolean isLong(SymTypeExpression type) { + if (type.isPrimitive()) { + return type.printFullName().equals(BasicSymbolsMill.LONG); + } + if (type.isObjectType()) { + return type.printFullName().equals("java.lang.Long"); + } + return false; + } + + public boolean isChar(SymTypeExpression type) { + if (type.isPrimitive()) { + return type.printFullName().equals(BasicSymbolsMill.CHAR); + } + if (type.isObjectType()) { + return type.printFullName().equals("java.lang.Character"); + } + return false; + } + + public boolean isShort(SymTypeExpression type) { + if (type.isPrimitive()) { + return type.printFullName().equals(BasicSymbolsMill.SHORT); + } + if (type.isObjectType()) { + return type.printFullName().equals("java.lang.Short"); + } + return false; + } + + public boolean isByte(SymTypeExpression type) { + if (type.isPrimitive()) { + return type.printFullName().equals(BasicSymbolsMill.BYTE); + } + if (type.isObjectType()) { + return type.printFullName().equals("java.lang.Byte"); + } + return false; + } + + public boolean isString(SymTypeExpression type) { + // unboxed version of String is unlikely to be defined + // as such we do not bother trying to unbox + if (type.isObjectType()) { + String fullName = type.printFullName(); + return fullName.equals("String") // unboxed + || fullName.equals("java.lang.String"); // boxed + } + else { + return false; + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types3/util/FunctionRelations.java b/monticore-grammar/src/main/java/de/monticore/types3/util/FunctionRelations.java new file mode 100644 index 0000000000..e1c8a83b1b --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types3/util/FunctionRelations.java @@ -0,0 +1,124 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.types3.util; + +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeOfFunction; +import de.monticore.types3.ISymTypeRelations; +import de.se_rwth.commons.logging.Log; + +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * calculates, e.g., whether a function can be called given specified arguments, + * one may assume this functionality ought to be in SymTypeOfFunction, + * however, it relies on other functionality in SymTypeRelations, + * and the behaviour of SymTypeClasses + * should not be dependent on the current type system + * (or one would need to pass the SymTypeRelations to the SymTypes) + * delegate of SymTypeRelations + */ +public class FunctionRelations { + + protected ISymTypeRelations symTypeRelations; + + public FunctionRelations(ISymTypeRelations symTypeRelations) { + this.symTypeRelations = symTypeRelations; + } + + protected ISymTypeRelations getSymTypeRelations() { + return symTypeRelations; + } + + public boolean canBeCalledWith( + SymTypeOfFunction func, + List args) { + // check amount of arguments + if (!func.isElliptic() && args.size() != func.sizeArgumentTypes()) { + return false; + } + else if (func.isElliptic() && args.size() < func.sizeArgumentTypes() - 1) { + return false; + } + // check the arguments themselves + else { + for (int i = 0; i < args.size(); i++) { + SymTypeExpression paramType = func.getArgumentType( + Math.min(i, func.getArgumentTypeList().size() - 1)); + if (!getSymTypeRelations().isCompatible(paramType, args.get(i))) { + // todo extend when adding generic support + return false; + } + } + } + return true; + } + + /** + * for overloaded functions, selects the best fitting one + * we expect inferred arities (no elliptic functions) + * simplified version (which can be extended) of: + * * Java spec v.20 15.12.2.5 + * * Java spec v.20 18.5.4 + */ + public Optional getMostSpecificFunction( + Collection funcs) { + Optional mostSpecificFunction; + if (funcs.isEmpty()) { + mostSpecificFunction = Optional.empty(); + } + else { + int arity = funcs.stream().findAny().get().sizeArgumentTypes(); + if (funcs.stream().anyMatch(SymTypeOfFunction::isElliptic) + || funcs.stream().anyMatch(f -> f.sizeArgumentTypes() != arity)) { + Log.error("0xFD11D internal error:" + + "expected a set of functions with same arity"); + mostSpecificFunction = Optional.empty(); + } + else { + Set potentialFuncs = new HashSet<>(funcs); + // if a potential function has a parameter type which is not a subType + // of all the corresponding parameter types of the other functions, + // the function is not as specific as the other functions + for (int i = 0; i < arity; i++) { + int it = i; + potentialFuncs.removeIf( + potFunc -> funcs.stream().anyMatch( + func -> !getSymTypeRelations().isSubTypeOf( + getSymTypeRelations().box(potFunc.getArgumentType(it)), + getSymTypeRelations().box(func.getArgumentType(it)) + ) + ) + ); + } + if (potentialFuncs.isEmpty()) { + Log.error("0xFDCBA could not determine most specific function of:" + + System.lineSeparator() + + funcs.stream() + .map(SymTypeOfFunction::printFullName) + .collect(Collectors.joining(System.lineSeparator())) + ); + mostSpecificFunction = Optional.empty(); + } + else if (potentialFuncs.size() > 1) { + Log.error("0xFDCBB could not determine most specific function of:" + + System.lineSeparator() + + potentialFuncs.stream() + .map(SymTypeOfFunction::printFullName) + .collect(Collectors.joining(System.lineSeparator())) + ); + mostSpecificFunction = Optional.empty(); + } + else { + mostSpecificFunction = potentialFuncs.stream().findAny(); + } + } + } + + return mostSpecificFunction; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types3/util/ILValueRelations.java b/monticore-grammar/src/main/java/de/monticore/types3/util/ILValueRelations.java new file mode 100644 index 0000000000..b6feb9a377 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types3/util/ILValueRelations.java @@ -0,0 +1,23 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.expressions.expressionsbasis.types3.util; + +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; + +/** + * Tests whether an expression can be considered an LValue + * for corresponding assignment CoCos. + * Expressions have a type and a value category. + * In our type systems values can be categorised by whether they have + * an identity, i.e., an address in memory, and thus can be assigned to. + * Values that can be assigned to are lvalues, + * examples include variables, e.g., + * int i = 0; // i is a variable and thus a lvalue + * int[] is = new int[3]; is[0] = 0; // is[0] is a variable and thus a lvalue + * Note, not every lvalue can be assigned to, + * e.g., a final variable that has been assigned to already. + */ +public interface ILValueRelations { + + boolean isLValue(ASTExpression expression); + +} \ No newline at end of file diff --git a/monticore-grammar/src/main/java/de/monticore/types3/util/NameExpressionTypeCalculator.java b/monticore-grammar/src/main/java/de/monticore/types3/util/NameExpressionTypeCalculator.java new file mode 100644 index 0000000000..d30dc0ab4f --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types3/util/NameExpressionTypeCalculator.java @@ -0,0 +1,231 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.types3.util; + +import de.monticore.symbols.basicsymbols._symboltable.FunctionSymbol; +import de.monticore.symbols.basicsymbols._symboltable.IBasicSymbolsScope; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.symbols.basicsymbols._symboltable.TypeVarSymbol; +import de.monticore.symbols.basicsymbols._symboltable.VariableSymbol; +import de.monticore.symboltable.IScope; +import de.monticore.symboltable.modifiers.AccessModifier; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types.check.SymTypeOfFunction; +import de.se_rwth.commons.logging.Log; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +/** + * contains the code to derive / synthesize the type of a single name. + * Names may contain a qualifier, e.g., + * a.b.c with a.b being a qualifier and a.b.c being a type would resolved, + * a.b.c with a.b being a type + * and c being a type within a.b would NOT be resolved. + */ +public class NameExpressionTypeCalculator { + + protected TypeContextCalculator typeCtxCalc; + + protected WithinTypeBasicSymbolsResolver withinTypeResolver; + + protected NameExpressionTypeCalculator( + TypeContextCalculator typeCtxCalc, + WithinTypeBasicSymbolsResolver withinTypeResolver + ) { + this.typeCtxCalc = typeCtxCalc; + this.withinTypeResolver = withinTypeResolver; + } + + public NameExpressionTypeCalculator() { + // default values + typeCtxCalc = new TypeContextCalculator(); + withinTypeResolver = new WithinTypeBasicSymbolsResolver(); + } + + public void setWithinTypeBasicSymbolsResolver( + WithinTypeBasicSymbolsResolver withinTypeResolver) { + this.withinTypeResolver = withinTypeResolver; + } + + public void setTypeContextCalculator(TypeContextCalculator typeCtxCalc) { + this.typeCtxCalc = typeCtxCalc; + } + + protected TypeContextCalculator getTypeCtxCalc() { + return typeCtxCalc; + } + + protected WithinTypeBasicSymbolsResolver getWithinTypeResolver() { + return withinTypeResolver; + } + + public Optional typeOfNameAsExpr( + IBasicSymbolsScope enclosingScope, + String name) { + // collect all (potential) types + Set types = new HashSet<>(); + + // to circumvent current shortcomings in our resolver, + // we resolve with the resolver AND resolve with the within type resolver + // afterwards we evaluate which result to use + + // not necessarily in an enclosing type + Optional optVar = enclosingScope.resolveVariable( + name, AccessModifier.ALL_INCLUSION, getVariablePredicate()) + .map(v -> v.getType()); + List funcs = enclosingScope + .resolveFunctionMany(name, getFunctionPredicate()).stream() + // todo remove creation of a set + // after resolver is fixed to not return duplicates + .collect(Collectors.toSet()) + .stream() + .map(FunctionSymbol::getFunctionType) + .collect(Collectors.toList()); + // within type + Optional enclosingType = + getTypeCtxCalc().getEnclosingType(enclosingScope); + Optional varInType = Optional.empty(); + List funcsInType = Collections.emptyList(); + if (enclosingType.isPresent()) { + SymTypeExpression enclosingTypeExpr = + SymTypeExpressionFactory.createFromSymbol(enclosingType.get()); + AccessModifier modifier = getTypeCtxCalc() + .getAccessModifier(enclosingType.get(), enclosingScope); + varInType = getWithinTypeResolver().resolveVariable( + enclosingTypeExpr, name, modifier, getVariablePredicate()); + funcsInType = getWithinTypeResolver().resolveFunctions( + enclosingTypeExpr, name, modifier, getFunctionPredicate()); + } + // get the correct variable + if (varInType.isPresent() && optVar.isPresent()) { + if (varInType.get().getTypeInfo() == optVar.get().getTypeInfo()) { + types.add(varInType.get()); + } + else if (optVar.get().getTypeInfo().getEnclosingScope() + .isProperSubScopeOf(varInType.get().getTypeInfo().getEnclosingScope())) { + types.add(optVar.get()); + } + else { + types.add(varInType.get()); + } + } + else if (varInType.isPresent()) { + types.add(varInType.get()); + } + else if (optVar.isPresent()) { + types.add(optVar.get()); + } + // get the correct functions + // heuristic, as we assume the resolver to be extended in the near future, + // and the correct solution would take longer to implement, + // in Javalight, these cases may never happen anyways + types.addAll(funcsInType); + for (SymTypeOfFunction func : funcs) { + if (funcsInType.stream().noneMatch(f -> f.getSymbol() == func.getSymbol())) { + types.add(func); + } + } + + if (types.size() <= 1) { + return types.stream().findAny(); + } + else { + // this can be extended to mark the intersection as + // an intersection that one has to select a type of. + // The current interpretation is, that the result is all the possible types, + // e.g. "a.b" could have the types of (int, int->int, boolean->int). + // in Java, this would be filtered out earlier, + // however, we support more (e.g. SymTypeOfFunction). + return Optional.of(SymTypeExpressionFactory.createIntersection(types)); + } + } + + /** + * used to filter function symbols + * this is an extension point to, + * e.g., filter out constructors in OO-Symbols + */ + protected Predicate getFunctionPredicate() { + return f -> true; + } + + /** + * used to filter variable symbols + * this is an extension point + */ + protected Predicate getVariablePredicate() { + return v -> true; + } + + public Optional typeOfNameAsTypeId( + IBasicSymbolsScope enclosingScope, + String name) { + Optional type; + // variable + Optional optTypeVar = enclosingScope.resolveTypeVar( + name, AccessModifier.ALL_INCLUSION, getTypeVarPredicate() + ); + // object + Optional optObj = enclosingScope.resolveType(name, + AccessModifier.ALL_INCLUSION, getTypePredicate() + .and(t -> optTypeVar.map(tv -> tv != t).orElse(true)) + ); + // in Java the type variable is preferred + // e.g. class C{class U{} U v;} //new C().v has type Float + if (optTypeVar.isPresent() && optObj.isPresent()) { + Log.info("found type variable and object type for \"" + + name + + "\", selecting type variable", + "TypeVisitor"); + } + if (optTypeVar.isPresent()) { + type = Optional.of(SymTypeExpressionFactory + .createTypeVariable(optTypeVar.get())); + } + else if (optObj.isPresent()) { + type = Optional.of(SymTypeExpressionFactory + .createFromSymbol(optObj.get()) + ); + } + else { + type = Optional.empty(); + } + return type; + } + + /** + * used to filter type symbols + * this is NOT used to filter type variables + * this is an extension point + */ + protected Predicate getTypePredicate() { + return t -> true; + } + + /** + * used to filter type variable symbols + * this is an extension point + */ + protected Predicate getTypeVarPredicate() { + return tv -> true; + } + + // Helper + + protected IBasicSymbolsScope getAsBasicSymbolsScope(IScope scope) { + // is accepted only here, decided on 07.04.2020 + if (!(scope instanceof IBasicSymbolsScope)) { + Log.error("0xA2307 the enclosing scope of the expression" + + "does not implement the interface IBasicSymbolsScope"); + } + // is accepted only here, decided on 07.04.2020 + return (IBasicSymbolsScope) scope; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types3/util/NominalSuperTypeCalculator.java b/monticore-grammar/src/main/java/de/monticore/types3/util/NominalSuperTypeCalculator.java new file mode 100644 index 0000000000..6b80589d60 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types3/util/NominalSuperTypeCalculator.java @@ -0,0 +1,148 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.types3.util; + +import de.monticore.symbols.basicsymbols._symboltable.TypeVarSymbol; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeOfGenerics; +import de.monticore.types.check.SymTypeOfIntersection; +import de.monticore.types.check.SymTypeOfUnion; +import de.monticore.types.check.SymTypeVariable; +import de.monticore.types3.ISymTypeRelations; +import de.se_rwth.commons.logging.Log; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public class NominalSuperTypeCalculator { + + protected final static String LOG_NAME = "NominalSuperTypes"; + + protected ISymTypeRelations symTypeRelations; + + protected SymTypeVariableReplaceVisitor replaceVisitor; + + protected NominalSuperTypeCalculator() { + // default values + // SymTypeRelations has no default, + // as this tends to be part of SymTypeRelations + this.replaceVisitor = new SymTypeVariableReplaceVisitor(); + } + + public NominalSuperTypeCalculator(ISymTypeRelations typeRelations) { + // default values + this(); + this.symTypeRelations = typeRelations; + } + + protected ISymTypeRelations getSymTypeRelations() { + return symTypeRelations; + } + + /** + * supertypes, but modified according to type parameters. + * Practically, this is meant to be used with object types including generics. + * This returns the list of nominal supertypes, + * e.g., in Java using extends / implements + * e.g. Collection is an explicit super type of List, + * List is a super type of List, + * but not an explicitly defined one. + * We consider explicitly defined super types to be the ones + * given by the list of super types in the type symbol. + */ + public List getNominalSuperTypes(SymTypeExpression thisType) { + if (!isSupported(thisType)) { + Log.info("tried to get list of nominal super types " + + "of unsupported type: " + thisType.printFullName(), + LOG_NAME); + return Collections.emptyList(); + } + List superTypes; + List unmodifiedSuperTypes = + getUnmodifiedSuperTypesList(thisType); + if (thisType.isGenericType()) { + Map replaceMap = + ((SymTypeOfGenerics) thisType).getTypeVariableReplaceMap(); + superTypes = new ArrayList<>(); + for (SymTypeExpression superType : unmodifiedSuperTypes) { + superTypes.add(replaceVariables(superType, replaceMap)); + } + } + else { + superTypes = unmodifiedSuperTypes; + } + return superTypes; + } + + // Helper + + protected List getUnmodifiedSuperTypesList(SymTypeExpression thisType) { + List unmodifiedSuperTypes; + // object + if (thisType.isObjectType() || thisType.isGenericType()) { + unmodifiedSuperTypes = thisType.getTypeInfo().getSuperTypesList(); + } + // type variable + else if (thisType.isTypeVariable()) { + SymTypeExpression upperBound = ((SymTypeVariable) thisType).getUpperBound(); + // directly split intersection / union + if (upperBound.isIntersectionType() || upperBound.isUnionType()) { + unmodifiedSuperTypes = getUnmodifiedSuperTypesList(upperBound); + } + else { + unmodifiedSuperTypes = new ArrayList<>(); + unmodifiedSuperTypes.add(upperBound); + } + } + // intersection + // s. java spec 20 4.10.2: + // The intersected types are the direct superTypes of the intersection. + else if (thisType.isIntersectionType()) { + unmodifiedSuperTypes = new ArrayList<>( + ((SymTypeOfIntersection) thisType).getIntersectedTypeSet()); + } + // union + // The direct superTypes of a union are the direct supertypes + // of the LuB of it's unionized types. + // This is somewhat(!) similar to Java spec 20 14.20, + // but Java barely supports union types in the first place. + else if (thisType.isUnionType()) { + Collection unionizedTypes = + ((SymTypeOfUnion) thisType).getUnionizedTypeSet(); + Optional lubOpt = + getSymTypeRelations().leastUpperBound(unionizedTypes); + unmodifiedSuperTypes = lubOpt + .filter(lub -> isSupported(lub)) + .map(lub -> getNominalSuperTypes(lub)) + .orElse(Collections.emptyList()); + } + // extension point + else { + Log.info("tried to get nominal supertypes of " + + thisType.printFullName() + + " which is currently not supported", + LOG_NAME + ); + unmodifiedSuperTypes = Collections.emptyList(); + } + return unmodifiedSuperTypes; + } + + protected boolean isSupported(SymTypeExpression type) { + return type.isObjectType() || + type.isGenericType() || + type.isTypeVariable() || + type.isIntersectionType() || + type.isUnionType(); + } + + protected SymTypeExpression replaceVariables( + SymTypeExpression type, + Map replaceMap) { + return replaceVisitor.calculate(type, replaceMap); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types3/util/OONameExpressionTypeCalculator.java b/monticore-grammar/src/main/java/de/monticore/types3/util/OONameExpressionTypeCalculator.java new file mode 100644 index 0000000000..7f9e9c0e04 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types3/util/OONameExpressionTypeCalculator.java @@ -0,0 +1,41 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.types3.util; + +import de.monticore.symbols.basicsymbols._symboltable.FunctionSymbol; +import de.monticore.symbols.oosymbols.OOSymbolsMill; +import de.monticore.symbols.oosymbols._symboltable.MethodSymbol; + +import java.util.function.Predicate; + +/** + * contains the code to derive / synthesize the type of a single name, + * but is "OO-aware" e.g. constructors are filtered out. + */ +public class OONameExpressionTypeCalculator + extends NameExpressionTypeCalculator { + + public OONameExpressionTypeCalculator() { + // default values + super( + new TypeContextCalculator(), + new OOWithinTypeBasicSymbolsResolver() + ); + } + + /** + * filter out any constructors + */ + @Override + protected Predicate getFunctionPredicate() { + return f -> { + if (OOSymbolsMill.typeDispatcher().isMethod(f)) { + MethodSymbol m = OOSymbolsMill.typeDispatcher().asMethod(f); + if (m.isIsConstructor()) { + return false; + } + } + return true; + }; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types3/util/OOWithinTypeBasicSymbolsResolver.java b/monticore-grammar/src/main/java/de/monticore/types3/util/OOWithinTypeBasicSymbolsResolver.java new file mode 100644 index 0000000000..48086d868a --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types3/util/OOWithinTypeBasicSymbolsResolver.java @@ -0,0 +1,90 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.types3.util; + +import de.monticore.symbols.basicsymbols._symboltable.FunctionSymbol; +import de.monticore.symbols.basicsymbols._symboltable.IBasicSymbolsScope; +import de.monticore.symbols.oosymbols.OOSymbolsMill; +import de.monticore.symbols.oosymbols._symboltable.MethodSymbol; +import de.monticore.symboltable.modifiers.AccessModifier; +import de.monticore.symboltable.modifiers.StaticAccessModifier; + +import java.util.List; +import java.util.Map; +import java.util.function.Predicate; + +/** + * resolves within a type, + * unlike {@link WithinTypeBasicSymbolsResolver}, + * we further filter by "OO-rules", + * e.g., a constructor cannot be called like other methods + */ +public class OOWithinTypeBasicSymbolsResolver + extends WithinTypeBasicSymbolsResolver { + + public OOWithinTypeBasicSymbolsResolver(SymTypeRelations symTypeRelations) { + super(symTypeRelations); + } + + public OOWithinTypeBasicSymbolsResolver() { + super(); + } + + // Helper + + /** + * resolves locally, EXCLUDING supertypes + * this filters out constructors + */ + @Override + protected List resolveFunctionLocally( + IBasicSymbolsScope scope, + String name, + AccessModifier accessModifier, + Predicate predicate) { + return super.resolveFunctionLocally( + scope, name, accessModifier, + predicate.and(Predicate.not(this::isConstructor)) + ); + } + + /** + * same as {@link #resolveFunctionLocally} + * but does only returns constructors + */ + public List resolveConstructorLocally( + IBasicSymbolsScope scope, + String name, + AccessModifier accessModifier, + Predicate predicate + ) { + return super.resolveFunctionLocally( + scope, name, + removeStaticness(accessModifier), + predicate.and(this::isConstructor) + ); + } + + // Helper + + protected boolean isConstructor(FunctionSymbol func) { + if (OOSymbolsMill.typeDispatcher().isMethod(func)) { + MethodSymbol method = + OOSymbolsMill.typeDispatcher().asMethod(func); + return method.isIsConstructor(); + } + return false; + } + + /** + * replaces any static/non-static access with all access + * this is done as we want to ignore isStatic in constructors + * There are some languages, where this distinction is relevant (e.g., C#) + */ + protected AccessModifier removeStaticness(AccessModifier accessModifier) { + AccessModifier newModifier = accessModifier.shallowCopy(); + Map map = newModifier.getDimensionToModifierMap(); + map.remove(StaticAccessModifier.DIMENSION); + return newModifier; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types3/util/SymTypeBoxingVisitor.java b/monticore-grammar/src/main/java/de/monticore/types3/util/SymTypeBoxingVisitor.java new file mode 100644 index 0000000000..248a76ba0c --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types3/util/SymTypeBoxingVisitor.java @@ -0,0 +1,151 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.types3.util; + +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types.check.SymTypeOfGenerics; +import de.monticore.types.check.SymTypeOfObject; +import de.monticore.types.check.SymTypePrimitive; +import de.se_rwth.commons.logging.Log; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +/** + * Boxes SymTypeExpressions, + * including, but not limited to, Java primitive boxing + * e.g., int -> java.lang.Integer + * e.g., List -> java.util.List + * Usage: + * calculate(symType) + */ +public class SymTypeBoxingVisitor extends SymTypeDeepCloneVisitor { + + /** + * Map for boxing primitive types (e.g. "int" -> "java.lang.Integer") + * Results are fully qualified. + */ + protected static final Map primitiveBoxMap; + + /** + * Map for boxing object types (e.g. "String" -> "java.lang.String") + * Results are fully qualified. + */ + protected static final Map objectBoxMap; + + /** + * Map for boxing generic types (e.g. "List" -> "java.util.List") + * Results are fully qualified. + */ + protected static final Map genericBoxMap; + + public Map getPrimitiveBoxMap() { + return primitiveBoxMap; + } + + public Map getObjectBoxMap() { + return objectBoxMap; + } + + public Map getGenericBoxMap() { + return genericBoxMap; + } + + /** + * initializing the maps + */ + static { + Map primitiveBoxMap_temp = new HashMap<>(); + primitiveBoxMap_temp.put("boolean", "java.lang.Boolean"); + primitiveBoxMap_temp.put("byte", "java.lang.Byte"); + primitiveBoxMap_temp.put("char", "java.lang.Character"); + primitiveBoxMap_temp.put("double", "java.lang.Double"); + primitiveBoxMap_temp.put("float", "java.lang.Float"); + primitiveBoxMap_temp.put("int", "java.lang.Integer"); + primitiveBoxMap_temp.put("long", "java.lang.Long"); + primitiveBoxMap_temp.put("short", "java.lang.Short"); + primitiveBoxMap = Collections.unmodifiableMap(primitiveBoxMap_temp); + + Map objectBoxMap_temp = new HashMap<>(); + objectBoxMap_temp.put("String", "java.lang.String"); + objectBoxMap = Collections.unmodifiableMap(objectBoxMap_temp); + + Map genericBoxMap_temp = new HashMap<>(); + genericBoxMap_temp.put("Optional", "java.util.Optional"); + genericBoxMap_temp.put("Set", "java.util.Set"); + genericBoxMap_temp.put("List", "java.util.List"); + genericBoxMap_temp.put("Map", "java.util.Map"); + genericBoxMap = Collections.unmodifiableMap(genericBoxMap_temp); + } + + @Override + public void visit(SymTypeOfGenerics symType) { + final String name = symType.getTypeConstructorFullName(); + Optional typeSymbolOpt = + resolveBoxedSymType(name, getGenericBoxMap()); + TypeSymbol typeSymbol = typeSymbolOpt.orElse(symType.getTypeInfo()); + pushTransformedSymType(SymTypeExpressionFactory.createGenerics( + typeSymbol, + applyToCollection(symType.getArgumentList()) + )); + } + + @Override + public void visit(SymTypeOfObject symType) { + final String name = symType.printFullName(); + Optional typeSymbolOpt = + resolveBoxedSymType(name, getObjectBoxMap()); + pushTransformedSymType(SymTypeExpressionFactory.createTypeObject( + typeSymbolOpt.orElse(symType.getTypeInfo()) + )); + } + + @Override + public void visit(SymTypePrimitive symType) { + final String name = symType.getPrimitiveName(); + Optional typeSymbolOpt = + resolveBoxedSymType(name, getPrimitiveBoxMap()); + if (typeSymbolOpt.isPresent()) { + pushTransformedSymType( + SymTypeExpressionFactory.createTypeObject(typeSymbolOpt.get()) + ); + } + else { + pushTransformedSymType( + SymTypeExpressionFactory.createPrimitive(symType.getTypeInfo()) + ); + } + } + + // Helpers + + /** + * iff there is a boxed variant of the given symtype, + * this tries to resolve it + */ + protected Optional resolveBoxedSymType(String name, Map boxMap) { + // getting the correct name to use of a SymTypeExpression is inconsistent, + // as such we pass it as a parameter along with the map + if (boxMap.containsKey(name)) { + final String boxedName = boxMap.get(name); + Optional boxedTypeSymbolOpt = + BasicSymbolsMill.globalScope().resolveType(boxedName); + if (!boxedTypeSymbolOpt.isPresent()) { + Log.info("symbol for boxed type " + + boxedName + + " is not found for type " + + name + + " and is thus not used", + "Typing"); + } + return boxedTypeSymbolOpt; + } + else { + return Optional.empty(); + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types3/util/SymTypeCompatibilityCalculator.java b/monticore-grammar/src/main/java/de/monticore/types3/util/SymTypeCompatibilityCalculator.java new file mode 100644 index 0000000000..ccad34c297 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types3/util/SymTypeCompatibilityCalculator.java @@ -0,0 +1,533 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.types3.util; + +import de.monticore.types.check.SymTypeArray; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types.check.SymTypeOfFunction; +import de.monticore.types.check.SymTypeOfGenerics; +import de.monticore.types.check.SymTypeOfIntersection; +import de.monticore.types.check.SymTypeOfObject; +import de.monticore.types.check.SymTypeOfUnion; +import de.monticore.types.check.SymTypePrimitive; +import de.monticore.types.check.SymTypeVariable; +import de.monticore.types3.ISymTypeRelations; +import de.se_rwth.commons.logging.Log; + +import java.util.ArrayList; +import java.util.List; + +import static de.monticore.types.check.SymTypeExpressionFactory.createObscureType; + +/** + * checks for compatibility between SymTypes + * delegate of SymTypeRelations + */ +public class SymTypeCompatibilityCalculator { + + protected ISymTypeRelations symTypeRelations; + + public SymTypeCompatibilityCalculator(ISymTypeRelations symTypeRelations) { + this.symTypeRelations = symTypeRelations; + } + + protected ISymTypeRelations getSymTypeRelations() { + return symTypeRelations; + } + + public boolean isCompatible( + SymTypeExpression assignee, + SymTypeExpression assigner) { + boolean result; + // null is compatible to any non-primitive type + if (!assignee.isPrimitive() && assigner.isNullType()) { + result = true; + } + // subtypes are assignable to their supertypes + // in addition, we allow boxing + else { + SymTypeExpression boxedAssignee = + getSymTypeRelations().box(assignee); + SymTypeExpression boxedAssigner = + getSymTypeRelations().box(assigner); + result = internal_isSubTypeOf(boxedAssigner, boxedAssignee, true); + } + return result; + } + + public boolean isSubTypeOf(SymTypeExpression subType, SymTypeExpression superType) { + return internal_isSubTypeOf(subType, superType, false); + } + + /** + * isSubTypeOf and canBeSubTypeOf, as they are very similar + * subTypeIsSoft if it is only a possibility, that it is a subtype + */ + public boolean internal_isSubTypeOf( + SymTypeExpression subType, + SymTypeExpression superType, + boolean subTypeIsSoft + ) { + SymTypeExpression normalizedSubType = + getSymTypeRelations().normalize(subType); + SymTypeExpression normalizedSuperType = + getSymTypeRelations().normalize(superType); + return internal_isSubTypeOfPreNormalized( + normalizedSubType, + normalizedSuperType, + subTypeIsSoft + ); + } + + public boolean internal_isSubTypeOfPreNormalized( + SymTypeExpression subType, + SymTypeExpression superType, + boolean subTypeIsSoft + ) { + boolean result; + // subType union -> all unionized types must be a subtype + if (subType.isUnionType()) { + result = true; + for (SymTypeExpression subUType : + ((SymTypeOfUnion) subType).getUnionizedTypeSet()) { + result = result + && internal_isSubTypeOfPreNormalized(subUType, superType, subTypeIsSoft); + } + } + // supertype union -> must be a subtype of any unionized type + else if (superType.isUnionType()) { + result = false; + for (SymTypeExpression superUType : + ((SymTypeOfUnion) superType).getUnionizedTypeSet()) { + result = result + || internal_isSubTypeOfPreNormalized(subType, superUType, subTypeIsSoft); + } + } + // supertype intersection -> must be a subtype of all intersected types + else if (superType.isIntersectionType()) { + result = true; + for (SymTypeExpression superIType : + ((SymTypeOfIntersection) superType).getIntersectedTypeSet()) { + result = result + && internal_isSubTypeOfPreNormalized(subType, superIType, subTypeIsSoft); + } + } + // subType intersection -> any intersected type must be a subtype + else if (subType.isIntersectionType()) { + result = false; + for (SymTypeExpression subIType : + ((SymTypeOfIntersection) subType).getIntersectedTypeSet()) { + result = result + || internal_isSubTypeOfPreNormalized(subIType, superType, subTypeIsSoft); + } + } + else { + result = singleIsSubTypeOf(subType, superType, subTypeIsSoft); + } + return result; + } + + // extension points + // all extension points expect normalized types as input + + /** + * this tests all symtypes that are not union or intersection types + * (and don't contain them) + */ + protected boolean singleIsSubTypeOf( + SymTypeExpression subType, + SymTypeExpression superType, + boolean subTypeIsSoft + ) { + boolean result; + // variable + if (superType.isTypeVariable() || subType.isTypeVariable()) { + result = typeVarIsSubTypeOf(subType, superType, subTypeIsSoft); + } + // arrays + else if (superType.isArrayType() && subType.isArrayType()) { + result = arrayIsSubTypeOf( + (SymTypeArray) subType, + (SymTypeArray) superType, + subTypeIsSoft + ); + } + // unboxed primitives + else if (superType.isPrimitive() && subType.isPrimitive()) { + result = unboxedPrimitiveIsSubTypeOf( + (SymTypePrimitive) subType, + (SymTypePrimitive) superType, + subTypeIsSoft + ); + } + // boxed primitives + else if ( + !superType.isPrimitive() && !subType.isPrimitive() && + (getSymTypeRelations().isNumericType(superType) || + getSymTypeRelations().isBoolean(superType)) && + (getSymTypeRelations().isNumericType(subType) || + getSymTypeRelations().isBoolean(subType)) + ) { + result = boxedPrimitiveIsSubTypeOf( + (SymTypeOfObject) subType, + (SymTypeOfObject) superType, + subTypeIsSoft + ); + } + // functions + else if (superType.isFunctionType() && subType.isFunctionType()) { + result = functionIsSubTypeOf( + (SymTypeOfFunction) subType, + (SymTypeOfFunction) superType, + subTypeIsSoft + ); + } + // objects + else if ( + (superType.isObjectType() || superType.isGenericType()) && + (subType.isObjectType() || subType.isGenericType())) { + result = objectIsSubTypeOf(subType, superType, subTypeIsSoft); + } + else { + result = false; + } + return result; + } + + /** + * whether one expression is the subType of another, + * with at least one of the types being a variable. + */ + protected boolean typeVarIsSubTypeOf( + SymTypeExpression subType, + SymTypeExpression superType, + boolean subTypeIsSoft + ) { + // this is not enough for full generics + boolean result; + // T is compatible to T + // this has to be checked specifically, + // as two unbounded type variable are not subTypes of each other otherwise + if (subType.isTypeVariable() && + superType.isTypeVariable() && + subType.deepEquals(superType) + ) { + result = true; + } + // T extends B is a subType of A iff B is a subType of A. + else if (subType.isTypeVariable()) { + SymTypeVariable subVar = (SymTypeVariable) subType; + SymTypeExpression normalizedUpperBound = + getSymTypeRelations().normalize(subVar.getUpperBound()); + result = internal_isSubTypeOfPreNormalized( + normalizedUpperBound, superType, subTypeIsSoft); + } + // T super A is a superType of B iff A is a superType of B. + // This example cannot be (directly) represented using Java, + // but can occur while type checking Java (s. Wild FJ (2005)) + else if (superType.isTypeVariable()) { + SymTypeVariable superVar = (SymTypeVariable) superType; + SymTypeExpression normalizedLowerBound = + getSymTypeRelations().normalize(superVar.getLowerBound()); + result = internal_isSubTypeOfPreNormalized( + subType, normalizedLowerBound, subTypeIsSoft); + } + else { + Log.error("0xFDB32 internal error, " + + "expected an type variable"); + result = false; + } + return result; + } + + /** + * whether one array is the subType of another + */ + protected boolean arrayIsSubTypeOf( + SymTypeArray subArray, + SymTypeArray superArray, + boolean subTypeIsSoft + ) { + return superArray.getDim() == subArray.getDim() && + internal_isSubTypeOfPreNormalized( + superArray.getArgument(), + subArray.getArgument(), + subTypeIsSoft + ); + } + + /** + * whether unboxed primitives are in a subType relation + * s. Java spec 20 4.10.1 + */ + protected boolean unboxedPrimitiveIsSubTypeOf( + SymTypePrimitive subType, + SymTypePrimitive superType, + boolean subTypeIsSoft + ) { + return primitiveIsSubTypeOf(subType, superType, subTypeIsSoft); + } + + /** + * whether a boxed primitive is the subType of another + * This version is NOT conform to the java spec (20); + * Java does not allow, e.g., Integer i = 2; Float f = i; + * Note, that this is (nearly) never an issue about losing object identity: + * E.g., Integer x = 2; Integer y = 2; // x == y holds + * but, Integer x = 222; Integer y = 222; // x != y holds + * (tested in JDK 17) + * s.a. Java spec 20 5.1.7 + * Thus, unlike Java we allow Integer i = 2; Float f = i;, + * is a subTypeRelation analoge to unboxed primitives + * This method needs to be overridden + * if a more Java-conform check is required + */ + protected boolean boxedPrimitiveIsSubTypeOf( + SymTypeOfObject subType, + SymTypeOfObject superType, + boolean subTypeIsSoft + ) { + return primitiveIsSubTypeOf(subType, superType, subTypeIsSoft); + } + + protected boolean primitiveIsSubTypeOf( + SymTypeExpression subType, + SymTypeExpression superType, + boolean subTypeIsSoft + ) { + boolean result; + if (getSymTypeRelations().isBoolean(superType) && + getSymTypeRelations().isBoolean(subType)) { + result = true; + } + else if (getSymTypeRelations().isNumericType(superType) && + getSymTypeRelations().isNumericType(subType)) { + if (getSymTypeRelations().isDouble(superType)) { + result = true; + } + else if (getSymTypeRelations().isFloat(superType) && + (getSymTypeRelations().isFloat(subType) || + getSymTypeRelations().isIntegralType(subType) + )) { + result = true; + } + else if (getSymTypeRelations().isLong(superType) && + getSymTypeRelations().isIntegralType( + subType)) { + result = true; + } + else if (getSymTypeRelations().isInt(superType) && + getSymTypeRelations().isIntegralType(subType) && + !getSymTypeRelations().isLong(subType)) { + result = true; + } + else if (getSymTypeRelations().isChar(superType) && + getSymTypeRelations().isChar(subType)) { + result = true; + } + else if (getSymTypeRelations().isShort(superType) && + (getSymTypeRelations().isShort(subType) + || getSymTypeRelations().isByte(subType))) { + result = true; + } + else if (getSymTypeRelations().isByte(superType) && + getSymTypeRelations().isByte(subType)) { + result = true; + } + else { + result = false; + } + } + else { + result = false; + } + return result; + } + + protected boolean functionIsSubTypeOf( + SymTypeOfFunction subFunc, + SymTypeOfFunction superFunc, + boolean subTypeIsSoft + ) { + boolean result = true; + // return type + result = result && + internal_isSubTypeOfPreNormalized(superFunc.getType(), subFunc.getType(), subTypeIsSoft); + // if the super function is elliptic, the sub one must be as well + result = result && (subFunc.isElliptic() || !superFunc.isElliptic()); + // if they are not elliptic, the number of arguments must be the same + result = result && (subFunc.isElliptic() || + subFunc.sizeArgumentTypes() == superFunc.sizeArgumentTypes()); + // check if all arguments are compatible + int argsToCheck = Math.max( + superFunc.sizeArgumentTypes(), + subFunc.isElliptic() ? + subFunc.sizeArgumentTypes() - 1 : + subFunc.sizeArgumentTypes() + ); + for (int i = 0; result && i < argsToCheck; i++) { + SymTypeExpression subParamType = + subFunc.isEmptyArgumentTypes() ? + createObscureType() : + subFunc.getArgumentType( + Math.min(i, subFunc.sizeArgumentTypes() - 1) + ); + SymTypeExpression superParamType = + superFunc.isEmptyArgumentTypes() ? + createObscureType() : + superFunc.getArgumentType( + Math.min(i, superFunc.sizeArgumentTypes() - 1) + ); + result = result && internal_isSubTypeOfPreNormalized( + subParamType, superParamType, subTypeIsSoft + ); + } + return result; + } + + /** + * whether one object is a subType of another + * this includes SymTypeOfObject, as well as SymTypeOfGenerics + */ + protected boolean objectIsSubTypeOf( + SymTypeExpression subType, + SymTypeExpression superType, + boolean subTypeIsSoft + ) { + boolean result; + String superName; + List superArgs; + if (superType.isGenericType()) { + superName = ((SymTypeOfGenerics) superType).getTypeConstructorFullName(); + superArgs = ((SymTypeOfGenerics) superType).getArgumentList(); + } + else { + superName = superType.printFullName(); + superArgs = new ArrayList<>(); + } + String subName; + List subArgs; + if (subType.isGenericType()) { + subName = ((SymTypeOfGenerics) subType).getTypeConstructorFullName(); + subArgs = ((SymTypeOfGenerics) subType).getArgumentList(); + } + else { + subName = subType.printFullName(); + subArgs = new ArrayList<>(); + } + if (subName.equals(superName)) { + if (subArgs.size() != superArgs.size()) { + Log.error("0xFD6AD internal error: type \"" + subName + "\" " + + "used with inconsistent amount of type arguments: " + + subType.printFullName() + " and " + + superType.printFullName() + ); + return false; + } + // cannot use subTyping-relation for type arguments; + // List is not a superType of List or vice versa. + // However, e.g., List is a superType of List + result = true; + for (int i = 0; i < subArgs.size(); i++) { + result = result && + containsPreNormalized(subArgs.get(i), superArgs.get(i)); + } + } + else { + // check super classes + result = false; + for (SymTypeExpression subSuperExpr : getSuperTypes(subType)) { + result = result || + // here we box the symTypes, as we did not before + // this leads to List being a subType of java.util.List + // as Lists of primitives are not available in Java, + // they are interpreted as being identical. + internal_isSubTypeOfPreNormalized( + getSymTypeRelations().normalize( + getSymTypeRelations().box(subSuperExpr) + ), + getSymTypeRelations().box(superType), + subTypeIsSoft + ); + } + } + return result; + } + + // Helper + + /** + * Is the set of types denoted by subSetType a subSet + * of the set of types denoted by superSetType? + * s. Java spec 20 4.5.1, 4.10.2 + * A type variable represents multiple Types, + * in other cases, this is an identity check. + * s. a. {@link #typeVarIsSubTypeOf} + * The arguments are expected to be normalized. + * Additionally, type variables within other symTypes + * are currently not supported, + * this helper function (currently) is only to check subtyping of generics. + *

+ * Fundamentally, T1 "contains" T2 ("T2 <= T1") + * if the set of types denoted by T1 is (provably) a superSet + * of the types denoted by T2. + * This translates to the reflexive and transitive closure of (from spec): + *

    + *
  • ? extends T <= ? extends S if T <: S + *
  • ? extends T <= ? + *
  • ? super T <= ? super S if S <: T + *
  • ? super T <= ? + *
  • ? super T <= ? extends Object + *
  • T <= T + *
  • T <= ? extends T + *
  • T <= ? super T + *
+ */ + protected boolean containsPreNormalized( + SymTypeExpression subSetType, + SymTypeExpression superSetType + ) { + boolean result; + // stop recursive checks (e.g., A>) + if (subSetType.deepEquals(superSetType)) { + result = true; + } + // check both upper and lower bound + else { + // convert both arguments into TypeVariables with upper and lower bound + SymTypeVariable subSetVar; + SymTypeVariable superSetVar; + if (subSetType.isTypeVariable()) { + subSetVar = (SymTypeVariable) subSetType; + } + else { + subSetVar = SymTypeExpressionFactory + .createTypeVariable(subSetType, subSetType); + } + if (superSetType.isTypeVariable()) { + superSetVar = (SymTypeVariable) superSetType; + } + else { + superSetVar = SymTypeExpressionFactory + .createTypeVariable(superSetType, superSetType); + } + // check that the subSetVar bounds are within the superSetVar bounds + if (!internal_isSubTypeOfPreNormalized(subSetVar.getUpperBound(), superSetVar.getUpperBound(), + false)) { + result = false; + } + else if (!internal_isSubTypeOfPreNormalized(superSetVar.getLowerBound(), + subSetVar.getLowerBound(), false)) { + result = false; + } + else { + result = true; + } + } + return result; + } + + protected List getSuperTypes(SymTypeExpression thisType) { + return getSymTypeRelations().getNominalSuperTypes(thisType); + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types3/util/SymTypeDeepCloneVisitor.java b/monticore-grammar/src/main/java/de/monticore/types3/util/SymTypeDeepCloneVisitor.java new file mode 100644 index 0000000000..6ce09ad71e --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types3/util/SymTypeDeepCloneVisitor.java @@ -0,0 +1,228 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.types3.util; + +import de.monticore.symbols.basicsymbols._symboltable.FunctionSymbol; +import de.monticore.types.check.SymTypeArray; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types.check.SymTypeObscure; +import de.monticore.types.check.SymTypeOfFunction; +import de.monticore.types.check.SymTypeOfGenerics; +import de.monticore.types.check.SymTypeOfIntersection; +import de.monticore.types.check.SymTypeOfNull; +import de.monticore.types.check.SymTypeOfObject; +import de.monticore.types.check.SymTypeOfUnion; +import de.monticore.types.check.SymTypeOfWildcard; +import de.monticore.types.check.SymTypePrimitive; +import de.monticore.types.check.SymTypeVariable; +import de.monticore.types.check.SymTypeVoid; +import de.monticore.types3.ISymTypeVisitor; +import de.se_rwth.commons.logging.Log; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.Stack; + +/** + * clones SymTypeExpressions + * its main usage is to be derived from, + * to generate SymTypeExpressions, which are "not quite" clones, + * e.g., boxing of SymTypes + * Usage: + * calculate(mySymType) + */ +public class SymTypeDeepCloneVisitor implements ISymTypeVisitor { + + protected Stack transformedSymTypes = new Stack<>(); + + protected Stack getTransformedSymTypes() { + return transformedSymTypes; + } + + public void reset() { + this.transformedSymTypes = new Stack<>(); + } + + /** + * returns the transformed SymTypeExpression + */ + public SymTypeExpression getTransformedSymType() { + if (getTransformedSymTypes().isEmpty()) { + Log.error("0xFD822 internal error: getting empty result," + + " did the visitor not run?"); + } + if (getTransformedSymTypes().size() > 1) { + Log.error("0xFD823 internal error: getting result" + + " while partial results are present"); + } + return getTransformedSymTypes().peek(); + } + + protected SymTypeExpression popTransformedSubSymType() { + if (getTransformedSymTypes().size() < 1) { + Log.error("0xFD824 internal error: getting partial result" + + " while partial results are not present"); + } + return getTransformedSymTypes().pop(); + } + + protected void pushTransformedSymType(SymTypeExpression type) { + getTransformedSymTypes().push(type); + } + + @Override + public void visit(SymTypeArray symType) { + symType.getArgument().accept(this); + pushTransformedSymType(SymTypeExpressionFactory + .createTypeArray(popTransformedSubSymType(), symType.getDim())); + } + + @Override + public void visit(SymTypeObscure symType) { + pushTransformedSymType(SymTypeExpressionFactory.createObscureType()); + } + + @Override + public void visit(SymTypeOfFunction symType) { + symType.getType().accept(this); + FunctionSymbol symbol = null; + if (symType.hasSymbol()) { + symbol = symType.getSymbol(); + } + SymTypeExpression transformedReturnType = popTransformedSubSymType(); + List transformedArgumentsTypes = + applyToCollection(symType.getArgumentTypeList()); + pushTransformedSymType(SymTypeExpressionFactory.createFunction( + symbol, + transformedReturnType, + transformedArgumentsTypes, + symType.isElliptic() + )); + } + + @Override + public void visit(SymTypeOfGenerics symType) { + List clonedArguments = + applyToCollection(symType.getArgumentList()); + pushTransformedSymType( + SymTypeExpressionFactory.createGenerics(symType.getTypeInfo(), clonedArguments) + ); + } + + @Override + public void visit(SymTypeOfIntersection symType) { + Set clonedIntersectedTypes = + applyToCollection(symType.getIntersectedTypeSet()); + pushTransformedSymType( + SymTypeExpressionFactory.createIntersection(clonedIntersectedTypes) + ); + } + + @Override + public void visit(SymTypeOfNull symType) { + pushTransformedSymType(SymTypeExpressionFactory.createTypeOfNull()); + } + + @Override + public void visit(SymTypeOfObject symType) { + pushTransformedSymType( + SymTypeExpressionFactory.createTypeObject(symType.getTypeInfo()) + ); + } + + @Override + public void visit(SymTypeOfUnion symType) { + Set clonedUnionizedTypes = + applyToCollection(symType.getUnionizedTypeSet()); + pushTransformedSymType( + SymTypeExpressionFactory.createUnion(clonedUnionizedTypes) + ); + } + + @Override + public void visit(SymTypePrimitive symType) { + pushTransformedSymType( + SymTypeExpressionFactory.createPrimitive(symType.getTypeInfo()) + ); + } + + @Override + public void visit(SymTypeVariable symType) { + SymTypeVariable result; + if (symType.hasTypeVarSymbol()) { + result = SymTypeExpressionFactory.createTypeVariable( + symType.getTypeVarSymbol(), + symType.getLowerBound(), + symType.getUpperBound() + ); + } + else { + result = SymTypeExpressionFactory.createTypeVariable( + null, + symType.getLowerBound(), + symType.getUpperBound() + ); + } + pushTransformedSymType(result); + } + + @Override + public void visit(SymTypeVoid symType) { + pushTransformedSymType(SymTypeExpressionFactory.createTypeVoid()); + } + + @Override + public void visit(SymTypeOfWildcard symType) { + SymTypeOfWildcard clone; + if (symType.hasBound()) { + symType.getBound().accept(this); + clone = SymTypeExpressionFactory.createWildcard( + symType.isUpper(), popTransformedSubSymType() + ); + } + else { + clone = SymTypeExpressionFactory.createWildcard(); + } + pushTransformedSymType(clone); + } + + // Helpers + + /** + * uses this visitor with the provided symType and returns the result. + * it is reset during the process. + */ + public SymTypeExpression calculate(SymTypeExpression symType) { + // save stack to allow for recursive calling + Stack oldStack = this.transformedSymTypes; + reset(); + symType.accept(this); + SymTypeExpression result = getTransformedSymType(); + // restore stack + this.transformedSymTypes = oldStack; + return result; + } + + protected List applyToCollection( + List symTypes) { + List transformedTypes = new ArrayList<>(); + for (SymTypeExpression type : symTypes) { + type.accept(this); + transformedTypes.add(popTransformedSubSymType()); + } + return transformedTypes; + } + + protected Set applyToCollection( + Set symTypes) { + Set transformedTypes = new HashSet<>(); + for (SymTypeExpression type : symTypes) { + type.accept(this); + transformedTypes.add(popTransformedSubSymType()); + } + return transformedTypes; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types3/util/SymTypeLubCalculator.java b/monticore-grammar/src/main/java/de/monticore/types3/util/SymTypeLubCalculator.java new file mode 100644 index 0000000000..f2edc4db7c --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types3/util/SymTypeLubCalculator.java @@ -0,0 +1,237 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.types3.util; + +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.types.check.SymTypeArray; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeOfIntersection; +import de.monticore.types.check.SymTypeOfUnion; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static de.monticore.types.check.SymTypeExpressionFactory.createIntersection; +import static de.monticore.types.check.SymTypeExpressionFactory.createObscureType; +import static de.monticore.types.check.SymTypeExpressionFactory.createPrimitive; +import static de.monticore.types.check.SymTypeExpressionFactory.createTypeArray; + +/** + * calculates the least upper bound of a collection of SymTypes + * delegate of SymTypeRelations + */ +public class SymTypeLubCalculator { + + protected SymTypeRelations symTypeRelations; + + public SymTypeLubCalculator(SymTypeRelations symTypeRelations) { + this.symTypeRelations = symTypeRelations; + } + + protected SymTypeRelations getSymTypeRelations() { + return symTypeRelations; + } + + public Optional leastUpperBound( + Collection types) { + Optional lub; + // the function may be extended to calculate further upper bounds + // e.g. in Java: + // lub(A, B[]) is lub(A, arraySuperType) + + // normalize the types + // we rely on the types being structured + // unions on first level, intersections on second + // and arrays on the third + Set typeSet = types.stream() + .map(symTypeRelations::normalize) + .collect(Collectors.toSet()); + + // unpack unions + Set typesSet_tmp = typeSet; + typeSet = new HashSet<>(); + for (SymTypeExpression type : typesSet_tmp) { + if (type.isUnionType()) { + typeSet.addAll(((SymTypeOfUnion) type).getUnionizedTypeSet()); + } + else { + typeSet.add(type); + } + } + + // now that we removed the top level unions, + // remove top level and intersection level variables, + // without removing the top level intersections yet + // we can do this as we do not plan to support constraint solving + typesSet_tmp = typeSet; + typeSet = new HashSet<>(); + for (SymTypeExpression type : typesSet_tmp) { + if (type.isIntersectionType()) { + SymTypeOfIntersection inter = (SymTypeOfIntersection) type; + // todo after discussing (and implementing) type contexts, + // check if var is bound + inter.getIntersectedTypeSet() + .removeIf(SymTypeExpression::isTypeVariable); + typeSet.add(symTypeRelations.normalize(inter)); + } + // todo after discussing (and implementing) type contexts, + // check if var is bound + else if (type.isTypeVariable()) { + // no-op + } + else { + typeSet.add(type); + } + } + + // lub without any information + if (typeSet.isEmpty()) { + // lub of only variables (or nothing) is unbounded + lub = Optional.empty(); + } + // lub of all the same type + else if (allDeepEquals(typeSet)) { + lub = Optional.of(typeSet.stream().findFirst().get().deepClone()); + } + // at least two different types, try (boxed) primitives first + // lub of boolean + else if (typeSet.stream().allMatch(t -> symTypeRelations.isBoolean(t))) { + // note: at least one is not boxed, so unbox all + lub = Optional.of(createPrimitive(BasicSymbolsMill.BOOLEAN)); + } + // lub of number + // based on Java Spec (20): Numeric Conditional Expressions + else if (typeSet.stream().allMatch(t -> symTypeRelations.isNumericType(t))) { + Stream numbers = typeSet.stream().map(symTypeRelations::unbox); + // lub of byte + if (numbers.allMatch(t -> symTypeRelations.isByte(t))) { + lub = Optional.of(createPrimitive(BasicSymbolsMill.BYTE)); + } + // lub of byte|short + else if (numbers.allMatch(t -> symTypeRelations.isByte(t) + || symTypeRelations.isShort(t))) { + lub = Optional.of(createPrimitive(BasicSymbolsMill.SHORT)); + } + // lub using numeric promotion + else { + lub = Optional.of(symTypeRelations.numericPromotion( + numbers.collect(Collectors.toList()) + )); + } + } + // lub of incompatible set of primitives + else if (typeSet.stream().allMatch(t -> + symTypeRelations.isNumericType(t) || + symTypeRelations.isBoolean(t))) { + lub = Optional.of(createObscureType()); // or empty? + } + // here the function may be extended to calculate further upper bounds + // cases involving subtyping + else { + // lub may be an intersection, e.g. two interfaces, + // start with intersection of current types + Set acceptedLubs = new HashSet<>(); + Set currentPotentialLubs; + Set nextPotentialLubs = new HashSet<>(); + + // unpack intersections + typesSet_tmp = typeSet; + typeSet = new HashSet<>(); + for (SymTypeExpression type : typesSet_tmp) { + if (type.isUnionType()) { + typeSet.addAll(((SymTypeOfUnion) type).getUnionizedTypeSet()); + } + else { + typeSet.add(type); + } + } + + // extract "arrayness" + Set arrayDims = new HashSet<>(); + if (typeSet.stream().allMatch(SymTypeExpression::isArrayType)) { + typesSet_tmp = typeSet; + typeSet = new HashSet<>(); + for (SymTypeExpression type : typesSet_tmp) { + SymTypeArray array = (SymTypeArray) type; + arrayDims.add(array.getDim()); + typeSet.add(array.getArgument()); + } + // can only have lub if arrays have the same dimension + if (arrayDims.size() == 1) { + currentPotentialLubs = new HashSet<>(typeSet); + } + else { + currentPotentialLubs = new HashSet<>(); + } + } + else if (typeSet.stream().noneMatch(SymTypeExpression::isArrayType)) { + currentPotentialLubs = new HashSet<>(typeSet); + } + else { + currentPotentialLubs = new HashSet<>(); + } + + // we have no unions, intersections or arrays on the top level + // here the algorithm may be extended with regards to variables + while (!currentPotentialLubs.isEmpty()) { + for (SymTypeExpression type : typeSet) { + for (Iterator i = currentPotentialLubs.iterator(); + i.hasNext(); ) { + SymTypeExpression potentialLub = i.next(); + // if a type cannot be a subtype of a potential lub, it is not a lub + if (!symTypeRelations.internal_isSubTypeOf(type, potentialLub, true)) { + i.remove(); + // however, the supertypes could still be lubs + Collection superTypes = + getSymTypeRelations().getNominalSuperTypes(potentialLub); + nextPotentialLubs.addAll(superTypes); + } + } + } + acceptedLubs.addAll(currentPotentialLubs); + currentPotentialLubs = nextPotentialLubs; + nextPotentialLubs = new HashSet<>(); + } + + // re-add "arrayness" + if (arrayDims.size() == 1) { + int arrayDim = arrayDims.stream().findFirst().get(); + acceptedLubs = acceptedLubs.stream() + .map(t -> createTypeArray(t, arrayDim)) + .collect(Collectors.toSet()); + } + + // lub is the intersection of the calculated lubs (minus superclasses) + // exception being the empty intersection -> no lub + if (acceptedLubs.size() > 1) { + lub = Optional.of(symTypeRelations.normalize(createIntersection(acceptedLubs))); + } + else if (acceptedLubs.size() == 1) { + lub = acceptedLubs.stream().findFirst(); + } + else { + lub = Optional.of(createObscureType()); + } + } + + return lub; + } + + // Helper + + protected boolean allDeepEquals(Collection types) { + if (types.isEmpty()) { + return true; + } + else { + final SymTypeExpression typeNonUnionTmp = + types.stream().findFirst().get(); + return types.stream().allMatch(typeNonUnionTmp::deepEquals); + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types3/util/SymTypeNormalizeVisitor.java b/monticore-grammar/src/main/java/de/monticore/types3/util/SymTypeNormalizeVisitor.java new file mode 100644 index 0000000000..a028716c64 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types3/util/SymTypeNormalizeVisitor.java @@ -0,0 +1,381 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.types3.util; + +import de.monticore.types.check.SymTypeArray; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types.check.SymTypeOfIntersection; +import de.monticore.types.check.SymTypeOfUnion; +import de.se_rwth.commons.logging.Log; + +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * tries to normalize SymTypeExpressions, + * including, but not limited to, unions and intersections + * e.g., (A|A|B) -> A, if A extends B + * Usage: + * calculate(symType) + *

+ * note, this visitor does not box/unbox, + * boxing/unboxing should be done before this visitor if required + */ +public class SymTypeNormalizeVisitor extends SymTypeDeepCloneVisitor { + + protected SymTypeRelations typeRel; + + public SymTypeNormalizeVisitor(SymTypeRelations typeRel) { + this.typeRel = typeRel; + } + + protected SymTypeRelations getTypeRel() { + if (typeRel == null) { + Log.error("0xFD815 internal error:" + + "SymTypeNormalizeVisitor not set up correctly"); + } + return typeRel; + } + + @Override + public void visit(SymTypeOfUnion union) { + // set of normalized unionized types + Set types = + applyToCollection(union.getUnionizedTypeSet()); + SymTypeExpression normalized = normalizeUnionWithNormalizedTypes( + SymTypeExpressionFactory.createUnion(types) + ); + pushTransformedSymType(normalized); + } + + protected SymTypeExpression normalizeUnionWithNormalizedTypes(SymTypeOfUnion union) { + // set of already normalized unionized types + Set types = new HashSet<>(union.getUnionizedTypeSet()); + // remove all occurrences of obscure + // (A|B|obscure) -> (A|B) + types.removeIf(SymTypeExpression::isObscureType); + // no union of unions + // (A|(B|C)) -> (A|B|C) + Set splittedTypes = splitUnions(types); + // here would be an opportunity to unbox types, + // however, we lose information, so we don't do so + // z.B. (int|Float) -> float would be a Java-esque option + + // no type twice + // (A|A|B) -> (A|B) + // also no subtypes + // (A|B|C) -> (B|C) if A extends B + Set uniqueTypes = new HashSet<>(); + for (SymTypeExpression newType : splittedTypes) { + boolean shouldAdd = true; + // if A extends B, do not add A if B is in union + for (SymTypeExpression addedType : uniqueTypes) { + if (getTypeRel().internal_isSubTypeOfPreNormalized(newType, addedType, false)) { + shouldAdd = false; + break; + } + } + // add to the union + if (shouldAdd) { + // remove all subtypes that have been added already + // if A,B both extend C, adding C to (A|B|D) results in (C|D) + // because A extending C can be represented by replacing A with (A&C) + // thus (A|C) -> ((A&C)|C) -> C + uniqueTypes.removeIf( + addedType -> getTypeRel().internal_isSubTypeOfPreNormalized(addedType, newType, false)); + uniqueTypes.add(newType); + } + } + + SymTypeExpression normalized; + // empty union is obscure + if (uniqueTypes.isEmpty()) { + // empty union is obscure, + if (!union.getUnionizedTypeSet().isEmpty()) { + normalized = SymTypeExpressionFactory.createObscureType(); + } + // except if it has been added deliberately + // this is likely to change in the future + // if we add a way to normalize into empty unions + else { + normalized = SymTypeExpressionFactory.createBottomType(); + } + } + // union of one is no union + // (A) = A + else if (uniqueTypes.size() == 1) { + normalized = uniqueTypes.stream().findFirst().get(); + } + // we still have a union + else { + normalized = SymTypeExpressionFactory.createUnion(uniqueTypes); + } + return normalized; + } + + @Override + public void visit(SymTypeOfIntersection intersection) { + SymTypeExpression normalized; + // set of normalized intersected types + Set types = + applyToCollection(intersection.getIntersectedTypeSet()); + // if any element is obscure, the whole intersection is obscure + // (A&B&obscure) -> obscure + if (types.stream().anyMatch(SymTypeExpression::isObscureType)) { + normalized = SymTypeExpressionFactory.createObscureType(); + } + else { + // we transform an intersection that may have unions + // into a union of intersections + // A&B&(C|D) -> (A&B&C)|(A&B&D) + Set intersectionsWithoutUnions + = intersectionOfUnions2UnionOfIntersections(types); + // normalize each intersection + Set normalizedUnionTypes = new HashSet<>(); + for (SymTypeOfIntersection intersectionWithoutUnion + : intersectionsWithoutUnions) { + normalizedUnionTypes.add( + normalizeIntersectionWithoutUnions(intersectionWithoutUnion) + ); + } + // normalize the new union + normalized = normalizeUnionWithNormalizedTypes( + SymTypeExpressionFactory.createUnion(normalizedUnionTypes) + ); + } + pushTransformedSymType(normalized); + } + + /** + * normalizes an intersection + * the contained expressions are expected to be normalized and not unions + * s. visit(SymTypeOfIntersection) + */ + protected SymTypeExpression normalizeIntersectionWithoutUnions( + SymTypeOfIntersection intersection + ) { + Set types = new HashSet<>(intersection.getIntersectedTypeSet()); + // no intersection of intersections + // (A&(B&C)) -> (A&B&C) + Set splittedTypes = splitIntersections(types); + // arrays all have the same dimension + // (A[]&B[][]) -> Obscure + if (splittedTypes.stream().anyMatch(SymTypeExpression::isArrayType)) { + if (!splittedTypes.stream().allMatch(SymTypeExpression::isArrayType) + || splittedTypes.stream() + .map(t -> ((SymTypeArray) t).getDim()) + .collect(Collectors.toSet()) + .size() > 1 + ) { + splittedTypes = new HashSet<>(); + splittedTypes.add(SymTypeExpressionFactory.createObscureType()); + } + } + + // no type twice + // (A&A&B) -> (A&B) + // also no supertypes + // (A&B&C) -> (A&C) if A extends B + Set uniqueTypes = new HashSet<>(); + for (SymTypeExpression newType : splittedTypes) { + boolean shouldAdd = true; + // if A extends B, do not add B if A is in intersection + for (SymTypeExpression addedType : uniqueTypes) { + if (getTypeRel().internal_isSubTypeOfPreNormalized(addedType, newType, false)) { + shouldAdd = false; + break; + } + } + // add to the intersection + if (shouldAdd) { + // remove all supertypes that have been added already + // if A extends B, adding A to (B|C) results in (A|C) + // because A extending B can be represented by replacing A with (A&B) + // thus (A&B&C) -> ((A&B)&B&C) -> (A&C) + uniqueTypes.removeIf(addedType -> + getTypeRel().internal_isSubTypeOfPreNormalized(newType, addedType, false)); + uniqueTypes.add(newType); + } + } + + SymTypeExpression normalized; + // empty intersection contains all values + if (uniqueTypes.isEmpty()) { + // this is not expected + if (!intersection.getIntersectedTypeSet().isEmpty()) { + Log.error("0xFDA6B internal error: " + + "normalized intersection is empty," + + "this was not expected"); + normalized = SymTypeExpressionFactory.createObscureType(); + } + // except if it has been added deliberately + // this is likely to change in the future if we ever + // add ways to normalize into bottom / top types + else { + normalized = SymTypeExpressionFactory.createTopType(); + } + } + // intersection of one is not an intersection + // (A) = A + else if (uniqueTypes.size() == 1) { + normalized = uniqueTypes.stream().findFirst().get(); + } + // we still have an intersection + else { + normalized = SymTypeExpressionFactory.createIntersection(uniqueTypes); + } + return normalized; + } + + @Override + public void visit(SymTypeArray array) { + array.getArgument().accept(this); + SymTypeExpression normalizedArgument = popTransformedSubSymType(); + SymTypeExpression normalized; + // A -> A (length of 0) + if (array.getDim() == 0) { + normalized = normalizedArgument; + } + // Obscure[] -> Obscure + else if (normalizedArgument.isObscureType()) { + normalized = SymTypeExpressionFactory.createObscureType(); + } + // (A[][])[] -> A[][][] + else if (normalizedArgument.isArrayType()) { + SymTypeArray subArray = (SymTypeArray) normalizedArgument; + normalized = SymTypeExpressionFactory.createTypeArray( + subArray.getArgument(), array.getDim() + subArray.getDim() + ); + } + // move arrays below set operations + // (A|(B&C))[] -> (A[]|(B[]&C[])) + else if (normalizedArgument.isUnionType()) { + Set unionizedTypes = + ((SymTypeOfUnion) normalizedArgument).getUnionizedTypeSet(); + unionizedTypes = unionizedTypes.stream() + .map(t -> SymTypeExpressionFactory.createTypeArray(t, array.getDim())) + .collect(Collectors.toSet()); + unionizedTypes = applyToCollection(unionizedTypes); + normalized = SymTypeExpressionFactory.createUnion(unionizedTypes); + } + else if (normalizedArgument.isIntersectionType()) { + Set intersectedTypes = + ((SymTypeOfIntersection) normalizedArgument).getIntersectedTypeSet(); + intersectedTypes = intersectedTypes.stream() + .map(t -> SymTypeExpressionFactory.createTypeArray(t, array.getDim())) + .collect(Collectors.toSet()); + intersectedTypes = applyToCollection(intersectedTypes); + normalized = SymTypeExpressionFactory.createIntersection(intersectedTypes); + } + // A[] -> A[] + else { + normalized = SymTypeExpressionFactory.createTypeArray( + normalizedArgument, array.getDim() + ); + } + + pushTransformedSymType(normalized); + } + + // Helpers + + /** + * splits up unions + * e.g., {(A|(B|C)),D} -> {A,B,C,D} + * used for normalization + */ + protected Set splitUnions(Set types) { + Set result = new HashSet<>(); + for (SymTypeExpression type : types) { + if (type.isUnionType()) { + SymTypeOfUnion union = (SymTypeOfUnion) type; + result.addAll(splitUnions(union.getUnionizedTypeSet())); + } + else { + result.add(type); + } + } + return result; + } + + /** + * splits up intersections + * e.g., {(A&(B&C)),D} -> {A,B,C,D} + * used for normalization + */ + protected Set splitIntersections( + Set types) { + Set result = new HashSet<>(); + for (SymTypeExpression type : types) { + if (type.isIntersectionType()) { + SymTypeOfIntersection intersection = (SymTypeOfIntersection) type; + result.addAll(splitIntersections(intersection.getIntersectedTypeSet())); + } + else { + result.add(type); + } + } + return result; + } + + /** + * takes an intersection, which may contain unions + * and creates a union which contains intersections + * A&B&(C|D) -> (A&B&C)|(A&B&D) + * Note that this only calculates the given intersection, + * not the intersection contained within the given intersection. + * An additional characteristic to mention is that + * NO empty intersections / unions are created + * if there have been none to begin with + */ + protected Set intersectionOfUnions2UnionOfIntersections( + Set intersectedTypes) { + Set intersections = new HashSet<>(); + if (!intersectedTypes.isEmpty()) { + //temporarily make every non-union type in the intersection a union type + // (A|B)&C -> (A|B)&(C) + Set unions = new HashSet<>(); + for (SymTypeExpression type : intersectedTypes) { + if (type.isUnionType()) { + unions.add((SymTypeOfUnion) type); + } + else { + unions.add(SymTypeExpressionFactory.createUnion(type)); + } + } + // create a union of intersections from the intersection of unions + // in rare cases, this can be exponential + // (A|B)&(C|D) -> (A&C)|(A&D)|(B&C)|(B&D) + // handle the first union differently to set up the set of intersections + SymTypeOfUnion firstUnion = unions.stream().findAny().get(); + unions.remove(firstUnion); + for (SymTypeExpression type : firstUnion.getUnionizedTypeSet()) { + intersections.add(SymTypeExpressionFactory.createIntersection(type)); + } + //now combine the other unions with the already existing intersections + for (SymTypeOfUnion union : unions) { + Set currentIntersectionSets = intersections; + intersections = new HashSet<>(); + for (SymTypeExpression unionizedType : union.getUnionizedTypeSet()) { + for (SymTypeOfIntersection oldIntersection : currentIntersectionSets) { + SymTypeOfIntersection newIntersection = + (SymTypeOfIntersection) oldIntersection.deepClone(); + newIntersection.addIntersectedType(unionizedType); + intersections.add(newIntersection); + } + } + } + // we now have a union of intersections + } + // given an empty intersection, we assume that this is the top type + else { + // note, this has to be changed if we make changes to the top type + intersections.add((SymTypeOfIntersection) + SymTypeExpressionFactory.createTopType() + ); + } + return intersections; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types3/util/SymTypeRelations.java b/monticore-grammar/src/main/java/de/monticore/types3/util/SymTypeRelations.java new file mode 100644 index 0000000000..ca0250b0f9 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types3/util/SymTypeRelations.java @@ -0,0 +1,164 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types3.util; + +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeOfFunction; +import de.monticore.types3.ISymTypeRelations; + +import java.util.Collection; +import java.util.List; +import java.util.Optional; + +/** + * default implementation + */ +public class SymTypeRelations implements ISymTypeRelations { + + protected SymTypeCompatibilityCalculator compatibilityDelegate; + + protected NominalSuperTypeCalculator superTypeCalculator; + + protected SymTypeBoxingVisitor boxingVisitor; + + protected SymTypeUnboxingVisitor unboxingVisitor; + + protected SymTypeNormalizeVisitor normalizeVisitor; + + protected SymTypeLubCalculator lubDelegate; + + protected BuiltInTypeRelations builtInRelationsDelegate; + + protected FunctionRelations functionRelationsDelegate; + + public SymTypeRelations() { + // default values + this.compatibilityDelegate = new SymTypeCompatibilityCalculator(this); + this.superTypeCalculator = new NominalSuperTypeCalculator(this); + this.boxingVisitor = new SymTypeBoxingVisitor(); + this.unboxingVisitor = new SymTypeUnboxingVisitor(); + this.normalizeVisitor = new SymTypeNormalizeVisitor(this); + this.lubDelegate = new SymTypeLubCalculator(this); + this.builtInRelationsDelegate = new BuiltInTypeRelations(); + this.functionRelationsDelegate = new FunctionRelations(this); + } + + public boolean isCompatible(SymTypeExpression assignee, SymTypeExpression assigner) { + return compatibilityDelegate.isCompatible(assignee, assigner); + } + + public boolean isSubTypeOf(SymTypeExpression subType, SymTypeExpression superType) { + return compatibilityDelegate.isSubTypeOf(subType, superType); + } + + public List getNominalSuperTypes(SymTypeExpression thisType) { + return superTypeCalculator.getNominalSuperTypes(thisType); + } + + public Optional leastUpperBound( + Collection types) { + return lubDelegate.leastUpperBound(types); + } + + public SymTypeExpression box(SymTypeExpression unboxed) { + return boxingVisitor.calculate(unboxed); + } + + public SymTypeExpression unbox(SymTypeExpression boxed) { + return unboxingVisitor.calculate(boxed); + } + + public SymTypeExpression numericPromotion(List types) { + return builtInRelationsDelegate.numericPromotion(types); + } + + public boolean isNumericType(SymTypeExpression type) { + return builtInRelationsDelegate.isNumericType(type); + } + + public boolean isIntegralType(SymTypeExpression type) { + return builtInRelationsDelegate.isIntegralType(type); + } + + public boolean isBoolean(SymTypeExpression type) { + return builtInRelationsDelegate.isBoolean(type); + } + + public boolean isInt(SymTypeExpression type) { + return builtInRelationsDelegate.isInt(type); + } + + public boolean isDouble(SymTypeExpression type) { + return builtInRelationsDelegate.isDouble(type); + } + + public boolean isFloat(SymTypeExpression type) { + return builtInRelationsDelegate.isFloat(type); + } + + public boolean isLong(SymTypeExpression type) { + return builtInRelationsDelegate.isLong(type); + } + + public boolean isChar(SymTypeExpression type) { + return builtInRelationsDelegate.isChar(type); + } + + public boolean isShort(SymTypeExpression type) { + return builtInRelationsDelegate.isShort(type); + } + + public boolean isByte(SymTypeExpression type) { + return builtInRelationsDelegate.isByte(type); + } + + public boolean isString(SymTypeExpression type) { + return builtInRelationsDelegate.isString(type); + } + + /** + * @deprecated use {@link FunctionRelations} + */ + @Deprecated + public boolean canBeCalledWith( + SymTypeOfFunction func, + List args + ) { + return functionRelationsDelegate.canBeCalledWith(func, args); + } + + /** + * @deprecated use {@link FunctionRelations} + */ + @Deprecated + public Optional getMostSpecificFunction( + Collection funcs + ) { + return functionRelationsDelegate.getMostSpecificFunction(funcs); + } + + public SymTypeExpression normalize(SymTypeExpression type) { + return normalizeVisitor.calculate(type); + } + + // Helper, internals + + public boolean internal_isSubTypeOf( + SymTypeExpression subType, + SymTypeExpression superType, + boolean subTypeIsSoft + ) { + return compatibilityDelegate + .internal_isSubTypeOf(subType, superType, subTypeIsSoft); + } + + public boolean internal_isSubTypeOfPreNormalized( + SymTypeExpression subType, + SymTypeExpression superType, + boolean subTypeIsSoft + ) { + return compatibilityDelegate + .internal_isSubTypeOfPreNormalized(subType, superType, subTypeIsSoft); + } + +} + diff --git a/monticore-grammar/src/main/java/de/monticore/types3/util/SymTypeUnboxingVisitor.java b/monticore-grammar/src/main/java/de/monticore/types3/util/SymTypeUnboxingVisitor.java new file mode 100644 index 0000000000..4878f007af --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types3/util/SymTypeUnboxingVisitor.java @@ -0,0 +1,143 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.types3.util; + +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types.check.SymTypeOfGenerics; +import de.monticore.types.check.SymTypeOfObject; +import de.se_rwth.commons.logging.Log; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +/** + * Unboxes SymTypeExpressions, + * including, but not limited to, Java primitive unboxing + * e.g., java.lang.Integer -> int + * e.g., java.util.List -> List + * Usage: + * calculate(symType) + */ +public class SymTypeUnboxingVisitor extends SymTypeDeepCloneVisitor { + + /** + * Map for unboxing to primitive types (e.g. "java.lang.Integer" -> "int") + * Results are fully qualified. + */ + protected static final Map primitiveUnboxMap; + + /** + * Map for unboxing to object types (e.g. "java.lang.String" -> "String") + * Results are fully qualified. + */ + protected static final Map objectUnboxMap; + + /** + * Map for unboxing to generic types (e.g. "java.util.List" -> "List") + * Results are fully qualified. + */ + protected static final Map genericUnboxMap; + + public Map getPrimitiveUnboxMap() { + return primitiveUnboxMap; + } + + public Map getObjectUnboxMap() { + return objectUnboxMap; + } + + public Map getGenericUnboxMap() { + return genericUnboxMap; + } + + /** + * initializing the maps + */ + static { + Map primitiveUnboxMap_temp = new HashMap<>(); + primitiveUnboxMap_temp.put("java.lang.Boolean", "boolean"); + primitiveUnboxMap_temp.put("java.lang.Byte", "byte"); + primitiveUnboxMap_temp.put("java.lang.Character", "char"); + primitiveUnboxMap_temp.put("java.lang.Double", "double"); + primitiveUnboxMap_temp.put("java.lang.Float", "float"); + primitiveUnboxMap_temp.put("java.lang.Integer", "int"); + primitiveUnboxMap_temp.put("java.lang.Long", "long"); + primitiveUnboxMap_temp.put("java.lang.Short", "short"); + primitiveUnboxMap = Collections.unmodifiableMap(primitiveUnboxMap_temp); + + Map objectUnboxMap_temp = new HashMap<>(); + objectUnboxMap_temp.put("java.lang.String", "String"); + objectUnboxMap = Collections.unmodifiableMap(objectUnboxMap_temp); + + Map genericUnboxMap_temp = new HashMap<>(); + genericUnboxMap_temp.put("java.util.Optional", "Optional"); + genericUnboxMap_temp.put("java.util.Set", "Set"); + genericUnboxMap_temp.put("java.util.List", "List"); + genericUnboxMap_temp.put("java.util.Map", "Map"); + genericUnboxMap = Collections.unmodifiableMap(genericUnboxMap_temp); + } + + @Override + public void visit(SymTypeOfGenerics symType) { + final String name = symType.getTypeConstructorFullName(); + Optional typeSymbolOpt = + resolveUnboxedSymType(name, getGenericUnboxMap()); + TypeSymbol typeSymbol = typeSymbolOpt.orElse(symType.getTypeInfo()); + pushTransformedSymType(SymTypeExpressionFactory.createGenerics( + typeSymbol, + applyToCollection(symType.getArgumentList()) + )); + } + + @Override + public void visit(SymTypeOfObject symType) { + final String name = symType.printFullName(); + // try primitives first + Optional typeSymbolOpt = + resolveUnboxedSymType(name, getPrimitiveUnboxMap()); + if (typeSymbolOpt.isPresent()) { + pushTransformedSymType( + SymTypeExpressionFactory.createPrimitive(typeSymbolOpt.get()) + ); + } + else { + // if it was no primitive, it might be an object + typeSymbolOpt = resolveUnboxedSymType(name, getObjectUnboxMap()); + pushTransformedSymType(SymTypeExpressionFactory.createTypeObject( + typeSymbolOpt.orElse(symType.getTypeInfo()) + )); + } + } + + // Helpers + + /** + * iff there is a unboxed variant of the given symtype, + * this tries to resolve it + */ + protected Optional resolveUnboxedSymType(String name, Map unboxMap) { + // getting the correct name to use of a SymTypeExpression is inconsistent, + // as such we pass it as a parameter along with the map + if (unboxMap.containsKey(name)) { + final String unboxedName = unboxMap.get(name); + Optional unboxedTypeSymbolOpt = + BasicSymbolsMill.globalScope().resolveType(unboxedName); + if (!unboxedTypeSymbolOpt.isPresent()) { + Log.info("symbol for unboxed type " + + unboxedName + + " is not found for type " + + name + + " and is thus not used", + "Typing"); + } + return unboxedTypeSymbolOpt; + } + else { + return Optional.empty(); + } + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types3/util/SymTypeVariableReplaceVisitor.java b/monticore-grammar/src/main/java/de/monticore/types3/util/SymTypeVariableReplaceVisitor.java new file mode 100644 index 0000000000..2b30276912 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types3/util/SymTypeVariableReplaceVisitor.java @@ -0,0 +1,61 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.types3.util; + +import de.monticore.symbols.basicsymbols._symboltable.TypeVarSymbol; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeVariable; + +import java.util.HashMap; +import java.util.Map; + +/** + * replaces TypeVariables using a given map + * e.g., T, {T->int,U->float} -> int + * e.g., List, {T->int} -> List + * Usage: + * calculate(symType, replaceMap) + */ +public class SymTypeVariableReplaceVisitor extends SymTypeDeepCloneVisitor { + + /** + * Map for replacing Type Variables + */ + protected Map replaceMap = new HashMap<>(); + + public Map getReplaceMap() { + return replaceMap; + } + + public void setReplaceMap(Map replaceMap) { + this.replaceMap = replaceMap; + } + + @Override + public void visit(SymTypeVariable typVar) { + // as containsKey uses equals, we need to go other it ourselves + boolean inMap = false; + for (TypeVarSymbol varSym : getReplaceMap().keySet()) { + if (typVar.hasTypeVarSymbol() && + varSym.deepEquals(typVar.getTypeVarSymbol()) & !inMap) { + pushTransformedSymType(getReplaceMap().get(varSym)); + inMap = true; + } + } + if (!inMap) { + pushTransformedSymType(typVar); + } + } + + // Helpers + + public SymTypeExpression calculate( + SymTypeExpression symType, + Map replaceMap + ) { + Map oldMap = this.replaceMap; + setReplaceMap(replaceMap); + SymTypeExpression result = calculate(symType); + setReplaceMap(oldMap); + return result; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/types3/util/TypeContextCalculator.java b/monticore-grammar/src/main/java/de/monticore/types3/util/TypeContextCalculator.java new file mode 100644 index 0000000000..9741ef69eb --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types3/util/TypeContextCalculator.java @@ -0,0 +1,118 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.types3.util; + +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.symbols.basicsymbols._util.BasicSymbolsTypeDispatcher; +import de.monticore.symboltable.IScope; +import de.monticore.symboltable.ISymbol; +import de.monticore.symboltable.modifiers.AccessModifier; +import de.monticore.symboltable.modifiers.BasicAccessModifier; +import de.monticore.symboltable.modifiers.CompoundAccessModifier; +import de.monticore.symboltable.modifiers.StaticAccessModifier; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * provides context information for an expression. + * first and foremost, whether the expression is + * within the context of a type (e.g., in a method) + */ +public class TypeContextCalculator { + + /** + * given an expression's enclosing scope, returns the enclosing type symbol + */ + public Optional getEnclosingType(IScope enclosingScope) { + Optional enclosingType = Optional.empty(); + for (IScope scope = enclosingScope; + scope != null && enclosingType.isEmpty(); + scope = scope.getEnclosingScope()) { + if (scope.isPresentSpanningSymbol() && + getTypeDispatcher().isType(scope.getSpanningSymbol())) { + enclosingType = Optional.of( + getTypeDispatcher().asType(scope.getSpanningSymbol()) + ); + } + } + return enclosingType; + } + + /** + * given an expression's scope that accesses the type, + * return the appropriate AccessModifier + * e.g., an expression in a method can access private members, + * if the method is static, only static members can be accessed + * supported: visibility (private and protected), staticness, + * missing: package_local + * this function does NOT handle supertypes + * ({@link WithinTypeBasicSymbolsResolver} handles supertypes) + * for access via typeID, static access can be forced on (e.g., C.v) + */ + public AccessModifier getAccessModifier( + TypeSymbol type, + IScope enclosingScope, + boolean forceStatic + ) { + boolean accessIsStatic = forceStatic; + boolean accessIsStaticIfInType = false; + boolean exprIsInType = false; + for (IScope scope = enclosingScope; + scope != null && !exprIsInType; + scope = scope.getEnclosingScope()) { + if (scope.isPresentSpanningSymbol()) { + ISymbol spanningSymbol = scope.getSpanningSymbol(); + // static function? + if (getTypeDispatcher().isFunction(spanningSymbol) && + getTypeDispatcher().asFunction(spanningSymbol) + .getAccessModifier().getDimensionToModifierMap() + .getOrDefault(StaticAccessModifier.DIMENSION, null) + == StaticAccessModifier.STATIC) { + accessIsStaticIfInType = true; + } + else if (getTypeDispatcher().isType(spanningSymbol)) { + TypeSymbol typeSymbol = getTypeDispatcher().asType(spanningSymbol); + if (typeSymbol == type) { + exprIsInType = true; + } + // static inner type + if (!exprIsInType && + type.getAccessModifier().getDimensionToModifierMap() + .getOrDefault(StaticAccessModifier.DIMENSION, null) + == StaticAccessModifier.STATIC) { + accessIsStaticIfInType = true; + } + } + } + } + if (exprIsInType) { + accessIsStatic |= accessIsStaticIfInType; + } + List modifiers = new ArrayList<>(); + if (exprIsInType) { + modifiers.add(BasicAccessModifier.PRIVATE); + } + else { + modifiers.add(BasicAccessModifier.PUBLIC); + } + if (accessIsStatic) { + modifiers.add(StaticAccessModifier.STATIC); + } + return new CompoundAccessModifier(modifiers); + } + + public AccessModifier getAccessModifier( + TypeSymbol type, + IScope enclosingScope) { + return getAccessModifier(type, enclosingScope, false); + } + + // Helper + + protected BasicSymbolsTypeDispatcher getTypeDispatcher() { + return BasicSymbolsMill.typeDispatcher(); + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/types3/util/WithinTypeBasicSymbolsResolver.java b/monticore-grammar/src/main/java/de/monticore/types3/util/WithinTypeBasicSymbolsResolver.java new file mode 100644 index 0000000000..d97541d313 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/types3/util/WithinTypeBasicSymbolsResolver.java @@ -0,0 +1,422 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.types3.util; + +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.FunctionSymbol; +import de.monticore.symbols.basicsymbols._symboltable.IBasicSymbolsScope; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.symbols.basicsymbols._symboltable.TypeVarSymbol; +import de.monticore.symbols.basicsymbols._symboltable.VariableSymbol; +import de.monticore.symbols.basicsymbols._util.BasicSymbolsTypeDispatcher; +import de.monticore.symboltable.IScope; +import de.monticore.symboltable.ISymbol; +import de.monticore.symboltable.modifiers.AccessModifier; +import de.monticore.symboltable.modifiers.BasicAccessModifier; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types.check.SymTypeOfFunction; +import de.monticore.types.check.SymTypeOfGenerics; +import de.monticore.types.check.SymTypeOfUnion; +import de.se_rwth.commons.logging.Log; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +/** + * resolves within a type, + * but in a more type correct way then our resolve algorithm, + * as some additions cannot be (simply) added to it. + * E.g., given generics, the correct type parameters will be set. + * The results will be in form of SymTypeExpressions. + */ +public class WithinTypeBasicSymbolsResolver { + + protected static final String LOG_NAME = "WithinTypeResolving"; + + protected SymTypeVariableReplaceVisitor replaceVisitor; + + protected SymTypeRelations symTypeRelations; + + public WithinTypeBasicSymbolsResolver(SymTypeRelations symTypeRelations) { + this.symTypeRelations = symTypeRelations; + // default values + replaceVisitor = new SymTypeVariableReplaceVisitor(); + } + + public WithinTypeBasicSymbolsResolver() { + // default values + replaceVisitor = new SymTypeVariableReplaceVisitor(); + symTypeRelations = new SymTypeRelations(); + } + + protected SymTypeRelations getSymTypeRelations() { + return symTypeRelations; + } + + /** + * resolves within a type including supertypes + */ + public Optional resolveVariable( + SymTypeExpression thisType, + String name, + AccessModifier accessModifier, + Predicate predicate) { + Optional resolvedSymType; + Optional spannedScopeOpt = getSpannedScope(thisType); + if (spannedScopeOpt.isEmpty()) { + resolvedSymType = Optional.empty(); + } + // search in this scope + else { + Optional resolvedSymbol = resolveVariableLocally( + spannedScopeOpt.get(), + name, + accessModifier, + predicate + ); + resolvedSymType = resolvedSymbol.map( + s -> replaceVariablesIfNecessary(thisType, s.getType()) + ); + } + // search in super types + if (resolvedSymType.isEmpty()) { + // private -> protected while searching in super types + AccessModifier superModifier = private2Protected(accessModifier); + List superTypes = getSuperTypes(thisType); + resolvedSymType = Optional.empty(); + for (SymTypeExpression superType : superTypes) { + Optional resolvedInSuper = + resolveVariable(superType, name, superModifier, predicate); + if (resolvedSymType.isPresent() && resolvedInSuper.isPresent()) { + Log.error("0xFD222 found variables with name \"" + + name + "\" in multiple super types of \"" + + thisType.printFullName() + "\""); + } + else if (resolvedSymType.isEmpty() && resolvedInSuper.isPresent()) { + resolvedSymType = resolvedInSuper; + } + //filter based on local variables + } + } + return resolvedSymType; + } + + /** + * resolves within a type including supertypes + */ + public List resolveFunctions( + SymTypeExpression thisType, + String name, + AccessModifier accessModifier, + Predicate predicate) { + List resolvedSymTypes = new ArrayList<>(); + Optional spannedScopeOpt = getSpannedScope(thisType); + if (spannedScopeOpt.isEmpty()) { + resolvedSymTypes = new ArrayList<>(); + } + // search in this scope + else { + //todo outer types (and vs. supertypes) not really? + List resolvedSymbols = resolveFunctionLocally( + thisType.getTypeInfo().getSpannedScope(), + name, + accessModifier, + predicate + ); + List resolvedTypesUnmodified = resolvedSymbols.stream() + .map(FunctionSymbol::getFunctionType) + .collect(Collectors.toList()); + resolvedSymTypes = resolvedTypesUnmodified.stream() + .map(f -> (SymTypeOfFunction) replaceVariablesIfNecessary(thisType, f)) + .collect(Collectors.toList()); + } + // search in super types + // private -> protected while searching in super types + AccessModifier superModifier = private2Protected(accessModifier); + List superTypes = getSuperTypes(thisType); + List superFuncs = new ArrayList<>(); + for (SymTypeExpression superType : superTypes) { + List resolvedInSuper = + resolveFunctions(superType, name, superModifier, predicate); + // filter based on being overridden / hidden (static) + // Java Spec 20 8.4.8.1 overriding methods need to have the SAME signature, + // e.g., Integer getX() overrides Number getX() + // e.g., void setX(Number x) does not override void setX(Integer x) + // we assume that CoCos corresponding to the compile time errors of + // Java Spec 20 8.4.8 are used + for (Iterator fItr = resolvedInSuper.iterator(); + fItr.hasNext(); ) { + SymTypeOfFunction superFunc = fItr.next(); + if (resolvedSymTypes.stream() + .anyMatch(f -> f.deepEqualsSignature(superFunc))) { + fItr.remove(); + } + } + superFuncs.addAll(resolvedInSuper); + } + // filter based on being inherited twice (diamond pattern) + // we cannot (solely) rely on the symbols in case of generics + List filteredSuperFuncs = new ArrayList<>(); + for (SymTypeOfFunction func1 : superFuncs) { + boolean duplicate = false; + for (SymTypeOfFunction func2 : filteredSuperFuncs) { + if (func1.getSymbol() == func2.getSymbol() + && func1.deepEqualsSignature(func2)) { + duplicate = true; + } + } + if (!duplicate) { + filteredSuperFuncs.add(func1); + } + } + resolvedSymTypes.addAll(filteredSuperFuncs); + + return resolvedSymTypes; + } + + /** + * resolves within a type including supertypes + */ + public Optional resolveType( + SymTypeExpression thisType, + String name, + AccessModifier accessModifier, + Predicate predicate) { + Optional resolvedSymType; + Optional spannedScopeOpt = getSpannedScope(thisType); + if (spannedScopeOpt.isEmpty()) { + resolvedSymType = Optional.empty(); + } + // search in this scope + else { + Optional resolvedSymbol = resolveTypeLocally( + spannedScopeOpt.get(), + name, + accessModifier, + predicate + ); + if (resolvedSymbol.isPresent()) { + SymTypeExpression resolvedTypeUnmodified = + SymTypeExpressionFactory.createFromSymbol(resolvedSymbol.get()); + resolvedSymType = Optional.of( + replaceVariablesIfNecessary(thisType, resolvedTypeUnmodified) + ); + } + else { + resolvedSymType = Optional.empty(); + } + } + // search in super types + if (resolvedSymType.isEmpty()) { + // private -> protected while searching in super types + AccessModifier superModifier = private2Protected(accessModifier); + List superTypes = getSuperTypes(thisType); + resolvedSymType = Optional.empty(); + for (SymTypeExpression superType : superTypes) { + Optional resolvedInSuper = + resolveType(superType, name, superModifier, predicate); + if (resolvedSymType.isPresent() && resolvedInSuper.isPresent()) { + Log.error("0xFD224 found type with name \"" + + name + "\" in multiple super types of \"" + + thisType.printFullName() + "\""); + } + resolvedSymType = resolvedInSuper; + } + } + + return resolvedSymType; + } + + /** + * checks if the symtypeExpression is of a (sym)type to be resolved in, + * e.g., this includes objects but excludes primitives. + * This method is intended to be used + * to increase the specificity of error messages, + * it is NOT necessary to check a type with it before calling resolve[...](). + * s. a. {@link NominalSuperTypeCalculator} + */ + public boolean canResolveIn(SymTypeExpression thisType) { + return thisType.isObjectType() || + thisType.isGenericType() || + thisType.isTypeVariable() || + thisType.isUnionType() || + thisType.isIntersectionType(); + // array.size not supported yet + } + + // Helper + + /** + * resolves locally, EXCLUDING supertypes + */ + protected Optional resolveVariableLocally( + IBasicSymbolsScope scope, + String name, + AccessModifier accessModifier, + Predicate predicate) { + // may include symbols of supertypes, thus the predicate + Optional resolved = scope.resolveVariable( + name, + accessModifier, + predicate.and(getIsLocalSymbolPredicate(scope)) + ); + return resolved; + } + + /** + * resolves locally, EXCLUDING supertypes + */ + protected List resolveFunctionLocally( + IBasicSymbolsScope scope, + String name, + AccessModifier accessModifier, + Predicate predicate) { + // may include symbols of supertypes, thus the predicate + List resolved = scope.resolveFunctionLocallyMany( + false, + name, + accessModifier, + predicate.and(getIsLocalSymbolPredicate(scope)) + ); + return resolved; + } + + /** + * resolves locally, EXCLUDING supertypes + */ + protected Optional resolveTypeLocally( + IBasicSymbolsScope scope, + String name, + AccessModifier accessModifier, + Predicate predicate) { + // may include symbols of supertypes, thus the predicate + List resolved = scope.resolveTypeLocallyMany( + false, + name, + accessModifier, + predicate.and(getIsLocalSymbolPredicate(scope)) + ); + // prefer variables to concrete types in the same scope, + // e.g., class A{class B{} B b = new B();} is not valid Java + if (resolved.stream().anyMatch(getTypeDispatcher()::isTypeVar)) { + resolved = resolved.stream() + .filter(Predicate.not(getTypeDispatcher()::isTypeVar)) + .collect(Collectors.toList()); + } + if (resolved.size() > 1) { + Log.error("0xFD221 resolved multiple types \"" + + name + "\" (locally in the same scope)"); + } + return resolved.stream().findAny(); + } + + /** + * Even more legacy code workarounds: + * filter out anything that is not in the exact scope + * due to questionable resolve strategy overrides in Scope classes... + */ + protected Predicate getIsLocalSymbolPredicate(IScope localScope) { + return s -> { + if (s.getEnclosingScope() != localScope) { + Log.trace("filtered symbol '" + + s.getFullName() + "' as it was resolved " + + "in a different scope, even though " + + "\"resolve[...]Locally[...]\" was used", + LOG_NAME + ); + return false; + } + return true; + }; + } + + protected BasicSymbolsTypeDispatcher getTypeDispatcher() { + return BasicSymbolsMill.typeDispatcher(); + } + + protected SymTypeExpression replaceVariablesIfNecessary( + SymTypeExpression dependencyType, + SymTypeExpression dependentType) { + if (dependencyType.isGenericType()) { + Map replaceMap = + ((SymTypeOfGenerics) dependencyType).getTypeVariableReplaceMap(); + return replaceVariables(dependentType, replaceMap); + } + else { + return dependentType; + } + } + + protected SymTypeExpression replaceVariables( + SymTypeExpression type, + Map replaceMap) { + return replaceVisitor.calculate(type, replaceMap); + } + + protected Optional getSpannedScope(SymTypeExpression type) { + // note that this function has to be kept in sync with + // SymTypeRelations::getNominalSuperTypes, + // e.g., the union's lub is considered to be + // an alternative (, less specified) representation of the union's type, + // while the type's in an intersection are the intersection's supertypes. + Optional spannedScope; + // object + if (type.isObjectType() || type.isGenericType()) { + spannedScope = Optional.of(type.getTypeInfo().getSpannedScope()); + } + // type variable + // considered unknown scope with super types + else if (type.isTypeVariable()) { + spannedScope = Optional.empty(); + } + // intersection + // considered empty scope with super types + else if (type.isIntersectionType()) { + spannedScope = Optional.empty(); + } + // union + // only its lub is known to be present + else if (type.isUnionType()) { + Collection unionizedTypes = + ((SymTypeOfUnion) type).getUnionizedTypeSet(); + Optional lubOpt = + getSymTypeRelations().leastUpperBound(unionizedTypes); + spannedScope = lubOpt.flatMap(lub -> getSpannedScope(lub)); + } + // extension point + else { + Log.info("tried to get the spanned scope of " + + type.printFullName() + + " which is currently not supported", + LOG_NAME + ); + spannedScope = Optional.empty(); + } + return spannedScope; + } + + protected List getSuperTypes(SymTypeExpression thisType) { + return getSymTypeRelations().getNominalSuperTypes(thisType); + } + + /** + * replaces any private access with protected access + * this is done to resolve in supertypes + * if there is no private access, this is id() + */ + protected AccessModifier private2Protected(AccessModifier accessModifier) { + AccessModifier newModifier = accessModifier.shallowCopy(); + Map map = newModifier.getDimensionToModifierMap(); + if (map.containsKey(BasicAccessModifier.DIMENSION) + && map.get(BasicAccessModifier.DIMENSION) == BasicAccessModifier.PRIVATE) { + map.put(BasicAccessModifier.DIMENSION, BasicAccessModifier.PROTECTED); + } + return newModifier; + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/umlmodifier/_ast/ASTModifierBuilder.java b/monticore-grammar/src/main/java/de/monticore/umlmodifier/_ast/ASTModifierBuilder.java new file mode 100644 index 0000000000..64128e6e19 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/umlmodifier/_ast/ASTModifierBuilder.java @@ -0,0 +1,66 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.umlmodifier._ast; + +import de.monticore.umlstereotype.UMLStereotypeMill; +import de.monticore.umlstereotype._ast.ASTStereoValue; + +import java.util.Arrays; +import java.util.Optional; + +public class ASTModifierBuilder extends ASTModifierBuilderTOP { + public ASTModifierBuilder addStereoValues(ASTStereoValue... value) { + if (!this.stereotype.isPresent()) { + this.stereotype = Optional.of(UMLStereotypeMill.stereotypeBuilder().build()); + } + this.stereotype.get().addAllValues(Arrays.asList(value)); + return this.realBuilder; + } + + public ASTModifierBuilder PUBLIC() { + setPublic(true); + return this.realBuilder; + } + + public ASTModifierBuilder PRIVATE() { + setPrivate(true); + return this.realBuilder; + + } + + public ASTModifierBuilder PROTECTED() { + setProtected(true); + return this.realBuilder; + } + + public ASTModifierBuilder FINAL() { + setFinal(true); + return this.realBuilder; + } + + public ASTModifierBuilder ABSTRACT() { + setAbstract(true); + return this.realBuilder; + } + + public ASTModifierBuilder LOCAL() { + setLocal(true); + return this.realBuilder; + } + + public ASTModifierBuilder DERIVED() { + setDerived(true); + return this.realBuilder; + + } + + public ASTModifierBuilder READONLY() { + setReadonly(true); + return this.realBuilder; + } + + public ASTModifierBuilder STATIC() { + setStatic(true); + return this.realBuilder; + } + +} diff --git a/monticore-grammar/src/main/java/de/monticore/umlmodifier/_prettyprint/UMLModifierPrettyPrinter.java b/monticore-grammar/src/main/java/de/monticore/umlmodifier/_prettyprint/UMLModifierPrettyPrinter.java new file mode 100644 index 0000000000..8ffe941330 --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/umlmodifier/_prettyprint/UMLModifierPrettyPrinter.java @@ -0,0 +1,75 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.umlmodifier._prettyprint; + +import de.monticore.prettyprint.IndentPrinter; + +public class UMLModifierPrettyPrinter extends UMLModifierPrettyPrinterTOP { + + protected boolean useShortForms = false; + + public UMLModifierPrettyPrinter(IndentPrinter printer, boolean printComments) { + super(printer, printComments); + } + + public boolean isUseShortForms() { + return useShortForms; + } + + public void setUseShortForms(boolean useShortForms) { + this.useShortForms = useShortForms; + } + + @Override + public void handle(de.monticore.umlmodifier._ast.ASTModifier node) { + // hand-written due to the possible short forms + + if (this.isPrintComments()) { + de.monticore.prettyprint.CommentPrettyPrinter.printPreComments(node, getPrinter()); + } + + if (node.isPresentStereotype()) { + node.getStereotype().accept(getTraverser()); + } + + if (node.isPublic()) { + getPrinter().print(this.isUseShortForms() ? "+ " : "public "); + } + + if (node.isPrivate()) { + getPrinter().print(this.isUseShortForms() ? "- " : "private "); + } + + if (node.isProtected()) { + getPrinter().print(this.isUseShortForms() ? "# " : "protected "); + } + + if (node.isFinal()) { + getPrinter().print("final "); + } + + if (node.isAbstract()) { + getPrinter().print("abstract "); + } + + if (node.isLocal()) { + getPrinter().print("local "); + } + + if (node.isDerived()) { + getPrinter().print(this.isUseShortForms() ? "/ " : "derived "); + } + + if (node.isReadonly()) { + getPrinter().print(this.isUseShortForms() ? "? " : "readonly "); + } + + if (node.isStatic()) { + getPrinter().print("static "); + } + + if (this.isPrintComments()) { + de.monticore.prettyprint.CommentPrettyPrinter.printPostComments(node, getPrinter()); + } + + } +} diff --git a/monticore-grammar/src/main/java/de/monticore/umlstereotype/_ast/ASTStereoValue.java b/monticore-grammar/src/main/java/de/monticore/umlstereotype/_ast/ASTStereoValue.java new file mode 100644 index 0000000000..56cc1b471c --- /dev/null +++ b/monticore-grammar/src/main/java/de/monticore/umlstereotype/_ast/ASTStereoValue.java @@ -0,0 +1,23 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.umlstereotype._ast; + +public class ASTStereoValue extends ASTStereoValueTOP { + + @Override + public boolean equalAttributes(Object o) { + ASTStereoValue comp; + if ((o instanceof ASTStereoValue)) { + comp = (ASTStereoValue) o; + } else { + return false; + } + + // Don't check derived attributes + // comparing name + if ( (this.name == null && comp.name != null) + || (this.name != null && !this.name.equals(comp.name)) ) { + return false; + } + return true; + } +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/TestCardinality.mc4 b/monticore-grammar/src/test/grammars/de/monticore/TestCardinality.mc4 new file mode 100644 index 0000000000..3ac18526a6 --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/TestCardinality.mc4 @@ -0,0 +1,7 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore; + +grammar TestCardinality extends de.monticore.Cardinality { + +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/TestCompleteness.mc4 b/monticore-grammar/src/test/grammars/de/monticore/TestCompleteness.mc4 new file mode 100644 index 0000000000..bdca840dd2 --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/TestCompleteness.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore; + +grammar TestCompleteness extends de.monticore.Completeness, de.monticore.MCBasics { + + CompletenessList = "[" (Completeness | ",")* "]" ; + +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/TestJavaLight.mc4 b/monticore-grammar/src/test/grammars/de/monticore/TestJavaLight.mc4 new file mode 100644 index 0000000000..af31d14426 --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/TestJavaLight.mc4 @@ -0,0 +1,15 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore; + +grammar TestJavaLight extends de.monticore.JavaLight, de.monticore.literals.MCCommonLiterals, de.monticore.types.MCCollectionTypes, de.monticore.types.MCArrayTypes { + start JavaMethod; + + ExtTypeParameters = MCTypeArgument; + + ExtType = MCType; + + ExtReturnType = MCReturnType; + + ExtTypeArgument = MCTypeArgument; +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/TestMCCommon.mc4 b/monticore-grammar/src/test/grammars/de/monticore/TestMCCommon.mc4 new file mode 100644 index 0000000000..9603a41dba --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/TestMCCommon.mc4 @@ -0,0 +1,7 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore; + +grammar TestMCCommon extends de.monticore.MCCommon { + A = Stereotype Modifier Completeness; +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/TestMCHexNumbers.mc4 b/monticore-grammar/src/test/grammars/de/monticore/TestMCHexNumbers.mc4 new file mode 100644 index 0000000000..82dfd88949 --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/TestMCHexNumbers.mc4 @@ -0,0 +1,7 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore; + +grammar TestMCHexNumbers extends MCHexNumbers { + +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/TestMCLiteralsV2.mc4 b/monticore-grammar/src/test/grammars/de/monticore/TestMCLiteralsV2.mc4 new file mode 100644 index 0000000000..b226d43a37 --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/TestMCLiteralsV2.mc4 @@ -0,0 +1,26 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore; + +grammar TestMCLiteralsV2 extends + MCNumbers, + // de.monticore.MCLiteralsV2, + StringLiterals + { + + DecimalList = "[" (Decimal | ",")* "]" ; + IntegerList = "[" (Integer | ",")* "]" ; + CharList = "[" (CharLiteral | ",")* "]" ; + StringList = "[" (StringLiteral | ",")* "]" ; + + // test reuse of a fragment + token XHexDigit = 'X' HexDigit* 'X' ; + BTest = XHexDigit "," XHexDigit; + + // List with only optional separator (but also space separates) + // Thi scan be used to check end-detection for tokens + AnyTokenList = ":" (AnyToken ","?)* ":" ; + AnyToken = DecimalToken | CharToken | StringToken + | "[" | "]" | ".." ; + +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/TestMCLiteralsV3.mc4 b/monticore-grammar/src/test/grammars/de/monticore/TestMCLiteralsV3.mc4 new file mode 100644 index 0000000000..fc59f45542 --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/TestMCLiteralsV3.mc4 @@ -0,0 +1,20 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore; + +grammar TestMCLiteralsV3 extends + MCNumbers, + MCHexNumbers, + StringLiterals +{ + + NumberList = "[" (Number | ",")* "]" ; + DecimalList = "[" (Decimal | ",")* "]" ; + IntegerList = "[" (Integer | ",")* "]" ; + + // List with only optional separator (but also space separates) + // Thi scan be used to check end-detection for tokens + AnyTokenList = "[" (AnyToken ","?)* "]" ; + AnyToken = DecimalToken | CharToken | StringToken | HexadecimalToken ; + +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/TestMCNumbers.mc4 b/monticore-grammar/src/test/grammars/de/monticore/TestMCNumbers.mc4 new file mode 100644 index 0000000000..6315d0fc35 --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/TestMCNumbers.mc4 @@ -0,0 +1,7 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore; + +grammar TestMCNumbers extends MCNumbers { + +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/TestStringLiterals.mc4 b/monticore-grammar/src/test/grammars/de/monticore/TestStringLiterals.mc4 new file mode 100644 index 0000000000..6c70b0fb8b --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/TestStringLiterals.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore; + +grammar TestStringLiterals extends StringLiterals { + + CharLiteralList = "[" (CharLiteral | ",")* "]" ; + StringLiteralList = "[" (StringLiteral | ",")* "]" ; + +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/TestUMLModifier.mc4 b/monticore-grammar/src/test/grammars/de/monticore/TestUMLModifier.mc4 new file mode 100644 index 0000000000..99fd1a2fe6 --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/TestUMLModifier.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore; + +grammar TestUMLModifier extends de.monticore.UMLModifier { + + A = "test" Modifier; + +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/TestUMLStereotype.mc4 b/monticore-grammar/src/test/grammars/de/monticore/TestUMLStereotype.mc4 new file mode 100644 index 0000000000..b0025fb995 --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/TestUMLStereotype.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore; + +grammar TestUMLStereotype extends de.monticore.UMLStereotype { + + StereotypeList = "[" (Stereotype | ",")* "]" ; + StereoValueList = "[" (StereoValue | ",")* "]" ; + +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/aggregation/Blah.mc4 b/monticore-grammar/src/test/grammars/de/monticore/aggregation/Blah.mc4 new file mode 100644 index 0000000000..30408947ad --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/aggregation/Blah.mc4 @@ -0,0 +1,13 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.aggregation; + +grammar Blah extends de.monticore.MCBasics { + + BlahModel = "blahmodel" "{" Blub* "}"; + + scope symbol Blub = "blubScope" Name "{" (Dummy|Blub)* "}"; + + symbol Dummy = "symbol" Name; + +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/aggregation/Foo.mc4 b/monticore-grammar/src/test/grammars/de/monticore/aggregation/Foo.mc4 new file mode 100644 index 0000000000..f23205228f --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/aggregation/Foo.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.aggregation; + +grammar Foo extends de.monticore.expressions.CommonExpressions, de.monticore.MCBasics, de.monticore.literals.MCCommonLiterals { + + symbol scope Bar = "bar" "{" Expression "}" Name; + + ExtLiteral = Literal; +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/expressions/AbstractTypeCheckTest.mc4 b/monticore-grammar/src/test/grammars/de/monticore/expressions/AbstractTypeCheckTest.mc4 new file mode 100644 index 0000000000..ef87e847f8 --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/expressions/AbstractTypeCheckTest.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions; + +grammar AbstractTypeCheckTest extends + de.monticore.expressions.CommonExpressions, + de.monticore.literals.MCCommonLiterals, + de.monticore.symbols.BasicSymbols { + + start Expression; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/grammars/de/monticore/expressions/CombineExpressionsWithLiterals.mc4 b/monticore-grammar/src/test/grammars/de/monticore/expressions/CombineExpressionsWithLiterals.mc4 new file mode 100644 index 0000000000..27113f1812 --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/expressions/CombineExpressionsWithLiterals.mc4 @@ -0,0 +1,30 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.expressions; + +grammar CombineExpressionsWithLiterals extends + de.monticore.expressions.AssignmentExpressions, + de.monticore.expressions.CommonExpressions, + de.monticore.expressions.BitExpressions, + de.monticore.literals.MCCommonLiterals, + de.monticore.expressions.JavaClassExpressions, + de.monticore.expressions.LambdaExpressions, + de.monticore.types.MCFullGenericTypes, + de.monticore.types.MCArrayTypes, + de.monticore.types.MCFunctionTypes, + de.monticore.symbols.OOSymbols { + + Foo="bar" Expression; + + @Override + ExtType = MCType; + + @Override + ExtReturnType = MCReturnType; + + @Override + ExtTypeArgument = MCTypeArgument; + + TModifier implements MCModifier = "private" | "static"; + +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/expressions/TestAssignmentExpressions.mc4 b/monticore-grammar/src/test/grammars/de/monticore/expressions/TestAssignmentExpressions.mc4 new file mode 100644 index 0000000000..534a2b80e9 --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/expressions/TestAssignmentExpressions.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions; + + +grammar TestAssignmentExpressions + extends de.monticore.expressions.AssignmentExpressions, de.monticore.literals.MCCommonLiterals { + + start Expression; +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/expressions/TestBitExpressions.mc4 b/monticore-grammar/src/test/grammars/de/monticore/expressions/TestBitExpressions.mc4 new file mode 100644 index 0000000000..dcb871962b --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/expressions/TestBitExpressions.mc4 @@ -0,0 +1,6 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions; + +grammar TestBitExpressions extends de.monticore.expressions.BitExpressions, de.monticore.literals.MCCommonLiterals { + start Expression; +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/expressions/TestCommonExpressions.mc4 b/monticore-grammar/src/test/grammars/de/monticore/expressions/TestCommonExpressions.mc4 new file mode 100644 index 0000000000..7e3c6a9306 --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/expressions/TestCommonExpressions.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.expressions; + +grammar TestCommonExpressions extends de.monticore.expressions.CommonExpressions, de.monticore.literals.MCCommonLiterals, + de.monticore.MCBasics { + start Expression; + + ExtLiteral = Literal; + +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/expressions/TestJavaClassExpressions.mc4 b/monticore-grammar/src/test/grammars/de/monticore/expressions/TestJavaClassExpressions.mc4 new file mode 100644 index 0000000000..fc9d5ed5c7 --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/expressions/TestJavaClassExpressions.mc4 @@ -0,0 +1,18 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions; + +grammar TestJavaClassExpressions extends de.monticore.expressions.JavaClassExpressions, + de.monticore.literals.MCCommonLiterals, + de.monticore.types.MCCollectionTypes{ + + start Expression; + + ExtType = MCType; + + ExtReturnType = MCReturnType; + + ExtTypeArgument = MCTypeArgument; + + TModifier implements MCModifier = "private" | "static"; + +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/expressions/TestLambdaExpressions.mc4 b/monticore-grammar/src/test/grammars/de/monticore/expressions/TestLambdaExpressions.mc4 new file mode 100644 index 0000000000..a7f1265a4d --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/expressions/TestLambdaExpressions.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions; + +grammar TestLambdaExpressions + extends de.monticore.expressions.LambdaExpressions, + de.monticore.literals.MCCommonLiterals, + de.monticore.types.MCBasicTypes { + + start Expression; + +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/expressions/TestStreamExpressions.mc4 b/monticore-grammar/src/test/grammars/de/monticore/expressions/TestStreamExpressions.mc4 new file mode 100644 index 0000000000..bb6ef36160 --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/expressions/TestStreamExpressions.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions; + +grammar TestStreamExpressions extends de.monticore.expressions.StreamExpressions, + de.monticore.literals.MCCommonLiterals { + + start Expression; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/grammars/de/monticore/literals/TestMCCommonLiterals.mc4 b/monticore-grammar/src/test/grammars/de/monticore/literals/TestMCCommonLiterals.mc4 new file mode 100644 index 0000000000..ff4096bc15 --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/literals/TestMCCommonLiterals.mc4 @@ -0,0 +1,7 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.literals; + +grammar TestMCCommonLiterals extends de.monticore.literals.MCCommonLiterals { + +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/literals/TestMCJavaLiterals.mc4 b/monticore-grammar/src/test/grammars/de/monticore/literals/TestMCJavaLiterals.mc4 new file mode 100644 index 0000000000..e252b0bbe2 --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/literals/TestMCJavaLiterals.mc4 @@ -0,0 +1,7 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.literals; + +grammar TestMCJavaLiterals extends de.monticore.literals.MCJavaLiterals { + +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/statements/TestMCArrayStatements.mc4 b/monticore-grammar/src/test/grammars/de/monticore/statements/TestMCArrayStatements.mc4 new file mode 100644 index 0000000000..263ffb80d1 --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/statements/TestMCArrayStatements.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.statements; + +grammar TestMCArrayStatements extends de.monticore.statements.MCArrayStatements, + de.monticore.expressions.CombineExpressionsWithLiterals { + + TModifier implements MCModifier = "private"; + +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/statements/TestMCAssertStatements.mc4 b/monticore-grammar/src/test/grammars/de/monticore/statements/TestMCAssertStatements.mc4 new file mode 100644 index 0000000000..e8b5438763 --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/statements/TestMCAssertStatements.mc4 @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.statements; + +grammar TestMCAssertStatements extends de.monticore.statements.MCAssertStatements, + de.monticore.expressions.CombineExpressionsWithLiterals { + +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/statements/TestMCCommonStatements.mc4 b/monticore-grammar/src/test/grammars/de/monticore/statements/TestMCCommonStatements.mc4 new file mode 100644 index 0000000000..ce28b35aad --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/statements/TestMCCommonStatements.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.statements; + +grammar TestMCCommonStatements extends de.monticore.statements.MCCommonStatements, + de.monticore.expressions.CombineExpressionsWithLiterals, + de.monticore.types.MCArrayTypes { +start MCStatement; +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/statements/TestMCExceptionStatements.mc4 b/monticore-grammar/src/test/grammars/de/monticore/statements/TestMCExceptionStatements.mc4 new file mode 100644 index 0000000000..762cb11841 --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/statements/TestMCExceptionStatements.mc4 @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.statements; + +grammar TestMCExceptionStatements extends de.monticore.statements.MCExceptionStatements, + de.monticore.expressions.CombineExpressionsWithLiterals { + +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/statements/TestMCFullJavaStatements.mc4 b/monticore-grammar/src/test/grammars/de/monticore/statements/TestMCFullJavaStatements.mc4 new file mode 100644 index 0000000000..5572c20a36 --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/statements/TestMCFullJavaStatements.mc4 @@ -0,0 +1,7 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.statements; + +grammar TestMCFullJavaStatements extends de.monticore.statements.MCFullJavaStatements, de.monticore.literals.MCCommonLiterals { + +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/statements/TestMCLowLevelStatements.mc4 b/monticore-grammar/src/test/grammars/de/monticore/statements/TestMCLowLevelStatements.mc4 new file mode 100644 index 0000000000..cce8a96145 --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/statements/TestMCLowLevelStatements.mc4 @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.statements; + +grammar TestMCLowLevelStatements extends de.monticore.statements.MCLowLevelStatements , de.monticore.literals.MCCommonLiterals{ + + +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/statements/TestMCReturnStatements.mc4 b/monticore-grammar/src/test/grammars/de/monticore/statements/TestMCReturnStatements.mc4 new file mode 100644 index 0000000000..80c3ed2b81 --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/statements/TestMCReturnStatements.mc4 @@ -0,0 +1,7 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.statements; + +grammar TestMCReturnStatements extends de.monticore.statements.MCReturnStatements, de.monticore.literals.MCCommonLiterals { + +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/statements/TestMCSynchronizedStatements.mc4 b/monticore-grammar/src/test/grammars/de/monticore/statements/TestMCSynchronizedStatements.mc4 new file mode 100644 index 0000000000..8cfd22ecb6 --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/statements/TestMCSynchronizedStatements.mc4 @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.statements; + +grammar TestMCSynchronizedStatements extends de.monticore.statements.MCSynchronizedStatements, + de.monticore.expressions.CombineExpressionsWithLiterals { + +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/statements/TestMCVarDeclarationStatements.mc4 b/monticore-grammar/src/test/grammars/de/monticore/statements/TestMCVarDeclarationStatements.mc4 new file mode 100644 index 0000000000..7c4864253b --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/statements/TestMCVarDeclarationStatements.mc4 @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.statements; + +grammar TestMCVarDeclarationStatements extends de.monticore.statements.MCVarDeclarationStatements, + de.monticore.expressions.CombineExpressionsWithLiterals { + + RootVarDeclaration = LocalVariableDeclarationStatement; + + TModifier implements MCModifier = "private" | "static"; + +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/testsymtabmill/TestSymTabMill.mc4 b/monticore-grammar/src/test/grammars/de/monticore/testsymtabmill/TestSymTabMill.mc4 new file mode 100644 index 0000000000..f82c5404c7 --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/testsymtabmill/TestSymTabMill.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.testsymtabmill; + +grammar TestSymTabMill extends de.monticore.MCBasics { + + symbol scope A = "B" Name; + +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/typepersistence/Variable.mc4 b/monticore-grammar/src/test/grammars/de/monticore/typepersistence/Variable.mc4 new file mode 100644 index 0000000000..6320ca9a67 --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/typepersistence/Variable.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.typepersistence; + +grammar Variable extends de.monticore.types.MCBasicTypes { + + + symbol Var = "var" MCType Name; + +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/types/GenericTypesTest.mc4 b/monticore-grammar/src/test/grammars/de/monticore/types/GenericTypesTest.mc4 new file mode 100644 index 0000000000..26ef66497e --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/types/GenericTypesTest.mc4 @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.types; + +grammar GenericTypesTest extends de.monticore.types.MCFullGenericTypes, de.monticore.types.MCArrayTypes { + +start MCType; +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/types/MCArrayTypesTest.mc4 b/monticore-grammar/src/test/grammars/de/monticore/types/MCArrayTypesTest.mc4 new file mode 100644 index 0000000000..e1c5d59ae9 --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/types/MCArrayTypesTest.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.types; + +grammar MCArrayTypesTest extends de.monticore.types.MCArrayTypes { + +A = "dummy"; + +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/types/MCBasicTypesTest.mc4 b/monticore-grammar/src/test/grammars/de/monticore/types/MCBasicTypesTest.mc4 new file mode 100644 index 0000000000..d9976438e9 --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/types/MCBasicTypesTest.mc4 @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.types; + +grammar MCBasicTypesTest extends de.monticore.types.MCBasicTypes { + + +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/types/MCCollectionTypesTest.mc4 b/monticore-grammar/src/test/grammars/de/monticore/types/MCCollectionTypesTest.mc4 new file mode 100644 index 0000000000..c30961501d --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/types/MCCollectionTypesTest.mc4 @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.types; + +grammar MCCollectionTypesTest extends de.monticore.types.MCCollectionTypes { + + +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/types/MCCollectionTypesWithoutPrimitivesTest.mc4 b/monticore-grammar/src/test/grammars/de/monticore/types/MCCollectionTypesWithoutPrimitivesTest.mc4 new file mode 100644 index 0000000000..76567f5941 --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/types/MCCollectionTypesWithoutPrimitivesTest.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.types; + +grammar MCCollectionTypesWithoutPrimitivesTest extends de.monticore.types.MCCollectionTypes { + + MCPrimitiveTypeArgument implements MCTypeArgument <190> = + "THIS_MUST_NOT_OCCCUR_SOMEWHERE"; +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/types/MCFullGenericTypesTest.mc4 b/monticore-grammar/src/test/grammars/de/monticore/types/MCFullGenericTypesTest.mc4 new file mode 100644 index 0000000000..d982788cae --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/types/MCFullGenericTypesTest.mc4 @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.types; + +grammar MCFullGenericTypesTest extends de.monticore.types.MCFullGenericTypes { + + +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/types/MCFunctionTypesTest.mc4 b/monticore-grammar/src/test/grammars/de/monticore/types/MCFunctionTypesTest.mc4 new file mode 100644 index 0000000000..d4b644d83c --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/types/MCFunctionTypesTest.mc4 @@ -0,0 +1,7 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.types; + +grammar MCFunctionTypesTest extends de.monticore.types.MCFunctionTypes { + +} diff --git a/monticore-grammar/src/test/grammars/de/monticore/types/MCSimpleGenericTypesTest.mc4 b/monticore-grammar/src/test/grammars/de/monticore/types/MCSimpleGenericTypesTest.mc4 new file mode 100644 index 0000000000..ef395e90b9 --- /dev/null +++ b/monticore-grammar/src/test/grammars/de/monticore/types/MCSimpleGenericTypesTest.mc4 @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.types; + +grammar MCSimpleGenericTypesTest extends de.monticore.types.MCSimpleGenericTypes { + + +} diff --git a/monticore-grammar/src/test/java/de/monticore/MCCommonUnitTest.java b/monticore-grammar/src/test/java/de/monticore/MCCommonUnitTest.java new file mode 100644 index 0000000000..4d91df4782 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/MCCommonUnitTest.java @@ -0,0 +1,293 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore; + +import de.monticore.cardinality._ast.ASTCardinality; +import de.monticore.completeness._ast.ASTCompleteness; +import de.monticore.literals.mccommonliterals._ast.ASTNatLiteral; +import de.monticore.literals.mccommonliterals._ast.ASTStringLiteral; +import de.monticore.testmccommon.TestMCCommonMill; +import de.monticore.testmccommon._parser.TestMCCommonParser; +import de.monticore.umlmodifier._ast.ASTModifier; +import de.monticore.umlstereotype._ast.ASTStereoValue; +import de.monticore.umlstereotype._ast.ASTStereotype; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.List; +import java.util.Optional; + +import static org.junit.Assert.*; + + +public class MCCommonUnitTest { + + // setup the language infrastructure + TestMCCommonParser parser = new TestMCCommonParser() ; + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + LogStub.getFindings().clear(); + TestMCCommonMill.reset(); + TestMCCommonMill.init(); + } + + // -------------------------------------------------------------------- + // Numbers: Nat + // -------------------------------------------------------------------- + + // -------------------------------------------------------------------- + @Test + public void testNat1() throws IOException { + ASTNatLiteral ast = parser.parse_StringNatLiteral( " 9" ).get(); + assertEquals("9", ast.getSource()); + assertEquals(9, ast.getValue()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testNat4() throws IOException { + ASTNatLiteral ast = parser.parse_StringNatLiteral( " 42 " ).get(); + assertEquals("42", ast.getSource()); + assertEquals(42, ast.getValue()); + + assertTrue(Log.getFindings().isEmpty()); + } + + // -------------------------------------------------------------------- + // UMLStereotype + // -------------------------------------------------------------------- + + // -------------------------------------------------------------------- + @Test + public void testModifier() throws IOException { + ASTModifier ast = parser.parse_StringModifier( "# final" ).get(); + assertEquals(true, ast.isProtected()); + assertEquals(true, ast.isFinal()); + assertEquals(false, ast.isLocal()); + + assertTrue(Log.getFindings().isEmpty()); + } + + + // -------------------------------------------------------------------- + @Test + public void testModifierStereo() throws IOException { + ASTModifier ast = parser.parse_StringModifier( "<>#+?" ).get(); + assertEquals(true, ast.isProtected()); + assertEquals(true, ast.isPublic()); + assertEquals(true, ast.isReadonly()); + assertEquals(false, ast.isFinal()); + assertEquals(true, ast.isPresentStereotype()); + ASTStereotype sty = ast.getStereotype(); + assertEquals("x1", sty.getValue("bla")); + + assertTrue(Log.getFindings().isEmpty()); + } + + + + // -------------------------------------------------------------------- + // UMLStereotype + // -------------------------------------------------------------------- + + // -------------------------------------------------------------------- + @Test + public void testStereoValue() throws IOException { + ASTStereoValue ast = parser.parse_StringStereoValue( "bla=\"17\"" ).get(); + assertEquals("bla", ast.getName()); + assertEquals(true, ast.isPresentText()); + assertEquals("17", ast.getText().getValue()); + assertEquals("17", ast.getValue()); + + assertTrue(Log.getFindings().isEmpty()); + } + + + // -------------------------------------------------------------------- + @Test + public void testStereoValue2() throws IOException { + ASTStereoValue ast = parser.parse_StringStereoValue( "cc" ).get(); + assertEquals("cc", ast.getName()); + assertEquals(false, ast.isPresentText()); + assertEquals("", ast.getValue()); + + assertTrue(Log.getFindings().isEmpty()); + } + + + // -------------------------------------------------------------------- + @Test + public void testStereotype() throws IOException { + ASTStereotype ast = parser.parse_StringStereotype( "<< a1 >>" ).get(); + List svl = ast.getValuesList(); + assertEquals(1, svl.size()); + assertEquals(true, ast.contains("a1")); + assertEquals(false, ast.contains("bla")); + assertEquals(true, ast.contains("a1","")); + assertEquals(false, ast.contains("a1","wert1")); + + assertTrue(Log.getFindings().isEmpty()); + } + + + // -------------------------------------------------------------------- + @Test + public void testStereotype2() throws IOException { + ASTStereotype ast = parser.parse_StringStereotype( + "<< bla, a1=\"wert1\" >>" ).get(); + List svl = ast.getValuesList(); + assertEquals(2, svl.size()); + assertEquals(true, ast.contains("a1")); + assertEquals(false, ast.contains("a1","")); + assertEquals(true, ast.contains("a1","wert1")); + + assertTrue(Log.getFindings().isEmpty()); + } + + + // -------------------------------------------------------------------- + @Test + public void testGetValue() throws IOException { + ASTStereotype ast = parser.parse_StringStereotype( + "<< bla, a1=\"wert1\" >>" ).get(); + assertEquals("wert1", ast.getValue("a1")); + try { + assertEquals("", ast.getValue("foo")); + fail("Expected an Exception to be thrown"); + } catch (java.util.NoSuchElementException ex) { } + assertEquals("", ast.getValue("bla")); + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void testEnding() throws IOException { + Optional oast = parser.parse_StringStereotype( + "<< bla, a1=\"wert1\" > >" ); + assertEquals(false, oast.isPresent()); + + } + + + // -------------------------------------------------------------------- + // Completeness + // -------------------------------------------------------------------- + + // -------------------------------------------------------------------- + @Test + public void testBasics() throws IOException { + ASTCompleteness ast = parser.parse_StringCompleteness( "(c)" ).get(); + assertEquals(true, ast.isComplete()); + assertEquals(false, ast.isIncomplete()); + + assertTrue(Log.getFindings().isEmpty()); + } + + + // -------------------------------------------------------------------- + @Test + public void testBasics2() throws IOException { + ASTCompleteness ast = parser.parse_StringCompleteness( "(...)" ).get(); + assertEquals(false, ast.isComplete()); + assertEquals(true, ast.isIncomplete()); + assertEquals(false, ast.isRightComplete()); + assertEquals(false, ast.isLeftComplete()); + + assertTrue(Log.getFindings().isEmpty()); + } + + + // -------------------------------------------------------------------- + @Test + public void testBasics3() throws IOException { + ASTCompleteness ast = parser.parse_StringCompleteness( "(...,c)" ).get(); + assertEquals(false, ast.isComplete()); + assertEquals(false, ast.isIncomplete()); + assertEquals(true, ast.isRightComplete()); + assertEquals(false, ast.isLeftComplete()); + + assertTrue(Log.getFindings().isEmpty()); + } + + + // -------------------------------------------------------------------- + @Test + public void testIllegalComplete() throws IOException { + Optional ast = + parser.parse_StringCompleteness( "(...,d)" ); + assertEquals(false, ast.isPresent()); + } + + // -------------------------------------------------------------------- + // Cardinality + // -------------------------------------------------------------------- + + // -------------------------------------------------------------------- + @Test + public void testMany() throws IOException { + ASTCardinality ast = parser.parse_StringCardinality("[*]").get(); + assertEquals(true, ast.isMany()); + assertEquals(0, ast.getLowerBound()); + assertEquals(0, ast.getUpperBound()); + + assertTrue(Log.getFindings().isEmpty()); + } + + + // -------------------------------------------------------------------- + @Test + public void testLowAndStar() throws IOException { + ASTCardinality ast = parser.parse_StringCardinality("[7..*]").get(); + assertEquals(false, ast.isMany()); + assertEquals(true, ast.isNoUpperLimit()); + assertEquals(7, ast.getLowerBound()); + assertEquals(0, ast.getUpperBound()); + + assertTrue(Log.getFindings().isEmpty()); + } + + + // -------------------------------------------------------------------- + @Test + public void testLowAndUp() throws IOException { + ASTCardinality ast = parser.parse_StringCardinality("[17..235]").get(); + assertEquals(false, ast.isMany()); + assertEquals(17, ast.getLowerBound()); + assertEquals(235, ast.getUpperBound()); + + assertTrue(Log.getFindings().isEmpty()); + } + + + // -------------------------------------------------------------------- + @Test + public void testSpace() throws IOException { + ASTCardinality ast = parser.parse_StringCardinality(" [ 34 .. 15 ] ").get(); + assertEquals(false, ast.isMany()); + assertEquals(34, ast.getLowerBound()); + assertEquals(15, ast.getUpperBound()); + + assertTrue(Log.getFindings().isEmpty()); + } + + + // -------------------------------------------------------------------- + // Nachweis dass Cardinality Hex und negatives als Cardinality nicht + // akzeptiert + @Test + public void testHex() throws IOException { + Optional oast = parser.parse_StringCardinality( + "[0x34..0x15]"); + assertEquals(false, oast.isPresent()); + } + + +} diff --git a/monticore-grammar/src/test/java/de/monticore/SymbolImportTest.java b/monticore-grammar/src/test/java/de/monticore/SymbolImportTest.java new file mode 100644 index 0000000000..e8c0f38abd --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/SymbolImportTest.java @@ -0,0 +1,92 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore; + +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbolSurrogate; +import de.monticore.grammar.grammar_withconcepts.Grammar_WithConceptsMill; +import de.monticore.grammar.grammar_withconcepts._symboltable.IGrammar_WithConceptsGlobalScope; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.nio.file.Paths; +import java.util.Optional; +import java.util.stream.Collectors; + +public class SymbolImportTest { + + @Before + public void init() { + Log.init(); + LogStub.enableFailQuick(false); + + Grammar_WithConceptsMill.reset(); + Grammar_WithConceptsMill.init(); + + IGrammar_WithConceptsGlobalScope globalScope = Grammar_WithConceptsMill.globalScope(); + globalScope.clear(); + + globalScope.getSymbolPath().addEntry(Paths.get("../monticore-grammar/src/test/resources")); + BasicSymbolsMill.initializePrimitives(); + } + + @Test + public void testTestFullyQualified() throws IOException { + // Test extends de.monticore.grammar.SamePackage, de.monticore.grammar.pack.DifferentPackage + test("../monticore-grammar/src/test/resources/de/monticore/grammar/TestFullyQualifiedGrammar.mc4"); + } + + @Test + public void testTestFullyQualifiedGrammarSamePackage() throws IOException { + // Test extends SamePackage, de.monticore.grammar.pack.DifferentPackage + test("../monticore-grammar/src/test/resources/de/monticore/grammar/TestFullyQualifiedGrammarSamePackage.mc4"); + } + + @Test + public void testQualifiedImport() throws IOException { + // Test + // import de.monticore.grammar.SamePackage + // import de.monticore.grammar.pack.DifferentPackage; + // extends SamePackage, DifferentPackage + test("../monticore-grammar/src/test/resources/de/monticore/grammar/TestQualifiedImportGrammar.mc4"); + } + + + @Test + public void testTestStarImportGrammar() throws IOException { + // Test + // import de.monticore.grammar.* + // import de.monticore.grammar.pack.*; + // extends SamePackage, DifferentPackage + test("../monticore-grammar/src/test/resources/de/monticore/grammar/TestStarImportGrammar.mc4"); + } + + protected void test(String filename) throws IOException { + Optional grammarOpt = Grammar_WithConceptsMill.parser().parse(filename); + Assert.assertTrue(grammarOpt.isPresent()); + Grammar_WithConceptsMill.scopesGenitorDelegator().createFromAST(grammarOpt.get()); + MCGrammarSymbol symbol = grammarOpt.get().getSymbol(); + + for (MCGrammarSymbolSurrogate surrogate : symbol.getSuperGrammars()) { + Assert.assertTrue("Unable to lazy load delegate " + surrogate.getName() + " of " + surrogate.getEnclosingScope(), surrogate.checkLazyLoadDelegate()); + } + + String allSuperGrammars = symbol.getSuperGrammars().stream().map(MCGrammarSymbol::getFullName).collect(Collectors.joining(", ")); + String allSuperGrammarsLazy = symbol.getSuperGrammars().stream().map(MCGrammarSymbolSurrogate::lazyLoadDelegate).map(MCGrammarSymbol::getFullName).collect(Collectors.joining(", ")); + + // check if the surrogate is returning the correct symbol + Assert.assertTrue("SamePackage import failed: " + allSuperGrammars, symbol.getSuperGrammars().stream().anyMatch(x -> x.lazyLoadDelegate().getFullName().equals("de.monticore.grammar.SamePackage"))); + Assert.assertTrue("DifferentPackage import failed: " + allSuperGrammars, symbol.getSuperGrammars().stream().anyMatch(x -> x.lazyLoadDelegate().getFullName().equals("de.monticore.grammar.pack.DifferentPackage"))); + + // check if the surrogate is returning the correct fullname + Assert.assertTrue("SamePackage lazy import failed: " + allSuperGrammarsLazy, symbol.getSuperGrammars().stream().anyMatch(x -> x.getFullName().equals("de.monticore.grammar.SamePackage"))); + Assert.assertTrue("DifferentPackage lazy import failed: " + allSuperGrammarsLazy, symbol.getSuperGrammars().stream().anyMatch(x -> x.getFullName().equals("de.monticore.grammar.pack.DifferentPackage"))); + + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/aggregation/AggregationTest.java b/monticore-grammar/src/test/java/de/monticore/aggregation/AggregationTest.java new file mode 100644 index 0000000000..e70b6c354d --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/aggregation/AggregationTest.java @@ -0,0 +1,118 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.aggregation; + +import de.monticore.aggregation.blah.BlahMill; +import de.monticore.aggregation.blah._ast.ASTBlahModel; +import de.monticore.aggregation.blah._parser.BlahParser; +import de.monticore.aggregation.blah._symboltable.BlahScopesGenitor; +import de.monticore.aggregation.blah._symboltable.DummySymbol; +import de.monticore.aggregation.blah._symboltable.IBlahArtifactScope; +import de.monticore.aggregation.blah._visitor.BlahTraverser; +import de.monticore.aggregation.foo.FooMill; +import de.monticore.aggregation.foo._ast.ASTBar; +import de.monticore.aggregation.foo._parser.FooParser; +import de.monticore.aggregation.foo._symboltable.BarSymbol; +import de.monticore.aggregation.foo._symboltable.FooGlobalScope; +import de.monticore.aggregation.foo._symboltable.FooScopesGenitorDelegator; +import de.monticore.aggregation.foo._symboltable.IFooArtifactScope; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static junit.framework.TestCase.assertTrue; + +public class AggregationTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + FooMill.reset(); + FooMill.init(); + BlahMill.reset(); + BlahMill.init(); + } + + @Test + public void test() throws IOException { + + // infrastruktur aufbauen, modelle zum resolven einlesen, SymTab aufbauen, adapter schreiben, globalscope foo und blah verbinden + // TransitiveAdapterResolvingFilter implementieren und im globscope registrieren, + // + /* *************************************************************************************************************** + ****************************************************************************************************************** + Blah/Blub Infrastruktur + ****************************************************************************************************************** + */ + + //Create global scope for our language combination + FooGlobalScope globalScope = (FooGlobalScope) FooMill.globalScope(); + + //Parse blah model + BlahParser blahParser = new BlahParser(); + Optional blahModel = blahParser.parse_String( + "blahmodel {" + + "blubScope blubScope1 {" + + "blubScope blubScope2 {" + + "symbol blubSymbol2" + + "}" + + "symbol blubSymbol1" + + "}" + + "}" + ); + + // create symbol table for "blah" + BlahTraverser traverser = BlahMill.traverser(); + BlahScopesGenitor blahSymbolTableCreator = new BlahScopesGenitor(); + blahSymbolTableCreator.addToScopeStack(globalScope.getIBlahGS()); + traverser.add4Blah(blahSymbolTableCreator); + traverser.setBlahHandler(blahSymbolTableCreator); + + IBlahArtifactScope blahSymbolTable = blahSymbolTableCreator.createFromAST(blahModel.get()); + blahSymbolTable.setName("blahmodel"); + + // check dummy symbol is present in local scope + Optional blubSymbol1 = blahSymbolTable.resolveDummy("blahmodel.blubScope1.blubSymbol1"); + + assertTrue(blubSymbol1.isPresent()); +// +// + + // check dummy symbol is present in global scope + Optional barSymbol = globalScope.resolveBar("blahmodel.blubScope1.blubSymbol1"); + + assertTrue(barSymbol.isPresent()); + + + /* *************************************************************************************************************** + ****************************************************************************************************************** + Foo/Bar Infrastruktur + ****************************************************************************************************************** + */ + + //parse foo model + FooParser fooParser = new FooParser(); + Optional fooModel = fooParser.parse_String("bar { blubSymbol1() } name"); + + // Check foo model is parsed + assertTrue(fooModel.isPresent()); + + // create symbol table for "foo" + FooScopesGenitorDelegator fooSymbolTableCreator = FooMill.scopesGenitorDelegator(); + IFooArtifactScope fooScope = fooSymbolTableCreator.createFromAST(fooModel.get()); + + // check symbol is resolvable + Optional k = fooScope.resolveBar("name"); + assertTrue(k.isPresent()); + + assertTrue(Log.getFindings().isEmpty()); + } + + +} diff --git a/monticore-grammar/src/test/java/de/monticore/aggregation/blah/Bar2DummySymbol.java b/monticore-grammar/src/test/java/de/monticore/aggregation/blah/Bar2DummySymbol.java new file mode 100644 index 0000000000..3da95fa70a --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/aggregation/blah/Bar2DummySymbol.java @@ -0,0 +1,21 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.aggregation.blah; + +import de.monticore.aggregation.blah._symboltable.DummySymbol; +import de.monticore.aggregation.foo._symboltable.BarSymbol; +import de.monticore.symboltable.resolving.ISymbolAdapter; + +public class Bar2DummySymbol extends BarSymbol implements ISymbolAdapter { + + private DummySymbol adaptee; + + @Override + public DummySymbol getAdaptee() { + return adaptee; + } + + public Bar2DummySymbol(DummySymbol symbol){ + super(symbol.getName()); + this.adaptee=symbol; + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/aggregation/foo/_symboltable/FooGlobalScope.java b/monticore-grammar/src/test/java/de/monticore/aggregation/foo/_symboltable/FooGlobalScope.java new file mode 100644 index 0000000000..c6c1469ec6 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/aggregation/foo/_symboltable/FooGlobalScope.java @@ -0,0 +1,61 @@ +// (c) https://github.com/MontiCore/monticore + +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.aggregation.foo._symboltable; + +import de.monticore.aggregation.blah.Bar2DummySymbol; +import de.monticore.aggregation.blah.BlahMill; +import de.monticore.aggregation.blah._symboltable.DummySymbol; +import de.monticore.aggregation.blah._symboltable.IBlahGlobalScope; +import de.monticore.io.paths.MCPath; +import de.monticore.symboltable.modifiers.AccessModifier; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.function.Predicate; + +public class FooGlobalScope extends de.monticore.aggregation.foo._symboltable.FooGlobalScopeTOP { + public FooGlobalScope() { + super(); + iBlahGS = BlahMill + .globalScope(); + iBlahGS.setSymbolPath(symbolPath); + iBlahGS.setFileExt("blah"); + } + + public FooGlobalScope(MCPath symbolPath, String ext){ + super(symbolPath, "blah"); + iBlahGS = BlahMill + .globalScope(); + iBlahGS.setSymbolPath(symbolPath); + iBlahGS.setFileExt("blah"); + } + + IBlahGlobalScope iBlahGS; + + @Override + public List resolveAdaptedBar(boolean foundSymbols, + String symbolName, AccessModifier modifier, Predicate predicate) { + Collection vardefs = iBlahGS.resolveDummyMany(foundSymbols, symbolName, modifier); + List list = new ArrayList<>(); + for (DummySymbol x : vardefs) { + Bar2DummySymbol bar2DummySymbol = new Bar2DummySymbol(x); + list.add(bar2DummySymbol); + } + return list; + } + + public IBlahGlobalScope getIBlahGS() { + return iBlahGS; + } + + public void setiBlahGS(IBlahGlobalScope iBlahGS) { + this.iBlahGS = iBlahGS; + } + + @Override + public FooGlobalScope getRealThis() { + return this; + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/expressions/assignmentexpressions/cocos/AssignmentExpressionsOnlyAssignToLValuesCoCoTest.java b/monticore-grammar/src/test/java/de/monticore/expressions/assignmentexpressions/cocos/AssignmentExpressionsOnlyAssignToLValuesCoCoTest.java new file mode 100644 index 0000000000..2dc0e2d074 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/expressions/assignmentexpressions/cocos/AssignmentExpressionsOnlyAssignToLValuesCoCoTest.java @@ -0,0 +1,200 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.expressions.assignmentexpressions.cocos; + +import de.monticore.expressions.assignmentexpressions._cocos.AssignmentExpressionsASTAssignmentExpressionCoCo; +import de.monticore.expressions.assignmentexpressions._cocos.AssignmentExpressionsCoCoChecker; +import de.monticore.expressions.combineexpressionswithliterals.CombineExpressionsWithLiteralsMill; +import de.monticore.expressions.commonexpressions.types3.util.CommonExpressionsLValueRelations; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.expressionsbasis.types3.util.ILValueRelations; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertTrue; + +public class AssignmentExpressionsOnlyAssignToLValuesCoCoTest { + + @Before + public void before() { + LogStub.init(); + Log.enableFailQuick(false); + // using a language containing lvalue and non-lvalue expressions + CombineExpressionsWithLiteralsMill.reset(); + CombineExpressionsWithLiteralsMill.init(); + // this test is (currently) not type dependent, + // as whether something is a lvalue can be derived from the ASTNode-type + // thus nothing type related needs to be initialized + } + + @Test + public void testNameExprAssignments() throws IOException { + testValid("a = 42"); + testValid("a += 0"); + testValid("a -= 0"); + testValid("a *= 0"); + testValid("a /= 0"); + testValid("a &= 0"); + testValid("a |= 0"); + testValid("a ^= 0"); + testValid("a >>= 0"); + testValid("a >>>= 0"); + testValid("a <<= 0"); + testValid("a %= 0"); + testValid("C++"); + testValid("C--"); + testValid("--a"); + testValid("++a"); + } + + @Test + public void testFieldAccessExprAssignments() throws IOException { + testValid("a.a = 42"); + testValid("a.a += 0"); + testValid("a.a -= 0"); + testValid("a.a *= 0"); + testValid("a.a /= 0"); + testValid("a.a &= 0"); + testValid("a.a |= 0"); + testValid("a.a ^= 0"); + testValid("a.a >>= 0"); + testValid("a.a >>>= 0"); + testValid("a.a <<= 0"); + testValid("a.a %= 0"); + testValid("a.a++"); + testValid("a.a--"); + testValid("--a.a"); + testValid("++a.a"); + } + + @Test + public void testLiteralAssignments() throws IOException { + testInvalid("true = true"); + testInvalid("'a' = 'a'"); + testInvalid("1 = 1"); + testInvalid("1l = 1l"); + testInvalid("0.1f = 0.1f"); + testInvalid("0.1 = 0.1"); + testInvalid("'a' += 'a'"); + testInvalid("1 += 1"); + testInvalid("1l += 1l"); + testInvalid("0.1f += 0.1f"); + testInvalid("0.1 += 0.1"); + testInvalid("'a' -= 'a'"); + testInvalid("1 -= 1"); + testInvalid("1l -= 1l"); + testInvalid("0.1f -= 0.1f"); + testInvalid("0.1 -= 0.1"); + testInvalid("'a' *= 'a'"); + testInvalid("1 *= 1"); + testInvalid("1l *= 1l"); + testInvalid("0.1f *= 0.1f"); + testInvalid("0.1 *= 0.1"); + testInvalid("'a' /= 'a'"); + testInvalid("1 /= 1"); + testInvalid("1l /= 1l"); + testInvalid("0.1f /= 0.1f"); + testInvalid("0.1 /= 0.1"); + testInvalid("'a' %= 'a'"); + testInvalid("1 %= 1"); + testInvalid("1l %= 1l"); + testInvalid("0.1f %= 0.1f"); + testInvalid("0.1 %= 0.1"); + testInvalid("'a' >>= 'a'"); + testInvalid("1 >>= 1"); + testInvalid("1l >>= 1l"); + testInvalid("0.1f >>= 0.1f"); + testInvalid("0.1 >>= 0.1"); + testInvalid("'a' <<= 'a'"); + testInvalid("1 <<= 1"); + testInvalid("1l <<= 1l"); + testInvalid("0.1f <<= 0.1f"); + testInvalid("0.1 <<= 0.1"); + testInvalid("'a' >>>= 'a'"); + testInvalid("1 >>>= 1"); + testInvalid("1l >>>= 1l"); + testInvalid("0.1f >>>= 0.1f"); + testInvalid("0.1 >>>= 0.1"); + testInvalid("true &= true"); + testInvalid("'a' &= 'a'"); + testInvalid("1 &= 1"); + testInvalid("1l &= 1l"); + testInvalid("true |= true"); + testInvalid("'a' |= 'a'"); + testInvalid("1 |= 1"); + testInvalid("1l |= 1l"); + testInvalid("true ^= true"); + testInvalid("'a' ^= 'a'"); + testInvalid("1 ^= 1"); + testInvalid("1l ^= 1l"); + testInvalid("++'c'"); + testInvalid("++1"); + testInvalid("++1l"); + testInvalid("++0.1f"); + testInvalid("++0.1"); + testInvalid("--'c'"); + testInvalid("--1"); + testInvalid("--1l"); + testInvalid("--0.1f"); + testInvalid("--0.1"); + testInvalid("'c'++"); + testInvalid("1++"); + testInvalid("1l++"); + testInvalid("0.1f++"); + testInvalid("0.1++"); + testInvalid("'c'--"); + testInvalid("1--"); + testInvalid("1l--"); + testInvalid("0.1f--"); + testInvalid("0.1--"); + } + + @Test + public void testFurtherInvalidAssignments() throws IOException { + testInvalid("(a) = (42)"); + testInvalid("a + a = 84"); + testInvalid("1 + 1 = 2"); + testInvalid("getToInc()++"); + testInvalid("getVar() = 2"); + } + + // Helper + + protected void testValid(String exprStr) throws IOException { + check(exprStr); + assertTrue(Log.getFindings().isEmpty()); + Log.clearFindings(); + } + + protected void testInvalid(String exprStr) throws IOException { + check(exprStr); + assertTrue(!Log.getFindings().isEmpty()); + assertTrue(Log.getFindings().stream().anyMatch( + f -> f.getMsg().contains("0xFDD47") + )); + Log.clearFindings(); + } + + protected void check(String exprStr) throws IOException { + Optional exprOpt = CombineExpressionsWithLiteralsMill + .parser().parse_StringExpression(exprStr); + assertTrue(exprOpt.isPresent()); + assertTrue(Log.getFindings().isEmpty()); + getChecker().checkAll(exprOpt.get()); + } + + protected AssignmentExpressionsCoCoChecker getChecker() { + ILValueRelations ilValueRelations = new CommonExpressionsLValueRelations(); + AssignmentExpressionsCoCoChecker checker = + new AssignmentExpressionsCoCoChecker(); + checker.addCoCo((AssignmentExpressionsASTAssignmentExpressionCoCo) + new AssignmentExpressionsOnlyAssignToLValuesCoCo(ilValueRelations) + ); + return checker; + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/expressions/cocos/ExpressionValidTest.java b/monticore-grammar/src/test/java/de/monticore/expressions/cocos/ExpressionValidTest.java new file mode 100644 index 0000000000..1c34ace624 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/expressions/cocos/ExpressionValidTest.java @@ -0,0 +1,74 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.cocos; + +import de.monticore.expressions.combineexpressionswithliterals.CombineExpressionsWithLiteralsMill; +import de.monticore.expressions.combineexpressionswithliterals._parser.CombineExpressionsWithLiteralsParser; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.expressionsbasis._cocos.ExpressionsBasisCoCoChecker; +import de.monticore.grammar.cocos.CocoTest; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.types.check.FullDeriveFromCombineExpressionsWithLiterals; +import de.monticore.types.check.TypeCalculator; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.*; + +public class ExpressionValidTest extends CocoTest { + + protected ExpressionsBasisCoCoChecker checker; + + @Before + public void init() { + CombineExpressionsWithLiteralsMill.reset(); + CombineExpressionsWithLiteralsMill.init(); + BasicSymbolsMill.initializePrimitives(); + TypeCalculator typeCheck = new TypeCalculator(null, new FullDeriveFromCombineExpressionsWithLiterals()); + checker = new ExpressionsBasisCoCoChecker(); + checker.addCoCo(new ExpressionValid(typeCheck)); + new TypeCalculator(null, new FullDeriveFromCombineExpressionsWithLiterals()); + } + + public void checkValid(String expressionString) throws IOException { + CombineExpressionsWithLiteralsParser parser = new CombineExpressionsWithLiteralsParser(); + Optional optAST = parser.parse_StringExpression(expressionString); + assertTrue(optAST.isPresent()); + Log.getFindings().clear(); + checker.checkAll(optAST.get()); + assertTrue(Log.getFindings().toString(), Log.getFindings().isEmpty()); + } + + public void checkInvalid(String expressionString) throws IOException { + CombineExpressionsWithLiteralsParser parser = new CombineExpressionsWithLiteralsParser(); + Optional optAST = parser.parse_StringExpression(expressionString); + assertTrue(optAST.isPresent()); + Log.getFindings().clear(); + checker.checkAll(optAST.get()); + assertFalse(Log.getFindings().isEmpty()); + } + + @Test + public void testValid() throws IOException { + checkValid("7-4*2"); + checkValid("4/2*6%4"); + checkValid("(5<6)&&(1<=1)"); + checkValid("!true||false&&(5>=0)"); + checkValid("5.0/2.5%2"); + } + + @Test + public void testInvalid() throws IOException { + checkInvalid("5+false"); + checkInvalid("true-true"); + checkInvalid("!false!=5"); + checkInvalid("5||7"); + checkInvalid("true++"); + checkInvalid("(true&&6)||(false>=37)"); + } +} \ No newline at end of file diff --git a/monticore-grammar/src/test/java/de/monticore/expressions/combineexpressionswithliterals/_cocos/CombineExpressionsWithLiteralsCoCoChecker.java b/monticore-grammar/src/test/java/de/monticore/expressions/combineexpressionswithliterals/_cocos/CombineExpressionsWithLiteralsCoCoChecker.java new file mode 100644 index 0000000000..6d4646e8b5 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/expressions/combineexpressionswithliterals/_cocos/CombineExpressionsWithLiteralsCoCoChecker.java @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.combineexpressionswithliterals._cocos; + +public class CombineExpressionsWithLiteralsCoCoChecker extends CombineExpressionsWithLiteralsCoCoCheckerTOP { + + public CombineExpressionsWithLiteralsCoCoChecker getCombineExpressionsWithLiteralsCoCoChecker() { + CombineExpressionsWithLiteralsCoCoChecker checker = new CombineExpressionsWithLiteralsCoCoChecker(); + checker.addCoCo(new NoClassExpressionForGenerics()); + return checker; + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/expressions/combineexpressionswithliterals/_cocos/NoClassExpressionForGenerics.java b/monticore-grammar/src/test/java/de/monticore/expressions/combineexpressionswithliterals/_cocos/NoClassExpressionForGenerics.java new file mode 100644 index 0000000000..6ebdef226b --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/expressions/combineexpressionswithliterals/_cocos/NoClassExpressionForGenerics.java @@ -0,0 +1,40 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.combineexpressionswithliterals._cocos; + +import de.monticore.expressions.combineexpressionswithliterals._ast.ASTExtReturnType; +import de.monticore.expressions.javaclassexpressions._ast.ASTClassExpression; +import de.monticore.expressions.javaclassexpressions._cocos.JavaClassExpressionsASTClassExpressionCoCo; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.types.mcbasictypes._ast.ASTMCReturnType; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.mccollectiontypes._ast.ASTMCGenericType; +import de.monticore.types.mcsimplegenerictypes._prettyprint.MCSimpleGenericTypesFullPrettyPrinter; +import de.se_rwth.commons.logging.Log; + +/** + * prototype for the CoCo of your language. Because of the use of externals, it can only be # + * implemented as a prototype in the language extending JavaClassExpressions. + */ +public class NoClassExpressionForGenerics implements JavaClassExpressionsASTClassExpressionCoCo { + + public static final String ERROR_CODE = "0xA0302"; + + public static final String ERROR_MSG_FORMAT = " Generic types like %s cannot use the ClassExpression"; + + @Override + public void check(ASTClassExpression node) { + checkNoGeneric((ASTExtReturnType) node.getExtReturnType()); + } + + private void checkNoGeneric(ASTExtReturnType extreturnType){ + MCSimpleGenericTypesFullPrettyPrinter prettyPrinter = new MCSimpleGenericTypesFullPrettyPrinter(new IndentPrinter()); + ASTMCReturnType returnType = extreturnType.getMCReturnType(); + if(returnType.isPresentMCType()){ + ASTMCType type = returnType.getMCType(); + if(type instanceof ASTMCGenericType){ + Log.error(String.format(ERROR_CODE+ERROR_MSG_FORMAT,prettyPrinter.prettyprint(returnType))); + } + } + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/expressions/combineexpressionswithliterals/_cocos/TestNoClassExpressionForGenerics.java b/monticore-grammar/src/test/java/de/monticore/expressions/combineexpressionswithliterals/_cocos/TestNoClassExpressionForGenerics.java new file mode 100644 index 0000000000..50c97c6256 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/expressions/combineexpressionswithliterals/_cocos/TestNoClassExpressionForGenerics.java @@ -0,0 +1,75 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.combineexpressionswithliterals._cocos; + +import de.monticore.expressions.combineexpressionswithliterals._parser.CombineExpressionsWithLiteralsParser; +import de.monticore.expressions.javaclassexpressions._ast.ASTClassExpression; +import de.monticore.expressions.javaclassexpressions._ast.ASTJavaClassExpressionsNode; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.*; + +public class TestNoClassExpressionForGenerics { + + CombineExpressionsWithLiteralsParser p = new CombineExpressionsWithLiteralsParser(); + + @Before + public void setup(){ + LogStub.init(); + Log.enableFailQuick(false); + } + + @Test + public void testValid() throws IOException { + Optional optClass = p.parse_StringClassExpression("Integer.class"); + + assertTrue(optClass.isPresent()); + + CombineExpressionsWithLiteralsCoCoChecker coCoChecker = new CombineExpressionsWithLiteralsCoCoChecker().getCombineExpressionsWithLiteralsCoCoChecker(); + coCoChecker.checkAll((ASTJavaClassExpressionsNode) optClass.get()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testValid2() throws IOException{ + Optional optClass = p.parse_StringClassExpression("int.class"); + + assertTrue(optClass.isPresent()); + + CombineExpressionsWithLiteralsCoCoChecker coCoChecker = new CombineExpressionsWithLiteralsCoCoChecker().getCombineExpressionsWithLiteralsCoCoChecker(); + coCoChecker.checkAll((ASTJavaClassExpressionsNode) optClass.get()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testInvalidGeneric() throws IOException{ + //MCListType + Optional optClass = p.parse_StringClassExpression("List.class"); + + assertTrue(optClass.isPresent()); + + CombineExpressionsWithLiteralsCoCoChecker coCoChecker = new CombineExpressionsWithLiteralsCoCoChecker().getCombineExpressionsWithLiteralsCoCoChecker(); + coCoChecker.checkAll((ASTJavaClassExpressionsNode) optClass.get()); + assertFalse(Log.getFindings().isEmpty()); + assertTrue(Log.getFindings().get(Log.getFindings().size()-1).getMsg().startsWith(NoClassExpressionForGenerics.ERROR_CODE)); + } + + @Test + public void testInvalidGeneric2() throws IOException{ + //MCBasicGenericType + Optional optClass = p.parse_StringClassExpression("a.b.List.class"); + + assertTrue(optClass.isPresent()); + + CombineExpressionsWithLiteralsCoCoChecker coCoChecker = new CombineExpressionsWithLiteralsCoCoChecker().getCombineExpressionsWithLiteralsCoCoChecker(); + coCoChecker.checkAll((ASTJavaClassExpressionsNode) optClass.get()); + assertFalse(Log.getFindings().isEmpty()); + assertTrue(Log.getFindings().get(Log.getFindings().size()-1).getMsg().startsWith(NoClassExpressionForGenerics.ERROR_CODE)); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/expressions/exptojava/AssignmentExpressionsJavaPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/expressions/exptojava/AssignmentExpressionsJavaPrinterTest.java new file mode 100644 index 0000000000..f2bf006b55 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/expressions/exptojava/AssignmentExpressionsJavaPrinterTest.java @@ -0,0 +1,345 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.exptojava; + +import de.monticore.expressions.assignmentexpressions.AssignmentExpressionsMill; +import de.monticore.expressions.assignmentexpressions._ast.*; +import de.monticore.expressions.assignmentexpressions._prettyprint.AssignmentExpressionsFullPrettyPrinter; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.testassignmentexpressions.TestAssignmentExpressionsMill; +import de.monticore.expressions.testassignmentexpressions._parser.TestAssignmentExpressionsParser; +import de.monticore.prettyprint.IndentPrinter; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static de.monticore.expressions.assignmentexpressions._ast.ASTConstantsAssignmentExpressions.*; +import static org.junit.Assert.*; + +public class AssignmentExpressionsJavaPrinterTest { + + protected TestAssignmentExpressionsParser parser; + protected AssignmentExpressionsFullPrettyPrinter javaPrinter; + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestAssignmentExpressionsMill.reset(); + TestAssignmentExpressionsMill.init(); + parser = new TestAssignmentExpressionsParser(); + javaPrinter = new AssignmentExpressionsFullPrettyPrinter(new IndentPrinter()); + javaPrinter.getPrinter().clearBuffer(); + } + + @Test + public void testIncPrefixExpression() throws IOException { + Optional result = parser.parse_StringIncPrefixExpression("++a"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTIncPrefixExpression ast = result.get(); + + String output = javaPrinter.prettyprint(ast); + + result = parser.parse_StringIncPrefixExpression(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testDecPrefixExpression() throws IOException { + Optional result = parser.parse_StringDecPrefixExpression("--a"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTDecPrefixExpression ast = result.get(); + + String output = javaPrinter.prettyprint(ast); + + result = parser.parse_StringDecPrefixExpression(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testIncSuffixExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + ASTIncSuffixExpression result = AssignmentExpressionsMill.incSuffixExpressionBuilder() + .setExpression(a.get()) + .build(); + + String output = javaPrinter.prettyprint(result); + + assertEquals("a++", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testDecSuffixExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + ASTDecSuffixExpression result = AssignmentExpressionsMill.decSuffixExpressionBuilder() + .setExpression(a.get()) + .build(); + + String output = javaPrinter.prettyprint(result); + + assertEquals("a--", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testRegularAssignmentEqualsExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTAssignmentExpression result = AssignmentExpressionsMill.assignmentExpressionBuilder() + .setLeft(a.get()) + .setRight(b.get()) + .setOperator(EQUALS) + .build(); + + String output = javaPrinter.prettyprint(result); + + assertEquals("a=b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testRegularAssignmentPlusEqualsExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTAssignmentExpression result = AssignmentExpressionsMill.assignmentExpressionBuilder() + .setLeft(a.get()) + .setRight(b.get()) + .setOperator(PLUSEQUALS) + .build(); + + String output = javaPrinter.prettyprint(result); + + assertEquals("a+=b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testRegularAssignmentMinusExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTAssignmentExpression result = AssignmentExpressionsMill.assignmentExpressionBuilder() + .setLeft(a.get()) + .setRight(b.get()) + .setOperator(MINUSEQUALS) + .build(); + + String output = javaPrinter.prettyprint(result); + + assertEquals("a-=b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testRegularAssignmentPercentEqualsExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTAssignmentExpression result = AssignmentExpressionsMill.assignmentExpressionBuilder() + .setLeft(a.get()) + .setRight(b.get()) + .setOperator(PERCENTEQUALS) + .build(); + + String output = javaPrinter.prettyprint(result); + + assertEquals("a%=b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testRegularAssignmentAndEqualsExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTAssignmentExpression result = AssignmentExpressionsMill.assignmentExpressionBuilder() + .setLeft(a.get()) + .setRight(b.get()) + .setOperator(AND_EQUALS) + .build(); + + String output = javaPrinter.prettyprint(result); + + assertEquals("a&=b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testRegularAssignmentRoofEqualsExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTAssignmentExpression result = AssignmentExpressionsMill.assignmentExpressionBuilder() + .setLeft(a.get()) + .setRight(b.get()) + .setOperator(ROOFEQUALS) + .build(); + + String output = javaPrinter.prettyprint(result); + + assertEquals("a^=b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testRegularAssignmentSlashEqualsExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTAssignmentExpression result = AssignmentExpressionsMill.assignmentExpressionBuilder() + .setLeft(a.get()) + .setRight(b.get()) + .setOperator(SLASHEQUALS) + .build(); + + String output = javaPrinter.prettyprint(result); + + assertEquals("a/=b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testRegularAssignmentStarEqualsExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTAssignmentExpression result = AssignmentExpressionsMill.assignmentExpressionBuilder() + .setLeft(a.get()) + .setRight(b.get()) + .setOperator(STAREQUALS) + .build(); + + String output = javaPrinter.prettyprint(result); + + assertEquals("a*=b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testRegularAssignmentPipeEqualsExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTAssignmentExpression result = AssignmentExpressionsMill.assignmentExpressionBuilder() + .setLeft(a.get()) + .setRight(b.get()) + .setOperator(PIPEEQUALS) + .build(); + + String output = javaPrinter.prettyprint(result); + + assertEquals("a|=b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testRegularAssignmentLTLTEqualsExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTAssignmentExpression result = AssignmentExpressionsMill.assignmentExpressionBuilder() + .setLeft(a.get()) + .setRight(b.get()) + .setOperator(LTLTEQUALS) + .build(); + + String output = javaPrinter.prettyprint(result); + + assertEquals("a<<=b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testRegularAssignmentGTGTEqualsExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTAssignmentExpression result = AssignmentExpressionsMill.assignmentExpressionBuilder() + .setLeft(a.get()) + .setRight(b.get()) + .setOperator(GTGTEQUALS) + .build(); + + String output = javaPrinter.prettyprint(result); + + assertEquals("a>>=b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testRegularAssignmentGTGTGTEqualsExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTAssignmentExpression result = AssignmentExpressionsMill.assignmentExpressionBuilder() + .setLeft(a.get()) + .setRight(b.get()) + .setOperator(GTGTGTEQUALS) + .build(); + + String output = javaPrinter.prettyprint(result); + + assertEquals("a>>>=b", output); + + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/expressions/exptojava/BitExpressionsJavaPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/expressions/exptojava/BitExpressionsJavaPrinterTest.java new file mode 100644 index 0000000000..9598a78a8a --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/expressions/exptojava/BitExpressionsJavaPrinterTest.java @@ -0,0 +1,150 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.exptojava; + +import de.monticore.expressions.bitexpressions._prettyprint.BitExpressionsFullPrettyPrinter; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.testbitexpressions._auxiliary.BitExpressionsMillForTestBitExpressions; +import de.monticore.expressions.testbitexpressions._parser.TestBitExpressionsParser; +import de.monticore.prettyprint.IndentPrinter; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class BitExpressionsJavaPrinterTest { + + protected TestBitExpressionsParser parser; + protected BitExpressionsFullPrettyPrinter javaPrinter; + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + BitExpressionsMillForTestBitExpressions.reset(); + BitExpressionsMillForTestBitExpressions.init(); + parser = new TestBitExpressionsParser(); + javaPrinter= new BitExpressionsFullPrettyPrinter(new IndentPrinter()); + javaPrinter.getPrinter().clearBuffer(); + } + + @Test + public void testLeftShiftExpression() throws IOException { + Optional result = parser.parse_StringExpression("a< result = parser.parse_StringExpression("a>>b"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + ASTExpression ast = result.get(); + + String output = javaPrinter.prettyprint(ast); + + result = parser.parse_StringExpression(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testLogicalRightShiftExpression() throws IOException { + Optional result = parser.parse_StringExpression("a>>>b"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + ASTExpression ast = result.get(); + + String output = javaPrinter.prettyprint(ast); + + result = parser.parse_StringExpression(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testBinaryOrOpExpression() throws IOException { + Optional result = parser.parse_StringExpression("a|b"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + ASTExpression ast = result.get(); + + String output = javaPrinter.prettyprint(ast); + + result = parser.parse_StringExpression(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testBinaryXorExpression() throws IOException { + Optional result = parser.parse_StringExpression("a^b"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + ASTExpression ast = result.get(); + + String output = javaPrinter.prettyprint(ast); + + result = parser.parse_StringExpression(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testBinaryAndExpression() throws IOException { + Optional result = parser.parse_StringExpression("a&b"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + ASTExpression ast = result.get(); + + String output = javaPrinter.prettyprint(ast); + + result = parser.parse_StringExpression(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/expressions/exptojava/CommonExpressionsJavaPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/expressions/exptojava/CommonExpressionsJavaPrinterTest.java new file mode 100644 index 0000000000..2ad9f539a3 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/expressions/exptojava/CommonExpressionsJavaPrinterTest.java @@ -0,0 +1,473 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.exptojava; + +import de.monticore.expressions.commonexpressions.CommonExpressionsMill; +import de.monticore.expressions.commonexpressions._ast.*; +import de.monticore.expressions.commonexpressions._prettyprint.CommonExpressionsFullPrettyPrinter; +import de.monticore.expressions.expressionsbasis._ast.ASTArguments; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.testcommonexpressions.TestCommonExpressionsMill; +import de.monticore.expressions.testcommonexpressions._parser.TestCommonExpressionsParser; +import de.monticore.prettyprint.IndentPrinter; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; + +public class CommonExpressionsJavaPrinterTest { + + protected TestCommonExpressionsParser parser; + protected CommonExpressionsFullPrettyPrinter javaPrinter; + + CommonExpressionsFullPrettyPrinter prepareJavaPrinter(){ + CommonExpressionsFullPrettyPrinter commonExpressionsFullPrettyPrinter = new CommonExpressionsFullPrettyPrinter(new IndentPrinter()); + CommonExpressionsJavaPrinter.applyJavaPrinter(commonExpressionsFullPrettyPrinter.getTraverser(), commonExpressionsFullPrettyPrinter.getPrinter(), true); + return commonExpressionsFullPrettyPrinter; + } + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestCommonExpressionsMill.reset(); + TestCommonExpressionsMill.init(); + parser = new TestCommonExpressionsParser(); + javaPrinter = prepareJavaPrinter(); + IndentPrinter indentPrinter = new IndentPrinter(); + javaPrinter.getPrinter().clearBuffer(); + } + + @Test + public void testMinusPrefixExpression() throws IOException { + Optional result = parser.parse_StringMinusPrefixExpression("-a"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTMinusPrefixExpression ast = result.get(); + + String output = javaPrinter.prettyprint(ast); + + result = parser.parse_StringMinusPrefixExpression(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testPlusPrefixExpression() throws IOException { + Optional result = parser.parse_StringPlusPrefixExpression("+a"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTPlusPrefixExpression ast = result.get(); + + String output = javaPrinter.prettyprint(ast); + + result = parser.parse_StringPlusPrefixExpression(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + @Test + public void testBooleanNotExpression() throws IOException { + Optional result = parser.parse_StringBooleanNotExpression("~a"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTBooleanNotExpression ast = result.get(); + + String output = javaPrinter.prettyprint(ast); + + result = parser.parse_StringBooleanNotExpression(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testLogicalNotExpression() throws IOException { + Optional result = parser.parse_StringLogicalNotExpression("!a"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTLogicalNotExpression ast = result.get(); + + String output = javaPrinter.prettyprint(ast); + + result = parser.parse_StringLogicalNotExpression(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void testBracketExpression() throws IOException { + Optional result = parser.parse_StringBracketExpression("(a == b)"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTBracketExpression ast = result.get(); + + String output = javaPrinter.prettyprint(ast); + + result = parser.parse_StringBracketExpression(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void testArguments() throws IOException { + Optional result = parser.parse_StringArguments("(a , b , c)"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTArguments ast = result.get(); + + String output = javaPrinter.prettyprint(ast); + + result = parser.parse_StringArguments(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCallExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional arguments = parser.parse_StringArguments("(b, c)"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(arguments.isPresent()); + ASTCallExpression result = CommonExpressionsMill.callExpressionBuilder() + .setExpression(a.get()) + .setArguments(arguments.get()) + .build(); + + String output = javaPrinter.prettyprint(result); + + assertEquals("a(b,c)", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testFieldAccessExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + ASTFieldAccessExpression result = CommonExpressionsMill.fieldAccessExpressionBuilder() + .setExpression(a.get()) + .setName("foo") + .build(); + + String output = javaPrinter.prettyprint(result); + + assertEquals("a.getFoo()", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMultExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTMultExpression result = CommonExpressionsMill.multExpressionBuilder() + .setLeft(a.get()) + .setOperator("*") + .setRight(b.get()) + .build(); + + String output = javaPrinter.prettyprint(result); + + assertEquals("a*b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testDivideExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTDivideExpression result = CommonExpressionsMill.divideExpressionBuilder() + .setLeft(a.get()) + .setOperator("/") + .setRight(b.get()) + .build(); + + String output = javaPrinter.prettyprint(result); + + assertEquals("a/b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testModuloExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTModuloExpression result = CommonExpressionsMill.moduloExpressionBuilder() + .setLeft(a.get()) + .setOperator("%") + .setRight(b.get()) + .build(); + + String output = javaPrinter.prettyprint(result); + + assertEquals("a%b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testPlusExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTPlusExpression result = CommonExpressionsMill.plusExpressionBuilder() + .setLeft(a.get()) + .setOperator("+") + .setRight(b.get()) + .build(); + + String output = javaPrinter.prettyprint(result); + + assertEquals("a+b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMinusExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTMinusExpression result = CommonExpressionsMill.minusExpressionBuilder() + .setLeft(a.get()) + .setOperator("-") + .setRight(b.get()) + .build(); + + String output = javaPrinter.prettyprint(result); + + assertEquals("a-b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testLessEqualExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTLessEqualExpression result = CommonExpressionsMill.lessEqualExpressionBuilder() + .setLeft(a.get()) + .setOperator("<=") + .setRight(b.get()) + .build(); + + String output = javaPrinter.prettyprint(result); + + assertEquals("a<=b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testGreaterEqualExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTGreaterEqualExpression result = CommonExpressionsMill.greaterEqualExpressionBuilder() + .setLeft(a.get()) + .setOperator(">=") + .setRight(b.get()) + .build(); + + String output = javaPrinter.prettyprint(result); + + assertEquals("a>=b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testLessThanExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTLessThanExpression result = CommonExpressionsMill.lessThanExpressionBuilder() + .setLeft(a.get()) + .setOperator("<") + .setRight(b.get()) + .build(); + + String output = javaPrinter.prettyprint(result); + + assertEquals("a a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTGreaterThanExpression result = CommonExpressionsMill.greaterThanExpressionBuilder() + .setLeft(a.get()) + .setOperator(">") + .setRight(b.get()) + .build(); + + String output = javaPrinter.prettyprint(result); + + assertEquals("a>b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testEqualsExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTEqualsExpression result = CommonExpressionsMill.equalsExpressionBuilder() + .setLeft(a.get()) + .setOperator("==") + .setRight(b.get()) + .build(); + + String output = javaPrinter.prettyprint(result); + + assertEquals("a==b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testNotEqualsExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTNotEqualsExpression result = CommonExpressionsMill.notEqualsExpressionBuilder() + .setLeft(a.get()) + .setOperator("!=") + .setRight(b.get()) + .build(); + + String output = javaPrinter.prettyprint(result); + + assertEquals("a!=b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testBooleanAndOpExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTBooleanAndOpExpression result = CommonExpressionsMill.booleanAndOpExpressionBuilder() + .setLeft(a.get()) + .setOperator("&&") + .setRight(b.get()) + .build(); + + String output = javaPrinter.prettyprint(result); + + assertEquals("a&&b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testBooleanOrOpExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTBooleanOrOpExpression result = CommonExpressionsMill.booleanOrOpExpressionBuilder() + .setLeft(a.get()) + .setOperator("||") + .setRight(b.get()) + .build(); + + String output = javaPrinter.prettyprint(result); + + assertEquals("a||b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testConditionalExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + Optional c = parser.parse_StringExpression("c"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + assertTrue(c.isPresent()); + ASTConditionalExpression result = CommonExpressionsMill.conditionalExpressionBuilder() + .setCondition(a.get()) + .setTrueExpression(b.get()) + .setFalseExpression(c.get()) + .build(); + + String output = javaPrinter.prettyprint(result); + + assertEquals("a ? b:c", output); + + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/expressions/exptojava/JavaClassExpressionsJavaPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/expressions/exptojava/JavaClassExpressionsJavaPrinterTest.java new file mode 100644 index 0000000000..f3cf8df81d --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/expressions/exptojava/JavaClassExpressionsJavaPrinterTest.java @@ -0,0 +1,199 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.exptojava; + +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.javaclassexpressions.JavaClassExpressionsMill; +import de.monticore.expressions.javaclassexpressions._ast.*; +import de.monticore.expressions.javaclassexpressions._prettyprint.JavaClassExpressionsFullPrettyPrinter; +import de.monticore.expressions.testjavaclassexpressions.TestJavaClassExpressionsMill; +import de.monticore.expressions.testjavaclassexpressions._parser.TestJavaClassExpressionsParser; +import de.monticore.prettyprint.IndentPrinter; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.*; + +public class JavaClassExpressionsJavaPrinterTest { + + protected TestJavaClassExpressionsParser parser; + protected JavaClassExpressionsFullPrettyPrinter javaPrinter; + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestJavaClassExpressionsMill.reset(); + TestJavaClassExpressionsMill.init(); + parser = new TestJavaClassExpressionsParser(); + javaPrinter= new JavaClassExpressionsFullPrettyPrinter(new IndentPrinter()); + javaPrinter.getPrinter().clearBuffer(); + } + + @Test + public void testPrimaryThisExpression() throws IOException { + Optional result = parser.parse_StringPrimaryThisExpression("this"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTPrimaryThisExpression ast = result.get(); + + String output = javaPrinter.prettyprint(ast); + + result = parser.parse_StringPrimaryThisExpression(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testPrimarySuperExpression() throws IOException { + Optional result = parser.parse_StringPrimarySuperExpression("super"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTPrimarySuperExpression ast = result.get(); + + String output = javaPrinter.prettyprint(ast); + + result = parser.parse_StringPrimarySuperExpression(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testThisExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + ASTThisExpression result = JavaClassExpressionsMill.thisExpressionBuilder() + .setExpression(a.get()) + .build(); + + String output = javaPrinter.prettyprint(result); + + assertEquals("a.this", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testArrayExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTArrayExpression result = JavaClassExpressionsMill.arrayExpressionBuilder() + .setExpression(a.get()) + .setIndexExpression(b.get()) + .build(); + + String output = javaPrinter.prettyprint(result); + + assertEquals("a[b]", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testSuperExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringSuperSuffix("(b)"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTSuperExpression result = JavaClassExpressionsMill.superExpressionBuilder() + .setExpression(a.get()) + .setSuperSuffix(b.get()) + .build(); + + String output = javaPrinter.prettyprint(result); + + assertEquals("a.super(b)", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testGenericInvocationSuffixThis() throws IOException { + Optional result = parser.parse_StringGenericInvocationSuffix("this(a)"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTGenericInvocationSuffix ast = result.get(); + + String output = javaPrinter.prettyprint(ast); + + result = parser.parse_StringGenericInvocationSuffix(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testGenericInvocationSuffixSuper() throws IOException { + Optional result = parser.parse_StringGenericInvocationSuffix("super(b)"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTGenericInvocationSuffix ast = result.get(); + + String output = javaPrinter.prettyprint(ast); + + result = parser.parse_StringGenericInvocationSuffix(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testGenericInvocationSuffixSimple() throws IOException { + Optional result = parser.parse_StringGenericInvocationSuffix("a(b)"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTGenericInvocationSuffix ast = result.get(); + + String output = javaPrinter.prettyprint(ast); + + result = parser.parse_StringGenericInvocationSuffix(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testTypePattern() throws IOException { + Optional result = parser.parse_StringTypePattern("String s"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTTypePattern ast = result.get(); + + String output = javaPrinter.prettyprint(ast); + + result = parser.parse_StringTypePattern(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/expressions/prettyprint/AssignmentExpressionsPrettyPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/expressions/prettyprint/AssignmentExpressionsPrettyPrinterTest.java new file mode 100644 index 0000000000..e4071975f7 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/expressions/prettyprint/AssignmentExpressionsPrettyPrinterTest.java @@ -0,0 +1,345 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.prettyprint; + +import de.monticore.expressions.assignmentexpressions.AssignmentExpressionsMill; +import de.monticore.expressions.assignmentexpressions._ast.*; +import de.monticore.expressions.assignmentexpressions._prettyprint.AssignmentExpressionsFullPrettyPrinter; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.testassignmentexpressions.TestAssignmentExpressionsMill; +import de.monticore.expressions.testassignmentexpressions._parser.TestAssignmentExpressionsParser; +import de.monticore.prettyprint.IndentPrinter; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static de.monticore.expressions.assignmentexpressions._ast.ASTConstantsAssignmentExpressions.*; +import static org.junit.Assert.*; + +public class AssignmentExpressionsPrettyPrinterTest { + + protected TestAssignmentExpressionsParser parser; + protected AssignmentExpressionsFullPrettyPrinter prettyPrinter; + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestAssignmentExpressionsMill.reset(); + TestAssignmentExpressionsMill.init(); + parser = new TestAssignmentExpressionsParser(); + prettyPrinter = new AssignmentExpressionsFullPrettyPrinter(new IndentPrinter()); + prettyPrinter.getPrinter().clearBuffer(); + } + + @Test + public void testIncPrefixExpression() throws IOException { + Optional result = parser.parse_StringIncPrefixExpression("++a"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTIncPrefixExpression ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringIncPrefixExpression(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testDecPrefixExpression() throws IOException { + Optional result = parser.parse_StringDecPrefixExpression("--a"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTDecPrefixExpression ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringDecPrefixExpression(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testIncSuffixExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + ASTIncSuffixExpression result = AssignmentExpressionsMill.incSuffixExpressionBuilder() + .setExpression(a.get()) + .build(); + + String output = prettyPrinter.prettyprint(result).trim(); + + assertEquals("a++", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testDecSuffixExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + ASTDecSuffixExpression result = AssignmentExpressionsMill.decSuffixExpressionBuilder() + .setExpression(a.get()) + .build(); + + String output = prettyPrinter.prettyprint(result).trim(); + + assertEquals("a--", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testRegularAssignmentEqualsExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTAssignmentExpression result = AssignmentExpressionsMill.assignmentExpressionBuilder() + .setLeft(a.get()) + .setRight(b.get()) + .setOperator(EQUALS) + .build(); + + String output = prettyPrinter.prettyprint(result).trim(); + + assertEquals("a=b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testRegularAssignmentPlusEqualsExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTAssignmentExpression result = AssignmentExpressionsMill.assignmentExpressionBuilder() + .setLeft(a.get()) + .setRight(b.get()) + .setOperator(PLUSEQUALS) + .build(); + + String output = prettyPrinter.prettyprint(result).trim(); + + assertEquals("a+=b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testRegularAssignmentMinusExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTAssignmentExpression result = AssignmentExpressionsMill.assignmentExpressionBuilder() + .setLeft(a.get()) + .setRight(b.get()) + .setOperator(MINUSEQUALS) + .build(); + + String output = prettyPrinter.prettyprint(result).trim(); + + assertEquals("a-=b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testRegularAssignmentPercentEqualsExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTAssignmentExpression result = AssignmentExpressionsMill.assignmentExpressionBuilder() + .setLeft(a.get()) + .setRight(b.get()) + .setOperator(PERCENTEQUALS) + .build(); + + String output = prettyPrinter.prettyprint(result).trim(); + + assertEquals("a%=b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testRegularAssignmentAndEqualsExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTAssignmentExpression result = AssignmentExpressionsMill.assignmentExpressionBuilder() + .setLeft(a.get()) + .setRight(b.get()) + .setOperator(AND_EQUALS) + .build(); + + String output = prettyPrinter.prettyprint(result).trim(); + + assertEquals("a&=b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testRegularAssignmentRoofEqualsExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTAssignmentExpression result = AssignmentExpressionsMill.assignmentExpressionBuilder() + .setLeft(a.get()) + .setRight(b.get()) + .setOperator(ROOFEQUALS) + .build(); + + String output = prettyPrinter.prettyprint(result).trim(); + + assertEquals("a^=b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testRegularAssignmentSlashEqualsExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTAssignmentExpression result = AssignmentExpressionsMill.assignmentExpressionBuilder() + .setLeft(a.get()) + .setRight(b.get()) + .setOperator(SLASHEQUALS) + .build(); + + String output = prettyPrinter.prettyprint(result).trim(); + + assertEquals("a/=b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testRegularAssignmentStarEqualsExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTAssignmentExpression result = AssignmentExpressionsMill.assignmentExpressionBuilder() + .setLeft(a.get()) + .setRight(b.get()) + .setOperator(STAREQUALS) + .build(); + + String output = prettyPrinter.prettyprint(result).trim(); + + assertEquals("a*=b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testRegularAssignmentPipeEqualsExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTAssignmentExpression result = AssignmentExpressionsMill.assignmentExpressionBuilder() + .setLeft(a.get()) + .setRight(b.get()) + .setOperator(PIPEEQUALS) + .build(); + + String output = prettyPrinter.prettyprint(result).trim(); + + assertEquals("a|=b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testRegularAssignmentLTLTEqualsExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTAssignmentExpression result = AssignmentExpressionsMill.assignmentExpressionBuilder() + .setLeft(a.get()) + .setRight(b.get()) + .setOperator(LTLTEQUALS) + .build(); + + String output = prettyPrinter.prettyprint(result).trim(); + + assertEquals("a<<=b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testRegularAssignmentGTGTEqualsExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTAssignmentExpression result = AssignmentExpressionsMill.assignmentExpressionBuilder() + .setLeft(a.get()) + .setRight(b.get()) + .setOperator(GTGTEQUALS) + .build(); + + String output = prettyPrinter.prettyprint(result).trim(); + + assertEquals("a>>=b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testRegularAssignmentGTGTGTEqualsExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTAssignmentExpression result = AssignmentExpressionsMill.assignmentExpressionBuilder() + .setLeft(a.get()) + .setRight(b.get()) + .setOperator(GTGTGTEQUALS) + .build(); + + String output = prettyPrinter.prettyprint(result).trim(); + + assertEquals("a>>>=b", output); + + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/expressions/prettyprint/BitExpressionsPrettyPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/expressions/prettyprint/BitExpressionsPrettyPrinterTest.java new file mode 100644 index 0000000000..f797522807 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/expressions/prettyprint/BitExpressionsPrettyPrinterTest.java @@ -0,0 +1,153 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.prettyprint; + +import de.monticore.expressions.bitexpressions._prettyprint.BitExpressionsFullPrettyPrinter; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.testbitexpressions.TestBitExpressionsMill; +import de.monticore.expressions.testbitexpressions._parser.TestBitExpressionsParser; +import de.monticore.prettyprint.IndentPrinter; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.*; + +public class BitExpressionsPrettyPrinterTest { + protected TestBitExpressionsParser parser; + protected BitExpressionsFullPrettyPrinter prettyPrinter; + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestBitExpressionsMill.reset(); + TestBitExpressionsMill.init(); + parser = new TestBitExpressionsParser(); + prettyPrinter = new BitExpressionsFullPrettyPrinter(new IndentPrinter()); + prettyPrinter.getPrinter().clearBuffer(); + } + + @Test + public void testLeftShiftExpression() throws IOException { + Optional result = parser.parse_StringExpression("a< result = parser.parse_StringExpression("a>>b"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + assertTrue(result.isPresent()); + + ASTExpression ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringExpression(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testLogicalRightShiftExpression() throws IOException { + Optional result = parser.parse_StringExpression("a>>>b"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + assertTrue(result.isPresent()); + + ASTExpression ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringExpression(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testBinaryOrOpExpression() throws IOException { + Optional result = parser.parse_StringExpression("a|b"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + assertTrue(result.isPresent()); + + ASTExpression ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringExpression(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testBinaryXorExpression() throws IOException { + Optional result = parser.parse_StringExpression("a^b"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + assertTrue(result.isPresent()); + + ASTExpression ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringExpression(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testBinaryAndExpression() throws IOException { + Optional result = parser.parse_StringExpression("a&b"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + assertTrue(result.isPresent()); + + ASTExpression ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringExpression(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/expressions/prettyprint/CommonExpressionsPrettyPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/expressions/prettyprint/CommonExpressionsPrettyPrinterTest.java new file mode 100644 index 0000000000..10fcae58e9 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/expressions/prettyprint/CommonExpressionsPrettyPrinterTest.java @@ -0,0 +1,464 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.prettyprint; + +import de.monticore.expressions.commonexpressions.CommonExpressionsMill; +import de.monticore.expressions.commonexpressions._ast.*; +import de.monticore.expressions.commonexpressions._prettyprint.CommonExpressionsFullPrettyPrinter; +import de.monticore.expressions.expressionsbasis._ast.ASTArguments; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.testcommonexpressions.TestCommonExpressionsMill; +import de.monticore.expressions.testcommonexpressions._parser.TestCommonExpressionsParser; +import de.monticore.prettyprint.IndentPrinter; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.*; + +public class CommonExpressionsPrettyPrinterTest { + + protected TestCommonExpressionsParser parser; + protected CommonExpressionsFullPrettyPrinter prettyPrinter; + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestCommonExpressionsMill.reset(); + TestCommonExpressionsMill.init(); + parser = new TestCommonExpressionsParser(); + prettyPrinter = new CommonExpressionsFullPrettyPrinter(new IndentPrinter()); + prettyPrinter.getPrinter().clearBuffer(); + } + + @Test + public void testMinusPrefixExpression() throws IOException { + Optional result = parser.parse_StringMinusPrefixExpression("-a"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTMinusPrefixExpression ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringMinusPrefixExpression(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testPlusPrefixExpression() throws IOException { + Optional result = parser.parse_StringPlusPrefixExpression("+a"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTPlusPrefixExpression ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringPlusPrefixExpression(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + @Test + public void testBooleanNotExpression() throws IOException { + Optional result = parser.parse_StringBooleanNotExpression("~a"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTBooleanNotExpression ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringBooleanNotExpression(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testLogicalNotExpression() throws IOException { + Optional result = parser.parse_StringLogicalNotExpression("!a"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTLogicalNotExpression ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringLogicalNotExpression(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void testBracketExpression() throws IOException { + Optional result = parser.parse_StringBracketExpression("(a == b)"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTBracketExpression ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringBracketExpression(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void testArguments() throws IOException { + Optional result = parser.parse_StringArguments("(a , b , c)"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTArguments ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringArguments(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCallExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional arguments = parser.parse_StringArguments("(b, c)"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(arguments.isPresent()); + ASTCallExpression result = CommonExpressionsMill.callExpressionBuilder() + .setExpression(a.get()) + .setArguments(arguments.get()) + .build(); + + String output = prettyPrinter.prettyprint(result); + + assertEquals("a(b,c)", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testFieldAccessExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + ASTFieldAccessExpression result = CommonExpressionsMill.fieldAccessExpressionBuilder() + .setExpression(a.get()) + .setName("foo") + .build(); + + String output = prettyPrinter.prettyprint(result); + + assertEquals("a.foo", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMultExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTMultExpression result = CommonExpressionsMill.multExpressionBuilder() + .setLeft(a.get()) + .setOperator("*") + .setRight(b.get()) + .build(); + + String output = prettyPrinter.prettyprint(result); + + assertEquals("a*b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testDivideExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTDivideExpression result = CommonExpressionsMill.divideExpressionBuilder() + .setLeft(a.get()) + .setOperator("/") + .setRight(b.get()) + .build(); + + String output = prettyPrinter.prettyprint(result); + + assertEquals("a/b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testModuloExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTModuloExpression result = CommonExpressionsMill.moduloExpressionBuilder() + .setLeft(a.get()) + .setOperator("%") + .setRight(b.get()) + .build(); + + String output = prettyPrinter.prettyprint(result); + + assertEquals("a%b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testPlusExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTPlusExpression result = CommonExpressionsMill.plusExpressionBuilder() + .setLeft(a.get()) + .setOperator("+") + .setRight(b.get()) + .build(); + + String output = prettyPrinter.prettyprint(result); + + assertEquals("a+b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMinusExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTMinusExpression result = CommonExpressionsMill.minusExpressionBuilder() + .setLeft(a.get()) + .setOperator("-") + .setRight(b.get()) + .build(); + + String output = prettyPrinter.prettyprint(result); + + assertEquals("a-b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testLessEqualExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTLessEqualExpression result = CommonExpressionsMill.lessEqualExpressionBuilder() + .setLeft(a.get()) + .setOperator("<=") + .setRight(b.get()) + .build(); + + String output = prettyPrinter.prettyprint(result); + + assertEquals("a<=b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testGreaterEqualExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTGreaterEqualExpression result = CommonExpressionsMill.greaterEqualExpressionBuilder() + .setLeft(a.get()) + .setOperator(">=") + .setRight(b.get()) + .build(); + + String output = prettyPrinter.prettyprint(result); + + assertEquals("a>=b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testLessThanExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTLessThanExpression result = CommonExpressionsMill.lessThanExpressionBuilder() + .setLeft(a.get()) + .setOperator("<") + .setRight(b.get()) + .build(); + + String output = prettyPrinter.prettyprint(result); + + assertEquals("a a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTGreaterThanExpression result = CommonExpressionsMill.greaterThanExpressionBuilder() + .setLeft(a.get()) + .setOperator(">") + .setRight(b.get()) + .build(); + + String output = prettyPrinter.prettyprint(result); + + assertEquals("a>b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testEqualsExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTEqualsExpression result = CommonExpressionsMill.equalsExpressionBuilder() + .setLeft(a.get()) + .setOperator("==") + .setRight(b.get()) + .build(); + + String output = prettyPrinter.prettyprint(result); + + assertEquals("a==b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testNotEqualsExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTNotEqualsExpression result = CommonExpressionsMill.notEqualsExpressionBuilder() + .setLeft(a.get()) + .setOperator("!=") + .setRight(b.get()) + .build(); + + String output = prettyPrinter.prettyprint(result); + + assertEquals("a!=b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testBooleanAndOpExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTBooleanAndOpExpression result = CommonExpressionsMill.booleanAndOpExpressionBuilder() + .setLeft(a.get()) + .setOperator("&&") + .setRight(b.get()) + .build(); + + String output = prettyPrinter.prettyprint(result); + + assertEquals("a&&b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testBooleanOrOpExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTBooleanOrOpExpression result = CommonExpressionsMill.booleanOrOpExpressionBuilder() + .setLeft(a.get()) + .setOperator("||") + .setRight(b.get()) + .build(); + + String output = prettyPrinter.prettyprint(result); + + assertEquals("a||b", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testConditionalExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + Optional c = parser.parse_StringExpression("c"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + assertTrue(c.isPresent()); + ASTConditionalExpression result = CommonExpressionsMill.conditionalExpressionBuilder() + .setCondition(a.get()) + .setTrueExpression(b.get()) + .setFalseExpression(c.get()) + .build(); + + String output = prettyPrinter.prettyprint(result); + + assertEquals("a ? b:c", output); + + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/expressions/prettyprint/JavaClassExpressionsPrettyPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/expressions/prettyprint/JavaClassExpressionsPrettyPrinterTest.java new file mode 100644 index 0000000000..43e740eca0 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/expressions/prettyprint/JavaClassExpressionsPrettyPrinterTest.java @@ -0,0 +1,290 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.prettyprint; + +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.javaclassexpressions.JavaClassExpressionsMill; +import de.monticore.expressions.javaclassexpressions._ast.*; +import de.monticore.expressions.javaclassexpressions._prettyprint.JavaClassExpressionsFullPrettyPrinter; +import de.monticore.expressions.testjavaclassexpressions.TestJavaClassExpressionsMill; +import de.monticore.expressions.testjavaclassexpressions._ast.ASTExtType; +import de.monticore.expressions.testjavaclassexpressions._parser.TestJavaClassExpressionsParser; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.*; + +public class JavaClassExpressionsPrettyPrinterTest { + + private TestJavaClassExpressionsParser parser = new TestJavaClassExpressionsParser(); + + private JavaClassExpressionsFullPrettyPrinter prettyPrinter= new JavaClassExpressionsFullPrettyPrinter(new IndentPrinter()); + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + JavaClassExpressionsMill.reset(); + JavaClassExpressionsMill.init(); + BasicSymbolsMill.initializePrimitives(); + prettyPrinter.getPrinter().clearBuffer(); + } + + + @Test + public void testPrimaryThisExpression() throws IOException { + Optional result = parser.parse_StringPrimaryThisExpression("this"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTPrimaryThisExpression ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringPrimaryThisExpression(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testPrimarySuperExpression() throws IOException { + Optional result = parser.parse_StringPrimarySuperExpression("super"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTPrimarySuperExpression ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringPrimarySuperExpression(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testTypeCaseExpression() throws IOException { + Optional result = parser.parse_StringTypeCastExpression("(Integer) a"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTTypeCastExpression ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + // does not print 'Integer' because functionality for type printing has to be added over delegation form + // prettyprinter of langauge that fills the external + String pattern = "^\\(.*\\)a$"; + boolean matches = output.matches(pattern); + assertEquals( matches, true); + } + + @Test + public void testClassExpression() throws IOException { + Optional result = parser.parse_StringClassExpression("Integer.class"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTClassExpression ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + // does not print 'Integer' because functionality for type printing has to be added over delegation form + // prettyprinter of langauge that fills the external + assertEquals(".class", output); + } + + @Test + public void testPrimaryGenericInvocationExpressionExpression() throws IOException { + Optional result = parser.parse_StringPrimaryGenericInvocationExpression(" super(a)"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTPrimaryGenericInvocationExpression ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + // does not print 'Integer' because functionality for type printing has to be added over delegation form + // prettyprinter of langauge that fills the external + assertEquals("<>super(a)", output); + } + + @Test + public void testInstanceofExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional type = parser.parse_StringExtType("Integer"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(type.isPresent()); + ASTInstanceofExpression result = JavaClassExpressionsMill.instanceofExpressionBuilder() + .setExpression(a.get()) + .setExtType(type.get()) + .build(); + String output = prettyPrinter.prettyprint(result); + + // does not print 'Integer' because functionality for type printing has to be added over delegation form + // prettyprinter of langauge that fills the external + assertEquals("a instanceof", output); + } + + @Test + public void testThisExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + ASTThisExpression result = JavaClassExpressionsMill.thisExpressionBuilder() + .setExpression(a.get()) + .build(); + + String output = prettyPrinter.prettyprint(result); + + assertEquals("a.this", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testArrayExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringExpression("b"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTArrayExpression result = JavaClassExpressionsMill.arrayExpressionBuilder() + .setExpression(a.get()) + .setIndexExpression(b.get()) + .build(); + + String output = prettyPrinter.prettyprint(result); + + assertEquals("a[b]", output); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testSuperExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringSuperSuffix("(b)"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTSuperExpression result = JavaClassExpressionsMill.superExpressionBuilder() + .setExpression(a.get()) + .setSuperSuffix(b.get()) + .build(); + + String output = prettyPrinter.prettyprint(result); + + assertEquals("a.super(b)", output); + + assertTrue(Log.getFindings().isEmpty()); + } + @Test + public void testGenericInvocationExpressionExpression() throws IOException { + Optional a = parser.parse_StringExpression("a"); + Optional b = parser.parse_StringPrimaryGenericInvocationExpression(" c(b)"); + assertFalse(parser.hasErrors()); + assertTrue(a.isPresent()); + assertTrue(b.isPresent()); + ASTGenericInvocationExpression result = JavaClassExpressionsMill.genericInvocationExpressionBuilder() + .setExpression(a.get()) + .setPrimaryGenericInvocationExpression(b.get()) + .build(); + + String output = prettyPrinter.prettyprint(result); + + // does not print 'd' because functionality for type printing has to be added over delegation form + // prettyprinter of langauge that fills the external + assertEquals("a.<>c(b)", output); + } + + @Test + public void testGenericInvocationSuffixThis() throws IOException { + Optional result = parser.parse_StringGenericInvocationSuffix("this(a)"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTGenericInvocationSuffix ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringGenericInvocationSuffix(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testGenericInvocationSuffixSuper() throws IOException { + Optional result = parser.parse_StringGenericInvocationSuffix("super(b)"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTGenericInvocationSuffix ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringGenericInvocationSuffix(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testGenericInvocationSuffixSimple() throws IOException { + Optional result = parser.parse_StringGenericInvocationSuffix("a(b)"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTGenericInvocationSuffix ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringGenericInvocationSuffix(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCreatorExpression() throws IOException { + Optional result = parser.parse_StringCreatorExpression("new Integer(a,b)"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTCreatorExpression ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + // does not print 'Integer' because functionality for type printing has to be added over delegation from + // prettyprinter of language that fills the external + assertEquals("new(a,b)", output); + } + + @Test + public void testCreatorExpression2() throws IOException { + Optional result = parser.parse_StringCreatorExpression("new double[a][]"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTCreatorExpression ast = result.get(); + String output = prettyPrinter.prettyprint(ast); + // does not print 'double' because functionality for type printing has to be added over delegation from + // prettyprinter of language that fills the external + assertEquals("new[a][]", output); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/expressions/prettyprint/LambdaExpressionsPrettyPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/expressions/prettyprint/LambdaExpressionsPrettyPrinterTest.java new file mode 100644 index 0000000000..c180124bcf --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/expressions/prettyprint/LambdaExpressionsPrettyPrinterTest.java @@ -0,0 +1,140 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.prettyprint; + +import de.monticore.expressions.lambdaexpressions._ast.ASTLambdaExpression; +import de.monticore.expressions.lambdaexpressions._prettyprint.LambdaExpressionsFullPrettyPrinter; +import de.monticore.expressions.testlambdaexpressions.TestLambdaExpressionsMill; +import de.monticore.expressions.testlambdaexpressions._parser.TestLambdaExpressionsParser; +import de.monticore.prettyprint.IndentPrinter; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; +import java.util.regex.Pattern; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class LambdaExpressionsPrettyPrinterTest { + + protected TestLambdaExpressionsParser parser; + protected LambdaExpressionsFullPrettyPrinter prettyPrinter; + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestLambdaExpressionsMill.reset(); + TestLambdaExpressionsMill.init(); + parser = new TestLambdaExpressionsParser(); + prettyPrinter = + new LambdaExpressionsFullPrettyPrinter(new IndentPrinter()); + prettyPrinter.getPrinter().clearBuffer(); + } + + @Test + public void testLambdaWithoutParameter() throws IOException { + testLambdaExpression("() -> a"); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testLambdaWithoutTypeWithoutParenthesis() throws IOException { + testLambdaExpression("a -> a"); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testLambdaWithoutTypeWithParenthesis() throws IOException { + testLambdaExpression("(a) -> a"); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testLambdaWithType() throws IOException { + ASTLambdaExpression ast = parseLambdaExpression("(int a) -> a"); + String output = prettyPrinter.prettyprint(ast); + // does not print 'int' because functionality for type printing has to be added + // over delegation from prettyprinter of language that fills the external + // pattern matches e.g. "(name a) -> a" and "( a)->a" + Pattern pattern = Pattern.compile("" + // "(" + + "\\(\\p{Space}*" + // "name " or " " + + "\\p{Alpha}*\\p{Space}+" + // "a" + + "a\\p{Space}*" + // ")" + + "\\)\\p{Space}*" + // "->" + + "->\\p{Space}*" + // "a" + + "a" + ); + assertTrue(pattern.asPredicate().test(output)); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testLambdaMultipeParametersWithoutType() throws IOException { + testLambdaExpression("(a, b) -> a"); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testLambdaMultipeParametersWithType() throws IOException { + ASTLambdaExpression ast = parseLambdaExpression("(int a, int b) -> a"); + String output = prettyPrinter.prettyprint(ast); + // does not print 'int' because functionality for type printing has to be added + // over delegation from prettyprinter of language that fills the external + // pattern matches e.g. "(name a, name2 b) -> a" and "( a, b)->a" + Pattern pattern = Pattern.compile("" + // "(" + + "\\(\\p{Space}*" + // "name " or " " + + "\\p{Alpha}*\\p{Space}+" + // "a" + + "a\\p{Space}*" + // "," + + ",\\p{Space}*" + // "name2 " or " " + + "\\p{Alpha}*\\p{Space}+" + // "b" + + "b\\p{Space}*" + // ")" + + "\\)\\p{Space}*" + // "->" + + "->\\p{Space}*" + // "a" + + "a" + ); + assertTrue(pattern.asPredicate().test(output)); + + assertTrue(Log.getFindings().isEmpty()); + } + + public void testLambdaExpression(String exp) throws IOException { + ASTLambdaExpression ast = parseLambdaExpression(exp); + String output = prettyPrinter.prettyprint(ast); + ASTLambdaExpression ast2 = parseLambdaExpression(output); + assertTrue("Parse equals: " + exp + " vs " + output, ast.deepEquals(ast2)); + + assertTrue(Log.getFindings().isEmpty()); + } + + public ASTLambdaExpression parseLambdaExpression(String exp) throws IOException { + Optional result = parser.parse_StringLambdaExpression(exp); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + return result.get(); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/expressions/prettyprint/StreamExpressionsPrettyPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/expressions/prettyprint/StreamExpressionsPrettyPrinterTest.java new file mode 100644 index 0000000000..38cf5f64f2 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/expressions/prettyprint/StreamExpressionsPrettyPrinterTest.java @@ -0,0 +1,113 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.expressions.prettyprint; + +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.streamexpressions._ast.*; +import de.monticore.expressions.streamexpressions._prettyprint.StreamExpressionsFullPrettyPrinter; +import de.monticore.expressions.teststreamexpressions.TestStreamExpressionsMill; +import de.monticore.expressions.teststreamexpressions._parser.TestStreamExpressionsParser; +import de.monticore.prettyprint.IndentPrinter; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class StreamExpressionsPrettyPrinterTest { + + protected TestStreamExpressionsParser parser; + protected StreamExpressionsFullPrettyPrinter prettyPrinter; + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestStreamExpressionsMill.reset(); + TestStreamExpressionsMill.init(); + parser = TestStreamExpressionsMill.parser(); + prettyPrinter = + new StreamExpressionsFullPrettyPrinter(new IndentPrinter()); + prettyPrinter.getPrinter().clearBuffer(); + } + + @Test + public void testEmptyStream() throws IOException { + Optional result = parser.parse_StringEmptyStreamExpression("<>"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTEmptyStreamExpression ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringEmptyStreamExpression(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testAppendStream() throws IOException { + Optional result = parser.parse_StringExpression("stream1 : stream2"); + + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTExpression ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringExpression(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testConcatStream() throws IOException { + Optional result = parser.parse_StringExpression("stream1 ^^ stream2"); + + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTExpression ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringExpression(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testLengthStream() throws IOException { + Optional result = parser.parse_StringLengthStreamExpression("#stream"); + + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTLengthStreamExpression ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringLengthStreamExpression(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/GrammarGlobalScopeTestFactory.java b/monticore-grammar/src/test/java/de/monticore/grammar/GrammarGlobalScopeTestFactory.java new file mode 100644 index 0000000000..11ade9e768 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/GrammarGlobalScopeTestFactory.java @@ -0,0 +1,25 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar; + + +import de.monticore.grammar.grammar_withconcepts.Grammar_WithConceptsMill; +import de.monticore.grammar.grammar_withconcepts._symboltable.Grammar_WithConceptsGlobalScope; +import de.monticore.grammar.grammar_withconcepts._symboltable.IGrammar_WithConceptsGlobalScope; + +import java.nio.file.Paths; + +public class GrammarGlobalScopeTestFactory { + + + public static Grammar_WithConceptsGlobalScope create() { + IGrammar_WithConceptsGlobalScope scope = Grammar_WithConceptsMill.globalScope(); + // reset global scope + scope.clear(); + scope.setFileExt("mc4"); + scope.getSymbolPath().addEntry(Paths.get("src/test/resources")); + scope.getSymbolPath().addEntry(Paths.get("target/test/resources")); + return (Grammar_WithConceptsGlobalScope) scope; + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/MCGrammarParserTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/MCGrammarParserTest.java new file mode 100644 index 0000000000..215a003dda --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/MCGrammarParserTest.java @@ -0,0 +1,223 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar; + +import de.monticore.grammar.grammar._ast.*; +import de.monticore.grammar.grammar_withconcepts.Grammar_WithConceptsMill; +import de.monticore.grammar.grammar_withconcepts._parser.GrammarTransformer; +import de.monticore.grammar.grammar_withconcepts._parser.Grammar_WithConceptsParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.io.StringReader; +import java.util.Optional; + +import static org.junit.Assert.*; + +public class MCGrammarParserTest { + + @Before + public void before() { + LogStub.init(); + Log.enableFailQuick(false); + Grammar_WithConceptsMill.reset(); + Grammar_WithConceptsMill.init(); + } + + @Test + public void testParse() throws IOException { + String model = "src/test/resources/de/monticore/Statechart.mc4"; + + Grammar_WithConceptsParser parser = + Grammar_WithConceptsMill.parser(); + Optional result = parser.parse(model); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTMCGrammar grammar = result.get(); + assertEquals("Statechart", grammar.getName()); + assertEquals(7, grammar.getClassProdList().size()); + assertEquals(3, grammar.getExternalProdList().size()); + assertEquals(1, grammar.getInterfaceProdList().size()); + GrammarTransformer.transform(grammar); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testASTRule() throws IOException { + String str; + + str = "astrule MCGrammar = GrammarOption max=1 ;"; + Grammar_WithConceptsParser parser = new Grammar_WithConceptsParser(); + Optional result = parser.parseASTRule(new StringReader(str)); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + str = " astrule State = method public String getName(){ return \"\";};"; + result = parser.parseASTRule(new StringReader(str)); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(Log.getFindings().isEmpty()); + + } + + @Test + public void testSematicPred() throws IOException { + String str = "{(0 != cmpCounter)}?"; + + Grammar_WithConceptsParser parser = new Grammar_WithConceptsParser(); + Optional result = parser.parseSemanticpredicateOrAction(new StringReader(str)); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testScript() throws IOException { + String model = "src/test/resources/de/monticore/script/ScriptExample.mc4"; + + Grammar_WithConceptsParser parser = new Grammar_WithConceptsParser(); + Optional result = parser.parse(model); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testAutomatonV1() throws IOException { + String model = "src/test/resources/de/monticore/script/AutomatonV1.mc4"; + + Grammar_WithConceptsParser parser = new Grammar_WithConceptsParser(); + Optional result = parser.parse(model); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testAutomatonV2() throws IOException { + String model = "src/test/resources/de/monticore/script/AutomatonV2.mc4"; + + Grammar_WithConceptsParser parser = new Grammar_WithConceptsParser(); + Optional result = parser.parse(model); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testAutomatonV3() throws IOException { + String model = "src/test/resources/de/monticore/script/AutomatonV3.mc4"; + + Grammar_WithConceptsParser parser = new Grammar_WithConceptsParser(); + Optional result = parser.parse(model); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + } + + @Test + public void testHierarchicalAutomaton() throws IOException { + String model = "src/test/resources/de/monticore/script/HierarchicalAutomaton.mc4"; + + Grammar_WithConceptsParser parser = new Grammar_WithConceptsParser(); + Optional result = parser.parse(model); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testAutomatonWithInvsComp() throws IOException { + String model = "src/test/resources/de/monticore/script/AutomatonWithInvsComp.mc4"; + + Grammar_WithConceptsParser parser = new Grammar_WithConceptsParser(); + Optional result = parser.parse(model); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertEquals(1, Log.getFindings().size()); + assertEquals("0xA4003 The grammar name InvAutomaton must be identical to the file name" + + " AutomatonWithInvsComp of the grammar (without its file extension).", Log.getFindings().get(0).getMsg()); + + Log.getFindings().clear(); + } + + @Test + public void testAutomatonWithInvs() throws IOException { + String model = "src/test/resources/de/monticore/script/AutomatonWithInvs.mc4"; + + Grammar_WithConceptsParser parser = new Grammar_WithConceptsParser(); + Optional result = parser.parse(model); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + } + + @Test + public void testAutomatonWithInvsAndStartRule() throws IOException { + String model = "src/test/resources/de/monticore/script/AutomatonWithInvsAndStartRule.mc4"; + + Grammar_WithConceptsParser parser = new Grammar_WithConceptsParser(); + Optional result = parser.parse(model); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + } + + @Test + public void testGrammarSymbolTableInfo() throws IOException { + String model = "src/test/resources/Automaton.mc4"; + + Grammar_WithConceptsParser parser = new Grammar_WithConceptsParser(); + Optional result = parser.parse(model); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + ASTMCGrammar grammar = result.get(); + assertEquals(4, grammar.getClassProdList().size()); + + ASTClassProd transition = grammar.getClassProdList().get(2); + ASTNonTerminal fromState = (ASTNonTerminal) transition.getAltList().get(0).getComponentList().get(0); + assertTrue(fromState.isPresentReferencedSymbol()); + assertEquals("State", fromState.getReferencedSymbol()); + + ASTNonTerminal toState = (ASTNonTerminal) transition.getAltList().get(0).getComponentList().get(0); + assertTrue(toState.isPresentReferencedSymbol()); + assertEquals("State", toState.getReferencedSymbol()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testPackageNameWithPointsDefined() throws IOException { + String model = "src/test/resources/de/monticore/point.in.packagename/PackagePathTest.mc4"; + + Grammar_WithConceptsParser parser = new Grammar_WithConceptsParser(); + parser.parse(model); + assertEquals(1, Log.getFindings().size()); + assertEquals("0xA4004 The package declaration point.in.packagename of the grammar must not differ from " + + "the package of the grammar file.", Log.getFindings().get(0).getMsg()); + + Log.getFindings().clear(); + } + + @Test + public void testPackageWrongPackageDefined() throws IOException { + String model = "src/test/resources/de/monticore/WrongPackage.mc4"; + + Grammar_WithConceptsParser parser = new Grammar_WithConceptsParser(); + parser.parse(model); + assertEquals(1, Log.getFindings().size()); + assertEquals("0xA4004 The package declaration de.ronticore of the grammar " + + "must not differ from the package of the grammar file.", Log.getFindings().get(0).getMsg()); + + Log.getFindings().clear(); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/MCGrammarPrettyPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/MCGrammarPrettyPrinterTest.java new file mode 100644 index 0000000000..bc5d0b2bef --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/MCGrammarPrettyPrinterTest.java @@ -0,0 +1,164 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar; + +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar_withconcepts.Grammar_WithConceptsMill; +import de.monticore.grammar.grammar_withconcepts._parser.Grammar_WithConceptsParser; +import de.monticore.grammar.grammar_withconcepts._prettyprint.Grammar_WithConceptsFullPrettyPrinter; +import de.monticore.prettyprint.IndentPrinter; +import de.se_rwth.commons.logging.Finding; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.io.StringReader; +import java.util.Optional; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class MCGrammarPrettyPrinterTest { + + @Before + public void before() { + LogStub.init(); + Log.enableFailQuick(false); + Grammar_WithConceptsMill.reset(); + Grammar_WithConceptsMill.init(); + } + + @Test + // Test simple grammar + public void testStatechart() throws IOException { + String model = "src/test/resources/de/monticore/Statechart.mc4"; + + // Parsing input + Grammar_WithConceptsParser parser = new Grammar_WithConceptsParser(); + Optional result = parser.parseMCGrammar(model); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTMCGrammar grammar = result.get(); + + // Prettyprinting input + Grammar_WithConceptsFullPrettyPrinter prettyPrinter = new Grammar_WithConceptsFullPrettyPrinter(new IndentPrinter()); + String output = prettyPrinter.prettyprint(grammar); + + // Parsing printed input + result = parser.parseMCGrammar(new StringReader (output)); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue("Failed to deep equals: \n" + output, grammar.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + // Test grammar with symbols and scopes + public void testAutomaton() throws IOException { + String model = "src/test/resources/Automaton.mc4"; + + // Parsing input + Grammar_WithConceptsParser parser = new Grammar_WithConceptsParser(); + Optional result = parser.parseMCGrammar(model); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTMCGrammar grammar = result.get(); + + // Prettyprinting input + Grammar_WithConceptsFullPrettyPrinter prettyPrinter = new Grammar_WithConceptsFullPrettyPrinter(new IndentPrinter()); + String output = prettyPrinter.prettyprint(grammar); + + // Parsing printed input + result = parser.parseMCGrammar(new StringReader(output)); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue("Failed to deep equals: \n" + output, grammar.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + // Test grammar with symbols and scopes + public void testGrammar() throws IOException { + String model = "src/test/resources/de/monticore/TestGrammar.mc4"; + + // Parsing input + Grammar_WithConceptsParser parser = new Grammar_WithConceptsParser(); + Optional result = parser.parseMCGrammar(model); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTMCGrammar grammar = result.get(); + + // Prettyprinting input + Grammar_WithConceptsFullPrettyPrinter prettyPrinter = new Grammar_WithConceptsFullPrettyPrinter(new IndentPrinter()); + String output = prettyPrinter.prettyprint(grammar); + + // Parsing printed input + result = parser.parseMCGrammar(new StringReader(output)); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue("Failed to deep equals: \n" + output, grammar.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + // test lexicals with lexer commands and end actions + public void testLexicals() throws IOException { + String model = "src/test/resources/de/monticore/common/TestLexicals.mc4"; + + // Parsing input + Grammar_WithConceptsParser parser = new Grammar_WithConceptsParser(); + Optional result = parser.parseMCGrammar(model); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTMCGrammar grammar = result.get(); + + // Prettyprinting input + Grammar_WithConceptsFullPrettyPrinter prettyPrinter = new Grammar_WithConceptsFullPrettyPrinter(new IndentPrinter()); + String output = prettyPrinter.prettyprint(grammar); + + // Parsing printed input + result = parser.parseMCGrammar(new StringReader(output)); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(grammar.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + // test annotations + public void testAnnotations() throws IOException { + String model = "src/test/resources/de/monticore/Annotations.mc4"; + + // Parsing input + Grammar_WithConceptsParser parser = new Grammar_WithConceptsParser(); + Optional result = parser.parseMCGrammar(model); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTMCGrammar grammar = result.get(); + + // Prettyprinting input + Grammar_WithConceptsFullPrettyPrinter prettyPrinter = new Grammar_WithConceptsFullPrettyPrinter(new IndentPrinter()); + String output = prettyPrinter.prettyprint(grammar); + + // Parsing printed input + result = parser.parseMCGrammar(new StringReader(output)); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue("Failed to deep equals: \n" + output, grammar.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ASTRuleAndNTUseSameAttrNameForDiffNTsTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ASTRuleAndNTUseSameAttrNameForDiffNTsTest.java new file mode 100644 index 0000000000..aa8a109b23 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ASTRuleAndNTUseSameAttrNameForDiffNTsTest.java @@ -0,0 +1,30 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class ASTRuleAndNTUseSameAttrNameForDiffNTsTest extends CocoTest{ + private final String grammar = "de.monticore.grammar.cocos.invalid.A4028.A4028"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new ASTRuleAndNTUseSameAttrNameForDiffNTs()); + } + + @Test + public void testInvalid() { + testInvalidGrammar(grammar, ASTRuleAndNTUseSameAttrNameForDiffNTs.ERROR_CODE, + String.format(ASTRuleAndNTUseSameAttrNameForDiffNTs.ERROR_MSG_FORMAT, "E", "a", "B", "A"), + checker); + } + + @Test + public void testCorrect(){ + testValidGrammar("de.monticore.grammar.cocos.valid.ASTRules", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/AbstractNTNotExtendInterfaceOrExternalNTsTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/AbstractNTNotExtendInterfaceOrExternalNTsTest.java new file mode 100644 index 0000000000..e4cefcb9cd --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/AbstractNTNotExtendInterfaceOrExternalNTsTest.java @@ -0,0 +1,37 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class AbstractNTNotExtendInterfaceOrExternalNTsTest extends CocoTest { + + private final String grammar = "de.monticore.grammar.cocos.invalid.A2107.A2107"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new AbstractNTNotExtendInterfaceOrExternalNTs()); + } + + @Test + public void testExtendInterfaceNT() { + testInvalidGrammar(grammar + "a", AbstractNTNotExtendInterfaceOrExternalNTs.ERROR_CODE, + String.format(AbstractNTNotExtendInterfaceOrExternalNTs.ERROR_MSG_FORMAT, "B", "interface", + "A"), checker); + } + + @Test + public void testExtendExternalNT() { + testInvalidGrammar(grammar + "b", AbstractNTNotExtendInterfaceOrExternalNTs.ERROR_CODE, + String.format(AbstractNTNotExtendInterfaceOrExternalNTs.ERROR_MSG_FORMAT, "B", "external", + "A"), checker); + } + + @Test + public void testExtendNT() { + testValidGrammar("de.monticore.grammar.cocos.valid.ExtendNTs", checker); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/AbstractNTOnlyExtendsOneNTOrClassTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/AbstractNTOnlyExtendsOneNTOrClassTest.java new file mode 100644 index 0000000000..1ffcbf28b6 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/AbstractNTOnlyExtendsOneNTOrClassTest.java @@ -0,0 +1,35 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class AbstractNTOnlyExtendsOneNTOrClassTest extends CocoTest{ + private final String grammar = "de.monticore.grammar.cocos.invalid.A4012.A4012"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new AbstractNTOnlyExtendsOneNTOrClass()); + } + + @Test + public void testExtendMultiple() { + testInvalidGrammar(grammar + "a", AbstractNTOnlyExtendsOneNTOrClass.ERROR_CODE, + String.format(AbstractNTOnlyExtendsOneNTOrClass.ERROR_MSG_FORMAT, "C", "extend", "nonterminal"), checker); + } + + @Test + public void testAstExtendMultiple() { + testInvalidGrammar(grammar + "b", AbstractNTOnlyExtendsOneNTOrClass.ERROR_CODE, + String.format(AbstractNTOnlyExtendsOneNTOrClass.ERROR_MSG_FORMAT, "A", "astextend", "class"), checker); + } + + @Test + public void testExtendNT(){ + testValidGrammar("de.monticore.grammar.cocos.valid.ExtendNTs", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/AbstractNTOnlyExtendsOrAstextendsNTOrClassTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/AbstractNTOnlyExtendsOrAstextendsNTOrClassTest.java new file mode 100644 index 0000000000..3e3f6adacb --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/AbstractNTOnlyExtendsOrAstextendsNTOrClassTest.java @@ -0,0 +1,31 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class AbstractNTOnlyExtendsOrAstextendsNTOrClassTest extends CocoTest{ + + private final String grammar = "de.monticore.grammar.cocos.invalid.A4030.A4030"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new AbstractNTOnlyExtendOrAstextendNTOrClass()); + } + + @Test + public void testASTExtendMultiple() { + testInvalidGrammar(grammar, AbstractNTOnlyExtendOrAstextendNTOrClass.ERROR_CODE, + String.format(AbstractNTOnlyExtendOrAstextendNTOrClass.ERROR_MSG_FORMAT, "B"), + checker); + } + + @Test + public void testExtendNT(){ + testValidGrammar("de.monticore.grammar.cocos.valid.ExtendNTs", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/AbstractNTOnlyImplementInterfaceNTsTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/AbstractNTOnlyImplementInterfaceNTsTest.java new file mode 100644 index 0000000000..7df9bcafb4 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/AbstractNTOnlyImplementInterfaceNTsTest.java @@ -0,0 +1,52 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class AbstractNTOnlyImplementInterfaceNTsTest extends CocoTest { + private final String MESSAGE = String.format( + AbstractNTOnlyImplementInterfaceNTs.ERROR_MSG_FORMAT, "B", "A"); + + private final String grammar = "de.monticore.grammar.cocos.invalid.A2106.A2106"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new AbstractNTOnlyImplementInterfaceNTs()); + } + + @Test + public void testImplementsNormalNT() { + testInvalidGrammar(grammar + "a", AbstractNTOnlyImplementInterfaceNTs.ERROR_CODE, MESSAGE, + checker); + } + + @Test + public void testImplementsExternalNT() { + testInvalidGrammar(grammar + "b", AbstractNTOnlyImplementInterfaceNTs.ERROR_CODE, MESSAGE, + checker); + } + + @Test + public void testImplementsAbstractNT() { + testInvalidGrammar(grammar + "c", AbstractNTOnlyImplementInterfaceNTs.ERROR_CODE, MESSAGE, + checker); + } + + @Test + public void testImplementsEnumNT() { + testInvalidGrammar(grammar + "d", AbstractNTOnlyImplementInterfaceNTs.ERROR_CODE, MESSAGE, + checker); + } + + @Test + public void testImplementsInterfaceNT(){ + testValidGrammar("de.monticore.grammar.cocos.valid.ImplementInterfaceNTs", checker); + } + + + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/AbstractNTWithoutExtensionOnlyInComponentGrammarTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/AbstractNTWithoutExtensionOnlyInComponentGrammarTest.java new file mode 100644 index 0000000000..d2ba9d6905 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/AbstractNTWithoutExtensionOnlyInComponentGrammarTest.java @@ -0,0 +1,46 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + + +public class AbstractNTWithoutExtensionOnlyInComponentGrammarTest extends CocoTest{ + private final String grammar = "de.monticore.grammar.cocos.invalid.A0277.A0277"; + private final String grammar2 = "de.monticore.grammar.cocos.invalid.A0277.A0277b"; + private final String grammar3 = "de.monticore.grammar.cocos.invalid.A0277.A0277c"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new AbstractNTWithoutExtensionOnlyInComponentGrammar()); + } + + @Test + public void testInvalid() { + testInvalidGrammar(grammar, AbstractNTWithoutExtensionOnlyInComponentGrammar.ERROR_CODE, + String.format(AbstractNTWithoutExtensionOnlyInComponentGrammar.ERROR_MSG_FORMAT, "A"), + checker); + } + + @Test + public void testInvalid2() { + testInvalidGrammar(grammar2, AbstractNTWithoutExtensionOnlyInComponentGrammar.ERROR_CODE, + String.format(AbstractNTWithoutExtensionOnlyInComponentGrammar.ERROR_MSG_FORMAT, "A"), checker); + } + + @Test + public void testInvalid3() { + testInvalidGrammar(grammar3, AbstractNTWithoutExtensionOnlyInComponentGrammar.ERROR_CODE, + String.format(AbstractNTWithoutExtensionOnlyInComponentGrammar.ERROR_MSG_FORMAT, "A"), checker); + } + + @Test + public void testCorrect(){ + testValidGrammar("de.monticore.grammar.cocos.valid.Component", checker); + } + + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/AttributeNameLowerCaseTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/AttributeNameLowerCaseTest.java new file mode 100644 index 0000000000..b6f7ae87c9 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/AttributeNameLowerCaseTest.java @@ -0,0 +1,32 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class AttributeNameLowerCaseTest extends CocoTest{ + + private final String MESSAGE = " The name C used for the nonterminal A referenced by the production B " + + "should start with a lower-case letter."; + private final String grammar = "de.monticore.grammar.cocos.invalid.A4005.A4005"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new AttributeNameLowerCase()); + } + + @Test + public void testUpperCasedAttribute() { + testInvalidGrammar(grammar, AttributeNameLowerCase.ERROR_CODE, MESSAGE, checker); + } + + + @Test + public void testAttributes(){ + testValidGrammar("de.monticore.grammar.cocos.valid.Attributes", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/CocoTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/CocoTest.java new file mode 100644 index 0000000000..5a44e8e49f --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/CocoTest.java @@ -0,0 +1,88 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.GrammarGlobalScopeTestFactory; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar_withconcepts.Grammar_WithConceptsMill; +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import de.monticore.grammar.grammar_withconcepts._symboltable.Grammar_WithConceptsGlobalScope; +import de.se_rwth.commons.logging.Finding; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; + +import static org.junit.Assert.*; + +public abstract class CocoTest { + + protected Grammar_WithConceptsCoCoChecker checker; + + @Before + public void setup(){ + LogStub.init(); + Log.enableFailQuick(false); + Grammar_WithConceptsMill.reset(); + Grammar_WithConceptsMill.init(); + } + + protected void testValidGrammar(String grammar, Grammar_WithConceptsCoCoChecker checker) { + final Grammar_WithConceptsGlobalScope globalScope = GrammarGlobalScopeTestFactory.create(); + + // test grammar symbol + final MCGrammarSymbol grammarSymbol = globalScope + .resolveMCGrammar(grammar) + .orElse(null); + assertNotNull(grammarSymbol); + assertTrue(grammarSymbol.getAstGrammar().isPresent()); + + Log.getFindings().clear(); + checker.checkAll(grammarSymbol.getAstGrammar().get()); + + assertTrue(Log.getFindings().isEmpty()); + } + + protected void testInvalidGrammar(String grammar, String code, String message, + Grammar_WithConceptsCoCoChecker checker) { + testInvalidGrammar(grammar, code, message, checker, 1); + } + + protected void testInvalidGrammar(String grammar, String code, String message, + Grammar_WithConceptsCoCoChecker checker, int numberOfFindings) { + final Grammar_WithConceptsGlobalScope globalScope = GrammarGlobalScopeTestFactory.create(); + + // test grammar symbol + final MCGrammarSymbol grammarSymbol = globalScope + .resolveMCGrammar(grammar) + .orElse(null); + assertNotNull(grammarSymbol); + assertTrue(grammarSymbol.getAstGrammar().isPresent()); + + Log.getFindings().clear(); + checker.checkAll(grammarSymbol.getAstGrammar().get()); + + assertFalse(Log.getFindings().isEmpty()); + assertEquals(numberOfFindings, Log.getFindings().size()); + for (Finding f : Log.getFindings()) { + assertEquals(code + message, f.getMsg()); + } + } + + protected void testInvalidGrammarKeepFindings(String grammar, String code, String message, + Grammar_WithConceptsCoCoChecker checker) { + final Grammar_WithConceptsGlobalScope globalScope = GrammarGlobalScopeTestFactory.create(); + + // test grammar symbol + final MCGrammarSymbol grammarSymbol = globalScope + .resolveMCGrammar(grammar) + .orElse(null); + assertNotNull(grammarSymbol); + assertTrue(grammarSymbol.getAstGrammar().isPresent()); + checker.checkAll(grammarSymbol.getAstGrammar().get()); + assertFalse(Log.getFindings().isEmpty()); + assertEquals(1, Log.getFindings().size()); + for (Finding f : Log.getFindings()) { + assertEquals(code + message, f.getMsg()); + } + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ConservativeExtensionCheckTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ConservativeExtensionCheckTest.java new file mode 100644 index 0000000000..2f70eef052 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ConservativeExtensionCheckTest.java @@ -0,0 +1,64 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class ConservativeExtensionCheckTest extends CocoTest { + private final String grammar = "de.monticore.grammar.cocos.invalid.A2007.A2007Sup"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new ConservativeExtensionCheck()); + } + + @Test + public void testInvalid1() { + testInvalidGrammar(grammar +"1", ConservativeExtensionCheck.ERROR_CODE, + String.format(ConservativeExtensionCheck.ERROR_MSG_FORMAT, "A", "de.monticore.grammar.cocos.invalid.A2007.A2007Super.M", "name"), checker); + } + + @Test + public void testInvalid2() { + testInvalidGrammar(grammar +"2", ConservativeExtensionCheck.ERROR_CODE, + String.format(ConservativeExtensionCheck.ERROR_MSG_FORMAT, "M", "de.monticore.grammar.cocos.invalid.A2007.A2007Super.M", "name"), checker); + } + + @Test + public void testInvalid3() { + testInvalidGrammar(grammar +"3", ConservativeExtensionCheck.ERROR_CODE, + String.format(ConservativeExtensionCheck.ERROR_MSG_FORMAT, "A", "de.monticore.grammar.cocos.invalid.A2007.A2007Super.M", "name"), checker); + } + + @Test + public void testInvalid4() { + testInvalidGrammar(grammar +"4", ConservativeExtensionCheck.ERROR_CODE, + String.format(ConservativeExtensionCheck.ERROR_MSG_FORMAT, "M", "de.monticore.grammar.cocos.invalid.A2007.A2007Super.M", "name"), checker); + } + + @Test + public void testInvalid5() { + testInvalidGrammar(grammar +"5", ConservativeExtensionCheck.ERROR_CODE, + String.format(ConservativeExtensionCheck.ERROR_MSG_FORMAT, "N", "de.monticore.grammar.cocos.invalid.A2007.A2007Super.N", "m"), checker); + } + + @Test + public void testInvalid6() { + testInvalidGrammar(grammar +"6", ConservativeExtensionCheck.ERROR_CODE, + String.format(ConservativeExtensionCheck.ERROR_MSG_FORMAT, "A", "de.monticore.grammar.cocos.invalid.A2007.A2007Super.M", "name"), checker); + } + + @Test + public void testInvalid7() { + testInvalidGrammar(grammar +"7", ConservativeExtensionCheck.ERROR_CODE, + String.format(ConservativeExtensionCheck.ERROR_MSG_FORMAT, "M", "de.monticore.grammar.cocos.invalid.A2007.A2007Super.M", "name"), checker); + } + + @Test + public void testCorrect(){ + testValidGrammar("de.monticore.grammar.cocos.valid.ConservativeExtensionSup", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/DerivedAndManualListNameTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/DerivedAndManualListNameTest.java new file mode 100644 index 0000000000..dc651f6cef --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/DerivedAndManualListNameTest.java @@ -0,0 +1,45 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; +public class DerivedAndManualListNameTest extends CocoTest{ + private final String grammar = "de.monticore.grammar.cocos.invalid.A2008.A2008"; + + @Before + public void disableFailQuick() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new DerivedAndManualListName()); + } + + @Test + public void testInvalidA() { + testInvalidGrammar(grammar +"a", DerivedAndManualListName.ERROR_CODE, + String.format(DerivedAndManualListName.ERROR_MSG_FORMAT, "B", "as"), checker); + } + + + @Test + public void testInvalidB() { + testInvalidGrammar(grammar +"b", DerivedAndManualListName.ERROR_CODE, + String.format(DerivedAndManualListName.ERROR_MSG_FORMAT, "B", "as"), checker); + } + + @Test + public void testInvalidC() { + testInvalidGrammar(grammar +"c", DerivedAndManualListName.ERROR_CODE, + String.format(DerivedAndManualListName.ERROR_MSG_FORMAT, "B", "names"), checker); + } + + @Test + public void testCorrectAttributes(){ + testValidGrammar("de.monticore.grammar.cocos.valid.Attributes", checker); + } + + @Test + public void testCorrectASTRules(){ + testValidGrammar("de.monticore.grammar.cocos.valid.ASTRules", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/DuplicatedEnumConstantTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/DuplicatedEnumConstantTest.java new file mode 100644 index 0000000000..734d90efa2 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/DuplicatedEnumConstantTest.java @@ -0,0 +1,32 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class DuplicatedEnumConstantTest extends CocoTest{ + + private final String MESSAGE = " Duplicate enum constant: a."; + public static final String HINT = "\nHint: The constants of enumerations must be unique within an enumeration."; + private final String grammar = "de.monticore.grammar.cocos.invalid.A4014.A4014"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new DuplicatedEnumConstant()); + } + + @Test + public void testInvalid(){ + testInvalidGrammar(grammar, DuplicatedEnumConstant.ERROR_CODE, MESSAGE+HINT, checker); + } + + + @Test + public void testCorrect(){ + testValidGrammar("de.monticore.grammar.cocos.valid.Attributes", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/DuplicatedSymbolDefinitionInProdTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/DuplicatedSymbolDefinitionInProdTest.java new file mode 100644 index 0000000000..e4204d6368 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/DuplicatedSymbolDefinitionInProdTest.java @@ -0,0 +1,30 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class DuplicatedSymbolDefinitionInProdTest extends CocoTest { + + private final String MESSAGE = " Symbol or scope is mentioned more than once in the declaration 'A'."; + private final String grammar = "de.monticore.grammar.cocos.invalid.A4041.A4041"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new DuplicatedSymbolDefinitionInProd()); + } + + @Test + public void testDuplicatedSymbolDefinition() { + testInvalidGrammar(grammar, DuplicatedSymbolDefinitionInProd.ERROR_CODE, MESSAGE, checker); + } + + @Test + public void testCorrect(){ + testValidGrammar("de.monticore.grammar.cocos.valid.Attributes", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ExternalNTNoASTRuleTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ExternalNTNoASTRuleTest.java new file mode 100644 index 0000000000..957374265e --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ExternalNTNoASTRuleTest.java @@ -0,0 +1,28 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class ExternalNTNoASTRuleTest extends CocoTest { + + private static final String grammar = "de.monticore.grammar.cocos.invalid.A4118.A4118"; + private static final String MESSAGE = " The external production A must not have a corresponding ASTRule."; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new ExternalNTNoASTRule()); + } + + @Test + public void testValidModel(){ + testValidGrammar("de.monticore.grammar.cocos.valid.Component", checker); + } + + @Test + public void testInvalidModel(){ + testInvalidGrammar(grammar, ExternalNTNoASTRule.ERROR_CODE, MESSAGE, checker); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ExternalNTOnlyInComponentGrammarTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ExternalNTOnlyInComponentGrammarTest.java new file mode 100644 index 0000000000..940fd63f2c --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ExternalNTOnlyInComponentGrammarTest.java @@ -0,0 +1,39 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class ExternalNTOnlyInComponentGrammarTest extends CocoTest{ + + private final String MESSAGE = " The external nonterminal A must not be used in a grammar not marked " + + "as a grammar component."; + private final String grammar = "de.monticore.grammar.cocos.invalid.A0276.A0276"; + private final String grammar2 = "de.monticore.grammar.cocos.invalid.A0276.A0276b"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new ExternalNTOnlyInComponentGrammar()); + } + + @Test + public void testInvalid(){ + testInvalidGrammar(grammar, ExternalNTOnlyInComponentGrammar.ERROR_CODE, MESSAGE, checker); + } + + @Test + public void testInvalid2(){ + testInvalidGrammar(grammar2, ExternalNTOnlyInComponentGrammar.ERROR_CODE, MESSAGE, checker); + } + + + @Test + public void testCorrect(){ + testValidGrammar("de.monticore.grammar.cocos.valid.Component", checker); + } + + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/GrammarExtensionOnceTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/GrammarExtensionOnceTest.java new file mode 100644 index 0000000000..239e2e3ff9 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/GrammarExtensionOnceTest.java @@ -0,0 +1,33 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +import static de.se_rwth.commons.logging.Log.getFindings; + +public class GrammarExtensionOnceTest extends CocoTest { + + private final String MESSAGE = GrammarExtensionOnce.ERROR_MSG_FORMAT; + private final String grammar = "de.monticore.grammar.cocos.invalid.A4150.A4150"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new GrammarExtensionOnce()); + } + + @Test + public void testInvalid() { + testInvalidGrammar(grammar, GrammarExtensionOnce.ERROR_CODE, MESSAGE, checker); + getFindings().clear(); + } + + @Test + public void testCorrect() { + // Any grammar that extends another grammar + testValidGrammar("de.monticore.grammar.cocos.valid.SubGrammarWithSymbol", checker); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/GrammarInheritanceCycleTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/GrammarInheritanceCycleTest.java new file mode 100644 index 0000000000..70994302b1 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/GrammarInheritanceCycleTest.java @@ -0,0 +1,55 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import de.monticore.grammar.grammar_withconcepts._parser.Grammar_WithConceptsParser; +import de.se_rwth.commons.logging.Finding; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static de.monticore.grammar.cocos.GrammarInheritanceCycle.ERROR_CODE; +import static de.se_rwth.commons.logging.Log.getFindings; +import static java.lang.String.format; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +public class GrammarInheritanceCycleTest extends CocoTest { + + private final String MESSAGE = " The grammar A4023%s introduces an inheritance cycle."; + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new GrammarInheritanceCycle()); + } + + @Test + public void testInvalid() throws IOException { + Grammar_WithConceptsParser parser = new Grammar_WithConceptsParser(); + Optional grammar = parser.parse("src/test/resources/de/monticore/grammar/cocos/invalid/A4023/A4023.mc4"); + + getFindings().clear(); + checker.checkAll(grammar.get()); + + assertFalse(getFindings().isEmpty()); + assertEquals(1, getFindings().size()); + for (Finding f : getFindings()) { + assertEquals(format(ERROR_CODE + MESSAGE, ""), f.getMsg()); + } + } + + @Test + public void testCorrect() { + testValidGrammar("de.monticore.grammar.cocos.valid.Overriding", checker); + } + + @Test + public void testCorrectB() { + testValidGrammar("de.monticore.grammar.cocos.valid.enum.Enum", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/GrammarNameEqualsFileNameTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/GrammarNameEqualsFileNameTest.java new file mode 100644 index 0000000000..7778db4301 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/GrammarNameEqualsFileNameTest.java @@ -0,0 +1,47 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts.Grammar_WithConceptsMill; +import de.monticore.grammar.grammar_withconcepts._parser.Grammar_WithConceptsParser; +import de.se_rwth.commons.logging.Finding; +import de.se_rwth.commons.logging.Log; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +public class GrammarNameEqualsFileNameTest extends CocoTest { + + protected Grammar_WithConceptsParser parser; + @Before + public void init() { + parser = new Grammar_WithConceptsParser(); + } + @Test + public void testInvalidFilename() throws IOException { + Log.getFindings().clear(); + parser.parse("src/test/resources/de/monticore/grammar/cocos/invalid/A4003/A4003.mc4"); + + assertFalse(Log.getFindings().isEmpty()); + for(Finding f : Log.getFindings()){ + assertEquals("0"+"xA4003 The grammar name A4002 must be identical to the file name" + + " A4003 of the grammar (without its file extension).", f.getMsg()); + } + } + + @Test + public void testInvalidPackage() throws IOException { + Log.getFindings().clear(); + parser.parse("src/test/resources/de/monticore/grammar/cocos/invalid/A4004/A4004.mc4"); + + assertFalse(Log.getFindings().isEmpty()); + for(Finding f : Log.getFindings()){ + assertEquals("0"+"xA4004 The package declaration de.monticore.grammar.cocos.invalid.A4003 of the grammar must not" + + " differ from the package of the grammar file.", f.getMsg()); + } + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/GrammarNameUpperCaseTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/GrammarNameUpperCaseTest.java new file mode 100644 index 0000000000..d8b1a4922d --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/GrammarNameUpperCaseTest.java @@ -0,0 +1,31 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class GrammarNameUpperCaseTest extends CocoTest{ + + private final String MESSAGE = " The grammar's name a4033 should start with an upper-case letter."; + private final String grammar = "de.monticore.grammar.cocos.invalid.A4033.a4033"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new GrammarNameUpperCase()); + } + + @Test + public void testLowerCasedGrammarName() { + testInvalidGrammar(grammar, GrammarNameUpperCase.ERROR_CODE, MESSAGE, checker); + } + + + @Test + public void testCorrect(){ + testValidGrammar("de.monticore.grammar.cocos.valid.Attributes", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/InheritedModiOverwriteTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/InheritedModiOverwriteTest.java new file mode 100644 index 0000000000..7c7fa3b6cc --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/InheritedModiOverwriteTest.java @@ -0,0 +1,24 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class InheritedModiOverwriteTest extends CocoTest{ + private final String grammar = "de.monticore.grammar.cocos.invalid.A4069.A4069Sub"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new InheritedModiOverwrite()); + } + + @Test + public void testInvalid() { + testInvalidGrammar(grammar, InheritedModiOverwrite.ERROR_CODE, + String.format(InheritedModiOverwrite.ERROR_MSG_FORMAT, + "EndTag","A4069Sub", "TEXT","EndTag", "A4069Super"), checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/InheritedScopePropertyTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/InheritedScopePropertyTest.java new file mode 100644 index 0000000000..02852f4209 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/InheritedScopePropertyTest.java @@ -0,0 +1,49 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; +public class InheritedScopePropertyTest extends CocoTest { + + private final String grammar = "de.monticore.grammar.cocos.invalid.A0135.A0135"; + private final String grammar2 = "de.monticore.grammar.cocos.invalid.A0135.A0135a"; + private final String grammar3 = "de.monticore.grammar.cocos.invalid.A0135.A0135b"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new InheritedScopeProperty()); + } + + /** + * implements -> from interface + */ + @Test + public void testInvalid() { + testInvalidGrammar(grammar, InheritedScopeProperty.ERROR_CODE, + String.format(InheritedScopeProperty.ERROR_MSG_FORMAT, "A"), checker); + } + + @Test + public void testInvalid2() { + testInvalidGrammar(grammar2, InheritedScopeProperty.ERROR_CODE, + String.format(InheritedScopeProperty.ERROR_MSG_FORMAT, "A"), checker); + } + + /** + * extends -> from class + */ + @Test + public void testInvalid3() { + testInvalidGrammar(grammar3, InheritedScopeProperty.ERROR_CODE, + String.format(InheritedScopeProperty.ERROR_MSG_FORMAT, "A"), checker); + } + + @Test + public void testCorrect() { + testValidGrammar("de.monticore.grammar.cocos.valid.Attributes", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/InheritedSymbolPropertyTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/InheritedSymbolPropertyTest.java new file mode 100644 index 0000000000..5205a02466 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/InheritedSymbolPropertyTest.java @@ -0,0 +1,40 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class InheritedSymbolPropertyTest extends CocoTest { + + private final String grammar = "de.monticore.grammar.cocos.invalid.A0125.A0125"; + private final String grammar2 = "de.monticore.grammar.cocos.invalid.A0125.A0125a"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new InheritedSymbolProperty()); + } + + /** + * implements -> from interface + */ + @Test + public void testInvalid() { + testInvalidGrammar(grammar, InheritedSymbolProperty.ERROR_CODE, + String.format(InheritedSymbolProperty.ERROR_MSG_FORMAT, "A"), checker); + } + + @Test + public void testInvalid2() { + testInvalidGrammar(grammar2, InheritedSymbolProperty.ERROR_CODE, + String.format(InheritedSymbolProperty.ERROR_MSG_FORMAT, "A"), checker); + } + + @Test + public void testCorrect() { + testValidGrammar("de.monticore.grammar.cocos.valid.Attributes", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/InterfaceNTOnlyExtendInterfaceNTsTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/InterfaceNTOnlyExtendInterfaceNTsTest.java new file mode 100644 index 0000000000..86f70704be --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/InterfaceNTOnlyExtendInterfaceNTsTest.java @@ -0,0 +1,49 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class InterfaceNTOnlyExtendInterfaceNTsTest extends CocoTest { + + private final String MESSAGE = " The interface nonterminal B must not extend the%s nonterminal A. " + + "Interface nonterminals may only extend interface nonterminals."; + private final String grammar = "de.monticore.grammar.cocos.invalid.A2116.A2116"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new InterfaceNTOnlyExtendInterfaceNTs()); + } + + @Test + public void testExtendAbstractNT() { + testInvalidGrammar(grammar + "a", InterfaceNTOnlyExtendInterfaceNTs.ERROR_CODE, + String.format(MESSAGE, " abstract"), checker); + } + + @Test + public void testExtendExternalNT() { + testInvalidGrammar(grammar + "b", InterfaceNTOnlyExtendInterfaceNTs.ERROR_CODE, + String.format(MESSAGE, " external"), checker); + } + + @Test + public void testExtendNormalNT() { + testInvalidGrammar(grammar + "c", InterfaceNTOnlyExtendInterfaceNTs.ERROR_CODE, + String.format(MESSAGE, ""), checker); + } + + @Test + public void testExtendNormalNT2() { + testInvalidGrammar(grammar + "d", InterfaceNTOnlyExtendInterfaceNTs.ERROR_CODE, + String.format(MESSAGE, ""), checker); + } + + @Test + public void testExtendInterfaceNT(){ + testValidGrammar("de.monticore.grammar.cocos.valid.ExtendNTs", checker); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/InterfaceNTWithoutImplementationOnlyInComponentGrammarTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/InterfaceNTWithoutImplementationOnlyInComponentGrammarTest.java new file mode 100644 index 0000000000..02d994c1a2 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/InterfaceNTWithoutImplementationOnlyInComponentGrammarTest.java @@ -0,0 +1,107 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import de.se_rwth.commons.logging.LogStub; +import org.checkerframework.checker.units.qual.A; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import static de.monticore.grammar.cocos.InterfaceNTWithoutImplementationOnlyInComponentGrammar.ERROR_CODE; + +import de.se_rwth.commons.logging.Log; + +public class InterfaceNTWithoutImplementationOnlyInComponentGrammarTest extends CocoTest { + + private final String MESSAGE_A = " The interface nonterminal A must not be used without nonterminals " + + "implementing it in a grammar not marked as a grammar component."; + + private final String MESSAGE_D = " The interface nonterminal D must not be used without nonterminals " + + "implementing it in a grammar not marked as a grammar component."; + + private final String MESSAGE_Z = " The interface nonterminal Z must not be used without nonterminals " + + "implementing it in a grammar not marked as a grammar component."; + + private final String grammar = "de.monticore.grammar.cocos.invalid.A0278.A0278"; + + private final String grammar2 = "de.monticore.grammar.cocos.invalid.A0278.A0278b"; + + private final String grammar3 = "de.monticore.grammar.cocos.invalid.A0278.A0278c"; + + private final String grammar4 = "de.monticore.grammar.cocos.invalid.A0278.A0278d"; + + private final String grammar5 = "de.monticore.grammar.cocos.valid.InterfaceWithoutImplementation"; + + private final String grammar6 = "de.monticore.grammar.cocos.valid.InterfaceWithoutImplementation2"; + + private final String grammar7 = "de.monticore.grammar.cocos.valid.InterfaceWithoutImplementation3"; + + private final String grammar8 = "de.monticore.grammar.cocos.invalid.A0278.A0278e"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new InterfaceNTWithoutImplementationOnlyInComponentGrammar()); + } + + @Test + public void testInvalid() { + testInvalidGrammar(grammar, ERROR_CODE, + MESSAGE_A, checker); + } + + @Test + public void testInvalid2() { + testInvalidGrammar(grammar2, ERROR_CODE, + MESSAGE_A, checker); + } + + @Test + public void testInvalid3() { + testInvalidGrammar(grammar3, ERROR_CODE, + MESSAGE_A, checker); + } + + @Test + public void testInvalid4() { + testInvalidGrammar(grammar4, ERROR_CODE, MESSAGE_D, checker); + } + + @Test + public void testInvalid5() { + testInvalidGrammar(grammar8, ERROR_CODE, MESSAGE_Z, checker); + } + + @Test + public void testCorrect() { + testValidGrammar("de.monticore.grammar.cocos.valid.Component", checker); + } + + @Test + public void testCorrect2() { + testValidGrammar("de.monticore.grammar.cocos.valid.Overriding", checker); + } + + @Test + public void testCorrect3() { + testValidGrammar(grammar5, checker); + } + + @Test + public void testCorrect4() { + testValidGrammar(grammar6, checker); + } + + @Test + public void testCorrect5() { + testValidGrammar(grammar7, checker); + } + + @After + public void after() { + Log.getFindings().clear(); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/KeyConstantInvalidTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/KeyConstantInvalidTest.java new file mode 100644 index 0000000000..6fe093a5af --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/KeyConstantInvalidTest.java @@ -0,0 +1,30 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class KeyConstantInvalidTest extends CocoTest { + + private final String grammar = "de.monticore.grammar.cocos.invalid.A4063.A4063"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new KeyConstantInvalid()); + } + + @Test + public void testInvalid1() { + testInvalidGrammar(grammar, KeyConstantInvalid.ERROR_CODE, + String.format(KeyConstantInvalid.ERROR_MSG_FORMAT, "--"), checker); + } + + @Test + public void testCorrect() { + testValidGrammar("de.monticore.grammar.cocos.valid.Attributes", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/KeyRuleWithoutNameTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/KeyRuleWithoutNameTest.java new file mode 100644 index 0000000000..31cc685f16 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/KeyRuleWithoutNameTest.java @@ -0,0 +1,33 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class KeyRuleWithoutNameTest extends CocoTest{ + private final String grammar = "de.monticore.grammar.cocos.invalid.A0142.A0142"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new KeyRuleWithoutName()); + } + + @Test + public void testInvalid() { + testInvalidGrammar(grammar, KeyRuleWithoutName.ERROR_CODE, KeyRuleWithoutName.ERROR_MSG_FORMAT, checker); + } + + @Test + public void testInvalid2() { + testInvalidGrammar(grammar+"a", KeyRuleWithoutName.ERROR_CODE, KeyRuleWithoutName.ERROR_MSG_FORMAT, checker); + } + + @Test + public void testCorrect(){ + testValidGrammar("de.monticore.grammar.cocos.valid.Attributes", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/KeywordAlternativeNameTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/KeywordAlternativeNameTest.java new file mode 100644 index 0000000000..d3f94da8f5 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/KeywordAlternativeNameTest.java @@ -0,0 +1,48 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.GrammarGlobalScopeTestFactory; +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import de.monticore.grammar.grammar_withconcepts._symboltable.Grammar_WithConceptsGlobalScope; +import de.se_rwth.commons.logging.Finding; +import de.se_rwth.commons.logging.Log; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +public class KeywordAlternativeNameTest extends CocoTest { + private final String MESSAGE = " The name of the constant group could't be ascertained"; + + private final String grammar = "de.monticore.grammar.cocos.invalid.A4019.A4019"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + } + + @Test + public void testKeywordAlternativeWithoutName() throws IllegalArgumentException { + final Grammar_WithConceptsGlobalScope globalScope = GrammarGlobalScopeTestFactory.create(); + + Log.getFindings().clear(); + + // test grammar symbol + globalScope.resolveMCGrammar(grammar).orElse(null); + + assertFalse(Log.getFindings().isEmpty()); + assertEquals(1, Log.getFindings().size()); + for (Finding f : Log.getFindings()) { + assertEquals("0xA2345" + MESSAGE, f.getMsg()); + } + } + + @Test + public void testSingleKeyword() { + checker.addCoCo(new KeywordAlternativeName()); + testValidGrammar("de.monticore.grammar.cocos.valid.Attributes", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/KeywordInvalidNameTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/KeywordInvalidNameTest.java new file mode 100644 index 0000000000..bf6d8ffc77 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/KeywordInvalidNameTest.java @@ -0,0 +1,32 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +import static de.monticore.grammar.cocos.KeywordInvalidName.ERROR_CODE; + +public class KeywordInvalidNameTest extends CocoTest { + private final String MESSAGE = " The production A must not use the keyword ` without naming it."; + private final String grammar = "de.monticore.grammar.cocos.invalid.A4018.A4018"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new KeywordInvalidName()); + } + + @Test + public void testInvalid() throws IllegalArgumentException { + testInvalidGrammar(grammar, ERROR_CODE, MESSAGE, checker); + } + + @Test + public void testCorrect() { + testValidGrammar("de.monticore.grammar.cocos.valid.Attributes", checker); + } + + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/KeywordRuleInvalidTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/KeywordRuleInvalidTest.java new file mode 100644 index 0000000000..509959c8df --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/KeywordRuleInvalidTest.java @@ -0,0 +1,29 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class KeywordRuleInvalidTest extends CocoTest { + private final String grammar = "de.monticore.grammar.cocos.invalid.A4064.A4064"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new KeywordRuleInvalid()); + } + + @Test + public void testInvalid1() { + testInvalidGrammar(grammar, KeywordRuleInvalid.ERROR_CODE, + String.format(KeywordRuleInvalid.ERROR_MSG_FORMAT, "--"), checker); + } + + @Test + public void testCorrect() { + testValidGrammar("de.monticore.grammar.cocos.valid.Attributes", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/LeftRecursiveRulesInBlockTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/LeftRecursiveRulesInBlockTest.java new file mode 100644 index 0000000000..edf55ab68c --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/LeftRecursiveRulesInBlockTest.java @@ -0,0 +1,35 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class LeftRecursiveRulesInBlockTest extends CocoTest { + private final String grammar = "de.monticore.grammar.cocos.invalid.A4056.A4056"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new LeftRecursiveRulesInBlock()); + } + + @Test + public void testSimpleLeftRecursion() { + testInvalidGrammar(grammar, LeftRecursiveRulesInBlock.ERROR_CODE, + String.format(LeftRecursiveRulesInBlock.ERROR_MSG_FORMAT, "A"), checker); + } + + @Test + public void testComplexLeftRecursion() { + testInvalidGrammar(grammar+"a", LeftRecursiveRulesInBlock.ERROR_CODE, + String.format(LeftRecursiveRulesInBlock.ERROR_MSG_FORMAT, "PlusExpression"), checker); + } + + @Test + public void testCorrect(){ + testValidGrammar("de.monticore.grammar.cocos.valid.Attributes", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/LexNTsNotEmptyTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/LexNTsNotEmptyTest.java new file mode 100644 index 0000000000..dd10ece36a --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/LexNTsNotEmptyTest.java @@ -0,0 +1,42 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +import static de.monticore.grammar.cocos.LexNTsNotEmpty.ERROR_CODE; + + +public class LexNTsNotEmptyTest extends CocoTest { + + private final String MESSAGE = " The lexical production A must not allow the empty token."; + private final String grammar = "de.monticore.grammar.cocos.invalid.A4015.A4015"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new LexNTsNotEmpty()); + } + + @Test + public void testInvalid() { + testInvalidGrammar(grammar, ERROR_CODE, MESSAGE, checker); + } + + @Test + public void testCorrect() { + testValidGrammar("de.monticore.grammar.cocos.valid.Attributes", checker); + } + + @Test + public void testCorrect2() { + testValidGrammar("de.monticore.common.TestLexicals", checker); + } + + @Test + public void testCorrect3() { + testValidGrammar("de.monticore.common.TestLiterals", checker); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/LexNTsOnlyUseLexNTsTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/LexNTsOnlyUseLexNTsTest.java new file mode 100644 index 0000000000..98e0d20bc2 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/LexNTsOnlyUseLexNTsTest.java @@ -0,0 +1,33 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class LexNTsOnlyUseLexNTsTest extends CocoTest { + + private final String MESSAGE = " The lexical production A must not use" + + " the nonterminal B because B is defined by a production of" + + " another type than lexical. Lexical productions may only reference nonterminals" + + " defined by lexical productions."; + private final String grammar = "de.monticore.grammar.cocos.invalid.A4017.A4017"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new LexNTsOnlyUseLexNTs()); + } + + @Test + public void testInvalid() { + testInvalidGrammar(grammar, LexNTsOnlyUseLexNTs.ERROR_CODE, MESSAGE, checker); + } + + @Test + public void testCorrect(){ + testValidGrammar("de.monticore.grammar.cocos.valid.Attributes", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/LexProdModeNameUpperCaseTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/LexProdModeNameUpperCaseTest.java new file mode 100644 index 0000000000..b8b7238970 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/LexProdModeNameUpperCaseTest.java @@ -0,0 +1,27 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class LexProdModeNameUpperCaseTest extends CocoTest{ + private final String MESSAGE = " The lexical production %s must use Upper-case mode names."; + private final String grammar = "de.monticore.grammar.cocos.invalid.A4038.A4038"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new LexProdModeNameUpperCase()); + } + + @Test + public void testInvalid() { + testInvalidGrammar(grammar, LexProdModeNameUpperCase.ERROR_CODE, + String.format(LexProdModeNameUpperCase.ERROR_MSG_FORMAT, + "EndTag"), checker); + } + + + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/MultipleASTRulesTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/MultipleASTRulesTest.java new file mode 100644 index 0000000000..0eee064221 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/MultipleASTRulesTest.java @@ -0,0 +1,31 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class MultipleASTRulesTest extends CocoTest{ + + private final String MESSAGE = " There must not exist more than one AST" + + " rule for the nonterminal A."; + private final String grammar = "de.monticore.grammar.cocos.invalid.A4020.A4020"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new MultipleASTRules()); + } + + @Test + public void testInvalid() { + testInvalidGrammar(grammar, MultipleASTRules.ERROR_CODE, MESSAGE, checker); + } + + @Test + public void testCorrect(){ + testValidGrammar("de.monticore.grammar.cocos.valid.ASTRules", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NTAndASTRuleExtendTypeTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NTAndASTRuleExtendTypeTest.java new file mode 100644 index 0000000000..0f7552baac --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NTAndASTRuleExtendTypeTest.java @@ -0,0 +1,32 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class NTAndASTRuleExtendTypeTest extends CocoTest{ + + private final String MESSAGE = " The AST rule for A must not extend the type " + + "C because the production already extends a type."; + private final String grammar = "de.monticore.grammar.cocos.invalid.A4013.A4013"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new NTAndASTRuleExtendType()); + } + + @Test + public void testInvalid(){ + testInvalidGrammar(grammar, NTAndASTRuleExtendType.ERROR_CODE, MESSAGE, checker); + } + + + @Test + public void testCorrect(){ + testValidGrammar("de.monticore.grammar.cocos.valid.ASTRules", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NTDefinedByAtmostOneProductionTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NTDefinedByAtmostOneProductionTest.java new file mode 100644 index 0000000000..49313b28f4 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NTDefinedByAtmostOneProductionTest.java @@ -0,0 +1,34 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import de.se_rwth.commons.logging.Log; +import org.junit.Before; +import org.junit.Test; + + +public class NTDefinedByAtmostOneProductionTest extends CocoTest { + + private final String MESSAGE = " The nonterminal A must not be defined by more than one production."; + + private final String grammar = "de.monticore.grammar.cocos.invalid.A2025.A2025"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new NTDefinedByAtmostOneProduction()); + } + + @Test + public void testInvalid() { + Log.getFindings().clear(); + testInvalidGrammar(grammar, NTDefinedByAtmostOneProduction.ERROR_CODE, MESSAGE, checker, 11); + } + + @Test + public void testCorrect() { + testValidGrammar("de.monticore.grammar.cocos.valid.Attributes", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NTForASTRuleExistsTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NTForASTRuleExistsTest.java new file mode 100644 index 0000000000..903896c0bd --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NTForASTRuleExistsTest.java @@ -0,0 +1,31 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class NTForASTRuleExistsTest extends CocoTest{ + + private final String MESSAGE = " There must not exist an AST rule for the nonterminal A" + + " because there exists no production defining A"; + private final String grammar = "de.monticore.grammar.cocos.invalid.A4021.A4021"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new NTForASTRuleExists()); + } + + @Test + public void testInvalid(){ + testInvalidGrammar(grammar, NTForASTRuleExists.ERROR_CODE, MESSAGE, checker); + } + + @Test + public void testCorrect(){ + testValidGrammar("de.monticore.grammar.cocos.valid.ASTRules", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NTNotExtendInterfaceOrExternalNTsTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NTNotExtendInterfaceOrExternalNTsTest.java new file mode 100644 index 0000000000..381ea05256 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NTNotExtendInterfaceOrExternalNTsTest.java @@ -0,0 +1,37 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class NTNotExtendInterfaceOrExternalNTsTest extends CocoTest { + + private final String MESSAGE = " The nonterminal B must not extend the %s nonterminal A. " + + "Nonterminals may only extend abstract or normal nonterminals."; + private final String grammar = "de.monticore.grammar.cocos.invalid.A2103.A2103"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new NTNotExtendInterfaceOrExternalNTs()); + } + + @Test + public void testExtendInterfaceNT() { + testInvalidGrammar(grammar + "a", NTNotExtendInterfaceOrExternalNTs.ERROR_CODE, + String.format(MESSAGE, "interface"), checker); + } + + @Test + public void testExtendExternalNT() { + testInvalidGrammar(grammar + "b", NTNotExtendInterfaceOrExternalNTs.ERROR_CODE, + String.format(MESSAGE, "external"), checker); + } + + @Test + public void testExtendNT(){ + testValidGrammar("de.monticore.grammar.cocos.valid.ExtendNTs", checker); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NTOnlyExtendsOneNTOrClassTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NTOnlyExtendsOneNTOrClassTest.java new file mode 100644 index 0000000000..10eab3ce2c --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NTOnlyExtendsOneNTOrClassTest.java @@ -0,0 +1,41 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +import static de.monticore.grammar.cocos.NTOnlyExtendsOneNTOrClass.ERROR_CODE; +import static java.lang.String.format; + + +public class NTOnlyExtendsOneNTOrClassTest extends CocoTest { + + private final String MESSAGE = " The nonterminal %s must not %s more than one %s."; + private final String grammar = "de.monticore.grammar.cocos.invalid.A4011.A4011"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new NTOnlyExtendsOneNTOrClass()); + } + + @Test + public void testExtendMultiple() { + testInvalidGrammar(grammar + "a", ERROR_CODE, + format(MESSAGE, "C", "extend", "nonterminal"), checker); + } + + @Test + public void testASTExtendMultiple() { + testInvalidGrammar(grammar + "b", ERROR_CODE, + format(MESSAGE, "A", "astextend", "class"), checker); + } + + @Test + public void testExtendNT() { + testValidGrammar("de.monticore.grammar.cocos.valid.ExtendNTs", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NTOnlyExtendsOrAstextendsNTOrClassTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NTOnlyExtendsOrAstextendsNTOrClassTest.java new file mode 100644 index 0000000000..d1a26a766c --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NTOnlyExtendsOrAstextendsNTOrClassTest.java @@ -0,0 +1,30 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class NTOnlyExtendsOrAstextendsNTOrClassTest extends CocoTest{ + + private final String MESSAGE = " The nonterminal B must not extend and astextend a type."; + private final String grammar = "de.monticore.grammar.cocos.invalid.A4029.A4029"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new NTOnlyExtendOrAstextendNTOrClass()); + } + + @Test + public void testASTExtendMultiple() { + testInvalidGrammar(grammar, NTOnlyExtendOrAstextendNTOrClass.ERROR_CODE, MESSAGE, checker); + } + + @Test + public void testExtendNT(){ + testValidGrammar("de.monticore.grammar.cocos.valid.ExtendNTs", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NTOnlyImplementInterfaceNTsTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NTOnlyImplementInterfaceNTsTest.java new file mode 100644 index 0000000000..46aa85c369 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NTOnlyImplementInterfaceNTsTest.java @@ -0,0 +1,46 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class NTOnlyImplementInterfaceNTsTest extends CocoTest { + private final String MESSAGE = " The nonterminal B must not implement the nonterminal A." + + " Nonterminals may only implement interface nonterminals."; + private final String grammar = "de.monticore.grammar.cocos.invalid.A2102.A2102"; + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new NTOnlyImplementInterfaceNTs()); + } + + @Test + public void testImplementsNormalNT() { + testInvalidGrammar(grammar + "a", NTOnlyImplementInterfaceNTs.ERROR_CODE, MESSAGE, checker); + } + + @Test + public void testImplementsExternalNT() { + testInvalidGrammar(grammar + "b", NTOnlyImplementInterfaceNTs.ERROR_CODE, MESSAGE, checker); + } + + @Test + public void testImplementsAbstractNT() { + testInvalidGrammar(grammar + "c", NTOnlyImplementInterfaceNTs.ERROR_CODE, MESSAGE, checker); + } + + @Test + public void testImplementsEnumNT() { + testInvalidGrammar(grammar + "d", NTOnlyImplementInterfaceNTs.ERROR_CODE, MESSAGE, checker); + } + + @Test + public void testImplementsInterfaceNT(){ + testValidGrammar("de.monticore.grammar.cocos.valid.ImplementInterfaceNTs", checker); + } + + + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NTUniqueIgnoreCaseTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NTUniqueIgnoreCaseTest.java new file mode 100644 index 0000000000..be951a1e63 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NTUniqueIgnoreCaseTest.java @@ -0,0 +1,37 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import de.se_rwth.commons.logging.Finding; +import de.se_rwth.commons.logging.Log; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +public class NTUniqueIgnoreCaseTest extends CocoTest { + + private final String MESSAGE = " The nonterminal A must not be defined by more than one production: nonterminals aren't case-sensitive."; + + private final String grammar = "de.monticore.grammar.cocos.invalid.A2026.A2026"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new NTUniqueIgnoreCase()); + } + + @Test + public void testInvalid() { + Log.getFindings().clear(); + testInvalidGrammar(grammar, NTUniqueIgnoreCase.ERROR_CODE, MESSAGE, checker); + assertFalse(Log.getFindings().isEmpty()); + assertEquals(1, Log.getFindings().size()); + for (Finding f : Log.getFindings()) { + assertEquals(NTUniqueIgnoreCase.ERROR_CODE + MESSAGE, f.getMsg()); + } + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoASTExtendsForClassesTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoASTExtendsForClassesTest.java new file mode 100644 index 0000000000..eb3c21f2b7 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoASTExtendsForClassesTest.java @@ -0,0 +1,36 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class NoASTExtendsForClassesTest extends CocoTest{ + + private final String MESSAGE = " It is forbidden to extend the rule A with the external class Observer."; + private final String grammar = "de.monticore.grammar.cocos.invalid.A4097.A4097"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new NoASTExtendsForClasses()); + } + + + @Test + public void testInvalida() { + testInvalidGrammar(grammar + "a", NoASTExtendsForClasses.ERROR_CODE, MESSAGE, checker); + } + + @Test + public void testInvalidb() { + testInvalidGrammar(grammar + "b", NoASTExtendsForClasses.ERROR_CODE, MESSAGE, checker); + } + + @Test + public void testCorrect(){ + testValidGrammar("de.monticore.grammar.cocos.valid.ASTRules", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoASTRuleForEnumNTsTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoASTRuleForEnumNTsTest.java new file mode 100644 index 0000000000..acbdd4f759 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoASTRuleForEnumNTsTest.java @@ -0,0 +1,31 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class NoASTRuleForEnumNTsTest extends CocoTest{ + + private final String MESSAGE = " There must not exist an AST rule for the enum nonterminal A."; + private final String grammar = "de.monticore.grammar.cocos.invalid.A4032.A4032"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new NoASTRuleForEnumNTs()); + } + + @Test + public void testInvalid(){ + testInvalidGrammar(grammar, NoASTRuleForEnumNTs.ERROR_CODE, MESSAGE, checker); + } + + + @Test + public void testCorrect(){ + testValidGrammar("de.monticore.grammar.cocos.valid.ASTRules", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoExtensionOfSymbolThatOnlySpansScopeTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoExtensionOfSymbolThatOnlySpansScopeTest.java new file mode 100644 index 0000000000..23c580d332 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoExtensionOfSymbolThatOnlySpansScopeTest.java @@ -0,0 +1,46 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class NoExtensionOfSymbolThatOnlySpansScopeTest extends CocoTest { + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new NoExtensionOfSymbolThatOnlySpansScope()); + } + + @Test + public void testInvalid1(){ + String grammar = "de.monticore.grammar.cocos.invalid.A0810.A0810a"; + String message = " The production B extends the symbol production A and spans a scope " + + "without being a symbol itself."; + testInvalidGrammar(grammar, NoExtensionOfSymbolThatOnlySpansScope.ERROR_CODE, message, checker); + } + + @Test + public void testInvalid2(){ + String grammar = "de.monticore.grammar.cocos.invalid.A0810.A0810b"; + String message = " The production F extends the symbol production E and spans a scope " + + "without being a symbol itself."; + testInvalidGrammar(grammar, NoExtensionOfSymbolThatOnlySpansScope.ERROR_CODE, message, checker); + } + + @Test + public void testInvalid3(){ + String grammar = "de.monticore.grammar.cocos.invalid.A0810.A0810c"; + String message = " The production J extends the symbol production A and spans a scope " + + "without being a symbol itself."; + testInvalidGrammar(grammar, NoExtensionOfSymbolThatOnlySpansScope.ERROR_CODE, message, checker); + } + + @Test + public void testValid1(){ + testValidGrammar("de.monticore.grammar.cocos.valid.CorrectSymbolInheritance",checker); + } + + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoForbiddenGrammarNameTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoForbiddenGrammarNameTest.java new file mode 100644 index 0000000000..aab65a7148 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoForbiddenGrammarNameTest.java @@ -0,0 +1,29 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class NoForbiddenGrammarNameTest extends CocoTest { + + private final String MESSAGE = " There must not exist a grammar with the name I."; + private final String grammar = "de.monticore.grammar.cocos.invalid.A4036.I"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new NoForbiddenGrammarName()); + } + + @Test + public void testInvalid1(){ + testInvalidGrammar(grammar, NoForbiddenGrammarName.ERROR_CODE, MESSAGE, checker); + } + + @Test + public void testValid1(){ + testValidGrammar("de.monticore.grammar.cocos.valid.ExtendNTs",checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoForbiddenProdAndSymbolNameTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoForbiddenProdAndSymbolNameTest.java new file mode 100644 index 0000000000..952721f932 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoForbiddenProdAndSymbolNameTest.java @@ -0,0 +1,30 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class NoForbiddenProdAndSymbolNameTest extends CocoTest{ + + private final String MESSAGE = " There must not exist a production with the name ASymbol in the grammar A4122 if " + + "there already exists a symbol with the name A."; + private final String grammar = "de.monticore.grammar.cocos.invalid.A4122.A4122"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new NoForbiddenProdAndSymbolName()); + } + + @Test + public void testInvalid1(){ + testInvalidGrammar(grammar, NoForbiddenProdAndSymbolName.ERROR_CODE, MESSAGE, checker); + } + + @Test + public void testValid1(){ + testValidGrammar("de.monticore.grammar.cocos.valid.ExtendNTs",checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoForbiddenProdNameAddonTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoForbiddenProdNameAddonTest.java new file mode 100644 index 0000000000..ad9d968d91 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoForbiddenProdNameAddonTest.java @@ -0,0 +1,29 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class NoForbiddenProdNameAddonTest extends CocoTest{ + + private final String MESSAGE = " There must not exist a production with the name ABuilder in the grammar A4120 if there is already a production with the name A."; + private final String grammar = "de.monticore.grammar.cocos.invalid.A4120.A4120"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new NoForbiddenProdNameAddon()); + } + + @Test + public void testInvalid1(){ + testInvalidGrammar(grammar, NoForbiddenProdNameAddon.ERROR_CODE, MESSAGE, checker); + } + + @Test + public void testValid1(){ + testValidGrammar("de.monticore.grammar.cocos.valid.ExtendNTs",checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoForbiddenProdNameTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoForbiddenProdNameTest.java new file mode 100644 index 0000000000..76066a1912 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoForbiddenProdNameTest.java @@ -0,0 +1,72 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class NoForbiddenProdNameTest extends CocoTest{ + + private final String MESSAGE = " There must not exist a production with the name %s in the grammar %s."; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new NoForbiddenProdName()); + } + + @Test + public void testInvalid1(){ + String grammar = "de.monticore.grammar.cocos.invalid.A4096.A4096a"; + String message = String.format(MESSAGE, "ConstantsA4096a", "A4096a"); + testInvalidGrammar(grammar, NoForbiddenProdName.ERROR_CODE, message, checker); + } + + @Test + public void testInvalid2(){ + String grammar = "de.monticore.grammar.cocos.invalid.A4096.A4096b"; + String message = String.format(MESSAGE, "A4096bNode", "A4096b"); + testInvalidGrammar(grammar, NoForbiddenProdName.ERROR_CODE, message, checker); + } + + @Test + public void testInvalid3(){ + String grammar = "de.monticore.grammar.cocos.invalid.A4096.A4096c"; + String message = String.format(MESSAGE, "EnclosingScope", "A4096c"); + testInvalidGrammar(grammar, NoForbiddenProdName.ERROR_CODE, message, checker); + } + + @Test + public void testInvalid4(){ + String grammar = "de.monticore.grammar.cocos.invalid.A4096.A4096d"; + String message = String.format(MESSAGE, "Traverser", "A4096d"); + testInvalidGrammar(grammar, NoForbiddenProdName.ERROR_CODE, message, checker); + } + + @Test + public void testInvalid5(){ + String grammar = "de.monticore.grammar.cocos.invalid.A4096.A4096e"; + String message = String.format(MESSAGE, "Node", "A4096e"); + testInvalidGrammar(grammar, NoForbiddenProdName.ERROR_CODE, message, checker); + } + + @Test + public void testInvalid6(){ + String grammar = "de.monticore.grammar.cocos.invalid.A4096.A4096f"; + String message = String.format(MESSAGE, "Class", "A4096f"); + testInvalidGrammar(grammar, NoForbiddenProdName.ERROR_CODE, message, checker); + } + + @Test + public void testInvalid7(){ + String grammar = "de.monticore.grammar.cocos.invalid.A4096.A4096g"; + String message = String.format(MESSAGE, "Mode", "A4096g"); + testInvalidGrammar(grammar, NoForbiddenProdName.ERROR_CODE, message, checker); + } + + @Test + public void testValid1(){ + testValidGrammar("de.monticore.grammar.cocos.valid.ExtendNTs",checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoForbiddenSymbolNameAddonTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoForbiddenSymbolNameAddonTest.java new file mode 100644 index 0000000000..69b390682f --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoForbiddenSymbolNameAddonTest.java @@ -0,0 +1,37 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class NoForbiddenSymbolNameAddonTest extends CocoTest{ + + private final String MESSAGE = " There must not exist a symbol production with the name %s in the grammar %s if there is already a symbol production A."; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new NoForbiddenSymbolNameAddon()); + } + + @Test + public void testInvalid1(){ + String grammar = "de.monticore.grammar.cocos.invalid.A4121.A4121a"; + String message = String.format(MESSAGE, "AMany", "A4121a"); + testInvalidGrammar(grammar, NoForbiddenSymbolNameAddon.ERROR_CODE, message, checker); + } + + @Test + public void testInvalid2(){ + String grammar = "de.monticore.grammar.cocos.invalid.A4121.A4121b"; + String message = String.format(MESSAGE, "AdaptedA", "A4121b"); + testInvalidGrammar(grammar, NoForbiddenSymbolNameAddon.ERROR_CODE, message, checker); + } + + @Test + public void testValid1(){ + testValidGrammar("de.monticore.grammar.cocos.valid.ExtendNTs",checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoForbiddenSymbolNameTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoForbiddenSymbolNameTest.java new file mode 100644 index 0000000000..bb479af50e --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoForbiddenSymbolNameTest.java @@ -0,0 +1,29 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class NoForbiddenSymbolNameTest extends CocoTest{ + + private final String MESSAGE = " There must not exist a symbol production with the name A4099 in the grammar A4099Symbol."; + private final String grammar = "de.monticore.grammar.cocos.invalid.A4099.A4099Symbol"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new NoForbiddenSymbolName()); + } + + @Test + public void testInvalid1(){ + testInvalidGrammar(grammar, NoForbiddenSymbolName.ERROR_CODE, MESSAGE, checker); + } + + @Test + public void testValid1(){ + testValidGrammar("de.monticore.grammar.cocos.valid.ExtendNTs",checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoMultipleSymbolRuleTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoMultipleSymbolRuleTest.java new file mode 100644 index 0000000000..f0e9578743 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoMultipleSymbolRuleTest.java @@ -0,0 +1,24 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class NoMultipleSymbolRuleTest extends CocoTest{ + + private final String MESSAGE = " A symbolRule must not exist twice for a single nonterminal. Violation by A"; + private final String grammar = "de.monticore.grammar.cocos.invalid.A4151.A4151"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new NoMultipleSymbolRule()); + } + + @Test + public void testMultipleRulesSingleNT() { + testInvalidGrammar(grammar, NoMultipleSymbolRule.ERROR_CODE, MESSAGE, checker,2); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoNTInheritanceCycleTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoNTInheritanceCycleTest.java new file mode 100644 index 0000000000..214d3e95ba --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoNTInheritanceCycleTest.java @@ -0,0 +1,56 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.GrammarGlobalScopeTestFactory; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import de.monticore.grammar.grammar_withconcepts._symboltable.Grammar_WithConceptsGlobalScope; +import de.se_rwth.commons.logging.Log; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class NoNTInheritanceCycleTest extends CocoTest { + private final String grammar = "de.monticore.grammar.cocos.invalid.A4022.A4022"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new NoNTInheritanceCycle()); + } + + @Test + public void testInvalid() { + testInvalidGrammar(grammar, NoNTInheritanceCycle.ERROR_CODE, + String.format(NoNTInheritanceCycle.ERROR_MSG_FORMAT, "de.monticore.grammar.cocos.invalid.A4022.A4022.A"), checker); + } + + @Test + public void testInvalid2() { + + final Grammar_WithConceptsGlobalScope globalScope = GrammarGlobalScopeTestFactory.create(); + + // test grammar symbol + final MCGrammarSymbol grammarSymbol = (MCGrammarSymbol) globalScope.resolveMCGrammar(grammar + "b").orElse(null); + assertNotNull(grammarSymbol); + assertTrue(grammarSymbol.getAstGrammar().isPresent()); + + Log.getFindings().clear(); + checker.checkAll(grammarSymbol.getAstGrammar().get()); + + assertEquals(2, Log.getFindings().size()); + assertEquals(NoNTInheritanceCycle.ERROR_CODE + String.format(NoNTInheritanceCycle.ERROR_MSG_FORMAT, "de.monticore.grammar.cocos.invalid.A4022.A4022b.A"), + Log.getFindings().get(0).getMsg()); + assertEquals(NoNTInheritanceCycle.ERROR_CODE + String.format(NoNTInheritanceCycle.ERROR_MSG_FORMAT, "de.monticore.grammar.cocos.invalid.A4022.A4022b.B"), + Log.getFindings().get(1).getMsg()); + + } + + @Test + public void testValid() { + testValidGrammar("de.monticore.grammar.cocos.valid.ExtendNTs", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoNestedGenericsInAdditionalAttributesTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoNestedGenericsInAdditionalAttributesTest.java new file mode 100644 index 0000000000..592d698819 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoNestedGenericsInAdditionalAttributesTest.java @@ -0,0 +1,87 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class NoNestedGenericsInAdditionalAttributesTest extends CocoTest { + + public final String MESSAGE = " %srule does not allow the definition of nested generics. " + + "Problem in grammar '%s', rule for '%s', with additional attribute: '%s'."; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new NoNestedGenericsInAdditionalAttributes()); + } + + @Test + public void testInvalidNestedGeneric() { + testInvalidGrammar("de.monticore.grammar.cocos.invalid.A4102.A4102a", NoNestedGenericsInAdditionalAttributes.ERROR_CODE, + String.format(MESSAGE, "Ast", "A4102a", "A", "b:List>"), checker); + } + + @Test + public void testInvalidPlus() { + testInvalidGrammar("de.monticore.grammar.cocos.invalid.A4102.A4102b", NoNestedGenericsInAdditionalAttributes.ERROR_CODE, + String.format(MESSAGE, "Ast", "A4102b", "A", "b:Optional+"), checker); + } + + @Test + public void testInvalidQuestion() { + testInvalidGrammar("de.monticore.grammar.cocos.invalid.A4102.A4102c", NoNestedGenericsInAdditionalAttributes.ERROR_CODE, + String.format(MESSAGE, "Ast", "A4102c", "A", "b:Optional?"), checker); + } + + @Test + public void testInvalidMaxNumber() { + testInvalidGrammar("de.monticore.grammar.cocos.invalid.A4102.A4102d", NoNestedGenericsInAdditionalAttributes.ERROR_CODE, + String.format(MESSAGE, "Ast", "A4102d", "A", "b:Optional max=5"), checker); + } + + @Test + public void testInvalidStar() { + testInvalidGrammar("de.monticore.grammar.cocos.invalid.A4102.A4102e", NoNestedGenericsInAdditionalAttributes.ERROR_CODE, + String.format(MESSAGE, "Ast", "A4102e", "A", "b:Optional*"), checker); + } + + @Test + public void testInvalidMinNull() { + testInvalidGrammar("de.monticore.grammar.cocos.invalid.A4102.A4102f", NoNestedGenericsInAdditionalAttributes.ERROR_CODE, + String.format(MESSAGE, "Ast", "A4102f", "A", "b:Optional min=0"), checker); + } + + @Test + public void testInvalidSymbolRule() { + testInvalidGrammar("de.monticore.grammar.cocos.invalid.A4102.A4102g", NoNestedGenericsInAdditionalAttributes.ERROR_CODE, + String.format(MESSAGE, "Symbol", "A4102g", "A", "b:Set>"), checker); + } + + @Test + public void testInvalidScopeRule() { + testInvalidGrammar("de.monticore.grammar.cocos.invalid.A4102.A4102h", NoNestedGenericsInAdditionalAttributes.ERROR_CODE, + String.format(MESSAGE, "Scope", "A4102h", "A4102hScope", "b:Optional max=5"), checker); + } + + @Test + public void testInvalidMaxStar() { + testInvalidGrammar("de.monticore.grammar.cocos.invalid.A4102.A4102i", NoNestedGenericsInAdditionalAttributes.ERROR_CODE, + String.format(MESSAGE, "Ast", "A4102i", "A", "b:Optional max=*"), checker); + } + + @Test + public void testCorrectASTRule() { + testValidGrammar("de.monticore.grammar.cocos.valid.ASTRules", checker); + } + + @Test + public void testCorrectSymbolRule() { + testValidGrammar("de.monticore.grammar.cocos.valid.SymbolRules", checker); + } + + @Test + public void testCorrectScopeRule() { + testValidGrammar("de.monticore.grammar.cocos.valid.ScopeRule", checker); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoOverridingNTHasAnnotationTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoOverridingNTHasAnnotationTest.java new file mode 100644 index 0000000000..e1e0f481c1 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoOverridingNTHasAnnotationTest.java @@ -0,0 +1,29 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class NoOverridingNTHasAnnotationTest extends CocoTest{ + private final String grammar = "de.monticore.grammar.cocos.invalid.A4094.A4094"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new NoOverridingNTHasAnnotation()); + } + + @Test + public void testInvalid() { + testInvalidGrammar(grammar, NoOverridingNTHasAnnotation.ERROR_CODE, + String.format(NoOverridingNTHasAnnotation.ERROR_MSG_FORMAT, "Foo"), checker); + } + + @Test + public void testCorrect(){ + testValidGrammar("de.monticore.grammar.cocos.valid.Overriding", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoReplaceKeywordRuleOnConstantGroupTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoReplaceKeywordRuleOnConstantGroupTest.java new file mode 100644 index 0000000000..b1e0b8d329 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoReplaceKeywordRuleOnConstantGroupTest.java @@ -0,0 +1,28 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class NoReplaceKeywordRuleOnConstantGroupTest extends CocoTest { + private final String grammar = "de.monticore.grammar.cocos.invalid.A4162.A4162"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new NoReplaceKeywordRuleOnConstantGroup()); + } + + @Test + public void testInvalid() { + testInvalidGrammar(grammar, NoReplaceKeywordRuleOnConstantGroup.ERROR_CODE, + String.format(NoReplaceKeywordRuleOnConstantGroup.ERROR_MSG_FORMAT, "cg"), checker); + } + + @Test + public void testCorrect() { + testValidGrammar("de.monticore.grammar.cocos.valid.ReplaceKeyword", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoReplaceKeywordRuleOnUsageNamedAttributeTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoReplaceKeywordRuleOnUsageNamedAttributeTest.java new file mode 100644 index 0000000000..f538c0ed21 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoReplaceKeywordRuleOnUsageNamedAttributeTest.java @@ -0,0 +1,28 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class NoReplaceKeywordRuleOnUsageNamedAttributeTest extends CocoTest { + private final String grammar = "de.monticore.grammar.cocos.invalid.A4161.A4161"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new NoReplaceKeywordRuleOnUsageNamedAttribute()); + } + + @Test + public void testInvalid() { + testInvalidGrammar(grammar, NoReplaceKeywordRuleOnUsageNamedAttribute.ERROR_CODE, + String.format(NoReplaceKeywordRuleOnUsageNamedAttribute.ERROR_MSG_FORMAT, "b"), checker); + } + + @Test + public void testCorrect() { + testValidGrammar("de.monticore.grammar.cocos.valid.ReplaceKeyword", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoTokenDefinedTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoTokenDefinedTest.java new file mode 100644 index 0000000000..f55e3559a7 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoTokenDefinedTest.java @@ -0,0 +1,28 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; +public class NoTokenDefinedTest extends CocoTest { + private final String grammarInvalid = "de.monticore.grammar.cocos.invalid.A4101.A4101"; + private final String grammarValid = "de.monticore.grammar.cocos.valid.Attributes"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new NoTokenDefined()); + } + + @Test + public void testInvalid() { + testInvalidGrammar(grammarInvalid, NoTokenDefined.ERROR_CODE, + String.format(NoTokenDefined.ERROR_MSG_FORMAT, "A4101"), checker); + } + + @Test + public void testValid() { + testValidGrammar(grammarValid, checker); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoTokenModeInComponentGrammarTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoTokenModeInComponentGrammarTest.java new file mode 100644 index 0000000000..9e849cf092 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/NoTokenModeInComponentGrammarTest.java @@ -0,0 +1,28 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class NoTokenModeInComponentGrammarTest extends CocoTest { + private final String MESSAGE = " The lexical production %s must not define a mode in a Component Grammar."; + private final String grammar = "de.monticore.grammar.cocos.invalid.A4068.A4068"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new NoTokenModeInComponentGrammar()); + } + + @Test + public void testUpperCasedPackage() { + testInvalidGrammar(grammar, NoTokenModeInComponentGrammar.ERROR_CODE, + String.format(NoTokenModeInComponentGrammar.ERROR_MSG_FORMAT, + "EndTag"), checker); //kommt EndTag instead + } + + +} + + diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/OverridingAbstractNTsHaveNoSuperRulesTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/OverridingAbstractNTsHaveNoSuperRulesTest.java new file mode 100644 index 0000000000..dd657830a0 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/OverridingAbstractNTsHaveNoSuperRulesTest.java @@ -0,0 +1,32 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class OverridingAbstractNTsHaveNoSuperRulesTest extends CocoTest{ + + private final String MESSAGE = " The abstract production ArrayType overriding a production of " + + "a sub grammar must not extend the production Name.\n" + + "Hint: Overriding productions can only implement interfaces."; + private final String grammar = "de.monticore.grammar.cocos.invalid.A4002.A4002"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new OverridingAbstractNTsHaveNoSuperRules()); + } + + @Test + public void testInvalid() { + testInvalidGrammar(grammar, OverridingAbstractNTsHaveNoSuperRules.ERROR_CODE, MESSAGE, checker); + } + + @Test + public void testCorrect(){ + testValidGrammar("de.monticore.grammar.cocos.valid.Overriding", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/OverridingAbstractNTsTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/OverridingAbstractNTsTest.java new file mode 100644 index 0000000000..805d01d34c --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/OverridingAbstractNTsTest.java @@ -0,0 +1,50 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class OverridingAbstractNTsTest extends CocoTest{ + + private final String MESSAGE = " The production for the abstract nonterminal ArrayType must not be overridden\n" + + "by a production for an %s nonterminal."; + private final String grammar = "de.monticore.grammar.cocos.invalid.A4008.A4008"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new OverridingAbstractNTs()); + } + + @Test + public void testInvalidA() { + testInvalidGrammar(grammar + "a", OverridingAbstractNTs.ERROR_CODE, + String.format(MESSAGE, "interface"), checker); + } + + @Test + public void testInvalidB() { + testInvalidGrammar(grammar + "b", OverridingAbstractNTs.ERROR_CODE, + String.format(MESSAGE, "enum"), checker); + } + + @Test + public void testInvalidC() { + testInvalidGrammar(grammar + "c", OverridingAbstractNTs.ERROR_CODE, + String.format(MESSAGE, "lexical"), checker); + } + + @Test + public void testInvalidD() { + testInvalidGrammar(grammar + "d", OverridingAbstractNTs.ERROR_CODE, + String.format(MESSAGE, "external"), checker); + } + + @Test + public void testCorrect(){ + testValidGrammar("de.monticore.grammar.cocos.valid.Overriding", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/OverridingAdditionalAttriutesTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/OverridingAdditionalAttriutesTest.java new file mode 100644 index 0000000000..80b5f97b0c --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/OverridingAdditionalAttriutesTest.java @@ -0,0 +1,46 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + + +public class OverridingAdditionalAttriutesTest extends CocoTest{ + private final String grammar = "de.monticore.grammar.cocos.invalid.A4035.A4035"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new OverridingAdditionalAttributes()); + } + + @Test + public void testInvalid() { + testInvalidGrammar(grammar, OverridingAdditionalAttributes.ERROR_CODE, + String.format(OverridingAdditionalAttributes.ERROR_MSG_FORMAT, "b", "S"), + checker, 2); + } + + @Test + public void testInvalid1() { + testInvalidGrammar(grammar+"a", OverridingAdditionalAttributes.ERROR_CODE, + String.format(OverridingAdditionalAttributes.ERROR_MSG_FORMAT, "b", "S"), + checker, 2); + } + + @Test + public void testInvalid2() { + testInvalidGrammar(grammar+"b", OverridingAdditionalAttributes.ERROR_CODE, + String.format(OverridingAdditionalAttributes.ERROR_MSG_FORMAT, "b", "T"), + checker); + } + + @Test + public void testCorrect(){ + testValidGrammar("de.monticore.grammar.cocos.valid.SymbolRules", checker); + } + + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/OverridingEnumNTsTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/OverridingEnumNTsTest.java new file mode 100644 index 0000000000..0886167530 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/OverridingEnumNTsTest.java @@ -0,0 +1,30 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class OverridingEnumNTsTest extends CocoTest { + + private final String MESSAGE = " The production for the enum nonterminal E must not be overridden."; + private final String grammar = "de.monticore.grammar.cocos.invalid.A4027.A4027"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new OverridingEnumNTs()); + } + + @Test + public void testInvalid() { + testInvalidGrammar(grammar, OverridingEnumNTs.ERROR_CODE, MESSAGE, checker); + } + + @Test + public void testCorrect() { + testValidGrammar("de.monticore.grammar.cocos.valid.Overriding", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/OverridingInterfaceNTsTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/OverridingInterfaceNTsTest.java new file mode 100644 index 0000000000..8a4d2263ba --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/OverridingInterfaceNTsTest.java @@ -0,0 +1,31 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class OverridingInterfaceNTsTest extends CocoTest{ + + private final String MESSAGE = " The production for the interface nonterminal ReturnType must not be overridden."; + private final String grammar = "de.monticore.grammar.cocos.invalid.A4007.A4007a"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new OverridingInterfaceNTs()); + } + + @Test + public void testInvalid(){ + testInvalidGrammar(grammar, OverridingInterfaceNTs.ERROR_CODE, MESSAGE, checker); + } + + + @Test + public void testCorrect(){ + testValidGrammar("de.monticore.grammar.cocos.valid.Overriding", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/OverridingLexNTsTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/OverridingLexNTsTest.java new file mode 100644 index 0000000000..00b24e2cb7 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/OverridingLexNTsTest.java @@ -0,0 +1,32 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class OverridingLexNTsTest extends CocoTest{ + + private final String MESSAGE = " The lexical production CARDINALITY must not use a different " + + "type to store the token than the overridden production."; + private final String grammar = "de.monticore.grammar.cocos.invalid.A4026.A4026b"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new OverridingLexNTs()); + } + + @Test + public void testInvalid() { + testInvalidGrammar(grammar, OverridingLexNTs.ERROR_CODE, String.format(MESSAGE, "interface"), + checker); + } + + @Test + public void testCorrect(){ + testValidGrammar("de.monticore.grammar.cocos.valid.Overriding", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/OverridingNTHasNoAnnotationTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/OverridingNTHasNoAnnotationTest.java new file mode 100644 index 0000000000..50e62847c4 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/OverridingNTHasNoAnnotationTest.java @@ -0,0 +1,29 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class OverridingNTHasNoAnnotationTest extends CocoTest{ + private final String grammar = "de.monticore.grammar.cocos.invalid.A4098.A4098"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new OverridingNTHasNoAnnotation()); + } + + @Test + public void testInvalid() { + testInvalidGrammar(grammar, OverridingNTHasNoAnnotation.ERROR_CODE, + String.format(OverridingNTHasNoAnnotation.ERROR_MSG_FORMAT, "Foo", "de.monticore.grammar.cocos.invalid.A4098.A4098Super.Foo" ), checker); + } + + @Test + public void testCorrect(){ + testValidGrammar("de.monticore.grammar.cocos.valid.Overriding", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/OverridingNTsHaveNoSuperRulesTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/OverridingNTsHaveNoSuperRulesTest.java new file mode 100644 index 0000000000..d16773c60f --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/OverridingNTsHaveNoSuperRulesTest.java @@ -0,0 +1,32 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class OverridingNTsHaveNoSuperRulesTest extends CocoTest{ + + private final String MESSAGE = " The production QualifiedName overriding a production of " + + "a sub grammar must not extend the production Name.\n" + + "Hint: Overriding productions can only implement interfaces."; + private final String grammar = "de.monticore.grammar.cocos.invalid.A4001.A4001"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new OverridingNTsHaveNoSuperRules()); + } + + @Test + public void testInvalid(){ + testInvalidGrammar(grammar, OverridingNTsHaveNoSuperRules.ERROR_CODE, MESSAGE, checker); + } + + @Test + public void testCorrect(){ + testValidGrammar("de.monticore.grammar.cocos.valid.Overriding", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/OverridingNTsTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/OverridingNTsTest.java new file mode 100644 index 0000000000..fce9d85774 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/OverridingNTsTest.java @@ -0,0 +1,60 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +import static de.monticore.grammar.cocos.OverridingNTs.ERROR_CODE; +import static java.lang.String.format; + + +public class OverridingNTsTest extends CocoTest { + + private final String MESSAGE = " The production for the nonterminal QualifiedName must not be overridden " + + "by a production for an %s nonterminal."; + private final String grammar = "de.monticore.grammar.cocos.invalid.A4009.A4009"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new OverridingNTs()); + } + + @Test + public void testInvalidA() { + testInvalidGrammar(grammar + "a", ERROR_CODE, + format(MESSAGE, "interface"), checker); + } + + @Test + public void testInvalidB() { + testInvalidGrammar(grammar + "b", ERROR_CODE, format(MESSAGE, "enum"), + checker); + } + + @Test + public void testInvalidC() { + testInvalidGrammar(grammar + "c", ERROR_CODE, format(MESSAGE, "lexical"), + checker); + } + + @Test + public void testInvalidD() { + testInvalidGrammar(grammar + "d", ERROR_CODE, format(MESSAGE, "external"), + checker); + } + + @Test + public void testInvalidE() { + testInvalidGrammar(grammar + "e", ERROR_CODE, format(MESSAGE, "abstract"), + checker); + } + + @Test + public void testCorrect() { + testValidGrammar("de.monticore.grammar.cocos.valid.Overriding", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/PackageNameLowerCaseTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/PackageNameLowerCaseTest.java new file mode 100644 index 0000000000..7f8f449dba --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/PackageNameLowerCaseTest.java @@ -0,0 +1,32 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class PackageNameLowerCaseTest extends CocoTest{ + + private final String MESSAGE = " The name C used for the nonterminal A referenced by the production B " + + "should start with a lower-case letter."; + private final String grammar = "de.monticore.grammar.cocos.invalid.A4006.A4006"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new PackageNameLowerCase()); + } + + @Test + public void testUpperCasedPackage() { + testInvalidGrammar(grammar, PackageNameLowerCase.ERROR_CODE, String.format(PackageNameLowerCase.ERROR_MSG_FORMAT, "de.monticore.grammar.cocos.invalid.A4006"), checker); + } + + + @Test + public void testAttributes(){ + testValidGrammar("de.monticore.grammar.cocos.valid.Attributes", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ProdAndExtendedProdUseSameAttrNameForDiffNTsTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ProdAndExtendedProdUseSameAttrNameForDiffNTsTest.java new file mode 100644 index 0000000000..f3cdc27de0 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ProdAndExtendedProdUseSameAttrNameForDiffNTsTest.java @@ -0,0 +1,122 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class ProdAndExtendedProdUseSameAttrNameForDiffNTsTest extends CocoTest { + + private final String MESSAGE = " The production B extending the production A must not use the\n" + + "name a for the nonterminal D as A already uses this name for the nonterminal C."; + private final String grammar = "de.monticore.grammar.cocos.invalid.A4024.A4024"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new ProdAndExtendedProdUseSameAttrNameForDiffNTs()); + } + + @Test + public void testInvalid() { + testInvalidGrammar(grammar, ProdAndExtendedProdUseSameAttrNameForDiffNTs.ERROR_CODE, MESSAGE, + checker); + } + + @Test + public void testInvalid_b() { + //Super = C; + //Sub extends Super = c:A; + String message = " The production Sub extending the production Super must not use the\n" + + "name c for the nonterminal A as Super already uses this name for the nonterminal C."; + testInvalidGrammar(grammar + "_b", ProdAndExtendedProdUseSameAttrNameForDiffNTs.ERROR_CODE, message, + checker); + } + + @Test + public void testInvalid_c() { + // Super = c:["state"]; + // Sub extends Super = c:A; + String message = " The production Sub extending the production Super must not use the\n" + + "name c for the nonterminal A as Super already uses this name for the production that is not a constant group."; + testInvalidGrammar(grammar + "_c", ProdAndExtendedProdUseSameAttrNameForDiffNTs.ERROR_CODE, message, + checker); + } + + @Test + public void testInvalid_d() { + // Super = c:"state"; + // Sub extends Super = c:A; + String message = " The production Sub extending the production Super must not use the\n" + + "name c for the nonterminal A as Super already uses this name for the production that is a terminal named c."; + testInvalidGrammar(grammar + "_d", ProdAndExtendedProdUseSameAttrNameForDiffNTs.ERROR_CODE, message, + checker); + } + + @Test + public void testInvalid_e() { + // Super = ["c"]; + // Sub extends Super = c:A; + String message = " The production Sub extending the production Super must not use the\n" + + "name c for the nonterminal A as Super already uses this name for the production that is not a constant group."; + testInvalidGrammar(grammar + "_e", ProdAndExtendedProdUseSameAttrNameForDiffNTs.ERROR_CODE, message, + checker); + } + + @Test + public void testInvalid_f() { + // Super = [c:"state"]; + // Sub extends Super = c:A; + String message = " The production Sub extending the production Super must not use the\n" + + "name c for the nonterminal A as Super already uses this name for the production that is not a constant group."; + testInvalidGrammar(grammar + "_f", ProdAndExtendedProdUseSameAttrNameForDiffNTs.ERROR_CODE, message, + checker); + } + + @Test + public void testInvalid_g() { + // Super1 = c:D; + // Sub extends Super = c:A; + String message = " The production Sub extending the production Super must not use the\n" + + "name c for the nonterminal A as Super already uses this name for the nonterminal D."; + testInvalidGrammar(grammar + "_g", ProdAndExtendedProdUseSameAttrNameForDiffNTs.ERROR_CODE, message, + checker); + } + + @Test + public void testInvalid_h() { + // Super1 = c:D; + // Sub extends Super = c:A; + String message = " The production Sub extending the production Super must not use the\n" + + "name c for the nonterminal A as Super already uses this name for the nonterminal Name."; + testInvalidGrammar(grammar + "_h", ProdAndExtendedProdUseSameAttrNameForDiffNTs.ERROR_CODE, message, + checker); + } + + @Test + public void testInvalid_i() { + // Super1 = c:D; + // Sub extends Super = c:A; + String message = " The production Sub extending the production Super must not use the\n" + + "name c for the nonterminal A as Super already uses this name for the nonterminal D."; + testInvalidGrammar(grammar + "_i", ProdAndExtendedProdUseSameAttrNameForDiffNTs.ERROR_CODE, message, + checker, 4); + } + + @Test + public void testCorrect() { + testValidGrammar("de.monticore.grammar.cocos.valid.Attributes", checker); + } + + @Test + public void testCorrect2() { + testValidGrammar("de.monticore.common.TestTypes", checker); + } + + @Test + public void testCorrect3() { + testValidGrammar("de.monticore.grammar.cocos.valid.ProdAndExtendedProdUseSameAttrNameForDiffNTs", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ProdAndOverriddenProdUseSameAttrNameForDiffNTsTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ProdAndOverriddenProdUseSameAttrNameForDiffNTsTest.java new file mode 100644 index 0000000000..247b88c127 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ProdAndOverriddenProdUseSameAttrNameForDiffNTsTest.java @@ -0,0 +1,125 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class ProdAndOverriddenProdUseSameAttrNameForDiffNTsTest extends CocoTest { + + private final String MESSAGE = " The overriding production QualifiedName must not use " + + "the name part for the nonterminal StringLiteral as the overridden production uses this name for the nonterminal Name"; + private final String grammar = "de.monticore.grammar.cocos.invalid.A4025.A4025"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new ProdAndOverriddenProdUseSameAttrNameForDiffNTs()); + } + + @Test + public void testInvalid() { + testInvalidGrammar(grammar, ProdAndOverriddenProdUseSameAttrNameForDiffNTs.ERROR_CODE, MESSAGE, + checker); + } + + @Test + public void testInvalid_b() { + //super + //C; + //A = C; + //sub + // B; + // A = c:B; + String message = " The overriding production A must not use the name c for the nonterminal B " + + "as the overridden production uses this name for the nonterminal C"; + testInvalidGrammar(grammar + "_sub_b", ProdAndOverriddenProdUseSameAttrNameForDiffNTs.ERROR_CODE, message, + checker); + } + + @Test + public void testInvalid_c() { + //super + //C; + //A = c:["b"]; + //sub + // B; + // A = c:B; + String message = " The overriding production A must not use the name c for the nonterminal B" + + " as the overridden production uses this name for the production of a constant group"; + testInvalidGrammar(grammar + "_sub_c", ProdAndOverriddenProdUseSameAttrNameForDiffNTs.ERROR_CODE, message, + checker); + } + + @Test + public void testInvalid_d() { + //super + //A = ["c"]; + //sub + // B; + // A = c:B; + String message = " The overriding production A must not use the name c for the nonterminal B" + + " as the overridden production uses this name for the production of a constant group"; + testInvalidGrammar(grammar + "_sub_d", ProdAndOverriddenProdUseSameAttrNameForDiffNTs.ERROR_CODE, message, + checker); + } + + @Test + public void testInvalid_e() { + //super + //A = [c:"b"]; + //sub + // B; + // A = c:B; + String message = " The overriding production A must not use the name c for the nonterminal B " + + "as the overridden production uses this name for the production of a constant group"; + testInvalidGrammar(grammar + "_sub_e", ProdAndOverriddenProdUseSameAttrNameForDiffNTs.ERROR_CODE, message, + checker); + } + + @Test + public void testInvalid_f() { + //super + // D; + // A = c:D; + //sub + // B; + // A = c:B; + String message = " The overriding production A must not use the name c for the nonterminal B" + + " as the overridden production uses this name for the nonterminal D"; + testInvalidGrammar(grammar + "_sub_f", ProdAndOverriddenProdUseSameAttrNameForDiffNTs.ERROR_CODE, message, + checker); + } + + @Test + public void testInvalid_g() { + //super + //A = c:"b"; + //sub + // B; + // A = c:B; + String message = " The overriding production A must not use the name c for the nonterminal " + + "B as the overridden production uses this name for the production of a terminal"; + testInvalidGrammar(grammar + "_sub_g", ProdAndOverriddenProdUseSameAttrNameForDiffNTs.ERROR_CODE, message, + checker); + } + + + @Test + public void testCorrect() { + testValidGrammar("de.monticore.grammar.cocos.valid.Attributes", checker); + } + + @Test + public void testCorrect2() { + testValidGrammar("de.monticore.common.TestTypes", checker); + } + + @Test + public void testCorrect3() { + testValidGrammar("de.monticore.grammar.cocos.valid.ProdAndOverriddenProdUseSameAttrNameForDiffNTs_sub", checker); + } + + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ProdExtendsNotExistingProdTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ProdExtendsNotExistingProdTest.java new file mode 100644 index 0000000000..c82142054f --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ProdExtendsNotExistingProdTest.java @@ -0,0 +1,35 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class ProdExtendsNotExistingProdTest extends CocoTest { + + private final String MESSAGE = " The production Sup extends or implements the non-existent production Super"; + private final String grammar = "de.monticore.grammar.cocos.invalid.A0113.A0113"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new ProdExtendsNotExistingProd()); + } + + @Test + public void testInvalid(){ + testInvalidGrammar(grammar,ProdExtendsNotExistingProd.ERROR_CODE,MESSAGE,checker); + } + + @Test + public void testInvalid_b(){ + testInvalidGrammar(grammar+"a",ProdExtendsNotExistingProd.ERROR_CODE,MESSAGE,checker); + } + + @Test + public void testValid(){ + testValidGrammar("de.monticore.grammar.cocos.valid.ProdExtendsNotExistingProd", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ProdStartsWithCapitalTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ProdStartsWithCapitalTest.java new file mode 100644 index 0000000000..ecb0792f18 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ProdStartsWithCapitalTest.java @@ -0,0 +1,49 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class ProdStartsWithCapitalTest extends CocoTest { + + private final String MESSAGE = " The nonterminal a should not start with a lower-case letter.";; + private final String grammar = "de.monticore.grammar.cocos.invalid.A4031.A4031"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new ProdStartsWithCapital()); + } + + @Test + public void testNT() { + testInvalidGrammar(grammar + "a", ProdStartsWithCapital.ERROR_CODE, MESSAGE, checker); + } + + @Test + public void testAbstractNT() { + testInvalidGrammar(grammar + "b", ProdStartsWithCapital.ERROR_CODE, MESSAGE, checker); + } + + @Test + public void testInterfaceNT() { + testInvalidGrammar(grammar + "c", ProdStartsWithCapital.ERROR_CODE, MESSAGE, checker); + } + + @Test + public void testExternalNT() { + testInvalidGrammar(grammar + "d", ProdStartsWithCapital.ERROR_CODE, MESSAGE, checker); + } + + @Test + public void testEnumNT() { + testInvalidGrammar(grammar + "e", ProdStartsWithCapital.ERROR_CODE, MESSAGE, checker); + } + + @Test + public void testExtendInterfaceNT(){ + testValidGrammar("de.monticore.grammar.cocos.valid.ExtendNTs", checker); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ProdWithDoubleAnnosTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ProdWithDoubleAnnosTest.java new file mode 100644 index 0000000000..42c8401f2e --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ProdWithDoubleAnnosTest.java @@ -0,0 +1,33 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +import static java.lang.String.format; +import de.se_rwth.commons.logging.Log; + +public class ProdWithDoubleAnnosTest extends CocoTest { + private final String grammar = "de.monticore.grammar.cocos.invalid.A4119.A4119"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new ProdWithDoubleAnnos()); + } + + @Test + public void testInvalid() { + testInvalidGrammar(grammar, ProdWithDoubleAnnos.ERROR_CODE, + format(ProdWithDoubleAnnos.ERROR_MSG_FORMAT, "A", "@Override"), checker); + } + + @Test + public void testInvalida() { + testInvalidGrammar(grammar + "a", ProdWithDoubleAnnos.ERROR_CODE, + format(ProdWithDoubleAnnos.ERROR_MSG_FORMAT, "A", "@Deprecated"), checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ProdWithExtensionMustNotBeOverriddenTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ProdWithExtensionMustNotBeOverriddenTest.java new file mode 100644 index 0000000000..2657ae7c47 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ProdWithExtensionMustNotBeOverriddenTest.java @@ -0,0 +1,41 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class ProdWithExtensionMustNotBeOverriddenTest extends CocoTest{ + + private final String MESSAGE = " The production ArrayType must not be overridden because there" + + " already exist productions extending it."; + private final String grammar = "de.monticore.grammar.cocos.invalid.A4010.A4010"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new ProdWithExtensionMustNotBeOverridden()); + } + + @Test + public void testInvalid() { + testInvalidGrammar(grammar, ProdWithExtensionMustNotBeOverridden.ERROR_CODE, MESSAGE, checker); + } + + @Test + public void testCorrect(){ + testValidGrammar("de.monticore.grammar.cocos.valid.Overriding2", checker); + } + + @Test + public void testCorrect2(){ + testValidGrammar("de.monticore.common.TestLiterals", checker); + } + + @Test + public void testCorrect3(){ + testValidGrammar("de.monticore.common.TestTypes", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ReferenceSymbolNotNameTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ReferenceSymbolNotNameTest.java new file mode 100644 index 0000000000..f912cfc3c8 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ReferenceSymbolNotNameTest.java @@ -0,0 +1,29 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class ReferenceSymbolNotNameTest extends CocoTest { + + private final String grammar = "de.monticore.grammar.cocos.invalid.A4039.A4039"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new ReferenceSymbolNotName()); + } + + @Test + public void testInvalid() { + testInvalidGrammar(grammar , ReferenceSymbolNotName.ERROR_CODE, + ReferenceSymbolNotName.ERROR_MSG_FORMAT, checker); + } + + @Test + public void testCorrect(){ + testValidGrammar("de.monticore.grammar.cocos.valid.ReferencedSymbol", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ReferenceSymbolSameAttributeTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ReferenceSymbolSameAttributeTest.java new file mode 100644 index 0000000000..4e8c54fd57 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ReferenceSymbolSameAttributeTest.java @@ -0,0 +1,28 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class ReferenceSymbolSameAttributeTest extends CocoTest { + private final String grammar = "de.monticore.grammar.cocos.invalid.A4100.A4100"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new ReferenceSymbolSameAttribute()); + } + + @Test + public void testInvalid() { + testInvalidGrammar(grammar, ReferenceSymbolSameAttributeVisitor.ERROR_CODE, + String.format(ReferenceSymbolSameAttributeVisitor.ERROR_MSG_FORMAT, "\"ref\"", "A","B"), checker); + } + + @Test + public void testCorrect(){ + testValidGrammar("de.monticore.grammar.cocos.valid.ReferencedSymbol", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ReferencedNTNotDefinedTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ReferencedNTNotDefinedTest.java new file mode 100644 index 0000000000..67a0b722c2 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ReferencedNTNotDefinedTest.java @@ -0,0 +1,61 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class ReferencedNTNotDefinedTest extends CocoTest { + + private final String MESSAGE = " The production A must not reference the " + + "%snonterminal B because there exists no defining production for B."; + private final String grammar = "de.monticore.grammar.cocos.invalid.A2030.A2030"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new ReferencedNTNotDefined()); + } + + @Test + public void testInvalidA() { + testInvalidGrammar(grammar + "a", ReferencedNTNotDefined.ERROR_CODE, + String.format(MESSAGE, ""), checker); + } + + @Test + public void testInvalidB() { + testInvalidGrammar(grammar + "b", ReferencedNTNotDefined.ERROR_CODE, + String.format(MESSAGE, "interface "), checker); + } + + @Test + public void testInvalidC() { + testInvalidGrammar(grammar + "c", ReferencedNTNotDefined.ERROR_CODE, + String.format(MESSAGE, "interface "), checker); + } + + @Test + public void testInvalidD() { + testInvalidGrammar(grammar + "d", ReferencedNTNotDefined.ERROR_CODE, + String.format(MESSAGE, ""), checker); + } + + @Test + public void testInvalidE() { + testInvalidGrammar(grammar + "e", ReferencedNTNotDefined.ERROR_CODE, + String.format(MESSAGE, "interface "), checker); + } + + @Test + public void testCorrect(){ + testValidGrammar("de.monticore.grammar.cocos.valid.Attributes", checker); + } + + @Test + public void testCorrect2(){ + testValidGrammar("de.monticore.grammar.cocos.valid.Overriding", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ReferencedSymbolExistsTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ReferencedSymbolExistsTest.java new file mode 100644 index 0000000000..15c09c3816 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ReferencedSymbolExistsTest.java @@ -0,0 +1,28 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class ReferencedSymbolExistsTest extends CocoTest { + private final String grammar = "de.monticore.grammar.cocos.invalid.A4037.A4037"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new ReferencedSymbolExists()); + } + + @Test + public void testInvalid() { + testInvalidGrammar(grammar, ReferencedSymbolExists.ERROR_CODE, + String.format(ReferencedSymbolExists.ERROR_MSG_FORMAT, "C"), checker); + } + + @Test + public void testCorrect() { + testValidGrammar("de.monticore.grammar.cocos.valid.ReferencedSymbol", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/RuleComponentsCompatibleTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/RuleComponentsCompatibleTest.java new file mode 100644 index 0000000000..75119811ac --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/RuleComponentsCompatibleTest.java @@ -0,0 +1,88 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class RuleComponentsCompatibleTest extends CocoTest { + private final String grammar = "de.monticore.grammar.cocos.invalid.A4090.A4090"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new RuleComponentsCompatible()); + } + + @Test + public void testInvalidA() { + testInvalidGrammar(grammar + "a", RuleComponentsCompatible.ERROR_CODE, + String.format(RuleComponentsCompatible.ERROR_MSG_FORMAT, "B", "who"), checker); + } + + @Test + public void testInvalidB() { + testInvalidGrammar(grammar + "b", RuleComponentsCompatible.ERROR_CODE, + String.format(RuleComponentsCompatible.ERROR_MSG_FORMAT, "B", "who"), checker); + } + + @Test + public void testInvalidC() { + testInvalidGrammar(grammar + "c", RuleComponentsCompatible.ERROR_CODE, + String.format(RuleComponentsCompatible.ERROR_MSG_FORMAT, "B", "who"), checker); + } + + @Test + public void testInvalidD() { + testInvalidGrammar(grammar + "d", RuleComponentsCompatible.ERROR_CODE, + String.format(RuleComponentsCompatible.ERROR_MSG_FORMAT, "B", "who"), checker); + } + + @Test + public void testInvalidE() { + testInvalidGrammar(grammar + "e", RuleComponentsCompatible.ERROR_CODE, + String.format(RuleComponentsCompatible.ERROR_MSG_FORMAT, "B", "who"), checker); + } + + @Test + public void testInvalidF() { + testInvalidGrammar(grammar + "f", RuleComponentsCompatible.ERROR_CODE, + String.format(RuleComponentsCompatible.ERROR_MSG_FORMAT, "B", "who"), checker); + } + + @Test + public void testInvalidG() { + testInvalidGrammar(grammar + "g", RuleComponentsCompatible.ERROR_CODE, + String.format(RuleComponentsCompatible.ERROR_MSG_FORMAT, "B", "who"), checker); + } + + @Test + public void testInvalidH() { + testInvalidGrammar(grammar + "h", RuleComponentsCompatible.ERROR_CODE, + String.format(RuleComponentsCompatible.ERROR_MSG_FORMAT, "B", "who"), checker); + } + + @Test + public void testInvalidI() { + testInvalidGrammar(grammar + "i", RuleComponentsCompatible.ERROR_CODE, + String.format(RuleComponentsCompatible.ERROR_MSG_FORMAT, "B", "who"), checker); + } + + @Test + public void testInvalidJ() { + testInvalidGrammar(grammar + "j", RuleComponentsCompatible.ERROR_CODE, + String.format(RuleComponentsCompatible.ERROR_MSG_FORMAT, "B", "a"), checker); + } + + @Test + public void testInvalidK() { + testInvalidGrammar(grammar + "k", RuleComponentsCompatible.ERROR_CODE, + String.format(RuleComponentsCompatible.ERROR_MSG_FORMAT, "B", "a"), checker); + } + + @Test + public void testCorrect() { + testValidGrammar("de.monticore.grammar.cocos.valid.ReferencedSymbol", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ScopeProdOverwrittenByScopeTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ScopeProdOverwrittenByScopeTest.java new file mode 100644 index 0000000000..4106c6b9e3 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/ScopeProdOverwrittenByScopeTest.java @@ -0,0 +1,27 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class ScopeProdOverwrittenByScopeTest extends CocoTest { + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new ScopeProdOverwrittenByScope()); + } + + @Test + public void TestInvalid() { + testInvalidGrammar("de.monticore.grammar.cocos.invalid.A0275.A0275Sub", ScopeProdOverwrittenByScope.ERROR_CODE, + String.format(ScopeProdOverwrittenByScope.ERROR_MSG_FORMAT, "Foo", "A0275Super", "Foo", "A0275Sub"), checker); + } + + @Test + public void testCorrect() { + testValidGrammar("de.monticore.grammar.cocos.valid.SymbolAndScopeOverwriting", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/SplitRuleInvalidTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/SplitRuleInvalidTest.java new file mode 100644 index 0000000000..daa84a6307 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/SplitRuleInvalidTest.java @@ -0,0 +1,35 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class SplitRuleInvalidTest extends CocoTest { + private final String grammar = "de.monticore.grammar.cocos.invalid.A4062.A4062"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new SplitRuleInvalid()); + } + + @Test + public void testInvalid1() { + testInvalidGrammar(grammar+"a", SplitRuleInvalid.ERROR_CODE, + String.format(SplitRuleInvalid.ERROR_MSG_FORMAT, "b-"), checker); + } + + @Test + public void testInvalid2() { + testInvalidGrammar(grammar+"b", SplitRuleInvalid.ERROR_CODE, + String.format(SplitRuleInvalid.ERROR_MSG_FORMAT, "-"), checker); + } + + @Test + public void testCorrect() { + testValidGrammar("de.monticore.grammar.cocos.valid.Attributes", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/SubrulesUseInterfaceNTsTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/SubrulesUseInterfaceNTsTest.java new file mode 100644 index 0000000000..795629c628 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/SubrulesUseInterfaceNTsTest.java @@ -0,0 +1,72 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class SubrulesUseInterfaceNTsTest extends CocoTest { + + private final String MESSAGE = " The production %s must use the Component %s from interface %s."; + private final String grammar = "de.monticore.grammar.cocos.invalid.A4047.A4047"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new SubrulesUseInterfaceNTs()); + } + + @Test + public void TestInvalid1() { + testInvalidGrammar(grammar + "a", SubrulesUseInterfaceNTs.ERROR_CODE, + String.format(MESSAGE, "B", "c", "A"), checker); + } + + @Test + public void TestInvalid2() { + testInvalidGrammar(grammar + "b", SubrulesUseInterfaceNTs.ERROR_CODE, + String.format(MESSAGE, "B", "c", "A"), checker); + } + + @Test + public void TestInvalid3() { + testInvalidGrammar(grammar + "c", SubrulesUseInterfaceNTs.ERROR_CODE, + String.format(MESSAGE, "D", "e", "A"), checker); + } + + @Test + public void TestInvalid4() { + testInvalidGrammar(grammar + "d", SubrulesUseInterfaceNTs.ERROR_CODE, + String.format(MESSAGE, "B", "foo", "A"), checker); + } + + @Test + public void TestInvalid5() { + testInvalidGrammar(grammar + "e", SubrulesUseInterfaceNTs.ERROR_CODE, + String.format(MESSAGE, "AImpl", "d*", "A"), checker); + } + + @Test + public void TestInvalid6() { + testInvalidGrammar(grammar + "f", SubrulesUseInterfaceNTs.ERROR_CODE, + String.format(MESSAGE, "BImpl", "d*", "B"), checker); + } + + @Test + public void TestInvalid7() { + testInvalidGrammar(grammar + "g", SubrulesUseInterfaceNTs.ERROR_CODE, + String.format(MESSAGE, "AImpl", "d?", "A"), checker); + } + + @Test + public void testCorrect() { + testValidGrammar("de.monticore.grammar.cocos.valid.ImplementInterfaceNTs", checker); + } + + @Test + public void testCorrectWithOverwriting() { + testValidGrammar(grammar + "i", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/SymbolProdOverwrittenBySymbolTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/SymbolProdOverwrittenBySymbolTest.java new file mode 100644 index 0000000000..6b3d5eadb1 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/SymbolProdOverwrittenBySymbolTest.java @@ -0,0 +1,27 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class SymbolProdOverwrittenBySymbolTest extends CocoTest { + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new SymbolProdOverwrittenBySymbol()); + } + + @Test + public void TestInvalid() { + testInvalidGrammar("de.monticore.grammar.cocos.invalid.A0274.A0274Sub", SymbolProdOverwrittenBySymbol.ERROR_CODE, + String.format(SymbolProdOverwrittenBySymbol.ERROR_MSG_FORMAT, "Foo", "A0274Super", "Foo", "A0274Sub"), checker); + } + + @Test + public void testCorrect() { + testValidGrammar("de.monticore.grammar.cocos.valid.SymbolAndScopeOverwriting", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/SymbolRuleHasNameTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/SymbolRuleHasNameTest.java new file mode 100644 index 0000000000..e0f40f8164 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/SymbolRuleHasNameTest.java @@ -0,0 +1,28 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class SymbolRuleHasNameTest extends CocoTest { + + private final String grammar = "de.monticore.grammar.cocos.invalid.A0118.A0118"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new SymbolRuleHasName()); + } + + @Test + public void testValid() { + testValidGrammar("de.monticore.grammar.cocos.valid.SymbolRules", checker); + } + + @Test + public void testInvalid() { + testInvalidGrammar(grammar, SymbolRuleHasName.ERROR_CODE, String.format(SymbolRuleHasName.ERROR_MSG, "StringReader:<8,4>"), checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/SymbolRuleWithoutSymbolRefTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/SymbolRuleWithoutSymbolRefTest.java new file mode 100644 index 0000000000..dbcd97a0c1 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/SymbolRuleWithoutSymbolRefTest.java @@ -0,0 +1,36 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class SymbolRuleWithoutSymbolRefTest extends CocoTest { + private final String grammar = "de.monticore.grammar.cocos.invalid.A0117.A0117"; + private final String grammar2 = "de.monticore.grammar.cocos.invalid.A0117.A0117a"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new SymbolRuleWithoutSymbolRef()); + } + + @Test + public void testInvalid() { + testInvalidGrammar(grammar, SymbolRuleWithoutSymbolRef.ERROR_CODE, + String.format(SymbolRuleWithoutSymbolRef.ERROR_MSG_FORMAT, "B"), checker); + } + + @Test + public void testInvalid2() { + testInvalidGrammar(grammar2, SymbolRuleWithoutSymbolRef.ERROR_CODE, + String.format(SymbolRuleWithoutSymbolRef.ERROR_MSG_FORMAT, "A"), checker); + } + + @Test + public void testCorrect() { + testValidGrammar("de.monticore.grammar.cocos.valid.Attributes", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/SymbolWithManyNamesTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/SymbolWithManyNamesTest.java new file mode 100644 index 0000000000..824cf84e48 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/SymbolWithManyNamesTest.java @@ -0,0 +1,33 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class SymbolWithManyNamesTest extends CocoTest { + private final String grammar = "de.monticore.grammar.cocos.invalid.A0279.A0279"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new SymbolWithManyNames()); + } + + @Test + public void testInvalid() { + testInvalidGrammar(grammar, SymbolWithManyNames.ERROR_CODE, + String.format(SymbolWithManyNames.ERROR_MSG_FORMAT, "A"), checker); + } + + @Test + public void testInvalida() { + testInvalidGrammar(grammar+"a", SymbolWithManyNames.ERROR_CODE, + String.format(SymbolWithManyNames.ERROR_MSG_FORMAT, "A"), checker, 2); + } + @Test + public void testCorrect() { + testValidGrammar("de.monticore.grammar.cocos.valid.SymbolRules", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/TerminalCriticalTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/TerminalCriticalTest.java new file mode 100644 index 0000000000..fda1a25c38 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/TerminalCriticalTest.java @@ -0,0 +1,28 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class TerminalCriticalTest extends CocoTest { + private final String grammar = "de.monticore.grammar.cocos.invalid.A4058.A4058"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new TerminalCritical()); + } + + @Test + public void testInvalid() { + testInvalidGrammar(grammar, TerminalCritical.ERROR_CODE, String.format(TerminalCritical.ERROR_MSG_FORMAT, "123"), checker); + } + + @Test + public void testCorrect() { + testValidGrammar("de.monticore.grammar.cocos.valid.Attributes", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/TerminalEmptyStringTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/TerminalEmptyStringTest.java new file mode 100644 index 0000000000..f197a0d7df --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/TerminalEmptyStringTest.java @@ -0,0 +1,38 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class TerminalEmptyStringTest extends CocoTest { + private final String grammar = "de.monticore.grammar.cocos.invalid.A4054.A4054"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new TerminalEmptyString()); + } + + @Test + public void testInvalid() { + testInvalidGrammar(grammar, TerminalEmptyString.ERROR_CODE, TerminalEmptyString.ERROR_MSG_FORMAT, checker); + } + + + @Test + public void testCorrect() { + testValidGrammar("de.monticore.grammar.cocos.valid.Attributes", checker); + } + + @Test + public void testCorrect2() { + testValidGrammar("de.monticore.common.TestLexicals", checker); + } + + @Test + public void testCorrect3(){ + testValidGrammar("de.monticore.common.TestLiterals", checker); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/TokenConstantInvalidTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/TokenConstantInvalidTest.java new file mode 100644 index 0000000000..95081a24a1 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/TokenConstantInvalidTest.java @@ -0,0 +1,34 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class TokenConstantInvalidTest extends CocoTest { + private final String grammar = "de.monticore.grammar.cocos.invalid.A4059.A4059"; + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new TokenConstantInvalid()); + } + + @Test + public void testInvalid1() { + testInvalidGrammar(grammar+"a", TokenConstantInvalid.ERROR_CODE, + String.format(TokenConstantInvalid.ERROR_MSG_FORMAT, "b-"), checker); + } + + @Test + public void testInvalid2() { + testInvalidGrammar(grammar+"b", TokenConstantInvalid.ERROR_CODE, + String.format(TokenConstantInvalid.ERROR_MSG_FORMAT, "-"), checker); + } + + @Test + public void testCorrect() { + testValidGrammar("de.monticore.grammar.cocos.valid.Attributes", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/UniqueProdNameInGrammarTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/UniqueProdNameInGrammarTest.java new file mode 100644 index 0000000000..21eb5bc96b --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/UniqueProdNameInGrammarTest.java @@ -0,0 +1,44 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class UniqueProdNameInGrammarTest extends CocoTest { + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new UniqueProdNameInGrammar()); + } + + @Test + public void testInvalid() { + testInvalidGrammar("de.monticore.grammar.cocos.invalid.A0112.A0112", UniqueProdNameInGrammar.ERROR_CODE, + String.format(UniqueProdNameInGrammar.ERROR_MSG_FORMAT, "A0112", "Bar"), checker); + } + + @Test + public void testInvalid2() { + testInvalidGrammar("de.monticore.grammar.cocos.invalid.A0112.A0112a", UniqueProdNameInGrammar.ERROR_CODE, + String.format(UniqueProdNameInGrammar.ERROR_MSG_FORMAT, "A0112a", "Bar"), checker); + } + + @Test + public void testInvalid3() { + testInvalidGrammar("de.monticore.grammar.cocos.invalid.A0112.A0112b", UniqueProdNameInGrammar.ERROR_CODE, + String.format(UniqueProdNameInGrammar.ERROR_MSG_FORMAT, "A0112b", "Bar"), checker); + } + + @Test + public void testInvalid4() { + testInvalidGrammar("de.monticore.grammar.cocos.invalid.A0112.A0112c", UniqueProdNameInGrammar.ERROR_CODE, + String.format(UniqueProdNameInGrammar.ERROR_MSG_FORMAT, "A0112c", "Bar"), checker); + } + + @Test + public void testCorrect() { + testValidGrammar("de.monticore.grammar.cocos.valid.Attributes", checker); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/UnnamedTerminalInInterfaceTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/UnnamedTerminalInInterfaceTest.java new file mode 100644 index 0000000000..1786d8aa4f --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/UnnamedTerminalInInterfaceTest.java @@ -0,0 +1,35 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +public class UnnamedTerminalInInterfaceTest extends CocoTest { + private final String grammar = "de.monticore.grammar.cocos.invalid.A0120.A0120"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new UnnamedTerminalInInterface()); + } + + @Test + public void testInvalidTerminal() { + testInvalidGrammar(grammar, UnnamedTerminalInInterface.ERROR_CODE, + String.format(UnnamedTerminalInInterface.ERROR_MSG_FORMAT, "Foo", "Terminal", "b"), checker); + } + + @Test + public void testInvalidKeyTerminal() { + testInvalidGrammar(grammar+ "b", UnnamedTerminalInInterface.ERROR_CODE, + String.format(UnnamedTerminalInInterface.ERROR_MSG_FORMAT, "Foo", "KeyTerminal", "b"), checker); + } + + + @Test + public void testCorrect() { + testValidGrammar("de.monticore.grammar.cocos.valid.ImplementInterfaceNTs", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/UsedLexNTNotDefinedTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/UsedLexNTNotDefinedTest.java new file mode 100644 index 0000000000..97d4001fa1 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/UsedLexNTNotDefinedTest.java @@ -0,0 +1,33 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +import static de.monticore.grammar.cocos.UsedLexNTNotDefined.ERROR_CODE; + +public class UsedLexNTNotDefinedTest extends CocoTest { + + private final String MESSAGE = " The lexical production A must not" + + " use the nonterminal B because there exists no lexical production defining B."; + private final String grammar = "de.monticore.grammar.cocos.invalid.A4016.A4016"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new UsedLexNTNotDefined()); + } + + @Test + public void testInvalid() { + testInvalidGrammar(grammar, ERROR_CODE, MESSAGE, checker); + } + + @Test + public void testCorrect() { + testValidGrammar("de.monticore.grammar.cocos.valid.Attributes", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/cocos/UsedNTNotDefinedTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/UsedNTNotDefinedTest.java new file mode 100644 index 0000000000..86d6e9e96a --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/cocos/UsedNTNotDefinedTest.java @@ -0,0 +1,42 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos; + +import de.monticore.grammar.grammar_withconcepts._cocos.Grammar_WithConceptsCoCoChecker; +import de.se_rwth.commons.logging.Finding; +import de.se_rwth.commons.logging.Log; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class UsedNTNotDefinedTest extends CocoTest { + + private final String MESSAGE =" The production A must not use the nonterminal " + + "B because there exists no production defining B."; + private final String grammar = "de.monticore.grammar.cocos.invalid.A2031.A2031"; + + @Before + public void init() { + checker = new Grammar_WithConceptsCoCoChecker(); + checker.addCoCo(new UsedNTNotDefined()); + } + + @Test + public void testInvalid() { + testInvalidGrammar(grammar, UsedNTNotDefined.ERROR_CODE, MESSAGE, checker); + assertFalse(Log.getFindings().isEmpty()); + assertEquals(1, Log.getFindings().size()); + boolean found = false; + for (Finding f : Log.getFindings()) { + found |= f.getMsg().equals(UsedNTNotDefined.ERROR_CODE + MESSAGE); + } + assertTrue(found); + } + + @Test + public void testCorrect(){ + testValidGrammar("de.monticore.grammar.cocos.valid.Attributes", checker); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/symboltable/IGrammarScopeTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/symboltable/IGrammarScopeTest.java new file mode 100644 index 0000000000..5ca71250f4 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/symboltable/IGrammarScopeTest.java @@ -0,0 +1,179 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.symboltable; + +import de.monticore.grammar.GrammarGlobalScopeTestFactory; +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar._symboltable.IGrammarScope; +import de.monticore.grammar.grammar._symboltable.MCGrammarSymbol; +import de.monticore.grammar.grammar_withconcepts.Grammar_WithConceptsMill; +import de.monticore.grammar.grammar_withconcepts._parser.Grammar_WithConceptsParser; +import de.monticore.grammar.grammar_withconcepts._symboltable.Grammar_WithConceptsGlobalScope; +import de.monticore.grammar.grammar_withconcepts._symboltable.Grammar_WithConceptsPhasedSTC; +import de.monticore.symboltable.modifiers.AccessModifier; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class IGrammarScopeTest { + + @Before + public void before() { + LogStub.init(); + Log.enableFailQuick(false); + Grammar_WithConceptsMill.reset(); + Grammar_WithConceptsMill.init(); + } + + @Test + public void testCombiningGrammarSymbolTable() throws IOException { + final Grammar_WithConceptsGlobalScope globalScope = GrammarGlobalScopeTestFactory.create(); + final Optional grammar = globalScope + .resolveMCGrammar("de.monticore.CombiningGrammar"); + assertTrue(grammar.isPresent()); + assertTrue(grammar.get().isPresentAstNode()); + ASTMCGrammar ast = grammar.get().getAstNode(); + + IGrammarScope innerScope = ast.getClassProd(0).getEnclosingScope(); + + assertTrue(innerScope.resolveProd("NewProd").isPresent()); + + assertTrue(innerScope.resolveProd("Automaton").isPresent()); + assertTrue(innerScope.resolveProd("Transition").isPresent()); + assertTrue(innerScope.resolveProd("State").isPresent()); + + assertTrue(innerScope.resolveProd("X").isPresent()); + assertTrue(innerScope.resolveProd("Y").isPresent()); + assertTrue(innerScope.resolveProd("J").isPresent()); + assertTrue(innerScope.resolveProd("G").isPresent()); + assertTrue(innerScope.resolveProd("K").isPresent()); + assertTrue(innerScope.resolveProd("N").isPresent()); + + assertFalse(innerScope.resolveProd("Supergrammar").isPresent()); + assertFalse(innerScope.resolveProd("de.monticore.inherited.Supergrammar").isPresent()); + assertFalse(innerScope.resolveProd("CombiningGrammar").isPresent()); + + assertTrue(innerScope.resolveMCGrammar("de.monticore.inherited.Supergrammar").isPresent()); + assertTrue(innerScope.resolveMCGrammar("CombiningGrammar").isPresent()); + assertTrue(innerScope.resolveMCGrammar("Automaton").isPresent()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCombiningGrammarResolveInSuperGrammars() throws IOException { + final Grammar_WithConceptsGlobalScope globalScope = GrammarGlobalScopeTestFactory.create(); + final Optional grammar = globalScope + .resolveMCGrammar("de.monticore.CombiningGrammar"); + assertTrue(grammar.isPresent()); + assertTrue(grammar.get().isPresentAstNode()); + ASTMCGrammar ast = grammar.get().getAstNode(); + + IGrammarScope innerScope = ast.getClassProd(0).getEnclosingScope(); + + assertTrue(innerScope.resolveInSuperGrammars("Automaton", AccessModifier.ALL_INCLUSION).isPresent()); + assertTrue(innerScope.resolveInSuperGrammars("Transition", AccessModifier.ALL_INCLUSION).isPresent()); + assertTrue(innerScope.resolveInSuperGrammars("State", AccessModifier.ALL_INCLUSION).isPresent()); + + assertTrue(innerScope.resolveInSuperGrammars("X", AccessModifier.ALL_INCLUSION).isPresent()); + assertTrue(innerScope.resolveInSuperGrammars("Y", AccessModifier.ALL_INCLUSION).isPresent()); + assertTrue(innerScope.resolveInSuperGrammars("J", AccessModifier.ALL_INCLUSION).isPresent()); + assertTrue(innerScope.resolveInSuperGrammars("G", AccessModifier.ALL_INCLUSION).isPresent()); + assertTrue(innerScope.resolveInSuperGrammars("K", AccessModifier.ALL_INCLUSION).isPresent()); + assertTrue(innerScope.resolveInSuperGrammars("N", AccessModifier.ALL_INCLUSION).isPresent()); + + assertFalse(innerScope.resolveInSuperGrammars("Supergrammar", AccessModifier.ALL_INCLUSION).isPresent()); + assertFalse(innerScope.resolveInSuperGrammars("de.monticore.inherited.Supergrammar", AccessModifier.ALL_INCLUSION).isPresent()); + assertFalse(innerScope.resolveInSuperGrammars("CombiningGrammar", AccessModifier.ALL_INCLUSION).isPresent()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testSubsubgrammarSymbolTable() throws IOException { + final Grammar_WithConceptsGlobalScope globalScope = GrammarGlobalScopeTestFactory.create(); + final Optional grammar = globalScope + .resolveMCGrammar("de.monticore.inherited.Subsubgrammar"); + assertTrue(grammar.isPresent()); + assertTrue(grammar.get().isPresentAstNode()); + ASTMCGrammar ast = grammar.get().getAstNode(); + + IGrammarScope innerScope = ast.getClassProd(0).getEnclosingScope(); + + assertTrue(innerScope.resolveProd("N").isPresent()); + assertTrue(innerScope.resolveProd("S").isPresent()); + + assertTrue(innerScope.resolveProd("A").isPresent()); + assertTrue(innerScope.resolveProd("B").isPresent()); + assertTrue(innerScope.resolveProd("M").isPresent()); + assertTrue(innerScope.resolveProd("D").isPresent()); + assertTrue(innerScope.resolveProd("L").isPresent()); + assertTrue(innerScope.resolveProd("O").isPresent()); + assertTrue(innerScope.resolveProd("M").isPresent()); + + assertTrue(innerScope.resolveProd("X").isPresent()); + assertTrue(innerScope.resolveProd("Y").isPresent()); + assertTrue(innerScope.resolveProd("J").isPresent()); + assertTrue(innerScope.resolveProd("G").isPresent()); + assertTrue(innerScope.resolveProd("K").isPresent()); + assertTrue(innerScope.resolveProd("N").isPresent()); + + assertFalse(innerScope.resolveProd("Supergrammar").isPresent()); + assertFalse(innerScope.resolveProd("de.monticore.inherited.Supergrammar").isPresent()); + assertFalse(innerScope.resolveProd("Subgrammar").isPresent()); + assertFalse(innerScope.resolveProd("de.monticore.inherited.Subgrammar").isPresent()); + assertFalse(innerScope.resolveProd("Subsubgrammar").isPresent()); + assertFalse(innerScope.resolveProd("de.monticore.inherited.Subsubgrammar").isPresent()); + + assertTrue(innerScope.resolveMCGrammar("de.monticore.inherited.Supergrammar").isPresent()); + assertTrue(innerScope.resolveMCGrammar("de.monticore.inherited.Subgrammar").isPresent()); + assertTrue(innerScope.resolveMCGrammar("de.monticore.inherited.Subsubgrammar").isPresent()); + assertTrue(innerScope.resolveMCGrammar("Subsubgrammar").isPresent()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testSubsubgrammarResolveInSuperGrammars() throws IOException { + final Grammar_WithConceptsGlobalScope globalScope = GrammarGlobalScopeTestFactory.create(); + final Optional grammar = globalScope + .resolveMCGrammar("de.monticore.inherited.Subsubgrammar"); + assertTrue(grammar.isPresent()); + assertTrue(grammar.get().isPresentAstNode()); + ASTMCGrammar ast = grammar.get().getAstNode(); + IGrammarScope innerScope = ast.getClassProd(0).getEnclosingScope(); + + assertTrue(innerScope.resolveInSuperGrammars("N", AccessModifier.ALL_INCLUSION).isPresent()); + + assertTrue(innerScope.resolveInSuperGrammars("A", AccessModifier.ALL_INCLUSION).isPresent()); + assertTrue(innerScope.resolveInSuperGrammars("B", AccessModifier.ALL_INCLUSION).isPresent()); + assertTrue(innerScope.resolveInSuperGrammars("M", AccessModifier.ALL_INCLUSION).isPresent()); + assertTrue(innerScope.resolveInSuperGrammars("D", AccessModifier.ALL_INCLUSION).isPresent()); + assertTrue(innerScope.resolveInSuperGrammars("L", AccessModifier.ALL_INCLUSION).isPresent()); + assertTrue(innerScope.resolveInSuperGrammars("O", AccessModifier.ALL_INCLUSION).isPresent()); + assertTrue(innerScope.resolveInSuperGrammars("M", AccessModifier.ALL_INCLUSION).isPresent()); + + assertTrue(innerScope.resolveInSuperGrammars("X", AccessModifier.ALL_INCLUSION).isPresent()); + assertTrue(innerScope.resolveInSuperGrammars("Y", AccessModifier.ALL_INCLUSION).isPresent()); + assertTrue(innerScope.resolveInSuperGrammars("J", AccessModifier.ALL_INCLUSION).isPresent()); + assertTrue(innerScope.resolveInSuperGrammars("G", AccessModifier.ALL_INCLUSION).isPresent()); + assertTrue(innerScope.resolveInSuperGrammars("K", AccessModifier.ALL_INCLUSION).isPresent()); + assertTrue(innerScope.resolveInSuperGrammars("N", AccessModifier.ALL_INCLUSION).isPresent()); + + assertFalse(innerScope.resolveInSuperGrammars("Supergrammar", AccessModifier.ALL_INCLUSION).isPresent()); + assertFalse(innerScope.resolveInSuperGrammars("de.monticore.inherited.Supergrammar", AccessModifier.ALL_INCLUSION).isPresent()); + assertFalse(innerScope.resolveInSuperGrammars("Subgrammar", AccessModifier.ALL_INCLUSION).isPresent()); + assertFalse(innerScope.resolveInSuperGrammars("de.monticore.inherited.Subgrammar", AccessModifier.ALL_INCLUSION).isPresent()); + assertFalse(innerScope.resolveInSuperGrammars("Subsubgrammar", AccessModifier.ALL_INCLUSION).isPresent()); + assertFalse(innerScope.resolveInSuperGrammars("de.monticore.inherited.Subsubgrammar", AccessModifier.ALL_INCLUSION).isPresent()); + + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/grammar/symboltable/MontiCoreGrammarSymbolTableCreatorTest.java b/monticore-grammar/src/test/java/de/monticore/grammar/symboltable/MontiCoreGrammarSymbolTableCreatorTest.java new file mode 100644 index 0000000000..ec705e5724 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/grammar/symboltable/MontiCoreGrammarSymbolTableCreatorTest.java @@ -0,0 +1,478 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.symboltable; + +import de.monticore.grammar.GrammarGlobalScopeTestFactory; +import de.monticore.grammar.grammar._ast.*; +import de.monticore.grammar.grammar._symboltable.*; +import de.monticore.grammar.grammar_withconcepts.Grammar_WithConceptsMill; +import de.monticore.grammar.grammar_withconcepts._symboltable.Grammar_WithConceptsGlobalScope; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import static org.junit.Assert.*; + +public class MontiCoreGrammarSymbolTableCreatorTest { + + @Before + public void before() { + LogStub.init(); + Log.enableFailQuick(false); + Grammar_WithConceptsMill.reset(); + Grammar_WithConceptsMill.init(); + } + + @Test + public void testSymbolTableOfGrammarStatechartDSL() { + final Grammar_WithConceptsGlobalScope globalScope = GrammarGlobalScopeTestFactory.create(); + + final Optional grammar = globalScope + .resolveMCGrammar("de.monticore.Statechart"); + + assertTrue(grammar.isPresent()); + assertTrue(grammar.get().isPresentAstNode()); + testGrammarSymbolOfStatechart(grammar.get()); + + assertTrue(Log.getFindings().isEmpty()); + } + + private void testGrammarSymbolOfStatechart(MCGrammarSymbol grammar) { + assertNotNull(grammar); + assertEquals("de.monticore.Statechart", grammar.getFullName()); + assertEquals("de.monticore", grammar.getPackageName()); + assertTrue(grammar.getStartProd().isPresent()); + + assertTrue(grammar.isIsComponent()); + assertEquals(1, grammar.getSuperGrammars().size()); + + assertEquals(12, grammar.getProds().size()); + + // AST + assertTrue(grammar.isPresentAstNode()); + assertSame(grammar.getEnclosingScope(), grammar.getAstNode().getEnclosingScope()); + + final ProdSymbol stateChartProd = grammar.getProd("Statechart").orElse(null); + assertNotNull(stateChartProd); + assertEquals("Statechart", stateChartProd.getName()); + assertEquals("de.monticore.Statechart.Statechart", stateChartProd.getFullName()); + assertEquals("de.monticore", stateChartProd.getPackageName()); + assertTrue(stateChartProd.isIsStartProd()); + assertTrue(stateChartProd.isClass()); + // generic vs. specific + Optional resolvedStateChartProd = grammar.getSpannedScope().resolveProd("Statechart"); + assertTrue(resolvedStateChartProd.isPresent()); + assertSame(stateChartProd, resolvedStateChartProd.get()); + // AST + testLinkBetweenSymbolAndAst(stateChartProd); + + final ProdSymbol entryActionProd = grammar.getProd("EntryAction").orElse(null); + assertNotNull(entryActionProd); + assertEquals("EntryAction", entryActionProd.getName()); + assertEquals("de.monticore.Statechart.EntryAction", entryActionProd.getFullName()); + assertFalse(entryActionProd.isIsStartProd()); + testLinkBetweenSymbolAndAst(entryActionProd); + + // test prod components + Collection rcsList = entryActionProd.getProdComponents(); + assertEquals(1, rcsList.size()); + + List prodComps = entryActionProd.getSpannedScope().resolveRuleComponentDownMany("block"); + assertFalse(prodComps.isEmpty()); + assertEquals("block", prodComps.get(0).getName()); + assertTrue(prodComps.get(0).isIsNonterminal()); + assertTrue(prodComps.get(0).isPresentAstNode()); + assertFalse(prodComps.get(0).isIsList()); + assertFalse(prodComps.get(0).isIsOptional()); + assertSame(entryActionProd.getSpannedScope(), prodComps.get(0).getEnclosingScope()); + // reference to defining prod + assertEquals("BlockStatement", prodComps.get(0).getReferencedProd().get().getName()); + assertTrue(prodComps.get(0).getReferencedProd().get().isIsExternal()); + + ProdSymbol scStructure = grammar.getProd("SCStructure").orElse(null); + assertNotNull(scStructure); + assertEquals("SCStructure", scStructure.getName()); + assertTrue(scStructure.isIsInterface()); + assertEquals(0, scStructure.getProdComponents().size()); + testLinkBetweenSymbolAndAst(scStructure); + + ProdSymbol abstractAnything = grammar.getProd("AbstractAnything").orElse(null); + assertNotNull(abstractAnything); + assertEquals("AbstractAnything", abstractAnything.getName()); + assertEquals("de.monticore.Statechart.AbstractAnything", + abstractAnything.getFullName()); + assertFalse(abstractAnything.isIsInterface()); + assertFalse(abstractAnything.isIsSymbolDefinition()); + assertEquals(0, abstractAnything.getProdComponents().size()); + testLinkBetweenSymbolAndAst(abstractAnything); + + final ProdSymbol stateProd = grammar.getProd("State").orElse(null); + assertNotNull(stateProd); + assertEquals("State", stateProd.getName()); + assertEquals("de.monticore.Statechart.State", stateProd.getFullName()); + assertTrue(stateProd.isClass()); + + assertEquals(1, stateProd.getSuperInterfaceProds().size()); + final ProdSymbolSurrogate superInterfaceScStructure = stateProd.getSuperInterfaceProds() + .get(0); + assertSame(scStructure, superInterfaceScStructure.lazyLoadDelegate()); + // TODO PN generic resolving in super prod + // AST + testLinkBetweenSymbolAndAst(stateProd); + + List initialComponents = stateProd.getSpannedScope().resolveRuleComponentDownMany("initial"); + assertFalse(initialComponents.isEmpty()); + RuleComponentSymbol initialComponent = initialComponents.get(0); + assertEquals("de.monticore.Statechart.State.initial", + initialComponent.getFullName()); + assertEquals("initial", initialComponent.getName()); + + ProdSymbol classBody = grammar.getProd("Classbody").orElse(null); + assertNotNull(classBody); + assertEquals("Classbody", classBody.getName()); + assertEquals(0, classBody.getProdComponents().size()); + assertTrue(classBody.isIsExternal()); + assertFalse(classBody.isIsSymbolDefinition()); + testLinkBetweenSymbolAndAst(classBody); + + ProdSymbol codeProd = grammar.getProd("Code").orElse(null); + assertNotNull(codeProd); + assertEquals("Code", codeProd.getName()); + assertEquals(1, codeProd.getProdComponents().size()); + prodComps = codeProd.getSpannedScope().resolveRuleComponentDownMany("body"); + assertFalse((prodComps.isEmpty())); + assertTrue(prodComps.get(0).getReferencedProd().isPresent()); + assertSame(classBody, prodComps.get(0).getReferencedProd().get().lazyLoadDelegate()); + testLinkBetweenSymbolAndAst(codeProd); + + assertTrue(Log.getFindings().isEmpty()); + } + + private void testLinkBetweenSymbolAndAst(ProdSymbol prodSymbol) { + assertTrue(prodSymbol.isPresentAstNode()); + assertSame(prodSymbol, prodSymbol.getAstNode().getSymbol()); + assertSame(prodSymbol.getEnclosingScope(), + prodSymbol.getAstNode().getEnclosingScope()); + + if (prodSymbol.isClass()) { + assertTrue(prodSymbol.getAstNode() instanceof ASTClassProd); + } + else if (prodSymbol.isIsInterface()) { + assertTrue(prodSymbol.getAstNode() instanceof ASTInterfaceProd); + } + else if (prodSymbol.isIsAbstract()) { + assertTrue(prodSymbol.getAstNode() instanceof ASTAbstractProd); + } + else if (prodSymbol.isIsLexerProd()) { + assertTrue(prodSymbol.getAstNode() instanceof ASTLexProd); + } + else if (prodSymbol.isIsExternal()) { + assertTrue(prodSymbol.getAstNode() instanceof ASTExternalProd); + } + } + + @Test + public void testGrammarTypeReferences() { + final Grammar_WithConceptsGlobalScope globalScope = GrammarGlobalScopeTestFactory.create(); + + MCGrammarSymbol grammar = globalScope.resolveMCGrammar("de.monticore.TypeReferences").orElse(null); + assertNotNull(grammar); + + assertEquals(5, grammar.getProds().size()); + + ProdSymbol c = grammar.getProd("C").orElse(null); + assertNotNull(c); + assertEquals("C", c.getName()); + assertTrue(c.isIsInterface()); + assertEquals(0, c.getProdComponents().size()); + + ProdSymbol q = grammar.getProd("Q").orElse(null); + assertNotNull(q); + assertEquals("Q", q.getName()); + assertTrue(q.isClass()); + + ProdSymbol p = grammar.getProd("P").orElse(null); + assertNotNull(p); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testSuperGrammar() { + final Grammar_WithConceptsGlobalScope globalScope = GrammarGlobalScopeTestFactory.create(); + + MCGrammarSymbol grammar = globalScope + .resolveMCGrammar("de.monticore.SubStatechart") + .orElse(null); + assertNotNull(grammar); + assertEquals("de.monticore.SubStatechart", grammar.getFullName()); + assertTrue(grammar.getStartProd().isPresent()); + + assertEquals(1, grammar.getSuperGrammars().size()); + MCGrammarSymbolSurrogate superGrammarRef = grammar.getSuperGrammars().get(0); + assertEquals("de.monticore.Statechart", superGrammarRef.getName()); + testGrammarSymbolOfStatechart(superGrammarRef.lazyLoadDelegate()); + + ProdSymbol firstProd = grammar.getProd("First").orElse(null); + assertNotNull(firstProd); + assertTrue(firstProd.isIsStartProd()); + assertSame(grammar.getStartProd().get(), firstProd); + + ProdSymbol secondProd = grammar.getProd("Second").orElse(null); + assertNotNull(secondProd); + assertFalse(secondProd.isIsStartProd()); + + assertEquals(2, grammar.getProdNames().size()); + assertEquals(19, grammar.getProdsWithInherited().size()); + + // get prod of super grammar + assertFalse(grammar.getProd("State").isPresent()); + final ProdSymbol stateProd = grammar.getProdWithInherited("State").orElse(null); + assertNotNull(stateProd); + assertEquals("de.monticore.Statechart.State", stateProd.getFullName()); + + // generic vs. specific search in super grammar + Optional resolvedProd = grammar.getSpannedScope().resolveProd("State"); + assertTrue(resolvedProd.isPresent()); + assertSame(stateProd, resolvedProd.get()); + + Optional resolvedProd2 = firstProd.getEnclosingScope().resolveProd("State"); + assertTrue(resolvedProd2.isPresent()); + assertSame(stateProd, resolvedProd2.get()); + + assertTrue(Log.getFindings().isEmpty()); + + } + + @Test + public void testMontiCoreGrammar() { + final Grammar_WithConceptsGlobalScope globalScope = GrammarGlobalScopeTestFactory.create(); + + MCGrammarSymbol grammar = globalScope.resolveMCGrammar("de.monticore.TestGrammar").orElse(null); + assertNotNull(grammar); + assertEquals("de.monticore.TestGrammar", grammar.getFullName()); + + assertEquals(3, countExternalProd(grammar)); + assertEquals(5, countInterfaceAndAbstractProds(grammar)); + + assertEquals(1, grammar.getSuperGrammars().size()); + final MCGrammarSymbolSurrogate superGrammarRef = grammar.getSuperGrammars().get(0); + final String superGrammarFullName = superGrammarRef.lazyLoadDelegate().getFullName(); + assertEquals("de.monticore.common.TestLiterals", superGrammarFullName); + + ProdSymbol prod = grammar.getProdWithInherited("StringLiteral").orElse(null); + assertNotNull(prod); + assertEquals(superGrammarFullName + ".StringLiteral", prod.getFullName()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testNonTerminalsWithSameName() { + final Grammar_WithConceptsGlobalScope globalScope = GrammarGlobalScopeTestFactory.create(); + + MCGrammarSymbol grammar = globalScope.resolveMCGrammar("de.monticore" + + ".NonTerminalsWithSameName").orElse(null); + assertNotNull(grammar); + assertEquals("de.monticore.NonTerminalsWithSameName", grammar.getFullName()); + + assertEquals(2, grammar.getProds().size()); + ProdSymbol transition = grammar.getProd("Transition").orElse(null); + assertNotNull(transition); + + List r = transition.getSpannedScope().resolveRuleComponentMany("arg"); + assertEquals(2, r.size()); + assertTrue(r.get(0).isIsList()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testTokenModes() { + final Grammar_WithConceptsGlobalScope globalScope = GrammarGlobalScopeTestFactory.create(); + + MCGrammarSymbol grammar = globalScope.resolveMCGrammar("de.monticore.Modes").orElse(null); + assertNotNull(grammar); + assertEquals("de.monticore.Modes", grammar.getFullName()); + + Map> tokenModes = grammar.getTokenModesWithInherited(); + assertEquals(3, tokenModes.size()); + assertEquals(4, tokenModes.get(MCGrammarSymbol.DEFAULT_MODE).size()); + assertEquals(1, tokenModes.get("FOO_MODE").size()); + assertEquals(1, tokenModes.get("BLA_MODE").size()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testReplaceKeywords() { + final Grammar_WithConceptsGlobalScope globalScope = GrammarGlobalScopeTestFactory.create(); + + MCGrammarSymbol grammar = globalScope.resolveMCGrammar("de.monticore.Keywords").orElse(null); + assertNotNull(grammar); + + Map> keywords = grammar.getReplacedKeywordsWithInherited(); + assertEquals(2, keywords.size()); + assertEquals(1, keywords.get("A").size()); + assertEquals(4, keywords.get("B").size()); + + assertTrue(Log.getFindings().isEmpty()); + } + + private int countExternalProd(MCGrammarSymbol grammar) { + int num = 0; + for (ProdSymbol rule : grammar.getProds()) { + if (rule.isIsExternal()) { + num++; + } + } + return num; + } + + private int countInterfaceAndAbstractProds(MCGrammarSymbol grammar) { + int num = 0; + for (ProdSymbol rule : grammar.getProds()) { + if (rule.isIsInterface() || rule.isIsAbstract()) { + num++; + } + } + return num; + } + + @Test + public void testSymbolTableOfAutomaton() { + final Grammar_WithConceptsGlobalScope globalScope = GrammarGlobalScopeTestFactory.create(); + + // test grammar symbol + MCGrammarSymbol grammar = globalScope.resolveMCGrammar("Automaton").orElse(null); + assertNotNull(grammar); + assertTrue(grammar.isPresentAstNode()); + + ProdSymbol autProd = grammar.getSpannedScope() + .resolveProd("Automaton").orElse(null); + assertNotNull(autProd); + assertTrue(autProd.isIsScopeSpanning()); + assertTrue(autProd.isIsSymbolDefinition()); + + ProdSymbol stateProd = grammar.getSpannedScope().resolveProd("State").orElse(null); + assertNotNull(stateProd); + assertFalse(stateProd.isIsScopeSpanning()); + assertTrue(stateProd.isIsSymbolDefinition()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testRuleWithSymbolReference() { + final Grammar_WithConceptsGlobalScope globalScope = GrammarGlobalScopeTestFactory.create(); + + MCGrammarSymbol grammar = globalScope.resolveMCGrammar("de.monticore" + + ".RuleWithSymbolReference").orElse(null); + assertNotNull(grammar); + assertEquals("de.monticore.RuleWithSymbolReference", grammar.getFullName()); + + assertEquals(7, grammar.getProds().size()); + + ProdSymbol s = grammar.getProd("S").orElse(null); + assertNotNull(s); + assertTrue(s.isIsSymbolDefinition()); + assertEquals("S", s.getName()); + + ProdSymbol t = grammar.getProd("T").orElse(null); + assertEquals("T", t.getName()); + assertFalse(t.isIsSymbolDefinition()); + + ProdSymbol a = grammar.getProd("A").orElse(null); + assertEquals("A", a.getName()); + assertFalse(a.isIsSymbolDefinition()); + + ProdSymbol b = grammar.getProd("B").orElse(null); + assertFalse(b.isIsSymbolDefinition()); + List comps = b.getSpannedScope().resolveRuleComponentDownMany("an"); + assertFalse(comps.isEmpty()); + RuleComponentSymbol aComponent = comps.get(0); + assertEquals("Name", aComponent.getReferencedProd().get().getName()); + + ProdSymbol e = grammar.getProd("E").orElse(null); + assertTrue(e.isIsExternal()); + assertTrue(e.isIsSymbolDefinition()); + + ProdSymbol r = grammar.getProd("R").orElse(null); + assertTrue(r.isIsAbstract()); + assertFalse(r.isIsInterface()); + assertTrue(r.isIsSymbolDefinition()); + + assertTrue(Log.getFindings().isEmpty()); + } + + /** + * tests that for ASTKey only a symbol is created if the key has a usage name + * e.g. key("b") -> no usage name -> no symbol + * b:key("b") -> with usage name -> has symbol + */ + @Test + public void testASTKeySymbolCreation() { + final Grammar_WithConceptsGlobalScope globalScope = GrammarGlobalScopeTestFactory.create(); + Optional grammarOpt = globalScope.resolveMCGrammar("de.monticore.KeyAndNext"); + assertTrue(grammarOpt.isPresent()); + MCGrammarSymbol grammar = grammarOpt.get(); + assertNotNull(grammar); + assertTrue(grammar.isPresentAstNode()); + + // no usage name + Optional aProd = grammar.getSpannedScope().resolveProd("A"); + assertTrue(aProd.isPresent()); + List comps = aProd.get().getSpannedScope().resolveRuleComponentDownMany("b"); + assertFalse(comps.isEmpty()); + RuleComponentSymbol aBRule= comps.get(0); + assertFalse(aBRule.isIsList()); + + // with usage name + Optional bProd = grammar.getSpannedScope().resolveProd("B"); + assertTrue(bProd.isPresent()); + List bBRules = bProd.get().getSpannedScope().resolveRuleComponentMany("b"); + assertTrue(!bBRules.isEmpty()); + assertTrue(bBRules.get(0).isIsList()); + + // no usage name + Optional cProd = grammar.getSpannedScope().resolveProd("C"); + assertTrue(cProd.isPresent()); + List cBRules= cProd.get().getSpannedScope().resolveRuleComponentMany("b"); + assertFalse(cBRules.isEmpty()); + assertFalse(cBRules.get(0).isIsList()); + + // no usage name + Optional dProd = grammar.getSpannedScope().resolveProd("D"); + assertTrue(dProd.isPresent()); + List dBRules= dProd.get().getSpannedScope().resolveRuleComponentMany("b"); + assertFalse(dBRules.isEmpty()); + assertFalse(dBRules.get(0).isIsList()); + + // with usage name + Optional eProd = grammar.getSpannedScope().resolveProd("E"); + assertTrue(eProd.isPresent()); + List eBRules = eProd.get().getSpannedScope().resolveRuleComponentMany("b"); + assertTrue(!eBRules.isEmpty()); + assertTrue(eBRules.get(0).isIsList()); + + // no usage name + Optional fProd = grammar.getSpannedScope().resolveProd("F"); + assertTrue(fProd.isPresent()); + List fBRules = fProd.get().getSpannedScope().resolveRuleComponentMany("b"); + assertTrue(fBRules.isEmpty()); + + // with usage name + Optional gProd = grammar.getSpannedScope().resolveProd("G"); + assertTrue(gProd.isPresent()); + List gBRules = gProd.get().getSpannedScope().resolveRuleComponentMany("b"); + assertFalse(gBRules.isEmpty()); + + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/javalight/cocos/ConstructorFormalParametersDifferentNameTest.java b/monticore-grammar/src/test/java/de/monticore/javalight/cocos/ConstructorFormalParametersDifferentNameTest.java new file mode 100644 index 0000000000..81cbd4947e --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/javalight/cocos/ConstructorFormalParametersDifferentNameTest.java @@ -0,0 +1,39 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.javalight.cocos; + +import de.monticore.javalight._cocos.JavaLightCoCoChecker; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; +import de.se_rwth.commons.logging.Log; + +public class ConstructorFormalParametersDifferentNameTest extends JavaLightCocoTest{ + private final String fileName = "de.monticore.javalight.cocos.invalid.A0821.A0821"; + @Before + public void initCoCo() { + checker = new JavaLightCoCoChecker(); + checker.addCoCo(new ConstructorFormalParametersDifferentName()); + } + + @Test + public void testInvalid() { + testInvalid(fileName, "const1", ConstructorFormalParametersDifferentName.ERROR_CODE, + String.format(ConstructorFormalParametersDifferentName.ERROR_MSG_FORMAT, "i", "const1"), checker); + } + + @Test + public void testInvalid2() { + testInvalid(fileName+"a", "const1", ConstructorFormalParametersDifferentName.ERROR_CODE, + String.format(ConstructorFormalParametersDifferentName.ERROR_MSG_FORMAT, "i", "const1"), checker); + } + + @Test + public void testCorrect() { + testValid("de.monticore.javalight.cocos.valid.A0821", "const1", checker); + + assertTrue(Log.getFindings().isEmpty()); + } + +} + diff --git a/monticore-grammar/src/test/java/de/monticore/javalight/cocos/ConstructorModifiersValidTest.java b/monticore-grammar/src/test/java/de/monticore/javalight/cocos/ConstructorModifiersValidTest.java new file mode 100644 index 0000000000..152d8c711d --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/javalight/cocos/ConstructorModifiersValidTest.java @@ -0,0 +1,35 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.javalight.cocos; + +import de.monticore.javalight._cocos.JavaLightCoCoChecker; +import de.se_rwth.commons.logging.Log; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + +public class ConstructorModifiersValidTest extends JavaLightCocoTest{ + private final String fileName = "de.monticore.javalight.cocos.invalid.A0820.A0820"; + + @Before + public void initCoCo() { + checker = new JavaLightCoCoChecker(); + checker.addCoCo(new ConstructorModifiersValid()); + } + + @Test + public void testInvalid() { + testInvalid(fileName, "constructor", ConstructorModifiersValid.ERROR_CODE, + String.format(ConstructorModifiersValid.ERROR_MSG_FORMAT, "constructor"), checker); + } + + @Test + public void testCorrect() { + testValid("de.monticore.javalight.cocos.valid.A0820", "constructor", checker); + + assertTrue(Log.getFindings().isEmpty()); + } + +} + + diff --git a/monticore-grammar/src/test/java/de/monticore/javalight/cocos/ConstructorNoAccessModifierPairTest.java b/monticore-grammar/src/test/java/de/monticore/javalight/cocos/ConstructorNoAccessModifierPairTest.java new file mode 100644 index 0000000000..aeecd564c8 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/javalight/cocos/ConstructorNoAccessModifierPairTest.java @@ -0,0 +1,33 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.javalight.cocos; + +import de.monticore.javalight._cocos.JavaLightCoCoChecker; +import de.se_rwth.commons.logging.Log; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + +public class ConstructorNoAccessModifierPairTest extends JavaLightCocoTest{ + private final String fileName = "de.monticore.javalight.cocos.invalid.A0809.A0809"; + + @Before + public void initCoCo() { + checker = new JavaLightCoCoChecker(); + checker.addCoCo(new ConstructorNoAccessModifierPair()); + } + + @Test + public void testInvalid() { + testInvalid(fileName, "constructor", ConstructorNoAccessModifierPair.ERROR_CODE, + String.format(ConstructorNoAccessModifierPair.ERROR_MSG_FORMAT, "constructor"), checker); + } + + @Test + public void testCorrect() { + testValid("de.monticore.javalight.cocos.valid.A0809", "constructor", checker); + + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/javalight/cocos/ConstructorNoDuplicateModifierTest.java b/monticore-grammar/src/test/java/de/monticore/javalight/cocos/ConstructorNoDuplicateModifierTest.java new file mode 100644 index 0000000000..9413cf6ff1 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/javalight/cocos/ConstructorNoDuplicateModifierTest.java @@ -0,0 +1,32 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.javalight.cocos; + +import de.monticore.javalight._cocos.JavaLightCoCoChecker; +import de.se_rwth.commons.logging.Log; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + +public class ConstructorNoDuplicateModifierTest extends JavaLightCocoTest{ + private final String fileName = "de.monticore.javalight.cocos.invalid.A0808.A0808"; + + @Before + public void initCoCo() { + checker = new JavaLightCoCoChecker(); + checker.addCoCo(new ConstructorNoDuplicateModifier()); + } + + @Test + public void testInvalid() { + testInvalid(fileName, "constructor", ConstructorNoDuplicateModifier.ERROR_CODE, + String.format(ConstructorNoDuplicateModifier.ERROR_MSG_FORMAT, "public", "constructor"), checker); + } + + @Test + public void testCorrect() { + testValid("de.monticore.javalight.cocos.valid.A0808", "constructor", checker); + + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/javalight/cocos/JavaLightCocoTest.java b/monticore-grammar/src/test/java/de/monticore/javalight/cocos/JavaLightCocoTest.java new file mode 100644 index 0000000000..b73338456d --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/javalight/cocos/JavaLightCocoTest.java @@ -0,0 +1,116 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.javalight.cocos; + +import de.monticore.io.FileReaderWriter; +import de.monticore.javalight._ast.ASTJavaLightNode; +import de.monticore.javalight._ast.ASTJavaMethod; +import de.monticore.javalight._cocos.JavaLightCoCoChecker; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.oosymbols._symboltable.MethodSymbol; +import de.monticore.symbols.oosymbols._symboltable.OOTypeSymbol; +import de.monticore.testjavalight.TestJavaLightMill; +import de.monticore.testjavalight._parser.TestJavaLightParser; +import de.monticore.testjavalight._symboltable.TestJavaLightArtifactScope; +import de.monticore.testjavalight._symboltable.TestJavaLightGlobalScope; +import de.se_rwth.commons.Names; +import de.se_rwth.commons.logging.Finding; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.After; +import org.junit.Before; + +import java.io.IOException; +import java.io.Reader; +import java.net.URL; +import java.nio.file.Paths; +import java.util.Optional; + +import static org.junit.Assert.*; + +public abstract class JavaLightCocoTest { + + static protected TestJavaLightGlobalScope globalScope; + + protected TestJavaLightArtifactScope artifactScope; + + protected JavaLightCoCoChecker checker; + + @Before + public void before() { + LogStub.init(); + Log.enableFailQuick(false); + TestJavaLightMill.reset(); + TestJavaLightMill.init(); + } + + @Before + public void init() { + globalScope = (TestJavaLightGlobalScope) TestJavaLightMill.globalScope(); + globalScope.clear(); + + globalScope.getSymbolPath().addEntry(Paths.get("src/test/resources")); + globalScope.getSymbolPath().addEntry(Paths.get("target/test/resources")); + BasicSymbolsMill.initializePrimitives(); + } + + protected void testValid(String fileName, String methodName, JavaLightCoCoChecker checker) { + loadFileForModelName(fileName); + // test method symbol + final MethodSymbol methodSymbol = artifactScope + .resolveMethod(methodName) + .orElse(null); + assertNotNull(methodSymbol); + assertTrue(methodSymbol.isPresentAstNode()); + + Log.getFindings().clear(); + checker.checkAll((ASTJavaLightNode) methodSymbol.getAstNode()); + + assertTrue(Log.getFindings().isEmpty()); + } + + protected void testInvalid(String fileName, String methodName, String code, String message, + JavaLightCoCoChecker checker) { + testInvalid(fileName, methodName, code, message, checker, 1); + } + + protected void testInvalid(String fileName, String methodName, String code, String message, + JavaLightCoCoChecker checker, int numberOfFindings) { + loadFileForModelName(fileName); + // test method symbol + final MethodSymbol methodSymbol = artifactScope + .resolveMethod(methodName) + .orElse(null); + assertNotNull(methodSymbol); + assertTrue(methodSymbol.isPresentAstNode()); + + Log.getFindings().clear(); + checker.checkAll((ASTJavaLightNode) methodSymbol.getAstNode()); + + assertFalse(Log.getFindings().isEmpty()); + assertEquals(numberOfFindings, Log.getFindings().size()); + for (Finding f : Log.getFindings()) { + assertEquals(code + message, f.getMsg()); + } + } + + protected void loadFileForModelName(String modelName) { + // 1. calculate potential location of model file and try to find it in model path + Optional url = globalScope.getSymbolPath().find(Names.getPathFromPackage(modelName) + ".java"); + + // 2. if the file was found, parse the model and create its symtab + if (url.isPresent()) { + Optional optAST; + try (Reader reader = FileReaderWriter.getReader(url.get())){ + optAST = new TestJavaLightParser().parse(reader); + if (optAST.isPresent()) { + artifactScope = (TestJavaLightArtifactScope) new JavaLightPhasedSymbolTableCreatorDelegator().createFromAST(optAST.get()); + globalScope.addSubScope(artifactScope); + } + } catch (IOException e) { + fail(); + } + } + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/javalight/cocos/JavaLightPhasedSymbolTableCreatorDelegator.java b/monticore-grammar/src/test/java/de/monticore/javalight/cocos/JavaLightPhasedSymbolTableCreatorDelegator.java new file mode 100644 index 0000000000..19673e1742 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/javalight/cocos/JavaLightPhasedSymbolTableCreatorDelegator.java @@ -0,0 +1,36 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.javalight.cocos; + +import de.monticore.javalight._ast.ASTJavaMethod; +import de.monticore.javalight._visitor.JavaLightTraverser; +import de.monticore.testjavalight.TestJavaLightMill; +import de.monticore.testjavalight._symboltable.ITestJavaLightArtifactScope; +import de.monticore.testjavalight._symboltable.ITestJavaLightGlobalScope; +import de.monticore.testjavalight._symboltable.TestJavaLightScopesGenitorDelegator; +import de.monticore.testjavalight._visitor.TestJavaLightTraverser; + +import java.util.ArrayList; +import java.util.List; + +public class JavaLightPhasedSymbolTableCreatorDelegator { + + protected ITestJavaLightGlobalScope globalScope ; + + protected TestJavaLightScopesGenitorDelegator scopesGenitorDelegator ; + + protected List priorityList ; + + public JavaLightPhasedSymbolTableCreatorDelegator() { + this.globalScope = TestJavaLightMill.globalScope(); + this.scopesGenitorDelegator = TestJavaLightMill.scopesGenitorDelegator(); + this.priorityList = new ArrayList<>(); + priorityList.add(new JavaLightSTCompleteTypesDelegator().getTraverser()); + } + + public ITestJavaLightArtifactScope createFromAST (ASTJavaMethod rootNode) { + ITestJavaLightArtifactScope as = scopesGenitorDelegator.createFromAST(rootNode); + this.priorityList.forEach(rootNode::accept); + return as; + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/javalight/cocos/JavaLightSTCompleteTypesDelegator.java b/monticore-grammar/src/test/java/de/monticore/javalight/cocos/JavaLightSTCompleteTypesDelegator.java new file mode 100644 index 0000000000..ffa78a74a6 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/javalight/cocos/JavaLightSTCompleteTypesDelegator.java @@ -0,0 +1,30 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.javalight.cocos; + +import de.monticore.javalight._symboltable.JavaLightSTCompleteTypes; +import de.monticore.javalight._visitor.JavaLightTraverser; +import de.monticore.statements.mccommonstatements._symboltable.MCCommonStatementsSTCompleteTypes; +import de.monticore.statements.mcvardeclarationstatements._symboltable.MCVarDeclarationStatementsSTCompleteTypes; +import de.monticore.testjavalight.TestJavaLightMill; +import de.monticore.testjavalight._visitor.TestJavaLightTraverser; + +public class JavaLightSTCompleteTypesDelegator { + + protected TestJavaLightTraverser traverser; + + public JavaLightSTCompleteTypesDelegator(){ + this.traverser = TestJavaLightMill.traverser(); + + traverser.add4MCVarDeclarationStatements(new MCVarDeclarationStatementsSTCompleteTypes()); + traverser.add4MCCommonStatements(new MCCommonStatementsSTCompleteTypes()); + traverser.add4JavaLight(new JavaLightSTCompleteTypes()); + } + + public TestJavaLightTraverser getTraverser() { + return traverser; + } + + public void setTraverser(TestJavaLightTraverser traverser) { + this.traverser = traverser; + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/javalight/cocos/MethodAbstractAndOtherModifiersTest.java b/monticore-grammar/src/test/java/de/monticore/javalight/cocos/MethodAbstractAndOtherModifiersTest.java new file mode 100644 index 0000000000..125b5be787 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/javalight/cocos/MethodAbstractAndOtherModifiersTest.java @@ -0,0 +1,36 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.javalight.cocos; + +import de.monticore.javalight._cocos.JavaLightCoCoChecker; +import de.se_rwth.commons.logging.Log; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + +public class MethodAbstractAndOtherModifiersTest extends JavaLightCocoTest{ + + private final String fileName = "de.monticore.javalight.cocos.invalid.A0802.A0802"; + + @Before + public void initCoco() { + checker = new JavaLightCoCoChecker(); + checker.addCoCo(new MethodAbstractAndOtherModifiers()); + } + + @Test + public void testInvalid() { + testInvalid(fileName, "a", MethodAbstractAndOtherModifiers.ERROR_CODE, + String.format(MethodAbstractAndOtherModifiers.ERROR_MSG_FORMAT, "a"), checker); + } + + @Test + public void testCorrect() { + testValid("de.monticore.javalight.cocos.valid.A0802", + "a", checker); + + assertTrue(Log.getFindings().isEmpty()); + } + + +} diff --git a/monticore-grammar/src/test/java/de/monticore/javalight/cocos/MethodBodyAbsenceTest.java b/monticore-grammar/src/test/java/de/monticore/javalight/cocos/MethodBodyAbsenceTest.java new file mode 100644 index 0000000000..d6efcbb98b --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/javalight/cocos/MethodBodyAbsenceTest.java @@ -0,0 +1,37 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.javalight.cocos; + +import de.monticore.javalight._cocos.JavaLightCoCoChecker; +import de.se_rwth.commons.logging.Log; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + +public class MethodBodyAbsenceTest extends JavaLightCocoTest{ + private final String fileName = "de.monticore.javalight.cocos.invalid.A0804.A0804"; + + @Before + public void initCoCo(){ + checker = new JavaLightCoCoChecker(); + checker.addCoCo(new MethodBodyAbsence());} + + @Test + public void testInvalid1(){ + testInvalid(fileName, "method", MethodBodyAbsence.ERROR_CODE, + String.format(MethodBodyAbsence.ERROR_MESSAGE, "method"), checker); + } + @Test + public void testInvalid2(){ + testInvalid(fileName + "a", "method", MethodBodyAbsence.ERROR_CODE, + String.format(MethodBodyAbsence.ERROR_MESSAGE, "method"),checker); + } + + @Test + public void testCorrect() { + testValid("de.monticore.javalight.cocos.valid.A0804", "method", checker); + + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/javalight/cocos/MethodBodyPresenceTest.java b/monticore-grammar/src/test/java/de/monticore/javalight/cocos/MethodBodyPresenceTest.java new file mode 100644 index 0000000000..7d4fc378cb --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/javalight/cocos/MethodBodyPresenceTest.java @@ -0,0 +1,37 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.javalight.cocos; + +import de.monticore.javalight._cocos.JavaLightCoCoChecker; +import de.se_rwth.commons.logging.Log; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + +public class MethodBodyPresenceTest extends JavaLightCocoTest{ + private final String fileName = "de.monticore.javalight.cocos.invalid.A0803.A0803"; + + @Before + public void initCoCo(){ + checker = new JavaLightCoCoChecker(); + checker.addCoCo(new MethodBodyPresence());} + + @Test + public void testInvalid1(){ + testInvalid(fileName, "method", MethodBodyPresence.ERROR_CODE, + String.format(MethodBodyPresence.ERROR_MESSAGE, "method"), checker); + } + @Test + public void testInvalid2(){ + testInvalid(fileName + "a", "method", MethodBodyPresence.ERROR_CODE, + String.format(MethodBodyPresence.ERROR_MESSAGE, "method"),checker); + } + + @Test + public void testCorrect() { + testValid("de.monticore.javalight.cocos.valid.A0803", "method", checker); + + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/javalight/cocos/MethodExceptionThrowsTest.java b/monticore-grammar/src/test/java/de/monticore/javalight/cocos/MethodExceptionThrowsTest.java new file mode 100644 index 0000000000..b14333360f --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/javalight/cocos/MethodExceptionThrowsTest.java @@ -0,0 +1,44 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.javalight.cocos; + +import de.monticore.javalight._cocos.JavaLightCoCoChecker; +import de.monticore.testjavalight.TestJavaLightMill; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types.check.SymTypeOfObject; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; +import de.se_rwth.commons.logging.Log; + +public class MethodExceptionThrowsTest extends JavaLightCocoTest { + private final String fileName = "de.monticore.javalight.cocos.invalid.A0811.A0811"; + + @Before + public void initCoco() { + checker = new JavaLightCoCoChecker(); + checker.addCoCo(new MethodExceptionThrows()); + } + + @Test + public void testInvalid() { + globalScope.add(TestJavaLightMill.oOTypeSymbolBuilder().setName("A").build()); + globalScope.add(TestJavaLightMill.oOTypeSymbolBuilder().setName("java.lang.Throwable").build()); + + testInvalid(fileName, "meth1", MethodExceptionThrows.ERROR_CODE, + String.format(MethodExceptionThrows.ERROR_MSG_FORMAT, "A"), checker); + } + + @Test + public void testCorrect() { + SymTypeOfObject sType = SymTypeExpressionFactory.createTypeObject("java.lang.Throwable", globalScope); + globalScope.add(TestJavaLightMill.oOTypeSymbolBuilder().setName("A").addSuperTypes(sType).build()); + globalScope.add(TestJavaLightMill.oOTypeSymbolBuilder().setName("java.lang.Throwable").build()); + + testValid("de.monticore.javalight.cocos.valid.MethodDecl", "meth1", checker); + + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/javalight/cocos/MethodFormalParametersDifferentNameTest.java b/monticore-grammar/src/test/java/de/monticore/javalight/cocos/MethodFormalParametersDifferentNameTest.java new file mode 100644 index 0000000000..84ba93c74d --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/javalight/cocos/MethodFormalParametersDifferentNameTest.java @@ -0,0 +1,39 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.javalight.cocos; + +import de.monticore.javalight._cocos.JavaLightCoCoChecker; +import de.se_rwth.commons.logging.Log; +import org.junit.Before; +import org.junit.Test; +import static org.junit.Assert.assertTrue; + +public class MethodFormalParametersDifferentNameTest extends JavaLightCocoTest { + private final String fileName = "de.monticore.javalight.cocos.invalid.A0812.A0812"; + + @Before + public void initCoCo() { + checker = new JavaLightCoCoChecker(); + checker.addCoCo(new MethodFormalParametersDifferentName()); + } + + @Test + public void testInvalid() { + testInvalid(fileName, "meth1", MethodFormalParametersDifferentName.ERROR_CODE, + String.format(MethodFormalParametersDifferentName.ERROR_MSG_FORMAT, "i", "meth1"), checker); + } + + @Test + public void testInvalid2() { + testInvalid(fileName+"a", "meth1", MethodFormalParametersDifferentName.ERROR_CODE, + String.format(MethodFormalParametersDifferentName.ERROR_MSG_FORMAT, "i", "meth1"), checker); + } + + @Test + public void testCorrect() { + testValid("de.monticore.javalight.cocos.valid.MethodDecl", "meth1", checker); + + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/javalight/cocos/MethodNoNativeAndStrictfpTest.java b/monticore-grammar/src/test/java/de/monticore/javalight/cocos/MethodNoNativeAndStrictfpTest.java new file mode 100644 index 0000000000..44b4c2796b --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/javalight/cocos/MethodNoNativeAndStrictfpTest.java @@ -0,0 +1,32 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.javalight.cocos; + +import de.monticore.javalight._cocos.JavaLightCoCoChecker; +import de.se_rwth.commons.logging.Log; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + +public class MethodNoNativeAndStrictfpTest extends JavaLightCocoTest{ + private final String fileName = "de.monticore.javalight.cocos.invalid.A0819.A0819"; + + @Before + public void initCoCo(){ + checker = new JavaLightCoCoChecker(); + checker.addCoCo(new MethodNoNativeAndStrictfp());} + + @Test + public void testInvalid1(){ + testInvalid(fileName, "method", MethodNoNativeAndStrictfp.ERROR_CODE, + String.format(MethodNoNativeAndStrictfp.ERROR_MESSAGE, "method"), checker); + } + + @Test + public void testCorrect() { + testValid("de.monticore.javalight.cocos.valid.A0819", "method", checker); + + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/javalight/cocos/ReturnTypeAssignmentIsValidTest.java b/monticore-grammar/src/test/java/de/monticore/javalight/cocos/ReturnTypeAssignmentIsValidTest.java new file mode 100644 index 0000000000..731b7b0b4a --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/javalight/cocos/ReturnTypeAssignmentIsValidTest.java @@ -0,0 +1,95 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.javalight.cocos; + +import de.monticore.javalight._cocos.JavaLightCoCoChecker; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._ast.ASTBasicSymbolsNode; +import de.monticore.symbols.oosymbols._ast.ASTMethod; +import de.monticore.testjavalight.TestJavaLightMill; +import de.monticore.testjavalight._parser.TestJavaLightParser; +import de.monticore.testjavalight._visitor.TestJavaLightTraverser; +import de.monticore.types.check.FlatExpressionScopeSetter; +import de.monticore.types.check.FullDeriveFromCombineExpressionsWithLiterals; +import de.monticore.types.check.FullSynthesizeFromCombineExpressionsWithLiterals; +import de.monticore.types.check.TypeCalculator; +import de.se_rwth.commons.logging.Log; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class ReturnTypeAssignmentIsValidTest extends JavaLightCocoTest { + + @Before + public void initCoco(){ + BasicSymbolsMill.initializePrimitives(); + checker = new JavaLightCoCoChecker(); + checker.addCoCo(new ReturnTypeAssignmentIsValid(new TypeCalculator(new FullSynthesizeFromCombineExpressionsWithLiterals(), new FullDeriveFromCombineExpressionsWithLiterals()))); + } + + public void checkValid(String expressionString) throws IOException { + + TestJavaLightParser parser = new TestJavaLightParser(); + Optional optAST = parser.parse_StringMethod(expressionString); + assertTrue(optAST.isPresent()); + Log.getFindings().clear(); + TestJavaLightTraverser traverser = getFlatExpressionScopeSetter(); + optAST.get().accept(traverser); + checker.checkAll((ASTBasicSymbolsNode) optAST.get()); + assertTrue(Log.getFindings().isEmpty()); + + } + + public void checkInvalid(String expressionString) throws IOException { + + TestJavaLightParser parser = new TestJavaLightParser(); + Optional optAST = parser.parse_StringMethod(expressionString); + assertTrue(optAST.isPresent()); + Log.getFindings().clear(); + TestJavaLightTraverser traverser = getFlatExpressionScopeSetter(); + optAST.get().accept(traverser); + checker.checkAll((ASTBasicSymbolsNode) optAST.get()); + assertFalse(Log.getFindings().isEmpty()); + + } + + @Test + public void testValid() throws IOException { + + checkValid("public void test(){return;}"); + checkValid("public int test(){return 3;}"); + checkValid("public char test(){return 'c';}"); + checkValid("public double test(){return 1.2;}"); + checkValid("public boolean test(){return false;}"); + + } + + @Test + public void testInvalid() throws IOException { + + checkInvalid("public void test(){return 3;}"); + checkInvalid("public int test(){return 3.0;}"); + checkInvalid("public char test(){return;}"); + checkInvalid("public double test(){return true;}"); + checkInvalid("public boolean test(){return 'f';}"); + + } + + protected TestJavaLightTraverser getFlatExpressionScopeSetter() { + FlatExpressionScopeSetter flatExpressionScopeSetter = new FlatExpressionScopeSetter(TestJavaLightMill.globalScope()); + TestJavaLightTraverser traverser = TestJavaLightMill.traverser(); + traverser.add4ExpressionsBasis(flatExpressionScopeSetter); + traverser.add4AssignmentExpressions(flatExpressionScopeSetter); + traverser.add4CommonExpressions(flatExpressionScopeSetter); + traverser.add4JavaClassExpressions(flatExpressionScopeSetter); + traverser.add4MCBasicTypes(flatExpressionScopeSetter); + traverser.add4MCCollectionTypes(flatExpressionScopeSetter); + traverser.add4MCCommonLiterals(flatExpressionScopeSetter); + return traverser; + } + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/java/de/monticore/mccommonliterals/DoubleCommonLiteralsTest.java b/monticore-grammar/src/test/java/de/monticore/mccommonliterals/DoubleCommonLiteralsTest.java new file mode 100644 index 0000000000..af2b9ad10f --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/mccommonliterals/DoubleCommonLiteralsTest.java @@ -0,0 +1,75 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.mccommonliterals; + +import de.monticore.literals.mccommonliterals._ast.ASTBasicDoubleLiteral; +import de.monticore.literals.mcliteralsbasis._ast.ASTLiteral; +import de.monticore.literals.testmccommonliterals.TestMCCommonLiteralsMill; +import de.monticore.literals.testmccommonliterals._parser.TestMCCommonLiteralsParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.io.StringReader; +import java.util.Optional; + +import static org.junit.Assert.*; + +public class DoubleCommonLiteralsTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestMCCommonLiteralsMill.reset(); + TestMCCommonLiteralsMill.init(); + } + + private void checkDoubleLiteral(double d, String s) throws IOException { + TestMCCommonLiteralsParser parser = new TestMCCommonLiteralsParser(); + Optional lit = parser.parseLiteral(new StringReader(s)); + assertTrue(lit.isPresent()); + assertTrue(lit.get() instanceof ASTBasicDoubleLiteral); + assertEquals(d, ((ASTBasicDoubleLiteral) lit.get()).getValue(), 0); + } + + private void checkFalse(String s) throws IOException { + TestMCCommonLiteralsParser parser = new TestMCCommonLiteralsParser(); + Optional lit = parser.parseBasicDoubleLiteral(new StringReader(s)); + assertTrue(!lit.isPresent()); + } + + @Test + public void testDoubleLiterals() { + try { + // decimal number + checkDoubleLiteral(0.0, "0.0"); + checkDoubleLiteral(0.0, "0.0"); + checkDoubleLiteral(3.0, "3.0"); + checkDoubleLiteral(3.0, "3.0"); + } + catch (IOException e) { + fail(e.getMessage()); + } + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testFalseDoubleLiterals() { + try { + // decimal number + checkFalse(".0d"); + checkFalse("0.d"); + checkFalse("5d"); + checkFalse("009e2d"); + checkFalse("0 .0"); + checkFalse("0.0 d"); + } + catch (IOException e) { + fail(e.getMessage()); + } + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/mccommonliterals/FloatCommonLiteralsTest.java b/monticore-grammar/src/test/java/de/monticore/mccommonliterals/FloatCommonLiteralsTest.java new file mode 100644 index 0000000000..16b7212f2f --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/mccommonliterals/FloatCommonLiteralsTest.java @@ -0,0 +1,110 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.mccommonliterals; + +import de.monticore.literals.mccommonliterals._ast.ASTBasicFloatLiteral; +import de.monticore.literals.mcliteralsbasis._ast.ASTLiteral; +import de.monticore.literals.testmccommonliterals.TestMCCommonLiteralsMill; +import de.monticore.literals.testmccommonliterals._parser.TestMCCommonLiteralsParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.io.StringReader; +import java.util.Optional; + +import static org.junit.Assert.*; + +public class FloatCommonLiteralsTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestMCCommonLiteralsMill.reset(); + TestMCCommonLiteralsMill.init(); + } + + private void checkFloatLiteral(float f, String s) throws IOException { + TestMCCommonLiteralsParser parser = new TestMCCommonLiteralsParser(); + Optional lit = parser.parseLiteral(new StringReader(s)); + assertTrue(lit.isPresent()); + assertTrue(lit.get() instanceof ASTBasicFloatLiteral); + assertEquals(f, ((ASTBasicFloatLiteral) lit.get()).getValue(), 0); + assertTrue(true); + + assertTrue(Log.getFindings().isEmpty()); + } + + private void checkFalse(String s) throws IOException { + TestMCCommonLiteralsParser parser = new TestMCCommonLiteralsParser(); + Optional lit = parser.parseBasicFloatLiteral(new StringReader(s)); + assertTrue(!lit.isPresent()); + } + + @Test + public void testFloatLiterals() { + try { + checkFloatLiteral(0.0f, "0.0f"); + checkFloatLiteral(23.4f, "23.4f"); + } + catch (IOException e) + { + fail(e.getMessage()); + } + } + + @Test + public void checkFalseLiterals() { + try { + checkFalse("0F"); + checkFalse(".4F"); + checkFalse("5.F"); + checkFalse("009.f"); + checkFalse("009f"); + checkFalse("009e2f"); + checkFalse("2e3F"); + checkFalse("2E-3F"); + checkFalse("009f"); + checkFalse(".1e1F"); + checkFalse(".1F"); + checkFalse(".11e12F"); + checkFalse(".11e+12F"); + checkFalse("29.18e08F"); + checkFalse("0029.0008e-00008F"); + checkFalse("0. 0f"); + checkFalse("0 .0f"); + checkFalse("23.4 f"); + + // hexadezimal number + checkFalse("0x5.p1f"); + checkFalse("0x.5p1f"); + checkFalse("0xFp-9f"); + checkFalse("0xfP2F"); + checkFalse("0xfp1F"); + checkFalse("0x.fP1F"); + checkFalse("0x0p0F"); + checkFalse("0x0.0p1F"); + checkFalse("0x.0p1F"); + checkFalse("0x.5AFp1f"); + checkFalse("0x0050AF.CD9p-008f"); + checkFalse("0x1.fffffeP+127f"); + checkFalse("0x0p-5f"); + checkFalse("0x0p1F"); + checkFalse("0x0p-5F"); + + // Examples from Java Language Specification + checkFalse("1e1f"); + checkFalse("2.f"); + checkFalse(".3f"); + checkFalse("0f"); + checkFalse("6.022137e+23f"); + } + catch (IOException e) + { + fail(e.getMessage()); + } + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/mccommonliterals/IntCommonLiteralsTest.java b/monticore-grammar/src/test/java/de/monticore/mccommonliterals/IntCommonLiteralsTest.java new file mode 100644 index 0000000000..c23ea0e078 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/mccommonliterals/IntCommonLiteralsTest.java @@ -0,0 +1,139 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.mccommonliterals; + +import de.monticore.literals.mccommonliterals._ast.ASTNatLiteral; +import de.monticore.literals.mccommonliterals._ast.ASTSignedNatLiteral; +import de.monticore.literals.mcliteralsbasis._ast.ASTLiteral; +import de.monticore.literals.testmccommonliterals.TestMCCommonLiteralsMill; +import de.monticore.literals.testmccommonliterals._parser.TestMCCommonLiteralsParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.io.StringReader; +import java.util.Optional; + +import static org.junit.Assert.*; + +public class IntCommonLiteralsTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestMCCommonLiteralsMill.reset(); + TestMCCommonLiteralsMill.init(); + } + + private void checkIntLiteral(int i, String s) throws IOException { + TestMCCommonLiteralsParser parser = new TestMCCommonLiteralsParser(); + Optional lit = parser.parseLiteral(new StringReader(s)); + assertTrue(lit.isPresent()); + assertTrue(lit.get() instanceof ASTNatLiteral); + assertEquals(i, ((ASTNatLiteral) lit.get()).getValue()); + + assertTrue(Log.getFindings().isEmpty()); + } + + private void checkSignedIntLiteral(int i, String s) throws IOException { + TestMCCommonLiteralsParser parser = new TestMCCommonLiteralsParser(); + Optional lit = parser.parseSignedNatLiteral(new StringReader(s)); + assertTrue(lit.isPresent()); + assertTrue(lit.get() instanceof ASTSignedNatLiteral); + assertEquals(i, ((ASTSignedNatLiteral) lit.get()).getValue()); + + assertTrue(Log.getFindings().isEmpty()); + } + + private void checkFalse(String s) throws IOException { + TestMCCommonLiteralsParser parser = new TestMCCommonLiteralsParser(); + Optional lit = parser.parseNatLiteral(new StringReader(s)); + assertTrue(!lit.isPresent()); + } + + private void checkSignedFalse(String s) throws IOException { + TestMCCommonLiteralsParser parser = new TestMCCommonLiteralsParser(); + Optional lit = parser.parseSignedNatLiteral(new StringReader(s)); + assertTrue(!lit.isPresent()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testIntLiterals() { + try { + // decimal number + checkIntLiteral(0, "0"); + checkIntLiteral(123, "123"); + checkIntLiteral(10, "10"); + checkIntLiteral(5, "5"); + + // number with leading 0 + checkIntLiteral(2, "02"); + checkIntLiteral(7, "07"); + checkIntLiteral(0, "00"); + checkIntLiteral(76543210, "076543210"); + checkIntLiteral(17, "00017"); + } + catch (IOException e) { + fail(e.getMessage()); + } + } + + @Test + public void testFalse() { + try { + // hexadezimal number + checkFalse("0x12"); + checkFalse("0Xeff"); + checkFalse("0x34567890"); + checkFalse("0xabcdef"); + checkFalse("0x0"); + checkFalse("0xa"); + checkFalse("0xC0FFEE"); + checkFalse("0x005f"); + } + catch (IOException e) { + fail(e.getMessage()); + } + } + + @Test + public void testSignedIntLiterals() { + try { + // decimal number + checkSignedIntLiteral(0, "0"); + checkSignedIntLiteral(-123, "-123"); + checkSignedIntLiteral(-10, "-10"); + checkSignedIntLiteral(-5, "-5"); + + // number with leading 0 + checkSignedIntLiteral(-2, "-02"); + checkSignedIntLiteral(-7, "-07"); + checkSignedIntLiteral(0, "00"); + checkSignedIntLiteral(-76543210, "-076543210"); + checkSignedIntLiteral(-17, "-00017"); + } + catch (IOException e) { + fail(e.getMessage()); + } + } + + @Test + public void testSignedFalse() { + try { + // hexadezimal number + checkFalse("0x12"); + checkFalse("- 2"); + checkFalse("- 02"); + } + catch (IOException e) { + fail(e.getMessage()); + } + } + + +} diff --git a/monticore-grammar/src/test/java/de/monticore/mccommonliterals/LongCommonLiteralsTest.java b/monticore-grammar/src/test/java/de/monticore/mccommonliterals/LongCommonLiteralsTest.java new file mode 100644 index 0000000000..dfcf91862d --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/mccommonliterals/LongCommonLiteralsTest.java @@ -0,0 +1,81 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.mccommonliterals; + +import de.monticore.literals.mccommonliterals._ast.ASTBasicLongLiteral; +import de.monticore.literals.mcliteralsbasis._ast.ASTLiteral; +import de.monticore.literals.testmccommonliterals.TestMCCommonLiteralsMill; +import de.monticore.literals.testmccommonliterals._parser.TestMCCommonLiteralsParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.io.StringReader; +import java.util.Optional; + +import static org.junit.Assert.*; + +public class LongCommonLiteralsTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestMCCommonLiteralsMill.reset(); + TestMCCommonLiteralsMill.init(); + } + + private void checkLongLiteral(long l, String s) throws IOException { + TestMCCommonLiteralsParser parser = new TestMCCommonLiteralsParser(); + Optional lit = parser.parseLiteral(new StringReader(s)); + assertTrue(lit.isPresent()); + assertTrue(lit.get() instanceof ASTBasicLongLiteral); + assertEquals(l, ((ASTBasicLongLiteral) lit.get()).getValue()); + + assertTrue(Log.getFindings().isEmpty()); + } + + private void checkFalse(String s) throws IOException { + TestMCCommonLiteralsParser parser = new TestMCCommonLiteralsParser(); + Optional lit = parser.parseBasicLongLiteral(new StringReader(s)); + assertTrue(!lit.isPresent()); + } + + @Test + public void testLongLiterals() { + try { + // decimal number + checkLongLiteral(0L, "0L"); + checkLongLiteral(123L, "123L"); + checkLongLiteral(10L, "10L"); + checkLongLiteral(5L, "5L"); + checkLongLiteral(5L, "05L"); + checkLongLiteral(5L, "05L"); + + } + catch (IOException e) { + fail(e.getMessage()); + } + } + + @Test + public void testFalse() { + try { + // hexadezimal number + checkFalse("0x12L"); + checkFalse("0XeffL"); + checkFalse("0x1234567890L"); + checkFalse("0xabcdefL"); + checkFalse("0x0L"); + checkFalse("0xaL"); + checkFalse("0xC0FFEEL"); + checkFalse("0x005fL"); + checkFalse("0 L"); + } + catch (IOException e) { + fail(e.getMessage()); + } + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/mccommonliterals/RangeCoCosTest.java b/monticore-grammar/src/test/java/de/monticore/mccommonliterals/RangeCoCosTest.java new file mode 100644 index 0000000000..8c53c1e4cd --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/mccommonliterals/RangeCoCosTest.java @@ -0,0 +1,206 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.mccommonliterals; + +import de.monticore.literals.mccommonliterals._ast.ASTSignedLiteral; +import de.monticore.literals.mccommonliterals._cocos.MCCommonLiteralsCoCoChecker; +import de.monticore.literals.mccommonliterals.cocos.*; +import de.monticore.literals.mcliteralsbasis._ast.ASTLiteral; +import de.monticore.literals.testmccommonliterals.TestMCCommonLiteralsMill; +import de.se_rwth.commons.logging.Log; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Optional; + +import static org.junit.Assert.*; + +public class RangeCoCosTest { + + @Before + public void setup(){ + Log.init(); + Log.enableFailQuick(false); + TestMCCommonLiteralsMill.reset(); + TestMCCommonLiteralsMill.init(); + } + + protected final void checkLiteral(String expression, BigInteger min, BigInteger max) throws IOException { + Log.clearFindings(); + Optional astex = TestMCCommonLiteralsMill.parser().parse_StringLiteral(expression); + assertTrue(astex.isPresent()); + + MCCommonLiteralsCoCoChecker checker = new MCCommonLiteralsCoCoChecker(); + checker.addCoCo(new BasicFloatLiteralRangeCoCo(new BigDecimal(min), new BigDecimal(max))); + checker.addCoCo(new BasicDoubleLiteralRangeCoCo(new BigDecimal(min), new BigDecimal(max))); + checker.addCoCo(new BasicLongLiteralRangeCoCo(min, max)); + checker.addCoCo(new NatLiteralRangeCoCo(min, max)); + + checker.checkAll(astex.get()); + + assertFalse(Log.getErrorCount() > 0); + } + + protected final void checkLiteral(String expression) throws IOException { + Log.clearFindings(); + Optional astex = TestMCCommonLiteralsMill.parser().parse_StringLiteral(expression); + assertTrue(astex.isPresent()); + + MCCommonLiteralsCoCoChecker checker = new MCCommonLiteralsCoCoChecker(); + checker.addCoCo(new BasicFloatLiteralRangeCoCo()); + checker.addCoCo(new BasicDoubleLiteralRangeCoCo()); + checker.addCoCo(new BasicLongLiteralRangeCoCo()); + checker.addCoCo(new NatLiteralRangeCoCo()); + + checker.checkAll(astex.get()); + + assertFalse(Log.getErrorCount() > 0); + } + + protected final void checkSignedLiteral(String expression) throws IOException { + Log.clearFindings(); + Optional astex = TestMCCommonLiteralsMill.parser().parse_StringSignedLiteral(expression); + assertTrue(astex.isPresent()); + + MCCommonLiteralsCoCoChecker checker = new MCCommonLiteralsCoCoChecker(); + checker.addCoCo(new SignedBasicFloatLiteralRangeCoCo()); + checker.addCoCo(new SignedBasicDoubleLiteralRangeCoCo()); + checker.addCoCo(new SignedBasicLongLiteralRangeCoCo()); + checker.addCoCo(new SignedNatLiteralRangeCoCo()); + + checker.checkAll(astex.get()); + + assertFalse(Log.getErrorCount() > 0); + } + + protected final void checkErrorLiteral(String expression, BigInteger min, BigInteger max, String expectedError) throws IOException { + Log.clearFindings(); + Optional astex = TestMCCommonLiteralsMill.parser().parse_StringLiteral(expression); + assertTrue(astex.isPresent()); + + MCCommonLiteralsCoCoChecker checker = new MCCommonLiteralsCoCoChecker(); + checker.addCoCo(new BasicFloatLiteralRangeCoCo(new BigDecimal(min), new BigDecimal(max))); + checker.addCoCo(new BasicDoubleLiteralRangeCoCo(new BigDecimal(min), new BigDecimal(max))); + checker.addCoCo(new BasicLongLiteralRangeCoCo(min, max)); + checker.addCoCo(new NatLiteralRangeCoCo(min, max)); + + checker.checkAll(astex.get()); + + assertEquals(1, Log.getErrorCount()); + assertTrue(Log.getFindings().get(0).getMsg().startsWith(expectedError)); + } + + protected final void checkErrorLiteral(String expression, String expectedError) throws IOException { + Log.clearFindings(); + Optional astex = TestMCCommonLiteralsMill.parser().parse_StringLiteral(expression); + assertTrue(astex.isPresent()); + + MCCommonLiteralsCoCoChecker checker = new MCCommonLiteralsCoCoChecker(); + checker.addCoCo(new BasicFloatLiteralRangeCoCo()); + checker.addCoCo(new BasicDoubleLiteralRangeCoCo()); + checker.addCoCo(new BasicLongLiteralRangeCoCo()); + checker.addCoCo(new NatLiteralRangeCoCo()); + + checker.checkAll(astex.get()); + + assertEquals(1, Log.getErrorCount()); + assertTrue(Log.getFindings().get(0).getMsg().startsWith(expectedError)); + } + + protected final void checkErrorSignedLiteral(String expression, BigInteger min, BigInteger max, String expectedError) throws IOException { + Log.clearFindings(); + Optional astex = TestMCCommonLiteralsMill.parser().parse_StringSignedLiteral(expression); + assertTrue(astex.isPresent()); + + MCCommonLiteralsCoCoChecker checker = new MCCommonLiteralsCoCoChecker(); + checker.addCoCo(new SignedBasicFloatLiteralRangeCoCo(new BigDecimal(min), new BigDecimal(max))); + checker.addCoCo(new SignedBasicDoubleLiteralRangeCoCo(new BigDecimal(min), new BigDecimal(max))); + checker.addCoCo(new SignedBasicLongLiteralRangeCoCo(min, max)); + checker.addCoCo(new SignedNatLiteralRangeCoCo(min, max)); + + checker.checkAll(astex.get()); + + assertEquals(1, Log.getErrorCount()); + assertTrue(Log.getFindings().get(0).getMsg().startsWith(expectedError)); + } + + protected final void checkErrorSignedLiteral(String expression, String expectedError) throws IOException { + Log.clearFindings(); + Optional astex = TestMCCommonLiteralsMill.parser().parse_StringSignedLiteral(expression); + assertTrue(astex.isPresent()); + + MCCommonLiteralsCoCoChecker checker = new MCCommonLiteralsCoCoChecker(); + checker.addCoCo(new SignedBasicFloatLiteralRangeCoCo()); + checker.addCoCo(new SignedBasicDoubleLiteralRangeCoCo()); + checker.addCoCo(new SignedBasicLongLiteralRangeCoCo()); + checker.addCoCo(new SignedNatLiteralRangeCoCo()); + + checker.checkAll(astex.get()); + + assertEquals(1, Log.getErrorCount()); + assertTrue(Log.getFindings().get(0).getMsg().startsWith(expectedError)); + } + + + + @Test + public void testIntegerLiteralRange() throws IOException { + checkLiteral(String.valueOf(Integer.MAX_VALUE)); + checkLiteral("1"); + checkLiteral("123"); + checkSignedLiteral(String.valueOf(Integer.MIN_VALUE)); + checkSignedLiteral("-1"); + checkSignedLiteral("-123"); + + checkErrorLiteral("2147483648", "0xA0208"); + checkErrorSignedLiteral("-2147483649", "0xA0210"); + + checkLiteral("2000", new BigInteger("-2000"), new BigInteger("2000")); + checkErrorLiteral("2001", new BigInteger("-2000"), new BigInteger("2000"), "0xA0208"); + checkErrorSignedLiteral("-2001", new BigInteger("-2000"), new BigInteger("2000"), "0xA0210"); + } + + @Test + public void testLongLiteralRange() throws IOException { + checkLiteral(String.valueOf(Long.MAX_VALUE) + "L"); + checkLiteral("1L"); + checkLiteral("123L"); + checkSignedLiteral(String.valueOf(Long.MIN_VALUE) + "L"); + checkSignedLiteral("-1L"); + checkSignedLiteral("-123L"); + + checkErrorLiteral("9223372036854775808L", "0xA0209"); + checkErrorSignedLiteral("-9223372036854775809L", "0xA0211"); + + checkLiteral("2000L", new BigInteger("-2000"), new BigInteger("2000")); + checkErrorLiteral("2001L", new BigInteger("-2000"), new BigInteger("2000"), "0xA0209"); + checkErrorSignedLiteral("-2001L", new BigInteger("-2000"), new BigInteger("2000"), "0xA0211"); + } + + @Test + public void testDoubleLiteralRange() throws IOException { + checkLiteral("1.0"); + checkLiteral("123.0"); + checkSignedLiteral("-1.0"); + checkSignedLiteral("-123.0"); + + checkLiteral("2000.0", new BigInteger("-2000"), new BigInteger("2000")); + checkErrorLiteral("2001.0", new BigInteger("-2000"), new BigInteger("2000"), "0xA0212"); + checkErrorSignedLiteral("-2001.0", new BigInteger("-2000"), new BigInteger("2000"), "0xA0214"); + } + + @Test + public void testFloatLiteralRange() throws IOException { + checkLiteral("1.0f"); + checkLiteral("123.0f"); + checkSignedLiteral("-1.0f"); + checkSignedLiteral("-123.0f"); + + checkLiteral("2000.0f", new BigInteger("-2000"), new BigInteger("2000")); + checkErrorLiteral("2001.0f", new BigInteger("-2000"), new BigInteger("2000"), "0xA0213"); + checkErrorSignedLiteral("-2001.0f", new BigInteger("-2000"), new BigInteger("2000"), "0xA0215"); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/mcjavaliterals/DoubleJavaLiteralsTest.java b/monticore-grammar/src/test/java/de/monticore/mcjavaliterals/DoubleJavaLiteralsTest.java new file mode 100644 index 0000000000..94db0fa39d --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/mcjavaliterals/DoubleJavaLiteralsTest.java @@ -0,0 +1,90 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.mcjavaliterals; + +import de.monticore.literals.mcjavaliterals._ast.ASTDoubleLiteral; +import de.monticore.literals.mcliteralsbasis._ast.ASTLiteral; +import de.monticore.literals.testmcjavaliterals.TestMCJavaLiteralsMill; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; + +import static org.junit.Assert.*; + +public class DoubleJavaLiteralsTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestMCJavaLiteralsMill.reset(); + TestMCJavaLiteralsMill.init(); + } + + private void checkDoubleLiteral(double d, String s) throws IOException { + ASTLiteral lit = MCJavaLiteralsTestHelper.getInstance().parseLiteral(s); + assertTrue(lit instanceof ASTDoubleLiteral); + assertEquals(d, ((ASTDoubleLiteral) lit).getValue(), 0); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testDoubleLiterals() { + try { + // decimal number + checkDoubleLiteral(0.0, "0.0"); + checkDoubleLiteral(.0, ".0"); + checkDoubleLiteral(0., "0."); + checkDoubleLiteral(5d, "5d"); + checkDoubleLiteral(.4, ".4"); + checkDoubleLiteral(000009.3, "000009.3"); + checkDoubleLiteral(5., "5."); + checkDoubleLiteral(009., "009."); + checkDoubleLiteral(009e2, "009e2"); + checkDoubleLiteral(23.4, "23.4"); + checkDoubleLiteral(2e3, "2e3"); + checkDoubleLiteral(2E-3, "2E-3"); + checkDoubleLiteral(009d, "009d"); + checkDoubleLiteral(.1e1, ".1e1"); + checkDoubleLiteral(.1, ".1"); + checkDoubleLiteral(.11e12, ".11e12"); + checkDoubleLiteral(.11e+12, ".11e+12"); + checkDoubleLiteral(29.18e08, "29.18e08"); + checkDoubleLiteral(0029.0008e-00008, "0029.0008e-00008"); + checkDoubleLiteral(0029.0008e-00008D, "0029.0008e-00008D"); + + // hexadezimal number + checkDoubleLiteral(0x5.p1, "0x5.p1"); + checkDoubleLiteral(0x.5p1, "0x.5p1"); + checkDoubleLiteral(0xFp-9, "0xFp-9"); + checkDoubleLiteral(0xfP2, "0xfP2"); + checkDoubleLiteral(0xfp1, "0xfp1"); + checkDoubleLiteral(0x.fP1, "0x.fP1"); + checkDoubleLiteral(0x0p0, "0x0p0"); + checkDoubleLiteral(0x0.0p1, "0x0.0p1"); + checkDoubleLiteral(0x.0p1, "0x.0p1"); + checkDoubleLiteral(0x.5AFp1, "0x.5AFp1"); + checkDoubleLiteral(0x0050AF.CD9p-008, "0x0050AF.CD9p-008"); + checkDoubleLiteral(0x0050AF.CD9p-008d, "0x0050AF.CD9p-008d"); + checkDoubleLiteral(0x0p-5, "0x0p-5"); + checkDoubleLiteral(0x0p1, "0x0p1"); + checkDoubleLiteral(0x0p1d, "0x0p1d"); + + // Examples from Java Language Specification + checkDoubleLiteral(.3, ".3"); + checkDoubleLiteral(1e1, "1e1"); + checkDoubleLiteral(2., "2."); + checkDoubleLiteral(0.0, "0.0"); + checkDoubleLiteral(3.14, "3.14"); + checkDoubleLiteral(1e-9d, "1e-9d"); + checkDoubleLiteral(1e137, "1e137"); + } + catch (IOException e) { + fail(e.getMessage()); + } + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/mcjavaliterals/FloatJavaLiteralsTest.java b/monticore-grammar/src/test/java/de/monticore/mcjavaliterals/FloatJavaLiteralsTest.java new file mode 100644 index 0000000000..d7d3301c8e --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/mcjavaliterals/FloatJavaLiteralsTest.java @@ -0,0 +1,90 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.mcjavaliterals; + +import de.monticore.literals.mcjavaliterals._ast.ASTFloatLiteral; +import de.monticore.literals.mcliteralsbasis._ast.ASTLiteral; +import de.monticore.literals.testmcjavaliterals.TestMCJavaLiteralsMill; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; + +import static org.junit.Assert.*; + +public class FloatJavaLiteralsTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestMCJavaLiteralsMill.reset(); + TestMCJavaLiteralsMill.init(); + } + + private void checkFloatLiteral(float f, String s) throws IOException { + ASTLiteral lit = MCJavaLiteralsTestHelper.getInstance().parseLiteral(s); + assertTrue(lit instanceof ASTFloatLiteral); + assertEquals(f, ((ASTFloatLiteral) lit).getValue(), 0); + assertTrue(true); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testFloatLiterals() { + // decimal number + try { + checkFloatLiteral(0F, "0F"); + checkFloatLiteral(0.0F, "0.0F"); + checkFloatLiteral(5F, "5F"); + checkFloatLiteral(.4F, ".4F"); + checkFloatLiteral(000009.3F, "000009.3F"); + checkFloatLiteral(5.F, "5.F"); + checkFloatLiteral(009.f, "009.f"); + checkFloatLiteral(009f, "009f"); + checkFloatLiteral(009e2f, "009e2f"); + checkFloatLiteral(23.4F, "23.4F"); + checkFloatLiteral(2e3F, "2e3F"); + checkFloatLiteral(2E-3F, "2E-3F"); + checkFloatLiteral(009f, "009f"); + checkFloatLiteral(.1e1F, ".1e1F"); + checkFloatLiteral(.1F, ".1F"); + checkFloatLiteral(.11e12F, ".11e12F"); + checkFloatLiteral(.11e+12F, ".11e+12F"); + checkFloatLiteral(29.18e08F, "29.18e08F"); + checkFloatLiteral(0029.0008e-00008F, "0029.0008e-00008F"); + + // hexadezimal number + checkFloatLiteral(0x5.p1f, "0x5.p1f"); + // checkFloatLiteral(0x.5p1f, "0x.5p1f"); + checkFloatLiteral(0xFp-9f, "0xFp-9f"); + checkFloatLiteral(0xfP2F, "0xfP2F"); + checkFloatLiteral(0xfp1F, "0xfp1F"); + checkFloatLiteral(0x.fP1F, "0x.fP1F"); + checkFloatLiteral(0x0p0F, "0x0p0F"); + checkFloatLiteral(0x0.0p1F, "0x0.0p1F"); + checkFloatLiteral(0x.0p1F, "0x.0p1F"); + checkFloatLiteral(0x.5AFp1f, "0x.5AFp1f"); + checkFloatLiteral(0x0050AF.CD9p-008f, "0x0050AF.CD9p-008f"); + checkFloatLiteral(0x1.fffffeP+127f, "0x1.fffffeP+127f"); + checkFloatLiteral(0x0p-5f, "0x0p-5f"); + checkFloatLiteral(0x0p1F, "0x0p1F"); + checkFloatLiteral(0x0p-5F, "0x0p-5F"); + + // Examples from Java Language Specification + checkFloatLiteral(1e1f, "1e1f"); + checkFloatLiteral(2.f, "2.f"); + checkFloatLiteral(.3f, ".3f"); + checkFloatLiteral(0f, "0f"); + checkFloatLiteral(3.14f, "3.14f"); + checkFloatLiteral(6.022137e+23f, "6.022137e+23f"); + } + catch (IOException e) + { + fail(e.getMessage()); + } + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/mcjavaliterals/IntJavaLiteralsTest.java b/monticore-grammar/src/test/java/de/monticore/mcjavaliterals/IntJavaLiteralsTest.java new file mode 100644 index 0000000000..c8d6c3bfce --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/mcjavaliterals/IntJavaLiteralsTest.java @@ -0,0 +1,65 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.mcjavaliterals; + +import de.monticore.literals.mcjavaliterals._ast.ASTIntLiteral; +import de.monticore.literals.mcliteralsbasis._ast.ASTLiteral; +import de.monticore.literals.testmcjavaliterals.TestMCJavaLiteralsMill; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; + +import static org.junit.Assert.*; + +public class IntJavaLiteralsTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestMCJavaLiteralsMill.reset(); + TestMCJavaLiteralsMill.init(); + } + + private void checkIntLiteral(int i, String s) throws IOException { + ASTLiteral lit = MCJavaLiteralsTestHelper.getInstance().parseLiteral(s); + assertTrue(lit instanceof ASTIntLiteral); + assertEquals(i, ((ASTIntLiteral) lit).getValue()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testIntLiterals() { + try { + // decimal number + checkIntLiteral(0, "0"); + checkIntLiteral(123, "123"); + checkIntLiteral(10, "10"); + checkIntLiteral(5, "5"); + + // hexadezimal number + checkIntLiteral(0x12, "0x12"); + checkIntLiteral(0Xeff, "0Xeff"); + checkIntLiteral(0x34567890, "0x34567890"); + checkIntLiteral(0xabcdef, "0xabcdef"); + checkIntLiteral(0x0, "0x0"); + checkIntLiteral(0xa, "0xa"); + checkIntLiteral(0xC0FFEE, "0xC0FFEE"); + checkIntLiteral(0x005f, "0x005f"); + + // octal number + checkIntLiteral(02, "02"); + checkIntLiteral(07, "07"); + checkIntLiteral(00, "00"); + checkIntLiteral(076543210, "076543210"); + checkIntLiteral(00017, "00017"); + } + catch (IOException e) { + fail(e.getMessage()); + } + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/mcjavaliterals/LongJavaLiteralsTest.java b/monticore-grammar/src/test/java/de/monticore/mcjavaliterals/LongJavaLiteralsTest.java new file mode 100644 index 0000000000..5cbf1728d2 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/mcjavaliterals/LongJavaLiteralsTest.java @@ -0,0 +1,66 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.mcjavaliterals; + +import de.monticore.literals.mcjavaliterals._ast.ASTLongLiteral; +import de.monticore.literals.mcliteralsbasis._ast.ASTLiteral; +import de.monticore.literals.testmcjavaliterals.TestMCJavaLiteralsMill; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; + +import static org.junit.Assert.*; + +public class LongJavaLiteralsTest { + + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestMCJavaLiteralsMill.reset(); + TestMCJavaLiteralsMill.init(); + } + + private void checkLongLiteral(long l, String s) throws IOException { + ASTLiteral lit = MCJavaLiteralsTestHelper.getInstance().parseLiteral(s); + assertTrue(lit instanceof ASTLongLiteral); + assertEquals(l, ((ASTLongLiteral) lit).getValue()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testLongLiterals() { + try { + // decimal number + checkLongLiteral(0L, "0L"); + checkLongLiteral(123L, "123L"); + checkLongLiteral(10L, "10L"); + checkLongLiteral(5L, "5L"); + + // hexadezimal number + checkLongLiteral(0x12L, "0x12L"); + checkLongLiteral(0XeffL, "0XeffL"); + checkLongLiteral(0x1234567890L, "0x1234567890L"); + checkLongLiteral(0xabcdefL, "0xabcdefL"); + checkLongLiteral(0x0L, "0x0L"); + checkLongLiteral(0xaL, "0xaL"); + checkLongLiteral(0xC0FFEEL, "0xC0FFEEL"); + checkLongLiteral(0x005fL, "0x005fL"); + + // octal number + checkLongLiteral(02L, "02L"); + checkLongLiteral(07L, "07L"); + checkLongLiteral(00L, "00L"); + checkLongLiteral(076543210L, "076543210L"); + checkLongLiteral(00017L, "00017L"); + } + catch (IOException e) { + fail(e.getMessage()); + } + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/mcjavaliterals/MCJavaLiteralsTestHelper.java b/monticore-grammar/src/test/java/de/monticore/mcjavaliterals/MCJavaLiteralsTestHelper.java new file mode 100644 index 0000000000..0db56550cf --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/mcjavaliterals/MCJavaLiteralsTestHelper.java @@ -0,0 +1,56 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.mcjavaliterals; + +import de.monticore.literals.mcliteralsbasis._ast.ASTLiteral; +import de.monticore.literals.testmcjavaliterals._parser.TestMCJavaLiteralsParser; +import junit.framework.TestCase; + +import java.io.IOException; +import java.io.StringReader; +import java.util.Optional; + +/** + * This class provides two methods that allow testing type grammar. The test + * parses a given input string to an AST. The AST is printed via prettyprint and + * parsed again. The resulting ASTs are compared. The TypeTestHelper is a + * singleton. + */ +public class MCJavaLiteralsTestHelper { + + private static MCJavaLiteralsTestHelper instance; + + /** + * We have a singleton. + */ + private MCJavaLiteralsTestHelper() { + } + + /** + * Returns the singleton instance. + * + * @return The instance. + */ + public static MCJavaLiteralsTestHelper getInstance() { + if (instance == null) { + instance = new MCJavaLiteralsTestHelper(); + } + return instance; + } + + /** + * This method parses a literal from a given string. + * + * @param input Literal as a string. + * @return The ASTLiteral or null. + * @throws IOException + */ + public ASTLiteral parseLiteral(String input) throws IOException { + TestMCJavaLiteralsParser parser = new TestMCJavaLiteralsParser(); + Optional res = parser.parseLiteral(new StringReader(input)); + TestCase.assertTrue(res.isPresent()); + return res.get(); + } + + +} diff --git a/monticore-grammar/src/test/java/de/monticore/mcjavaliterals/RangeCoCosTest.java b/monticore-grammar/src/test/java/de/monticore/mcjavaliterals/RangeCoCosTest.java new file mode 100644 index 0000000000..cb9df3ba50 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/mcjavaliterals/RangeCoCosTest.java @@ -0,0 +1,194 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.mcjavaliterals; + +import de.monticore.literals.mcjavaliterals._ast.*; +import de.monticore.literals.mcjavaliterals._cocos.MCJavaLiteralsCoCoChecker; +import de.monticore.literals.mcjavaliterals.cocos.DoubleLiteralRangeCoCo; +import de.monticore.literals.mcjavaliterals.cocos.FloatLiteralRangeCoCo; +import de.monticore.literals.mcjavaliterals.cocos.IntLiteralRangeCoCo; +import de.monticore.literals.mcjavaliterals.cocos.LongLiteralRangeCoCo; +import de.monticore.literals.testmcjavaliterals.TestMCJavaLiteralsMill; +import de.se_rwth.commons.logging.Log; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Optional; + +import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; + +public class RangeCoCosTest { + + @Before + public void setup(){ + Log.init(); + Log.enableFailQuick(false); + TestMCJavaLiteralsMill.reset(); + TestMCJavaLiteralsMill.init(); + } + + + protected final void checkIntLiteral(String expression, BigInteger min, BigInteger max) throws IOException { + Log.clearFindings(); + Optional astex = TestMCJavaLiteralsMill.parser().parse_StringIntLiteral(expression); + assertTrue(astex.isPresent()); + + MCJavaLiteralsCoCoChecker checker = new MCJavaLiteralsCoCoChecker(); + checker.addCoCo(new IntLiteralRangeCoCo(min, max)); + + checker.checkAll((ASTMCJavaLiteralsNode) astex.get()); + + assertFalse(Log.getErrorCount() > 0); + } + + protected final void checkLongLiteral(String expression, BigInteger min, BigInteger max) throws IOException { + Log.clearFindings(); + Optional astex = TestMCJavaLiteralsMill.parser().parse_StringLongLiteral(expression); + assertTrue(astex.isPresent()); + + MCJavaLiteralsCoCoChecker checker = new MCJavaLiteralsCoCoChecker(); + checker.addCoCo(new LongLiteralRangeCoCo(min, max)); + + checker.checkAll((ASTMCJavaLiteralsNode) astex.get()); + + assertFalse(Log.getErrorCount() > 0); + } + + protected final void checkDoubleLiteral(String expression, BigDecimal min, BigDecimal max) throws IOException { + Log.clearFindings(); + Optional astex = TestMCJavaLiteralsMill.parser().parse_StringDoubleLiteral(expression); + assertTrue(astex.isPresent()); + + MCJavaLiteralsCoCoChecker checker = new MCJavaLiteralsCoCoChecker(); + checker.addCoCo(new DoubleLiteralRangeCoCo(min, max)); + + checker.checkAll((ASTMCJavaLiteralsNode) astex.get()); + + assertFalse(Log.getErrorCount() > 0); + } + + protected final void checkFloatLiteral(String expression, BigDecimal min, BigDecimal max) throws IOException { + Log.clearFindings(); + Optional astex = TestMCJavaLiteralsMill.parser().parse_StringFloatLiteral(expression); + assertTrue(astex.isPresent()); + + MCJavaLiteralsCoCoChecker checker = new MCJavaLiteralsCoCoChecker(); + checker.addCoCo(new FloatLiteralRangeCoCo(min, max)); + + checker.checkAll((ASTMCJavaLiteralsNode) astex.get()); + + assertFalse(Log.getErrorCount() > 0); + } + + protected final void checkErrorIntLiteral(String expression, BigInteger min, BigInteger max, String expectedError) throws IOException { + Log.clearFindings(); + Optional astex = TestMCJavaLiteralsMill.parser().parse_StringIntLiteral(expression); + assertTrue(astex.isPresent()); + + MCJavaLiteralsCoCoChecker checker = new MCJavaLiteralsCoCoChecker(); + checker.addCoCo(new IntLiteralRangeCoCo(min, max)); + + checker.checkAll((ASTMCJavaLiteralsNode) astex.get()); + + assertEquals(1, Log.getErrorCount()); + assertTrue(Log.getFindings().get(0).getMsg().startsWith(expectedError)); + } + + protected final void checkErrorLongLiteral(String expression, BigInteger min, BigInteger max, String expectedError) throws IOException { + Log.clearFindings(); + Optional astex = TestMCJavaLiteralsMill.parser().parse_StringLongLiteral(expression); + assertTrue(astex.isPresent()); + + MCJavaLiteralsCoCoChecker checker = new MCJavaLiteralsCoCoChecker(); + checker.addCoCo(new LongLiteralRangeCoCo(min, max)); + + checker.checkAll((ASTMCJavaLiteralsNode) astex.get()); + + assertEquals(1, Log.getErrorCount()); + assertTrue(Log.getFindings().get(0).getMsg().startsWith(expectedError)); + } + + protected final void checkErrorDoubleLiteral(String expression, BigDecimal min, BigDecimal max, String expectedError) throws IOException { + Log.clearFindings(); + Optional astex = TestMCJavaLiteralsMill.parser().parse_StringDoubleLiteral(expression); + assertTrue(astex.isPresent()); + + MCJavaLiteralsCoCoChecker checker = new MCJavaLiteralsCoCoChecker(); + checker.addCoCo(new DoubleLiteralRangeCoCo(min, max)); + + checker.checkAll((ASTMCJavaLiteralsNode) astex.get()); + + assertEquals(1, Log.getErrorCount()); + assertTrue(Log.getFindings().get(0).getMsg().startsWith(expectedError)); + } + + protected final void checkErrorFloatLiteral(String expression, BigDecimal min, BigDecimal max, String expectedError) throws IOException { + Log.clearFindings(); + Optional astex = TestMCJavaLiteralsMill.parser().parse_StringFloatLiteral(expression); + assertTrue(astex.isPresent()); + + MCJavaLiteralsCoCoChecker checker = new MCJavaLiteralsCoCoChecker(); + checker.addCoCo(new FloatLiteralRangeCoCo(min, max)); + + checker.checkAll((ASTMCJavaLiteralsNode) astex.get()); + + assertEquals(1, Log.getErrorCount()); + assertTrue(Log.getFindings().get(0).getMsg().startsWith(expectedError)); + } + + @Test + public void testInt() throws IOException{ + checkIntLiteral(String.valueOf(Integer.MAX_VALUE), BigInteger.valueOf(Integer.MIN_VALUE), BigInteger.valueOf(Integer.MAX_VALUE)); + checkIntLiteral("1", BigInteger.valueOf(Integer.MIN_VALUE), BigInteger.valueOf(Integer.MAX_VALUE)); + checkIntLiteral("123", BigInteger.valueOf(Integer.MIN_VALUE), BigInteger.valueOf(Integer.MAX_VALUE)); + checkIntLiteral("0xABCDEF", BigInteger.valueOf(Integer.MIN_VALUE), BigInteger.valueOf(Integer.MAX_VALUE)); + + checkErrorIntLiteral("0x80000000", BigInteger.valueOf(Integer.MIN_VALUE), BigInteger.valueOf(Integer.MAX_VALUE), "0xA0216"); + + checkIntLiteral("2000", new BigInteger("-2000"), new BigInteger("2000")); + checkErrorIntLiteral("2001", new BigInteger("-2000"), new BigInteger("2000"), "0xA0216"); + } + + @Test + public void testLong() throws IOException{ + checkLongLiteral(String.valueOf(Long.MAX_VALUE) + "L", BigInteger.valueOf(Long.MIN_VALUE), BigInteger.valueOf(Long.MAX_VALUE)); + checkLongLiteral("1L", BigInteger.valueOf(Long.MIN_VALUE), BigInteger.valueOf(Long.MAX_VALUE)); + checkLongLiteral("123L", BigInteger.valueOf(Long.MIN_VALUE), BigInteger.valueOf(Long.MAX_VALUE)); + checkLongLiteral("0xABCDEFL", BigInteger.valueOf(Long.MIN_VALUE), BigInteger.valueOf(Long.MAX_VALUE)); + + checkErrorLongLiteral("0x8000000000000000L", BigInteger.valueOf(Long.MIN_VALUE), BigInteger.valueOf(Long.MAX_VALUE), "0xA0217"); + + checkLongLiteral("2000L", new BigInteger("-2000"), new BigInteger("2000")); + checkErrorLongLiteral("2001L", new BigInteger("-2000"), new BigInteger("2000"), "0xA0217"); + } + + @Test + public void testDouble() throws IOException { + checkDoubleLiteral(String.valueOf(Double.MAX_VALUE), BigDecimal.valueOf(-Double.MAX_VALUE), BigDecimal.valueOf(Double.MAX_VALUE)); + checkDoubleLiteral("1.0", BigDecimal.valueOf(-Double.MAX_VALUE), BigDecimal.valueOf(Double.MAX_VALUE)); + checkDoubleLiteral("123.0", BigDecimal.valueOf(-Double.MAX_VALUE), BigDecimal.valueOf(Double.MAX_VALUE)); + checkDoubleLiteral(String.valueOf(Double.MIN_VALUE), BigDecimal.valueOf(-Double.MAX_VALUE), BigDecimal.valueOf(Double.MAX_VALUE)); + + checkErrorDoubleLiteral("1.7976931348623157e+309", BigDecimal.valueOf(-Double.MAX_VALUE), BigDecimal.valueOf(Double.MAX_VALUE), "0xA0218"); + + checkDoubleLiteral("2000.0", new BigDecimal("-2000"), new BigDecimal("2000")); + checkErrorDoubleLiteral("2001.0", new BigDecimal("-2000"), new BigDecimal("2000"), "0xA0218"); + } + + @Test + public void testFloat() throws IOException{ + checkFloatLiteral("3.4028234663852886E+38f", BigDecimal.valueOf(-Float.MAX_VALUE), BigDecimal.valueOf(Float.MAX_VALUE)); + checkFloatLiteral("1.0f", BigDecimal.valueOf(-Float.MAX_VALUE), BigDecimal.valueOf(Float.MAX_VALUE)); + checkFloatLiteral("123.0f", BigDecimal.valueOf(-Float.MAX_VALUE), BigDecimal.valueOf(Float.MAX_VALUE)); + checkFloatLiteral(String.valueOf(Float.MIN_VALUE) + "f", BigDecimal.valueOf(-Float.MAX_VALUE), BigDecimal.valueOf(Float.MAX_VALUE)); + + checkErrorFloatLiteral("3.4028235e+39f", BigDecimal.valueOf(-Float.MAX_VALUE), BigDecimal.valueOf(Float.MAX_VALUE), "0xA0219"); + + checkFloatLiteral("2000.0f", new BigDecimal("-2000"), new BigDecimal("2000")); + checkErrorFloatLiteral("2001.0f", new BigDecimal("-2000"), new BigDecimal("2000"), "0xA0219"); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/mcliterals/CharLiteralsTest.java b/monticore-grammar/src/test/java/de/monticore/mcliterals/CharLiteralsTest.java new file mode 100644 index 0000000000..beea116cac --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/mcliterals/CharLiteralsTest.java @@ -0,0 +1,63 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.mcliterals; + +import de.monticore.literals.mccommonliterals.MCCommonLiteralsMill; +import de.monticore.literals.mccommonliterals._ast.ASTCharLiteral; +import de.monticore.literals.mcliteralsbasis._ast.ASTLiteral; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; + +import static org.junit.Assert.*; + +public class CharLiteralsTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + MCCommonLiteralsMill.reset(); + MCCommonLiteralsMill.init(); + } + + private void checkCharLiteral(char c, String s) throws IOException { + ASTLiteral lit = MCLiteralsTestHelper.getInstance().parseLiteral(s); + assertTrue(lit instanceof ASTCharLiteral); + assertEquals(c, ((ASTCharLiteral) lit).getValue()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCharLiterals() { + try { + checkCharLiteral('a', "'a'"); + checkCharLiteral(' ', "' '"); + checkCharLiteral('@', "'@'"); + // checkCharLiteral('§', "'§'"); + + // Escape Sequences: + checkCharLiteral('\b', "'\\b'"); + checkCharLiteral('\t', "'\\t'"); + checkCharLiteral('\n', "'\\n'"); + checkCharLiteral('\f', "'\\f'"); + checkCharLiteral('\r', "'\\r'"); + checkCharLiteral('\"', "'\\\"'"); + checkCharLiteral('\'', "'\\\''"); + checkCharLiteral('\\', "'\\\\'"); + + // Unicode: + checkCharLiteral('\u00ef', "'\\u00ef'"); + checkCharLiteral('\u0000', "'\\u0000'"); + checkCharLiteral('\uffff', "'\\uffff'"); + } + catch (IOException e) { + fail(e.getMessage()); + } + + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/mcliterals/MCLiteralsTestHelper.java b/monticore-grammar/src/test/java/de/monticore/mcliterals/MCLiteralsTestHelper.java new file mode 100644 index 0000000000..4bfc3f62de --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/mcliterals/MCLiteralsTestHelper.java @@ -0,0 +1,72 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.mcliterals; + +import de.monticore.literals.mccommonliterals._ast.ASTSignedLiteral; +import de.monticore.literals.mcliteralsbasis._ast.ASTLiteral; +import de.monticore.literals.testmccommonliterals._parser.TestMCCommonLiteralsParser; +import junit.framework.TestCase; + +import java.io.IOException; +import java.io.StringReader; +import java.util.Optional; + +/** + * This class provides two methods that allow testing type grammar. The test + * parses a given input string to an AST. The AST is printed via prettyprint and + * parsed again. The resulting ASTs are compared. The TypeTestHelper is a + * singleton. + * + */ +public class MCLiteralsTestHelper { + + private static MCLiteralsTestHelper instance; + + /** + * We have a singleton. + */ + private MCLiteralsTestHelper() { + } + + /** + * Returns the singleton instance. + * + * @return The instance. + */ + public static MCLiteralsTestHelper getInstance() { + if (instance == null) { + instance = new MCLiteralsTestHelper(); + } + return instance; + } + + /** + * This method parses a literal from a given string. + * + * @param input Literal as a string. + * @return The ASTLiteral or null. + * @throws IOException + */ + public ASTLiteral parseLiteral(String input) throws IOException { + TestMCCommonLiteralsParser parser = new TestMCCommonLiteralsParser(); + Optional res = parser.parseLiteral(new StringReader(input)); + TestCase.assertTrue(res.isPresent()); + return res.get(); + } + + /** + * This method parses a literal from a given string. + * + * @param input Literal as a string. + * @return The ASTLiteral or null. + * @throws IOException + */ + public ASTSignedLiteral parseSignedLiteral(String input) + throws IOException { + TestMCCommonLiteralsParser parser = new TestMCCommonLiteralsParser(); + Optional res = parser.parseSignedLiteral(new StringReader(input)); + TestCase.assertTrue(res.isPresent()); + return res.get(); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/mcliterals/MCLiteralsUnitTest.java b/monticore-grammar/src/test/java/de/monticore/mcliterals/MCLiteralsUnitTest.java new file mode 100644 index 0000000000..391b3a95f4 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/mcliterals/MCLiteralsUnitTest.java @@ -0,0 +1,279 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.mcliterals; + +import de.monticore.testmcliteralsv2.TestMCLiteralsV2Mill; +import de.monticore.testmcliteralsv2._ast.*; +import de.monticore.testmcliteralsv2._parser.TestMCLiteralsV2Parser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import mcnumbers._ast.ASTDecimal; +import mcnumbers._ast.ASTInteger; +import mcnumbers._ast.ASTNumber; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import stringliterals._ast.ASTCharLiteral; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +// import de.monticore.mcliteralsv2._ast.*; + +public class MCLiteralsUnitTest { + + // setup the language infrastructure + TestMCLiteralsV2Parser parser = new TestMCLiteralsV2Parser() ; + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestMCLiteralsV2Mill.reset(); + TestMCLiteralsV2Mill.init(); + } + + // -------------------------------------------------------------------- + // Numbers: Nat for Cardinality + // -------------------------------------------------------------------- + + // -------------------------------------------------------------------- + @Test + public void testCardinalityToken() throws IOException { + ASTAnyTokenList ast = parser.parse_StringAnyTokenList( ":[65..67]:" ).get(); + assertEquals(5, ast.sizeAnyTokens()); + ASTAnyToken t = ast.getAnyToken(0); + t = ast.getAnyToken(1); + assertTrue(t.isPresentDecimalToken()); + assertEquals("65", t.getDecimalToken()); + t = ast.getAnyToken(2); + t = ast.getAnyToken(3); + assertTrue(t.isPresentDecimalToken()); + assertEquals("67", t.getDecimalToken()); + t = ast.getAnyToken(4); + + assertTrue(Log.getFindings().isEmpty()); + } + + // -------------------------------------------------------------------- + // Numbers: Nat + // -------------------------------------------------------------------- + + // -------------------------------------------------------------------- + @Test + public void testNat1() throws IOException { + ASTDecimal ast = parser.parse_StringDecimal( " 9" ).get(); + assertEquals("9", ast.getSource()); + assertEquals(9, ast.getValue()); + assertEquals(9, ast.getValueInt()); + + assertTrue(Log.getFindings().isEmpty()); + } + @Test + public void testNat2() throws IOException { + ASTDecimal ast = parser.parse_StringDecimal( " 0" ).get(); + assertEquals("0", ast.getSource()); + assertEquals(0, ast.getValue()); + + assertTrue(Log.getFindings().isEmpty()); + } + @Test + public void testNat3() throws IOException { + Optional os = parser.parse_StringDecimal( " 00 0 " ); + assertEquals(false, os.isPresent()); + } + @Test + public void testNat4() throws IOException { + ASTDecimal ast = parser.parse_StringDecimal( " 23 " ).get(); + assertEquals("23", ast.getSource()); + assertEquals(23, ast.getValue()); + assertEquals(23, ast.getValueInt()); + + assertTrue(Log.getFindings().isEmpty()); + } + @Test + public void testNat5() throws IOException { + ASTDecimal ast = parser.parse_StringDecimal( " 463 " ).get(); + assertEquals(463, ast.getValue()); + + assertTrue(Log.getFindings().isEmpty()); + } + + // -------------------------------------------------------------------- + @Test + public void testNat6() throws IOException { + Optional os = parser.parse_StringDecimal( " 0x23 " ); + assertEquals(false, os.isPresent()); + } + + // -------------------------------------------------------------------- + @Test + public void testTokens() throws IOException { + ASTAnyTokenList ast = parser.parse_StringAnyTokenList( ":463 23:" ).get(); + assertEquals(2, ast.sizeAnyTokens()); + ASTAnyToken a0 = ast.getAnyToken(0); + assertTrue(a0.isPresentDecimalToken()); + assertEquals("463", a0.getDecimalToken()); + ASTAnyToken a1 = ast.getAnyToken(1); + assertTrue(a1.isPresentDecimalToken()); + assertEquals("23", a1.getDecimalToken()); + + assertTrue(Log.getFindings().isEmpty()); + } + + // -------------------------------------------------------------------- + @Test + public void testTokens2() throws IOException { + ASTAnyTokenList ast = parser.parse_StringAnyTokenList( + ":9 'a' 45 00 47:" ).get(); + assertEquals(6, ast.sizeAnyTokens()); + assertEquals("9", ast.getAnyToken(0).getDecimalToken()); + assertEquals("a", ast.getAnyToken(1).getCharToken()); + assertEquals("45", ast.getAnyToken(2).getDecimalToken()); + // Observe the separated '0's! + assertEquals("0", ast.getAnyToken(3).getDecimalToken()); + assertEquals("0", ast.getAnyToken(4).getDecimalToken()); + assertEquals("47", ast.getAnyToken(5).getDecimalToken()); + + assertTrue(Log.getFindings().isEmpty()); + } + + // -------------------------------------------------------------------- + @Test + public void testAbstractInterfaceFunctions() throws IOException { + ASTNumber ast = parser.parse_StringDecimal( " 234 " ).get(); + assertEquals(234, ast.getValue()); + assertEquals(234, ast.getValueInt()); + assertEquals("234", ast.getSource()); + + assertTrue(Log.getFindings().isEmpty()); + } + + // -------------------------------------------------------------------- + // Numbers: Integer + // -------------------------------------------------------------------- + + // -------------------------------------------------------------------- + @Test + public void testInt() throws IOException { + ASTInteger ast = parser.parse_StringInteger( " -463 " ).get(); + assertEquals(-463, ast.getValue()); + assertEquals(-463, ast.getValueInt()); + assertEquals("-463", ast.getSource()); + + assertTrue(Log.getFindings().isEmpty()); + } + + // -------------------------------------------------------------------- + @Test + public void testIntTokens2() throws IOException { + ASTIntegerList ast = parser.parse_StringIntegerList( + "[9, -45, -0, - 47]" ).get(); + assertEquals(4, ast.sizeIntegers()); + assertEquals(9, ast.getInteger(0).getValue()); + assertEquals("9", ast.getInteger(0).getSource()); + assertEquals(-45, ast.getInteger(1).getValue()); + assertEquals("-45", ast.getInteger(1).getSource()); + assertEquals(0, ast.getInteger(2).getValue()); + // "-" is still present + assertEquals("-0", ast.getInteger(2).getSource()); + assertEquals(-47, ast.getInteger(3).getValue()); + // space between the two token is missing + assertEquals("-47", ast.getInteger(3).getSource()); + + assertTrue(Log.getFindings().isEmpty()); + } + + // -------------------------------------------------------------------- + @Test + public void testIntNEG() throws IOException { + Optional os = parser.parse_StringInteger( " 0x34 " ); + assertEquals(false, os.isPresent()); + } + + // -------------------------------------------------------------------- + // test of the Test-Literal B + // -------------------------------------------------------------------- + + // -------------------------------------------------------------------- + @Test + public void testB() throws IOException { + ASTBTest ast = parser.parse_StringBTest( " X2X, XFF001DX" ).get(); + assertEquals("X2X", ast.getXHexDigit(0)); + assertEquals("XFF001DX", ast.getXHexDigit(1)); + + assertTrue(Log.getFindings().isEmpty()); + } + + + // -------------------------------------------------------------------- + // String + // -------------------------------------------------------------------- + + // -------------------------------------------------------------------- + @Test + public void testString() throws IOException { + ASTStringList ast = parser.parse_StringStringList( + "[\"ZWeR\",\"4\", \"',\\b,\\\\;\", \"S\\u34F4W\", \"o\"]" ).get(); + assertEquals("ZWeR", ast.getStringLiteral(0).getValue()); + assertEquals("4", ast.getStringLiteral(1).getValue()); + assertEquals("',\b,\\;", ast.getStringLiteral(2).getValue()); + assertEquals("S\u34F4W", ast.getStringLiteral(3).getValue()); + assertEquals("o", ast.getStringLiteral(4).getValue()); + + // repeat wg. buffering + assertEquals("ZWeR", ast.getStringLiteral(0).getValue()); + + assertTrue(Log.getFindings().isEmpty()); + } + + // -------------------------------------------------------------------- + // Char + // -------------------------------------------------------------------- + + // -------------------------------------------------------------------- + @Test + public void testChar() throws IOException { + ASTCharLiteral ast = parser.parse_StringCharLiteral( " 'h'" ).get(); + assertEquals("h", ast.getSource()); + assertEquals('h', ast.getValue()); + + assertTrue(Log.getFindings().isEmpty()); + } + + // -------------------------------------------------------------------- + @Test + public void testChar2() throws IOException { + ASTCharList ast = parser.parse_StringCharList( + "['Z','4','\\'', '\\b', '\\\\', '\7', '\\7', 'o']" ).get(); + assertEquals('Z', ast.getCharLiteral(0).getValue()); + assertEquals('4', ast.getCharLiteral(1).getValue()); + assertEquals('\'', ast.getCharLiteral(2).getValue()); + assertEquals('\b', ast.getCharLiteral(3).getValue()); + assertEquals('\\', ast.getCharLiteral(4).getValue()); + // Encoded by Java + assertEquals('\7', ast.getCharLiteral(5).getValue()); + assertEquals('o', ast.getCharLiteral(7).getValue()); + + assertTrue(Log.getFindings().isEmpty()); + } + + // -------------------------------------------------------------------- + // -------------------------------------------------------------------- + @Test + public void testCharUnicode() throws IOException { + ASTCharList ast = parser.parse_StringCharList( + "['\\u2345', '\\u23EF', '\\u0001', '\\uAFFA']" ).get(); + assertEquals('\u2345', ast.getCharLiteral(0).getValue()); + assertEquals('\u23EF', ast.getCharLiteral(1).getValue()); + assertEquals('\u0001', ast.getCharLiteral(2).getValue()); + assertEquals('\uAFFA', ast.getCharLiteral(3).getValue()); + + assertTrue(Log.getFindings().isEmpty()); + } + +} + diff --git a/monticore-grammar/src/test/java/de/monticore/mcliterals/NatLiteralsTest.java b/monticore-grammar/src/test/java/de/monticore/mcliterals/NatLiteralsTest.java new file mode 100644 index 0000000000..4233cc2582 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/mcliterals/NatLiteralsTest.java @@ -0,0 +1,68 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.mcliterals; + +import de.monticore.literals.mccommonliterals._ast.ASTNatLiteral; +import de.monticore.literals.testmccommonliterals.TestMCCommonLiteralsMill; +import de.monticore.literals.testmccommonliterals._parser.TestMCCommonLiteralsParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.*; + +public class NatLiteralsTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestMCCommonLiteralsMill.reset(); + TestMCCommonLiteralsMill.init(); + } + + private void checkNatLiteral(int i, String s) throws IOException { + TestMCCommonLiteralsParser parser = new TestMCCommonLiteralsParser(); + Optional ast = parser.parse_StringNatLiteral(s); + assertTrue(!parser.hasErrors()); + assertEquals(i, ast.get().getValue()); + + assertTrue(Log.getFindings().isEmpty()); + } + + private void checkFailingNatLiteral(String s) throws IOException { + TestMCCommonLiteralsParser parser = new TestMCCommonLiteralsParser(); + parser.parse_StringNatLiteral(s); + assertTrue(parser.hasErrors()); + } + + @Test + public void testNatLiterals() { + try { + // decimal number + checkNatLiteral(0, "0"); + checkNatLiteral(123, "123"); + checkNatLiteral(10, "10"); + checkNatLiteral(5, "5"); + } + catch (IOException e) { + fail(e.getMessage()); + } + } + + @Test + public void testFailingNatLiterals() throws IOException { + try { + checkFailingNatLiteral("0x5"); + checkFailingNatLiteral("-5"); + } + catch (IOException e) { + fail(e.getMessage()); + } + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/mcliterals/NullAndBooleanLiteralsTest.java b/monticore-grammar/src/test/java/de/monticore/mcliterals/NullAndBooleanLiteralsTest.java new file mode 100644 index 0000000000..453178c460 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/mcliterals/NullAndBooleanLiteralsTest.java @@ -0,0 +1,61 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.mcliterals; + +import de.monticore.literals.mccommonliterals._ast.ASTBooleanLiteral; +import de.monticore.literals.mccommonliterals._ast.ASTNullLiteral; +import de.monticore.literals.mcliteralsbasis._ast.ASTLiteral; +import de.monticore.literals.testmccommonliterals.TestMCCommonLiteralsMill; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; + +import static org.junit.Assert.*; + +public class NullAndBooleanLiteralsTest { + + @Before + public void initLog() { + LogStub.init(); + Log.enableFailQuick(false); + TestMCCommonLiteralsMill.reset(); + TestMCCommonLiteralsMill.init(); + } + + @Test + public void testNullLiteral() { + try { + ASTLiteral lit = MCLiteralsTestHelper.getInstance().parseLiteral("null"); + assertTrue(lit instanceof ASTNullLiteral); + } + catch (Exception e) { + fail(e.getMessage()); + } + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testBooleanLiterals() { + try { + // literal "true": + ASTLiteral lit = MCLiteralsTestHelper.getInstance().parseLiteral("true"); + assertTrue(lit instanceof ASTBooleanLiteral); + assertTrue(((ASTBooleanLiteral) lit).getValue()); + + // literal "false": + lit = MCLiteralsTestHelper.getInstance().parseLiteral("false"); + assertTrue(lit instanceof ASTBooleanLiteral); + assertFalse(((ASTBooleanLiteral) lit).getValue()); + } + catch (IOException e) { + fail(e.getMessage()); + } + + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/mcliterals/SignedNatLiteralsTest.java b/monticore-grammar/src/test/java/de/monticore/mcliterals/SignedNatLiteralsTest.java new file mode 100644 index 0000000000..99270931bd --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/mcliterals/SignedNatLiteralsTest.java @@ -0,0 +1,77 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.mcliterals; + +import de.monticore.literals.mccommonliterals._ast.ASTNatLiteral; +import de.monticore.literals.mccommonliterals._ast.ASTSignedNatLiteral; +import de.monticore.literals.testmccommonliterals.TestMCCommonLiteralsMill; +import de.monticore.literals.testmccommonliterals._parser.TestMCCommonLiteralsParser; +import de.monticore.testmcliteralsv2.TestMCLiteralsV2Mill; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.*; + +public class SignedNatLiteralsTest { + + @Before + public void initLog() { + LogStub.init(); + Log.enableFailQuick(false); + TestMCCommonLiteralsMill.reset(); + TestMCCommonLiteralsMill.init(); + } + + private void checkNatLiteral(int i, String s) throws IOException { + TestMCCommonLiteralsParser parser = new TestMCCommonLiteralsParser(); + Optional ast = parser.parse_StringSignedNatLiteral(s); + assertTrue(!parser.hasErrors()); + assertEquals(i, ast.get().getValue()); + + assertTrue(Log.getFindings().isEmpty()); + } + + private void checkFailingNatLiteral(String s) throws IOException { + TestMCCommonLiteralsParser parser = new TestMCCommonLiteralsParser(); + parser.parse_StringSignedNatLiteral(s); + assertTrue(parser.hasErrors()); + } + + @Test + public void testNatLiterals() { + try { + // decimal number + checkNatLiteral(-0, "-0"); + checkNatLiteral(-123, "-123"); + checkNatLiteral(-10, "-10"); + checkNatLiteral(-5, "-5"); + + + checkNatLiteral(0, "0"); + checkNatLiteral(123, "123"); + checkNatLiteral(10, "10"); + checkNatLiteral(5, "5"); + + } + catch (IOException e) { + fail(e.getMessage()); + } + } + + @Test + public void testFailingNatLiterals() throws IOException { + try { + checkFailingNatLiteral("0x5"); +// checkFailingNatLiteral("-5"); + } + catch (IOException e) { + fail(e.getMessage()); + } + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/mcliterals/StringLiteralsTest.java b/monticore-grammar/src/test/java/de/monticore/mcliterals/StringLiteralsTest.java new file mode 100644 index 0000000000..7d1faedf20 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/mcliterals/StringLiteralsTest.java @@ -0,0 +1,65 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.mcliterals; + +import de.monticore.literals.mccommonliterals._ast.ASTStringLiteral; +import de.monticore.literals.mcliteralsbasis._ast.ASTLiteral; +import de.monticore.literals.testmccommonliterals.TestMCCommonLiteralsMill; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; + +import static org.junit.Assert.*; + +public class StringLiteralsTest { + + @Before + public void initLog() { + LogStub.init(); + Log.enableFailQuick(false); + TestMCCommonLiteralsMill.reset(); + TestMCCommonLiteralsMill.init(); + } + + private void checkStringLiteral(String expected, String actual) throws IOException { + ASTLiteral lit = MCLiteralsTestHelper.getInstance().parseLiteral(actual); + assertTrue(lit instanceof ASTStringLiteral); + assertEquals(expected, ((ASTStringLiteral) lit).getValue()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testStringLiterals() { + try { + checkStringLiteral("abc ABC", "\"abc ABC\""); + checkStringLiteral("a", "\"a\""); + checkStringLiteral(" ", "\" \""); + checkStringLiteral(" a ", "\" a \""); + checkStringLiteral("\n", "\"\\n\""); + checkStringLiteral("\r", "\"\\r\""); + checkStringLiteral("", "\"\""); + checkStringLiteral("\\", "\"\\\\\""); + checkStringLiteral("\"", "\"\\\"\""); + checkStringLiteral("!\"§\\%&{([)]=}?´`*+~'#-_.:,;<>|^°@€", + "\"!\\\"§\\\\%&{([)]=}?´`*+~'#-_.:,;<>|^°@€\""); + + // Escape Sequences: + checkStringLiteral("\b\t\n\f\r\"\'\\", "\"\\b\\t\\n\\f\\r\\\"\\'\\\\\""); + + // Unicode: + checkStringLiteral("\u00ef", "\"\\u00ef\""); + checkStringLiteral("\u0000", "\"\\u0000\""); + checkStringLiteral("\uffff", "\"\\uffff\""); + checkStringLiteral("\u00aaf\u00dd1 123", "\"\\u00aaf\\u00dd1 123\""); + checkStringLiteral("\u010000", "\"\\u010000\""); + } + catch (IOException e) { + fail(e.getMessage()); + } + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/prettyprint/CardinalityPrettyPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/prettyprint/CardinalityPrettyPrinterTest.java new file mode 100644 index 0000000000..6d64411294 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/prettyprint/CardinalityPrettyPrinterTest.java @@ -0,0 +1,90 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.prettyprint; + +import de.monticore.cardinality._ast.ASTCardinality; +import de.monticore.testcardinality.TestCardinalityMill; +import de.monticore.testcardinality._parser.TestCardinalityParser; +import de.monticore.cardinality._prettyprint.CardinalityFullPrettyPrinter; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.io.StringReader; +import java.util.Optional; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class CardinalityPrettyPrinterTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestCardinalityMill.reset(); + TestCardinalityMill.init(); + } + + @Test + public void testCardinality1() throws IOException { + TestCardinalityParser parser = new TestCardinalityParser(); + Optional result = parser.parseCardinality(new StringReader("[*]")); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTCardinality cardinality = result.get(); + + CardinalityFullPrettyPrinter prettyPrinter = new CardinalityFullPrettyPrinter(new IndentPrinter()); + String output = prettyPrinter.prettyprint(cardinality); + + result = parser.parseCardinality(new StringReader(output)); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(cardinality.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCardinality2() throws IOException { + TestCardinalityParser parser = new TestCardinalityParser(); + Optional result = parser.parseCardinality(new StringReader("[5..9]")); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTCardinality cardinality = result.get(); + + CardinalityFullPrettyPrinter prettyPrinter = new CardinalityFullPrettyPrinter(new IndentPrinter()); + String output = prettyPrinter.prettyprint(cardinality); + + result = parser.parseCardinality(new StringReader(output)); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(cardinality.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCardinality3() throws IOException { + TestCardinalityParser parser = new TestCardinalityParser(); + Optional result = parser.parseCardinality(new StringReader("[6..*]")); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTCardinality cardinality = result.get(); + + CardinalityFullPrettyPrinter prettyPrinter = new CardinalityFullPrettyPrinter(new IndentPrinter()); + String output = prettyPrinter.prettyprint(cardinality); + + result = parser.parseCardinality(new StringReader(output)); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(cardinality.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/prettyprint/CompletenessPrettyPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/prettyprint/CompletenessPrettyPrinterTest.java new file mode 100644 index 0000000000..69b05eae05 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/prettyprint/CompletenessPrettyPrinterTest.java @@ -0,0 +1,110 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.prettyprint; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.io.StringReader; +import java.util.Optional; + +import de.monticore.testcompleteness.TestCompletenessMill; +import org.junit.Before; +import org.junit.Test; +import de.monticore.completeness._ast.ASTCompleteness; +import de.monticore.completeness._prettyprint.CompletenessFullPrettyPrinter; +import de.monticore.testcompleteness._parser.TestCompletenessParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; + +public class CompletenessPrettyPrinterTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestCompletenessMill.reset(); + TestCompletenessMill.init(); + } + + @Test + public void testCompleteness() throws IOException { + TestCompletenessParser parser = new TestCompletenessParser(); + Optional result = parser.parseCompleteness(new StringReader("(c)")); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTCompleteness completeness = result.get(); + + CompletenessFullPrettyPrinter prettyPrinter = new CompletenessFullPrettyPrinter(new IndentPrinter()); + String output = prettyPrinter.prettyprint(completeness); + + result = parser.parseCompleteness(new StringReader(output)); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(completeness.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testIncompleteness() throws IOException { + TestCompletenessParser parser = new TestCompletenessParser(); + Optional result = parser.parseCompleteness(new StringReader("(...)")); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTCompleteness completeness = result.get(); + + CompletenessFullPrettyPrinter prettyPrinter = new CompletenessFullPrettyPrinter(new IndentPrinter()); + String output = prettyPrinter.prettyprint(completeness); + + result = parser.parseCompleteness(new StringReader(output)); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(completeness.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testRightCompleteness() throws IOException { + TestCompletenessParser parser = new TestCompletenessParser(); + Optional result = parser.parseCompleteness(new StringReader("(...,c)")); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTCompleteness completeness = result.get(); + + CompletenessFullPrettyPrinter prettyPrinter = new CompletenessFullPrettyPrinter(new IndentPrinter()); + String output = prettyPrinter.prettyprint(completeness); + + result = parser.parseCompleteness(new StringReader(output)); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(completeness.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testLeftCompleteness() throws IOException { + TestCompletenessParser parser = new TestCompletenessParser(); + Optional result = parser.parseCompleteness(new StringReader("(c,...)")); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTCompleteness completeness = result.get(); + + CompletenessFullPrettyPrinter prettyPrinter = new CompletenessFullPrettyPrinter(new IndentPrinter()); + String output = prettyPrinter.prettyprint(completeness); + + result = parser.parseCompleteness(new StringReader(output)); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(completeness.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/prettyprint/GrammarPrettyPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/prettyprint/GrammarPrettyPrinterTest.java new file mode 100644 index 0000000000..51c994dc3e --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/prettyprint/GrammarPrettyPrinterTest.java @@ -0,0 +1,82 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.prettyprint; + +import de.monticore.grammar.grammar._ast.ASTMCGrammar; +import de.monticore.grammar.grammar_withconcepts.Grammar_WithConceptsMill; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Optional; +import java.util.stream.Stream; + +/** + * This test checks if all (hand-written) MontiCore grammars are print-able using the generated pretty printers + */ +public class GrammarPrettyPrinterTest { + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + Log.clearFindings(); + Grammar_WithConceptsMill.reset(); + Grammar_WithConceptsMill.init(); + } + + @Test + public void testPrintMainGrammars() throws IOException { + testPrintInPath("src/main/grammars"); + } + + + @Test + public void testPrintTestGrammars() throws IOException { + testPrintInPath("src/test/grammars"); + } + + public void testPrintInPath(String grammarDir) throws IOException { + File grammarFile = new File(grammarDir); + if (!grammarFile.exists()) return; + try ( + Stream stream = Files.walk(grammarFile.toPath())) { + stream.filter(Files::isRegularFile).filter(f -> f.getFileName().toString().endsWith(".mc4")).forEach(path -> { + try { + this.testPrintGrammar(path); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } + } + + public void testPrintGrammar(Path path) throws IOException { + System.err.println(path.toAbsolutePath()); + Optional astOpt = Grammar_WithConceptsMill.parser().parse(path.toString()); + + Assert.assertTrue(astOpt.isPresent()); + + String pretty = Grammar_WithConceptsMill.prettyPrint(astOpt.get(), true); + Assert.assertEquals("Failed to pretty print without findings: " + path, 0, Log.getFindingsCount()); + Optional parsedAST = Grammar_WithConceptsMill.parser().parse_String(pretty); + if (parsedAST.isEmpty()) { + Assert.assertEquals("Failed to parse " + path, Files.readString(path), pretty); + Assert.fail("Failed to parse " + path); + } + if (!Log.getFindings().isEmpty()) { + Assert.assertEquals("Failed to parse " + path + " without findings", Files.readString(path), pretty); + Assert.fail("Failed to parse " + path + " without findings"); + } + + if (!astOpt.get().deepEquals(parsedAST.get())) { + Assert.assertEquals("Failed to deep-equals " + path, Files.readString(path), pretty); + Assert.fail("Failed to deep-equals"); + } + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/prettyprint/JavaLightPrettyPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/prettyprint/JavaLightPrettyPrinterTest.java new file mode 100644 index 0000000000..282136ef90 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/prettyprint/JavaLightPrettyPrinterTest.java @@ -0,0 +1,306 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.prettyprint; + +import de.monticore.javalight._ast.*; +import de.monticore.testjavalight.TestJavaLightMill; +import de.monticore.testjavalight._parser.TestJavaLightParser; +import de.monticore.javalight._prettyprint.JavaLightFullPrettyPrinter; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class JavaLightPrettyPrinterTest { + + private TestJavaLightParser parser = new TestJavaLightParser(); + + private JavaLightFullPrettyPrinter prettyPrinter = new JavaLightFullPrettyPrinter(new IndentPrinter()); + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestJavaLightMill.reset(); + TestJavaLightMill.init(); + prettyPrinter.getPrinter().clearBuffer(); + } + + @Test + public void testMethodDeclaration() throws IOException { + Optional result = parser.parse_StringMethodDeclaration("private static final int foo(String s[], boolean b)[][][] throws e.Exception { private Integer foo = a; }"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTMethodDeclaration ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringMethodDeclaration(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testAbstractInterfaceMethod() throws IOException { + Optional result = parser.parse_StringMethodDeclaration("public void foo(String s, boolean b);"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTMethodDeclaration ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringMethodDeclaration(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + + assertTrue("Parse pp output: " + output, ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testDefaultMethod() throws IOException { + Optional result = parser.parse_StringMethodDeclaration("default int foo(String s, boolean b)[][] throws e.Exception { return expr; }"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTMethodDeclaration ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringMethodDeclaration(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testConstructorDeclaration() throws IOException { + Optional result = parser.parse_StringConstructorDeclaration("public ClassName(String s, boolean b) throws e.Exception { private Integer foo = a;}"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTConstructorDeclaration ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringConstructorDeclaration(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testConstDeclaration() throws IOException { + Optional result = parser.parse_StringConstDeclaration("private static Foo foo [][][] = a;"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTConstDeclaration ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringConstDeclaration(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testThrows() throws IOException { + Optional result = parser.parse_StringThrows("a.b.c.D, person.A "); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTThrows ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringThrows(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testFormalParameters() throws IOException { + Optional result = parser.parse_StringFormalParameters("(public float f, int i, private ASTNode n, Float ... a)"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTFormalParameters ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringFormalParameters(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testFormalParameterListing() throws IOException { + Optional result = parser.parse_StringFormalParameterListing("public float f, int i, private ASTNode n, Float ... a"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTFormalParameterListing ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringFormalParameterListing(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testLastFormalParameter() throws IOException { + Optional result = parser.parse_StringLastFormalParameter("private String ... a"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTLastFormalParameter ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringLastFormalParameter(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testAnnotation() throws IOException { + Optional result = parser.parse_StringAnnotation("@java.util.List (a = ++a, b = {c, d})"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTAnnotation ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringAnnotation(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testAnnotationPairArguments() throws IOException { + Optional result = parser.parse_StringAnnotationPairArguments("a = ++a, b = {c, d}"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTAnnotationPairArguments ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringAnnotationPairArguments(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void testElementValueOrExpr() throws IOException { + Optional result = parser.parse_StringElementValueOrExpr("++a"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTElementValueOrExpr ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringElementValueOrExpr(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testElementValueArrayInitializer() throws IOException { + Optional result = parser.parse_StringElementValueArrayInitializer("{c, d}"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTElementValueArrayInitializer ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringElementValueArrayInitializer(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testElementValuePair() throws IOException { + Optional result = parser.parse_StringElementValuePair("a = ++a"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTElementValuePair ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringElementValuePair(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCreatorExpression2() throws IOException { + Optional result = parser.parse_StringArrayDimensionByInitializer("[][]{{}}"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTArrayDimensionByInitializer ast = result.get(); + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringArrayDimensionByInitializer(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/prettyprint/MCCommonLiteralsPrettyPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/prettyprint/MCCommonLiteralsPrettyPrinterTest.java new file mode 100644 index 0000000000..46ea303d88 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/prettyprint/MCCommonLiteralsPrettyPrinterTest.java @@ -0,0 +1,252 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.prettyprint; + +import de.monticore.literals.mccommonliterals._prettyprint.MCCommonLiteralsFullPrettyPrinter; +import de.monticore.literals.mccommonliterals._ast.*; +import de.monticore.literals.testmccommonliterals.TestMCCommonLiteralsMill; +import de.monticore.literals.testmccommonliterals._parser.TestMCCommonLiteralsParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class MCCommonLiteralsPrettyPrinterTest { + + private TestMCCommonLiteralsParser parser = new TestMCCommonLiteralsParser(); + + private MCCommonLiteralsFullPrettyPrinter prettyPrinter = new MCCommonLiteralsFullPrettyPrinter(new IndentPrinter()); + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestMCCommonLiteralsMill.reset(); + TestMCCommonLiteralsMill.init(); + prettyPrinter.getPrinter().clearBuffer(); + } + + @Test + public void testIntLiteral() throws IOException { + Optional result = parser.parse_StringNullLiteral("null"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTNullLiteral ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringNullLiteral(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void testFalseBooleanLiteral() throws IOException { + Optional result = parser.parse_StringBooleanLiteral("true"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTBooleanLiteral ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringBooleanLiteral(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void testTrueBooleanLiteral() throws IOException { + Optional result = parser.parse_StringBooleanLiteral("false"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTBooleanLiteral ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringBooleanLiteral(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCharLiteral() throws IOException { + Optional result = parser.parse_StringCharLiteral("'c'"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTCharLiteral ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringCharLiteral(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testStringLiteral() throws IOException { + Optional result = parser.parse_StringStringLiteral("\"something\""); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTStringLiteral ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringStringLiteral(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testSignedNatLiteral() throws IOException { + Optional result = parser.parse_StringSignedNatLiteral("-123456789"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTSignedNatLiteral ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringSignedNatLiteral(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testBasicLongLiteral() throws IOException { + Optional result = parser.parse_StringBasicLongLiteral("13745934L"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTBasicLongLiteral ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringBasicLongLiteral(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testSignedBasicLongLiteral() throws IOException { + Optional result = parser.parse_StringSignedBasicLongLiteral("-13745934L"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTSignedBasicLongLiteral ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringSignedBasicLongLiteral(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testBasicFloatLiteral() throws IOException { + Optional result = parser.parse_StringBasicFloatLiteral("13745934.73649F"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTBasicFloatLiteral ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringBasicFloatLiteral(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testSignedBasicFloatLiteral() throws IOException { + Optional result = parser.parse_StringSignedBasicFloatLiteral("-13745934.38462F"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTSignedBasicFloatLiteral ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringSignedBasicFloatLiteral(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testBasicDoubleLiteral() throws IOException { + Optional result = parser.parse_StringBasicDoubleLiteral("13745934.73649"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTBasicDoubleLiteral ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringBasicDoubleLiteral(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testSignedBasicDoubleLiteral() throws IOException { + Optional result = parser.parse_StringSignedBasicDoubleLiteral("-13745934.38462"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTSignedBasicDoubleLiteral ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringSignedBasicDoubleLiteral(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/prettyprint/MCHexNumbersPrettyPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/prettyprint/MCHexNumbersPrettyPrinterTest.java new file mode 100644 index 0000000000..3f02ccafa6 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/prettyprint/MCHexNumbersPrettyPrinterTest.java @@ -0,0 +1,93 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.prettyprint; + +import de.monticore.testmchexnumbers.TestMCHexNumbersMill; +import de.monticore.testmchexnumbers._parser.TestMCHexNumbersParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import mchexnumbers._ast.ASTHexInteger; +import mchexnumbers._ast.ASTHexadecimal; +import mchexnumbers._prettyprint.MCHexNumbersFullPrettyPrinter; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.io.StringReader; +import java.util.Optional; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class MCHexNumbersPrettyPrinterTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestMCHexNumbersMill.reset(); + TestMCHexNumbersMill.init(); + } + + @Test + public void testHexadecimal() throws IOException { + TestMCHexNumbersParser parser = new TestMCHexNumbersParser(); + Optional result = parser.parseHexadecimal(new StringReader("0X6b90A")); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTHexadecimal hexadecimal = result.get(); + + MCHexNumbersFullPrettyPrinter prettyPrinter = new MCHexNumbersFullPrettyPrinter(new IndentPrinter()); + String output = prettyPrinter.prettyprint(hexadecimal); + + result = parser.parseHexadecimal(new StringReader(output)); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(hexadecimal.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testHexIntegerPositiv() throws IOException { + TestMCHexNumbersParser parser = new TestMCHexNumbersParser(); + Optional result = parser.parseHexInteger(new StringReader("0X6b90A")); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTHexInteger hexinteger = result.get(); + + MCHexNumbersFullPrettyPrinter prettyPrinter = new MCHexNumbersFullPrettyPrinter(new IndentPrinter()); + String output = prettyPrinter.prettyprint(hexinteger); + + result = parser.parseHexInteger(new StringReader(output)); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(hexinteger.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testHexIntegerNegative() throws IOException { + TestMCHexNumbersParser parser = new TestMCHexNumbersParser(); + Optional result = parser.parseHexInteger(new StringReader("-0xaf67")); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTHexInteger hexinteger = result.get(); + + MCHexNumbersFullPrettyPrinter prettyPrinter = new MCHexNumbersFullPrettyPrinter(new IndentPrinter()); + String output = prettyPrinter.prettyprint(hexinteger); + + result = parser.parseHexInteger(new StringReader(output)); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(hexinteger.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/prettyprint/MCJavaLiteralsPrettyPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/prettyprint/MCJavaLiteralsPrettyPrinterTest.java new file mode 100644 index 0000000000..4886ebc422 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/prettyprint/MCJavaLiteralsPrettyPrinterTest.java @@ -0,0 +1,111 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.prettyprint; + +import de.monticore.literals.mcjavaliterals._prettyprint.MCJavaLiteralsFullPrettyPrinter; +import de.monticore.literals.mcjavaliterals._ast.ASTDoubleLiteral; +import de.monticore.literals.mcjavaliterals._ast.ASTFloatLiteral; +import de.monticore.literals.mcjavaliterals._ast.ASTIntLiteral; +import de.monticore.literals.mcjavaliterals._ast.ASTLongLiteral; +import de.monticore.literals.testmcjavaliterals.TestMCJavaLiteralsMill; +import de.monticore.literals.testmcjavaliterals._parser.TestMCJavaLiteralsParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class MCJavaLiteralsPrettyPrinterTest { + + private TestMCJavaLiteralsParser parser = new TestMCJavaLiteralsParser(); + + private MCJavaLiteralsFullPrettyPrinter prettyPrinter = new MCJavaLiteralsFullPrettyPrinter(new IndentPrinter()); + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestMCJavaLiteralsMill.reset(); + TestMCJavaLiteralsMill.init(); + prettyPrinter.getPrinter().clearBuffer(); + } + @Test + public void testIntLiteral() throws IOException { + Optional result = parser.parse_StringIntLiteral("1110"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTIntLiteral ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringIntLiteral(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void testLongLiteral() throws IOException { + Optional result = parser.parse_StringLongLiteral("109584763l"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTLongLiteral ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringLongLiteral(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void testFloatLiteral() throws IOException { + Optional result = parser.parse_StringFloatLiteral("93475.2434356677f"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTFloatLiteral ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringFloatLiteral(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void testDoubleLiteral() throws IOException { + Optional result = parser.parse_StringDoubleLiteral("1110.45600233"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTDoubleLiteral ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringDoubleLiteral(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/prettyprint/MCNumbersPrettyPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/prettyprint/MCNumbersPrettyPrinterTest.java new file mode 100644 index 0000000000..c29e3d0755 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/prettyprint/MCNumbersPrettyPrinterTest.java @@ -0,0 +1,111 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.prettyprint; + +import de.monticore.testmcnumbers.TestMCNumbersMill; +import de.monticore.testmcnumbers._parser.TestMCNumbersParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import mcnumbers._ast.ASTDecimal; +import mcnumbers._ast.ASTInteger; +import mcnumbers._prettyprint.MCNumbersFullPrettyPrinter; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.io.StringReader; +import java.util.Optional; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class MCNumbersPrettyPrinterTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestMCNumbersMill.reset(); + TestMCNumbersMill.init(); + } + + @Test + public void testDecimalZero() throws IOException { + TestMCNumbersParser parser = new TestMCNumbersParser(); + Optional result = parser.parseDecimal(new StringReader("0")); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTDecimal decimal = result.get(); + + MCNumbersFullPrettyPrinter prettyPrinter = new MCNumbersFullPrettyPrinter(new IndentPrinter()); + String output = prettyPrinter.prettyprint(decimal); + + result = parser.parseDecimal(new StringReader(output)); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(decimal.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testDecimal() throws IOException { + TestMCNumbersParser parser = new TestMCNumbersParser(); + Optional result = parser.parseDecimal(new StringReader("9702")); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTDecimal decimal = result.get(); + + MCNumbersFullPrettyPrinter prettyPrinter = new MCNumbersFullPrettyPrinter(new IndentPrinter()); + String output = prettyPrinter.prettyprint(decimal); + + result = parser.parseDecimal(new StringReader(output)); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(decimal.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testIntegerPositive() throws IOException { + TestMCNumbersParser parser = new TestMCNumbersParser(); + Optional result = parser.parseInteger(new StringReader("780530")); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTInteger integer = result.get(); + + MCNumbersFullPrettyPrinter prettyPrinter = new MCNumbersFullPrettyPrinter(new IndentPrinter()); + String output = prettyPrinter.prettyprint(integer); + + result = parser.parseInteger(new StringReader(output)); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(integer.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testIntegerNegative() throws IOException { + TestMCNumbersParser parser = new TestMCNumbersParser(); + Optional result = parser.parseInteger(new StringReader("-9702")); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTInteger integer = result.get(); + + MCNumbersFullPrettyPrinter prettyPrinter = new MCNumbersFullPrettyPrinter(new IndentPrinter()); + String output = prettyPrinter.prettyprint(integer); + + result = parser.parseInteger(new StringReader(output)); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(integer.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/prettyprint/StringLiteralsPrettyPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/prettyprint/StringLiteralsPrettyPrinterTest.java new file mode 100644 index 0000000000..4049be06cb --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/prettyprint/StringLiteralsPrettyPrinterTest.java @@ -0,0 +1,96 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.prettyprint; + +import de.monticore.teststringliterals.TestStringLiteralsMill; +import de.monticore.teststringliterals._parser.TestStringLiteralsParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import stringliterals._ast.ASTCharLiteral; +import stringliterals._ast.ASTStringLiteral; +import stringliterals._prettyprint.StringLiteralsFullPrettyPrinter; + +import java.io.IOException; +import java.io.StringReader; +import java.util.Optional; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class StringLiteralsPrettyPrinterTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestStringLiteralsMill.reset(); + TestStringLiteralsMill.init(); + } + + @Test + public void testCharLiteralEscapeSequenz() throws IOException { + TestStringLiteralsParser parser = new TestStringLiteralsParser(); + Optional result = parser.parseCharLiteral(new StringReader("'\"'")); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTCharLiteral cliteral = result.get(); + + StringLiteralsFullPrettyPrinter prettyPrinter = new StringLiteralsFullPrettyPrinter( + new IndentPrinter()); + String output = prettyPrinter.prettyprint(cliteral); + + result = parser.parseCharLiteral(new StringReader(output)); + assertFalse(output, parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(cliteral.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCharLiteral() throws IOException { + TestStringLiteralsParser parser = new TestStringLiteralsParser(); + Optional result = parser.parseCharLiteral(new StringReader("'c'")); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTCharLiteral cliteral = result.get(); + + StringLiteralsFullPrettyPrinter prettyPrinter = new StringLiteralsFullPrettyPrinter( + new IndentPrinter()); + String output = prettyPrinter.prettyprint(cliteral); + + result = parser.parseCharLiteral(new StringReader(output)); + assertFalse(output, parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(cliteral.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testStringLiteral() throws IOException { + TestStringLiteralsParser parser = new TestStringLiteralsParser(); + Optional result = parser + .parseStringLiteral(new StringReader("\"Text mit 893\"")); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTStringLiteral sliteral = result.get(); + + StringLiteralsFullPrettyPrinter prettyPrinter = new StringLiteralsFullPrettyPrinter( + new IndentPrinter()); + String output = prettyPrinter.prettyprint(sliteral); + result = parser.parseStringLiteral(new StringReader(output)); + assertFalse(output, parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(sliteral.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/prettyprint/UMLModifierPrettyPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/prettyprint/UMLModifierPrettyPrinterTest.java new file mode 100644 index 0000000000..e843e997db --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/prettyprint/UMLModifierPrettyPrinterTest.java @@ -0,0 +1,73 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.prettyprint; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.io.StringReader; +import java.util.Optional; + +import de.monticore.testumlmodifier.TestUMLModifierMill; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.umlmodifier._prettyprint.UMLModifierFullPrettyPrinter; +import de.monticore.testumlmodifier._parser.TestUMLModifierParser; +import de.monticore.umlmodifier._ast.ASTModifier; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; + +public class UMLModifierPrettyPrinterTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestUMLModifierMill.reset(); + TestUMLModifierMill.init(); + } + + @Test + public void testModifierWord() throws IOException { + TestUMLModifierParser parser = new TestUMLModifierParser(); + Optional result = parser.parseModifier(new StringReader("private")); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTModifier modifier = result.get(); + + UMLModifierFullPrettyPrinter prettyPrinter = new UMLModifierFullPrettyPrinter(new IndentPrinter()); + String output = prettyPrinter.prettyprint(modifier); + + result = parser.parseModifier(new StringReader(output)); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(modifier.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testModifierSymbol() throws IOException { + TestUMLModifierParser parser = new TestUMLModifierParser(); + Optional result = parser.parseModifier(new StringReader("-")); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTModifier modifier = result.get(); + + UMLModifierFullPrettyPrinter prettyPrinter = new UMLModifierFullPrettyPrinter(new IndentPrinter()); + String output = prettyPrinter.prettyprint(modifier); + + result = parser.parseModifier(new StringReader(output)); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(modifier.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/prettyprint/UMLStereotypePrettyPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/prettyprint/UMLStereotypePrettyPrinterTest.java new file mode 100644 index 0000000000..5b914021f5 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/prettyprint/UMLStereotypePrettyPrinterTest.java @@ -0,0 +1,77 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.prettyprint; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import java.io.IOException; +import java.io.StringReader; +import java.util.Optional; + +import de.monticore.testumlstereotype.TestUMLStereotypeMill; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.umlstereotype._prettyprint.UMLStereotypeFullPrettyPrinter; +import de.monticore.testumlstereotype._parser.TestUMLStereotypeParser; +import de.monticore.umlstereotype._ast.ASTStereoValue; +import de.monticore.umlstereotype._ast.ASTStereotype; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; + +public class UMLStereotypePrettyPrinterTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestUMLStereotypeMill.reset(); + TestUMLStereotypeMill.init(); + } + + @Before + public void setUp() { + Log.getFindings().clear(); + } + + @Test + public void testStereotype() throws IOException { + TestUMLStereotypeParser parser = new TestUMLStereotypeParser(); + Optional result = parser.parseStereotype(new StringReader("<>")); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTStereotype stereotype = result.get(); + + UMLStereotypeFullPrettyPrinter prettyPrinter = new UMLStereotypeFullPrettyPrinter(new IndentPrinter()); + String output = prettyPrinter.prettyprint(stereotype); + + result = parser.parseStereotype(new StringReader(output)); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(stereotype.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testStereoValue() throws IOException { + TestUMLStereotypeParser parser = new TestUMLStereotypeParser(); + Optional result = parser.parseStereoValue(new StringReader("s1=\"S1\"")); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTStereoValue stereovalue = result.get(); + + UMLStereotypeFullPrettyPrinter prettyPrinter = new UMLStereotypeFullPrettyPrinter(new IndentPrinter()); + String output = prettyPrinter.prettyprint(stereovalue); + result = parser.parseStereoValue(new StringReader(output)); + + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(stereovalue.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/statements/cocos/AssertIsValidTest.java b/monticore-grammar/src/test/java/de/monticore/statements/cocos/AssertIsValidTest.java new file mode 100644 index 0000000000..b93cc6ddb9 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/statements/cocos/AssertIsValidTest.java @@ -0,0 +1,79 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements.cocos; + +import de.monticore.statements.mccommonstatements.cocos.AssertIsValid; +import de.monticore.statements.mcstatementsbasis._ast.ASTMCBlockStatement; +import de.monticore.statements.testmcassertstatements.TestMCAssertStatementsMill; +import de.monticore.statements.testmcassertstatements._cocos.TestMCAssertStatementsCoCoChecker; +import de.monticore.statements.testmcassertstatements._parser.TestMCAssertStatementsParser; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.types.check.FullDeriveFromCombineExpressionsWithLiterals; +import de.monticore.types.check.TypeCalculator; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class AssertIsValidTest { + + protected TestMCAssertStatementsCoCoChecker checker; + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestMCAssertStatementsMill.reset(); + TestMCAssertStatementsMill.init(); + BasicSymbolsMill.initializePrimitives(); + checker = new TestMCAssertStatementsCoCoChecker(); + checker.setTraverser(TestMCAssertStatementsMill.traverser()); + checker.addCoCo(new AssertIsValid(new TypeCalculator(null, new FullDeriveFromCombineExpressionsWithLiterals()))); + } + + public void checkValid(String expressionString) throws IOException { + + TestMCAssertStatementsParser parser = new TestMCAssertStatementsParser(); + Optional optAST = parser.parse_StringMCBlockStatement(expressionString); + assertTrue(optAST.isPresent()); + Log.getFindings().clear(); + checker.checkAll(optAST.get()); + assertTrue(Log.getFindings().isEmpty()); + + } + + public void checkInvalid(String expressionString) throws IOException { + + TestMCAssertStatementsParser parser = new TestMCAssertStatementsParser(); + Optional optAST = parser.parse_StringMCBlockStatement(expressionString); + assertTrue(optAST.isPresent()); + Log.getFindings().clear(); + checker.checkAll(optAST.get()); + assertFalse(Log.getFindings().isEmpty()); + + } + + @Test + public void testValid() throws IOException { + + checkValid("assert 5 >= 0;"); + checkValid("assert !(true||false)&&(5<6);"); + checkInvalid("assert 5 >= 0: 1+1;"); + + } + + @Test + public void testInvalid() throws IOException { + + checkInvalid("assert 4;"); + checkInvalid("assert 'c';"); + + } + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/java/de/monticore/statements/cocos/CatchIsValidTest.java b/monticore-grammar/src/test/java/de/monticore/statements/cocos/CatchIsValidTest.java new file mode 100644 index 0000000000..11f2c2e243 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/statements/cocos/CatchIsValidTest.java @@ -0,0 +1,90 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements.cocos; + +import de.monticore.statements.mccommonstatements.cocos.CatchIsValid; +import de.monticore.statements.mcexceptionstatements._ast.ASTCatchClause; +import de.monticore.statements.testmcexceptionstatements.TestMCExceptionStatementsMill; +import de.monticore.statements.testmcexceptionstatements._cocos.TestMCExceptionStatementsCoCoChecker; +import de.monticore.statements.testmcexceptionstatements._parser.TestMCExceptionStatementsParser; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.types.check.*; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class CatchIsValidTest { + + protected TestMCExceptionStatementsCoCoChecker checker; + + @Before + public void init(){ + LogStub.init(); + Log.enableFailQuick(false); + TestMCExceptionStatementsMill.reset(); + TestMCExceptionStatementsMill.init(); + BasicSymbolsMill.initializePrimitives(); + checker = new TestMCExceptionStatementsCoCoChecker(); + checker.setTraverser(TestMCExceptionStatementsMill.traverser()); + checker.addCoCo(new CatchIsValid(new TypeCalculator(null, new FullDeriveFromCombineExpressionsWithLiterals()))); + + SymTypeOfObject sType = SymTypeExpressionFactory.createTypeObject("java.lang.Throwable", TestMCExceptionStatementsMill.globalScope()); + SymTypeOfObject sTypeA = SymTypeExpressionFactory.createTypeObject("A", TestMCExceptionStatementsMill.globalScope()); + TestMCExceptionStatementsMill.globalScope().add(TestMCExceptionStatementsMill.oOTypeSymbolBuilder().setName("A").addSuperTypes(sType).build()); + TestMCExceptionStatementsMill.globalScope().add(TestMCExceptionStatementsMill.oOTypeSymbolBuilder().setName("java.lang.Throwable").build()); + TestMCExceptionStatementsMill.globalScope().add(TestMCExceptionStatementsMill.fieldSymbolBuilder().setName("a").setType(sTypeA).build()); + + SymTypeOfObject symType = SymTypeExpressionFactory.createTypeObject("java.lang.Object", TestMCExceptionStatementsMill.globalScope()); + SymTypeOfObject symTypeB = SymTypeExpressionFactory.createTypeObject("B", TestMCExceptionStatementsMill.globalScope()); + TestMCExceptionStatementsMill.globalScope().add(TestMCExceptionStatementsMill.oOTypeSymbolBuilder().setName("B").addSuperTypes(symType).build()); + TestMCExceptionStatementsMill.globalScope().add(TestMCExceptionStatementsMill.oOTypeSymbolBuilder().setName("java.lang.Object").build()); + TestMCExceptionStatementsMill.globalScope().add(TestMCExceptionStatementsMill.fieldSymbolBuilder().setName("b").setType(symTypeB).build()); + } + + public void checkValid(String expressionString) throws IOException { + + TestMCExceptionStatementsParser parser = new TestMCExceptionStatementsParser(); + Optional optAST = parser.parse_StringCatchClause(expressionString); + assertTrue(optAST.isPresent()); + ASTCatchClause ast = optAST.get(); + ast.setEnclosingScope(TestMCExceptionStatementsMill.globalScope()); + ast.getCatchTypeList().setEnclosingScope(TestMCExceptionStatementsMill.globalScope()); + ast.getCatchTypeList().forEachMCQualifiedNames(n -> n.setEnclosingScope(TestMCExceptionStatementsMill.globalScope())); + Log.getFindings().clear(); + checker.checkAll(optAST.get()); + assertTrue(Log.getFindings().isEmpty()); + + } + + public void checkInvalid(String expressionString) throws IOException { + + TestMCExceptionStatementsParser parser = new TestMCExceptionStatementsParser(); + Optional optAST = parser.parse_StringCatchClause(expressionString); + assertTrue(optAST.isPresent()); + ASTCatchClause ast = optAST.get(); + ast.setEnclosingScope(TestMCExceptionStatementsMill.globalScope()); + ast.getCatchTypeList().setEnclosingScope(TestMCExceptionStatementsMill.globalScope()); + ast.getCatchTypeList().forEachMCQualifiedNames(n -> n.setEnclosingScope(TestMCExceptionStatementsMill.globalScope())); + Log.getFindings().clear(); + checker.checkAll(optAST.get()); + assertFalse(Log.getFindings().isEmpty()); + + } + + @Test + public void testValid() throws IOException { + checkValid("catch(A a){}"); + } + + @Test + public void testInvalid() throws IOException { + checkInvalid("catch (B b){}"); + } + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/java/de/monticore/statements/cocos/DoWhileConditionHasBooleanTypeTest.java b/monticore-grammar/src/test/java/de/monticore/statements/cocos/DoWhileConditionHasBooleanTypeTest.java new file mode 100644 index 0000000000..4ebfc7ac83 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/statements/cocos/DoWhileConditionHasBooleanTypeTest.java @@ -0,0 +1,78 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements.cocos; + +import de.monticore.statements.mccommonstatements.cocos.DoWhileConditionHasBooleanType; +import de.monticore.statements.mcstatementsbasis._ast.ASTMCBlockStatement; +import de.monticore.statements.testmccommonstatements.TestMCCommonStatementsMill; +import de.monticore.statements.testmccommonstatements._cocos.TestMCCommonStatementsCoCoChecker; +import de.monticore.statements.testmccommonstatements._parser.TestMCCommonStatementsParser; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.types.check.FullDeriveFromCombineExpressionsWithLiterals; +import de.monticore.types.check.TypeCalculator; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class DoWhileConditionHasBooleanTypeTest { + + protected TestMCCommonStatementsCoCoChecker checker; + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestMCCommonStatementsMill.reset(); + TestMCCommonStatementsMill.init(); + BasicSymbolsMill.initializePrimitives(); + checker = new TestMCCommonStatementsCoCoChecker(); + checker.addCoCo(new DoWhileConditionHasBooleanType(new TypeCalculator(null,new FullDeriveFromCombineExpressionsWithLiterals()))); + } + + public void checkValid(String expressionString) throws IOException { + + TestMCCommonStatementsParser parser = new TestMCCommonStatementsParser(); + Optional optAST = parser.parse_StringMCBlockStatement(expressionString); + assertTrue(optAST.isPresent()); + Log.getFindings().clear(); + checker.checkAll(optAST.get()); + assertTrue(Log.getFindings().isEmpty()); + + } + + public void checkInvalid(String expressionString) throws IOException { + + TestMCCommonStatementsParser parser = new TestMCCommonStatementsParser(); + Optional optAST = parser.parse_StringMCBlockStatement(expressionString); + assertTrue(optAST.isPresent()); + Log.getFindings().clear(); + checker.checkAll(optAST.get()); + assertFalse(Log.getFindings().isEmpty()); + + } + + @Test + public void testValid() throws IOException { + + checkValid("do{}while(5>6);"); + checkValid("do{}while(true||false);"); + checkValid("do{}while(!true&&(5==6));"); + + } + + @Test + public void testInvalid() throws IOException { + + checkInvalid("do{}while(5+5);"); + checkInvalid("do{}while('c');"); + checkInvalid("do{}while(1.2-3.4);"); + + } + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/java/de/monticore/statements/cocos/ExpressionStatementIsValidTest.java b/monticore-grammar/src/test/java/de/monticore/statements/cocos/ExpressionStatementIsValidTest.java new file mode 100644 index 0000000000..6ab8ca6b71 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/statements/cocos/ExpressionStatementIsValidTest.java @@ -0,0 +1,97 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements.cocos; + +import de.monticore.statements.mccommonstatements.cocos.ExpressionStatementIsValid; +import de.monticore.statements.mcstatementsbasis._ast.ASTMCBlockStatement; +import de.monticore.statements.testmccommonstatements.TestMCCommonStatementsMill; +import de.monticore.statements.testmccommonstatements._cocos.TestMCCommonStatementsCoCoChecker; +import de.monticore.statements.testmccommonstatements._parser.TestMCCommonStatementsParser; +import de.monticore.statements.testmccommonstatements._visitor.TestMCCommonStatementsTraverser; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.oosymbols._symboltable.FieldSymbol; +import de.monticore.types.check.FlatExpressionScopeSetter; +import de.monticore.types.check.FullDeriveFromCombineExpressionsWithLiterals; +import de.monticore.types.check.FullSynthesizeFromCombineExpressionsWithLiterals; +import de.monticore.types.check.IDerive; +import de.monticore.types.check.ISynthesize; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types.check.TypeCalculator; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class ExpressionStatementIsValidTest { + + protected TestMCCommonStatementsCoCoChecker checker; + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestMCCommonStatementsMill.reset(); + TestMCCommonStatementsMill.init(); + TestMCCommonStatementsMill.globalScope().clear(); + BasicSymbolsMill.initializePrimitives(); + initSymbols(); + + ISynthesize synthesize = new FullSynthesizeFromCombineExpressionsWithLiterals(); + IDerive derive = new FullDeriveFromCombineExpressionsWithLiterals(); + TypeCalculator typeCalc = new TypeCalculator(synthesize, derive); + checker = new TestMCCommonStatementsCoCoChecker(); + checker.addCoCo(new ExpressionStatementIsValid(typeCalc)); + } + + protected static void initSymbols() { + FieldSymbol anInt = TestMCCommonStatementsMill.fieldSymbolBuilder() + .setName("anInt") + .setType(SymTypeExpressionFactory.createPrimitive(BasicSymbolsMill.INT)) + .setEnclosingScope(TestMCCommonStatementsMill.globalScope()) + .setAstNodeAbsent() + .build(); + TestMCCommonStatementsMill.globalScope().add(anInt); + } + + public void checkValid(String expressionString) throws IOException { + TestMCCommonStatementsParser parser = new TestMCCommonStatementsParser(); + Optional ast = parser.parse_StringMCBlockStatement(expressionString); + + TestMCCommonStatementsTraverser traverser = TestMCCommonStatementsMill.inheritanceTraverser(); + FlatExpressionScopeSetter scopeSetter = new FlatExpressionScopeSetter(TestMCCommonStatementsMill.globalScope()); + traverser.add4ExpressionsBasis(scopeSetter); + ast.orElseThrow().accept(traverser); + + checker.checkAll(ast.orElseThrow()); + assertTrue(Log.getFindings().toString(), Log.getFindings().isEmpty()); + } + + public void checkInvalid(String expressionString) throws IOException { + TestMCCommonStatementsParser parser = new TestMCCommonStatementsParser(); + Optional ast = parser.parse_StringMCBlockStatement(expressionString); + + TestMCCommonStatementsTraverser traverser = TestMCCommonStatementsMill.inheritanceTraverser(); + FlatExpressionScopeSetter scopeSetter = new FlatExpressionScopeSetter(TestMCCommonStatementsMill.globalScope()); + traverser.add4ExpressionsBasis(scopeSetter); + ast.orElseThrow().accept(traverser); + + checker.checkAll(ast.orElseThrow()); + assertFalse(Log.getFindings().toString(), Log.getFindings().isEmpty()); + } + + @Test + public void testIsValid() throws IOException { + checkValid("anInt = 5;"); + } + + @Test + public void testIsInvalid() throws IOException { + checkInvalid("anInt = true;"); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/statements/cocos/ForConditionHasBooleanTypeTest.java b/monticore-grammar/src/test/java/de/monticore/statements/cocos/ForConditionHasBooleanTypeTest.java new file mode 100644 index 0000000000..2f6f571384 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/statements/cocos/ForConditionHasBooleanTypeTest.java @@ -0,0 +1,76 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements.cocos; + +import de.monticore.statements.mccommonstatements.cocos.ForConditionHasBooleanType; +import de.monticore.statements.mcstatementsbasis._ast.ASTMCBlockStatement; +import de.monticore.statements.testmccommonstatements.TestMCCommonStatementsMill; +import de.monticore.statements.testmccommonstatements._cocos.TestMCCommonStatementsCoCoChecker; +import de.monticore.statements.testmccommonstatements._parser.TestMCCommonStatementsParser; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.types.check.FullDeriveFromCombineExpressionsWithLiterals; +import de.monticore.types.check.TypeCalculator; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class ForConditionHasBooleanTypeTest { + + protected TestMCCommonStatementsCoCoChecker checker; + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestMCCommonStatementsMill.reset(); + TestMCCommonStatementsMill.init(); + BasicSymbolsMill.initializePrimitives(); + checker = new TestMCCommonStatementsCoCoChecker(); + checker.addCoCo(new ForConditionHasBooleanType(new TypeCalculator(null,new FullDeriveFromCombineExpressionsWithLiterals()))); + } + + public void checkValid(String expressionString) throws IOException { + + TestMCCommonStatementsParser parser = new TestMCCommonStatementsParser(); + Optional optAST = parser.parse_StringMCBlockStatement(expressionString); + assertTrue(optAST.isPresent()); + Log.getFindings().clear(); + checker.checkAll(optAST.get()); + assertTrue(Log.getFindings().isEmpty()); + + } + + public void checkInvalid(String expressionString) throws IOException { + + TestMCCommonStatementsParser parser = new TestMCCommonStatementsParser(); + Optional optAST = parser.parse_StringMCBlockStatement(expressionString); + assertTrue(optAST.isPresent()); + Log.getFindings().clear(); + checker.checkAll(optAST.get()); + assertFalse(Log.getFindings().isEmpty()); + + } + + @Test + public void testValid() throws IOException { + + checkValid("for(5; true;5+1){}"); + checkValid("for(5; 5<6;5+1){}"); + checkValid("for(5; !(false || true) && (6==7);5+1){}"); + + } + + @Test + public void testInvalid() throws IOException { + + checkInvalid("for(1+1;2+2;'x'){}"); + + } +} \ No newline at end of file diff --git a/monticore-grammar/src/test/java/de/monticore/statements/cocos/ForEachIsValidTest.java b/monticore-grammar/src/test/java/de/monticore/statements/cocos/ForEachIsValidTest.java new file mode 100644 index 0000000000..2fb8db8a54 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/statements/cocos/ForEachIsValidTest.java @@ -0,0 +1,115 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements.cocos; + +import de.monticore.statements.mccommonstatements._ast.ASTEnhancedForControl; +import de.monticore.statements.mccommonstatements.cocos.ForEachIsValid; +import de.monticore.statements.testmccommonstatements.TestMCCommonStatementsMill; +import de.monticore.statements.testmccommonstatements._cocos.TestMCCommonStatementsCoCoChecker; +import de.monticore.statements.testmccommonstatements._parser.TestMCCommonStatementsParser; +import de.monticore.statements.testmccommonstatements._symboltable.ITestMCCommonStatementsScope; +import de.monticore.statements.testmccommonstatements._visitor.TestMCCommonStatementsTraverser; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.types.check.*; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class ForEachIsValidTest { + + protected TestMCCommonStatementsCoCoChecker checker; + + @Before + public void init(){ + LogStub.init(); + Log.enableFailQuick(false); + TestMCCommonStatementsMill.reset(); + TestMCCommonStatementsMill.init(); + BasicSymbolsMill.initializePrimitives(); + checker = new TestMCCommonStatementsCoCoChecker(); + checker.addCoCo(new ForEachIsValid(new TypeCalculator(new FullSynthesizeFromCombineExpressionsWithLiterals(), new FullDeriveFromCombineExpressionsWithLiterals()))); + + SymTypeOfObject sType = SymTypeExpressionFactory.createTypeObject("java.lang.Iterable", TestMCCommonStatementsMill.globalScope()); + SymTypeOfObject sTypeA = SymTypeExpressionFactory.createTypeObject("A", TestMCCommonStatementsMill.globalScope()); + TestMCCommonStatementsMill.globalScope().add(TestMCCommonStatementsMill.oOTypeSymbolBuilder().setName("A").addSuperTypes(sType).build()); + TestMCCommonStatementsMill.globalScope().add(TestMCCommonStatementsMill.oOTypeSymbolBuilder().setName("java.lang.Iterable").build()); + TestMCCommonStatementsMill.globalScope().add(TestMCCommonStatementsMill.oOTypeSymbolBuilder().setName("java.util.Arrays").build()); + TestMCCommonStatementsMill.globalScope().add(TestMCCommonStatementsMill.fieldSymbolBuilder().setName("a").setType(sTypeA).build()); + + SymTypeOfObject sTypeS = SymTypeExpressionFactory.createTypeObject("Object", TestMCCommonStatementsMill.globalScope()); + TestMCCommonStatementsMill.globalScope().add(TestMCCommonStatementsMill.oOTypeSymbolBuilder().setName("Object").build()); + TestMCCommonStatementsMill.globalScope().add(TestMCCommonStatementsMill.fieldSymbolBuilder().setName("o").setType(sTypeS).build()); + } + + private void addToTraverser(TestMCCommonStatementsTraverser traverser, ITestMCCommonStatementsScope enclosingScope) { + FlatExpressionScopeSetter flatExpressionScopeSetter = new FlatExpressionScopeSetter(enclosingScope); + traverser.add4ExpressionsBasis(flatExpressionScopeSetter); + traverser.add4CommonExpressions(flatExpressionScopeSetter); + traverser.add4MCBasicTypes(flatExpressionScopeSetter); + traverser.add4MCCollectionTypes(flatExpressionScopeSetter); + traverser.add4MCArrayTypes(flatExpressionScopeSetter); + traverser.add4MCCommonLiterals(flatExpressionScopeSetter); + } + + private TestMCCommonStatementsTraverser flatExpressionScopeSetterTraverser; + + public void checkValid(String expressionString) throws IOException { + + TestMCCommonStatementsParser parser = new TestMCCommonStatementsParser(); + Optional optAST = parser.parse_StringEnhancedForControl(expressionString); + assertTrue(optAST.isPresent()); + ASTEnhancedForControl ast = optAST.get(); + + TestMCCommonStatementsTraverser traverser = TestMCCommonStatementsMill.traverser(); + addToTraverser(traverser, TestMCCommonStatementsMill.globalScope()); + ast.accept(traverser); + ast.setEnclosingScope(TestMCCommonStatementsMill.globalScope()); + + Log.getFindings().clear(); + checker.checkAll(optAST.get()); + assertTrue(Log.getFindings().isEmpty()); + + } + + public void checkInvalid(String expressionString) throws IOException { + + TestMCCommonStatementsParser parser = new TestMCCommonStatementsParser(); + Optional optAST = parser.parse_StringEnhancedForControl(expressionString); + assertTrue(optAST.isPresent()); + ASTEnhancedForControl ast = optAST.get(); + + TestMCCommonStatementsTraverser traverser = TestMCCommonStatementsMill.traverser(); + addToTraverser(traverser, TestMCCommonStatementsMill.globalScope()); + ast.accept(traverser); + + ast.setEnclosingScope(TestMCCommonStatementsMill.globalScope()); + Log.getFindings().clear(); + checker.checkAll(optAST.get()); + assertFalse(Log.getFindings().isEmpty()); + + } + + @Test + public void testValid() throws IOException { + + checkValid("Object o : a"); + + } + + @Test + public void testInvalid() throws IOException { + checkInvalid("Object o : 3"); + } + + @Test + public void testInvalid2() throws IOException { + checkInvalid("Object o : o"); + } + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/java/de/monticore/statements/cocos/IfConditionHasBooleanTypeTest.java b/monticore-grammar/src/test/java/de/monticore/statements/cocos/IfConditionHasBooleanTypeTest.java new file mode 100644 index 0000000000..c1026bfb3f --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/statements/cocos/IfConditionHasBooleanTypeTest.java @@ -0,0 +1,78 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements.cocos; + +import de.monticore.statements.mccommonstatements.cocos.IfConditionHasBooleanType; +import de.monticore.statements.mcstatementsbasis._ast.ASTMCBlockStatement; +import de.monticore.statements.testmccommonstatements.TestMCCommonStatementsMill; +import de.monticore.statements.testmccommonstatements._cocos.TestMCCommonStatementsCoCoChecker; +import de.monticore.statements.testmccommonstatements._parser.TestMCCommonStatementsParser; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.types.check.FullDeriveFromCombineExpressionsWithLiterals; +import de.monticore.types.check.TypeCalculator; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class IfConditionHasBooleanTypeTest { + + protected TestMCCommonStatementsCoCoChecker checker; + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestMCCommonStatementsMill.reset(); + TestMCCommonStatementsMill.init(); + BasicSymbolsMill.initializePrimitives(); + checker = new TestMCCommonStatementsCoCoChecker(); + checker.addCoCo(new IfConditionHasBooleanType(new TypeCalculator(null,new FullDeriveFromCombineExpressionsWithLiterals()))); + } + + public void checkValid(String expressionString) throws IOException { + + TestMCCommonStatementsParser parser = new TestMCCommonStatementsParser(); + Optional optAST = parser.parse_StringMCBlockStatement(expressionString); + assertTrue(optAST.isPresent()); + Log.getFindings().clear(); + checker.checkAll(optAST.get()); + assertTrue(Log.getFindings().isEmpty()); + + } + + public void checkInvalid(String expressionString) throws IOException { + + TestMCCommonStatementsParser parser = new TestMCCommonStatementsParser(); + Optional optAST = parser.parse_StringMCBlockStatement(expressionString); + assertTrue(optAST.isPresent()); + Log.getFindings().clear(); + checker.checkAll(optAST.get()); + assertFalse(Log.getFindings().isEmpty()); + + } + + @Test + public void testValid() throws IOException{ + + checkValid("if(true){}"); + checkValid("if(1<2){}"); + checkValid("if(!true&&(5==6)){}"); + checkValid("if((1<2)||(5%2==1)){}"); + + } + + @Test + public void testInvalid()throws IOException{ + + checkInvalid("if(1+1){}"); + checkInvalid("if('c'+10){}"); + checkInvalid("if(1.2-5.5){}"); + + } +} \ No newline at end of file diff --git a/monticore-grammar/src/test/java/de/monticore/statements/cocos/ResourceInTryStatementCloseableTest.java b/monticore-grammar/src/test/java/de/monticore/statements/cocos/ResourceInTryStatementCloseableTest.java new file mode 100644 index 0000000000..647aa1e454 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/statements/cocos/ResourceInTryStatementCloseableTest.java @@ -0,0 +1,100 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements.cocos; + +import de.monticore.statements.mccommonstatements.cocos.ResourceInTryStatementCloseable; +import de.monticore.statements.mcexceptionstatements._ast.ASTMCExceptionStatementsNode; +import de.monticore.statements.mcexceptionstatements._ast.ASTTryLocalVariableDeclaration; +import de.monticore.statements.mcexceptionstatements._ast.ASTTryStatement3; +import de.monticore.statements.testmcexceptionstatements.TestMCExceptionStatementsMill; +import de.monticore.statements.testmcexceptionstatements._cocos.TestMCExceptionStatementsCoCoChecker; +import de.monticore.statements.testmcexceptionstatements._parser.TestMCExceptionStatementsParser; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.types.check.*; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class ResourceInTryStatementCloseableTest { + + protected TestMCExceptionStatementsCoCoChecker checker; + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestMCExceptionStatementsMill.reset(); + TestMCExceptionStatementsMill.init(); + BasicSymbolsMill.initializePrimitives(); + + checker = new TestMCExceptionStatementsCoCoChecker(); + checker.setTraverser(TestMCExceptionStatementsMill.traverser()); + checker.addCoCo(new ResourceInTryStatementCloseable(new TypeCalculator(null, new FullDeriveFromCombineExpressionsWithLiterals()))); + SymTypeOfObject sType = SymTypeExpressionFactory.createTypeObject("java.io.Closeable", TestMCExceptionStatementsMill.globalScope()); + SymTypeOfObject sTypeA = SymTypeExpressionFactory.createTypeObject("A", TestMCExceptionStatementsMill.globalScope()); + TestMCExceptionStatementsMill.globalScope().add(TestMCExceptionStatementsMill.oOTypeSymbolBuilder().setName("A").addSuperTypes(sType).build()); + TestMCExceptionStatementsMill.globalScope().add(TestMCExceptionStatementsMill.oOTypeSymbolBuilder().setName("java.io.Closeable").build()); + TestMCExceptionStatementsMill.globalScope().add(TestMCExceptionStatementsMill.fieldSymbolBuilder().setName("a").setType(sTypeA).build()); + + SymTypeOfObject sTypeB = SymTypeExpressionFactory.createTypeObject("B", TestMCExceptionStatementsMill.globalScope()); + TestMCExceptionStatementsMill.globalScope().add(TestMCExceptionStatementsMill.oOTypeSymbolBuilder().setName("B").build()); + TestMCExceptionStatementsMill.globalScope().add(TestMCExceptionStatementsMill.fieldSymbolBuilder().setName("b").setType(sTypeB).build()); + + } + + public void checkValid(String expressionString) throws IOException { + + TestMCExceptionStatementsParser parser = new TestMCExceptionStatementsParser(); + Optional optAST = parser.parse_StringTryStatement3(expressionString); + assertTrue(optAST.isPresent()); + ASTTryStatement3 ast = optAST.get(); + ast.setEnclosingScope(TestMCExceptionStatementsMill.globalScope()); + for (ASTTryLocalVariableDeclaration dec: ast.getTryLocalVariableDeclarationList()){ + + dec.getExpression().setEnclosingScope(TestMCExceptionStatementsMill.globalScope()); + Log.getFindings().clear(); + checker.checkAll((ASTMCExceptionStatementsNode) optAST.get()); + assertTrue(Log.getFindings().isEmpty()); + + } + } + + public void checkInvalid(String expressionString) throws IOException { + + TestMCExceptionStatementsParser parser = new TestMCExceptionStatementsParser(); + Optional optAST = parser.parse_StringTryStatement3(expressionString); + assertTrue(optAST.isPresent()); + ASTTryStatement3 ast = optAST.get(); + ast.setEnclosingScope(TestMCExceptionStatementsMill.globalScope()); + for (ASTTryLocalVariableDeclaration dec: ast.getTryLocalVariableDeclarationList()){ + + dec.getExpression().setEnclosingScope(TestMCExceptionStatementsMill.globalScope()); + Log.getFindings().clear(); + checker.checkAll((ASTMCExceptionStatementsNode) optAST.get()); + assertFalse(Log.getFindings().isEmpty()); + + } + } + + @Test + public void testValid() throws IOException { + + checkValid("try(A c = a){}"); + + } + + @Test + public void testInvalid() throws IOException { + + checkInvalid("try(B c = b){}"); + + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/statements/cocos/SwitchStatementValidTest.java b/monticore-grammar/src/test/java/de/monticore/statements/cocos/SwitchStatementValidTest.java new file mode 100644 index 0000000000..dbe32b075d --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/statements/cocos/SwitchStatementValidTest.java @@ -0,0 +1,77 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements.cocos; + +import de.monticore.statements.mccommonstatements.cocos.SwitchStatementValid; +import de.monticore.statements.mcstatementsbasis._ast.ASTMCBlockStatement; +import de.monticore.statements.testmccommonstatements.TestMCCommonStatementsMill; +import de.monticore.statements.testmccommonstatements._cocos.TestMCCommonStatementsCoCoChecker; +import de.monticore.statements.testmccommonstatements._parser.TestMCCommonStatementsParser; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.types.check.FullDeriveFromCombineExpressionsWithLiterals; +import de.monticore.types.check.TypeCalculator; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class SwitchStatementValidTest { + + protected TestMCCommonStatementsCoCoChecker checker; + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestMCCommonStatementsMill.reset(); + TestMCCommonStatementsMill.init(); + BasicSymbolsMill.initializePrimitives(); + checker = new TestMCCommonStatementsCoCoChecker(); + checker.addCoCo(new SwitchStatementValid(new TypeCalculator(null,new FullDeriveFromCombineExpressionsWithLiterals()))); + } + + public void checkValid(String expressionString) throws IOException { + + TestMCCommonStatementsParser parser = new TestMCCommonStatementsParser(); + Optional optAST = parser.parse_StringMCBlockStatement(expressionString); + assertTrue(optAST.isPresent()); + Log.getFindings().clear(); + checker.checkAll(optAST.get()); + assertTrue(Log.getFindings().isEmpty()); + + } + + public void checkInvalid(String expressionString) throws IOException { + + TestMCCommonStatementsParser parser = new TestMCCommonStatementsParser(); + Optional optAST = parser.parse_StringMCBlockStatement(expressionString); + assertTrue(optAST.isPresent()); + Log.getFindings().clear(); + checker.checkAll(optAST.get()); + assertFalse(Log.getFindings().isEmpty()); + + } + + @Test + public void testValid() throws IOException { + + checkValid("switch(5){}"); + checkValid("switch('c'){}"); + + } + + @Test + public void testInvalid() throws IOException { + + checkInvalid("switch(5.5){}"); + checkInvalid("switch(5.5F){}"); + checkInvalid("switch(false){}"); + + } + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/java/de/monticore/statements/cocos/SynchronizedArgIsReftypeTest.java b/monticore-grammar/src/test/java/de/monticore/statements/cocos/SynchronizedArgIsReftypeTest.java new file mode 100644 index 0000000000..ab3f155d66 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/statements/cocos/SynchronizedArgIsReftypeTest.java @@ -0,0 +1,85 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements.cocos; + +import de.monticore.statements.mccommonstatements.cocos.SynchronizedArgIsReftype; +import de.monticore.statements.mcsynchronizedstatements._ast.ASTMCSynchronizedStatementsNode; +import de.monticore.statements.mcsynchronizedstatements._ast.ASTSynchronizedStatement; +import de.monticore.statements.testmcexceptionstatements.TestMCExceptionStatementsMill; +import de.monticore.statements.testmcsynchronizedstatements.TestMCSynchronizedStatementsMill; +import de.monticore.statements.testmcsynchronizedstatements._cocos.TestMCSynchronizedStatementsCoCoChecker; +import de.monticore.statements.testmcsynchronizedstatements._parser.TestMCSynchronizedStatementsParser; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.types.check.*; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class SynchronizedArgIsReftypeTest { + + protected TestMCSynchronizedStatementsCoCoChecker checker; + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestMCSynchronizedStatementsMill.reset(); + TestMCSynchronizedStatementsMill.init(); + BasicSymbolsMill.initializePrimitives(); + checker = new TestMCSynchronizedStatementsCoCoChecker(); + checker.addCoCo(new SynchronizedArgIsReftype(new TypeCalculator(null, new FullDeriveFromCombineExpressionsWithLiterals()))); + + SymTypeOfObject sType = SymTypeExpressionFactory.createTypeObject("java.lang.Object", TestMCExceptionStatementsMill.globalScope()); + TestMCExceptionStatementsMill.globalScope().add(TestMCExceptionStatementsMill.oOTypeSymbolBuilder().setName("java.lang.Object").build()); + TestMCExceptionStatementsMill.globalScope().add(TestMCExceptionStatementsMill.fieldSymbolBuilder().setName("a1").setType(sType).build()); + } + + public void checkValid(String expressionString) throws IOException { + + TestMCSynchronizedStatementsParser parser = new TestMCSynchronizedStatementsParser(); + Optional optAST = parser.parse_StringSynchronizedStatement(expressionString); + assertTrue(optAST.isPresent()); + ASTSynchronizedStatement ast = optAST.get(); + ast.getExpression().setEnclosingScope(TestMCExceptionStatementsMill.globalScope()); + Log.getFindings().clear(); + checker.checkAll((ASTMCSynchronizedStatementsNode) optAST.get()); + assertTrue(Log.getFindings().isEmpty()); + + } + + public void checkInvalid(String expressionString) throws IOException { + + TestMCSynchronizedStatementsParser parser = new TestMCSynchronizedStatementsParser(); + Optional optAST = parser.parse_StringSynchronizedStatement(expressionString); + assertTrue(optAST.isPresent()); + ASTSynchronizedStatement ast = optAST.get(); + ast.getExpression().setEnclosingScope(TestMCExceptionStatementsMill.globalScope()); + Log.getFindings().clear(); + checker.checkAll((ASTMCSynchronizedStatementsNode) optAST.get()); + assertFalse(Log.getFindings().isEmpty()); + + } + + @Test + public void testValid() throws IOException { + + checkValid("synchronized(a1){}"); + + } + + @Test + public void testInvalid() throws IOException { + + checkInvalid("synchronized('f'){}"); + checkInvalid("synchronized(5.5){}"); + checkInvalid("synchronized(false){}"); + + } + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/java/de/monticore/statements/cocos/ThrowIsValidTest.java b/monticore-grammar/src/test/java/de/monticore/statements/cocos/ThrowIsValidTest.java new file mode 100644 index 0000000000..1023f5d2f3 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/statements/cocos/ThrowIsValidTest.java @@ -0,0 +1,87 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements.cocos; + +import de.monticore.statements.mccommonstatements.cocos.ThrowIsValid; +import de.monticore.statements.mcexceptionstatements._ast.ASTMCExceptionStatementsNode; +import de.monticore.statements.mcexceptionstatements._ast.ASTThrowStatement; +import de.monticore.statements.testmcexceptionstatements.TestMCExceptionStatementsMill; +import de.monticore.statements.testmcexceptionstatements._cocos.TestMCExceptionStatementsCoCoChecker; +import de.monticore.statements.testmcexceptionstatements._parser.TestMCExceptionStatementsParser; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.types.check.*; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class ThrowIsValidTest { + + protected TestMCExceptionStatementsCoCoChecker checker; + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + + TestMCExceptionStatementsMill.reset(); + TestMCExceptionStatementsMill.init(); + BasicSymbolsMill.initializePrimitives(); + checker = new TestMCExceptionStatementsCoCoChecker(); + checker.setTraverser(TestMCExceptionStatementsMill.traverser()); + checker.addCoCo(new ThrowIsValid(new TypeCalculator(null, new FullDeriveFromCombineExpressionsWithLiterals()))); + SymTypeOfObject sType = SymTypeExpressionFactory.createTypeObject("java.lang.Throwable", TestMCExceptionStatementsMill.globalScope()); + SymTypeOfObject sTypeA = SymTypeExpressionFactory.createTypeObject("A", TestMCExceptionStatementsMill.globalScope()); + TestMCExceptionStatementsMill.globalScope().add(TestMCExceptionStatementsMill.oOTypeSymbolBuilder().setName("A").addSuperTypes(sType).build()); + TestMCExceptionStatementsMill.globalScope().add(TestMCExceptionStatementsMill.oOTypeSymbolBuilder().setName("java.lang.Throwable").build()); + TestMCExceptionStatementsMill.globalScope().add(TestMCExceptionStatementsMill.fieldSymbolBuilder().setName("a").setType(sTypeA).build()); + + SymTypeOfObject sTypeB = SymTypeExpressionFactory.createTypeObject("B", TestMCExceptionStatementsMill.globalScope()); + TestMCExceptionStatementsMill.globalScope().add(TestMCExceptionStatementsMill.oOTypeSymbolBuilder().setName("B").build()); + TestMCExceptionStatementsMill.globalScope().add(TestMCExceptionStatementsMill.fieldSymbolBuilder().setName("b").setType(sTypeB).build()); + } + + public void checkValid(String expressionString) throws IOException { + + TestMCExceptionStatementsParser parser = new TestMCExceptionStatementsParser(); + Optional optAST = parser.parse_StringThrowStatement(expressionString); + assertTrue(optAST.isPresent()); + ASTThrowStatement ast = optAST.get(); + ast.setEnclosingScope(TestMCExceptionStatementsMill.globalScope()); + ast.getExpression().setEnclosingScope(TestMCExceptionStatementsMill.globalScope()); + Log.getFindings().clear(); + checker.checkAll((ASTMCExceptionStatementsNode) optAST.get()); + assertTrue(Log.getFindings().isEmpty()); + + } + + public void checkInvalid(String expressionString) throws IOException { + + TestMCExceptionStatementsParser parser = new TestMCExceptionStatementsParser(); + Optional optAST = parser.parse_StringThrowStatement(expressionString); + assertTrue(optAST.isPresent()); + ASTThrowStatement ast = optAST.get(); + ast.setEnclosingScope(TestMCExceptionStatementsMill.globalScope()); + ast.getExpression().setEnclosingScope(TestMCExceptionStatementsMill.globalScope()); + Log.getFindings().clear(); + checker.checkAll((ASTMCExceptionStatementsNode) optAST.get()); + assertFalse(Log.getFindings().isEmpty()); + + } + + @Test + public void testValid() throws IOException{ + checkValid("throw a;"); + } + + @Test + public void testInvalid() throws IOException{ + checkInvalid("throw b;"); + } +} \ No newline at end of file diff --git a/monticore-grammar/src/test/java/de/monticore/statements/cocos/VarDeclarationInitializationHasCorrectTypeTest.java b/monticore-grammar/src/test/java/de/monticore/statements/cocos/VarDeclarationInitializationHasCorrectTypeTest.java new file mode 100644 index 0000000000..365bed771e --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/statements/cocos/VarDeclarationInitializationHasCorrectTypeTest.java @@ -0,0 +1,126 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements.cocos; + +import com.google.common.collect.Lists; +import de.monticore.statements.mcvardeclarationstatements._cocos.VarDeclarationInitializationHasCorrectType; +import de.monticore.statements.mcvardeclarationstatements._symboltable.MCVarDeclarationStatementsSTCompleteTypes; +import de.monticore.statements.testmcvardeclarationstatements.TestMCVarDeclarationStatementsMill; +import de.monticore.statements.testmcvardeclarationstatements._ast.ASTRootVarDeclaration; +import de.monticore.statements.testmcvardeclarationstatements._cocos.TestMCVarDeclarationStatementsCoCoChecker; +import de.monticore.statements.testmcvardeclarationstatements._parser.TestMCVarDeclarationStatementsParser; +import de.monticore.statements.testmcvardeclarationstatements._visitor.TestMCVarDeclarationStatementsTraverser; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.oosymbols._symboltable.OOTypeSymbol; +import de.monticore.types.check.FullDeriveFromCombineExpressionsWithLiterals; +import de.se_rwth.commons.logging.Finding; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import static org.junit.Assert.assertEquals; + +public class VarDeclarationInitializationHasCorrectTypeTest { + + protected TestMCVarDeclarationStatementsCoCoChecker checker; + protected TestMCVarDeclarationStatementsParser parser; + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestMCVarDeclarationStatementsMill.reset(); + TestMCVarDeclarationStatementsMill.init(); + + TestMCVarDeclarationStatementsMill.globalScope().clear(); + BasicSymbolsMill.initializePrimitives(); + addMyTypeToGlobalScope(); + addStringToGlobalScope(); + + checker = new TestMCVarDeclarationStatementsCoCoChecker(); + checker.setTraverser(TestMCVarDeclarationStatementsMill.traverser()); + checker.addCoCo(new VarDeclarationInitializationHasCorrectType(new FullDeriveFromCombineExpressionsWithLiterals())); + parser = new TestMCVarDeclarationStatementsParser(); + } + + protected static void addMyTypeToGlobalScope() { + OOTypeSymbol type = TestMCVarDeclarationStatementsMill.oOTypeSymbolBuilder() + .setName("MyType") + .setSpannedScope(TestMCVarDeclarationStatementsMill.scope()) + .build(); + TestMCVarDeclarationStatementsMill.globalScope().add(type); + TestMCVarDeclarationStatementsMill.globalScope().addSubScope(type.getSpannedScope()); + } + + protected static void addStringToGlobalScope() { + OOTypeSymbol type = TestMCVarDeclarationStatementsMill.oOTypeSymbolBuilder() + .setName("String") + .setSpannedScope(TestMCVarDeclarationStatementsMill.scope()) + .build(); + TestMCVarDeclarationStatementsMill.globalScope().add(type); + TestMCVarDeclarationStatementsMill.globalScope().addSubScope(type.getSpannedScope()); + } + + protected void checkExpectedErrors(ASTRootVarDeclaration decl, List expectedErrorCodes) { + TestMCVarDeclarationStatementsMill.scopesGenitorDelegator().createFromAST(decl); + TestMCVarDeclarationStatementsTraverser completerTraverser = TestMCVarDeclarationStatementsMill.traverser(); + completerTraverser.add4MCVarDeclarationStatements(new MCVarDeclarationStatementsSTCompleteTypes()); + decl.accept(completerTraverser); + // We must manually set a name for the ArtifactScope. Else we get an exception. + decl.getEnclosingScope().setName("Foo"); + + // When + checker.checkAll(decl); + + // Then + List actualErrors = Log.getFindings().stream() + .filter(Finding::isError) + .map(err -> err.getMsg().split(" ")[0]) + .collect(Collectors.toList()); + assertEquals(expectedErrorCodes, actualErrors); + } + + @Test + public void testValidMultiVarDeclaration() throws IOException { + // Given + String multiVarDeclaration = "int a = 10, b, c = -12;"; + List expectedErrors = new ArrayList<>(); + ASTRootVarDeclaration astDecl = parser.parse_StringRootVarDeclaration(multiVarDeclaration).get(); + + // When & Then + checkExpectedErrors(astDecl, expectedErrors); + } + + @Test + public void testInvalidMultiVarDeclaration() throws IOException { + // Given + String multiVarDeclaration = "int a = \"oh no\", b = 10, c, d = \"no no no\";"; + List expectedErrors = Lists.newArrayList( + VarDeclarationInitializationHasCorrectType.ERROR_CODE, + VarDeclarationInitializationHasCorrectType.ERROR_CODE + ); + ASTRootVarDeclaration astDecl = parser.parse_StringRootVarDeclaration(multiVarDeclaration).get(); + + // When & Then + checkExpectedErrors(astDecl, expectedErrors); + } + + @Test + public void testInvalidMultiVarDeclarationWithTypeReference() throws IOException { + // Given + String multiVarDeclaration = "int a = 3, b, c = MyType, d = \"no no no\";"; + List expectedErrors = Lists.newArrayList( + VarDeclarationInitializationHasCorrectType.TYPE_REF_ASSIGNMENT_ERROR_CODE, + VarDeclarationInitializationHasCorrectType.ERROR_CODE + ); + ASTRootVarDeclaration astDecl = parser.parse_StringRootVarDeclaration(multiVarDeclaration).get(); + + // When & Then + checkExpectedErrors(astDecl, expectedErrors); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/statements/cocos/WhileConditionHasBooleanTypeTest.java b/monticore-grammar/src/test/java/de/monticore/statements/cocos/WhileConditionHasBooleanTypeTest.java new file mode 100644 index 0000000000..bc3bf93010 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/statements/cocos/WhileConditionHasBooleanTypeTest.java @@ -0,0 +1,72 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements.cocos; + +import de.monticore.statements.mccommonstatements.cocos.WhileConditionHasBooleanType; +import de.monticore.statements.mcstatementsbasis._ast.ASTMCBlockStatement; +import de.monticore.statements.testmccommonstatements.TestMCCommonStatementsMill; +import de.monticore.statements.testmccommonstatements._cocos.TestMCCommonStatementsCoCoChecker; +import de.monticore.statements.testmccommonstatements._parser.TestMCCommonStatementsParser; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.types.check.FullDeriveFromCombineExpressionsWithLiterals; +import de.monticore.types.check.TypeCalculator; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class WhileConditionHasBooleanTypeTest { + + protected TestMCCommonStatementsCoCoChecker checker; + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestMCCommonStatementsMill.reset(); + TestMCCommonStatementsMill.init(); + BasicSymbolsMill.initializePrimitives(); + checker = new TestMCCommonStatementsCoCoChecker(); + checker.addCoCo(new WhileConditionHasBooleanType(new TypeCalculator(null,new FullDeriveFromCombineExpressionsWithLiterals()))); + } + + public void checkValid(String expressionString) throws IOException { + TestMCCommonStatementsParser parser = new TestMCCommonStatementsParser(); + Optional optAST = parser.parse_StringMCBlockStatement(expressionString); + assertTrue(optAST.isPresent()); + Log.getFindings().clear(); + checker.checkAll(optAST.get()); + assertTrue(Log.getFindings().isEmpty()); + } + + public void checkInvalid(String expressionString) throws IOException { + TestMCCommonStatementsParser parser = new TestMCCommonStatementsParser(); + Optional optAST = parser.parse_StringMCBlockStatement(expressionString); + assertTrue(optAST.isPresent()); + Log.getFindings().clear(); + checker.checkAll(optAST.get()); + assertFalse(Log.getFindings().isEmpty()); + } + + @Test + public void testValid() throws IOException{ + checkValid("while(true){}"); + checkValid("while(1<2){}"); + checkValid("while(!true&&(5==6)){}"); + checkValid("while((1<2)||(5%2==1)){}"); + } + + @Test + public void testInvalid()throws IOException{ + checkInvalid("while(1+1){}"); + checkInvalid("while('c'+10){}"); + checkInvalid("while(1.2-5.5){}"); + } + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/java/de/monticore/statements/prettyprint/MCArrayStatementsPrettyPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/statements/prettyprint/MCArrayStatementsPrettyPrinterTest.java new file mode 100644 index 0000000000..a922e4b1df --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/statements/prettyprint/MCArrayStatementsPrettyPrinterTest.java @@ -0,0 +1,74 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements.prettyprint; + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.statements.mcarraystatements._ast.ASTArrayDeclaratorId; +import de.monticore.statements.mcarraystatements._ast.ASTArrayInit; +import de.monticore.statements.mcarraystatements._prettyprint.MCArrayStatementsFullPrettyPrinter; +import de.monticore.statements.testmcarraystatements.TestMCArrayStatementsMill; +import de.monticore.statements.testmcarraystatements._parser.TestMCArrayStatementsParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class MCArrayStatementsPrettyPrinterTest { + + private TestMCArrayStatementsParser parser = new TestMCArrayStatementsParser(); + + private MCArrayStatementsFullPrettyPrinter prettyPrinter = new MCArrayStatementsFullPrettyPrinter(new IndentPrinter()); + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestMCArrayStatementsMill.reset(); + TestMCArrayStatementsMill.init(); + prettyPrinter.getPrinter().clearBuffer(); + } + + + @Test + public void testArrayInit() throws IOException { + Optional result = parser.parse_StringArrayInit("{a, b, foo}"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTArrayInit ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringArrayInit(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testArrayDeclaratorId() throws IOException { + Optional result = parser.parse_StringArrayDeclaratorId("a [] []"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTArrayDeclaratorId ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringArrayDeclaratorId(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/statements/prettyprint/MCAssertStatementsPrettyPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/statements/prettyprint/MCAssertStatementsPrettyPrinterTest.java new file mode 100644 index 0000000000..c9713616e5 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/statements/prettyprint/MCAssertStatementsPrettyPrinterTest.java @@ -0,0 +1,53 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements.prettyprint; + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.statements.mcassertstatements._ast.ASTAssertStatement; +import de.monticore.statements.mcassertstatements._prettyprint.MCAssertStatementsFullPrettyPrinter; +import de.monticore.statements.testmcassertstatements.TestMCAssertStatementsMill; +import de.monticore.statements.testmcassertstatements._parser.TestMCAssertStatementsParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class MCAssertStatementsPrettyPrinterTest { + + private TestMCAssertStatementsParser parser = new TestMCAssertStatementsParser(); + + private MCAssertStatementsFullPrettyPrinter prettyPrinter= new MCAssertStatementsFullPrettyPrinter(new IndentPrinter()); + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestMCAssertStatementsMill.reset(); + TestMCAssertStatementsMill.init(); + prettyPrinter.getPrinter().clearBuffer(); + } + + @Test + public void testAssertStatement() throws IOException { + Optional result = parser.parse_StringAssertStatement("assert a : b;"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTAssertStatement ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringAssertStatement(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/statements/prettyprint/MCCommonStatementsPrettyPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/statements/prettyprint/MCCommonStatementsPrettyPrinterTest.java new file mode 100644 index 0000000000..a5529ac7a5 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/statements/prettyprint/MCCommonStatementsPrettyPrinterTest.java @@ -0,0 +1,418 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements.prettyprint; + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.statements.mccommonstatements._ast.*; +import de.monticore.statements.mccommonstatements._prettyprint.MCCommonStatementsFullPrettyPrinter; +import de.monticore.statements.mcstatementsbasis._ast.ASTMCBlockStatement; +import de.monticore.statements.mcvardeclarationstatements._ast.ASTDeclaratorId; +import de.monticore.statements.mcvardeclarationstatements._ast.ASTLocalVariableDeclaration; +import de.monticore.statements.mcvardeclarationstatements._ast.ASTVariableDeclarator; +import de.monticore.statements.testmccommonstatements.TestMCCommonStatementsMill; +import de.monticore.statements.testmccommonstatements._parser.TestMCCommonStatementsParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class MCCommonStatementsPrettyPrinterTest { + + private TestMCCommonStatementsParser parser = new TestMCCommonStatementsParser(); + + private MCCommonStatementsFullPrettyPrinter prettyPrinter= new MCCommonStatementsFullPrettyPrinter(new IndentPrinter()); + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestMCCommonStatementsMill.reset(); + TestMCCommonStatementsMill.init(); + prettyPrinter.getPrinter().clearBuffer(); + } + + @Test + public void testBlock() throws IOException { + Optional result = parser.parse_StringMCJavaBlock("{ private Integer foo = a; }"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTMCJavaBlock ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringMCJavaBlock(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testBlockStatement() throws IOException { + Optional result = parser.parse_StringMCBlockStatement("private Integer foo = a;"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTMCBlockStatement ast = result.get(); + + ast.accept(prettyPrinter.getTraverser()); + String output = prettyPrinter.getPrinter().getContent(); + + result = parser.parse_StringMCBlockStatement(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testLocalVariableDeclaration() throws IOException { + Optional result = parser.parse_StringLocalVariableDeclaration("private Integer foo = a"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTLocalVariableDeclaration ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringLocalVariableDeclaration(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testVariableDeclaration() throws IOException { + Optional result = parser.parse_StringVariableDeclarator("foo = a"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTVariableDeclarator ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringVariableDeclarator(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testDeclaratorId() throws IOException { + Optional result = parser.parse_StringDeclaratorId("a"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTDeclaratorId ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringDeclaratorId(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testIfStatement() throws IOException { + Optional result = parser.parse_StringIfStatement("if(a) ; else ;"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTIfStatement ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringIfStatement(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testForStatement() throws IOException { + Optional result = parser.parse_StringForStatement("for(i; i ; i) a;"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTForStatement ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringForStatement(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCommonForControl() throws IOException { + Optional result = parser.parse_StringCommonForControl("i; i ; i"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTCommonForControl ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringCommonForControl(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testEnhancedForControl() throws IOException { + Optional result = parser.parse_StringEnhancedForControl("protected List l : a"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTEnhancedForControl ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringEnhancedForControl(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testFormalParameter() throws IOException { + Optional result = parser.parse_StringFormalParameter("public float f"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTFormalParameter ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringFormalParameter(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testForInitByExpressions() throws IOException { + Optional result = parser.parse_StringForInitByExpressions("i, b, c"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTForInitByExpressions ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringForInitByExpressions(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testWhileStatement() throws IOException { + Optional result = parser.parse_StringWhileStatement("while (a) ;"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTWhileStatement ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringWhileStatement(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testDoWhileStatement() throws IOException { + Optional result = parser.parse_StringDoWhileStatement("do ; while (a);"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTDoWhileStatement ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringDoWhileStatement(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testSwitchStatement() throws IOException { + Optional result = parser.parse_StringSwitchStatement("switch (a) {case b : c; default : d;}"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTSwitchStatement ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringSwitchStatement(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testEmptyStatement() throws IOException { + Optional result = parser.parse_StringEmptyStatement(";"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTEmptyStatement ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringEmptyStatement(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testExpressionStatement() throws IOException { + Optional result = parser.parse_StringExpressionStatement("a;"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTExpressionStatement ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringExpressionStatement(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testSwitchBlockStatementGroup() throws IOException { + Optional result = parser.parse_StringSwitchBlockStatementGroup("case a: foo;"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTSwitchBlockStatementGroup ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringSwitchBlockStatementGroup(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testConstantExpressionSwitchLabel() throws IOException { + Optional result = parser.parse_StringConstantExpressionSwitchLabel("case a :"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTConstantExpressionSwitchLabel ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringConstantExpressionSwitchLabel(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testEnumConstantSwitchLabel() throws IOException { + Optional result = parser.parse_StringEnumConstantSwitchLabel("case a :"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTEnumConstantSwitchLabel ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringEnumConstantSwitchLabel(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testDefaultSwitchLabel() throws IOException { + Optional result = parser.parse_StringDefaultSwitchLabel("default :"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTDefaultSwitchLabel ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringDefaultSwitchLabel(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testBreakStatement() throws IOException { + Optional result = parser.parse_StringBreakStatement("break ;"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTBreakStatement ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringBreakStatement(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/statements/prettyprint/MCExceptionStatementsPrettyPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/statements/prettyprint/MCExceptionStatementsPrettyPrinterTest.java new file mode 100644 index 0000000000..950cc24116 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/statements/prettyprint/MCExceptionStatementsPrettyPrinterTest.java @@ -0,0 +1,168 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements.prettyprint; + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.statements.mcexceptionstatements._ast.*; +import de.monticore.statements.mcexceptionstatements._prettyprint.MCExceptionStatementsFullPrettyPrinter; +import de.monticore.statements.testmcexceptionstatements.TestMCExceptionStatementsMill; +import de.monticore.statements.testmcexceptionstatements._parser.TestMCExceptionStatementsParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class MCExceptionStatementsPrettyPrinterTest { + + private TestMCExceptionStatementsParser parser = new TestMCExceptionStatementsParser(); + + private MCExceptionStatementsFullPrettyPrinter prettyPrinter = new MCExceptionStatementsFullPrettyPrinter(new IndentPrinter()); + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestMCExceptionStatementsMill.reset(); + TestMCExceptionStatementsMill.init(); + prettyPrinter.getPrinter().clearBuffer(); + } + + @Test + public void testTryStatement2() throws IOException { + Optional result = parser.parse_StringTryStatement2(" try { Integer foo = a;} finally { Integer foo = a; }"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTTryStatement2 ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringTryStatement2(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testTryStatement1() throws IOException { + Optional result = parser.parse_StringTryStatement1(" try { Integer foo = a; } catch (private static a.b.c | d.e.G foo) {Integer foo = a; }"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTTryStatement1 ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringTryStatement1(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void testTryStatements() throws IOException { + Optional result = parser.parse_StringTryStatement3("try ( public Integer a = foo; ) " + + "{ public String foo = a ;} " + + "catch (private static a.b.c | d.e.G foo) { public String foo = a ;}"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTTryStatement3 ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringTryStatement3(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void testTryvariableDeclaration() throws IOException { + Optional result = parser.parse_StringTryLocalVariableDeclaration("public Integer a = foo"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTTryLocalVariableDeclaration ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringTryLocalVariableDeclaration(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void testCatchClause() throws IOException { + Optional result = parser.parse_StringCatchClause("catch (private static a.b.c | d.e.G foo) { public String foo = a; }"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTCatchClause ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringCatchClause(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void testCatchType() throws IOException { + Optional result = parser.parse_StringCatchTypeList(" a.b.c | d.e.G "); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTCatchTypeList ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringCatchTypeList(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void testThrowStatement() throws IOException { + Optional result = parser.parse_StringThrowStatement("throw Exception;"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTThrowStatement ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringThrowStatement(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/statements/prettyprint/MCLowLevelStatementsPrettyPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/statements/prettyprint/MCLowLevelStatementsPrettyPrinterTest.java new file mode 100644 index 0000000000..231cf94fd4 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/statements/prettyprint/MCLowLevelStatementsPrettyPrinterTest.java @@ -0,0 +1,91 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements.prettyprint; + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.statements.mclowlevelstatements._ast.ASTLabelledBreakStatement; +import de.monticore.statements.mclowlevelstatements._ast.ASTContinueStatement; +import de.monticore.statements.mclowlevelstatements._ast.ASTLabel; +import de.monticore.statements.mclowlevelstatements._prettyprint.MCLowLevelStatementsFullPrettyPrinter; +import de.monticore.statements.testmclowlevelstatements.TestMCLowLevelStatementsMill; +import de.monticore.statements.testmclowlevelstatements._parser.TestMCLowLevelStatementsParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class MCLowLevelStatementsPrettyPrinterTest { + + private TestMCLowLevelStatementsParser parser = new TestMCLowLevelStatementsParser(); + + private MCLowLevelStatementsFullPrettyPrinter prettyPrinter = new MCLowLevelStatementsFullPrettyPrinter(new IndentPrinter()); + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestMCLowLevelStatementsMill.reset(); + TestMCLowLevelStatementsMill.init(); + prettyPrinter.getPrinter().clearBuffer(); + } + + @Test + public void testBreakStatement() throws IOException { + Optional result = parser.parse_StringLabelledBreakStatement("break a ;"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTLabelledBreakStatement ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringLabelledBreakStatement(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testLabeledStatement() throws IOException { + Optional result = parser.parse_StringLabel("a : break foo;"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTLabel ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringLabel(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testContinueStatement() throws IOException { + Optional result = parser.parse_StringContinueStatement("continue foo;"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTContinueStatement ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringContinueStatement(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/statements/prettyprint/MCReturnStatementsPrettyPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/statements/prettyprint/MCReturnStatementsPrettyPrinterTest.java new file mode 100644 index 0000000000..998c5d303d --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/statements/prettyprint/MCReturnStatementsPrettyPrinterTest.java @@ -0,0 +1,53 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements.prettyprint; + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.statements.mcreturnstatements._ast.ASTReturnStatement; +import de.monticore.statements.mcreturnstatements._prettyprint.MCReturnStatementsFullPrettyPrinter; +import de.monticore.statements.testmcreturnstatements.TestMCReturnStatementsMill; +import de.monticore.statements.testmcreturnstatements._parser.TestMCReturnStatementsParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class MCReturnStatementsPrettyPrinterTest { + + private TestMCReturnStatementsParser parser = new TestMCReturnStatementsParser(); + + private MCReturnStatementsFullPrettyPrinter prettyPrinter = new MCReturnStatementsFullPrettyPrinter(new IndentPrinter()); + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestMCReturnStatementsMill.reset(); + TestMCReturnStatementsMill.init(); + prettyPrinter.getPrinter().clearBuffer(); + } + + @Test + public void testReturnStatement() throws IOException { + Optional result = parser.parse_StringReturnStatement("return a ;"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTReturnStatement ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringReturnStatement(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/statements/prettyprint/MCSynchronizedStatementsPrettyPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/statements/prettyprint/MCSynchronizedStatementsPrettyPrinterTest.java new file mode 100644 index 0000000000..bc1af36263 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/statements/prettyprint/MCSynchronizedStatementsPrettyPrinterTest.java @@ -0,0 +1,54 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements.prettyprint; + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.statements.mcsynchronizedstatements._ast.ASTSynchronizedStatement; +import de.monticore.statements.mcsynchronizedstatements._prettyprint.MCSynchronizedStatementsFullPrettyPrinter; +import de.monticore.statements.testmcsynchronizedstatements.TestMCSynchronizedStatementsMill; +import de.monticore.statements.testmcsynchronizedstatements._parser.TestMCSynchronizedStatementsParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class MCSynchronizedStatementsPrettyPrinterTest { + + private TestMCSynchronizedStatementsParser parser = new TestMCSynchronizedStatementsParser(); + + private MCSynchronizedStatementsFullPrettyPrinter prettyPrinter = new MCSynchronizedStatementsFullPrettyPrinter(new IndentPrinter()); + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestMCSynchronizedStatementsMill.reset(); + TestMCSynchronizedStatementsMill.init(); + prettyPrinter.getPrinter().clearBuffer(); + } + + @Test + public void testReturnStatement() throws IOException { + Optional result = parser.parse_StringSynchronizedStatement("synchronized (foo) { final Integer foo = a ;}"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTSynchronizedStatement ast = result.get(); + + String output = prettyPrinter.prettyprint(ast); + + result = parser.parse_StringSynchronizedStatement(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/statements/prettyprint/MCVarDeclarationStatementsPrettyPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/statements/prettyprint/MCVarDeclarationStatementsPrettyPrinterTest.java new file mode 100644 index 0000000000..64c1551758 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/statements/prettyprint/MCVarDeclarationStatementsPrettyPrinterTest.java @@ -0,0 +1,55 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.statements.prettyprint; + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.statements.mcvardeclarationstatements._ast.ASTLocalVariableDeclaration; +import de.monticore.statements.mcvardeclarationstatements._prettyprint.MCVarDeclarationStatementsFullPrettyPrinter; +import de.monticore.statements.testmcvardeclarationstatements.TestMCVarDeclarationStatementsMill; +import de.monticore.statements.testmcvardeclarationstatements._parser.TestMCVarDeclarationStatementsParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class MCVarDeclarationStatementsPrettyPrinterTest { + + private TestMCVarDeclarationStatementsParser parser = new TestMCVarDeclarationStatementsParser(); + + private MCVarDeclarationStatementsFullPrettyPrinter prettyPrinter = new MCVarDeclarationStatementsFullPrettyPrinter(new IndentPrinter()); + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestMCVarDeclarationStatementsMill.reset(); + TestMCVarDeclarationStatementsMill.init(); + prettyPrinter.getPrinter().clearBuffer(); + } + + @Test + public void testLocalVariableDeclaration() throws IOException { + Optional result = parser.parse_StringLocalVariableDeclaration("List a = b, c = d"); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + ASTLocalVariableDeclaration ast = result.get(); + + ast.accept(prettyPrinter.getTraverser()); + String output = prettyPrinter.getPrinter().getContent(); + + result = parser.parse_StringLocalVariableDeclaration(output); + assertFalse(parser.hasErrors()); + assertTrue(result.isPresent()); + + assertTrue(ast.deepEquals(result.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/symbols/basicsymbols/_symboltable/BasicSymbolsSymbols2JsonTest.java b/monticore-grammar/src/test/java/de/monticore/symbols/basicsymbols/_symboltable/BasicSymbolsSymbols2JsonTest.java new file mode 100644 index 0000000000..ed7cfc0ce4 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/symbols/basicsymbols/_symboltable/BasicSymbolsSymbols2JsonTest.java @@ -0,0 +1,173 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symbols.basicsymbols._symboltable; + + +import com.google.common.collect.Lists; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.util.Optional; + +import static org.junit.Assert.*; + +public class BasicSymbolsSymbols2JsonTest { + + private IBasicSymbolsArtifactScope scope; + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + + //initialize scope, add some TypeSymbols, TypeVarSymbols, VariableSymbols and FunctionSymbols + BasicSymbolsMill.reset(); + BasicSymbolsMill.init(); + BasicSymbolsMill.initializePrimitives(); + + scope = BasicSymbolsMill.artifactScope(); + scope.setPackageName(""); + scope.setImportsList(Lists.newArrayList()); + scope.setName("Test"); + + IBasicSymbolsScope typeSpannedScope = BasicSymbolsMill.scope(); + + //put type into main scope + TypeSymbol type = BasicSymbolsMill.typeSymbolBuilder() + .setName("Type") + .setSpannedScope(typeSpannedScope) + .setEnclosingScope(scope) + .build(); + + SymTypeExpression symType1 = SymTypeExpressionFactory.createTypeObject("Type", scope); + + //put subtype into main scope, test if supertypes are serialized correctly + TypeSymbol subtype = BasicSymbolsMill.typeSymbolBuilder() + .setName("SubType") + .setSpannedScope(BasicSymbolsMill.scope()) + .setEnclosingScope(scope) + .setSuperTypesList(Lists.newArrayList(symType1)) + .build(); + + //put TypeVariable T into spanned scope of type + TypeVarSymbol t = BasicSymbolsMill.typeVarSymbolBuilder() + .setName("T") + .setEnclosingScope(type.getSpannedScope()) + .setSpannedScope(BasicSymbolsMill.scope()) + .build(); + + typeSpannedScope.add(t); + + //put Variable variable into spanned scope of type + VariableSymbol variable = BasicSymbolsMill.variableSymbolBuilder() + .setName("variable") + .setEnclosingScope(type.getSpannedScope()) + .setType(SymTypeExpressionFactory.createPrimitive("double")) + .build(); + + typeSpannedScope.add(variable); + + //put Function function into spanned scope of type + FunctionSymbol function = BasicSymbolsMill.functionSymbolBuilder() + .setName("function") + .setEnclosingScope(type.getSpannedScope()) + .setSpannedScope(BasicSymbolsMill.scope()) + .setType(SymTypeExpressionFactory.createPrimitive("int")) + .build(); + + function.setSpannedScope(BasicSymbolsMill.scope()); + + typeSpannedScope.add(function); + + scope.add(type); + scope.add(subtype); + } + + @Test + public void testDeSer(){ + performRoundTripSerialization(scope); + + assertTrue(Log.getFindings().isEmpty()); + } + + public void performRoundTripSerialization(IBasicSymbolsArtifactScope scope){ + //first serialize the scope using the symbols2json + BasicSymbolsSymbols2Json symbols2Json = new BasicSymbolsSymbols2Json(); + String serialized = symbols2Json.serialize(scope); + // then deserialize it + IBasicSymbolsArtifactScope deserialized = symbols2Json.deserialize(serialized); + assertNotNull(deserialized); + // and assert that the deserialized scope equals the one before + //check that both can resolve the type "Type" + + Optional type = scope.resolveType("Type"); + Optional deserializedType = deserialized.resolveType("Type"); + assertTrue(type.isPresent()); + assertTrue(deserializedType.isPresent()); + + //check that both can resolve the type "SubType" with the supertype "Type" + Optional subtype = scope.resolveType("SubType"); + Optional deserializedSubType = deserialized.resolveType("SubType"); + assertTrue(subtype.isPresent()); + assertTrue(deserializedSubType.isPresent()); + assertEquals(1, subtype.get().getSuperTypesList().size()); + assertEquals(1, deserializedSubType.get().getSuperTypesList().size()); + assertEquals("Type", subtype.get().getSuperTypesList().get(0).print()); + assertEquals("Type", deserializedSubType.get().getSuperTypesList().get(0).print()); + + IBasicSymbolsScope typeSpanned = type.get().getSpannedScope(); + IBasicSymbolsScope deserTypeSpanned = deserializedType.get().getSpannedScope(); + + //check that both can resolve the TypeVariable "T" in their "Type" + assertTrue(typeSpanned.resolveTypeVar("T").isPresent()); + assertTrue(deserTypeSpanned.resolveTypeVar("T").isPresent()); + + //check for Variable variable in Type + Optional variable = typeSpanned.resolveVariable("variable"); + Optional deserializedVariable = deserTypeSpanned.resolveVariable("variable"); + assertTrue(variable.isPresent()); + assertTrue(deserializedVariable.isPresent()); + assertEquals("double", variable.get().getType().print()); + assertEquals("double", deserializedVariable.get().getType().print()); + + //check for Function function in Type + Optional function = typeSpanned.resolveFunction("function"); + Optional deserializedFunction = deserTypeSpanned.resolveFunction("function"); + assertTrue(function.isPresent()); + assertTrue(deserializedFunction.isPresent()); + assertEquals("int", function.get().getType().print()); + assertEquals("int", deserializedFunction.get().getType().print()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testSerializedUnknownKind() { + BasicSymbolsSymbols2Json symbols2Json = new BasicSymbolsSymbols2Json(); + symbols2Json.deserialize("{\"symbols\": [{\"kind\":\"unknown\", \"name\":\"test\"}]}"); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testInvalidJsonForSerializingReturnsError(){ + String invalidJsonForSerializing = "{\n\t\"symbols\":[{\"noKind\":true}]}\n}"; + String invalidJsonForSerializing2 = "{\"symbols\": [\"SymbolIsNotAnObject\"]}"; + String invalidJsonForSerializing3 = "{\"symbols\": [{\"kind\":\"unknown\"}]}"; + + BasicSymbolsSymbols2Json symbols2Json = new BasicSymbolsSymbols2Json(); + symbols2Json.deserialize(invalidJsonForSerializing); + assertTrue(Log.getFindings().get(0).getMsg().startsWith("0xA1238")); + + symbols2Json.deserialize(invalidJsonForSerializing2); + assertTrue(Log.getFindings().get(1).getMsg().startsWith("0xA1233")); + + symbols2Json.deserialize(invalidJsonForSerializing3); + assertTrue(Log.getFindings().get(2).getMsg().startsWith("0xA0572")); + } + + +} diff --git a/monticore-grammar/src/test/java/de/monticore/symbols/library/StreamTypeTest.java b/monticore-grammar/src/test/java/de/monticore/symbols/library/StreamTypeTest.java new file mode 100644 index 0000000000..f875636c27 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/symbols/library/StreamTypeTest.java @@ -0,0 +1,107 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.symbols.library; + +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.FunctionSymbol; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeOfGenerics; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.util.Optional; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class StreamTypeTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + + BasicSymbolsMill.reset(); + BasicSymbolsMill.init(); + BasicSymbolsMill.initializePrimitives(); + new StreamType().addStreamType(); + } + + @Test + public void resolveStreamType() { + Optional streamOpt = BasicSymbolsMill.globalScope() + .resolveType(StreamType.STREAM_TYPE_NAME); + assertTrue(streamOpt.isPresent()); + TypeSymbol stream = streamOpt.get(); + assertNotNull(stream.getSpannedScope()); + assertEquals(1, stream.getSpannedScope().getTypeVarSymbols().size()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void resolveStreamFunctionEmpty() { + FunctionSymbol function = resolveFunction("emptyStream"); + assertTrue(function.getParameterList().isEmpty()); + assertIsStreamWithTypeVar(function.getType()); + } + + @Test + public void resolveStreamFunctionAppendFirst() { + FunctionSymbol function = resolveFunction("appendFirst"); + assertEquals(2, function.getParameterList().size()); + assertTrue(function.getParameterList().get(0).getType().isTypeVariable()); + assertIsStreamWithTypeVar(function.getParameterList().get(1).getType()); + assertIsStreamWithTypeVar(function.getType()); + } + + @Test + public void resolveStreamFunctionLen() { + FunctionSymbol function = resolveFunction("len"); + assertEquals(1, function.getParameterList().size()); + assertTrue(function.getParameterList().get(0).getType().isGenericType()); + assertEquals(StreamType.STREAM_TYPE_NAME, + function.getParameterList().get(0).getType().getTypeInfo().getName()); + assertEquals(0, + ((SymTypeOfGenerics) function.getParameterList().get(0).getType()) + .getArgumentList().size()); + assertEquals(BasicSymbolsMill.LONG, function.getType().getTypeInfo().getName()); + } + + @Test + public void resolveStreamFunctions() { + resolveFunction("emptyStream"); + resolveFunction("appendFirst"); + resolveFunction("conc"); + resolveFunction("len"); + resolveFunction("first"); + resolveFunction("dropFirst"); + resolveFunction("nth"); + resolveFunction("take"); + resolveFunction("drop"); + assertEquals(2, BasicSymbolsMill.globalScope().resolveFunctionMany("times").size()); + resolveFunction("map"); + resolveFunction("iterate"); + resolveFunction("filter"); + resolveFunction("takeWhile"); + resolveFunction("dropWhile"); + resolveFunction("rcDups"); + } + + protected void assertIsStreamWithTypeVar(SymTypeExpression type) { + assertNotNull(type); + assertTrue(type.isGenericType()); + assertEquals(StreamType.STREAM_TYPE_NAME, type.getTypeInfo().getName()); + assertEquals(1, ((SymTypeOfGenerics) type).getArgumentList().size()); + assertTrue(((SymTypeOfGenerics) type).getArgument(0).isTypeVariable()); + } + + protected FunctionSymbol resolveFunction(String name) { + Optional emptyOpt = BasicSymbolsMill.globalScope().resolveFunction(name); + assertTrue(emptyOpt.isPresent()); + return emptyOpt.get(); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/symbols/oosymbols/_symboltable/ModifierTest.java b/monticore-grammar/src/test/java/de/monticore/symbols/oosymbols/_symboltable/ModifierTest.java new file mode 100644 index 0000000000..6a5a55d848 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/symbols/oosymbols/_symboltable/ModifierTest.java @@ -0,0 +1,166 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symbols.oosymbols._symboltable; + +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.oosymbols.OOSymbolsMill; +import de.monticore.symboltable.modifiers.BasicAccessModifier; +import de.monticore.symboltable.modifiers.CompoundAccessModifier; +import de.monticore.symboltable.modifiers.StaticAccessModifier; +import de.monticore.types.check.DefsTypeBasic; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.List; + +import static org.junit.Assert.*; + +public class ModifierTest { + + protected IOOSymbolsScope symbolTable; + + @Before + public void init(){ + LogStub.init(); + Log.enableFailQuick(false); + + OOSymbolsMill.reset(); + OOSymbolsMill.init(); + BasicSymbolsMill.initializePrimitives(); + + symbolTable = OOSymbolsMill.scope(); + + //FieldSymbols + FieldSymbol barPackagePrivate = DefsTypeBasic.field("bar", SymTypeExpressionFactory.createPrimitive("int")); + FieldSymbol barPublic = DefsTypeBasic.field("bar", SymTypeExpressionFactory.createPrimitive("int")); + barPublic.setIsPublic(true); + FieldSymbol barProtected = DefsTypeBasic.field("bar", SymTypeExpressionFactory.createPrimitive("int")); + barProtected.setIsProtected(true); + FieldSymbol barPrivate = DefsTypeBasic.field("bar", SymTypeExpressionFactory.createPrimitive("int")); + barPrivate.setIsPrivate(true); + FieldSymbol barStatic = DefsTypeBasic.field("bar", SymTypeExpressionFactory.createPrimitive("int")); + barStatic.setIsStatic(true); + FieldSymbol barPublicStatic = DefsTypeBasic.field("bar", SymTypeExpressionFactory.createPrimitive("int")); + barPublicStatic.setIsPublic(true); + barPublicStatic.setIsStatic(true); + + symbolTable.add(barPackagePrivate); + symbolTable.add(barPublic); + symbolTable.add(barProtected); + symbolTable.add(barPrivate); + symbolTable.add(barStatic); + symbolTable.add(barPublicStatic); + + //MethodSymbols + MethodSymbol fooPackagePrivate = DefsTypeBasic.method("foo", SymTypeExpressionFactory.createPrimitive("int")); + MethodSymbol fooPublic = DefsTypeBasic.method("foo", SymTypeExpressionFactory.createPrimitive("int")); + fooPublic.setIsPublic(true); + MethodSymbol fooProtected = DefsTypeBasic.method("foo", SymTypeExpressionFactory.createPrimitive("int")); + fooProtected.setIsProtected(true); + MethodSymbol fooPrivate = DefsTypeBasic.method("foo", SymTypeExpressionFactory.createPrimitive("int")); + fooPrivate.setIsPrivate(true); + MethodSymbol fooStatic = DefsTypeBasic.method("foo", SymTypeExpressionFactory.createPrimitive("int")); + fooPrivate.setIsStatic(true); + MethodSymbol fooProtectedStatic = DefsTypeBasic.method("foo", SymTypeExpressionFactory.createPrimitive("int")); + fooProtectedStatic.setIsProtected(true); + fooProtectedStatic.setIsStatic(true); + + symbolTable.add(fooPackagePrivate); + symbolTable.add(fooPublic); + symbolTable.add(fooProtected); + symbolTable.add(fooPrivate); + symbolTable.add(fooStatic); + symbolTable.add(fooProtectedStatic); + + //TypeSymbols + OOTypeSymbol testPackagePrivate = DefsTypeBasic.type("Test"); + OOTypeSymbol testPublic = DefsTypeBasic.type("Test"); + testPublic.setIsPublic(true); + OOTypeSymbol testProtected = DefsTypeBasic.type("Test"); + testProtected.setIsProtected(true); + OOTypeSymbol testPrivate = DefsTypeBasic.type("Test"); + testPrivate.setIsPrivate(true); + OOTypeSymbol testStatic = DefsTypeBasic.type("Test"); + testStatic.setIsStatic(true); + OOTypeSymbol testPrivateStatic = DefsTypeBasic.type("Test"); + testPrivateStatic.setIsPrivate(true); + testPrivateStatic.setIsStatic(true); + + symbolTable.add(testPackagePrivate); + symbolTable.add(testPublic); + symbolTable.add(testProtected); + symbolTable.add(testPrivate); + symbolTable.add(testStatic); + symbolTable.add(testPrivateStatic); + } + + @Test + public void testType(){ + List typesAllInclusion = symbolTable.resolveOOTypeMany("Test"); + assertEquals(6, typesAllInclusion.size()); + + List typesPublic = symbolTable.resolveOOTypeMany("Test", BasicAccessModifier.PUBLIC); + assertEquals(1, typesPublic.size()); + + List typesProtected = symbolTable.resolveOOTypeMany("Test", BasicAccessModifier.PROTECTED); + assertEquals(2, typesProtected.size()); + + List typesPrivate = symbolTable.resolveOOTypeMany("Test", BasicAccessModifier.PRIVATE); + assertEquals(6, typesPrivate.size()); + + List typesStatic = symbolTable.resolveOOTypeMany("Test", StaticAccessModifier.STATIC); + assertEquals(2, typesStatic.size()); + + List typesPrivateStatic = symbolTable + .resolveOOTypeMany("Test", new CompoundAccessModifier(List.of(BasicAccessModifier.PRIVATE, StaticAccessModifier.STATIC))); + assertEquals(2, typesPrivateStatic.size()); + } + + @Test + public void testMethod(){ + List methodsAllInclusion = symbolTable.resolveMethodMany("foo"); + assertEquals(6, methodsAllInclusion.size()); + + List methodsPublic = symbolTable.resolveMethodMany("foo", BasicAccessModifier.PUBLIC); + assertEquals(1, methodsPublic.size()); + + List methodsProtected = symbolTable.resolveMethodMany("foo", BasicAccessModifier.PROTECTED); + assertEquals(3, methodsProtected.size()); + + List methodsPrivate = symbolTable.resolveMethodMany("foo", BasicAccessModifier.PRIVATE); + assertEquals(6, methodsPrivate.size()); + + List methodsStatic = symbolTable.resolveMethodMany("foo", StaticAccessModifier.STATIC); + assertEquals(2, methodsStatic.size()); + + List methodsProtectedStatic = symbolTable + .resolveMethodMany("foo", new CompoundAccessModifier(List.of(BasicAccessModifier.PROTECTED, StaticAccessModifier.STATIC))); + assertEquals(1, methodsProtectedStatic.size()); + } + + @Test + public void testField(){ + List fieldsAllInclusion = symbolTable.resolveFieldMany("bar"); + assertEquals(6, fieldsAllInclusion.size()); + + List fieldsPublic = symbolTable.resolveFieldMany("bar", BasicAccessModifier.PUBLIC); + assertEquals(2, fieldsPublic.size()); + + List fieldsProtected = symbolTable.resolveFieldMany("bar", BasicAccessModifier.PROTECTED); + assertEquals(3, fieldsProtected.size()); + + List fieldsPrivate = symbolTable.resolveFieldMany("bar", BasicAccessModifier.PRIVATE); + assertEquals(6, fieldsPrivate.size()); + + List fieldsStatic = symbolTable.resolveFieldMany("bar", StaticAccessModifier.STATIC); + assertEquals(2, fieldsStatic.size()); + + List fieldsPublicStatic = symbolTable + .resolveFieldMany("bar", new CompoundAccessModifier(BasicAccessModifier.PUBLIC, StaticAccessModifier.STATIC)); + assertEquals(1, fieldsPublicStatic.size()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/symbols/oosymbols/_symboltable/OOSymbolsSymbols2JsonTest.java b/monticore-grammar/src/test/java/de/monticore/symbols/oosymbols/_symboltable/OOSymbolsSymbols2JsonTest.java new file mode 100644 index 0000000000..4a7698314d --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/symbols/oosymbols/_symboltable/OOSymbolsSymbols2JsonTest.java @@ -0,0 +1,164 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symbols.oosymbols._symboltable; + +import com.google.common.collect.Lists; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.oosymbols.OOSymbolsMill; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.Optional; + +import static org.junit.Assert.*; + +public class OOSymbolsSymbols2JsonTest { + + private IOOSymbolsArtifactScope scope; + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + + //initialize scope, add some TypeSymbols, TypeVarSymbols, VariableSymbols and FunctionSymbols + OOSymbolsMill.reset(); + OOSymbolsMill.init(); + BasicSymbolsMill.initializePrimitives(); + scope = OOSymbolsMill.artifactScope(); + scope.setPackageName(""); + scope.setImportsList(Lists.newArrayList()); + scope.setName("Test"); + + IOOSymbolsScope typeSpannedScope = OOSymbolsMill.scope(); + + //put type into main scope + OOTypeSymbol type = OOSymbolsMill.oOTypeSymbolBuilder() + .setName("Type") + .setSpannedScope(typeSpannedScope) + .setEnclosingScope(scope) + .build(); + + type.setSpannedScope(typeSpannedScope); + + SymTypeExpression symType1 = SymTypeExpressionFactory.createTypeObject("Type", scope); + + //put subtype into main scope, test if supertypes are serialized correctly + OOTypeSymbol subtype = OOSymbolsMill.oOTypeSymbolBuilder() + .setName("SubType") + .setSpannedScope(OOSymbolsMill.scope()) + .setEnclosingScope(scope) + .setSuperTypesList(Lists.newArrayList(symType1)) + .build(); + + subtype.setSpannedScope(OOSymbolsMill.scope()); + + //put Variable variable into spanned scope of type + FieldSymbol variable = OOSymbolsMill.fieldSymbolBuilder() + .setName("variable") + .setEnclosingScope(type.getSpannedScope()) + .setType(SymTypeExpressionFactory.createPrimitive("double")) + .build(); + + typeSpannedScope.add(variable); + + //put Function function into spanned scope of type + MethodSymbol function = OOSymbolsMill.methodSymbolBuilder() + .setName("function") + .setEnclosingScope(type.getSpannedScope()) + .setSpannedScope(OOSymbolsMill.scope()) + .setType(SymTypeExpressionFactory.createPrimitive("int")) + .build(); + + function.setSpannedScope(OOSymbolsMill.scope()); + + typeSpannedScope.add(function); + + scope.add(type); + scope.add(subtype); + } + + @Test + public void testDeSer(){ + performRoundTripSerialization(scope); + + assertTrue(Log.getFindings().isEmpty()); + } + + + public void performRoundTripSerialization(IOOSymbolsScope scope){ + //first serialize the scope using the deser + OOSymbolsSymbols2Json s2j = ((OOSymbolsGlobalScope) OOSymbolsMill.globalScope()).getSymbols2Json(); + String serialized = s2j.serialize(scope); + // then deserialize it + OOSymbolsSymbols2Json symbols2Json = new OOSymbolsSymbols2Json(); + IOOSymbolsArtifactScope deserialized = symbols2Json.deserialize(serialized); + assertNotNull(deserialized); + // and assert that the deserialized scope equals the one before + + Optional type = scope.resolveOOType("Type"); + Optional deserializedType = deserialized.resolveOOType("Type"); + assertTrue(type.isPresent()); + assertTrue(deserializedType.isPresent()); + + //check that both can resolve the type "SubType" with the supertype "Type" + Optional subtype = scope.resolveOOType("SubType"); + Optional deserializedSubType = deserialized.resolveOOType("SubType"); + assertTrue(subtype.isPresent()); + assertTrue(deserializedSubType.isPresent()); + assertEquals(1, subtype.get().getSuperTypesList().size()); + assertEquals(1, deserializedSubType.get().getSuperTypesList().size()); + assertEquals("Type", subtype.get().getSuperTypesList().get(0).print()); + assertEquals("Type", deserializedSubType.get().getSuperTypesList().get(0).print()); + + IOOSymbolsScope typeSpanned = type.get().getSpannedScope(); + IOOSymbolsScope deserializedTypeSpanned = deserializedType.get().getSpannedScope(); + + //check for Variable variable in Type + Optional variable = typeSpanned.resolveField("variable"); + Optional deserializedVariable = deserializedTypeSpanned.resolveField("variable"); + assertTrue(variable.isPresent()); + assertTrue(deserializedVariable.isPresent()); + assertEquals("double", variable.get().getType().print()); + assertEquals("double", deserializedVariable.get().getType().print()); + + //check for Function function in Type + Optional function = typeSpanned.resolveMethod("function"); + Optional deserializedFunction = deserializedTypeSpanned.resolveMethod("function"); + assertTrue(function.isPresent()); + assertTrue(deserializedFunction.isPresent()); + assertEquals("int", function.get().getType().print()); + assertEquals("int", deserializedFunction.get().getType().print()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testSerializedUnknownKind() { + OOSymbolsSymbols2Json symbols2Json = new OOSymbolsSymbols2Json(); + symbols2Json.deserialize("{\"symbols\": [{\"kind\":\"unknown\", \"name\":\"test\"}]}"); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testInvalidJsonForSerializingReturnsError(){ + String invalidJsonForSerializing = "{\n\t\"symbols\":[{\"noKind\":true}]}\n}"; + String invalidJsonForSerializing2 = "{\"symbols\": [\"SymbolIsNotAnObject\"]}"; + String invalidJsonForSerializing3 = "{\"symbols\": [{\"kind\":\"unknown\"}]}"; + + OOSymbolsSymbols2Json symbols2Json = new OOSymbolsSymbols2Json(); + symbols2Json.deserialize(invalidJsonForSerializing); + assertTrue(Log.getFindings().get(0).getMsg().startsWith("0xA1238")); + + symbols2Json.deserialize(invalidJsonForSerializing2); + assertTrue(Log.getFindings().get(1).getMsg().startsWith("0xA1233")); + + symbols2Json.deserialize(invalidJsonForSerializing3); + assertTrue(Log.getFindings().get(2).getMsg().startsWith("0xA0572")); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/testsymtabmill/MillTest.java b/monticore-grammar/src/test/java/de/monticore/testsymtabmill/MillTest.java new file mode 100644 index 0000000000..ae5486f5fb --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/testsymtabmill/MillTest.java @@ -0,0 +1,45 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.testsymtabmill; + +import de.monticore.io.paths.MCPath; +import de.monticore.testsymtabmill.testsymtabmill.TestSymTabMillMill; +import de.monticore.testsymtabmill.testsymtabmill._symboltable.*; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class MillTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestSymTabMillMill.reset(); + TestSymTabMillMill.init(); + } + + @Test + public void testMill(){ + ITestSymTabMillScope scope = TestSymTabMillMill.scope(); + ITestSymTabMillArtifactScope artifactScope = TestSymTabMillMill.artifactScope(); + artifactScope.setPackageName("sym"); + ITestSymTabMillGlobalScope globalScope = TestSymTabMillMill.globalScope(); + globalScope.setSymbolPath(new MCPath()); + globalScope.setFileExt("mill"); + TestSymTabMillScopesGenitor symbolTableCreator = TestSymTabMillMill.scopesGenitor(); + symbolTableCreator.putOnStack(scope); + + TestSymTabMillScopesGenitorDelegator symbolTableCreatorDelegator = TestSymTabMillMill.scopesGenitorDelegator(); + + assertFalse(scope.isShadowing()); + assertTrue(symbolTableCreator.getCurrentScope().get().equals(scope)); + + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/testsymtabmill/testsymtabmill/_symboltable/TestSymTabMillArtifactScope.java b/monticore-grammar/src/test/java/de/monticore/testsymtabmill/testsymtabmill/_symboltable/TestSymTabMillArtifactScope.java new file mode 100644 index 0000000000..c887a2cf11 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/testsymtabmill/testsymtabmill/_symboltable/TestSymTabMillArtifactScope.java @@ -0,0 +1,23 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.testsymtabmill.testsymtabmill._symboltable; + +import de.monticore.symboltable.ImportStatement; + +import java.util.List; +import java.util.Optional; + +public class TestSymTabMillArtifactScope extends TestSymTabMillArtifactScopeTOP { + + public TestSymTabMillArtifactScope(){ + super(); + } + + public TestSymTabMillArtifactScope(String packageName, List imports){ + super(packageName,imports); + } + + public TestSymTabMillArtifactScope(Optional enclosingScope, String packageName, List imports){ + super(enclosingScope,packageName,imports); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/testsymtabmill/testsymtabmill/_symboltable/TestSymTabMillGlobalScope.java b/monticore-grammar/src/test/java/de/monticore/testsymtabmill/testsymtabmill/_symboltable/TestSymTabMillGlobalScope.java new file mode 100644 index 0000000000..266ac8e51b --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/testsymtabmill/testsymtabmill/_symboltable/TestSymTabMillGlobalScope.java @@ -0,0 +1,24 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.testsymtabmill.testsymtabmill._symboltable; + +import de.monticore.io.paths.MCPath; + +public class TestSymTabMillGlobalScope extends TestSymTabMillGlobalScopeTOP { + + public TestSymTabMillGlobalScope(MCPath symbolPath){ + super(symbolPath, "tsm"); + } + + public TestSymTabMillGlobalScope(MCPath symbolPath, String modelFileExtension){ + super(symbolPath, modelFileExtension); + } + + public TestSymTabMillGlobalScope(){ + super(); + } + + @Override public TestSymTabMillGlobalScope getRealThis() { + return this; + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/testsymtabmill/testsymtabmill/_symboltable/TestSymTabMillScope.java b/monticore-grammar/src/test/java/de/monticore/testsymtabmill/testsymtabmill/_symboltable/TestSymTabMillScope.java new file mode 100644 index 0000000000..c38457d7d2 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/testsymtabmill/testsymtabmill/_symboltable/TestSymTabMillScope.java @@ -0,0 +1,14 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.testsymtabmill.testsymtabmill._symboltable; + +public class TestSymTabMillScope extends TestSymTabMillScopeTOP { + + public TestSymTabMillScope(){ + super(); + } + + public TestSymTabMillScope(boolean isShadowing){ + super(isShadowing); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/testsymtabmill/testsymtabmill/_symboltable/TestSymTabMillScopesGenitor.java b/monticore-grammar/src/test/java/de/monticore/testsymtabmill/testsymtabmill/_symboltable/TestSymTabMillScopesGenitor.java new file mode 100644 index 0000000000..5549cfcaff --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/testsymtabmill/testsymtabmill/_symboltable/TestSymTabMillScopesGenitor.java @@ -0,0 +1,9 @@ +/*(c) https://github.com/MontiCore/monticore*/ +package de.monticore.testsymtabmill.testsymtabmill._symboltable; + +public class TestSymTabMillScopesGenitor extends TestSymTabMillScopesGenitorTOP { + + public TestSymTabMillScopesGenitor(){ + super(); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/testsymtabmill/testsymtabmill/_symboltable/TestSymTabMillScopesGenitorDelegator.java b/monticore-grammar/src/test/java/de/monticore/testsymtabmill/testsymtabmill/_symboltable/TestSymTabMillScopesGenitorDelegator.java new file mode 100644 index 0000000000..c0cdb60fc6 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/testsymtabmill/testsymtabmill/_symboltable/TestSymTabMillScopesGenitorDelegator.java @@ -0,0 +1,10 @@ +/*(c) https://github.com/MontiCore/monticore*/ +package de.monticore.testsymtabmill.testsymtabmill._symboltable; + +public class TestSymTabMillScopesGenitorDelegator extends TestSymTabMillScopesGenitorDelegatorTOP { + + public TestSymTabMillScopesGenitorDelegator(){ + super(); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/typepersistence/TypePersistenceTest.java b/monticore-grammar/src/test/java/de/monticore/typepersistence/TypePersistenceTest.java new file mode 100644 index 0000000000..c4f0028433 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/typepersistence/TypePersistenceTest.java @@ -0,0 +1,58 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.typepersistence; + +import de.monticore.io.paths.MCPath; +import de.monticore.typepersistence.variable.VariableMill; +import de.monticore.typepersistence.variable._ast.ASTVar; +import de.monticore.typepersistence.variable._parser.VariableParser; +import de.monticore.typepersistence.variable._symboltable.IVariableGlobalScope; +import de.monticore.typepersistence.variable._symboltable.IVariableScope; +import de.monticore.typepersistence.variable._symboltable.VariableScopesGenitorDelegator; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertTrue; + +public class TypePersistenceTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + VariableMill.reset(); + VariableMill.init(); + } + + @Test + public void test() throws IOException { + + // infrastruktur aufbauen, modelle zum resolven einlesen, SymTab aufbauen, adapter schreiben, globalscope foo und blah verbinden + // TransitiveAdapterResolvingFilter implementieren und im globscope registrieren, + // + /* *************************************************************************************************************** + ****************************************************************************************************************** + Blah/Blub Infrastruktur + ****************************************************************************************************************** + */ + + //Create global scope for our language combination + IVariableGlobalScope globalScope = VariableMill + .globalScope(); + globalScope.setSymbolPath(new MCPath()); + globalScope.setFileExt("tp"); + + //Parse blah model + VariableParser blahParser = new VariableParser(); + Optional varModel = blahParser.parse_String("var String a"); + VariableScopesGenitorDelegator varSymbolTableCreator = VariableMill.scopesGenitorDelegator(); + IVariableScope blahSymbolTable = varSymbolTableCreator.createFromAST(varModel.get()); + assertTrue(varModel.isPresent()); + + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/AlwaysTheSameASTTest.java b/monticore-grammar/src/test/java/de/monticore/types/AlwaysTheSameASTTest.java new file mode 100644 index 0000000000..b09079f7e9 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/AlwaysTheSameASTTest.java @@ -0,0 +1,532 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types; + +import de.monticore.types.mcbasictypes._ast.ASTMCQualifiedType; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.mcbasictypestest.MCBasicTypesTestMill; +import de.monticore.types.mcbasictypestest._parser.MCBasicTypesTestParser; +import de.monticore.types.mccollectiontypes.MCCollectionTypesMill; +import de.monticore.types.mccollectiontypes._ast.*; +import de.monticore.types.mccollectiontypestest.MCCollectionTypesTestMill; +import de.monticore.types.mccollectiontypestest._parser.MCCollectionTypesTestParser; +import de.monticore.types.mcfullgenerictypes.MCFullGenericTypesMill; +import de.monticore.types.mcfullgenerictypestest.MCFullGenericTypesTestMill; +import de.monticore.types.mcfullgenerictypestest._parser.MCFullGenericTypesTestParser; +import de.monticore.types.mcsimplegenerictypes._ast.ASTMCCustomTypeArgument; +import de.monticore.types.mcsimplegenerictypestest.MCSimpleGenericTypesTestMill; +import de.monticore.types.mcsimplegenerictypestest._parser.MCSimpleGenericTypesTestParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.*; + +public class AlwaysTheSameASTTest { + + private MCBasicTypesTestParser basicTypesTestParser; + private MCCollectionTypesTestParser mcCollectionTypesTestParser; + private MCSimpleGenericTypesTestParser customGenericTypesTestParser; + private MCFullGenericTypesTestParser genericTypesTestParser; + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + + //only initializing this way as we only use the parser + MCCollectionTypesTestMill.reset(); + MCCollectionTypesTestMill.init(); + + MCBasicTypesTestMill.reset(); + MCBasicTypesTestMill.init(); + + MCSimpleGenericTypesTestMill.reset(); + MCSimpleGenericTypesTestMill.init(); + + MCFullGenericTypesTestMill.reset(); + MCFullGenericTypesTestMill.init(); + + this.mcCollectionTypesTestParser = new MCCollectionTypesTestParser(); + this.basicTypesTestParser = new MCBasicTypesTestParser(); + this.customGenericTypesTestParser = new MCSimpleGenericTypesTestParser(); + this.genericTypesTestParser = new MCFullGenericTypesTestParser(); + } + + @Test + public void testMCListType() throws IOException { + String list = "List"; + + Optional basicGenericAst = mcCollectionTypesTestParser.parse_StringMCGenericType(list); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(basicGenericAst.isPresent()); + assertTrue(basicGenericAst.get() instanceof ASTMCListType); + + Optional customAst = customGenericTypesTestParser.parse_StringMCGenericType(list); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(customAst.isPresent()); + assertTrue(customAst.get() instanceof ASTMCListType); + + Optional genericAST = genericTypesTestParser.parse_StringMCGenericType(list); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(genericAST.isPresent()); + assertTrue(genericAST.get() instanceof ASTMCListType); + + ASTMCListType basicGenericList = (ASTMCListType) basicGenericAst.get(); + ASTMCListType customList = (ASTMCListType) customAst.get(); + ASTMCListType genericList = (ASTMCListType) genericAST.get(); + + assertTrue(basicGenericList.deepEquals(customList)); + assertTrue(basicGenericList.deepEquals(genericList)); + assertTrue(genericList.deepEquals(customList)); + + assertEquals(basicGenericAst.get().printType().split("\\.").length, 1); + + assertEquals(basicGenericAst.get().printWithoutTypeArguments().split("\\.")[0], "List"); + + assertEquals(basicGenericAst.get().getMCTypeArgumentList().size(), 1); + + ASTMCTypeArgument argument = basicGenericAst.get().getMCTypeArgumentList().get(0); + Optional argument2 = mcCollectionTypesTestParser.parse_StringMCTypeArgument("String"); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(argument2.isPresent()); + assertTrue(argument.deepEquals(argument2.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void testMCListTypeWithCollectionTypeParser() throws IOException { + String list = "List"; + + Optional basicGenericAst = mcCollectionTypesTestParser.parse_StringMCGenericType(list); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(basicGenericAst.isPresent()); + assertTrue(basicGenericAst.get() instanceof ASTMCListType); + + Optional customAst = customGenericTypesTestParser.parse_StringMCGenericType(list); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(customAst.isPresent()); + assertTrue(customAst.get() instanceof ASTMCListType); + + Optional genericAST = genericTypesTestParser.parse_StringMCGenericType(list); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(genericAST.isPresent()); + assertTrue(genericAST.get() instanceof ASTMCListType); + + ASTMCListType basicGenericList = (ASTMCListType) basicGenericAst.get(); + ASTMCListType customList = (ASTMCListType) customAst.get(); + ASTMCListType genericList = (ASTMCListType) genericAST.get(); + + assertTrue(basicGenericList.deepEquals(customList)); + assertTrue(basicGenericList.deepEquals(genericList)); + assertTrue(genericList.deepEquals(customList)); + + assertEquals(basicGenericAst.get().printType().split("\\.").length, 1); + + assertEquals(basicGenericAst.get().printWithoutTypeArguments().split("\\.")[0], "List"); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCListTypeWithTypeParser() throws IOException { + String list = "List"; + + Optional basicGenericAst = mcCollectionTypesTestParser.parse_StringMCType(list); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(basicGenericAst.isPresent()); + assertTrue(basicGenericAst.get() instanceof ASTMCListType); + + Optional customAst = customGenericTypesTestParser.parse_StringMCType(list); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(customAst.isPresent()); + assertTrue(customAst.get() instanceof ASTMCListType); + + Optional genericAST = genericTypesTestParser.parse_StringMCType(list); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(genericAST.isPresent()); + assertTrue(genericAST.get() instanceof ASTMCListType); + + ASTMCListType basicGenericList = (ASTMCListType) basicGenericAst.get(); + ASTMCListType customList = (ASTMCListType) customAst.get(); + ASTMCListType genericList = (ASTMCListType) genericAST.get(); + + assertTrue(basicGenericList.deepEquals(customList)); + assertTrue(basicGenericList.deepEquals(genericList)); + assertTrue(genericList.deepEquals(customList)); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCMapTypeWithGenericCollectionTypeParser() throws IOException { + String map = "Map"; + + Optional basicGenericAst = mcCollectionTypesTestParser.parse_StringMCGenericType(map); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(basicGenericAst.isPresent()); + assertTrue(basicGenericAst.get() instanceof ASTMCMapType); + + Optional customAst = customGenericTypesTestParser.parse_StringMCGenericType(map); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(customAst.isPresent()); + assertTrue(customAst.get() instanceof ASTMCMapType); + + Optional genericAST = genericTypesTestParser.parse_StringMCGenericType(map); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(genericAST.isPresent()); + assertTrue(genericAST.get() instanceof ASTMCMapType); + + assertTrue(basicGenericAst.get().deepEquals(customAst.get())); + assertTrue(basicGenericAst.get().deepEquals(genericAST.get())); + assertTrue(genericAST.get().deepEquals(customAst.get())); + + assertEquals(basicGenericAst.get().printType().split("\\.").length, 1); + + assertEquals(basicGenericAst.get().printWithoutTypeArguments().split("\\.")[0], "Map"); + + assertEquals(basicGenericAst.get().getMCTypeArgumentList().size(), 2); + + ASTMCTypeArgument argument = basicGenericAst.get().getMCTypeArgumentList().get(0); + Optional argument2 = mcCollectionTypesTestParser.parse_StringMCTypeArgument("Integer"); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(argument2.isPresent()); + assertTrue(argument.deepEquals(argument2.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCMapTypeWithTypeParser() throws IOException { + String map = "Map"; + + Optional basicGenericAst = mcCollectionTypesTestParser.parse_StringMCType(map); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(basicGenericAst.isPresent()); + assertTrue(basicGenericAst.get() instanceof ASTMCMapType); + + Optional customAst = customGenericTypesTestParser.parse_StringMCType(map); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(customAst.isPresent()); + assertTrue(customAst.get() instanceof ASTMCMapType); + + Optional genericAST = genericTypesTestParser.parse_StringMCType(map); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(genericAST.isPresent()); + assertTrue(genericAST.get() instanceof ASTMCMapType); + + assertTrue(basicGenericAst.get().deepEquals(customAst.get())); + assertTrue(basicGenericAst.get().deepEquals(genericAST.get())); + assertTrue(genericAST.get().deepEquals(customAst.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCMapTypeWithCollectionTypeParser() throws IOException { + String map = "Map"; + + Optional basicGenericAst = mcCollectionTypesTestParser.parse_StringMCGenericType(map); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(basicGenericAst.isPresent()); + assertTrue(basicGenericAst.get() instanceof ASTMCMapType); + + Optional customAst = customGenericTypesTestParser.parse_StringMCGenericType(map); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(customAst.isPresent()); + assertTrue(customAst.get() instanceof ASTMCMapType); + + Optional genericAST = genericTypesTestParser.parse_StringMCGenericType(map); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(genericAST.isPresent()); + assertTrue(genericAST.get() instanceof ASTMCMapType); + + assertTrue(basicGenericAst.get().deepEquals(customAst.get())); + assertTrue(basicGenericAst.get().deepEquals(genericAST.get())); + assertTrue(genericAST.get().deepEquals(customAst.get())); + + assertEquals(basicGenericAst.get().printType().split("\\.").length, 1); + + assertEquals(basicGenericAst.get().printWithoutTypeArguments().split("\\.")[0], "Map"); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCOptionalType() throws IOException { + String optional = "Optional"; + + Optional basicGenericAst = mcCollectionTypesTestParser.parse_StringMCGenericType(optional); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(basicGenericAst.isPresent()); + assertTrue(basicGenericAst.get() instanceof ASTMCOptionalType); + + Optional customAst = customGenericTypesTestParser.parse_StringMCGenericType(optional); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(customAst.isPresent()); + assertTrue(customAst.get() instanceof ASTMCOptionalType); + + Optional genericAST = genericTypesTestParser.parse_StringMCGenericType(optional); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(genericAST.isPresent()); + assertTrue(genericAST.get() instanceof ASTMCOptionalType); + + + assertTrue(basicGenericAst.get().deepEquals(customAst.get())); + assertTrue(basicGenericAst.get().deepEquals(genericAST.get())); + assertTrue(genericAST.get().deepEquals(customAst.get())); + + assertEquals(basicGenericAst.get().printType().split("\\.").length, 1); + + assertEquals(basicGenericAst.get().printWithoutTypeArguments().split("\\.")[0], "Optional"); + + assertEquals(basicGenericAst.get().getMCTypeArgumentList().size(), 1); + + ASTMCTypeArgument argument = basicGenericAst.get().getMCTypeArgumentList().get(0); + Optional argument2 = mcCollectionTypesTestParser.parse_StringMCTypeArgument("String"); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(argument2.isPresent()); + assertTrue(argument.deepEquals(argument2.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCOptionalTypeWithTypeParser() throws IOException { + String optional = "Optional"; + + Optional basicGenericAst = mcCollectionTypesTestParser.parse_StringMCType(optional); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(basicGenericAst.isPresent()); + assertTrue(basicGenericAst.get() instanceof ASTMCOptionalType); + + Optional customAst = customGenericTypesTestParser.parse_StringMCType(optional); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(customAst.isPresent()); + assertTrue(customAst.get() instanceof ASTMCOptionalType); + + Optional genericAST = genericTypesTestParser.parse_StringMCType(optional); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(genericAST.isPresent()); + assertTrue(genericAST.get() instanceof ASTMCOptionalType); + + + assertTrue(basicGenericAst.get().deepEquals(customAst.get())); + assertTrue(basicGenericAst.get().deepEquals(genericAST.get())); + assertTrue(genericAST.get().deepEquals(customAst.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCOptionalTypeWithCollectionTypeParser() throws IOException { + String optional = "Optional"; + + Optional basicGenericAst = mcCollectionTypesTestParser.parse_StringMCGenericType(optional); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(basicGenericAst.isPresent()); + assertTrue(basicGenericAst.get() instanceof ASTMCOptionalType); + + Optional customAst = customGenericTypesTestParser.parse_StringMCGenericType(optional); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(customAst.isPresent()); + assertTrue(customAst.get() instanceof ASTMCOptionalType); + + Optional genericAST = genericTypesTestParser.parse_StringMCGenericType(optional); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(genericAST.isPresent()); + assertTrue(genericAST.get() instanceof ASTMCOptionalType); + + + assertTrue(basicGenericAst.get().deepEquals(customAst.get())); + assertTrue(basicGenericAst.get().deepEquals(genericAST.get())); + assertTrue(genericAST.get().deepEquals(customAst.get())); + + assertEquals(basicGenericAst.get().printType().split("\\.").length, 1); + + assertEquals(basicGenericAst.get().printWithoutTypeArguments().split("\\.")[0], "Optional"); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCSetTypeWithGenericCollectionTypeParser() throws IOException { + String set = "Set"; + + Optional basicGenericAst = mcCollectionTypesTestParser.parse_StringMCGenericType(set); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(basicGenericAst.isPresent()); + assertTrue(basicGenericAst.get() instanceof ASTMCSetType); + + Optional customAst = customGenericTypesTestParser.parse_StringMCGenericType(set); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(customAst.isPresent()); + assertTrue(customAst.get() instanceof ASTMCSetType); + + Optional genericAST = genericTypesTestParser.parse_StringMCGenericType(set); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(genericAST.isPresent()); + assertTrue(genericAST.get() instanceof ASTMCSetType); + + + assertTrue(basicGenericAst.get().deepEquals(customAst.get())); + assertTrue(basicGenericAst.get().deepEquals(genericAST.get())); + assertTrue(genericAST.get().deepEquals(customAst.get())); + + assertEquals(basicGenericAst.get().printType().split("\\.").length, 1); + + assertEquals(basicGenericAst.get().printWithoutTypeArguments().split("\\.")[0], "Set"); + + assertEquals(basicGenericAst.get().getMCTypeArgumentList().size(), 1); + + ASTMCTypeArgument argument = basicGenericAst.get().getMCTypeArgumentList().get(0); + Optional argument2 = mcCollectionTypesTestParser.parse_StringMCTypeArgument("String"); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(argument2.isPresent()); + assertTrue(argument.deepEquals(argument2.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCSetTypeWithTypeParser() throws IOException { + String set = "Set"; + + Optional basicGenericAst = mcCollectionTypesTestParser.parse_StringMCType(set); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(basicGenericAst.isPresent()); + assertTrue(basicGenericAst.get() instanceof ASTMCSetType); + + Optional customAst = customGenericTypesTestParser.parse_StringMCType(set); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(customAst.isPresent()); + assertTrue(customAst.get() instanceof ASTMCSetType); + + Optional genericAST = genericTypesTestParser.parse_StringMCType(set); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(genericAST.isPresent()); + assertTrue(genericAST.get() instanceof ASTMCSetType); + + + assertTrue(basicGenericAst.get().deepEquals(customAst.get())); + assertTrue(basicGenericAst.get().deepEquals(genericAST.get())); + assertTrue(genericAST.get().deepEquals(customAst.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCSetTypeWithCollectionTypeParser() throws IOException { + String set = "Set"; + + Optional basicGenericAst = mcCollectionTypesTestParser.parse_StringMCGenericType(set); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(basicGenericAst.isPresent()); + assertTrue(basicGenericAst.get() instanceof ASTMCSetType); + + Optional customAst = customGenericTypesTestParser.parse_StringMCGenericType(set); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(customAst.isPresent()); + assertTrue(customAst.get() instanceof ASTMCSetType); + + Optional genericAST = genericTypesTestParser.parse_StringMCGenericType(set); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(genericAST.isPresent()); + assertTrue(genericAST.get() instanceof ASTMCSetType); + + + assertTrue(basicGenericAst.get().deepEquals(customAst.get())); + assertTrue(basicGenericAst.get().deepEquals(genericAST.get())); + assertTrue(genericAST.get().deepEquals(customAst.get())); + + assertEquals(basicGenericAst.get().printType().split("\\.").length, 1); + + assertEquals(basicGenericAst.get().printWithoutTypeArguments().split("\\.")[0], "Set"); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCBasicTypeArgument() throws IOException { + String type = "de.monticore.ASTNode"; + + Optional basicGenericAst = mcCollectionTypesTestParser.parse_StringMCTypeArgument(type); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(basicGenericAst.isPresent()); + assertTrue(basicGenericAst.get() instanceof ASTMCBasicTypeArgument); + + Optional customAst = customGenericTypesTestParser.parse_StringMCTypeArgument(type); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(customAst.isPresent()); + assertTrue(customAst.get() instanceof ASTMCBasicTypeArgument); + + Optional genericAST = genericTypesTestParser.parse_StringMCTypeArgument(type); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(genericAST.isPresent()); + assertTrue(genericAST.get() instanceof ASTMCBasicTypeArgument); + + assertTrue(basicGenericAst.get().deepEquals(customAst.get())); + assertTrue(basicGenericAst.get().deepEquals(genericAST.get())); + assertTrue(genericAST.get().deepEquals(customAst.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCCustomTypeArgument() throws IOException { + String type = "List"; + + Optional customAst = customGenericTypesTestParser.parse_StringMCTypeArgument(type); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(customAst.isPresent()); + assertTrue(customAst.get() instanceof ASTMCCustomTypeArgument); + + Optional genericAST = genericTypesTestParser.parse_StringMCTypeArgument(type); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(genericAST.isPresent()); + assertTrue(genericAST.get() instanceof ASTMCCustomTypeArgument); + + assertTrue(genericAST.get().deepEquals(customAst.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCQualifiedType() throws IOException { + String type = "de.monticore.ASTNode"; + + Optional basicAST = basicTypesTestParser.parse_StringMCType(type); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(basicAST.isPresent()); + assertTrue(basicAST.get() instanceof ASTMCQualifiedType); + + Optional basicGenericAst = mcCollectionTypesTestParser.parse_StringMCType(type); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(basicGenericAst.isPresent()); + assertTrue(basicGenericAst.get() instanceof ASTMCQualifiedType); + + Optional customAst = customGenericTypesTestParser.parse_StringMCType(type); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(customAst.isPresent()); + assertTrue(customAst.get() instanceof ASTMCQualifiedType); + + Optional genericAST = genericTypesTestParser.parse_StringMCType(type); + assertFalse(mcCollectionTypesTestParser.hasErrors()); + assertTrue(genericAST.isPresent()); + assertTrue(genericAST.get() instanceof ASTMCQualifiedType); + + assertTrue(basicAST.get().deepEquals(customAst.get())); + assertTrue(basicAST.get().deepEquals(basicGenericAst.get())); + assertTrue(basicAST.get().deepEquals(genericAST.get())); + assertTrue(basicGenericAst.get().deepEquals(customAst.get())); + assertTrue(basicGenericAst.get().deepEquals(genericAST.get())); + assertTrue(genericAST.get().deepEquals(customAst.get())); + + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/MCBasicTypesTest.java b/monticore-grammar/src/test/java/de/monticore/types/MCBasicTypesTest.java new file mode 100644 index 0000000000..21f67eec2f --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/MCBasicTypesTest.java @@ -0,0 +1,125 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types; + +import de.monticore.types.mcbasictypes._ast.*; +import de.monticore.types.mcbasictypestest.MCBasicTypesTestMill; +import de.monticore.types.mcbasictypestest._parser.MCBasicTypesTestParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.*; + +public class MCBasicTypesTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + MCBasicTypesTestMill.reset(); + MCBasicTypesTestMill.init(); + } + + + @Test + public void testPrimitiveTypesAPI() throws IOException { + MCBasicTypesTestParser mcBasicTypesParser = new MCBasicTypesTestParser(); + Optional boolOpt = mcBasicTypesParser.parse_StringMCPrimitiveType("boolean"); + assertTrue(boolOpt.isPresent()); + + ASTMCPrimitiveType bool = boolOpt.get(); + + boolean isBool = bool.isBoolean(); + assertTrue(isBool); + boolean isByte = bool.isByte(); + assertFalse(isByte); + boolean isChar = bool.isChar(); + assertFalse(isChar); + boolean isDouble = bool.isDouble(); + assertFalse(isDouble); + boolean isFloat = bool.isFloat(); + assertFalse(isFloat); + boolean isInt = bool.isInt(); + assertFalse(isInt); + boolean isShort = bool.isShort(); + assertFalse(isShort); + + assertEquals(bool.toString(), "boolean"); + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void testPrimitiveTypes() { + + Class foo = boolean.class; + + String[] primitives = new String[]{"boolean", "byte", "char", "short", "int", "long", + "float", "double"}; + try { + for (String primitive : primitives) { + MCBasicTypesTestParser mcBasicTypesParser = new MCBasicTypesTestParser(); + // .parseType(primitive); + + Optional type = mcBasicTypesParser.parse_String(primitive); + + assertNotNull(type); + assertTrue(type.isPresent()); + assertTrue(type.get() instanceof ASTMCPrimitiveType); + } + } catch (IOException e) { + e.printStackTrace(); + } + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCQualifiedType() throws IOException { + String[] types = new String[]{"socnet.Person", "Person"}; + for (String type : types) { + MCBasicTypesTestParser mcBasicTypesParser = new MCBasicTypesTestParser(); + Optional astType = mcBasicTypesParser.parse_String(type); + assertNotNull(astType); + assertTrue(astType.isPresent()); + assertTrue(astType.get() instanceof ASTMCQualifiedType); + } + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCQualifiedName() throws IOException { + String[] types = new String[]{"socnet.Person", "Person"}; + for (String type : types) { + MCBasicTypesTestParser mcBasicTypesParser = new MCBasicTypesTestParser(); + Optional astType = mcBasicTypesParser.parse_StringMCQualifiedName(type); + assertNotNull(astType); + assertTrue(astType.isPresent()); + //test toString + assertEquals(astType.get().toString(), type); + } + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void testMCImportStatement() throws IOException { + String type = "import socnet.Person.*;"; + MCBasicTypesTestParser mcBasicTypesParser = new MCBasicTypesTestParser(); + Optional astType = mcBasicTypesParser.parse_StringMCImportStatement(type); + assertNotNull(astType); + assertTrue(astType.isPresent()); + //test getQName method + assertEquals(astType.get().getQName(), "socnet.Person"); + + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/MCCollectionTypesTest.java b/monticore-grammar/src/test/java/de/monticore/types/MCCollectionTypesTest.java new file mode 100644 index 0000000000..e9e35c1ef8 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/MCCollectionTypesTest.java @@ -0,0 +1,270 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types; + + +import de.monticore.types.mcbasictypes._ast.ASTMCObjectType; +import de.monticore.types.mcbasictypes._ast.ASTMCQualifiedType; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.mccollectiontypes.MCCollectionTypesMill; +import de.monticore.types.mccollectiontypes._ast.*; +import de.monticore.types.mccollectiontypes._visitor.MCCollectionTypesTraverser; +import de.monticore.types.mccollectiontypes._visitor.MCCollectionTypesVisitor2; +import de.monticore.types.mccollectiontypestest.MCCollectionTypesTestMill; +import de.monticore.types.mccollectiontypestest._parser.MCCollectionTypesTestParser; +import de.monticore.types.mccollectiontypeswithoutprimitivestest._parser.MCCollectionTypesWithoutPrimitivesTestParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.*; + +public class MCCollectionTypesTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + MCCollectionTypesTestMill.reset(); + MCCollectionTypesTestMill.init(); + } + + @Test + public void testBasicGenericsTypes() throws IOException { + + String[] types = new String[]{"List", "Optional", + "Set", "Map", "List" + , "List" + }; + for (String testType : types) { + MCCollectionTypesTestParser mcBasicTypesParser = new MCCollectionTypesTestParser(); + // .parseType(primitive); + Optional type = mcBasicTypesParser.parse_StringMCType(testType); + + assertNotNull(type); + assertTrue(type.isPresent()); + assertTrue(type.get() instanceof ASTMCObjectType); + + ASTMCObjectType t = (ASTMCObjectType) type.get(); + MCCollectionTypesTraverser traverser = MCCollectionTypesMill.traverser(); + traverser.add4MCCollectionTypes(new CheckTypeVisitor()); + t.accept(traverser); + } + + assertTrue(Log.getFindings().isEmpty()); + } + + private class CheckTypeVisitor implements MCCollectionTypesVisitor2 { + public void visit(ASTMCType node) { + if (!(node instanceof ASTMCQualifiedType)) { + fail("Found not String"); + } + } + } + + @Test + public void testMCListTypeValid() throws IOException { + MCCollectionTypesTestParser parser = new MCCollectionTypesTestParser(); + Optional type = parser.parse_StringMCGenericType("List"); + assertFalse(parser.hasErrors()); + assertNotNull(type); + assertTrue(type.isPresent()); + assertTrue(type.get() instanceof ASTMCListType); + + //test specific methods + ASTMCListType listType = (ASTMCListType) type.get(); + assertEquals(listType.getNameList().size(), 1); + + assertEquals(listType.getNameList().get(0), "List"); + + assertEquals(listType.getMCTypeArgumentList().size(), 1); + + ASTMCTypeArgument argument = listType.getMCTypeArgumentList().get(0); + Optional argument2 = parser.parse_StringMCTypeArgument("String"); + assertFalse(parser.hasErrors()); + assertTrue(argument2.isPresent()); + assertTrue(argument.deepEquals(argument2.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCListTypeInvalid() throws IOException { + MCCollectionTypesTestParser parser = new MCCollectionTypesTestParser(); + Optional type = parser.parse_StringMCGenericType("java.util.List"); + assertTrue(parser.hasErrors()); + assertFalse(type.isPresent()); + } + + @Test + public void testMCMapTypeValid() throws IOException { + MCCollectionTypesTestParser parser = new MCCollectionTypesTestParser(); + Optional type = parser.parse_StringMCGenericType("Map"); + assertFalse(parser.hasErrors()); + assertNotNull(type); + assertTrue(type.isPresent()); + assertTrue(type.get() instanceof ASTMCMapType); + + //test specific methods + ASTMCMapType mapType = (ASTMCMapType) type.get(); + assertEquals(mapType.getNameList().size(), 1); + + assertEquals(mapType.getNameList().get(0), "Map"); + + assertEquals(mapType.getMCTypeArgumentList().size(), 2); + + ASTMCTypeArgument argument = mapType.getMCTypeArgumentList().get(0); + Optional argument2 = parser.parse_StringMCTypeArgument("Integer"); + assertFalse(parser.hasErrors()); + assertTrue(argument2.isPresent()); + assertTrue(argument.deepEquals(argument2.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCMapTypeInvalid() throws IOException { + MCCollectionTypesTestParser parser = new MCCollectionTypesTestParser(); + Optional type = parser.parse_StringMCGenericType("java.util.Map"); + assertTrue(parser.hasErrors()); + assertFalse(type.isPresent()); + } + + + @Test + public void testMCOptionalTypeValid() throws IOException { + MCCollectionTypesTestParser parser = new MCCollectionTypesTestParser(); + Optional type = parser.parse_StringMCGenericType("Optional"); + assertFalse(parser.hasErrors()); + assertNotNull(type); + assertTrue(type.isPresent()); + assertTrue(type.get() instanceof ASTMCOptionalType); + + //test specific methods + ASTMCOptionalType optionalType = (ASTMCOptionalType) type.get(); + assertEquals(optionalType.getNameList().size(), 1); + + assertEquals(optionalType.getNameList().get(0), "Optional"); + + assertEquals(optionalType.getMCTypeArgumentList().size(), 1); + + ASTMCTypeArgument argument = optionalType.getMCTypeArgumentList().get(0); + Optional argument2 = parser.parse_StringMCTypeArgument("String"); + assertFalse(parser.hasErrors()); + assertTrue(argument2.isPresent()); + assertTrue(argument.deepEquals(argument2.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCOptionalTypeInvalid() throws IOException { + MCCollectionTypesTestParser parser = new MCCollectionTypesTestParser(); + Optional type = parser.parse_StringMCGenericType("java.util.Optional"); + assertTrue(parser.hasErrors()); + assertFalse(type.isPresent()); + } + + + @Test + public void testMCSetTypeValid() throws IOException { + MCCollectionTypesTestParser parser = new MCCollectionTypesTestParser(); + Optional type = parser.parse_StringMCGenericType("Set"); + assertFalse(parser.hasErrors()); + assertNotNull(type); + assertTrue(type.isPresent()); + assertTrue(type.get() instanceof ASTMCSetType); + + //test specific methods + ASTMCSetType setType = (ASTMCSetType) type.get(); + assertEquals(setType.getNameList().size(), 1); + + assertEquals(setType.getNameList().get(0), "Set"); + + assertEquals(setType.getMCTypeArgumentList().size(), 1); + + ASTMCTypeArgument argument = setType.getMCTypeArgumentList().get(0); + Optional argument2 = parser.parse_StringMCTypeArgument("String"); + assertFalse(parser.hasErrors()); + assertTrue(argument2.isPresent()); + assertTrue(argument.deepEquals(argument2.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCSetTypeInvalid() throws IOException { + MCCollectionTypesTestParser parser = new MCCollectionTypesTestParser(); + Optional type = parser.parse_StringMCGenericType("java.util.Set"); + assertTrue(parser.hasErrors()); + assertFalse(type.isPresent()); + } + + @Test + public void testMCTypeArgumentValid() throws IOException { + MCCollectionTypesTestParser parser = new MCCollectionTypesTestParser(); + Optional type = parser.parse_StringMCTypeArgument("a.b.c"); + assertFalse(parser.hasErrors()); + assertNotNull(type); + assertTrue(type.isPresent()); + assertTrue(type.get() instanceof ASTMCBasicTypeArgument); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCTypeArgumentInvalid() throws IOException { + MCCollectionTypesTestParser parser = new MCCollectionTypesTestParser(); + Optional type = parser.parse_StringMCTypeArgument("List"); + assertTrue(parser.hasErrors()); + assertFalse(type.isPresent()); + } + + + @Test + public void collectionTypeWithInt() throws IOException { + MCCollectionTypesTestParser parser = new MCCollectionTypesTestParser(); + Optional type = parser.parse_StringMCGenericType("List"); + assertTrue(type.isPresent()); + assertEquals("List", type.get().printWithoutTypeArguments()); + assertTrue(type.get().getMCTypeArgumentList().get(0) instanceof ASTMCPrimitiveTypeArgument); + + assertTrue(Log.getFindings().isEmpty()); + + } + + @Test + public void collectionTypeWithIntFail() throws IOException { + MCCollectionTypesWithoutPrimitivesTestParser parser = new MCCollectionTypesWithoutPrimitivesTestParser(); + Optional type = parser.parse_StringMCGenericType("List"); + assertTrue(parser.hasErrors()); + assertFalse(type.isPresent()); + } + + @Test + public void testPrintTypeWithoutTypeArguments() throws IOException { + MCCollectionTypesTestParser parser = new MCCollectionTypesTestParser(); + Optional listType = parser.parse_StringMCListType("List"); + Optional optionalType = parser.parse_StringMCOptionalType("Optional"); + Optional setType = parser.parse_StringMCSetType("Set"); + Optional mapType = parser.parse_StringMCMapType("Map"); + Optional genericType = parser.parse_StringMCGenericType("Map"); + assertTrue(listType.isPresent()); + assertTrue(optionalType.isPresent()); + assertTrue(setType.isPresent()); + assertTrue(mapType.isPresent()); + assertTrue(genericType.isPresent()); + assertEquals("List", listType.get().printWithoutTypeArguments()); + assertEquals("Optional", optionalType.get().printWithoutTypeArguments()); + assertEquals("Set", setType.get().printWithoutTypeArguments()); + assertEquals("Map", genericType.get().printWithoutTypeArguments()); + assertEquals("Map", genericType.get().printWithoutTypeArguments()); + assertFalse(parser.hasErrors()); + + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/MCFullGenericTypesTest.java b/monticore-grammar/src/test/java/de/monticore/types/MCFullGenericTypesTest.java new file mode 100644 index 0000000000..3189728513 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/MCFullGenericTypesTest.java @@ -0,0 +1,43 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types; + +import de.monticore.types.mccollectiontypes._ast.ASTMCGenericType; +import de.monticore.types.mcfullgenerictypes._ast.ASTMCMultipleGenericType; +import de.monticore.types.mcfullgenerictypestest.MCFullGenericTypesTestMill; +import de.monticore.types.mcfullgenerictypestest._parser.MCFullGenericTypesTestParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.*; + +public class MCFullGenericTypesTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + MCFullGenericTypesTestMill.reset(); + MCFullGenericTypesTestMill.init(); + } + + @Test + public void testPrintTypeWithoutTypeArguments() throws IOException { + MCFullGenericTypesTestParser parser = new MCFullGenericTypesTestParser(); + Optional multipleGenericType = parser.parse_StringMCMultipleGenericType("a.B.D.E.G"); + Optional genericType = parser.parse_StringMCGenericType("a.B.D.E.G"); + assertTrue(genericType.isPresent()); + assertTrue(multipleGenericType.isPresent()); + assertEquals("a.B.D.E.G", multipleGenericType.get().printWithoutTypeArguments()); + assertEquals("a.B.D.E.G", genericType.get().printWithoutTypeArguments()); + assertFalse(parser.hasErrors()); + + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/MCFunctionTypesTest.java b/monticore-grammar/src/test/java/de/monticore/types/MCFunctionTypesTest.java new file mode 100644 index 0000000000..a775fd61bc --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/MCFunctionTypesTest.java @@ -0,0 +1,123 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types; + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.mcfunctiontypes._ast.ASTMCFunctionType; +import de.monticore.types.mcfunctiontypestest.MCFunctionTypesTestMill; +import de.monticore.types.mcfunctiontypestest._parser.MCFunctionTypesTestParser; +import de.monticore.types.mcbasictypes._prettyprint.MCBasicTypesFullPrettyPrinter; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class MCFunctionTypesTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + MCFunctionTypesTestMill.reset(); + MCFunctionTypesTestMill.init(); + } + + @Test + public void testRunnableFunctionType() throws IOException { + ASTMCFunctionType type = parse("() -> void"); + assertEquals("void", + type.getMCReturnType() + .printType()); + assertFalse(type.getMCFunctionParTypes().isPresentIsElliptic()); + assertEquals(0, type.getMCFunctionParTypes().getMCTypeList().size()); + } + + @Test + public void testSupplierFunctionType() throws IOException { + ASTMCFunctionType type = parse("() -> int"); + assertEquals("int", + type.getMCReturnType() + .printType()); + assertFalse(type.getMCFunctionParTypes().isPresentIsElliptic()); + assertEquals(0, type.getMCFunctionParTypes().getMCTypeList().size()); + } + + @Test + public void testWithInputFunctionType() throws IOException { + ASTMCFunctionType type = parse("(int, long) -> void"); + assertEquals("void", + type.getMCReturnType() + .printType()); + assertFalse(type.getMCFunctionParTypes().isPresentIsElliptic()); + assertEquals(2, type.getMCFunctionParTypes().getMCTypeList().size()); + assertEquals("int", + type.getMCFunctionParTypes().getMCType(0) + .printType()); + assertEquals("long", + type.getMCFunctionParTypes().getMCType(1) + .printType()); + } + + @Test + public void testEllipticFunctionType1() throws IOException { + ASTMCFunctionType type = parse("(long...) -> void"); + assertEquals("void", + type.getMCReturnType() + .printType()); + assertTrue(type.getMCFunctionParTypes().isPresentIsElliptic()); + assertEquals(1, type.getMCFunctionParTypes().getMCTypeList().size()); + assertEquals("long", + type.getMCFunctionParTypes().getMCType(0) + .printType()); + } + + @Test + public void testEllipticFunctionType2() throws IOException { + ASTMCFunctionType type = parse("(int, long...) -> long"); + assertEquals("long", + type.getMCReturnType() + .printType()); + assertTrue(type.getMCFunctionParTypes().isPresentIsElliptic()); + assertEquals(2, type.getMCFunctionParTypes().getMCTypeList().size()); + assertEquals("int", + type.getMCFunctionParTypes().getMCType(0) + .printType()); + assertEquals("long", + type.getMCFunctionParTypes().getMCType(1) + .printType()); + } + + @Test + public void testHigherOrderFunctionType1() throws IOException { + ASTMCFunctionType type = parse("() -> () -> void"); + assertFalse(type.getMCFunctionParTypes().isPresentIsElliptic()); + assertEquals(0, type.getMCFunctionParTypes().getMCTypeList().size()); + } + + @Test + public void testHigherOrderFunctionType2() throws IOException { + ASTMCFunctionType type = parse("((long) -> void) -> (int) -> long"); + assertFalse(type.getMCFunctionParTypes().isPresentIsElliptic()); + assertEquals(1, type.getMCFunctionParTypes().getMCTypeList().size()); + } + + protected ASTMCFunctionType parse(String mcTypeStr) throws IOException { + MCFunctionTypesTestParser parser = new MCFunctionTypesTestParser(); + Optional typeOpt = parser.parse_StringMCType(mcTypeStr); + assertNotNull(typeOpt); + assertTrue(typeOpt.isPresent()); + assertTrue(typeOpt.get() instanceof ASTMCFunctionType); + ASTMCFunctionType type = (ASTMCFunctionType) typeOpt.get(); + assertEquals(0, Log.getFindingsCount()); + return type; + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/MCGenericsTypesTest.java b/monticore-grammar/src/test/java/de/monticore/types/MCGenericsTypesTest.java new file mode 100644 index 0000000000..d486820edb --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/MCGenericsTypesTest.java @@ -0,0 +1,198 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types; + +import de.monticore.types.generictypestest.GenericTypesTestMill; +import de.monticore.types.generictypestest._parser.GenericTypesTestParser; +import de.monticore.types.mcarraytypes._ast.ASTMCArrayType; +import de.monticore.types.mccollectiontypes._ast.ASTMCTypeArgument; +import de.monticore.types.mcbasictypes._ast.ASTMCObjectType; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.mcfullgenerictypes._ast.ASTMCMultipleGenericType; +import de.monticore.types.mcfullgenerictypes._ast.ASTMCWildcardTypeArgument; +import de.monticore.types.mcfullgenerictypestest.MCFullGenericTypesTestMill; +import de.monticore.types.mcfullgenerictypestest._parser.MCFullGenericTypesTestParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Optional; + +import static org.junit.Assert.*; + +public class MCGenericsTypesTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + } + + + @Test + public void testBasicGenericsTypes() throws IOException { + MCFullGenericTypesTestMill.reset(); + MCFullGenericTypesTestMill.init(); + Class foo = boolean.class; + String[] types = new String[]{"Foo.Bar>", + "List","List>","Optional", + "Set","Map","List" + }; + + for (String testType : types) { + MCFullGenericTypesTestParser mcBasicTypesParser = new MCFullGenericTypesTestParser(); + // .parseType(primitive); + + Optional type = mcBasicTypesParser.parse_StringMCType(testType); + assertNotNull(type); + assertTrue(type.isPresent()); + assertTrue(type.get() instanceof ASTMCObjectType); + System.out.println(type.get().getClass()); + ASTMCObjectType t = (ASTMCObjectType) type.get(); + } + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testArrayTypes() throws IOException { + GenericTypesTestMill.reset(); + GenericTypesTestMill.init(); + Class foo = boolean.class; + String[] types = new String[]{"String[][]","java.util.List[][]", + "boolean[][]" + }; + + for (String testType : types) { + GenericTypesTestParser genericTypesTestParser = new GenericTypesTestParser(); + // .parseType(primitive); + + Optional type = genericTypesTestParser.parse_StringMCType(testType); + + assertNotNull(type); + assertTrue(type.isPresent()); + assertTrue(type.get() instanceof ASTMCArrayType); + ASTMCArrayType t = (ASTMCArrayType) type.get(); + assertEquals(2,t.getDimensions()); + } + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCComplexReferenceTypeValid() throws IOException { + MCFullGenericTypesTestMill.reset(); + MCFullGenericTypesTestMill.init(); + MCFullGenericTypesTestParser parser = new MCFullGenericTypesTestParser(); + Optional type = parser.parse_StringMCType("java.util.List.Set.some.Collection"); + assertFalse(parser.hasErrors()); + assertNotNull(type); + assertTrue(type.isPresent()); + assertTrue(type.get() instanceof ASTMCMultipleGenericType); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMcWildcardTypeArgument() throws IOException { + MCFullGenericTypesTestMill.reset(); + MCFullGenericTypesTestMill.init(); + MCFullGenericTypesTestParser parser = new MCFullGenericTypesTestParser(); + Optional type = parser.parse_StringMCTypeArgument("? extends java.util.Set"); + assertFalse(parser.hasErrors()); + assertNotNull(type); + assertTrue(type.isPresent()); + assertTrue(type.get() instanceof ASTMCWildcardTypeArgument); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testOldComplexArrayTypes() { + GenericTypesTestMill.reset(); + GenericTypesTestMill.init(); + GenericTypesTestParser parser = new GenericTypesTestParser(); + try { + // test-data + HashMap testdata = new HashMap(); + testdata.put("Collection[]", 1); + testdata.put("L[]", 1); + testdata.put("C>[]", 1); + testdata.put("Pair[]", 1); + testdata.put("A>>>[]", 1); + testdata.put("A>>>,I>[]", 1); + + // checks + for (String teststring : testdata.keySet()) { + Optional type = parser.parse_StringMCType(teststring); + assertTrue(type.isPresent()); + // check typing and dimension: + assertTrue(type.get() instanceof ASTMCArrayType); + ASTMCArrayType arrayType = (ASTMCArrayType) type.get(); + assertEquals(testdata.get(teststring).intValue(), arrayType.getDimensions()); + assertTrue(arrayType.getMCType() instanceof ASTMCObjectType); + } + } + catch (IOException e) { + fail(e.getMessage()); + } + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void testOldComplexTypes() throws IOException { + GenericTypesTestMill.reset(); + GenericTypesTestMill.init(); + String[] typesToTest = new String[]{ + "Type" + , "Type" + , "packageName.OuterClass.Type" + , "java.util.Foo.Set.some.Collection" + , "a.b.Type.C" + , "a.b.Type.C.D" + , "OwnClass" + , "a.b.c" + , "_$testABC_1._5" + , "a.b" + , "Seq>" + , "Pair" + , "Seq>" + , "A>>>" + , "A>>>,I>" + , "Vector" + , "A.B.C" + , "A.B.C" + , "L" + , "C[]>" + , "a.b.c" + , "a.b.c.d" + // Wildcards: + , "Collection" + , "List" + , "ReferenceQueue" + , "Pair" + , "B" + , "Pair" + }; + + for (String testType : typesToTest) { + GenericTypesTestParser genericTypesTestParser = new GenericTypesTestParser(); + // .parseType(primitive); + + Optional type = genericTypesTestParser.parse_StringMCType(testType); + + assertNotNull(type); + assertTrue(type.isPresent()); + //assertTrue(type.get() instanceof ASTMCMultipleGenericType); + + } + + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/MCSimpleGenericsTypesTest.java b/monticore-grammar/src/test/java/de/monticore/types/MCSimpleGenericsTypesTest.java new file mode 100644 index 0000000000..d108df7f96 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/MCSimpleGenericsTypesTest.java @@ -0,0 +1,207 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types; + +import de.monticore.types.mcbasictypes._ast.ASTMCObjectType; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.mccollectiontypes._ast.*; +import de.monticore.types.mcsimplegenerictypes._ast.ASTMCBasicGenericType; +import de.monticore.types.mcsimplegenerictypestest.MCSimpleGenericTypesTestMill; +import de.monticore.types.mcsimplegenerictypestest._parser.MCSimpleGenericTypesTestParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.*; + +public class MCSimpleGenericsTypesTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + MCSimpleGenericTypesTestMill.reset(); + MCSimpleGenericTypesTestMill.init(); + } + + @Test + public void testCustomGenericsTypes() throws IOException { + String[] types = new String[]{"List>","socnet.Person, SecondaryParam>"}; + + for (String testType : types) { + System.out.println("Teste "+testType); + MCSimpleGenericTypesTestParser mcBasicTypesParser = new MCSimpleGenericTypesTestParser(); + + Optional type = mcBasicTypesParser.parse_StringMCType(testType); + + assertNotNull(type); + assertTrue(type.isPresent()); + assertTrue(type.get() instanceof ASTMCObjectType); + System.out.println(type.get().getClass()); + + ASTMCObjectType t = (ASTMCObjectType) type.get(); + } + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCListTypeValid() throws IOException { + MCSimpleGenericTypesTestParser parser = new MCSimpleGenericTypesTestParser(); + Optional type = parser.parse_StringMCGenericType("List"); + assertFalse(parser.hasErrors()); + assertNotNull(type); + assertTrue(type.isPresent()); + assertTrue(type.get() instanceof ASTMCListType); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCListTypeValid2() throws IOException { + MCSimpleGenericTypesTestParser parser = new MCSimpleGenericTypesTestParser(); + Optional type = parser.parse_StringMCGenericType("java.util.List"); + assertFalse(parser.hasErrors()); + assertNotNull(type); + assertTrue(type.isPresent()); + assertTrue(type.get() instanceof ASTMCBasicGenericType); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCMapTypeValid() throws IOException { + MCSimpleGenericTypesTestParser parser = new MCSimpleGenericTypesTestParser(); + Optional type = parser.parse_StringMCGenericType("Map"); + assertFalse(parser.hasErrors()); + assertNotNull(type); + assertTrue(type.isPresent()); + assertTrue(type.get() instanceof ASTMCMapType); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCMapTypeValid2() throws IOException { + MCSimpleGenericTypesTestParser parser = new MCSimpleGenericTypesTestParser(); + Optional type = parser.parse_StringMCGenericType("java.util.Map, String>"); + assertFalse(parser.hasErrors()); + assertNotNull(type); + assertTrue(type.isPresent()); + assertTrue(type.get() instanceof ASTMCBasicGenericType); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCMapTypeValid3() throws IOException { + MCSimpleGenericTypesTestParser parser = new MCSimpleGenericTypesTestParser(); + Optional type = parser.parse_StringMCGenericType("java.util.HashMap>"); + assertFalse(parser.hasErrors()); + assertNotNull(type); + assertTrue(type.isPresent()); + assertTrue(type.get() instanceof ASTMCBasicGenericType); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCOptionalTypeValid() throws IOException { + MCSimpleGenericTypesTestParser parser = new MCSimpleGenericTypesTestParser(); + Optional type = parser.parse_StringMCGenericType("Optional"); + assertFalse(parser.hasErrors()); + assertNotNull(type); + assertTrue(type.isPresent()); + assertTrue(type.get() instanceof ASTMCOptionalType); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCOptionalTypeValid2() throws IOException { + MCSimpleGenericTypesTestParser parser = new MCSimpleGenericTypesTestParser(); + Optional type = parser.parse_StringMCGenericType("java.util.Optional>"); + assertFalse(parser.hasErrors()); + assertNotNull(type); + assertTrue(type.isPresent()); + assertTrue(type.get() instanceof ASTMCBasicGenericType); + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void testMCSetTypeValid() throws IOException { + MCSimpleGenericTypesTestParser parser = new MCSimpleGenericTypesTestParser(); + Optional type = parser.parse_StringMCGenericType("Set"); + assertFalse(parser.hasErrors()); + assertNotNull(type); + assertTrue(type.isPresent()); + assertTrue(type.get() instanceof ASTMCSetType); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCSetTypeValid2() throws IOException { + MCSimpleGenericTypesTestParser parser = new MCSimpleGenericTypesTestParser(); + Optional type = parser.parse_StringMCGenericType("java.util.Set>"); + assertFalse(parser.hasErrors()); + assertNotNull(type); + assertTrue(type.isPresent()); + assertTrue(type.get() instanceof ASTMCBasicGenericType); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCTypeArgumentValid() throws IOException { + MCSimpleGenericTypesTestParser parser = new MCSimpleGenericTypesTestParser(); + Optional type = parser.parse_StringMCTypeArgument("a.b.c"); + assertFalse(parser.hasErrors()); + assertNotNull(type); + assertTrue(type.isPresent()); + assertTrue(type.get() instanceof ASTMCBasicTypeArgument); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCTypeArgumentValid2() throws IOException { + MCSimpleGenericTypesTestParser parser = new MCSimpleGenericTypesTestParser(); + Optional type = parser.parse_StringMCGenericType("List"); + assertFalse(parser.hasErrors()); + assertNotNull(type); + assertTrue(type.isPresent()); + assertTrue(type.get() instanceof ASTMCListType); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCComplexReferenceTypeInvalid() throws IOException { + //not defined in that grammar, only in MCGenericsTypes + MCSimpleGenericTypesTestParser parser = new MCSimpleGenericTypesTestParser(); + Optional type = parser.parse_StringMCType("java.util.List.Set.some.Collection"); + assertTrue(parser.hasErrors()); + assertFalse(type.isPresent()); + } + + @Test + public void testPrintTypeWithoutTypeArguments() throws IOException { + MCSimpleGenericTypesTestParser parser = new MCSimpleGenericTypesTestParser(); + Optional basicGenericType = parser.parse_StringMCBasicGenericType("a.B"); + Optional genericType = parser.parse_StringMCGenericType("a.B"); + assertTrue(genericType.isPresent()); + assertTrue(basicGenericType.isPresent()); + assertEquals("a.B", basicGenericType.get().printWithoutTypeArguments()); + assertEquals("a.B", genericType.get().printWithoutTypeArguments()); + assertFalse(parser.hasErrors()); + + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/MCTypeFacadeTest.java b/monticore-grammar/src/test/java/de/monticore/types/MCTypeFacadeTest.java new file mode 100644 index 0000000000..bc4ec40b0b --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/MCTypeFacadeTest.java @@ -0,0 +1,525 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types; + +import com.google.common.collect.Lists; +import de.monticore.types.mcarraytypes._ast.ASTMCArrayType; +import de.monticore.types.mcbasictypes._ast.ASTMCPrimitiveType; +import de.monticore.types.mcbasictypes._ast.ASTMCQualifiedType; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.mcbasictypes._ast.ASTMCVoidType; +import de.monticore.types.mccollectiontypes._ast.*; +import de.monticore.types.mcfullgenerictypes._ast.ASTMCWildcardTypeArgument; +import de.monticore.types.mcsimplegenerictypes._ast.ASTMCBasicGenericType; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class MCTypeFacadeTest { + + private MCTypeFacade mcTypeFacade; + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + this.mcTypeFacade = MCTypeFacade.getInstance(); + } + + @Test + public void testCreateQualifiedTypeName() { + ASTMCQualifiedType qualifiedType = mcTypeFacade.createQualifiedType("a.b.c.Foo"); + assertEquals(Lists.newArrayList("a", "b", "c", "Foo"), qualifiedType.getNameList()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCreateQualifiedTypeClass() { + ASTMCQualifiedType qualifiedType = mcTypeFacade.createQualifiedType(java.lang.String.class); + assertEquals(Lists.newArrayList("String"), qualifiedType.getNameList()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCreateBasicTypeArgumentOf() { + ASTMCBasicTypeArgument type = mcTypeFacade.createBasicTypeArgumentOf("a.b.c.Foo"); + assertEquals(Lists.newArrayList("a", "b", "c", "Foo"), type.getMCQualifiedType().getNameList()); + assertTrue(type.getMCTypeOpt().isPresent()); + assertTrue(type.getMCTypeOpt().get() instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("a", "b", "c", "Foo"), ((ASTMCQualifiedType) type.getMCTypeOpt().get()).getNameList()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCreateWildCardWithNoBounds() { + ASTMCWildcardTypeArgument type = mcTypeFacade.createWildCardWithNoBounds(); + assertFalse(type.isPresentLowerBound()); + assertFalse(type.isPresentUpperBound()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCreateWildCardWithUpperBoundTypeClass() { + ASTMCWildcardTypeArgument type = mcTypeFacade.createWildCardWithUpperBoundType(String.class); + assertFalse(type.isPresentLowerBound()); + assertTrue(type.isPresentUpperBound()); + assertTrue(type.getUpperBound() instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("String"), ((ASTMCQualifiedType) type.getUpperBound()).getNameList()); + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void testCreateWildCardWithUpperBoundTypeName() { + ASTMCWildcardTypeArgument type = mcTypeFacade.createWildCardWithUpperBoundType("a.b.c.Foo"); + assertFalse(type.isPresentLowerBound()); + assertTrue(type.isPresentUpperBound()); + assertTrue(type.getUpperBound() instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("a", "b", "c", "Foo"), ((ASTMCQualifiedType) type.getUpperBound()).getNameList()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCreateWildCardWithUpperBoundTypeType() { + ASTMCWildcardTypeArgument type = mcTypeFacade.createWildCardWithUpperBoundType(mcTypeFacade.createQualifiedType("a.b.c.Foo")); + assertFalse(type.isPresentLowerBound()); + assertTrue(type.isPresentUpperBound()); + assertTrue(type.getUpperBound() instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("a", "b", "c", "Foo"), ((ASTMCQualifiedType) type.getUpperBound()).getNameList()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCreateOptionalTypeOfClass() { + ASTMCOptionalType type = mcTypeFacade.createOptionalTypeOf(String.class); + assertEquals(1, type.sizeNames()); + assertEquals("Optional", type.getName(0)); + assertEquals(1, type.sizeMCTypeArguments()); + assertTrue(type.getMCTypeArgument().getMCTypeOpt().isPresent()); + assertTrue(type.getMCTypeArgument().getMCTypeOpt().get() instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("String"), ((ASTMCQualifiedType) type.getMCTypeArgument(0).getMCTypeOpt().get()).getNameList()); + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void testCreateOptionalTypeOfName() { + ASTMCOptionalType type = mcTypeFacade.createOptionalTypeOf("a.b.c.Foo"); + assertEquals(1, type.sizeNames()); + assertEquals("Optional", type.getName(0)); + assertEquals(1, type.sizeMCTypeArguments()); + assertTrue(type.getMCTypeArgument().getMCTypeOpt().isPresent()); + assertTrue(type.getMCTypeArgument().getMCTypeOpt().get() instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("a", "b", "c", "Foo"), ((ASTMCQualifiedType) type.getMCTypeArgument(0).getMCTypeOpt().get()).getNameList()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCreateOptionalTypeOfType() { + ASTMCOptionalType type = mcTypeFacade.createOptionalTypeOf(mcTypeFacade.createQualifiedType("a.b.c.Foo")); + assertEquals(1, type.sizeNames()); + assertEquals("Optional", type.getName(0)); + assertEquals(1, type.sizeMCTypeArguments()); + assertTrue(type.getMCTypeArgument().getMCTypeOpt().isPresent()); + assertTrue(type.getMCTypeArgument().getMCTypeOpt().get() instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("a", "b", "c", "Foo"), ((ASTMCQualifiedType) type.getMCTypeArgument(0).getMCTypeOpt().get()).getNameList()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCreateOptionalTypeOfTypeArgument() { + ASTMCOptionalType type = mcTypeFacade.createOptionalTypeOf(mcTypeFacade.createBasicTypeArgumentOf("a.b.c.Foo")); + assertEquals(1, type.sizeNames()); + assertEquals("Optional", type.getName(0)); + assertEquals(1, type.sizeMCTypeArguments()); + assertTrue(type.getMCTypeArgument().getMCTypeOpt().isPresent()); + assertTrue(type.getMCTypeArgument().getMCTypeOpt().get() instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("a", "b", "c", "Foo"), ((ASTMCQualifiedType) type.getMCTypeArgument(0).getMCTypeOpt().get()).getNameList()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCreateListTypeOfClass() { + ASTMCListType type = mcTypeFacade.createListTypeOf(String.class); + assertEquals(1, type.sizeNames()); + assertEquals("List", type.getName(0)); + assertEquals(1, type.sizeMCTypeArguments()); + assertTrue(type.getMCTypeArgument().getMCTypeOpt().isPresent()); + assertTrue(type.getMCTypeArgument().getMCTypeOpt().get() instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("String"), ((ASTMCQualifiedType) type.getMCTypeArgument(0).getMCTypeOpt().get()).getNameList()); + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void testCreateListTypeOfName() { + ASTMCListType type = mcTypeFacade.createListTypeOf("a.b.c.Foo"); + assertEquals(1, type.sizeNames()); + assertEquals("List", type.getName(0)); + assertEquals(1, type.sizeMCTypeArguments()); + assertTrue(type.getMCTypeArgument().getMCTypeOpt().isPresent()); + assertTrue(type.getMCTypeArgument().getMCTypeOpt().get() instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("a", "b", "c", "Foo"), ((ASTMCQualifiedType) type.getMCTypeArgument(0).getMCTypeOpt().get()).getNameList()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCreateListTypeOfType() { + ASTMCListType type = mcTypeFacade.createListTypeOf(mcTypeFacade.createQualifiedType("a.b.c.Foo")); + assertEquals(1, type.sizeNames()); + assertEquals("List", type.getName(0)); + assertEquals(1, type.sizeMCTypeArguments()); + assertTrue(type.getMCTypeArgument().getMCTypeOpt().isPresent()); + assertTrue(type.getMCTypeArgument().getMCTypeOpt().get() instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("a", "b", "c", "Foo"), ((ASTMCQualifiedType) type.getMCTypeArgument(0).getMCTypeOpt().get()).getNameList()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCreateListTypeOfTypeArgument() { + ASTMCListType type = mcTypeFacade.createListTypeOf(mcTypeFacade.createBasicTypeArgumentOf("a.b.c.Foo")); + assertEquals(1, type.sizeNames()); + assertEquals("List", type.getName(0)); + assertEquals(1, type.sizeMCTypeArguments()); + assertTrue(type.getMCTypeArgument().getMCTypeOpt().isPresent()); + assertTrue(type.getMCTypeArgument().getMCTypeOpt().get() instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("a", "b", "c", "Foo"), ((ASTMCQualifiedType) type.getMCTypeArgument(0).getMCTypeOpt().get()).getNameList()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCreateSetTypeOfClass() { + ASTMCSetType type = mcTypeFacade.createSetTypeOf(String.class); + assertEquals(1, type.sizeNames()); + assertEquals("Set", type.getName(0)); + assertEquals(1, type.sizeMCTypeArguments()); + assertTrue(type.getMCTypeArgument().getMCTypeOpt().isPresent()); + assertTrue(type.getMCTypeArgument().getMCTypeOpt().get() instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("String"), ((ASTMCQualifiedType) type.getMCTypeArgument(0).getMCTypeOpt().get()).getNameList()); + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void testCreateSetTypeOfName() { + ASTMCSetType type = mcTypeFacade.createSetTypeOf("a.b.c.Foo"); + assertEquals(1, type.sizeNames()); + assertEquals("Set", type.getName(0)); + assertEquals(1, type.sizeMCTypeArguments()); + assertTrue(type.getMCTypeArgument().getMCTypeOpt().isPresent()); + assertTrue(type.getMCTypeArgument().getMCTypeOpt().get() instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("a", "b", "c", "Foo"), ((ASTMCQualifiedType) type.getMCTypeArgument(0).getMCTypeOpt().get()).getNameList()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCreateSetTypeOfType() { + ASTMCSetType type = mcTypeFacade.createSetTypeOf(mcTypeFacade.createQualifiedType("a.b.c.Foo")); + assertEquals(1, type.sizeNames()); + assertEquals("Set", type.getName(0)); + assertEquals(1, type.sizeMCTypeArguments()); + assertTrue(type.getMCTypeArgument().getMCTypeOpt().isPresent()); + assertTrue(type.getMCTypeArgument().getMCTypeOpt().get() instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("a", "b", "c", "Foo"), ((ASTMCQualifiedType) type.getMCTypeArgument(0).getMCTypeOpt().get()).getNameList()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCreateSetTypeOfTypeArgument() { + ASTMCSetType type = mcTypeFacade.createSetTypeOf(mcTypeFacade.createBasicTypeArgumentOf("a.b.c.Foo")); + assertEquals(1, type.sizeNames()); + assertEquals("Set", type.getName(0)); + assertEquals(1, type.sizeMCTypeArguments()); + assertTrue(type.getMCTypeArgument().getMCTypeOpt().isPresent()); + assertTrue(type.getMCTypeArgument().getMCTypeOpt().get() instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("a", "b", "c", "Foo"), ((ASTMCQualifiedType) type.getMCTypeArgument(0).getMCTypeOpt().get()).getNameList()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCreateCollectionTypeOfClass() { + ASTMCGenericType type = mcTypeFacade.createCollectionTypeOf(String.class); + assertEquals(1, type.sizeNames()); + assertEquals("Collection", type.getName(0)); + assertEquals(1, type.sizeMCTypeArguments()); + assertTrue(type.getMCTypeArgument(0).getMCTypeOpt().isPresent()); + assertTrue(type.getMCTypeArgument(0).getMCTypeOpt().get() instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("String"), ((ASTMCQualifiedType) type.getMCTypeArgument(0).getMCTypeOpt().get()).getNameList()); + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void testCreateCollectionTypeOfName() { + ASTMCGenericType type = mcTypeFacade.createCollectionTypeOf("a.b.c.Foo"); + assertEquals(1, type.sizeNames()); + assertEquals("Collection", type.getName(0)); + assertEquals(1, type.sizeMCTypeArguments()); + assertTrue(type.getMCTypeArgument(0).getMCTypeOpt().isPresent()); + assertTrue(type.getMCTypeArgument(0).getMCTypeOpt().get() instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("a", "b", "c", "Foo"), ((ASTMCQualifiedType) type.getMCTypeArgument(0).getMCTypeOpt().get()).getNameList()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCreateCollectionTypeOfType() { + ASTMCGenericType type = mcTypeFacade.createCollectionTypeOf(mcTypeFacade.createQualifiedType("a.b.c.Foo")); + assertEquals(1, type.sizeNames()); + assertEquals("Collection", type.getName(0)); + assertEquals(1, type.sizeMCTypeArguments()); + assertTrue(type.getMCTypeArgument(0).getMCTypeOpt().isPresent()); + assertTrue(type.getMCTypeArgument(0).getMCTypeOpt().get() instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("a", "b", "c", "Foo"), ((ASTMCQualifiedType) type.getMCTypeArgument(0).getMCTypeOpt().get()).getNameList()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCreateMapTypeOfClass() { + ASTMCMapType type = mcTypeFacade.createMapTypeOf(String.class, Integer.class); + assertEquals(1, type.sizeNames()); + assertEquals("Map", type.getName(0)); + assertEquals(2, type.sizeMCTypeArguments()); + assertTrue(type.getKey().getMCTypeOpt().isPresent()); + assertTrue(type.getKey().getMCTypeOpt().get() instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("String"), ((ASTMCQualifiedType) type.getKey().getMCTypeOpt().get()).getNameList()); + + assertTrue(type.getValue().getMCTypeOpt().isPresent()); + assertTrue(type.getValue().getMCTypeOpt().get() instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("Integer"), ((ASTMCQualifiedType) type.getValue().getMCTypeOpt().get()).getNameList()); + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void testCreateMapTypeOfName() { + ASTMCMapType type = mcTypeFacade.createMapTypeOf("a.b.c.Foo", "d.e.f.Bla"); + assertEquals(1, type.sizeNames()); + assertEquals("Map", type.getName(0)); + assertEquals(2, type.sizeMCTypeArguments()); + assertTrue(type.getKey().getMCTypeOpt().isPresent()); + assertTrue(type.getKey().getMCTypeOpt().get() instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("a", "b", "c", "Foo"), ((ASTMCQualifiedType) type.getKey().getMCTypeOpt().get()).getNameList()); + + assertTrue(type.getValue().getMCTypeOpt().isPresent()); + assertTrue(type.getValue().getMCTypeOpt().get() instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("d", "e", "f", "Bla"), ((ASTMCQualifiedType) type.getValue().getMCTypeOpt().get()).getNameList()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCreateMapTypeOfType() { + ASTMCMapType type = mcTypeFacade.createMapTypeOf(mcTypeFacade.createQualifiedType("a.b.c.Foo"), mcTypeFacade.createQualifiedType("d.e.f.Bla")); + assertEquals(1, type.sizeNames()); + assertEquals("Map", type.getName(0)); + assertEquals(2, type.sizeMCTypeArguments()); + assertTrue(type.getKey().getMCTypeOpt().isPresent()); + assertTrue(type.getKey().getMCTypeOpt().get() instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("a", "b", "c", "Foo"), ((ASTMCQualifiedType) type.getKey().getMCTypeOpt().get()).getNameList()); + + assertTrue(type.getValue().getMCTypeOpt().isPresent()); + assertTrue(type.getValue().getMCTypeOpt().get() instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("d", "e", "f", "Bla"), ((ASTMCQualifiedType) type.getValue().getMCTypeOpt().get()).getNameList()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCreateMapTypeOfTypeArgument() { + ASTMCMapType type = mcTypeFacade.createMapTypeOf(mcTypeFacade.createBasicTypeArgumentOf("a.b.c.Foo"), mcTypeFacade.createBasicTypeArgumentOf("d.e.f.Bla")); + assertEquals(1, type.sizeNames()); + assertEquals("Map", type.getName(0)); + assertEquals(2, type.sizeMCTypeArguments()); + assertTrue(type.getKey().getMCTypeOpt().isPresent()); + assertTrue(type.getKey().getMCTypeOpt().get() instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("a", "b", "c", "Foo"), ((ASTMCQualifiedType) type.getKey().getMCTypeOpt().get()).getNameList()); + + assertTrue(type.getValue().getMCTypeOpt().isPresent()); + assertTrue(type.getValue().getMCTypeOpt().get() instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("d", "e", "f", "Bla"), ((ASTMCQualifiedType) type.getValue().getMCTypeOpt().get()).getNameList()); + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void testCreateBasicGenericTypeOfNameList() { + ASTMCBasicTypeArgument basicTypeArgumentOfFoo = mcTypeFacade.createBasicTypeArgumentOf("a.b.c.Foo"); + ASTMCBasicTypeArgument basicTypeArgumentOfBla = mcTypeFacade.createBasicTypeArgumentOf("d.e.f.Bla"); + + ASTMCBasicGenericType type = mcTypeFacade.createBasicGenericTypeOf( + Lists.newArrayList("my", "special", "GenericType"), Lists.newArrayList(basicTypeArgumentOfFoo, basicTypeArgumentOfBla)); + assertEquals(3, type.sizeNames()); + assertEquals(Lists.newArrayList("my","special","GenericType"), type.getNameList()); + assertEquals(2, type.sizeMCTypeArguments()); + assertTrue(type.getMCTypeArgument(0).getMCTypeOpt().isPresent()); + assertTrue(type.getMCTypeArgument(0).getMCTypeOpt().get() instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("a", "b", "c", "Foo"), ((ASTMCQualifiedType) type.getMCTypeArgument(0).getMCTypeOpt().get()).getNameList()); + + assertTrue(type.getMCTypeArgument(1).getMCTypeOpt().isPresent()); + assertTrue(type.getMCTypeArgument(1).getMCTypeOpt().get() instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("d", "e", "f", "Bla"), ((ASTMCQualifiedType) type.getMCTypeArgument(1).getMCTypeOpt().get()).getNameList()); + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void testCreateBasicGenericTypeOfArgumentList() { + ASTMCBasicTypeArgument basicTypeArgumentOfFoo = mcTypeFacade.createBasicTypeArgumentOf("a.b.c.Foo"); + ASTMCBasicTypeArgument basicTypeArgumentOfBla = mcTypeFacade.createBasicTypeArgumentOf("d.e.f.Bla"); + + ASTMCBasicGenericType type = mcTypeFacade.createBasicGenericTypeOf( + "my.special.GenericType", Lists.newArrayList(basicTypeArgumentOfFoo, basicTypeArgumentOfBla)); + assertEquals(3, type.sizeNames()); + assertEquals(Lists.newArrayList("my","special","GenericType"), type.getNameList()); + assertEquals(2, type.sizeMCTypeArguments()); + assertTrue(type.getMCTypeArgument(0).getMCTypeOpt().isPresent()); + assertTrue(type.getMCTypeArgument(0).getMCTypeOpt().get() instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("a", "b", "c", "Foo"), ((ASTMCQualifiedType) type.getMCTypeArgument(0).getMCTypeOpt().get()).getNameList()); + + assertTrue(type.getMCTypeArgument(1).getMCTypeOpt().isPresent()); + assertTrue(type.getMCTypeArgument(1).getMCTypeOpt().get() instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("d", "e", "f", "Bla"), ((ASTMCQualifiedType) type.getMCTypeArgument(1).getMCTypeOpt().get()).getNameList()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCreateBasicGenericTypeOfArgumentVarArgs() { + ASTMCBasicTypeArgument basicTypeArgumentOfFoo = mcTypeFacade.createBasicTypeArgumentOf("a.b.c.Foo"); + ASTMCBasicTypeArgument basicTypeArgumentOfBla = mcTypeFacade.createBasicTypeArgumentOf("d.e.f.Bla"); + + ASTMCBasicGenericType type = mcTypeFacade.createBasicGenericTypeOf( + "my.special.GenericType", basicTypeArgumentOfFoo, basicTypeArgumentOfBla); + assertEquals(3, type.sizeNames()); + assertEquals(Lists.newArrayList("my","special","GenericType"), type.getNameList()); + assertEquals(2, type.sizeMCTypeArguments()); + assertTrue(type.getMCTypeArgument(0).getMCTypeOpt().isPresent()); + assertTrue(type.getMCTypeArgument(0).getMCTypeOpt().get() instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("a", "b", "c", "Foo"), ((ASTMCQualifiedType) type.getMCTypeArgument(0).getMCTypeOpt().get()).getNameList()); + + assertTrue(type.getMCTypeArgument(1).getMCTypeOpt().isPresent()); + assertTrue(type.getMCTypeArgument(1).getMCTypeOpt().get() instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("d", "e", "f", "Bla"), ((ASTMCQualifiedType) type.getMCTypeArgument(1).getMCTypeOpt().get()).getNameList()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCreateBasicGenericTypeOfArgumentString() { + ASTMCBasicGenericType type = mcTypeFacade.createBasicGenericTypeOf( + "my.special.GenericType", "a.b.c.Foo", "d.e.f.Bla"); + assertEquals(3, type.sizeNames()); + assertEquals(Lists.newArrayList("my","special","GenericType"), type.getNameList()); + assertEquals(2, type.sizeMCTypeArguments()); + assertTrue(type.getMCTypeArgument(0).getMCTypeOpt().isPresent()); + assertTrue(type.getMCTypeArgument(0).getMCTypeOpt().get() instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("a", "b", "c", "Foo"), ((ASTMCQualifiedType) type.getMCTypeArgument(0).getMCTypeOpt().get()).getNameList()); + + assertTrue(type.getMCTypeArgument(1).getMCTypeOpt().isPresent()); + assertTrue(type.getMCTypeArgument(1).getMCTypeOpt().get() instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("d", "e", "f", "Bla"), ((ASTMCQualifiedType) type.getMCTypeArgument(1).getMCTypeOpt().get()).getNameList()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCreateArrayTypeString() { + ASTMCArrayType type = mcTypeFacade.createArrayType("int", 3); + assertEquals(3, type.getDimensions()); + assertTrue(type.getMCType() instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("int"), ((ASTMCQualifiedType) type.getMCType()).getNameList()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCreateArrayTypeClass() { + ASTMCArrayType type = mcTypeFacade.createArrayType(Integer.class, 3); + assertEquals(3, type.getDimensions()); + assertTrue(type.getMCType() instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("Integer"), ((ASTMCQualifiedType) type.getMCType()).getNameList()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCreateArrayTypeType() { + ASTMCArrayType type = mcTypeFacade.createArrayType(mcTypeFacade.createQualifiedType("int"), 3); + assertEquals(3, type.getDimensions()); + assertTrue(type.getMCType() instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("int"), ((ASTMCQualifiedType) type.getMCType()).getNameList()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCreateVoidType() { + ASTMCVoidType type = mcTypeFacade.createVoidType(); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCreateBooleanType() { + ASTMCPrimitiveType type = mcTypeFacade.createBooleanType(); + assertTrue(type.isBoolean()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testIsBooleanType() { + ASTMCPrimitiveType booleanType = mcTypeFacade.createBooleanType(); + assertTrue(mcTypeFacade.isBooleanType(booleanType)); + + ASTMCType stringType = mcTypeFacade.createStringType(); + assertFalse(mcTypeFacade.isBooleanType(stringType)); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCreateIntType() { + ASTMCPrimitiveType type = mcTypeFacade.createIntType(); + assertTrue(type.isInt()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCreatePrimitiveType() { + ASTMCType type = mcTypeFacade.createStringType(); + assertTrue(type instanceof ASTMCQualifiedType); + assertEquals(Lists.newArrayList("String"), ((ASTMCQualifiedType) type).getNameList()); + + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/MCollectionTypesCorrectStateTest.java b/monticore-grammar/src/test/java/de/monticore/types/MCollectionTypesCorrectStateTest.java new file mode 100644 index 0000000000..66b32d8850 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/MCollectionTypesCorrectStateTest.java @@ -0,0 +1,212 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types; + +import com.google.common.collect.Lists; +import de.monticore.types.mcbasictypes._ast.ASTMCQualifiedName; +import de.monticore.types.mcbasictypes._ast.ASTMCQualifiedType; +import de.monticore.types.mccollectiontypes.MCCollectionTypesMill; +import de.monticore.types.mccollectiontypes._ast.*; +import de.monticore.types.mccollectiontypeswithoutprimitivestest._parser.MCCollectionTypesWithoutPrimitivesTestParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.*; + +public class MCollectionTypesCorrectStateTest { + + private ASTMCListType listTypeParser; + + private ASTMCOptionalType optTypeParser; + + private ASTMCSetType setTypeParser; + + private ASTMCMapType mapTypeParser; + + private ASTMCBasicTypeArgument typeArgumentInt; + + private ASTMCBasicTypeArgument typeArgumentString; + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + } + + @Before + public void setUp() throws IOException { + MCCollectionTypesWithoutPrimitivesTestParser parser = new MCCollectionTypesWithoutPrimitivesTestParser(); + Optional listTypeParser = parser.parse_StringMCListType("List"); + assertFalse(parser.hasErrors()); + assertTrue(listTypeParser.isPresent()); + this.listTypeParser = listTypeParser.get(); + + Optional optionalTypeParser = parser.parse_StringMCOptionalType("Optional"); + assertFalse(parser.hasErrors()); + assertTrue(optionalTypeParser.isPresent()); + this.optTypeParser = optionalTypeParser.get(); + + Optional setType = parser.parse_StringMCSetType("Set"); + assertFalse(parser.hasErrors()); + assertTrue(setType.isPresent()); + this.setTypeParser = setType.get(); + + Optional mapType = parser.parse_StringMCMapType("Map"); + assertFalse(parser.hasErrors()); + assertTrue(mapType.isPresent()); + this.mapTypeParser = mapType.get(); + + + ASTMCQualifiedName integerName = MCCollectionTypesMill.mCQualifiedNameBuilder().setPartsList(Lists.newArrayList("Integer")).build(); + ASTMCQualifiedType integerType = MCCollectionTypesMill.mCQualifiedTypeBuilder().setMCQualifiedName(integerName).build(); + typeArgumentInt = MCCollectionTypesMill.mCBasicTypeArgumentBuilder().setMCQualifiedType(integerType).build(); + + ASTMCQualifiedName stringName = MCCollectionTypesMill.mCQualifiedNameBuilder().setPartsList(Lists.newArrayList("String")).build(); + ASTMCQualifiedType stringType = MCCollectionTypesMill.mCQualifiedTypeBuilder().setMCQualifiedName(stringName).build(); + typeArgumentString = MCCollectionTypesMill.mCBasicTypeArgumentBuilder().setMCQualifiedType(stringType).build(); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void mCListTypeNameListFinal() { + // test that MCListType only contains one element 'List' + assertEquals(1, listTypeParser.getNameList().size()); + assertEquals("List", listTypeParser.getName(0)); + + // set name over getter + listTypeParser.getNameList().set(0, "Foo"); + assertEquals(1, listTypeParser.getNameList().size()); + assertEquals("List", listTypeParser.getName(0)); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void mCOptionalTypeNameListFinal() { + // test that MCListType only contains one element 'Optional' + assertEquals(1, optTypeParser.getNameList().size()); + assertEquals("Optional", optTypeParser.getName(0)); + + // set name over getter + optTypeParser.getNameList().set(0, "Foo"); + assertEquals(1, optTypeParser.getNameList().size()); + assertEquals("Optional", optTypeParser.getName(0)); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void mCSetTypeNameListFinal() { + // test that MCListType only contains one element 'Set' + assertEquals(1, setTypeParser.getNameList().size()); + assertEquals("Set", setTypeParser.getName(0)); + + // set name over getter + setTypeParser.getNameList().set(0, "Foo"); + assertEquals(1, setTypeParser.getNameList().size()); + assertEquals("Set", setTypeParser.getName(0)); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void mCMapTypeNameListFinal() throws IOException { + // test that MCListType only contains one element 'Map' + + assertEquals(1, mapTypeParser.getNameList().size()); + assertEquals("Map", mapTypeParser.getName(0)); + + // set name over getter + mapTypeParser.getNameList().set(0, "Foo"); + assertEquals(1, mapTypeParser.getNameList().size()); + assertEquals("Map", mapTypeParser.getName(0)); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void mCListTypeNameListFinalFromBuilder() { + // test that MCListType only contains one element 'List' + ASTMCListType listBuild = MCCollectionTypesMill.mCListTypeBuilder() + .setMCTypeArgument(typeArgumentInt) + .build(); + assertEquals(1, listBuild.getNameList().size()); + assertEquals("List", listBuild.getName(0)); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void mCOptionalTypeNameListFinalFromBuilder() { + // test that MCListType only contains one element 'Optional' + ASTMCOptionalType optBuild = MCCollectionTypesMill.mCOptionalTypeBuilder() + .setMCTypeArgument(typeArgumentInt) + .build(); + assertEquals(1, optBuild.getNameList().size()); + assertEquals("Optional", optBuild.getName(0)); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void mCSetTypeNameListFinalFromBuilder() { + // test that MCListType only contains one element 'Set' + ASTMCSetType setBuild = MCCollectionTypesMill.mCSetTypeBuilder() + .setMCTypeArgument(typeArgumentInt) + .build(); + assertEquals(1, setBuild.getNameList().size()); + assertEquals("Set", setBuild.getName(0)); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void mCMapTypeNameListFinalFromBuilder() { + // test that MCListType only contains one element 'Map' + ASTMCMapType mapBuildNoName = MCCollectionTypesMill.mCMapTypeBuilder() + .setKey(typeArgumentInt) + .setValue(typeArgumentString) + .build(); + assertEquals(1, mapBuildNoName.getNameList().size()); + assertEquals("Map", mapBuildNoName.getName(0)); + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void mCListTypeSetTypeArgument() { + listTypeParser.setMCTypeArgument(typeArgumentString); + assertEquals(1, listTypeParser.getMCTypeArgumentList().size()); + assertEquals("String", listTypeParser.getMCTypeArgument().printType()); + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void mcOptionalTypeSetTypeArgument() { + optTypeParser.setMCTypeArgument(typeArgumentString); + assertEquals(1, optTypeParser.getMCTypeArgumentList().size()); + assertEquals("String", optTypeParser.getMCTypeArgument().printType()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void mcMapTypeSetKey() { + mapTypeParser.setKey(typeArgumentString); + assertEquals(2, mapTypeParser.getMCTypeArgumentList().size()); + assertEquals("String", mapTypeParser.getKey().printType()); + assertEquals("Integer", mapTypeParser.getValue().printType()); + + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/TestGenerics.java b/monticore-grammar/src/test/java/de/monticore/types/TestGenerics.java new file mode 100644 index 0000000000..c3ec58626c --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/TestGenerics.java @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types; + +public class TestGenerics { + + public T i; + + public class Inner { + public U u; + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/TestGenericsTest.java b/monticore-grammar/src/test/java/de/monticore/types/TestGenericsTest.java new file mode 100644 index 0000000000..d7baf35774 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/TestGenericsTest.java @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types; + +public class TestGenericsTest { + + public void main() { + TestGenerics k = new TestGenerics<>(); + TestGenerics.Inner ku = k.new Inner(); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/check/AbstractDeriveTest.java b/monticore-grammar/src/test/java/de/monticore/types/check/AbstractDeriveTest.java new file mode 100644 index 0000000000..5c48b7c04b --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/check/AbstractDeriveTest.java @@ -0,0 +1,180 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import com.google.common.collect.Lists; +import de.monticore.expressions.abstracttypechecktest.AbstractTypeCheckTestMill; +import de.monticore.expressions.abstracttypechecktest._parser.AbstractTypeCheckTestParser; +import de.monticore.expressions.abstracttypechecktest._symboltable.IAbstractTypeCheckTestScope; +import de.monticore.expressions.abstracttypechecktest._visitor.AbstractTypeCheckTestTraverser; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.FunctionSymbol; +import de.monticore.symbols.basicsymbols._symboltable.IBasicSymbolsScope; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.symbols.basicsymbols._symboltable.VariableSymbol; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static de.monticore.types.check.DefsTypeBasic.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class AbstractDeriveTest { + + private IAbstractTypeCheckTestScope scope; + private FlatExpressionScopeSetter flatExpressionScopeSetter; + private AbstractTypeCheckTestTraverser traverser; + + // Parser used for convenience: + // (may be any other Parser that understands CommonExpressions) + AbstractTypeCheckTestParser p = new AbstractTypeCheckTestParser(); + + // This is an auxiliary + FullDeriveFromCombineExpressionsWithLiteralsAbstract derLit = new FullDeriveFromCombineExpressionsWithLiteralsAbstract(); + + // other arguments not used (and therefore deliberately null) + + // This is the TypeChecker under Test: + TypeCalculator tc = new TypeCalculator(null, derLit); + + + @Before + public void init(){ + LogStub.init(); + Log.enableFailQuick(false); + AbstractTypeCheckTestMill.reset(); + AbstractTypeCheckTestMill.init(); + + scope = AbstractTypeCheckTestMill.scope(); + scope.setEnclosingScope(null); + scope.setExportingSymbols(true); + scope.setAstNode(null); + scope.setEnclosingScope(AbstractTypeCheckTestMill.globalScope()); + AbstractTypeCheckTestMill.globalScope().clear(); + BasicSymbolsMill.initializePrimitives(); + + TypeSymbol person = AbstractTypeCheckTestMill.typeSymbolBuilder() + .setName("Person") + .setSpannedScope(AbstractTypeCheckTestMill.scope()) + .setEnclosingScope(scope) + .build(); + person.setSpannedScope(AbstractTypeCheckTestMill.scope()); + add2scope(scope, person); + TypeSymbol student = AbstractTypeCheckTestMill.typeSymbolBuilder() + .setName("Student") + .setSpannedScope(AbstractTypeCheckTestMill.scope()) + .setSuperTypesList(Lists.newArrayList(SymTypeExpressionFactory.createTypeObject("Person",scope))) + .setEnclosingScope(scope) + .build(); + add2scope(scope, student); + student.setSpannedScope(AbstractTypeCheckTestMill.scope()); + TypeSymbol firstsemesterstudent = AbstractTypeCheckTestMill.typeSymbolBuilder() + .setName("FirstSemesterStudent") + .setSpannedScope(AbstractTypeCheckTestMill.scope()) + .setSuperTypesList(Lists.newArrayList(SymTypeExpressionFactory.createTypeObject("Student",scope))) + .setEnclosingScope(scope) + .build(); + firstsemesterstudent.setSpannedScope(AbstractTypeCheckTestMill.scope()); + TypeSymbol address = AbstractTypeCheckTestMill.typeSymbolBuilder() + .setName("Address") + .setSpannedScope(AbstractTypeCheckTestMill.scope()) + .setSuperTypesList(Lists.newArrayList()) + .setEnclosingScope(person.getSpannedScope()) + .build(); + add2scope(person.getSpannedScope(), address); + add2scope(person.getSpannedScope(), method("foo", SymTypeExpressionFactory.createTypeVoid())); + add2scope(person.getSpannedScope(), field("bar", SymTypeExpressionFactory.createPrimitive("int"))); + add2scope(scope, firstsemesterstudent); + add2scope(scope, field("person1", SymTypeExpressionFactory.createTypeObject("Person", scope))); + add2scope(scope, field("firstsemester", SymTypeExpressionFactory. + createTypeObject("FirstSemesterStudent", scope))); + tc = new TypeCalculator(null, derLit); + flatExpressionScopeSetter = new FlatExpressionScopeSetter(scope); + traverser = getTraverser(flatExpressionScopeSetter); + } + + public void add2scope(IBasicSymbolsScope scope, TypeSymbol type){ + type.setEnclosingScope(scope); + scope.add(type); + } + + public void add2scope(IBasicSymbolsScope scope, VariableSymbol variable){ + variable.setEnclosingScope(scope); + scope.add(variable); + } + + public void add2scope(IBasicSymbolsScope scope, FunctionSymbol function){ + function.setEnclosingScope(scope); + scope.add(function); + } + + public AbstractTypeCheckTestTraverser getTraverser(FlatExpressionScopeSetter flatExpressionScopeSetter){ + AbstractTypeCheckTestTraverser traverser = AbstractTypeCheckTestMill.traverser(); + traverser.add4CommonExpressions(flatExpressionScopeSetter); + traverser.add4ExpressionsBasis(flatExpressionScopeSetter); + traverser.add4MCCommonLiterals(flatExpressionScopeSetter); + return traverser; + } + + @Test + public void testFieldAccessInnerVariables() throws IOException { + Optional expr = p.parse_StringExpression("person1.bar"); + assertTrue(expr.isPresent()); + expr.get().accept(traverser); + + assertEquals("int", tc.typeOf(expr.get()).print()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testFieldAccessInnerTypes() throws IOException { + Optional expr = p.parse_StringExpression("person1.Address"); + assertTrue(expr.isPresent()); + expr.get().accept(traverser); + + assertEquals("Address", tc.typeOf(expr.get()).print()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testCallInnerMethods() throws IOException { + Optional expr = p.parse_StringExpression("person1.foo()"); + assertTrue(expr.isPresent()); + expr.get().accept(traverser); + + assertEquals("void", tc.typeOf(expr.get()).print()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testInheritanceVariables() throws IOException { + Optional expr = p.parse_StringExpression("firstsemester.bar"); + assertTrue(expr.isPresent()); + expr.get().accept(traverser); + + assertEquals("int", tc.typeOf(expr.get()).print()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testInheritanceMethods() throws IOException { + Optional expr = p.parse_StringExpression("firstsemester.foo()"); + assertTrue(expr.isPresent()); + expr.get().accept(traverser); + + assertEquals("void", tc.typeOf(expr.get()).print()); + + assertTrue(Log.getFindings().isEmpty()); + } + + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/check/DefiningSymbolsTest.java b/monticore-grammar/src/test/java/de/monticore/types/check/DefiningSymbolsTest.java new file mode 100644 index 0000000000..9ed0b87648 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/check/DefiningSymbolsTest.java @@ -0,0 +1,184 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import com.google.common.collect.Lists; +import de.monticore.expressions.combineexpressionswithliterals.CombineExpressionsWithLiteralsMill; +import de.monticore.expressions.combineexpressionswithliterals._parser.CombineExpressionsWithLiteralsParser; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.ICombineExpressionsWithLiteralsArtifactScope; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.ICombineExpressionsWithLiteralsGlobalScope; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.ICombineExpressionsWithLiteralsScope; +import de.monticore.expressions.combineexpressionswithliterals._visitor.CombineExpressionsWithLiteralsTraverser; +import de.monticore.expressions.commonexpressions._ast.ASTCallExpression; +import de.monticore.expressions.commonexpressions._ast.ASTFieldAccessExpression; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.expressionsbasis._ast.ASTNameExpression; +import de.monticore.expressions.expressionsbasis._symboltable.IExpressionsBasisScope; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.symbols.oosymbols._symboltable.FieldSymbol; +import de.monticore.symbols.oosymbols._symboltable.MethodSymbol; +import de.monticore.symboltable.ISymbol; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class DefiningSymbolsTest { + + protected ICombineExpressionsWithLiteralsArtifactScope as; + protected ICombineExpressionsWithLiteralsScope typeScope; + protected CombineExpressionsWithLiteralsParser p; + protected FullDeriveFromCombineExpressionsWithLiterals deriver; + protected FullSynthesizeFromCombineExpressionsWithLiterals synthesizer; + protected FieldSymbol e; + protected MethodSymbol add; + protected TypeSymbol listOfInt; + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + + CombineExpressionsWithLiteralsMill.reset(); + CombineExpressionsWithLiteralsMill.init(); + BasicSymbolsMill.initializePrimitives(); + + p = CombineExpressionsWithLiteralsMill.parser(); + + ICombineExpressionsWithLiteralsGlobalScope gs = CombineExpressionsWithLiteralsMill.globalScope(); + as = CombineExpressionsWithLiteralsMill.artifactScope(); + as.setName("ListOfInt"); + gs.addSubScope(as); + + e = DefsTypeBasic.field("e", SymTypeExpressionFactory.createPrimitive("int")); + e.setIsStatic(true); + add = DefsTypeBasic.method("add", new SymTypeVoid()); + add.setIsStatic(true); + DefsTypeBasic.add(add, e); + listOfInt = DefsTypeBasic.type("ListOfInt", Lists.newArrayList(add), Lists.newArrayList(e), Lists.newArrayList(), Lists.newArrayList(), as); + as.add(listOfInt); + typeScope = (ICombineExpressionsWithLiteralsScope) listOfInt.getSpannedScope(); + deriver = new FullDeriveFromCombineExpressionsWithLiterals(); + synthesizer = new FullSynthesizeFromCombineExpressionsWithLiterals(); + } + + @Test + public void testQualified() throws IOException { + Optional expr = p.parse_StringExpression("ListOfInt.e"); + assertTrue(expr.isPresent()); + assertTrue(expr.get() instanceof ASTFieldAccessExpression); + ASTFieldAccessExpression e = (ASTFieldAccessExpression) expr.get(); + e.accept(getFlatExpressionScopeSetter(as)); + TypeCalculator tc = new TypeCalculator(null, deriver); + tc.typeOf(e); + assertTrue(e.getDefiningSymbol().isPresent()); + ISymbol definingSymbol = e.getDefiningSymbol().get(); + assertTrue(definingSymbol instanceof FieldSymbol); + assertEquals("e", definingSymbol.getName()); + + assertTrue(e.getExpression() instanceof ASTNameExpression); + ASTNameExpression listOfInt = (ASTNameExpression) e.getExpression(); + assertTrue(listOfInt.getDefiningSymbol().isPresent()); + assertEquals(listOfInt.getDefiningSymbol().get(), this.listOfInt); + + expr = p.parse_StringExpression("ListOfInt.add(3)"); + assertTrue(expr.isPresent()); + assertTrue(expr.get() instanceof ASTCallExpression); + ASTCallExpression add = (ASTCallExpression) expr.get(); + add.accept(getFlatExpressionScopeSetter(as)); + tc.typeOf(add); + assertTrue(add.getDefiningSymbol().isPresent()); + definingSymbol = add.getDefiningSymbol().get(); + assertTrue(definingSymbol instanceof MethodSymbol); + assertEquals("add", definingSymbol.getName()); + + assertTrue(add.getExpression() instanceof ASTFieldAccessExpression); + assertTrue(((ASTFieldAccessExpression) add.getExpression()).getExpression() instanceof ASTNameExpression); + listOfInt = (ASTNameExpression) ((ASTFieldAccessExpression) add.getExpression()).getExpression(); + assertTrue(listOfInt.getDefiningSymbol().isPresent()); + assertEquals(listOfInt.getDefiningSymbol().get(), this.listOfInt); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testUnqualified() throws IOException { + Optional expr = p.parse_StringExpression("e"); + assertTrue(expr.isPresent()); + assertTrue(expr.get() instanceof ASTNameExpression); + ASTNameExpression e = (ASTNameExpression) expr.get(); + e.accept(getFlatExpressionScopeSetter(typeScope)); + TypeCalculator tc = new TypeCalculator(null, deriver); + tc.typeOf(e); + assertTrue(e.getDefiningSymbol().isPresent()); + ISymbol definingSymbol = e.getDefiningSymbol().get(); + assertTrue(definingSymbol instanceof FieldSymbol); + assertEquals("e", definingSymbol.getName()); + + expr = p.parse_StringExpression("add(3)"); + assertTrue(expr.isPresent()); + assertTrue(expr.get() instanceof ASTCallExpression); + ASTCallExpression add = (ASTCallExpression) expr.get(); + add.accept(getFlatExpressionScopeSetter(typeScope)); + tc.typeOf(add); + assertTrue(add.getDefiningSymbol().isPresent()); + definingSymbol = add.getDefiningSymbol().get(); + assertTrue(definingSymbol instanceof MethodSymbol); + assertEquals("add", definingSymbol.getName()); + + expr = p.parse_StringExpression("ListOfInt"); + assertTrue(expr.isPresent()); + assertTrue(expr.get() instanceof ASTNameExpression); + ASTNameExpression listOfInt = (ASTNameExpression) expr.get(); + listOfInt.accept(getFlatExpressionScopeSetter(as)); + tc.typeOf(listOfInt); + assertTrue(listOfInt.getDefiningSymbol().isPresent()); + assertEquals(listOfInt.getDefiningSymbol().get(), this.listOfInt); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testTypes() throws IOException { + Optional type = as.resolveType("int"); + assertTrue(type.isPresent()); + Optional intType = p.parse_StringMCType("int"); + assertTrue(intType.isPresent()); + intType.get().accept(getFlatExpressionScopeSetter(as)); + TypeCalculator tc = new TypeCalculator(synthesizer, null); + tc.symTypeFromAST(intType.get()); + assertTrue(intType.get().getDefiningSymbol().isPresent()); + assertEquals(intType.get().getDefiningSymbol().get(), type.get()); + + Optional listOfIntType = p.parse_StringMCType("ListOfInt"); + assertTrue(listOfIntType.isPresent()); + listOfIntType.get().accept(getFlatExpressionScopeSetter(as)); + tc.symTypeFromAST(listOfIntType.get()); + assertTrue(listOfIntType.get().getDefiningSymbol().isPresent()); + assertEquals(listOfIntType.get().getDefiningSymbol().get(), this.listOfInt); + + assertTrue(Log.getFindings().isEmpty()); + } + + protected CombineExpressionsWithLiteralsTraverser getFlatExpressionScopeSetter(IExpressionsBasisScope scope){ + CombineExpressionsWithLiteralsTraverser traverser = CombineExpressionsWithLiteralsMill.traverser(); + FlatExpressionScopeSetter flatExpressionScopeSetter = new FlatExpressionScopeSetter(scope); + traverser.add4ExpressionsBasis(flatExpressionScopeSetter); + traverser.add4AssignmentExpressions(flatExpressionScopeSetter); + traverser.add4CommonExpressions(flatExpressionScopeSetter); + traverser.add4JavaClassExpressions(flatExpressionScopeSetter); + traverser.add4BitExpressions(flatExpressionScopeSetter); + traverser.add4MCBasicTypes(flatExpressionScopeSetter); + traverser.add4MCCollectionTypes(flatExpressionScopeSetter); + traverser.add4MCSimpleGenericTypes(flatExpressionScopeSetter); + traverser.add4MCCommonLiterals(flatExpressionScopeSetter); + return traverser; + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/check/DefsTypeBasic.java b/monticore-grammar/src/test/java/de/monticore/types/check/DefsTypeBasic.java new file mode 100644 index 0000000000..cf84717ddd --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/check/DefsTypeBasic.java @@ -0,0 +1,511 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.*; +import de.monticore.symbols.oosymbols.OOSymbolsMill; +import de.monticore.symbols.oosymbols._symboltable.*; +import de.monticore.symboltable.modifiers.AccessModifier; + +import java.util.List; + +/** + * DefsTypeBasic offers one Symbol-Infrastructure + * including Scopes etc. that is used to provide relevant Symbols. + * + * This infrastructure can be used for testing + */ + +public class DefsTypeBasic { + + // Original initialization + static { + setup(); + } + + /** + * Initialization of the structure (can be called again to reinitialize). + * Setup comes in two phases: + * a) Build the new objects + * b) add the linkage (and that is only necessary with complex objects + */ + public static void setup() { + // Phase A ----- create objects + set_Void(); // only A + set_Null(); // only A + set_thePrimitives(); // only A + set_BoxedPrimitives(); + set_array(); + set_String(); + set_Object(); + // Phase B ---- links + link_array(); + link_String(); + link_Object(); + } + + + /*********************************************************************/ + + /** + * Helpers that efficiently create Symbols + * (which by the way can also later be extended) + */ + public static OOTypeSymbol type(String name) { + return type(name,name); + } + + /** + * create TypeSymbols (some defaults apply) + */ + public static OOTypeSymbol type(String name, String fullName) { + return OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(OOSymbolsMill.scope()) + .setName(name) + .setFullName(fullName) + .setAccessModifier(AccessModifier.ALL_INCLUSION) + .build(); + } + + public static OOTypeSymbol type(String name, List superTypes){ + return OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(OOSymbolsMill.scope()) + .setName(name) + .setFullName(name) + .setSuperTypesList(superTypes) + .build(); + } + + public static OOTypeSymbol type(String name, List superTypes, List typeArguments){ + IOOSymbolsScope spannedScope = OOSymbolsMill.scope(); + OOTypeSymbol ts = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(spannedScope) + .setName(name) + .setFullName(name) + .setSuperTypesList(superTypes) + .build(); + typeArguments.forEach(a -> ts.addTypeVarSymbol(a)); + return ts; + } + + public static OOTypeSymbol type(String name, List methodList, List fieldList, + List superTypeList, List typeVariableList){ + OOTypeSymbol ts = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(OOSymbolsMill.scope()) + .setName(name) + .setFullName(name) + .setSuperTypesList(superTypeList) + .build(); + methodList.forEach(m -> ts.addMethodSymbol(m)); + fieldList.forEach(f -> ts.addFieldSymbol(f)); + typeVariableList.forEach(a -> ts.addTypeVarSymbol(a)); + return ts; + } + + public static OOTypeSymbol type(String name, List methodList, List fieldList, + List superTypeList, List typeVariableList, + IOOSymbolsScope enclosingScope){ + OOTypeSymbol t = OOSymbolsMill.oOTypeSymbolBuilder() + .setEnclosingScope(enclosingScope) + .setSpannedScope(OOSymbolsMill.scope()) + .setName(name) + .setFullName(name) + .setSuperTypesList(superTypeList) + .build(); + methodList.forEach(m -> t.addMethodSymbol(m)); + fieldList.forEach(f -> t.addFieldSymbol(f)); + typeVariableList.forEach(a -> t.addTypeVarSymbol(a)); + + t.getSpannedScope().setEnclosingScope(enclosingScope); + + for(MethodSymbol method: t.getMethodList()){ + method.getSpannedScope().setEnclosingScope(t.getSpannedScope()); + } + return t; + } + + /** + * create TypeVariableSymbols (some defaults apply) + */ + public static TypeVarSymbol typeVariable(String name){ + return OOSymbolsMill.typeVarSymbolBuilder() + .setName(name) + .setFullName(name) + .setSpannedScope(OOSymbolsMill.scope()) + .build(); + } + + public static OOTypeSymbol add(OOTypeSymbol t, FieldSymbol f) { + t.addFieldSymbol(f); + return t; + } + + public static OOTypeSymbol add(OOTypeSymbol t, MethodSymbol m) { + t.addMethodSymbol(m); + return t; + } + + /** + * create FunctionSymbols (some defaults apply) + */ + public static FunctionSymbol function(String name, SymTypeExpression returnType) { + FunctionSymbol f = BasicSymbolsMill.functionSymbolBuilder() + .setSpannedScope(BasicSymbolsMill.scope()) + .setName(name) + .setFullName(name) // can later be adapted, when fullname of Type is known + .setAccessModifier(AccessModifier.ALL_INCLUSION) + .setType(returnType) + .build(); + f.setSpannedScope(BasicSymbolsMill.scope()); + return f; + } + + /** + * create MethodSymbols (some defaults apply) + */ + public static MethodSymbol method(String name, SymTypeExpression returnType) { + MethodSymbol m = OOSymbolsMill.methodSymbolBuilder() + .setSpannedScope(OOSymbolsMill.scope()) + .setName(name) + .setFullName(name) // can later be adapted, when fullname of Type is known + .setAccessModifier(AccessModifier.ALL_INCLUSION) + .setType(returnType) + .build(); + m.setSpannedScope(OOSymbolsMill.scope()); + return m; + } + + public static MethodSymbol add(MethodSymbol m, FieldSymbol f) { + m.getSpannedScope().add(f); + return m; + } + + /** + * create VariableSymbols (some defaults apply) + */ + public static VariableSymbol variable(String name, SymTypeExpression type) { + return BasicSymbolsMill.variableSymbolBuilder() + .setName(name) + .setFullName(name) // can later be adapted, when fullname of Type is known + .setAccessModifier(AccessModifier.ALL_INCLUSION) + .setType(type) + .build(); + } + + /** + * create FieldSymbols (some defaults apply) + */ + public static FieldSymbol field(String name, SymTypeExpression type) { + return OOSymbolsMill.fieldSymbolBuilder() + .setName(name) + .setFullName(name) // can later be adapted, when fullname of Type is known + .setAccessModifier(AccessModifier.ALL_INCLUSION) + .setType(type) + .build(); + } + + /** + * add a Type to a Scope (bidirectional) + */ + public static void add2scope(IOOSymbolsScope p, TypeSymbol s) { + s.setEnclosingScope(p); + p.add(s); + } + + /** + * add an OOType to a Scope (bidirectional) + */ + public static void add2scope(IOOSymbolsScope p, OOTypeSymbol s) { + s.setEnclosingScope(p); + p.add(s); + p.add((TypeSymbol) s); + } + + /** + * add a Variable to a Scope (bidirectional) + */ + public static void add2scope(IOOSymbolsScope p, VariableSymbol s) { + s.setEnclosingScope(p); + p.add(s); + } + + /** + * add a Field (e.g. a Variable) to a Scope (bidirectional) + */ + public static void add2scope(IOOSymbolsScope p, FieldSymbol s) { + s.setEnclosingScope(p); + p.add(s); + } + + /** + * add a Method to a Scope (bidirectional) + */ + public static void add2scope(IOOSymbolsScope p, MethodSymbol s){ + s.setEnclosingScope(p); + p.add(s); + p.add((FunctionSymbol) s); + } + + /** + * add a Function to a Scope (bidirectional) + */ + public static void add2scope(IOOSymbolsScope p, FunctionSymbol s) { + s.setEnclosingScope(p); + p.add(s); + } + + /** + * add a TypeVariable to a Scope (bidirectional) + */ + public static void add2scope(IOOSymbolsScope p, TypeVarSymbol s) { + s.setEnclosingScope(p); + p.add(s); + } + + + /** + * It is tedious to allways add name and fullNamee individually: + * So this functions does that for Types,Methods,Fields afterwards + * (only the Type needs a full name, the rest is added) + */ + public static void completeFullnames(OOTypeSymbol s) { + // in the class Fullname must already set + String prefix = s.getPackageName(); + for (MethodSymbol m : s.getMethodList()) { + completeFullnames(m, prefix); + } + for (FieldSymbol f : s.getFieldList()) { + completeFullnames(f, prefix); + } + } + public static void completeFullnames(MethodSymbol s, String prefix) { + if(s.getFullName() == null || !s.getFullName().contains(".")) { + s.setFullName(prefix + "." + s.getName()); + } + } + public static void completeFullnames(FieldSymbol s, String prefix) { + if(s.getFullName() == null || !s.getFullName().contains(".")) { + s.setFullName(prefix + "." + s.getName()); + } + } + /*********************************************************************/ + + /*********************************************************************/ + + /** + * This is a predefined Symbol describing the + * externally accessible Fields, Methods, etc. of an array + * + * We deliberately choose Java 10 here and take methods from: + * https://docs.oracle.com/javase/10/docs/api/java/util/Arrays.html + * + * TODO RE, ggf. mit ND zusammen + * TODO RE,AB: 1) Besseres Verfahren überlegen, wie man Vordefinierte Signaturen einliest + * (zB durch handgeschriebene Symtabs, die man der RTE beilegt, + * oder auch doch durch Java Code wie den hier?) + * Das untenstehende beispiel ist grausam schlecht) + * + * TODO RE: 2) Die Liste ist unvollständig --> irgendwie vervollstänndigen + * wenige übersetzte beispiele: + * int i[]; i.length; i.toString(); i.equals(Object i); i.wait(long 3,int 3); + * + * TODO RE,AB: (mittelfristig) 2) Konfigurierbarkeit ist zB notwendig + * mit der Target-Java-Version (denn die Checks + * müssen nicht nach Java 10 gehen, sondern evtl. eine frühere Version ...) + * + */ + public static OOTypeSymbol _array; + // SymTypeExpression _arraySymType cannot be defined, because Arrays are generics and have varying arguments + + public static void set_array() { + _array = type("ArrayType"); + } + + public static void link_array() { + // TODO: Offen, Array ist eigentlich generisch? + // zur Zeit aber, leerer Typparameter: .setTypeParameter(new ArrayList<>()); + // die aktuell gelisteten Methoden haben auch keine generischen argumente + + MethodSymbol m; FieldSymbol f; + + // toString() + add(_array, method("toString", _StringSymType)) + .setFullName("java.lang.Object.toString"); + + completeFullnames(_array); + + } + + /*********************************************************************/ + + /** + * This is a predefined Symbol describing the + * externally accessible Fields, Methods, etc. of a String + * + * We deliberately choose Java 10 here and take methods from: + * https://docs.oracle.com/javase/10/docs/api/java/lang/String.html + */ + public static OOTypeSymbol _String; + public static SymTypeOfObject _StringSymType; + + public static void set_String() { + _String = type("String"); + OOTypeSymbol loader = new OOTypeSymbolSurrogate("String"); + loader.setEnclosingScope(createScopeWithString()); + _StringSymType = new SymTypeOfObject(loader); + } + + public static IOOSymbolsScope createScopeWithString() { + OOSymbolsScope typeSymbolsScope = new OOSymbolsScope(); + add2scope(typeSymbolsScope, _String); + return typeSymbolsScope; + } + + public static void link_String() { + MethodSymbol m; FieldSymbol f; + IOOSymbolsScope scope = OOSymbolsMill.scope(); + + _String.setSpannedScope(scope); + completeFullnames(_String); + } + + /*********************************************************************/ + + /** + * This is a predefined Symbol describing the + * externally accessible Fields, Methods, etc. of a Object + * + * We deliberately choose Java 10 here and take methods from: + * https://docs.oracle.com/javase/10/docs/api/java/lang/Object.html + */ + public static OOTypeSymbol _Object; + public static SymTypeOfObject _ObjectSymType; + + public static void set_Object() { + _Object = type("ObjectType"); + } + + public static void link_Object() { + MethodSymbol m; FieldSymbol f; + + completeFullnames(_Object); + OOTypeSymbolSurrogate loader = new OOTypeSymbolSurrogate("Object"); + loader.setEnclosingScope(createScopeWithObject()); + _ObjectSymType = new SymTypeOfObject(loader); + } + + public static IOOSymbolsScope createScopeWithObject() { + OOSymbolsScope typeSymbolsScope = new OOSymbolsScope(); + typeSymbolsScope.add(_Object); + return typeSymbolsScope; + } + + + + /*********************************************************************/ + + /** + * This is the predefined Symbol for all Primitives, such as "int" + * which has empty Fields and Methods + */ + public static SymTypePrimitive _intSymType; + public static SymTypePrimitive _charSymType; + public static SymTypePrimitive _booleanSymType; + public static SymTypePrimitive _doubleSymType; + public static SymTypePrimitive _floatSymType; + public static SymTypePrimitive _longSymType; + public static SymTypePrimitive _byteSymType; + public static SymTypePrimitive _shortSymType; + + public static void set_thePrimitives() { + IBasicSymbolsGlobalScope typeSymbolsScope = BasicSymbolsMill.globalScope(); + OOTypeSymbol loader = new OOTypeSymbolSurrogate("int"); + loader.setEnclosingScope(typeSymbolsScope); + _intSymType = new SymTypePrimitive(loader); + + loader = new OOTypeSymbolSurrogate("boolean"); + loader.setEnclosingScope(typeSymbolsScope); + _booleanSymType = new SymTypePrimitive(loader); + + loader = new OOTypeSymbolSurrogate("char"); + loader.setEnclosingScope(typeSymbolsScope); + _charSymType = new SymTypePrimitive(loader); + + loader = new OOTypeSymbolSurrogate("double"); + loader.setEnclosingScope(typeSymbolsScope); + _doubleSymType = new SymTypePrimitive(loader); + + loader = new OOTypeSymbolSurrogate("float"); + loader.setEnclosingScope(typeSymbolsScope); + _floatSymType = new SymTypePrimitive(loader); + + loader = new OOTypeSymbolSurrogate("long"); + loader.setEnclosingScope(typeSymbolsScope); + _longSymType = new SymTypePrimitive(loader); + + loader = new OOTypeSymbolSurrogate("short"); + loader.setEnclosingScope(typeSymbolsScope); + _shortSymType = new SymTypePrimitive(loader); + + loader = new OOTypeSymbolSurrogate("byte"); + loader.setEnclosingScope(typeSymbolsScope); + _byteSymType = new SymTypePrimitive(loader); + + + } + + /*********************************************************************/ + + /** + * This is the predefined Symbol for boxed Primitives, such as "Integer" + */ + public static SymTypeOfObject _IntegerSymType; + + public static void set_BoxedPrimitives() { + IBasicSymbolsGlobalScope typeSymbolsScope = BasicSymbolsMill.globalScope(); + OOTypeSymbol loader = new OOTypeSymbolSurrogate("Integer"); + loader.setEnclosingScope(typeSymbolsScope); + _IntegerSymType = new SymTypeOfObject(loader); + } + + + /*********************************************************************/ + + /** + * This is a predefined Dummy Symbol mimicking the + * pseudoType "void" with no Fields, no Methods, etc. + * It is used for internal derivations, but of course not for real type results + * + */ + public static OOTypeSymbol _void; + public static SymTypeVoid _voidSymType; + public static final String _voidTypeString = "voidType"; + + public static void set_Void() { + _void = type(_voidTypeString); // the name shouldn't be used + _void.setEnclosingScope(new OOSymbolsScope()); + _voidSymType = new SymTypeVoid(); + } + + + /*********************************************************************/ + + + /** + * This is a predefined Dummy Symbol mimicking the + * pseudoType "null" with no Fields, no Methods, etc. + */ + public static OOTypeSymbol _null; + public static SymTypeOfNull _nullSymType; + public static final String _nullTypeString = "nullType"; + + public static void set_Null() { + _null = type(_nullTypeString); // and the name shouldn't be used anyway, but it is at DeSer + _null.setEnclosingScope(new OOSymbolsScope()); + _nullSymType = new SymTypeOfNull(); + } + + /*********************************************************************/ + + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/check/DeriveSymTypeAbstractTest.java b/monticore-grammar/src/test/java/de/monticore/types/check/DeriveSymTypeAbstractTest.java new file mode 100644 index 0000000000..a768b0c698 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/check/DeriveSymTypeAbstractTest.java @@ -0,0 +1,212 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.expressions.assignmentexpressions._visitor.AssignmentExpressionsTraverser; +import de.monticore.expressions.bitexpressions._visitor.BitExpressionsTraverser; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.ICombineExpressionsWithLiteralsScope; +import de.monticore.expressions.commonexpressions._visitor.CommonExpressionsTraverser; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.expressionsbasis._symboltable.IExpressionsBasisScope; +import de.monticore.expressions.expressionsbasis._visitor.ExpressionsBasisTraverser; +import de.monticore.expressions.javaclassexpressions._visitor.JavaClassExpressionsTraverser; +import de.monticore.literals.mccommonliterals._visitor.MCCommonLiteralsTraverser; +import de.monticore.expressions.lambdaexpressions._visitor.LambdaExpressionsTraverser; +import de.monticore.types.mcbasictypes._visitor.MCBasicTypesTraverser; +import de.monticore.types.mccollectiontypes._visitor.MCCollectionTypesTraverser; +import de.monticore.types.mcsimplegenerictypes._visitor.MCSimpleGenericTypesTraverser; +import de.se_rwth.commons.logging.Finding; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import static org.junit.Assert.*; + +public abstract class DeriveSymTypeAbstractTest { + + @Before + public void setup() { + LogStub.init(); // replace log by a sideffect free variant + Log.enableFailQuick(false); + } + + // Setup the TypeCheck with according derive and synthesize + protected abstract void setupTypeCheck(); + + // Parse a String expression of the according language + protected abstract Optional parseStringExpression(String expression) throws IOException; + + // Provide the Traverser for the used language + protected abstract ExpressionsBasisTraverser getUsedLanguageTraverser(); + + private TypeCalculator tc; + + protected final void setTypeCheck(TypeCalculator tc) { + this.tc = tc; + } + + protected TypeCalculator getTypeCalculator() { + return this.tc; + } + + protected ASTExpression parseExpression(String expression) throws IOException { + Optional astExpression = parseStringExpression(expression); + assertTrue(astExpression.isPresent()); + return astExpression.get(); + } + + private ExpressionsBasisTraverser flatExpressionScopeSetterTraverser; + + protected final void setFlatExpressionScopeSetter(ICombineExpressionsWithLiteralsScope enclosingScope) { + flatExpressionScopeSetterTraverser = getUsedLanguageTraverser(); + addToTraverser(flatExpressionScopeSetterTraverser, enclosingScope); + } + + protected final void setFlatExpressionScope(ASTExpression astex) { + if (flatExpressionScopeSetterTraverser != null) { + astex.accept(flatExpressionScopeSetterTraverser); + } + } + + protected final void check(String expression, String expectedType) throws IOException { + setupTypeCheck(); + ASTExpression astex = parseExpression(expression); + setFlatExpressionScope(astex); + + assertEquals("Wrong return type for expression " + expression, expectedType, tc.typeOf(astex).print()); + } + + protected final void checkError(String expression, String expectedError) throws IOException { + setupTypeCheck(); + ASTExpression astex = parseExpression(expression); + setFlatExpressionScope(astex); + + Log.getFindings().clear(); + try { + SymTypeExpression result = tc.typeOf(astex); + assertTrue(result.isObscureType()); + assertEquals(expectedError, getFirstErrorCode()); + } catch (RuntimeException e) { + assertEquals(expectedError, getFirstErrorCode()); + } + } + + protected final void checkErrors(String expression, List expectedErrors) throws IOException { + setupTypeCheck(); + ASTExpression astex = parseExpression(expression); + setFlatExpressionScope(astex); + + Log.getFindings().clear(); + try { + tc.typeOf(astex); + } catch (RuntimeException e) { + assertEquals(expectedErrors, getAllErrorCodes()); + return; + } + fail(); + } + + protected final void checkErrors(String expression, String... expectedErrors) throws IOException { + checkErrors(expression, Arrays.asList(expectedErrors)); + } + + protected final void checkErrorsAndFailOnException(String expression, List expectedErrors) + throws IOException { + setupTypeCheck(); + ASTExpression astex = parseExpression(expression); + setFlatExpressionScope(astex); + + Log.getFindings().clear(); + try { + TypeCheckResult result = tc.iDerive.deriveType(astex); + + if(expectedErrors.isEmpty()) { + assertEquals("Found errors even though there should be none", 0, Log.getErrorCount()); + assertTrue("Missing type check result (in the form of a SymTypeExpression)", result.isPresentResult()); + } else { + assertEquals(expectedErrors, getAllErrorCodes()); + } + + } catch (Exception e) { + fail("An unexpected Exception was thrown during running the typecheck on " + expression + ":\n" + + e.getClass().getName() + e.getMessage() + ); + } + } + + protected final void checkErrorsAndFailOnException(String expression, String... expectedErrors) throws IOException { + checkErrorsAndFailOnException(expression, Arrays.asList(expectedErrors)); + } + + protected String getFirstErrorCode() { + if (Log.getFindings().size() > 0) { + String firstFinding = Log.getFindings().get(0).getMsg(); + return firstFinding.split(" ")[0]; + } + return ""; + } + + private List getFirstErrorCodes(long n) { + List errorsInLog = Log.getFindings().stream() + .filter(Finding::isError) + .map(err -> err.getMsg().split(" ")[0]) + .limit(n) + .collect(Collectors.toList()); + List errorsToReturn; + + if(errorsInLog.size() < n) { + errorsToReturn = errorsInLog; + for(int i = 0; i < n - errorsInLog.size(); i++) { + errorsToReturn.add(""); + } + } else { + errorsToReturn = errorsInLog.subList(0, (int) n); + } + return errorsToReturn; + } + + private List getAllErrorCodes() { + return getFirstErrorCodes(Log.getErrorCount()); + } + + + private void addToTraverser(ExpressionsBasisTraverser traverser, IExpressionsBasisScope enclosingScope) { + FlatExpressionScopeSetter flatExpressionScopeSetter = new FlatExpressionScopeSetter(enclosingScope); + traverser.add4ExpressionsBasis(flatExpressionScopeSetter); + if (traverser instanceof AssignmentExpressionsTraverser) { + ((AssignmentExpressionsTraverser) traverser).add4AssignmentExpressions(flatExpressionScopeSetter); + } + if (traverser instanceof CommonExpressionsTraverser) { + ((CommonExpressionsTraverser) traverser).add4CommonExpressions(flatExpressionScopeSetter); + } + if (traverser instanceof JavaClassExpressionsTraverser) { + ((JavaClassExpressionsTraverser) traverser).add4JavaClassExpressions(flatExpressionScopeSetter); + } + if (traverser instanceof LambdaExpressionsTraverser) { + ((LambdaExpressionsTraverser) traverser).add4LambdaExpressions(flatExpressionScopeSetter); + } + if (traverser instanceof BitExpressionsTraverser) { + ((BitExpressionsTraverser) traverser).add4BitExpressions(flatExpressionScopeSetter); + } + if (traverser instanceof MCBasicTypesTraverser) { + ((MCBasicTypesTraverser) traverser).add4MCBasicTypes(flatExpressionScopeSetter); + } + if(traverser instanceof MCCollectionTypesTraverser) { + ((MCCollectionTypesTraverser) traverser).add4MCCollectionTypes(flatExpressionScopeSetter); + } + if(traverser instanceof MCSimpleGenericTypesTraverser) { + ((MCSimpleGenericTypesTraverser) traverser).add4MCSimpleGenericTypes(flatExpressionScopeSetter); + } + if (traverser instanceof MCCommonLiteralsTraverser) { + ((MCCommonLiteralsTraverser) traverser).add4MCCommonLiterals(flatExpressionScopeSetter); + } + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/check/DeriveSymTypeOfAssignmentExpressionTest.java b/monticore-grammar/src/test/java/de/monticore/types/check/DeriveSymTypeOfAssignmentExpressionTest.java new file mode 100644 index 0000000000..6662b04001 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/check/DeriveSymTypeOfAssignmentExpressionTest.java @@ -0,0 +1,483 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import com.google.common.collect.Lists; +import de.monticore.expressions.combineexpressionswithliterals.CombineExpressionsWithLiteralsMill; +import de.monticore.expressions.combineexpressionswithliterals._parser.CombineExpressionsWithLiteralsParser; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.ICombineExpressionsWithLiteralsScope; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.expressionsbasis._visitor.ExpressionsBasisTraverser; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.oosymbols._symboltable.OOTypeSymbol; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static de.monticore.types.check.DefsTypeBasic._booleanSymType; +import static de.monticore.types.check.DefsTypeBasic._byteSymType; +import static de.monticore.types.check.DefsTypeBasic._charSymType; +import static de.monticore.types.check.DefsTypeBasic._doubleSymType; +import static de.monticore.types.check.DefsTypeBasic._floatSymType; +import static de.monticore.types.check.DefsTypeBasic._intSymType; +import static de.monticore.types.check.DefsTypeBasic._longSymType; +import static de.monticore.types.check.DefsTypeBasic._shortSymType; +import static de.monticore.types.check.DefsTypeBasic.add2scope; +import static de.monticore.types.check.DefsTypeBasic.field; + +public class DeriveSymTypeOfAssignmentExpressionTest extends DeriveSymTypeAbstractTest { + + // Parser used for convenience: + // (may be any other Parser that understands CommonExpressions) + CombineExpressionsWithLiteralsParser p = new CombineExpressionsWithLiteralsParser(); + + @Override + protected void setupTypeCheck() { + // This is an auxiliary + FullDeriveFromCombineExpressionsWithLiterals derLit = new FullDeriveFromCombineExpressionsWithLiterals(); + + // other arguments not used (and therefore deliberately null) + // This is the TypeChecker under Test: + setTypeCheck(new TypeCalculator(null, derLit)); + } + + @Override + protected Optional parseStringExpression(String expression) throws IOException { + return p.parse_StringExpression(expression); + } + + @Override + protected ExpressionsBasisTraverser getUsedLanguageTraverser() { + return CombineExpressionsWithLiteralsMill.traverser(); + } + + /** + * Focus: Deriving Type of Literals, here: + * literals/MCLiteralsBasis.mc4 + */ + + @Before + public void init() { + CombineExpressionsWithLiteralsMill.reset(); + CombineExpressionsWithLiteralsMill.init(); + BasicSymbolsMill.initializePrimitives(); + // Setting up a Scope Infrastructure (without a global Scope) + DefsTypeBasic.setup(); + ICombineExpressionsWithLiteralsScope scope = CombineExpressionsWithLiteralsMill.scope(); + scope.setEnclosingScope(null); // No enclosing Scope: Search ending here + scope.setExportingSymbols(true); + scope.setAstNode(null); + // we add a variety of TypeSymbols to the same scope (which in reality doesn't happen) + + add2scope(scope, DefsTypeBasic._array); + add2scope(scope, DefsTypeBasic._Object); + add2scope(scope, DefsTypeBasic._String); + + // some FieldSymbols (ie. Variables, Attributes) + OOTypeSymbol p = new OOTypeSymbol("Person"); + add2scope(scope, p); + OOTypeSymbol s = new OOTypeSymbol("Student"); + add2scope(scope, s); + s.setSuperTypesList(Lists.newArrayList(SymTypeExpressionFactory.createTypeObject("Person", scope))); + OOTypeSymbol f = new OOTypeSymbol("FirstSemesterStudent"); + add2scope(scope, f); + f.setSuperTypesList(Lists.newArrayList(SymTypeExpressionFactory.createTypeObject("Student", scope))); + add2scope(scope, field("varboolean", _booleanSymType)); + add2scope(scope, field("varbyte", _byteSymType)); + add2scope(scope, field("varchar", _charSymType)); + add2scope(scope, field("varshort", _shortSymType)); + add2scope(scope, field("varint", _intSymType)); + add2scope(scope, field("varlong", _longSymType)); + add2scope(scope, field("varfloat", _floatSymType)); + add2scope(scope, field("vardouble", _doubleSymType)); + add2scope(scope, field("varString", SymTypeExpressionFactory.createTypeObject("String", scope))); + add2scope(scope, field("person1", SymTypeExpressionFactory.createTypeObject("Person", scope))); + add2scope(scope, field("person2", SymTypeExpressionFactory.createTypeObject("Person", scope))); + add2scope(scope, field("student1", SymTypeExpressionFactory.createTypeObject("Student", scope))); + add2scope(scope, field("student2", SymTypeExpressionFactory.createTypeObject("Student", scope))); + add2scope(scope, field("firstsemester", SymTypeExpressionFactory.createTypeObject("FirstSemesterStudent", scope))); + setFlatExpressionScopeSetter(scope); + } + + /*--------------------------------------------------- TESTS ---------------------------------------------------------*/ + + /** + * test IncSuffixExpression + */ + @Test + public void deriveFromIncSuffixExpression() throws IOException { + + //example with byte + check("varbyte++", "byte"); + + //example with short + check("varshort++", "short"); + + //example with char + check("varchar++", "char"); + + //example with int + check("varint++", "int"); + + //example with float + check("varfloat++", "float"); + + //example with char + check("varchar++", "char"); + } + + @Test + public void testInvalidIncSuffixExpression() throws IOException { + //only possible wit variables + checkError("1++", "0xA0183"); + + //not applicable to Strings + checkError("varString++", "0xA0184"); + } + + /** + * test DecSuffixExpression + */ + @Test + public void deriveFromDecSuffixExpression() throws IOException { + //example with byte + check("varbyte--", "byte"); + + //example with short + check("varshort--", "short"); + + //example with char + check("varchar--", "char"); + + //example with int + check("varint--", "int"); + + //example with double + check("vardouble--", "double"); + } + + @Test + public void testInvalidDecSuffixExpression() throws IOException { + //only possible wit variables + checkError("1--", "0xA0183"); + + //not applicable to Strings + checkError("varString--", "0xA0184"); + } + + /** + * test IncPrefixExpression + */ + @Test + public void deriveFromIncPrefixExpression() throws IOException { + //example with byte + check("++varbyte", "byte"); + + //example with short + check("++varshort", "short"); + + //example with char + check("++varchar", "char"); + + //example with int + check("++varint", "int"); + + //example with long + check("++varlong", "long"); + } + + @Test + public void testInvalidIncPrefixExpression() throws IOException { + //only possible wit variables + checkError("++1", "0xA0183"); + + //not applicable to Strings + checkError("++varString", "0xA0184"); + } + + /** + * test DecPrefixExpression + */ + @Test + public void deriveFromDecPrefixExpression() throws IOException { + //example with byte + check("--varbyte", "byte"); + + //example with short + check("--varshort", "short"); + + //example with char + check("--varchar", "char"); + + //example with int + check("--varint", "int"); + + //example with float + check("--varfloat", "float"); + } + + @Test + public void testInvalidDecPrefixExpression() throws IOException { + //only possible wit variables + checkError("--1", "0xA0183"); + + //not applicable to Strings + checkError("--varString", "0xA0184"); + } + + /** + * test MinusPrefixExpression + */ + @Test + public void deriveFromMinusPrefixExpression() throws IOException { + //example with int + check("-5", "int"); + + //example with double + check("-15.7", "double"); + } + + @Test + public void testInvalidMinusPrefixExpression() throws IOException { + //only possible with numeric types + checkError("-\"Hello\"", "0xA0175"); + } + + /** + * test PlusPrefixExpression + */ + @Test + public void deriveFromPlusPrefixExpression() throws IOException { + //example with int + check("+34", "int"); + + //example with long + check("+4L", "long"); + } + + + @Test + public void testInvalidPlusPrefixExpression() throws IOException { + //only possible with numeric types + checkError("+\"Hello\"", "0xA0175"); + } + + /** + * test PlusAssignmentExpression + */ + @Test + public void deriveFromPlusAssignmentExpression() throws IOException { + //example with int - int + check("varint+=7", "int"); + //example with long - double + check("varlong+=5.6", "long"); + //example with String - Person + check("varString+=person1", "String"); + } + + @Test + public void testInvalidPlusAssignmentExpression() throws IOException { + //not possible because int = int + (int) String returns a casting error + checkError("varint+=\"Hello\"", "0xA0178"); + } + + /** + * test MinusAssignmentExpression + */ + @Test + public void deriveFromMinusAssignmentExpression() throws IOException { + //example with int - int + check("varint-=9", "int"); + //example with char - float + check("varchar-=4.5f", "char"); + } + + @Test + public void testInvalidMinusAssignmentExpression() throws IOException { + //not possible because int = int - (int) String returns a casting error + checkError("varint-=\"Hello\"", "0xA0178"); + } + + /** + * test MultAssignmentExpression + */ + @Test + public void deriveFromMultAssignmentExpression() throws IOException { + //example with int - int + check("varint*=9", "int"); + //example with double - int + check("vardouble*=5", "double"); + } + + @Test + public void testInvalidMultAssignmentExpression() throws IOException { + //not possible because int = int * (int) String returns a casting error + checkError("varint*=\"Hello\"", "0xA0178"); + } + + /** + * test DivideAssignmentExpression + */ + @Test + public void deriveFromDivideAssignmentExpression() throws IOException { + //example with int - int + check("varint/=9", "int"); + //example with float - long + check("varfloat/=4L", "float"); + } + + @Test + public void testInvalidDivideAssignmentExpression() throws IOException { + //not possible because int = int / (int) String returns a casting error + checkError("varint/=\"Hello\"", "0xA0178"); + } + + /** + * test ModuloAssignmentExpression + */ + @Test + public void deriveFromModuloAssignmentExpression() throws IOException { + //example with int - int + check("varint%=9", "int"); + //example with int - float + check("varint%=9.8f", "int"); + } + + @Test + public void testInvalidModuloAssignmentExpression() throws IOException { + //not possible because int = int % (int) String returns a casting error + checkError("varint%=\"Hello\"", "0xA0178"); + } + + /** + * test AndAssignmentExpression + */ + @Test + public void deriveFromAndAssignmentExpression() throws IOException { + //example with int - int + check("varint&=9", "int"); + //example with boolean - boolean + check("varboolean&=false", "boolean"); + //example with char - int + check("varchar&=4", "char"); + } + + @Test + public void testInvalidAndAssignmentExpression() throws IOException { + //not possible because int = int & (int) String returns a casting error + checkError("varint&=\"Hello\"", "0xA0176"); + } + + /** + * test OrAssignmentExpression + */ + @Test + public void deriveFromOrAssignmentExpression() throws IOException { + //example with int - int + check("varint|=9", "int"); + //example with boolean - boolean + check("varboolean|=true", "boolean"); + } + + @Test + public void testInvalidOrAssignmentExpression() throws IOException { + //not possible because int = int | (int) String returns a casting error + checkError("varint|=\"Hello\"", "0xA0176"); + } + + /** + * test BinaryXorAssignmentExpression + */ + @Test + public void deriveFromBinaryXorAssignmentExpression() throws IOException { + //example with int - int + check("varint^=9", "int"); + //example with boolean - boolean + check("varboolean^=false", "boolean"); + } + + @Test + public void testInvalidBinaryXorAssignmentExpression() throws IOException { + //not possible because int = int ^ (int) String returns a casting error + checkError("varint^=\"Hello\"", "0xA0176"); + } + + /** + * test DoubleLeftAssignmentExpression + */ + @Test + public void deriveFromDoubleLeftAssignmentExpression() throws IOException { + //example with int - int + check("varint<<=9", "int"); + //example with int - char + check("varint<<='c'", "int"); + } + + @Test + public void testInvalidDoubleLeftAssignmentExpression() throws IOException { + //not possible because int = int << (int) String returns a casting error + checkError("varint<<=\"Hello\"", "0xA0177"); + } + + /** + * test DoubleRightAssignmentExpression + */ + @Test + public void deriveFromDoubleRightAssignmentExpression() throws IOException { + //example with int - int + check("varint>>=9", "int"); + //example with char - int + check("varchar>>=12", "char"); + } + + @Test + public void testInvalidDoubleRightAssignmentExpression() throws IOException { + //not possible because int = int >> (int) String returns a casting error + checkError("varint>>=\"Hello\"", "0xA0177"); + } + + /** + * test LogicalRightAssignmentExpression + */ + @Test + public void deriveFromLogicalRightAssignmentExpression() throws IOException { + //example with int - int + check("varint>>>=9", "int"); + //example with char - char + check("varchar>>>='3'", "char"); + } + + @Test + public void testInvalidLogicalRightAssignmentExpression() throws IOException { + //not possible because int = int >>> (int) String returns a casting error + checkError("varint>>>=\"Hello\"", "0xA0177"); + } + + /** + * test RegularAssignmentExpression + */ + @Test + public void deriveFromRegularAssignmentExpression() throws IOException { + //example with int - int + check("varint=9", "int"); + //example with double - int + check("vardouble=12", "double"); + //example with person - student + check("person1 = student2", "Person"); + //example with person - firstsemesterstudent + check("person2 = firstsemester", "Person"); + } + + @Test + public void testInvalidRegularAssignmentExpression() throws IOException { + //not possible because int = (int) String returns a casting error + checkError("varint=\"Hello\"", "0xA0179"); + } + + @Test + public void testInvalidRegularAssignmentExpression2() throws IOException { + //test with no field on the left side of the assignment + checkError("3=4", "0xA0181"); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/check/DeriveSymTypeOfBitExpressionsTest.java b/monticore-grammar/src/test/java/de/monticore/types/check/DeriveSymTypeOfBitExpressionsTest.java new file mode 100644 index 0000000000..c1192fb2e2 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/check/DeriveSymTypeOfBitExpressionsTest.java @@ -0,0 +1,202 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import com.google.common.collect.Lists; +import de.monticore.expressions.combineexpressionswithliterals.CombineExpressionsWithLiteralsMill; +import de.monticore.expressions.combineexpressionswithliterals._parser.CombineExpressionsWithLiteralsParser; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.ICombineExpressionsWithLiteralsScope; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.expressionsbasis._visitor.ExpressionsBasisTraverser; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.oosymbols._symboltable.OOTypeSymbol; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static de.monticore.types.check.DefsTypeBasic.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class DeriveSymTypeOfBitExpressionsTest extends DeriveSymTypeAbstractTest { + + /** + * Focus: Deriving Type of Literals, here: + * literals/MCLiteralsBasis.mc4 + */ + + @Before + public void init() { + CombineExpressionsWithLiteralsMill.reset(); + CombineExpressionsWithLiteralsMill.init(); + BasicSymbolsMill.initializePrimitives(); + // Setting up a Scope Infrastructure (without a global Scope) + ICombineExpressionsWithLiteralsScope scope = CombineExpressionsWithLiteralsMill.scope(); + scope.setEnclosingScope(null); // No enclosing Scope: Search ending here + scope.setExportingSymbols(true); + scope.setAstNode(null); // hopefully unused + // we add a variety of TypeSymbols to the same scope (which in reality doesn't happen) + + add2scope(scope, DefsTypeBasic._array); + add2scope(scope, DefsTypeBasic._Object); + add2scope(scope, DefsTypeBasic._String); + + // some FieldSymbols (ie. Variables, Attributes) + OOTypeSymbol p = new OOTypeSymbol("Person"); + OOTypeSymbol s = new OOTypeSymbol("Student"); + s.setSuperTypesList(Lists.newArrayList(SymTypeExpressionFactory.createTypeObject("Person", scope))); + OOTypeSymbol f = new OOTypeSymbol("FirstSemesterStudent"); + f.setSuperTypesList(Lists.newArrayList(SymTypeExpressionFactory.createTypeObject("Student", scope))); + add2scope(scope, field("foo", _intSymType)); + add2scope(scope, field("bar2", _booleanSymType)); + add2scope(scope, field("vardouble", _doubleSymType)); + add2scope(scope, field("varchar", _charSymType)); + add2scope(scope, field("varfloat", _floatSymType)); + add2scope(scope, field("varlong", _longSymType)); + add2scope(scope, field("varint", _intSymType)); + add2scope(scope, field("varString",SymTypeExpressionFactory.createTypeObject("String",scope))); + add2scope(scope, field("person1",SymTypeExpressionFactory.createTypeObject("Person",scope))); + add2scope(scope, field("person2",SymTypeExpressionFactory.createTypeObject("Person",scope))); + add2scope(scope, field("student1",SymTypeExpressionFactory.createTypeObject("Student",scope))); + add2scope(scope,field("student2",SymTypeExpressionFactory.createTypeObject("Student",scope))); + add2scope(scope,field("firstsemester",SymTypeExpressionFactory.createTypeObject("FirstSemesterStudent",scope))); + + setFlatExpressionScopeSetter(scope); + } + + @Override + protected void setupTypeCheck() { + // This is an auxiliary + FullDeriveFromCombineExpressionsWithLiterals derLit = new FullDeriveFromCombineExpressionsWithLiterals(); + + // other arguments not used (and therefore deliberately null) + // This is the TypeChecker under Test: + setTypeCheck(new TypeCalculator(null, derLit)); + } + + // Parser used for convenience: + // (may be any other Parser that understands CommonExpressions) + CombineExpressionsWithLiteralsParser p = new CombineExpressionsWithLiteralsParser(); + @Override + protected Optional parseStringExpression(String expression) throws IOException { + return p.parse_StringExpression(expression); + } + + @Override + protected ExpressionsBasisTraverser getUsedLanguageTraverser() { + return CombineExpressionsWithLiteralsMill.traverser(); + } + + /*--------------------------------------------------- TESTS ---------------------------------------------------------*/ + + /** + * test LeftShiftExpression + */ + @Test + public void deriveFromLeftShiftExpressionTest() throws IOException { + //example with int - int + check("3<<5", "int"); + + //example with char - long + check("\'a\'<<4L", "int"); + } + + @Test + public void testInvalidLeftShiftExpression() throws IOException{ + //only possible with integral types + checkError("3<<4.5", "0xA0201"); + } + + /** + * test rightShiftExpression + */ + @Test + public void deriveFromRightShiftExpression() throws IOException { + //example with int - int + check("3>>5", "int"); + + //example with long - long + check("6L>>4L", "long"); + } + + @Test + public void testInvalidRightShiftExpression() throws IOException{ + //only possible with integral types + checkError("3>>4.5", "0xA0201"); + } + + /** + * test LogicalRightExpression + */ + @Test + public void deriveFromLogicalRightExpression() throws IOException { + //example with int - int + check("3>>>5", "int"); + + //example with int - long + check("12>>>4L", "int"); + } + + @Test + public void testInvalidLogicalRightExpression() throws IOException{ + //only possible with integral types + checkError("3>>>4.5", "0xA0201"); + } + + /** + * test BinaryOrOpExpression + */ + @Test + public void deriveFromBinaryOrOpExpression() throws IOException { + //example with int - int + check("3|5", "int"); + + //example with char - long + check("\'a\'|4L", "long"); + } + + @Test + public void testInvalidBinaryOrOpExpression() throws IOException{ + //only possible with integral types + checkError("3|4.5", "0xA0203"); + } + + /** + * test BinaryAndExpression + */ + @Test + public void deriveFromBinaryAndExpression() throws IOException { + //example with int - int + check("3&5", "int"); + + //example with long - long + check("4L&12L", "long"); + } + + @Test + public void testInvalidBinaryAndExpression() throws IOException{ + //only possible with integral types + checkError("3&4.5", "0xA0203"); + } + + /** + * test BinaryXorExpression + */ + @Test + public void deriveFromBinaryXorExpression() throws IOException { + //example with int - int + check("3^5", "int"); + + //example with boolean - boolean + check("true^false", "boolean"); + } + + @Test + public void testInvalidBinaryXorExpression() throws IOException{ + //only possible with integral types + checkError("3^4.5", "0xA0203"); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/check/DeriveSymTypeOfCombineExpressions.java b/monticore-grammar/src/test/java/de/monticore/types/check/DeriveSymTypeOfCombineExpressions.java new file mode 100644 index 0000000000..b338b09af3 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/check/DeriveSymTypeOfCombineExpressions.java @@ -0,0 +1,86 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.expressions.combineexpressionswithliterals._ast.ASTExtReturnType; +import de.monticore.expressions.combineexpressionswithliterals._ast.ASTExtType; +import de.monticore.expressions.combineexpressionswithliterals._ast.ASTExtTypeArgument; +import de.monticore.expressions.combineexpressionswithliterals._visitor.CombineExpressionsWithLiteralsHandler; +import de.monticore.expressions.combineexpressionswithliterals._visitor.CombineExpressionsWithLiteralsTraverser; +import de.monticore.expressions.combineexpressionswithliterals._visitor.CombineExpressionsWithLiteralsVisitor2; + +public class DeriveSymTypeOfCombineExpressions extends AbstractDeriveFromExpression implements CombineExpressionsWithLiteralsVisitor2, CombineExpressionsWithLiteralsHandler { + + private FullSynthesizeFromCombineExpressionsWithLiterals synthesizer; + + public FullSynthesizeFromCombineExpressionsWithLiterals getSynthesizer() { + return synthesizer; + } + + protected CombineExpressionsWithLiteralsTraverser traverser; + + @Override + public CombineExpressionsWithLiteralsTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(CombineExpressionsWithLiteralsTraverser traverser) { + this.traverser = traverser; + } + + public DeriveSymTypeOfCombineExpressions(FullSynthesizeFromCombineExpressionsWithLiterals synthesizer){ + this.synthesizer = synthesizer; + } + + @Override + public void traverse(ASTExtType type){ + SymTypeExpression wholeResult = null; + TypeCheckResult result = getSynthesizer().synthesizeType(type.getMCType()); + if(result.isPresentResult()){ + wholeResult=result.getResult(); + } + if(wholeResult!=null){ + getTypeCheckResult().setResult(wholeResult); + getTypeCheckResult().setType(); + }else{ + getTypeCheckResult().reset(); + } + } + + @Override + public void traverse(ASTExtReturnType returnType){ + SymTypeExpression wholeResult = null; + if(returnType.getMCReturnType().isPresentMCVoidType()){ + wholeResult = SymTypeExpressionFactory.createTypeVoid(); + }else if(returnType.getMCReturnType().isPresentMCType()){ + TypeCheckResult res = getSynthesizer().synthesizeType(returnType.getMCReturnType()); + if(res.isPresentResult()){ + wholeResult = res.getResult(); + } + } + if(wholeResult!=null){ + getTypeCheckResult().setResult(wholeResult); + getTypeCheckResult().setType(); + }else{ + getTypeCheckResult().reset(); + } + } + + @Override + public void traverse(ASTExtTypeArgument typeArgument){ + SymTypeExpression wholeResult = null; + if(typeArgument.getMCTypeArgument().getMCTypeOpt().isPresent()){ + TypeCheckResult res = getSynthesizer().synthesizeType(typeArgument.getMCTypeArgument().getMCTypeOpt().get()); + if(res.isPresentResult()){ + wholeResult = res.getResult(); + } + } + if(wholeResult!=null){ + getTypeCheckResult().setResult(wholeResult); + getTypeCheckResult().setType(); + }else{ + getTypeCheckResult().reset(); + } + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/check/DeriveSymTypeOfCommonExpressionTest.java b/monticore-grammar/src/test/java/de/monticore/types/check/DeriveSymTypeOfCommonExpressionTest.java new file mode 100644 index 0000000000..76e94c57b7 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/check/DeriveSymTypeOfCommonExpressionTest.java @@ -0,0 +1,1735 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import com.google.common.collect.Lists; +import de.monticore.expressions.combineexpressionswithliterals.CombineExpressionsWithLiteralsMill; +import de.monticore.expressions.combineexpressionswithliterals._parser.CombineExpressionsWithLiteralsParser; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.CombineExpressionsWithLiteralsScope; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.ICombineExpressionsWithLiteralsArtifactScope; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.ICombineExpressionsWithLiteralsGlobalScope; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.ICombineExpressionsWithLiteralsScope; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.expressionsbasis._visitor.ExpressionsBasisTraverser; +import de.monticore.io.paths.MCPath; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.FunctionSymbol; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.symbols.basicsymbols._symboltable.TypeVarSymbol; +import de.monticore.symbols.basicsymbols._symboltable.VariableSymbol; +import de.monticore.symbols.oosymbols.OOSymbolsMill; +import de.monticore.symbols.oosymbols._symboltable.FieldSymbol; +import de.monticore.symbols.oosymbols._symboltable.IOOSymbolsScope; +import de.monticore.symbols.oosymbols._symboltable.MethodSymbol; +import de.monticore.symbols.oosymbols._symboltable.OOTypeSymbol; +import de.se_rwth.commons.logging.Log; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static de.monticore.types.check.DefsTypeBasic.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class DeriveSymTypeOfCommonExpressionTest extends DeriveSymTypeAbstractTest { + + protected ICombineExpressionsWithLiteralsScope scope; + + @Override + protected void setupTypeCheck() { + // This is an auxiliary + FullDeriveFromCombineExpressionsWithLiterals derLit = new FullDeriveFromCombineExpressionsWithLiterals(); + + // other arguments not used (and therefore deliberately null) + // This is the TypeChecker under Test: + setTypeCheck(new TypeCalculator(null, derLit)); + } + + @Before + public void init(){ + // No enclosing Scopes: Search ending here + CombineExpressionsWithLiteralsMill.reset(); + CombineExpressionsWithLiteralsMill.init(); + BasicSymbolsMill.initializePrimitives(); + CombineExpressionsWithLiteralsMill.globalScope().setSymbolPath(new MCPath()); + CombineExpressionsWithLiteralsMill.globalScope().setFileExt("ce"); + + scope = CombineExpressionsWithLiteralsMill.scope(); + scope.setEnclosingScope(null); + scope.setExportingSymbols(true); + scope.setAstNode(null); + } + + // Parser used for convenience: + // (may be any other Parser that understands CommonExpressions) + CombineExpressionsWithLiteralsParser p = new CombineExpressionsWithLiteralsParser(); + @Override + protected Optional parseStringExpression(String expression) throws IOException { + return p.parse_StringExpression(expression); + } + + @Override + protected ExpressionsBasisTraverser getUsedLanguageTraverser() { + return CombineExpressionsWithLiteralsMill.traverser(); + } + + /*--------------------------------------------------- TESTS ---------------------------------------------------------*/ + + /** + * test correctness of addition + */ + @Test + public void deriveFromPlusExpression() throws IOException { + init_basic(); + + // example with two ints + check("3+4", "int"); + + // example with double and int + check("4.9+12", "double"); + + // example with Integer + check("foo2+foo2", "int"); + + // example with String + check("3 + \"Hallo\"", "String"); + } + + @Test + public void testInvalidPlusExpression() throws IOException { + checkError("3+true", "0xA0168"); + } + + /** + * test correctness of subtraction + */ + @Test + public void deriveFromMinusExpression() throws IOException { + // example with two ints + check("7-2", "int"); + + //example with float and long + check("7.9f-3L", "float"); + } + + @Test + public void testInvalidMinusExpression() throws IOException { + checkError("3-true", "0xA0168"); + } + + /** + * test correctness of multiplication + */ + @Test + public void deriveFromMultExpression() throws IOException { + //example with two ints + check("2*19", "int"); + + //example with long and char + check("'a'*3L", "long"); + } + + @Test + public void testInvalidMultExpression() throws IOException { + checkError("3*true", "0xA0168"); + } + + /** + * test correctness of division + */ + @Test + public void deriveFromDivideExpression() throws IOException { + //example with two ints + check("7/12", "int"); + + //example with float and double + check("5.4f/3.9", "double"); + } + + @Test + public void testInvalidDivideExpression() throws IOException { + checkError("3/true", "0xA0168"); + } + + /** + * tests correctness of modulo + */ + @Test + public void deriveFromModuloExpression() throws IOException { + //example with two ints + check("3%1", "int"); + + //example with long and double + check("0.8%3L", "double"); + } + + @Test + public void testInvalidModuloExpression() throws IOException { + checkError("3%true", "0xA0168"); + } + + /** + * test LessEqualExpression + */ + @Test + public void deriveFromLessEqualExpression() throws IOException { + //example with two ints + check("4<=9", "boolean"); + + //example with two other numeric types + check("2.4f<=3L", "boolean"); + } + + @Test + public void testInvalidLessEqualExpression() throws IOException { + checkError("3<=true", "0xA0167"); + } + + /** + * test GreaterEqualExpression + */ + @Test + public void deriveFromGreaterEqualExpression() throws IOException { + //example with two ints + check("7>=2", "boolean"); + + //example with two other numeric types + check("2.5>='d'", "boolean"); + } + + @Test + public void testInvalidGreaterEqualExpression() throws IOException { + checkError("3>=true", "0xA0167"); + } + + /** + * test LessThanExpression + */ + @Test + public void deriveFromLessThanExpression() throws IOException { + //example with two ints + check("4<9", "boolean"); + + //example with two other numeric types + check("2.4f<3L", "boolean"); + } + + @Test + public void testInvalidLessThanExpression() throws IOException { + checkError("32", "boolean"); + + //example with two other numeric types + check("2.5>'d'", "boolean"); + } + + @Test + public void testInvalidGreaterThanExpression() throws IOException { + checkError("3>true", "0xA0167"); + } + + /** + * initialize basic scope and a few symbols for testing + */ + public void init_basic() { + OOTypeSymbol person = OOSymbolsMill.oOTypeSymbolBuilder() + .setName("Person") + .setSpannedScope(OOSymbolsMill.scope()) + .setEnclosingScope(scope) + .build(); + add2scope(scope, person); + OOTypeSymbol student = OOSymbolsMill.oOTypeSymbolBuilder() + .setName("Student") + .setSpannedScope(OOSymbolsMill.scope()) + .setSuperTypesList(Lists.newArrayList(SymTypeExpressionFactory.createTypeObject("Person",scope))) + .setEnclosingScope(scope) + .build(); + add2scope(scope, student); + OOTypeSymbol firstsemesterstudent = OOSymbolsMill.oOTypeSymbolBuilder() + .setName("FirstSemesterStudent") + .setSpannedScope(OOSymbolsMill.scope()) + .setSuperTypesList(Lists.newArrayList(SymTypeExpressionFactory.createTypeObject("Student",scope))) + .setEnclosingScope(scope) + .build(); + add2scope(scope, firstsemesterstudent); + add2scope(scope, field("foo", _intSymType)); + add2scope(scope, field("foo2", _IntegerSymType)); + add2scope(scope, field("bar2", _booleanSymType)); + add2scope(scope, field("byteF", _byteSymType)); + add2scope(scope, field("shortF", _shortSymType)); + add2scope(scope, field("longF", _longSymType)); + add2scope(scope, field("person1", SymTypeExpressionFactory.createTypeObject("Person", scope))); + add2scope(scope, field("person2", SymTypeExpressionFactory.createTypeObject("Person", scope))); + add2scope(scope, field("student1", SymTypeExpressionFactory.createTypeObject("Student", scope))); + add2scope(scope, field("student2", SymTypeExpressionFactory.createTypeObject("Student", scope))); + add2scope(scope, field("firstsemester", SymTypeExpressionFactory. + createTypeObject("FirstSemesterStudent", scope))); + add2scope(scope, method("isInt", _booleanSymType)); + add2scope(scope, add(method("isInt", _booleanSymType), field("maxLength", _intSymType))); + + OOTypeSymbol str = type("String", Lists.newArrayList(), Lists.newArrayList(), Lists.newArrayList(), Lists.newArrayList(), scope); + add2scope(scope, str); + + setFlatExpressionScopeSetter(scope); + } + + /** + * test EqualsExpression + */ + @Test + public void deriveFromEqualsExpression() throws IOException { + //initialize symbol table + init_basic(); + + //example with two primitives + check("7==9.5f", "boolean"); + + //example with two objects of the same class + check("student1==student2", "boolean"); + + //example with two objects in sub-supertype relation + check("person1==student1", "boolean"); + } + + @Test + public void testInvalidEqualsExpression() throws IOException { + init_basic(); + + checkError("3==true", "0xA0166"); + } + + @Test + public void testInvalidEqualsExpression2() throws IOException{ + init_basic(); + + //person1 has the type Person, foo is a boolean + checkError("person1==foo", "0xA0166"); + } + + /** + * test NotEqualsExpression + */ + @Test + public void deriveFromNotEqualsExpression() throws IOException { + //initialize symbol table + init_basic(); + + //example with two primitives + check("true!=false", "boolean"); + + //example with two objects of the same class + check("person1!=person2", "boolean"); + + //example with two objects in sub-supertype relation + check("student2!=person2", "boolean"); + } + + @Test + public void testInvalidNotEqualsExpression() throws IOException { + init_basic(); + + checkError("3!=true", "0xA0166"); + } + + @Test + public void testInvalidNotEqualsExpression2() throws IOException{ + init_basic(); + //person1 is a Person, foo is a boolean + checkError("person1!=foo", "0xA0166"); + } + + /** + * test BooleanAndOpExpression + */ + @Test + public void deriveFromBooleanAndOpExpression() throws IOException { + //only possible with two booleans + check("true&&true", "boolean"); + + check("(3<=4&&5>6)", "boolean"); + } + + @Test + public void testInvalidAndOpExpression() throws IOException { + //only possible with two booleans + checkError("3&&true", "0xA0167"); + } + + /** + * test BooleanOrOpExpression + */ + @Test + public void deriveFromBooleanOrOpExpression() throws IOException { + //only possible with two booleans + check("true||false", "boolean"); + + check("(3<=4.5f||5.3>6)", "boolean"); + } + + @Test + public void testInvalidOrOpExpression() throws IOException { + //only possible with two booleans + checkError("3||true", "0xA0167"); + } + + /** + * test LogicalNotExpression + */ + @Test + public void deriveFromLogicalNotExpression() throws IOException { + //only possible with boolean as inner expression + check("!true", "boolean"); + + check("!(2.5>=0.3)", "boolean"); + } + + @Test + public void testInvalidLogicalNotExpression() throws IOException { + //only possible with a boolean as inner expression + checkError("!4", "0xA0171"); + } + + /** + * test BracketExpression + */ + @Test + public void deriveFromBracketExpression() throws IOException { + //initialize symbol table + init_basic(); + + //test with only a literal in the inner expression + check("(3)", "int"); + + //test with a more complex inner expression + check("(3+4*(18-7.5))", "double"); + + //test without primitive types in inner expression + check("(person1)", "Person"); + } + + @Test + public void testInvalidBracketExpression() throws IOException { + //a cannot be resolved -> a has no type + init_basic(); + checkError("(a)", "0xA0240"); + } + + /** + * test ConditionalExpression + */ + @Test + public void deriveFromConditionalExpression() throws IOException { + //initialize symbol table + init_basic(); + + //test with byte and short + check("bar2 ? byteF : shortF", "short"); + + //test with two ints as true and false expression + check("3<4?9:10", "int"); + + //test with float and long + check("3>4?4.5f:10L", "float"); + + //test without primitive types as true and false expression + check("3<9?person1:person2", "Person"); + + //test with two objects in a sub-supertype relation + check("3<9?student1:person2", "Person"); + } + + @Test + public void testInvalidConditionalExpression() throws IOException { + //true and 7 are not of the same type + checkError("3<4?true:7", "0xA0164"); + } + + @Test + public void testInvalidConditionalExpression2() throws IOException { + setupTypeCheck(); + ASTExpression astex = parseExpression("3?true:false"); + setFlatExpressionScope(astex); + + Log.getFindings().clear(); + getTypeCalculator().typeOf(astex); + assertEquals("0xA0165", getFirstErrorCode()); + } + + /** + * test BooleanNotExpression + */ + @Test + public void deriveFromBooleanNotExpression() throws IOException { + init_basic(); + + //test with a int + check("~3", "int"); + //test with a char + check("~'a'", "int"); + //test with a long + check("~longF", "long"); + } + + @Test + public void testInvalidBooleanNotExpression() throws IOException { + //only possible with an integral type (int, long, char, short, byte) + checkError("~3.4", "0xA0173"); + } + + /** + * initialize symboltable including global scope, artifact scopes and scopes with symbols for + * testing (mostly used for FieldAccessExpressions) + */ + public void init_advanced() { + ICombineExpressionsWithLiteralsGlobalScope globalScope = CombineExpressionsWithLiteralsMill.globalScope(); + + ICombineExpressionsWithLiteralsArtifactScope artifactScope2 = CombineExpressionsWithLiteralsMill.artifactScope(); + artifactScope2.setEnclosingScope(globalScope); + artifactScope2.setImportsList(Lists.newArrayList()); + artifactScope2.setPackageName(""); + artifactScope2.setName("types"); + + ICombineExpressionsWithLiteralsArtifactScope artifactScope3 = CombineExpressionsWithLiteralsMill.artifactScope(); + artifactScope3.setEnclosingScope(globalScope); + artifactScope3.setImportsList(Lists.newArrayList()); + artifactScope3.setName("types2"); + + ICombineExpressionsWithLiteralsArtifactScope artifactScope4 = CombineExpressionsWithLiteralsMill.artifactScope(); + artifactScope4.setEnclosingScope(globalScope); + artifactScope4.setImportsList(Lists.newArrayList()); + artifactScope4.setName("types3"); + artifactScope4.setPackageName("types3"); + + ICombineExpressionsWithLiteralsArtifactScope artifactScope5 = CombineExpressionsWithLiteralsMill.artifactScope(); + artifactScope5.setEnclosingScope(globalScope); + artifactScope5.setImportsList(Lists.newArrayList()); + artifactScope5.setName("functions1"); + artifactScope5.setPackageName("functions1"); + + ICombineExpressionsWithLiteralsArtifactScope artifactScope6 = CombineExpressionsWithLiteralsMill.artifactScope(); + artifactScope6.setEnclosingScope(globalScope); + artifactScope6.setImportsList(Lists.newArrayList()); + artifactScope6.setName("functions2"); + artifactScope6.setPackageName("functions2"); + + scope = globalScope; + // No enclosing Scope: Search ending here + + ICombineExpressionsWithLiteralsScope scope3 = CombineExpressionsWithLiteralsMill.scope(); + scope3.setName("types2"); + scope3.setEnclosingScope(artifactScope4); + scope3.setEnclosingScope(artifactScope4); + + // some FieldSymbols (ie. Variables, Attributes) + OOTypeSymbol person = OOSymbolsMill.oOTypeSymbolBuilder() + .setName("Person") + .setSpannedScope(OOSymbolsMill.scope()) + .setEnclosingScope(scope) + .build(); + OOTypeSymbol student = OOSymbolsMill.oOTypeSymbolBuilder() + .setName("Student") + .setSpannedScope(OOSymbolsMill.scope()) + .setSuperTypesList(Lists.newArrayList(SymTypeExpressionFactory.createTypeObject("Person",scope))) + .setEnclosingScope(scope) + .build(); + OOTypeSymbol firstsemesterstudent = OOSymbolsMill.oOTypeSymbolBuilder() + .setName("FirstSemesterStudent") + .setSpannedScope(OOSymbolsMill.scope()) + .setSuperTypesList(Lists.newArrayList(SymTypeExpressionFactory.createTypeObject("Student",scope))) + .setEnclosingScope(scope) + .build(); + OOTypeSymbol selfReflectiveStudent = OOSymbolsMill.oOTypeSymbolBuilder() + .setName("SelfReflectiveStudent") + .setSpannedScope(OOSymbolsMill.scope()) + .setSuperTypesList(Lists.newArrayList(SymTypeExpressionFactory.createTypeObject("Student",scope))) + .setEnclosingScope(scope) + .build(); + add2scope(artifactScope2, person); + add2scope(scope3, person); + add2scope(scope, person); + + add2scope(artifactScope2, student); + add2scope(scope3, student); + add2scope(scope, student); + + add2scope(artifactScope2, firstsemesterstudent); + add2scope(scope3, firstsemesterstudent); + add2scope(scope, firstsemesterstudent); + + add2scope(artifactScope2, selfReflectiveStudent); + add2scope(scope, selfReflectiveStudent); + + MethodSymbol studentSelfReflection = OOSymbolsMill.methodSymbolBuilder() + .setName("self") + .setType(SymTypeExpressionFactory.createTypeExpression(selfReflectiveStudent)) + .setSpannedScope(OOSymbolsMill.scope()) + .build(); + selfReflectiveStudent.addMethodSymbol(studentSelfReflection); + + FieldSymbol studentSelfReflectionField = field("selfField", + SymTypeExpressionFactory.createFunction(SymTypeExpressionFactory.createTypeExpression(selfReflectiveStudent)) + ); + selfReflectiveStudent.addFieldSymbol(studentSelfReflectionField); + + add2scope(scope, field("foo", _intSymType)); + add2scope(scope, field("bar2", _booleanSymType)); + add2scope(scope, field("person1", SymTypeExpressionFactory.createTypeObject("Person", scope))); + add2scope(scope, field("person2", SymTypeExpressionFactory.createTypeObject("Person", scope))); + add2scope(scope, field("student1", SymTypeExpressionFactory.createTypeObject("Student", scope))); + add2scope(scope, field("student2", SymTypeExpressionFactory.createTypeObject("Student", scope))); + add2scope(scope, field("firstsemester", + SymTypeExpressionFactory.createTypeObject("FirstSemesterStudent", scope)) + ); + add2scope(scope, field("selfReflectiveStudent", + SymTypeExpressionFactory.createTypeObject("SelfReflectiveStudent", scope)) + ); + MethodSymbol ms2 = method("isInt", _booleanSymType); + add2scope(scope, ms2); + add2scope(scope, add(method("isInt", _booleanSymType), field("maxLength", _intSymType))); + MethodSymbol ms0 = add(method("areInt", _booleanSymType), field("values", _intSymType)); + ms0.setIsElliptic(true); + add2scope(scope, ms0); + add2scope(scope, method("getIsInt", ms2.getFunctionType())); + add2scope(scope, method("getAreInt", ms0.getFunctionType())); + FieldSymbol fs = field("variable", _intSymType); + fs.setIsStatic(true); + MethodSymbol ms = method("store", _doubleSymType); + ms.setIsStatic(true); + MethodSymbol ms1 = add(method("pay", _voidSymType), field("cost",_intSymType)); + ms1.setIsStatic(true); + OOTypeSymbol testType = OOSymbolsMill.oOTypeSymbolBuilder() + .setName("Test") + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setEnclosingScope(scope) + .build(); + testType.setMethodList(Lists.newArrayList(ms,ms1)); + testType.addFieldSymbol(fs); + OOTypeSymbol testType2 = OOSymbolsMill.oOTypeSymbolBuilder() + .setName("Test") + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setEnclosingScope(artifactScope2) + .build(); + testType2.setMethodList(Lists.newArrayList(ms,ms1)); + testType2.addFieldSymbol(fs); + + OOTypeSymbol testType3 = OOSymbolsMill.oOTypeSymbolBuilder() + .setName("Test") + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setEnclosingScope(scope3) + .build(); + testType3.setMethodList(Lists.newArrayList(ms,ms1)); + testType3.addFieldSymbol(fs); + IOOSymbolsScope testScope = testType3.getSpannedScope(); + + FieldSymbol testVariable = field("testVariable",_shortSymType); + testVariable.setIsStatic(true); + OOTypeSymbol testInnerType = OOSymbolsMill.oOTypeSymbolBuilder() + .setName("TestInnerType") + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setEnclosingScope(testScope) + .build(); + testInnerType.addFieldSymbol(testVariable); + testInnerType.setIsStatic(true); + add2scope(testScope,testInnerType); + add2scope(testInnerType.getSpannedScope(),testVariable); + + testType3.setSpannedScope(testScope); + + add2scope(artifactScope2, testType2); + add2scope(scope3, testType3); + add2scope(scope,testType); + + OOTypeSymbol str = type("String", Lists.newArrayList(), Lists.newArrayList(), Lists.newArrayList(), Lists.newArrayList(), scope); + add2scope(scope, str); + + FunctionSymbol funcs = function("getPi", _floatSymType); + add2scope(artifactScope5, funcs); + add2scope(artifactScope6, funcs); + + // Creating types for legal access on "types.DeepNesting.firstLayer.onlyMember", where firstLayer and onlyMember are fields + OOTypeSymbol oneFieldMember = type("OneFieldMember", Lists.newArrayList(), Lists.newArrayList(), Lists.newArrayList(), Lists.newArrayList(), scope); + add2scope(scope, oneFieldMember); + FieldSymbol onlyMember = field("onlyMember", _intSymType); + add2scope(oneFieldMember.getSpannedScope(), onlyMember); + + OOTypeSymbol deepNesting = type("DeepNesting", Lists.newArrayList(), Lists.newArrayList(), Lists.newArrayList(), Lists.newArrayList(), scope); + add2scope(artifactScope2, deepNesting); + FieldSymbol firstLayer = field("firstLayer", SymTypeExpressionFactory.createTypeExpression(oneFieldMember)); + firstLayer.setIsStatic(true); + add2scope(deepNesting.getSpannedScope(), firstLayer); + + setFlatExpressionScopeSetter(scope); + } + + /** + * test FieldAccessExpression + */ + @Test + public void deriveFromFieldAccessExpression() throws IOException { + //initialize symbol table + init_advanced(); + + //test for type with only one package + check("types.Test", "Test"); + + //test for variable of a type with one package + check("types.Test.variable", "int"); + + //test for type with more than one package + check("types3.types2.Test", "Test"); + + //test for variable of type with more than one package + check("types3.types2.Test.variable", "int"); + + check("Test", "Test"); + + //test for variable in inner type + check("types3.types2.Test.TestInnerType.testVariable", "short"); + + // test for nested field access ("firstLayer" is a static member, "onlyMember" is an instance member) + check("types.DeepNesting.firstLayer.onlyMember", "int"); + } + + /** + * test CallExpression + */ + @Test + public void deriveFromCallExpression() throws IOException { + //initialize symbol table + init_advanced(); + + //test for method with unqualified name without parameters + check("isInt()", "boolean"); + + //test for method with unqualified name with parameters + check("isInt(4)", "boolean"); + + //test for method with varargs with no optional value + check("areInt()", "boolean"); + + //test for method with varargs with one optional value + check("areInt(1)", "boolean"); + + //test for method with varargs with multiple optional values + check("areInt(1, 2, 3)", "boolean"); + + //test for method with qualified name without parameters + check("types.Test.store()", "double"); + + //test for method with qualified name with parameters + check("types.Test.pay(4)", "void"); + + //test for function with that exists in another scope with + //the same name but different qualified name + check("functions1.functions1.getPi()", "float"); + + // test method chaining + check("selfReflectiveStudent.self().self()", "SelfReflectiveStudent"); + + // test function chaining + check("getIsInt()()", "boolean"); + check("(()->()->1)()()", "int"); + + // test indirect function chaining + check("(getIsInt())()", "boolean"); + check("((()->()->1)())()", "int"); + + // test function chaining with varargs + check("getAreInt()()", "boolean"); + check("getAreInt()(1,2)", "boolean"); + + // test function chaining using fields + check("selfReflectiveStudent.selfField().selfField()", "SelfReflectiveStudent"); + } + + @Test + public void testInvalidCallExpression() throws IOException { + //method isNot() is not in scope -> method cannot be resolved -> method has no return type + init_advanced(); + checkError("isNot()", "0xA1242"); + } + + @Test + public void testInvalidCallExpressionWithMissingNameAndNotComposedOfCallback() throws IOException { + // Expression (2 + 3)() and all other Expressions in front of brackets are parsable + init_advanced(); + checkError("(2 + 3)()", "0xA2239"); + } + + @Test + public void testInvalidCallExpressionWithInvalidQualifiedName() throws IOException { + //method isInt() is not in the specified scope -> method cannot be resolved + init_advanced(); + checkError("notAScope.isInt()", "0xA1242"); + } + + @Test + public void testInvalidCallExpressionWithFunctionChaining() throws IOException { + //function isNot() is part of the return type of getIsInt() -> function cannot be resolved + init_advanced(); + checkError("getIsInt.isNot()", "0xA1242"); + } + + @Test + public void testInvalidCallExpressionWithInvalidArgument() throws IOException { + String divideError = "0xA0168"; + + init_advanced(); + checkErrorsAndFailOnException("isInt(\"foo\" / 2)", divideError); + } + + @Test + public void testRegularAssignmentWithTwoMissingFields() throws IOException { + String[] regularAssignmentError = new String[] {"0xA0240", "0xA0240"}; + init_advanced(); + checkErrorsAndFailOnException("missingField = missingField2", regularAssignmentError); + } + + @Test + public void testMissingMethodWithMissingArgs() throws IOException { + String[] errors = new String[] {"0xA0240", "0xA0240", "0xA1242"}; + init_advanced(); + checkErrorsAndFailOnException("missingMethod(missing1, missing2)", errors); + } + + /** + * initialize the symbol table for a basic inheritance example + * we only have one scope and the symbols are all in this scope or in subscopes + */ + public void init_inheritance() { + //inheritance example + //super + MethodSymbol add = add(method("add", _voidSymType), field("element", _StringSymType)); + FieldSymbol field = field("field", _booleanSymType); + OOTypeSymbol superclass = OOSymbolsMill.oOTypeSymbolBuilder() + .setName("AList") + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setEnclosingScope(scope) + .build(); + superclass.addMethodSymbol(add); + superclass.addFieldSymbol(field); + add2scope(scope, superclass); + SymTypeExpression supclass = SymTypeExpressionFactory.createTypeObject("AList", scope); + + //sub + OOTypeSymbol subclass = OOSymbolsMill.oOTypeSymbolBuilder() + .setName("MyList") + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setSuperTypesList(Lists.newArrayList(supclass)) + .setEnclosingScope(scope) + .build(); + add2scope(scope, subclass); + + SymTypeExpression sub = SymTypeExpressionFactory.createTypeObject("MyList", scope); + FieldSymbol myList = field("myList", sub); + add2scope(scope, myList); + + //subsub + OOTypeSymbol subsubclass = OOSymbolsMill.oOTypeSymbolBuilder() + .setName("MySubList") + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setSuperTypesList(Lists.newArrayList(sub)) + .setEnclosingScope(scope) + .build(); + add2scope(scope, subsubclass); + SymTypeExpression subsub = SymTypeExpressionFactory.createTypeObject("MySubList", scope); + FieldSymbol mySubList = field("mySubList", subsub); + add2scope(scope, mySubList); + + OOTypeSymbol str = type("String", Lists.newArrayList(), Lists.newArrayList(), Lists.newArrayList(), Lists.newArrayList(), scope); + add2scope(scope, str); + + setFlatExpressionScopeSetter(scope); + } + + /** + * test if the methods and fields of superclasses can be used by subclasses + */ + @Test + public void testInheritance() throws IOException { + //initialize symbol table + init_inheritance(); + + //methods + //test normal inheritance + check("myList.add(\"Hello\")", "void"); + + //test inheritance over two levels + check("mySubList.add(\"World\")", "void"); + + //fields + check("myList.field", "boolean"); + + check("mySubList.field", "boolean"); + } + + /** + * test the inheritance of a generic type with one type variable + */ + @Test + public void testListAndArrayListInheritance() throws IOException { + //one generic parameter, supertype List + TypeVarSymbol t = typeVariable("T"); + add2scope(scope, t); + MethodSymbol addMethod = add(method("add", _booleanSymType), + field("x", SymTypeExpressionFactory.createTypeVariable("T", scope)) + ); + FieldSymbol nextField = field("next", SymTypeExpressionFactory.createTypeVariable("T", scope)); + OOTypeSymbol sym = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(OOSymbolsMill.scope()) + .setName("List") + .setEnclosingScope(scope) + .build(); + sym.addMethodSymbol(addMethod); + sym.addFieldSymbol(nextField); + sym.addTypeVarSymbol(t); + add2scope(scope, sym); + SymTypeExpression listIntSymTypeExp = SymTypeExpressionFactory + .createGenerics("List", scope, _intSymType); + FieldSymbol listVar = field("listVar", listIntSymTypeExp); + add2scope(scope, listVar); + + //one generic parameter, subtype ArrayList + TypeVarSymbol arrayListT = typeVariable("T"); + SymTypeExpression listTSymTypeExp = SymTypeExpressionFactory + .createGenerics("List", scope, + Lists.newArrayList(SymTypeExpressionFactory.createTypeVariable("T", scope))); + OOTypeSymbol subsym = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(OOSymbolsMill.scope()) + .setName("ArrayList") + .setSuperTypesList(Lists.newArrayList(listTSymTypeExp)) + .setEnclosingScope(scope) + .build(); + subsym.addTypeVarSymbol(arrayListT); + add2scope(scope, subsym); + SymTypeExpression subsymexp = SymTypeExpressionFactory. + createGenerics("ArrayList", scope, Lists.newArrayList(_intSymType)); + FieldSymbol arraylistVar = field("arraylistVar", subsymexp); + add2scope(scope, arraylistVar); + + setFlatExpressionScopeSetter(scope); + + //test methods and fields of the supertype + check("listVar.add(2)", "boolean"); + + check("listVar.next", "int"); + + //test inherited methods and fields of the subtype + check("arraylistVar.add(3)", "boolean"); + + check("arraylistVar.next", "int"); + } + + /** + * test the inheritance of generic types with two type variables + */ + @Test + public void testGenericInheritanceTwoTypeVariables() throws IOException { + //two generic parameters, supertype GenSup, create SymType GenSup + TypeVarSymbol t1 = typeVariable("S"); + TypeVarSymbol t2 = typeVariable("V"); + add2scope(scope, t1); + add2scope(scope, t2); + MethodSymbol load = add(method("load", + SymTypeExpressionFactory.createTypeVariable("S", scope)), + field("x", SymTypeExpressionFactory.createTypeVariable("V", scope)) + ); + FieldSymbol f1 = field("f1", SymTypeExpressionFactory.createTypeVariable("S", scope)); + FieldSymbol f2 = field("f2", SymTypeExpressionFactory.createTypeVariable("V", scope)); + OOTypeSymbol genSup = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(OOSymbolsMill.scope()) + .setName("GenSup") + .setEnclosingScope(scope) + .build(); + genSup.setMethodList(Lists.newArrayList(load,load.deepClone())); + genSup.addFieldSymbol(f1); + genSup.addFieldSymbol(f2); + genSup.addTypeVarSymbol(t1); + genSup.addTypeVarSymbol(t2); + add2scope(scope, genSup); + SymTypeExpression genSupType = SymTypeExpressionFactory. + createGenerics("GenSup", scope, Lists.newArrayList(_StringSymType, _intSymType)); + FieldSymbol genSupVar = field("genSupVar", genSupType); + add2scope(scope, genSupVar); + + //two generic parameters, subtype GenSub, create SymType GenSub + t1 = typeVariable("S"); + t2 = typeVariable("V"); + SymTypeExpression genTypeSV = SymTypeExpressionFactory. + createGenerics("GenSup", scope, Lists.newArrayList(SymTypeExpressionFactory. + createTypeVariable("S", scope), SymTypeExpressionFactory.createTypeVariable("V", scope))); + OOTypeSymbol genSub = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(OOSymbolsMill.scope()) + .setName("GenSub") + .setSuperTypesList(Lists.newArrayList(genTypeSV)) + .setEnclosingScope(scope).build(); + genSub.addFieldSymbol(f1.deepClone()); + genSub.addTypeVarSymbol(t1); + genSub.addTypeVarSymbol(t2); + + add2scope(scope, genSub); + SymTypeExpression genSubType = SymTypeExpressionFactory. + createGenerics("GenSub", scope, Lists.newArrayList(_StringSymType, _intSymType)); + FieldSymbol genSubVar = field("genSubVar", genSubType); + add2scope(scope, genSubVar); + + //two generic parameters, subsubtype GenSubSub, create GenSubSub + t1 = typeVariable("S"); + t2 = typeVariable("V"); + SymTypeExpression genSubTypeSV = SymTypeExpressionFactory. + createGenerics("GenSub", scope, Lists.newArrayList(SymTypeExpressionFactory.createTypeVariable("S", scope), + SymTypeExpressionFactory.createTypeVariable("V", scope))); + OOTypeSymbol genSubSub = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(OOSymbolsMill.scope()) + .setName("GenSubSub") + .setSuperTypesList(Lists.newArrayList(genSubTypeSV)) + .setEnclosingScope(scope) + .build(); + genSubSub.addTypeVarSymbol(t2); + genSubSub.addTypeVarSymbol(t1); + add2scope(scope, genSubSub); + SymTypeExpression genSubSubType = SymTypeExpressionFactory. + createGenerics("GenSubSub", scope, Lists.newArrayList(_StringSymType, _intSymType)); + FieldSymbol genSubSubVar = field("genSubSubVar", genSubSubType); + add2scope(scope, genSubSubVar); + + OOTypeSymbol str = type("String", Lists.newArrayList(), Lists.newArrayList(), Lists.newArrayList(), Lists.newArrayList(), scope); + add2scope(scope, str); + + setFlatExpressionScopeSetter(scope); + + //supertype: test methods and fields + check("genSupVar.load(3)", "String"); + + check("genSupVar.f1", "String"); + + check("genSupVar.f2", "int"); + + //subtype: test inherited methods and fields + check("genSubVar.load(3)", "String"); + + check("genSubVar.f1", "String"); + + check("genSubVar.f2", "int"); + + //subsubtype: test inherited methods and fields + check("genSubSubVar.load(\"Hello\")", "int"); + + check("genSubSubVar.f1", "int"); + + check("genSubSubVar.f2", "String"); + } + + /** + * test if methods and a field from a fixed subtype(generic type, but instead of type variable concrete type) + * are inherited correctly + */ + @Test + public void testSubVarSupFix() throws IOException { + //subtype with variable generic parameter, supertype with fixed generic parameter + //supertype with fixed generic parameter FixGen and SymType FixGen + TypeVarSymbol a = typeVariable("A"); + add2scope(scope, a); + MethodSymbol add2 = add(method("add", _booleanSymType), + field("a", SymTypeExpressionFactory.createTypeVariable("A", scope)) + ); + FieldSymbol next2 = field("next", SymTypeExpressionFactory.createTypeVariable("A", scope)); + OOTypeSymbol fixGen = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(OOSymbolsMill.scope()) + .setName("FixGen") + .setEnclosingScope(scope) + .build(); + fixGen.addMethodSymbol(add2); + fixGen.addFieldSymbol(next2); + fixGen.addTypeVarSymbol(a); + add2scope(scope, fixGen); + SymTypeExpression fixGenType = SymTypeExpressionFactory.createGenerics("FixGen", scope, + Lists.newArrayList(_intSymType)); + FieldSymbol fixGenVar = field("fixGenVar", fixGenType); + add2scope(scope, fixGenVar); + + //subtype with variable generic parameter VarGen which extends FixGen, SymType VarGen + TypeVarSymbol n = typeVariable("N"); + add2scope(scope, n); + MethodSymbol calculate = method("calculate", SymTypeExpressionFactory.createTypeVariable("N", scope)); + OOTypeSymbol varGenType = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(OOSymbolsMill.scope()) + .setName("VarGen") + .setSuperTypesList(Lists.newArrayList(fixGenType)) + .setEnclosingScope(scope) + .build(); + varGenType.addMethodSymbol(calculate); + varGenType.addTypeVarSymbol(n); + add2scope(scope, varGenType); + SymTypeExpression varGenSym = SymTypeExpressionFactory. + createGenerics("VarGen", scope, Lists.newArrayList(_StringSymType)); + FieldSymbol varGen = field("varGen", varGenSym); + add2scope(scope, varGen); + + setFlatExpressionScopeSetter(scope); + + //test own methods first + check("varGen.calculate()", "String"); + + //test inherited methods and fields + check("varGen.add(4)", "boolean"); + + check("varGen.next", "int"); + } + + /** + * Test-Case: SubType has more generic parameters than its supertype + */ + @Test + public void testSubTypeWithMoreGenericParameters() throws IOException { + //one generic parameter, supertype List + TypeVarSymbol t = typeVariable("T"); + add2scope(scope, t); + MethodSymbol addMethod = add(method("add", _booleanSymType), + field("x", SymTypeExpressionFactory.createTypeVariable("T", scope)) + ); + FieldSymbol nextField = field("next", SymTypeExpressionFactory.createTypeVariable("T", scope)); + OOTypeSymbol sym = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(OOSymbolsMill.scope()) + .setName("List") + .setEnclosingScope(scope) + .build(); + sym.addMethodSymbol(addMethod); + sym.addFieldSymbol(nextField); + sym.addTypeVarSymbol(t); + add2scope(scope, sym); + SymTypeExpression listIntSymTypeExp = SymTypeExpressionFactory + .createGenerics("List", scope, Lists.newArrayList(_intSymType)); + FieldSymbol listVar = field("listVar", listIntSymTypeExp); + add2scope(scope, listVar); + + //two generic parameters, subtype MoreGen + t = typeVariable("T"); + TypeVarSymbol moreType1 = typeVariable("F"); + add2scope(scope, moreType1); + SymTypeExpression listTSymTypeExp = SymTypeExpressionFactory + .createGenerics("List", scope, Lists.newArrayList(SymTypeExpressionFactory.createTypeVariable("T", scope))); + MethodSymbol insert = add( + method("insert", SymTypeExpressionFactory.createTypeVariable("T", scope)), + field("x", SymTypeExpressionFactory.createTypeVariable("F", scope)) + ); + OOTypeSymbol moreGenType = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(OOSymbolsMill.scope()) + .setName("MoreGen") + .setSuperTypesList(Lists.newArrayList(listTSymTypeExp)) + .setEnclosingScope(scope) + .build(); + moreGenType.addMethodSymbol(insert); + moreGenType.addTypeVarSymbol(t); + moreGenType.addTypeVarSymbol(moreType1); + add2scope(scope, moreGenType); + SymTypeExpression moreGenSym = SymTypeExpressionFactory. + createGenerics("MoreGen", scope, Lists.newArrayList(_intSymType, _longSymType)); + FieldSymbol moreGen = field("moreGen", moreGenSym); + add2scope(scope, moreGen); + + setFlatExpressionScopeSetter(scope); + + //test own method + check("moreGen.insert(12L)", "int"); + + //test inherited methods and fields + check("moreGen.add(12)", "boolean"); + + check("moreGen.next", "int"); + } + + /** + * Test-Case: SubType is a normal object type and extends a fixed generic type + */ + @Test + public void testSubTypeWithoutGenericParameter() throws IOException { + //one generic parameter, supertype List + TypeVarSymbol t = typeVariable("T"); + add2scope(scope, t); + MethodSymbol addMethod = add(method("add", _booleanSymType), + field("x", SymTypeExpressionFactory.createTypeVariable("T", scope)) + ); + FieldSymbol nextField = field("next", SymTypeExpressionFactory.createTypeVariable("T", scope)); + OOTypeSymbol sym = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(OOSymbolsMill.scope()) + .setName("List") + .setEnclosingScope(scope) + .build(); + sym.addMethodSymbol(addMethod); + sym.addFieldSymbol(nextField); + sym.addTypeVarSymbol(t); + add2scope(scope, sym); + SymTypeExpression listIntSymTypeExp = SymTypeExpressionFactory + .createGenerics("List", scope, Lists.newArrayList(_intSymType)); + FieldSymbol listVar = field("listVar", listIntSymTypeExp); + add2scope(scope, listVar); + + //subtype without generic parameter NotGen extends List + OOTypeSymbol notgeneric = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(OOSymbolsMill.scope()) + .setName("NotGen") + .setSuperTypesList(Lists.newArrayList(listIntSymTypeExp)) + .setEnclosingScope(scope) + .build(); + add2scope(scope, notgeneric); + SymTypeExpression notgenericType = SymTypeExpressionFactory.createTypeObject("NotGen", scope); + FieldSymbol ng = field("notGen", notgenericType); + add2scope(scope, ng); + + setFlatExpressionScopeSetter(scope); + + //test inherited methods and fields + check("notGen.add(14)", "boolean"); + + check("notGen.next", "int"); + } + + /** + * Test-Case: Multi-Inheritance 1, test if the methods and fields are inherited correctly + * every type in the example has exactly one type variable + */ + @Test + public void testMultiInheritance() throws IOException { + //supertype SupA + TypeVarSymbol t = typeVariable("T"); + add2scope(scope, t); + MethodSymbol testA = method("testA", SymTypeExpressionFactory.createTypeVariable("T", scope)); + FieldSymbol currentA = field("currentA", SymTypeExpressionFactory.createTypeVariable("T", scope)); + OOTypeSymbol supA = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(OOSymbolsMill.scope()) + .setName("SupA") + .setEnclosingScope(scope) + .build(); + supA.addMethodSymbol(testA); + supA.addFieldSymbol(currentA); + supA.addTypeVarSymbol(t); + add2scope(scope, supA); + SymTypeExpression supATExpr = SymTypeExpressionFactory + .createGenerics("SupA", scope, Lists.newArrayList(SymTypeExpressionFactory.createTypeVariable("T", scope))); + + //supertype SupB + t = typeVariable("T"); + MethodSymbol testB = method("testB", SymTypeExpressionFactory.createTypeVariable("T", scope)); + FieldSymbol currentB = field("currentB", SymTypeExpressionFactory.createTypeVariable("T", scope)); + OOTypeSymbol supB = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(OOSymbolsMill.scope()) + .setName("SupB") + .setEnclosingScope(scope) + .build(); + supB.addMethodSymbol(testB); + supB.addFieldSymbol(currentB); + supB.addTypeVarSymbol(t); + add2scope(scope, supB); + SymTypeExpression supBTExpr = SymTypeExpressionFactory. + createGenerics("SupB", scope, Lists.newArrayList(SymTypeExpressionFactory.createTypeVariable("T", scope))); + + //subType SubA + t = typeVariable("T"); + OOTypeSymbol subA = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(OOSymbolsMill.scope()) + .setName("SubA") + .setSuperTypesList(Lists.newArrayList(supATExpr, supBTExpr)) + .setEnclosingScope(scope) + .build(); + subA.addTypeVarSymbol(t); + add2scope(scope, subA); + SymTypeExpression subATExpr = SymTypeExpressionFactory + .createGenerics("SubA", scope, Lists.newArrayList(_charSymType)); + FieldSymbol sub = field("sub", subATExpr); + add2scope(scope, sub); + + setFlatExpressionScopeSetter(scope); + + check("sub.testA()", "char"); + + check("sub.currentA", "char"); + + check("sub.testB()", "char"); + + check("sub.currentB", "char"); + } + + /** + * Test-Case: Multi-Inheritance 1, test if the methods and fields are inherited correctly + * the supertypes have one type variable and the subtype has two type variables + */ + @Test + public void testMultiInheritanceSubTypeMoreGen() throws IOException { + //supertype SupA + TypeVarSymbol t = typeVariable("T"); + add2scope(scope, t); + MethodSymbol testA = method("testA", SymTypeExpressionFactory.createTypeVariable("T", scope)); + FieldSymbol currentA = field("currentA", SymTypeExpressionFactory.createTypeVariable("T", scope)); + OOTypeSymbol supA = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(OOSymbolsMill.scope()) + .setName("SupA") + .setEnclosingScope(scope) + .build(); + supA.addMethodSymbol(testA); + supA.addFieldSymbol(currentA); + supA.addTypeVarSymbol(t); + add2scope(scope, supA); + SymTypeExpression supATExpr = SymTypeExpressionFactory + .createGenerics("SupA", scope, Lists.newArrayList(SymTypeExpressionFactory.createTypeVariable("T", scope))); + //supertype SupB + TypeVarSymbol s = typeVariable("S"); + MethodSymbol testB = method("testB", SymTypeExpressionFactory.createTypeVariable("S", scope)); + FieldSymbol currentB = field("currentB", SymTypeExpressionFactory.createTypeVariable("S", scope)); + OOTypeSymbol supB = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(OOSymbolsMill.scope()) + .setName("SupB") + .setEnclosingScope(scope) + .build(); + supB.addMethodSymbol(testB); + supB.addFieldSymbol(currentB); + supB.addTypeVarSymbol(s); + add2scope(scope, supB); + SymTypeExpression supBTExpr = SymTypeExpressionFactory + .createGenerics("SupB", scope, Lists.newArrayList(SymTypeExpressionFactory.createTypeVariable("S", scope))); + + //subType SubA + t = typeVariable("T"); + s = typeVariable("S"); + OOTypeSymbol subA = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(OOSymbolsMill.scope()) + .setName("SubA") + .setSuperTypesList(Lists.newArrayList(supATExpr, supBTExpr)) + .setEnclosingScope(scope) + .build(); + subA.addTypeVarSymbol(s); + subA.addTypeVarSymbol(t); + add2scope(scope, subA); + SymTypeExpression subATExpr = SymTypeExpressionFactory + .createGenerics("SubA", scope, Lists.newArrayList(_charSymType, _booleanSymType)); + FieldSymbol sub = field("sub", subATExpr); + add2scope(scope, sub); + add2scope(scope, s); + + setFlatExpressionScopeSetter(scope); + + check("sub.testA()", "boolean"); + + check("sub.currentA", "boolean"); + + check("sub.testB()", "char"); + + check("sub.currentB", "char"); + } + + /** + * test if you can use methods, types and fields of the type or its supertypes in its method scopes + */ + @Test + public void testMethodScope() throws IOException { + //super + FieldSymbol elementField = field("element", _StringSymType); + MethodSymbol add = OOSymbolsMill.methodSymbolBuilder() + .setType(_voidSymType) + .setName("add") + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + add2scope(add.getSpannedScope(), elementField); + FieldSymbol field = field("field", _booleanSymType); + OOTypeSymbol superclass = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(OOSymbolsMill.scope()) + .setName("AList") + .setEnclosingScope(scope) + .build(); + superclass.addMethodSymbol(add); + superclass.addFieldSymbol(field); + add2scope(scope, superclass); + SymTypeExpression supclass = SymTypeExpressionFactory.createTypeObject("AList", scope); + + //sub + OOTypeSymbol subclass = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(OOSymbolsMill.scope()) + .setName("MyList") + .setSuperTypesList(Lists.newArrayList(supclass)) + .setEnclosingScope(scope) + .build(); + add2scope(scope, subclass); + + SymTypeExpression sub = SymTypeExpressionFactory.createTypeObject("MyList", scope); + FieldSymbol myList = field("myList", sub); + add2scope(scope, myList); + + //subsub + FieldSymbol myNext = field("myNext", _StringSymType); + MethodSymbol myAdd = OOSymbolsMill.methodSymbolBuilder() + .setName("myAdd") + .setType(_voidSymType) + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + OOTypeSymbol subsubclass = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("MySubList") + .setSuperTypesList(Lists.newArrayList(sub)) + .setEnclosingScope(scope) + .build(); + subsubclass.addMethodSymbol(myAdd); + subsubclass.addFieldSymbol(myNext); + //set correct scopes + subsubclass.getSpannedScope().setEnclosingScope(scope); + myAdd.getSpannedScope().setEnclosingScope(subsubclass.getSpannedScope()); + add2scope(scope, subsubclass); + SymTypeExpression subsub = SymTypeExpressionFactory.createTypeObject("MySubList", scope); + FieldSymbol mySubList = field("mySubList", subsub); + add2scope(scope, mySubList); + + OOTypeSymbol str = type("String", Lists.newArrayList(), Lists.newArrayList(), Lists.newArrayList(), Lists.newArrayList(), scope); + add2scope(scope, str); + + //set scope of method myAdd as standard resolving scope + setFlatExpressionScopeSetter((CombineExpressionsWithLiteralsScope) myAdd.getSpannedScope()); + + check("mySubList", "MySubList"); + + check("myAdd()", "void"); + + check("myNext", "String"); + + check("add(\"Hello\")", "void"); + + check("field", "boolean"); + } + + public void init_static_example(){ + //types A and B + MethodSymbol atest = method("test",_voidSymType); + atest.setIsStatic(true); + FieldSymbol afield = field("field",_intSymType); + afield.setIsStatic(true); + OOTypeSymbol a = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("A") + .setEnclosingScope(scope) + .build(); + a.addFieldSymbol(afield); + a.addMethodSymbol(atest); + //A has static inner type D + OOTypeSymbol aD = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(OOSymbolsMill.scope()) + .setName("D") + .setEnclosingScope(a.getSpannedScope()) + .build(); + aD.setIsStatic(true); + add2scope(a.getSpannedScope(), aD); + + add2scope(scope,a); + + MethodSymbol btest = method("test",_voidSymType); + FieldSymbol bfield = field("field",_intSymType); + OOTypeSymbol b = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("B") + .setEnclosingScope(scope) + .build(); + b.addFieldSymbol(bfield); + b.addMethodSymbol(btest); + //B has not static inner type D + OOTypeSymbol bD = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(OOSymbolsMill.scope()) + .setName("D") + .setEnclosingScope(b.getSpannedScope()) + .build(); + add2scope(b.getSpannedScope(), bD); + + add2scope(scope,b); + //A has static method test, static field field, static type D + //B has normal method test, normal field field, normal type D + //type C extends A and has no method, field or type + SymTypeExpression aSymType = SymTypeExpressionFactory.createTypeObject("A",scope); + OOTypeSymbol c = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(OOSymbolsMill.scope()) + .setName("C") + .setSuperTypesList(Lists.newArrayList(aSymType)) + .setEnclosingScope(scope) + .build(); + add2scope(scope,c); + + setFlatExpressionScopeSetter(scope); + } + + @Test + public void testStaticType() throws IOException { + init_static_example(); + + check("A.D", "D"); + } + + @Test + public void testInvalidStaticType() throws IOException { + init_static_example(); + + checkError("B.D", "0xA0241"); + } + + @Test + public void testStaticField() throws IOException { + init_static_example(); + + check("A.field", "int"); + } + + @Test + public void testInvalidStaticField() throws IOException { + init_static_example(); + + checkError("B.field", "0xA0241"); + } + + @Test + public void testStaticMethod() throws IOException { + init_static_example(); + + check("A.test()", "void"); + } + + @Test + public void testInvalidStaticMethod() throws IOException { + init_static_example(); + + checkError("B.test()", "0xA2239"); + } + + @Test + public void testMissingTypeQualified() throws IOException { + init_basic(); + + checkError("pac.kage.not.present.Type", "0xA0241"); + } + + @Test + public void testMissingFieldQualified() throws IOException { + init_static_example(); + + checkError("B.notPresentField", "0xA0241"); + } + + @Test + public void testMissingFieldUnqualified() throws IOException { + init_basic(); + + checkError("notPresentField", "0xA0240"); + } + + @Test + public void testMissingMethodQualified() throws IOException { + init_basic(); + + checkError("pac.kage.not.present.Type.method()", "0xA1242"); + } + + @Test + public void testSubClassesDoNotKnowStaticMethodsOfSuperClasses() throws IOException{ + init_static_example(); + + checkError("C.test()", "0xA2239"); + } + + @Test + public void testSubClassesDoNotKnowStaticFieldsOfSuperClasses() throws IOException{ + init_static_example(); + + checkError("C.field", "0xA0241"); + } + + @Test + public void testSubClassesDoNotKnowStaticTypesOfSuperClasses() throws IOException{ + init_static_example(); + + Optional sType = p.parse_StringExpression("C.D"); + assertTrue(sType.isPresent()); + //TODO ND: complete when inner types are added + } + + /** + * test if we can use functions and variables + * as e.g. imported by Class2MC + */ + @Test + public void testDoNotFilterBasicTypes() throws IOException{ + TypeSymbol A = BasicSymbolsMill.typeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("A") + .setEnclosingScope(scope) + .build(); + A.addFunctionSymbol(function("func", _voidSymType)); + A.addVariableSymbol(variable("var", _booleanSymType)); + VariableSymbol a = BasicSymbolsMill.variableSymbolBuilder() + .setName("a") + .setType(SymTypeExpressionFactory.createTypeObject(A)) + .setEnclosingScope(scope) + .build(); + add2scope(scope, A); + add2scope(scope, a); + setFlatExpressionScopeSetter(scope); + + // functions are available as if they were static + check("A.func()", "void"); + check("a.func()", "void"); + + // variables are available as if they were non-static + checkError("A.var", "0xA0241"); + check("a.var", "boolean"); + } + + public void init_method_test(){ + //see MC Ticket #3298 for this example, use test instead of bar because bar is a keyword in CombineExpressions + //create types A, B and C, B extends A, C extends B + TypeSymbol a = type("A"); + SymTypeExpression aSym = SymTypeExpressionFactory.createTypeObject(a); + TypeSymbol b = type("B", Lists.newArrayList(aSym)); + SymTypeExpression bSym = SymTypeExpressionFactory.createTypeObject(b); + TypeSymbol c = type("C", Lists.newArrayList(bSym)); + SymTypeExpression cSym = SymTypeExpressionFactory.createTypeObject(c); + + scope.add(a); + scope.add(b); + scope.add(c); + + //create method foo(A x) + MethodSymbol fooA = method("foo", aSym); + VariableSymbol fooAx = field("x", aSym); + fooA.getSpannedScope().add(fooAx); + scope.add(fooA); + + //create method foo(B x) + MethodSymbol fooB = method("foo", bSym); + VariableSymbol fooBx = field("x", bSym); + fooB.getSpannedScope().add(fooBx); + scope.add(fooB); + + //create method foo(C x) + MethodSymbol fooC = method("foo", cSym); + VariableSymbol fooCx = field("x", cSym); + fooC.getSpannedScope().add(fooCx); + scope.add(fooC); + + //create method foo(A x, A y) + MethodSymbol fooAA = method("foo", aSym); + VariableSymbol fooAAx = field("x", aSym); + fooAA.getSpannedScope().add(fooAAx); + VariableSymbol fooAAy = field("y", aSym); + fooAA.getSpannedScope().add(fooAAy); + scope.add(fooAA); + + //create method foo(A x, B y) + MethodSymbol fooAB = method("foo", bSym); + VariableSymbol fooABx = field("x", aSym); + fooAB.getSpannedScope().add(fooABx); + VariableSymbol fooABy = field("y", bSym); + fooAB.getSpannedScope().add(fooABy); + scope.add(fooAB); + + //create method foo(A x, C y) + MethodSymbol fooAC = method("foo", cSym); + VariableSymbol fooACx = field("x", aSym); + fooAC.getSpannedScope().add(fooACx); + VariableSymbol fooACy = field("y", cSym); + fooAC.getSpannedScope().add(fooACy); + scope.add(fooAC); + + //create method test(A x, B y, C z) + MethodSymbol testABC = method("test", aSym); + VariableSymbol testABCx = field("x", aSym); + testABC.getSpannedScope().add(testABCx); + VariableSymbol testABCy = field("y", bSym); + testABC.getSpannedScope().add(testABCy); + VariableSymbol testABCz = field("z", cSym); + testABC.getSpannedScope().add(testABCz); + scope.add(testABC); + + //create method test(C x, C y, A z) + MethodSymbol testCCA = method("test", aSym); + VariableSymbol testCCAx = field("x", cSym); + testCCA.getSpannedScope().add(testCCAx); + VariableSymbol testCCAy = field("y", cSym); + testCCA.getSpannedScope().add(testCCAy); + VariableSymbol testCCAz = field("z", aSym); + testCCA.getSpannedScope().add(testCCAz); + scope.add(testCCA); + + //create method test(A x, B y) + MethodSymbol testAB = method("test", bSym); + VariableSymbol testABx = field("x", aSym); + testAB.getSpannedScope().add(testABx); + VariableSymbol testABy = field("y", bSym); + testAB.getSpannedScope().add(testABy); + scope.add(testAB); + + //create method test(C x, A y) + MethodSymbol testCA = method("test", cSym); + VariableSymbol testCAx = field("x", cSym); + testCA.getSpannedScope().add(testCAx); + VariableSymbol testCAy = field("y", aSym); + testCA.getSpannedScope().add(testCAy); + scope.add(testCA); + + //create variables A a, B b and C c + VariableSymbol varA = field("a", aSym); + scope.add(varA); + VariableSymbol varB = field("b", bSym); + scope.add(varB); + VariableSymbol varC = field("c", cSym); + scope.add(varC); + + setFlatExpressionScopeSetter(scope); + } + + @Test + public void testCorrectMethodChosen() throws IOException { + init_method_test(); + + /* + available methods: + A foo(A x) + B foo(B x) + C foo(C x) + A foo(A x, A y) + B foo(A x, B y) + C foo(A x, C y) + A test(A x, B y, C z) + C test(C x, C y, A z) + B test(A x, B y) + C test(C x, A y) + */ + + check("foo(a)", "A"); + check("foo(b)", "B"); + check("foo(c)", "C"); + + check("foo(a, a)", "A"); + check("foo(a, b)", "B"); + check("foo(a, c)", "C"); + check("foo(b, a)", "A"); + check("foo(b, b)", "B"); + check("foo(b, c)", "C"); + check("foo(c, a)", "A"); + check("foo(c, b)", "B"); + check("foo(c, c)", "C"); + + checkError("test(c, c, c)", "0xA1243"); + + checkError("test(a, a)", "0xA1241"); + checkError("test(b, a)", "0xA1241"); + checkError("test(c, b)", "0xA1243"); + checkError("test(c, c)", "0xA1243"); + + check("test(a, b)", "B"); + check("test(a, c)", "B"); + check("test(b, b)", "B"); + check("test(b, c)", "B"); + check("test(c, a)", "C"); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/check/DeriveSymTypeOfExpressionTest.java b/monticore-grammar/src/test/java/de/monticore/types/check/DeriveSymTypeOfExpressionTest.java new file mode 100644 index 0000000000..246c7453fd --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/check/DeriveSymTypeOfExpressionTest.java @@ -0,0 +1,160 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import com.google.common.collect.Lists; +import de.monticore.expressions.combineexpressionswithliterals.CombineExpressionsWithLiteralsMill; +import de.monticore.expressions.combineexpressionswithliterals._parser.CombineExpressionsWithLiteralsParser; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.ICombineExpressionsWithLiteralsScope; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.expressionsbasis._visitor.ExpressionsBasisTraverser; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.TypeVarSymbol; +import de.monticore.symbols.oosymbols.OOSymbolsMill; +import de.monticore.symbols.oosymbols._symboltable.FieldSymbol; +import de.monticore.symbols.oosymbols._symboltable.OOTypeSymbol; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static de.monticore.types.check.DefsTypeBasic.*; +import static org.junit.Assert.assertEquals; + +public class DeriveSymTypeOfExpressionTest extends DeriveSymTypeAbstractTest { + + /** + * Focus: Deriving Type of Literals, here: + * literals/MCLiteralsBasis.mc4 + */ + + @Before + public void init() { + CombineExpressionsWithLiteralsMill.reset(); + CombineExpressionsWithLiteralsMill.init(); + BasicSymbolsMill.initializePrimitives(); + // Setting up a Scope Infrastructure (without a global Scope) + ICombineExpressionsWithLiteralsScope scope = CombineExpressionsWithLiteralsMill.scope(); + scope.setEnclosingScope(null); // No enclosing Scope: Search ending here + scope.setExportingSymbols(true); + scope.setAstNode(null); + // we add a variety of TypeSymbols to the same scope (which in reality doesn't happen) + + add2scope(scope, DefsTypeBasic._array); + add2scope(scope, DefsTypeBasic._Object); + add2scope(scope, DefsTypeBasic._String); + + // some FieldSymbols (ie. Variables, Attributes) + OOTypeSymbol p = new OOTypeSymbol("Person"); + add2scope(scope,p); + OOTypeSymbol s = new OOTypeSymbol("Student"); + add2scope(scope,s); + s.setSuperTypesList(Lists.newArrayList(SymTypeExpressionFactory.createTypeObject("Person", scope))); + OOTypeSymbol f = new OOTypeSymbol("FirstSemesterStudent"); + add2scope(scope,f); + f.setSuperTypesList(Lists.newArrayList(SymTypeExpressionFactory.createTypeObject("Student", scope))); + add2scope(scope, field("foo", _intSymType)); + add2scope(scope, field("bar2", _booleanSymType)); + add2scope(scope, field("person1",SymTypeExpressionFactory.createTypeObject("Person",scope))); + add2scope(scope, field("person2",SymTypeExpressionFactory.createTypeObject("Person",scope))); + add2scope(scope, field("student1",SymTypeExpressionFactory.createTypeObject("Student",scope))); + add2scope(scope,field("student2",SymTypeExpressionFactory.createTypeObject("Student",scope))); + add2scope(scope,field("firstsemester",SymTypeExpressionFactory.createTypeObject("FirstSemesterStudent",scope))); + + //testing for generics + TypeVarSymbol genArgs = typeVariable("GenArg"); + OOTypeSymbol genSuperType = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(OOSymbolsMill.scope()) + .setEnclosingScope(scope) + .setName("GenSuper") + .build(); + genSuperType.addTypeVarSymbol(genArgs); + SymTypeExpression genArg = SymTypeExpressionFactory.createTypeVariable("GenArg",scope); + SymTypeExpression genSuper = SymTypeExpressionFactory.createGenerics("GenSuper",scope,genArg); + OOTypeSymbol genSubType = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(OOSymbolsMill.scope()) + .setName("GenSub") + .setSuperTypesList(Lists.newArrayList(genSuper)) + .setEnclosingScope(scope) + .build(); + genSubType.addTypeVarSymbol(genArgs); + SymTypeExpression genSub = SymTypeExpressionFactory.createGenerics("GenSub", scope, genArg); + FieldSymbol genSubField = field("genericSub",genSub); + FieldSymbol genSuperField = field("genericSuper",genSuper); + add2scope(scope,genSuperType); + add2scope(scope,genSubType); + add2scope(scope,genSubField); + add2scope(scope,genSuperField); + + setFlatExpressionScopeSetter(scope); + } + + @Override + protected void setupTypeCheck() { + // This is an auxiliary + FullDeriveFromCombineExpressionsWithLiterals derLit = new FullDeriveFromCombineExpressionsWithLiterals(); + + // other arguments not used (and therefore deliberately null) + // This is the TypeChecker under Test: + setTypeCheck(new TypeCalculator(null, derLit)); + } + + // Parser used for convenience: + // (may be any other Parser that understands CommonExpressions) + CombineExpressionsWithLiteralsParser p = new CombineExpressionsWithLiteralsParser(); + @Override + protected Optional parseStringExpression(String expression) throws IOException { + return p.parse_StringExpression(expression); + } + + @Override + protected ExpressionsBasisTraverser getUsedLanguageTraverser() { + return CombineExpressionsWithLiteralsMill.traverser(); + } + + // ------------------------------------------------------ Tests for Function 2 + + + @Test + public void deriveTFromASTNameExpression() throws IOException { + check("foo", "int"); + } + + @Test + public void deriveTFromASTNameExpression2() throws IOException { + check("bar2", "boolean"); + } + + @Test + public void deriveTFromASTNameExpression3() throws IOException{ + check("person1", "Person"); + } + + @Test + public void deriveTFromASTNameExpression4() throws IOException{ + check("student1", "Student"); + } + + @Test + public void deriveTFromASTNameExpression5() throws IOException{ + check("firstsemester", "FirstSemesterStudent"); + } + + @Test + public void deriveTFromLiteral() throws IOException { + check("42", "int"); + } + + @Test + public void deriveTFromLiteralString() throws IOException { + check("\"aStringi\"", "String"); + } + + @Test + public void genericsTest() throws IOException { + check("genericSuper = genericSub", "GenSuper"); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/check/DeriveSymTypeOfJavaClassExpressionsTest.java b/monticore-grammar/src/test/java/de/monticore/types/check/DeriveSymTypeOfJavaClassExpressionsTest.java new file mode 100644 index 0000000000..a72d4f3c85 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/check/DeriveSymTypeOfJavaClassExpressionsTest.java @@ -0,0 +1,1212 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import com.google.common.collect.Lists; +import de.monticore.expressions.combineexpressionswithliterals.CombineExpressionsWithLiteralsMill; +import de.monticore.expressions.combineexpressionswithliterals._parser.CombineExpressionsWithLiteralsParser; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.CombineExpressionsWithLiteralsScope; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.ICombineExpressionsWithLiteralsGlobalScope; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.ICombineExpressionsWithLiteralsScope; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.expressionsbasis._visitor.ExpressionsBasisTraverser; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.TypeVarSymbol; +import de.monticore.symbols.oosymbols.OOSymbolsMill; +import de.monticore.symbols.oosymbols._symboltable.*; +import de.se_rwth.commons.logging.*; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static de.monticore.types.check.DefsTypeBasic.*; +import static org.junit.Assert.*; + +public class DeriveSymTypeOfJavaClassExpressionsTest extends DeriveSymTypeAbstractTest { + + protected ICombineExpressionsWithLiteralsScope scope; + /** + * Focus: Deriving Type of Literals, here: + * literals/MCLiteralsBasis.mc4 + */ + + @Before + public void init() { + CombineExpressionsWithLiteralsMill.reset(); + CombineExpressionsWithLiteralsMill.init(); + BasicSymbolsMill.initializePrimitives(); + // Setting up a Scope Infrastructure (without a global Scope) + DefsTypeBasic.setup(); + ICombineExpressionsWithLiteralsGlobalScope gs = CombineExpressionsWithLiteralsMill.globalScope(); + scope = CombineExpressionsWithLiteralsMill.scope(); + scope.setExportingSymbols(true); + scope.setAstNode(null); + scope.setEnclosingScope(gs); + // we add a variety of TypeSymbols to the same scope (which in reality doesn't happen) + + add2scope(scope, DefsTypeBasic._array); + add2scope(scope, DefsTypeBasic._Object); + add2scope(scope, DefsTypeBasic._String); + + IOOSymbolsArtifactScope scope2 = CombineExpressionsWithLiteralsMill.artifactScope(); + scope2.setPackageName("java.util"); + scope2.setName("Set"); + scope2.setEnclosingScope(gs); + OOTypeSymbol set = new OOTypeSymbol("Set"); + add2scope(scope2, set); + // some FieldSymbols (ie. Variables, Attributes) + OOTypeSymbol integer = new OOTypeSymbol("Integer"); + add2scope(scope, integer); + OOTypeSymbol p = new OOTypeSymbol("Person"); + add2scope(scope,p); + OOTypeSymbol s = new OOTypeSymbol("Student"); + add2scope(scope,s); + s.setSuperTypesList(Lists.newArrayList(SymTypeExpressionFactory.createTypeObject("Person", scope))); + OOTypeSymbol f = new OOTypeSymbol("FirstSemesterStudent"); + add2scope(scope,f); + f.setSuperTypesList(Lists.newArrayList(SymTypeExpressionFactory.createTypeObject("Student", scope))); + add2scope(scope, field("foo", _intSymType)); + add2scope(scope, field("bar2", _booleanSymType)); + add2scope(scope, field("vardouble", _doubleSymType)); + add2scope(scope, field("varchar", _charSymType)); + add2scope(scope, field("varfloat", _floatSymType)); + add2scope(scope, field("varlong", _longSymType)); + add2scope(scope, field("varint", _intSymType)); + add2scope(scope, field("varString", SymTypeExpressionFactory.createTypeObject("String", scope))); + add2scope(scope, field("person1", SymTypeExpressionFactory.createTypeObject("Person", scope))); + add2scope(scope, field("person2", SymTypeExpressionFactory.createTypeObject("Person", scope))); + add2scope(scope, field("student1", SymTypeExpressionFactory.createTypeObject("Student", scope))); + add2scope(scope, field("student2", SymTypeExpressionFactory.createTypeObject("Student", scope))); + add2scope(scope, field("firstsemester", SymTypeExpressionFactory.createTypeObject("FirstSemesterStudent", scope))); + setFlatExpressionScopeSetter(scope); + } + + @Override + protected void setupTypeCheck() { + // This is an auxiliary + FullDeriveFromCombineExpressionsWithLiterals derLit = new FullDeriveFromCombineExpressionsWithLiterals(); + + // other arguments not used (and therefore deliberately null) + // This is the TypeChecker under Test: + setTypeCheck(new TypeCalculator(null, derLit)); + } + + // Parser used for convenience: + // (may be any other Parser that understands CommonExpressions) + CombineExpressionsWithLiteralsParser p = new CombineExpressionsWithLiteralsParser(); + @Override + protected Optional parseStringExpression(String expression) throws IOException { + return p.parse_StringExpression(expression); + } + + @Override + protected ExpressionsBasisTraverser getUsedLanguageTraverser() { + return CombineExpressionsWithLiteralsMill.traverser(); + } + + /*--------------------------------------------------- TESTS ---------------------------------------------------------*/ + + + @Test + public void deriveFromPrimarySuperExpression() throws IOException { + OOTypeSymbol supType = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(OOSymbolsMill.scope()) + .setName("A") + .setEnclosingScope(scope) + .build(); + SymTypeExpression sup = SymTypeExpressionFactory.createTypeObject("A",scope); + supType.setIsClass(true); + add2scope(scope,supType); + + MethodSymbol get = OOSymbolsMill.methodSymbolBuilder() + .setName("get") + .setType(_voidSymType) + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + OOTypeSymbol p = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("AB") + .setSuperTypesList(Lists.newArrayList(sup)) + .setEnclosingScope(scope) + .build(); + p.setIsClass(true); + get.getSpannedScope().setEnclosingScope(p.getSpannedScope()); + add2scope(scope,p); + + //use the spanned scope of the type + setFlatExpressionScopeSetter((ICombineExpressionsWithLiteralsScope) p.getSpannedScope()); + check("super", "A"); + + //use the spanned scope of the method + setFlatExpressionScopeSetter((ICombineExpressionsWithLiteralsScope) get.getSpannedScope()); + check("super", "A"); + } + + @Test + public void failDeriveFromPrimarySuperExpression() throws IOException{ + //use the current scope -> there is no type spanning any enclosing scope + checkError("super", "0xA0280"); + } + + @Test + public void deriveFromPrimaryThisExpression() throws IOException { + MethodSymbol get = OOSymbolsMill.methodSymbolBuilder() + .setName("get") + .setType(_voidSymType) + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + OOTypeSymbol p = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("AB") + .setEnclosingScope(scope) + .build(); + p.addMethodSymbol(get); + get.getSpannedScope().setEnclosingScope(p.getSpannedScope()); + add2scope(scope,p); + + //use the spanned scope of the type + setFlatExpressionScopeSetter((ICombineExpressionsWithLiteralsScope) p.getSpannedScope()); + check("this", "AB"); + + //use the spanned scope of the method + setFlatExpressionScopeSetter((ICombineExpressionsWithLiteralsScope) get.getSpannedScope()); + check("this", "AB"); + } + + @Test + public void failDeriveFromPrimaryThisExpression() throws IOException{ + //use the current scope -> there is no type spanning any enclosing scope + checkError("this", "0xA0272"); + } + + @Test + public void deriveFromThisExpression() throws IOException{ + /* + * example for this test: + * class Outer{ + * class Inner{ + * public void methodInner(){ + * Outer outer = Outer.this; + * } + * class Innerinner{ + * public void methodInnerInner(){ + * Outer outer = Outer.this; + * Inner inner = Inner.this; + * } + * } + * } + * } + * + * one class Outer with an inner class Inner with a method methodInner + * -> try reaching the instance of Outer from this method + * the class Inner contains an inner class InnerInner with a method methodInnerInner + * -> try reaching the instances of Inner and Outer from this method + * */ + + //build infrastructure for previous comment + OOTypeSymbol outer = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("Outer") + .setEnclosingScope(scope) + .build(); + outer.getSpannedScope().setEnclosingScope(outer.getEnclosingScope()); + add2scope(scope,outer); + MethodSymbol methodInner = OOSymbolsMill.methodSymbolBuilder() + .setName("methodInner") + .setType(_voidSymType) + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + OOTypeSymbol inner = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("Inner") + .setEnclosingScope(outer.getSpannedScope()) + .build(); + inner.addMethodSymbol(methodInner); + inner.getSpannedScope().setEnclosingScope(inner.getEnclosingScope()); + methodInner.getSpannedScope().setEnclosingScope(inner.getSpannedScope()); + add2scope(outer.getEnclosingScope(),inner); + MethodSymbol methodInnerInner = OOSymbolsMill.methodSymbolBuilder() + .setName("methodInnerInner") + .setType(_voidSymType) + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + OOTypeSymbol innerinner = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("InnerInner") + .setEnclosingScope(inner.getSpannedScope()) + .build(); + innerinner.addMethodSymbol(methodInnerInner); + innerinner.getSpannedScope().setEnclosingScope(innerinner.getEnclosingScope()); + methodInnerInner.getSpannedScope().setEnclosingScope(innerinner.getSpannedScope()); + add2scope(inner.getSpannedScope(),innerinner); + + //test 1: Outer.this in methodInner() + setFlatExpressionScopeSetter((ICombineExpressionsWithLiteralsScope) methodInner.getSpannedScope()); + check("Outer.this", "Outer"); + + //test 2&3: Outer.this and Inner.this in methodInnerInner() + setFlatExpressionScopeSetter((ICombineExpressionsWithLiteralsScope) methodInnerInner.getSpannedScope()); + check("Outer.this", "Outer"); + check("Inner.this", "Inner"); + } + + @Test + public void failDeriveFromThisExpression1() throws IOException{ + //.this in enclosing class + OOTypeSymbol fail = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("Fail") + .setEnclosingScope(scope) + .build(); + add2scope(scope,fail); + + setFlatExpressionScopeSetter(scope); + checkError("Fail.this", "0xA0252"); + } + + @Test + public void failDeriveFromThisExpression2() throws IOException{ + //.this without a type + checkError("person1.this", "0xA0252"); + } + + @Test + public void deriveFromArrayExpressionTest() throws IOException{ + SymTypeArray arrType = SymTypeExpressionFactory.createTypeArray("int",scope,1,_intSymType); + FieldSymbol a = field("a",arrType); + add2scope(scope,a); + + MethodSymbol get = method("get",arrType); + add2scope(scope,get); + + SymTypeArray arrarrType = SymTypeExpressionFactory.createTypeArray("int",scope,2,_intSymType); + FieldSymbol b = field("b",arrarrType); + add2scope(scope,b); + + MethodSymbol test = method("test",arrarrType); + add2scope(scope,test); + + TypeVarSymbol t = typeVariable("T"); + add2scope(scope,t); + OOTypeSymbol list = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("List") + .setEnclosingScope(scope) + .build(); + list.addTypeVarSymbol(t); + add2scope(scope,list); + SymTypeExpression generic = SymTypeExpressionFactory.createGenerics("List",scope,_intSymType); + SymTypeArray genarrType = SymTypeExpressionFactory.createTypeArray("List",scope,1,generic); + FieldSymbol c = field("c",genarrType); + add2scope(scope,c); + + setFlatExpressionScopeSetter(scope); + + check("b[3]", "int[]"); + check("a[3]", "int"); + check("get()[3]", "int"); + check("test()[3]", "int[]"); + check("b[3][4]", "int"); + check("test()[3][4]", "int"); + check("c[1]", "List"); + } + + @Test + public void failDeriveSymTypeOfArrayExpression() throws IOException { + //assert that a type will throw an error -> no int[4] + OOTypeSymbol test = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("Test") + .setEnclosingScope(scope) + .build(); + add2scope(scope,test); + + setFlatExpressionScopeSetter(scope); + checkError("Test[4]", "0xA0255"); + } + + @Test + public void failDeriveSymTypeOfArrayExpression2() throws IOException{ + //no integral type in the brackets + SymTypeArray arrType = SymTypeExpressionFactory.createTypeArray("int",scope,1,_intSymType); + FieldSymbol a = field("a",arrType); + add2scope(scope,a); + + setFlatExpressionScopeSetter(scope); + checkError("a[7.5]", "0xA0257"); + } + + @Test + public void failDeriveSymTypeOfArrayExpression3() throws IOException{ + //type in the array brackets + SymTypeArray arrType = SymTypeExpressionFactory.createTypeArray("int",scope,1,_intSymType); + FieldSymbol a = field("a",arrType); + add2scope(scope,a); + + setFlatExpressionScopeSetter(scope); + checkError("a[Person]", "0xA0253"); + } + + @Test + public void deriveSymTypeOfClassExpression() throws IOException{ + //soll es so funktionieren wie in Java? Dann duerfte man naemlich keine Generics akzeptieren + //hier sind erst einmal Generics mit dabei, s. Testfall #3 + //falls man einschränken möchte, kann man die CoCo NoClassExpressionForGenerics + check("String.class", "Class"); + check("Integer.class", "Class"); + check("java.util.Set.class", "Class>"); + } + + @Test + public void failDeriveSymTypeOfClassExpression() throws IOException{ + //test that types must have a name + Optional class1 = p.parse_StringExpression("3.class"); + + assertFalse(class1.isPresent()); + + Optional class2 = p.parse_StringExpression("\"Hallo\".class"); + assertFalse(class2.isPresent()); + } + + @Test + public void deriveSymTypeOfInstanceofExpression() throws IOException{ + check("person1 instanceof Person", "boolean"); + check("person2 instanceof Student", "boolean"); + check("student1 instanceof Person", "boolean"); + } + + @Test + public void failDeriveSymTypeOfInstanceOfExpression() throws IOException{ + //the expression is a type + checkError("Student instanceof Person", "0xA0267"); + } + + @Test + public void deriveSymTypeOfTypeCastExpression() throws IOException { + check("(Person) student1", "Person"); + check("(Person) person1", "Person"); + check("(Student) person1", "Student"); + } + + @Test + public void failDeriveSymTypeOfTypeCastExpression() throws IOException{ + //the expression is a type + checkError("(Student) Person", "0xA0262"); + } + + @Test + public void failDeriveSymTypeOfTypeCastExpression2() throws IOException{ + //there exists no sub/super type relation between the expression and the type + checkError("(String) person1", "0xA0266"); + } + + @Test + public void deriveSymTypeOfSuperExpression() throws IOException { + /* + * example for this test: + * class SuperOuter{ + * int field; + * public int test(){ + * return 3; + * } + * } + * + * + * + * class Outer extends SuperOuter{ + * class Inner{ + * public void methodInner(){ + * Outer outer = Outer.this; + * int a = Outer.super.test(); + * int b = Outer.super.field; + * } + * class Innerinner{ + * public void methodInnerInner(){ + * Outer outer = Outer.this; + * Inner inner = Inner.this; + * int c = Outer.super.test(); + * int d = Outer.super.field; + * } + * } + * } + * } + * + * one class SuperOuter + * one class Outer that extends SuperOuter with an inner class Inner with a method methodInner + * -> try reaching the get()-Method of SuperOuter from this method + * the class Inner contains an inner class InnerInner with a method methodInnerInner + * -> try reaching the get()-Method of SuperOuter from this method + * */ + //build infrastructure for previous comment + //class SuperOuter and its methods and fields + MethodSymbol test = OOSymbolsMill.methodSymbolBuilder() + .setName("test") + .setType(_intSymType) + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + FieldSymbol field = field("field",_intSymType); + OOTypeSymbol superOuter = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("SuperOuter") + .setEnclosingScope(scope) + .build(); + superOuter.addMethodSymbol(test); + superOuter.addFieldSymbol(field); + superOuter.getSpannedScope().setEnclosingScope(superOuter.getEnclosingScope()); + test.getSpannedScope().setEnclosingScope(superOuter.getSpannedScope()); + superOuter.setIsClass(true); + SymTypeExpression superOuterType = SymTypeExpressionFactory.createTypeObject("SuperOuter",scope); + add2scope(scope,superOuter); + + //class Outer + OOTypeSymbol outer = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("Outer") + .setSuperTypesList(Lists.newArrayList(superOuterType)) + .setEnclosingScope(scope) + .build(); + outer.getSpannedScope().setEnclosingScope(outer.getEnclosingScope()); + add2scope(scope,outer); + MethodSymbol methodInner = OOSymbolsMill.methodSymbolBuilder() + .setName("methodInner") + .setType(_voidSymType) + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + + //class Inner + OOTypeSymbol inner = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("Inner") + .setEnclosingScope(outer.getSpannedScope()) + .build(); + inner.addMethodSymbol(methodInner); + inner.getSpannedScope().setEnclosingScope(inner.getEnclosingScope()); + methodInner.getSpannedScope().setEnclosingScope(inner.getSpannedScope()); + add2scope(outer.getEnclosingScope(),inner); + MethodSymbol methodInnerInner = OOSymbolsMill.methodSymbolBuilder() + .setName("methodInnerInner") + .setType(_voidSymType) + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + + //class InnerInner + OOTypeSymbol innerinner = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("InnerInner") + .setEnclosingScope(inner.getSpannedScope()) + .build(); + innerinner.addMethodSymbol(methodInnerInner); + innerinner.getSpannedScope().setEnclosingScope(innerinner.getEnclosingScope()); + methodInnerInner.getSpannedScope().setEnclosingScope(innerinner.getSpannedScope()); + add2scope(inner.getSpannedScope(),innerinner); + + setFlatExpressionScopeSetter((ICombineExpressionsWithLiteralsScope) methodInner.getSpannedScope()); + + check("Outer.super.test()", "int"); + check("Outer.super.field", "int"); + + setFlatExpressionScopeSetter((ICombineExpressionsWithLiteralsScope) methodInnerInner.getSpannedScope()); + check("Outer.super.test()", "int"); + check("Outer.super.field", "int"); + } + + @Test + public void failDeriveFromSuperExpression1() throws IOException{ + //.super in enclosing class + OOTypeSymbol fail = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("Fail") + .setEnclosingScope(scope) + .build(); + fail.getSpannedScope().setEnclosingScope(fail.getEnclosingScope()); + add2scope(scope,fail); + + setFlatExpressionScopeSetter(scope); + checkError("Fail.super.get()", "0xA0261"); + } + + @Test + public void failDeriveFromSuperExpression2() throws IOException{ + //.super without a type + checkError("person1.super.get()", "0xA0259"); + } + + @Test + public void failDeriveFromSuperExpression3() throws IOException{ + //.super with more than one super type + //first super type + MethodSymbol test = OOSymbolsMill.methodSymbolBuilder() + .setName("test") + .setType(_intSymType) + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + OOTypeSymbol superOuter = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("SuperOuter") + .setEnclosingScope(scope) + .build(); + superOuter.addMethodSymbol(test); + superOuter.getSpannedScope().setEnclosingScope(superOuter.getEnclosingScope()); + test.getSpannedScope().setEnclosingScope(superOuter.getSpannedScope()); + superOuter.setIsClass(true); + SymTypeExpression superOuterType = SymTypeExpressionFactory.createTypeObject("SuperOuter",scope); + add2scope(scope,superOuter); + + //second super type + OOTypeSymbol superOuterTwo = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("SuperOuterTwo") + .setEnclosingScope(scope) + .build(); + superOuterTwo.getSpannedScope().setEnclosingScope(superOuterTwo.getEnclosingScope()); + superOuterTwo.setIsClass(true); + SymTypeExpression superOuterTwoType = SymTypeExpressionFactory.createTypeObject("SuperOuterTwo",scope); + add2scope(scope,superOuterTwo); + + //class Outer + OOTypeSymbol outer = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("Outer") + .setSuperTypesList(Lists.newArrayList(superOuterType, superOuterTwoType)) + .setEnclosingScope(scope) + .build(); + outer.getSpannedScope().setEnclosingScope(outer.getEnclosingScope()); + add2scope(scope,outer); + MethodSymbol methodInner = OOSymbolsMill.methodSymbolBuilder() + .setName("methodInner") + .setType(_voidSymType) + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + + //class Inner + OOTypeSymbol inner = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("Inner") + .setEnclosingScope(outer.getSpannedScope()) + .build(); + inner.addMethodSymbol(methodInner); + inner.getSpannedScope().setEnclosingScope(inner.getEnclosingScope()); + methodInner.getSpannedScope().setEnclosingScope(inner.getSpannedScope()); + add2scope(outer.getEnclosingScope(),inner); + MethodSymbol methodInnerInner = OOSymbolsMill.methodSymbolBuilder() + .setName("methodInnerInner") + .setType(_voidSymType) + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + + //class InnerInner + OOTypeSymbol innerinner = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("InnerInner") + .setEnclosingScope(inner.getSpannedScope()) + .build(); + innerinner.addMethodSymbol(methodInnerInner); + innerinner.getSpannedScope().setEnclosingScope(innerinner.getEnclosingScope()); + methodInnerInner.getSpannedScope().setEnclosingScope(innerinner.getSpannedScope()); + add2scope(inner.getSpannedScope(),innerinner); + + setFlatExpressionScopeSetter((CombineExpressionsWithLiteralsScope) methodInnerInner.getSpannedScope()); + checkError("Outer.super.test()", "0xA0261"); + } + + @Test + public void failDeriveFromSuperExpression4() throws IOException{ + //.super without a super type + //class Outer + OOTypeSymbol outer = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("Outer") + .setSuperTypesList(Lists.newArrayList()) + .setEnclosingScope(scope) + .build(); + outer.getSpannedScope().setEnclosingScope(outer.getEnclosingScope()); + add2scope(scope,outer); + + setFlatExpressionScopeSetter(scope); + checkError("Outer.super.test()", "0xA0261"); + } + + @Test + public void failDeriveFromSuperExpression5() throws IOException{ + //.super with a super type but a method that the super type does not know + MethodSymbol test = OOSymbolsMill.methodSymbolBuilder() + .setName("test") + .setType(_intSymType) + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + OOTypeSymbol superOuter = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("SuperOuter") + .setEnclosingScope(scope) + .build(); + superOuter.addMethodSymbol(test); + superOuter.getSpannedScope().setEnclosingScope(superOuter.getEnclosingScope()); + test.getSpannedScope().setEnclosingScope(superOuter.getSpannedScope()); + superOuter.setIsClass(true); + SymTypeExpression superOuterType = SymTypeExpressionFactory.createTypeObject("SuperOuter",scope); + add2scope(scope,superOuter); + + //class Outer + OOTypeSymbol outer = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("Outer") + .setSuperTypesList(Lists.newArrayList(superOuterType)) + .setEnclosingScope(scope) + .build(); + outer.getSpannedScope().setEnclosingScope(outer.getEnclosingScope()); + add2scope(scope,outer); + + setFlatExpressionScopeSetter(scope); + + //SuperOuter does not have a method "get" + checkError("Outer.super.get()", "0xA0261"); + } + + + @Test + public void deriveSymTypeOfPrimaryGenericInvocationExpression() throws IOException { + //build symbol table for the test + //type variables for the methods + TypeVarSymbol t = typeVariable("T"); + TypeVarSymbol s = typeVariable("S"); + SymTypeExpression tType = SymTypeExpressionFactory.createTypeVariable("T",scope); + SymTypeExpression sType = SymTypeExpressionFactory.createTypeVariable("S",scope); + //type ASuper with constructor + MethodSymbol asuperconstr = OOSymbolsMill.methodSymbolBuilder() + .setName("ASuper") + .setType(_intSymType) + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + add2scope(asuperconstr.getSpannedScope(), t); + OOTypeSymbol aSuper = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("ASuper") + .setEnclosingScope(scope) + .build(); + aSuper.addMethodSymbol(asuperconstr); + aSuper.setIsClass(true); + aSuper.getSpannedScope().setEnclosingScope(aSuper.getEnclosingScope()); + asuperconstr.getSpannedScope().setEnclosingScope(aSuper.getSpannedScope()); + SymTypeExpression aSuperType = SymTypeExpressionFactory.createTypeObject("ASuper",scope); + asuperconstr.setType(aSuperType); + add2scope(scope,aSuper); + + //type A with constructor and methods test,get and set + MethodSymbol test = OOSymbolsMill.methodSymbolBuilder() + .setName("test") + .setType(_intSymType) + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + add2scope(test.getSpannedScope(), t); + MethodSymbol get = OOSymbolsMill.methodSymbolBuilder() + .setName("get") + .setType(tType) + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + add2scope(get.getSpannedScope(), t); + MethodSymbol aconstr = OOSymbolsMill.methodSymbolBuilder() + .setName("A") + .setType(_intSymType) + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + add2scope(aconstr.getSpannedScope(),t); + FieldSymbol sParam = field("s", sType); + FieldSymbol tParam = field("t", tType); + MethodSymbol set = OOSymbolsMill.methodSymbolBuilder() + .setName("set") + .setType(_StringSymType) + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + add2scope(set.getSpannedScope(), sParam); + add2scope(set.getSpannedScope(), tParam); + add2scope(set.getSpannedScope(), t); + add2scope(set.getSpannedScope(), s); + OOTypeSymbol a = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("A") + .setSuperTypesList(Lists.newArrayList(aSuperType)) + .setEnclosingScope(scope) + .build(); + a.setMethodList(Lists.newArrayList(test, aconstr, get, set)); + a.getSpannedScope().setEnclosingScope(a.getEnclosingScope()); + test.getSpannedScope().setEnclosingScope(a.getSpannedScope()); + get.getSpannedScope().setEnclosingScope(a.getSpannedScope()); + aconstr.getSpannedScope().setEnclosingScope(a.getSpannedScope()); + set.getSpannedScope().setEnclosingScope(a.getSpannedScope()); + SymTypeExpression aType = SymTypeExpressionFactory.createTypeObject("A",scope); + aconstr.setType(aType); + add2scope(scope, a); + + setFlatExpressionScopeSetter((ICombineExpressionsWithLiteralsScope) a.getSpannedScope()); + + //basic test + check("test()", "int"); + + //test with variable return type of method + check("get()", "int"); + check("get()", "double"); + + //test with more than one type variable and with args in method + check("set(3.2,4)", "String"); + + //test with super() + check("super()", "ASuper"); + + //test with this() + check("this()", "A"); + } + + @Test + public void failDeriveSymTypeOfPrimaryGenericInvocationExpression() throws IOException { + //super.method() + MethodSymbol help = OOSymbolsMill.methodSymbolBuilder() + .setName("help") + .setType(_doubleSymType) + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + help.setSpannedScope(CombineExpressionsWithLiteralsMill.scope()); + OOTypeSymbol sup = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("Sup") + .setEnclosingScope(scope) + .build(); + sup.addMethodSymbol(help); + sup.setIsClass(true); + sup.getSpannedScope().setEnclosingScope(sup.getEnclosingScope()); + help.getSpannedScope().setEnclosingScope(sup.getSpannedScope()); + add2scope(scope,sup); + SymTypeExpression supType = SymTypeExpressionFactory.createTypeObject("Sup",scope); + OOTypeSymbol sub = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("Sub") + .setSuperTypesList(Lists.newArrayList(supType)) + .setEnclosingScope(scope) + .build(); + sub.getSpannedScope().setEnclosingScope(sub.getEnclosingScope()); + add2scope(scope,sub); + + setFlatExpressionScopeSetter((ICombineExpressionsWithLiteralsScope) sub.getSpannedScope()); + checkError("super.help()", "0xA0285"); + } + + @Test + public void failDeriveSymTypeOfPrimaryGenericInvocationExpression2() throws IOException { + //super.method(arg) + FieldSymbol xParam = field("x", _intSymType); + MethodSymbol help = OOSymbolsMill.methodSymbolBuilder() + .setName("help") + .setType(_doubleSymType) + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + add2scope(help.getSpannedScope(), typeVariable("T")); + add2scope(help.getSpannedScope(), xParam); + OOTypeSymbol sup = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("Sup") + .setEnclosingScope(scope) + .build(); + sup.addMethodSymbol(help); + sup.setIsClass(true); + sup.getSpannedScope().setEnclosingScope(sup.getEnclosingScope()); + help.getSpannedScope().setEnclosingScope(sup.getSpannedScope()); + add2scope(scope,sup); + SymTypeExpression supType = SymTypeExpressionFactory.createTypeObject("Sup",scope); + OOTypeSymbol sub = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("Sub") + .setSuperTypesList(Lists.newArrayList(supType)) + .setEnclosingScope(scope) + .build(); + sub.getSpannedScope().setEnclosingScope(sub.getEnclosingScope()); + add2scope(scope,sub); + + setFlatExpressionScopeSetter((ICombineExpressionsWithLiteralsScope) sub.getSpannedScope()); + checkError("super.help(2)", "0xA0285"); + } + + @Test + public void failDeriveSymTypeOfPrimaryGenericInvocationExpression3() throws IOException { + //super.field + FieldSymbol test = field("test",_booleanSymType); + OOTypeSymbol sup = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("Sup") + .setEnclosingScope(scope) + .build(); + sup.addFieldSymbol(test); + sup.setIsClass(true); + sup.getSpannedScope().setEnclosingScope(sup.getEnclosingScope()); + add2scope(scope,sup); + SymTypeExpression supType = SymTypeExpressionFactory.createTypeObject("Sup",scope); + OOTypeSymbol sub = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("Sub") + .setSuperTypesList(Lists.newArrayList(supType)) + .setEnclosingScope(scope) + .build(); + sub.getSpannedScope().setEnclosingScope(sub.getEnclosingScope()); + add2scope(scope,sub); + + setFlatExpressionScopeSetter((ICombineExpressionsWithLiteralsScope) sub.getSpannedScope()); + checkError("super.test", "0xA0285"); + } + + @Test + public void deriveSymTypeOfGenericInvocationExpression() throws IOException { + //build symbol table for the test + TypeVarSymbol t = typeVariable("T"); + MethodSymbol test = OOSymbolsMill.methodSymbolBuilder() + .setName("test") + .setType(_charSymType) + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + test.setSpannedScope(CombineExpressionsWithLiteralsMill.scope()); + add2scope(test.getSpannedScope(),t); + OOTypeSymbol a = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("A") + .setEnclosingScope(scope) + .build(); + a.addMethodSymbol(test); + a.getSpannedScope().setEnclosingScope(a.getEnclosingScope()); + test.getSpannedScope().setEnclosingScope(a.getSpannedScope()); + SymTypeExpression aType = SymTypeExpressionFactory.createTypeObject("A",scope); + FieldSymbol aField = field("a",aType); + add2scope(scope,aField); + add2scope(scope,a); + + setFlatExpressionScopeSetter(scope); + check("a.test()", "char"); + } + + @Test + public void failDeriveSymTypeOfGenericInvocationExpression() throws IOException{ + //a.this() + TypeVarSymbol t = typeVariable("T"); + MethodSymbol constr = OOSymbolsMill.methodSymbolBuilder() + .setName("A") + .setType(_charSymType) + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + add2scope(constr.getSpannedScope(), t); + OOTypeSymbol a = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("A") + .setEnclosingScope(scope) + .build(); + a.addMethodSymbol(constr); + a.getSpannedScope().setEnclosingScope(a.getEnclosingScope()); + constr.getSpannedScope().setEnclosingScope(a.getSpannedScope()); + SymTypeExpression aType = SymTypeExpressionFactory.createTypeObject("A",scope); + constr.setType(aType); + FieldSymbol aField = field("a",aType); + add2scope(scope,aField); + add2scope(scope,a); + + setFlatExpressionScopeSetter(scope); + checkError("a.this()", "0xA0282"); + } + + @Test + public void failDeriveSymTypeOfGenericInvocationExpression2() throws IOException{ + //a.super() + TypeVarSymbol t = typeVariable("T"); + MethodSymbol constr = OOSymbolsMill.methodSymbolBuilder() + .setName("ASuper") + .setType(_charSymType) + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + add2scope(constr.getSpannedScope(), t); + OOTypeSymbol aSuper = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("ASuper") + .setEnclosingScope(scope) + .build(); + aSuper.addMethodSymbol(constr); + aSuper.getSpannedScope().setEnclosingScope(aSuper.getEnclosingScope()); + constr.getSpannedScope().setEnclosingScope(aSuper.getSpannedScope()); + SymTypeExpression asupertype = SymTypeExpressionFactory.createTypeObject("ASuper",scope); + constr.setType(asupertype); + add2scope(scope,aSuper); + OOTypeSymbol a = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("A") + .setEnclosingScope(scope) + .build(); + a.getSpannedScope().setEnclosingScope(a.getEnclosingScope()); + SymTypeExpression aType = SymTypeExpressionFactory.createTypeObject("A",scope); + FieldSymbol aField = field("a",aType); + add2scope(scope,aField); + add2scope(scope,a); + + setFlatExpressionScopeSetter(scope); + checkError("a.super()", "0xA0282"); + } + + @Test + public void failDeriveSymTypeOfGenericInvocationExpression3() throws IOException{ + //Type.test() + //build symbol table for the test + TypeVarSymbol t = typeVariable("T"); + MethodSymbol test = OOSymbolsMill.methodSymbolBuilder() + .setName("test") + .setType(_charSymType) + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + add2scope(test.getSpannedScope(), t); + OOTypeSymbol a = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("A") + .setEnclosingScope(scope) + .build(); + a.addMethodSymbol(test); + a.getSpannedScope().setEnclosingScope(a.getEnclosingScope()); + test.getSpannedScope().setEnclosingScope(a.getSpannedScope()); + add2scope(scope,a); + + setFlatExpressionScopeSetter(scope); + checkError("A.test()", "0xA0282"); + } + + @Test + public void testGenericInvocationExpressionStatic() throws IOException { + //Type.test() with static field test + //build symbol table for the test + TypeVarSymbol t = typeVariable("T"); + MethodSymbol test = OOSymbolsMill.methodSymbolBuilder() + .setName("test") + .setType(_charSymType) + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + test.setSpannedScope(CombineExpressionsWithLiteralsMill.scope()); + add2scope(test.getSpannedScope(), t); + test.setIsStatic(true); + OOTypeSymbol a = OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("A") + .setEnclosingScope(scope) + .build(); + a.addMethodSymbol(test); + a.getSpannedScope().setEnclosingScope(a.getEnclosingScope()); + test.getSpannedScope().setEnclosingScope(a.getSpannedScope()); + add2scope(scope,a); + + setFlatExpressionScopeSetter(scope); + check("A.test()", "char"); + } + + @Test + public void testDeriveFromCreatorExpressionAnonymousClass() throws IOException { + /*Test cases: + 1) Default-Constructor, Creator-Expression without Arguments + 2) Constructor without Arguments, Creator-Expression without Arguments + 3) One Constructor without Arguments and one with Arguments, Creator-Expression with exactly fitting Arguments + 4) One Constructor without Arguments and one with Arguments, Creator-Expression with subtypes of arguments + */ + + //Bsp1 + OOTypeSymbol bsp1 = CombineExpressionsWithLiteralsMill.oOTypeSymbolBuilder() + .setName("Bsp1") + .setEnclosingScope(CombineExpressionsWithLiteralsMill.scope()) + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + + add2scope(scope, bsp1); + + //Bsp2 + MethodSymbol bsp2constr = CombineExpressionsWithLiteralsMill.methodSymbolBuilder() + .setName("Bsp2") + .setType(_intSymType) + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + + OOTypeSymbol bsp2 = CombineExpressionsWithLiteralsMill.oOTypeSymbolBuilder() + .setName("Bsp2") + .setEnclosingScope(CombineExpressionsWithLiteralsMill.scope()) + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + bsp2.addMethodSymbol(bsp2constr); + + SymTypeExpression bsp2Sym = SymTypeExpressionFactory.createTypeObject("Bsp2",scope); + bsp2constr.setType(bsp2Sym); + + bsp2constr.setEnclosingScope(bsp2.getSpannedScope()); + bsp2constr.getSpannedScope().setEnclosingScope(bsp2constr.getEnclosingScope()); + add2scope(bsp2.getSpannedScope(), bsp2constr); + add2scope(scope, bsp2); + + //Bsp3 + FieldSymbol field1 = CombineExpressionsWithLiteralsMill.fieldSymbolBuilder() + .setName("a") + .setType(_intSymType) + .build(); + + FieldSymbol field2 = CombineExpressionsWithLiteralsMill.fieldSymbolBuilder() + .setName("b") + .setType(_doubleSymType) + .build(); + + //first constructor with arguments + MethodSymbol bsp3constr = CombineExpressionsWithLiteralsMill.methodSymbolBuilder() + .setName("Bsp3") + .setType(_intSymType) + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + + add2scope(bsp3constr.getSpannedScope(),field1); + add2scope(bsp3constr.getSpannedScope(),field2); + + //second constructor without arguments + MethodSymbol bsp3constr2 = CombineExpressionsWithLiteralsMill.methodSymbolBuilder() + .setName("Bsp3") + .setType(_intSymType) + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + + OOTypeSymbol bsp3 = CombineExpressionsWithLiteralsMill.oOTypeSymbolBuilder() + .setName("Bsp3") + .setEnclosingScope(CombineExpressionsWithLiteralsMill.scope()) + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + bsp3.addMethodSymbol(bsp3constr2); + + SymTypeExpression bsp3Sym = SymTypeExpressionFactory.createTypeObject("Bsp3", scope); + bsp3constr.setType(bsp3Sym); + bsp3constr2.setType(bsp3Sym); + + bsp3constr.setEnclosingScope(bsp3.getSpannedScope()); + bsp3constr.getSpannedScope().setEnclosingScope(bsp3constr.getEnclosingScope()); + add2scope(bsp3.getSpannedScope(), bsp3constr); + add2scope(bsp3.getSpannedScope(), bsp3constr2); + add2scope(scope, bsp3); + + setFlatExpressionScopeSetter(scope); + check("new Bsp1()", "Bsp1"); + check("new Bsp2()", "Bsp2"); + check("new Bsp3(3, 5.6)", "Bsp3"); + check("new Bsp3('a',4)", "Bsp3"); + } + + @Test + public void failDeriveFromCreatorExpressionAnonymousClass1() throws IOException { + //1) Error when using primitive types + checkError("new int()", "0xA1312"); + } + + @Test + public void failDeriveFromCreatorExpressionAnonymousClass2() throws IOException { + //2) No constructor, Creator-Expression with Arguments + //Bsp2 + OOTypeSymbol bsp2 = CombineExpressionsWithLiteralsMill.oOTypeSymbolBuilder() + .setName("Bsp2") + .setEnclosingScope(CombineExpressionsWithLiteralsMill.scope()) + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + + add2scope(scope, bsp2); + setFlatExpressionScopeSetter(scope); + checkError("new Bsp2(3,4)", "0xA1312"); + } + + @Test + public void failDeriveFromCreatorExpressionAnonymousClass3() throws IOException { + //3) Creator-Expression with wrong number of Arguments -> 0 Arguments, so that it also checks that the Default-constructor is not invoked in this case + //Bsp3 + MethodSymbol bsp3constr = CombineExpressionsWithLiteralsMill.methodSymbolBuilder() + .setName("Bsp3") + .setType(_intSymType) + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + + FieldSymbol field1 = CombineExpressionsWithLiteralsMill.fieldSymbolBuilder() + .setName("a") + .setType(_intSymType) + .build(); + + FieldSymbol field2 = CombineExpressionsWithLiteralsMill.fieldSymbolBuilder() + .setName("b") + .setType(_doubleSymType) + .build(); + + add2scope(bsp3constr.getSpannedScope(), field1); + add2scope(bsp3constr.getSpannedScope(), field2); + + OOTypeSymbol bsp3 = CombineExpressionsWithLiteralsMill.oOTypeSymbolBuilder() + .setName("Bsp3") + .setEnclosingScope(CombineExpressionsWithLiteralsMill.scope()) + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + bsp3.addMethodSymbol(bsp3constr); + + SymTypeExpression bsp3Sym = SymTypeExpressionFactory.createTypeObject("Bsp3", scope); + bsp3constr.setType(bsp3Sym); + + bsp3constr.setEnclosingScope(bsp3.getSpannedScope()); + bsp3constr.getSpannedScope().setEnclosingScope(bsp3constr.getEnclosingScope()); + add2scope(bsp3.getSpannedScope(), bsp3constr); + add2scope(scope, bsp3); + + setFlatExpressionScopeSetter(scope); + checkError("new Bsp3()", "0xA1312"); + } + + @Test + public void failDeriveFromCreatorExpressionAnonymousClass4() throws IOException { + //4) Creator-Expression with correct number of Arguments, but not compatible arguments + //Bsp4 + FieldSymbol field1 = CombineExpressionsWithLiteralsMill.fieldSymbolBuilder() + .setName("a") + .setType(_intSymType) + .build(); + + FieldSymbol field2 = CombineExpressionsWithLiteralsMill.fieldSymbolBuilder() + .setName("b") + .setType(_doubleSymType) + .build(); + + MethodSymbol bsp4constr = CombineExpressionsWithLiteralsMill.methodSymbolBuilder() + .setName("Bsp4") + .setType(_intSymType) + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + + OOTypeSymbol bsp4 = CombineExpressionsWithLiteralsMill.oOTypeSymbolBuilder() + .setName("Bsp4") + .setEnclosingScope(CombineExpressionsWithLiteralsMill.scope()) + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + bsp4.addMethodSymbol(bsp4constr); + + bsp4constr.setSpannedScope(CombineExpressionsWithLiteralsMill.scope()); + add2scope(bsp4constr.getSpannedScope(),field1); + add2scope(bsp4constr.getSpannedScope(),field2); + SymTypeExpression bsp4Sym = SymTypeExpressionFactory.createTypeObject("Bsp4",scope); + bsp4constr.setType(bsp4Sym); + add2scope(bsp4.getSpannedScope(), bsp4constr); + add2scope(scope,bsp4); + + setFlatExpressionScopeSetter(scope); + checkError("new Bsp4(true, 4)", "0xA1312"); + } + + @Test + public void testDeriveSymTypeOfCreatorExpressionArrayCreator() throws IOException { + //Tests mit ArrayInitByExpression + + //Test mit double[3][4] --> double[][] + check("new double[3][4]", "double[][]"); + + //Test mit int[3][][] --> int[3][][] + check("new int[3][][]", "int[][][]"); + } + + @Test + public void failDeriveFromCreatorExpressionArrayCreator1() throws IOException { + //Test mit ArrayInitByExpression, keine ganzzahl in Array (z.B. new String[3.4]) + checkError("new String[3.4]", "0xA0315"); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/check/DeriveSymTypeOfLambdaExpressionsTest.java b/monticore-grammar/src/test/java/de/monticore/types/check/DeriveSymTypeOfLambdaExpressionsTest.java new file mode 100644 index 0000000000..bac4eafedb --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/check/DeriveSymTypeOfLambdaExpressionsTest.java @@ -0,0 +1,130 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.expressions.combineexpressionswithliterals.CombineExpressionsWithLiteralsMill; +import de.monticore.expressions.combineexpressionswithliterals._ast.ASTFoo; +import de.monticore.expressions.combineexpressionswithliterals._parser.CombineExpressionsWithLiteralsParser; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.ICombineExpressionsWithLiteralsArtifactScope; +import de.monticore.expressions.combineexpressionswithliterals._visitor.CombineExpressionsWithLiteralsTraverser; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.expressionsbasis._visitor.ExpressionsBasisTraverser; +import de.monticore.expressions.lambdaexpressions._symboltable.LambdaExpressionsSTCompleteTypes; +import de.monticore.grammar.grammar_withconcepts.FullSynthesizeFromMCSGT4Grammar; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import de.se_rwth.commons.logging.Log; + +public class DeriveSymTypeOfLambdaExpressionsTest extends DeriveSymTypeAbstractTest { + + /** + * Focus: Deriving Type of Literals, here: + * literals/MCLiteralsBasis.mc4 + */ + + @Before + public void init() { + CombineExpressionsWithLiteralsMill.reset(); + CombineExpressionsWithLiteralsMill.init(); + BasicSymbolsMill.initializePrimitives(); + TypeSymbol str = CombineExpressionsWithLiteralsMill.typeSymbolBuilder() + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setName("String") + .build(); + CombineExpressionsWithLiteralsMill.globalScope().add(str); + } + + @Override + protected void setupTypeCheck() { + // This is an auxiliary + FullDeriveFromCombineExpressionsWithLiterals derLit = new FullDeriveFromCombineExpressionsWithLiterals(); + + // other arguments not used (and therefore deliberately null) + // This is the TypeChecker under Test: + setTypeCheck(new TypeCalculator(null, derLit)); + } + + // Parser used for convenience: + // (may be any other Parser that understands CommonExpressions) + CombineExpressionsWithLiteralsParser p = new CombineExpressionsWithLiteralsParser(); + + @Override + protected Optional parseStringExpression(String expression) throws IOException { + return p.parse_StringExpression(expression); + } + + @Override + protected ExpressionsBasisTraverser getUsedLanguageTraverser() { + return CombineExpressionsWithLiteralsMill.traverser(); + } + + protected ASTExpression parseAndGenerateSymbolTable(String expression) throws IOException { + ASTExpression astex = parseExpression(expression); + // add symbol table for the lambda parameter symbols + ASTFoo rootNode = CombineExpressionsWithLiteralsMill.fooBuilder() + .setExpression(astex) + .build(); + ICombineExpressionsWithLiteralsArtifactScope rootScope = + CombineExpressionsWithLiteralsMill.scopesGenitorDelegator().createFromAST(rootNode); + rootScope.setName("rootName"); + // complete the symbol table + CombineExpressionsWithLiteralsTraverser traverser = + CombineExpressionsWithLiteralsMill.traverser(); + traverser.add4LambdaExpressions( + new LambdaExpressionsSTCompleteTypes(new FullSynthesizeFromMCSGT4Grammar())); + astex.accept(traverser); + return astex; + } + + protected void checkWithST(String expression, String expectedType) throws IOException { + setupTypeCheck(); + ASTExpression astex = parseAndGenerateSymbolTable(expression); + + assertEquals(expectedType, getTypeCalculator().typeOf(astex).print()); + + assertTrue(Log.getFindings().isEmpty()); + } + + /*--------------------------------------------------- TESTS ---------------------------------------------------------*/ + + @Test + public void deriveFromLambdaExpressionNoParameterTest() throws IOException { + setFlatExpressionScopeSetter(CombineExpressionsWithLiteralsMill.globalScope()); + // example with int + check("() -> 5", "() -> int"); + // example with lambda nesting + check("() -> () -> 5", "() -> () -> int"); + } + + @Test + public void deriveFromLambdaExpressionOneParameterTest() throws IOException { + setFlatExpressionScopeSetter(CombineExpressionsWithLiteralsMill.globalScope()); + // example with int, long + check("(int x) -> 5L", "(int) -> long"); + // example with input equaling output + checkWithST("(int x) -> x", "(int) -> int"); + // example with lambda nesting + checkWithST("(int x) -> (int y) -> x + y", "(int) -> (int) -> int"); + } + + @Test + public void deriveFromLambdaExpressionMultipleParameterTest() throws IOException { + setFlatExpressionScopeSetter(CombineExpressionsWithLiteralsMill.globalScope()); + // example with int, long, int + check("(int x, long y) -> 5", "(int, long) -> int"); + // example with lambda nesting + check("(int x, long y) -> () -> 5", "(int, long) -> () -> int"); + // example with int, long, expression + checkWithST("(int x, long y) -> x + y", "(int, long) -> long"); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/check/DeriveSymTypeOfLiteralsTest.java b/monticore-grammar/src/test/java/de/monticore/types/check/DeriveSymTypeOfLiteralsTest.java new file mode 100644 index 0000000000..2cb15f1645 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/check/DeriveSymTypeOfLiteralsTest.java @@ -0,0 +1,54 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.expressions.combineexpressionswithliterals.CombineExpressionsWithLiteralsMill; +import de.monticore.literals.mccommonliterals.MCCommonLiteralsMill; +import de.monticore.literals.mcliteralsbasis._ast.ASTLiteral; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class DeriveSymTypeOfLiteralsTest { + + /** + * Focus: Deriving Type of Literals, here: + * literals/MCLiteralsBasis.mc4 + */ + + + @Before + public void before() { + LogStub.init(); + Log.enableFailQuick(false); + CombineExpressionsWithLiteralsMill.reset(); + CombineExpressionsWithLiteralsMill.init(); + BasicSymbolsMill.initializePrimitives(); + } + + + // This is the core Visitor under Test (but rather empty) + IDerive derLit = new FullDeriveFromCombineExpressionsWithLiterals(); + + // other arguments not used (and therefore deliberately null) + + // This is the TypeChecker under Test: + TypeCalculator tc = new TypeCalculator(null,derLit); + + // ------------------------------------------------------ Tests for Function 2b + + @Test + public void deriveTFromLiteral1(){ + ASTLiteral lit = MCCommonLiteralsMill.natLiteralBuilder().setDigits("17").build(); + assertEquals("int",tc.typeOf(lit).print()); + + assertTrue(Log.getFindings().isEmpty()); + } + + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/check/DeriveSymTypeOfMCCommonLiteralsTest.java b/monticore-grammar/src/test/java/de/monticore/types/check/DeriveSymTypeOfMCCommonLiteralsTest.java new file mode 100644 index 0000000000..eb0a74b3c1 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/check/DeriveSymTypeOfMCCommonLiteralsTest.java @@ -0,0 +1,122 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.expressions.combineexpressionswithliterals.CombineExpressionsWithLiteralsMill; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.CombineExpressionsWithLiteralsGlobalScope; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.ICombineExpressionsWithLiteralsGlobalScope; +import de.monticore.literals.mccommonliterals.MCCommonLiteralsMill; +import de.monticore.literals.mcliteralsbasis._ast.ASTLiteral; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.se_rwth.commons.logging.*; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class DeriveSymTypeOfMCCommonLiteralsTest { + + /** + * Focus: Deriving Type of Literals, here: + * literals/MCLiteralsBasis.mc4 + */ + + @Before + public void before() { + LogStub.init(); + Log.enableFailQuick(false); + CombineExpressionsWithLiteralsMill.reset(); + CombineExpressionsWithLiteralsMill.init(); + BasicSymbolsMill.initializePrimitives(); + } + + // This is the core Visitor under Test (but rather empty) + FullDeriveFromCombineExpressionsWithLiterals derLit = new FullDeriveFromCombineExpressionsWithLiterals(); + + // other arguments not used (and therefore deliberately null) + + // This is the TypeChecker under Test: + TypeCalculator tc = new TypeCalculator(null,derLit); + + // ------------------------------------------------------ Tests for Function 2b + + // Mill used ... alternative would be a Parser for Literals + @Test + public void deriveTFromLiteral1Null() throws IOException { + ASTLiteral lit = MCCommonLiteralsMill.nullLiteralBuilder().build(); + assertEquals("null", tc.typeOf(lit).print()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void deriveTFromLiteral1Boolean() throws IOException { + ASTLiteral lit = MCCommonLiteralsMill.booleanLiteralBuilder().setSource(0).build(); + assertEquals("boolean", tc.typeOf(lit).print()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void deriveTFromLiteral1Char() throws IOException { + ASTLiteral lit = MCCommonLiteralsMill.charLiteralBuilder().setSource("c").build(); + assertEquals("char", tc.typeOf(lit).print()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void deriveTFromLiteral1String() throws IOException { + ICombineExpressionsWithLiteralsGlobalScope gs = CombineExpressionsWithLiteralsMill.globalScope(); + TypeSymbol str = CombineExpressionsWithLiteralsMill.typeSymbolBuilder() + .setName("String") + .setEnclosingScope(gs) + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + gs.add(str); + ASTLiteral lit = MCCommonLiteralsMill.stringLiteralBuilder().setSource("hjasdk").build(); + lit.setEnclosingScope(gs); + assertEquals("String", tc.typeOf(lit).print()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void deriveTFromLiteral1Int() throws IOException { + ASTLiteral lit = MCCommonLiteralsMill.natLiteralBuilder().setDigits("17").build(); + assertEquals("int", tc.typeOf(lit).print()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void deriveTFromLiteral1BasicLong() throws IOException { + ASTLiteral lit = MCCommonLiteralsMill.basicLongLiteralBuilder().setDigits("17").build(); + assertEquals("long", tc.typeOf(lit).print()); + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void deriveTFromLiteral1BasicFloat() throws IOException { + ASTLiteral lit = MCCommonLiteralsMill.basicFloatLiteralBuilder().setPre("10").setPost("03").build(); + assertEquals("float", tc.typeOf(lit).print()); + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void deriveTFromLiteral1BasicDouble() throws IOException { + ASTLiteral lit = MCCommonLiteralsMill.basicDoubleLiteralBuilder().setPre("710").setPost("93").build(); + assertEquals("double", tc.typeOf(lit).print()); + + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/check/FlatExpressionScopeSetter.java b/monticore-grammar/src/test/java/de/monticore/types/check/FlatExpressionScopeSetter.java new file mode 100644 index 0000000000..5d14f40292 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/check/FlatExpressionScopeSetter.java @@ -0,0 +1,433 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.ast.ASTNode; +import de.monticore.expressions.assignmentexpressions._ast.*; +import de.monticore.expressions.assignmentexpressions._symboltable.IAssignmentExpressionsScope; +import de.monticore.expressions.assignmentexpressions._visitor.AssignmentExpressionsTraverser; +import de.monticore.expressions.assignmentexpressions._visitor.AssignmentExpressionsVisitor2; +import de.monticore.expressions.bitexpressions._ast.*; +import de.monticore.expressions.bitexpressions._symboltable.IBitExpressionsScope; +import de.monticore.expressions.bitexpressions._visitor.BitExpressionsTraverser; +import de.monticore.expressions.bitexpressions._visitor.BitExpressionsVisitor2; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.ICombineExpressionsWithLiteralsScope; +import de.monticore.expressions.commonexpressions._ast.*; +import de.monticore.expressions.commonexpressions._symboltable.ICommonExpressionsScope; +import de.monticore.expressions.commonexpressions._visitor.CommonExpressionsTraverser; +import de.monticore.expressions.commonexpressions._visitor.CommonExpressionsVisitor2; +import de.monticore.expressions.expressionsbasis._ast.ASTArguments; +import de.monticore.expressions.expressionsbasis._ast.ASTLiteralExpression; +import de.monticore.expressions.expressionsbasis._ast.ASTNameExpression; +import de.monticore.expressions.expressionsbasis._symboltable.IExpressionsBasisScope; +import de.monticore.expressions.expressionsbasis._visitor.ExpressionsBasisTraverser; +import de.monticore.expressions.expressionsbasis._visitor.ExpressionsBasisVisitor2; +import de.monticore.expressions.javaclassexpressions._ast.*; +import de.monticore.expressions.javaclassexpressions._symboltable.IJavaClassExpressionsScope; +import de.monticore.expressions.javaclassexpressions._visitor.JavaClassExpressionsTraverser; +import de.monticore.expressions.javaclassexpressions._visitor.JavaClassExpressionsVisitor2; +import de.monticore.expressions.lambdaexpressions._symboltable.ILambdaExpressionsScope; +import de.monticore.literals.mccommonliterals._ast.ASTStringLiteral; +import de.monticore.literals.mccommonliterals._symboltable.IMCCommonLiteralsScope; +import de.monticore.literals.mccommonliterals._visitor.MCCommonLiteralsVisitor2; +import de.monticore.literals.mcliteralsbasis._symboltable.IMCLiteralsBasisScope; +import de.monticore.expressions.lambdaexpressions._ast.ASTLambdaExpression; +import de.monticore.expressions.lambdaexpressions._visitor.LambdaExpressionsVisitor2; +import de.monticore.symbols.basicsymbols._symboltable.IBasicSymbolsScope; +import de.monticore.symboltable.IScope; +import de.monticore.symboltable.ISymbol; +import de.monticore.types.mcarraytypes._ast.ASTMCArrayType; +import de.monticore.types.mcarraytypes._symboltable.IMCArrayTypesScope; +import de.monticore.types.mcarraytypes._visitor.MCArrayTypesVisitor2; +import de.monticore.types.mcbasictypes._ast.*; +import de.monticore.types.mcbasictypes._symboltable.IMCBasicTypesScope; +import de.monticore.types.mcbasictypes._visitor.MCBasicTypesVisitor2; +import de.monticore.types.mccollectiontypes._ast.*; +import de.monticore.types.mccollectiontypes._symboltable.IMCCollectionTypesScope; +import de.monticore.types.mccollectiontypes._visitor.MCCollectionTypesVisitor2; +import de.monticore.types.mcfullgenerictypes._ast.ASTMCInnerType; +import de.monticore.types.mcfullgenerictypes._ast.ASTMCMultipleGenericType; +import de.monticore.types.mcfullgenerictypes._ast.ASTMCWildcardTypeArgument; +import de.monticore.types.mcfullgenerictypes._symboltable.IMCFullGenericTypesScope; +import de.monticore.types.mcfullgenerictypes._visitor.MCFullGenericTypesVisitor2; +import de.monticore.types.mcfunctiontypes._ast.ASTMCFunctionType; +import de.monticore.types.mcfunctiontypes._symboltable.IMCFunctionTypesScope; +import de.monticore.types.mcfunctiontypes._visitor.MCFunctionTypesVisitor2; +import de.monticore.types.mcsimplegenerictypes._ast.ASTMCBasicGenericType; +import de.monticore.types.mcsimplegenerictypes._ast.ASTMCCustomTypeArgument; +import de.monticore.types.mcsimplegenerictypes._symboltable.IMCSimpleGenericTypesScope; +import de.monticore.types.mcsimplegenerictypes._visitor.MCSimpleGenericTypesVisitor2; + +public class FlatExpressionScopeSetter implements AssignmentExpressionsVisitor2, CommonExpressionsVisitor2, JavaClassExpressionsVisitor2, LambdaExpressionsVisitor2, BitExpressionsVisitor2, + ExpressionsBasisVisitor2, MCBasicTypesVisitor2, MCCollectionTypesVisitor2, MCSimpleGenericTypesVisitor2, MCArrayTypesVisitor2, MCFullGenericTypesVisitor2, MCFunctionTypesVisitor2, MCCommonLiteralsVisitor2 { + + private IScope scope; + + public FlatExpressionScopeSetter(IScope scope){ + this.scope = scope; + } + + @Override + public void visit(IScope scope){} + + @Override + public void endVisit(IScope scope){} + + @Override + public void visit(ASTNode node){} + + @Override + public void endVisit(ASTNode node){} + + @Override + public void visit(ISymbol symbol){} + + @Override + public void endVisit(ISymbol symbol){} + + /*************************************************ASSIGNMENT EXPRESSIONS****************************************************/ + + @Override + public void visit(ASTAssignmentExpression expr){ + expr.setEnclosingScope((IAssignmentExpressionsScope) scope); + } + + @Override + public void visit(ASTMinusPrefixExpression expr){ + expr.setEnclosingScope((ICommonExpressionsScope) scope); + } + + @Override + public void visit(ASTPlusPrefixExpression expr){ + expr.setEnclosingScope((ICommonExpressionsScope) scope); + } + + @Override + public void visit(ASTDecPrefixExpression expr){ + expr.setEnclosingScope((IAssignmentExpressionsScope) scope); + } + + @Override + public void visit(ASTDecSuffixExpression expr){ + expr.setEnclosingScope((IAssignmentExpressionsScope) scope); + } + + @Override + public void visit(ASTIncPrefixExpression expr){ + expr.setEnclosingScope((IAssignmentExpressionsScope) scope); + } + + @Override + public void visit(ASTIncSuffixExpression expr){ + expr.setEnclosingScope((IAssignmentExpressionsScope) scope); + } + + /*************************************************COMMON EXPRESSIONS****************************************************/ + + @Override + public void visit(ASTGreaterEqualExpression expr) { + expr.setEnclosingScope((ICommonExpressionsScope) scope); + } + + @Override + public void visit(ASTLessEqualExpression expr){ + expr.setEnclosingScope((ICommonExpressionsScope) scope); + } + + @Override + public void visit(ASTGreaterThanExpression expr){ + expr.setEnclosingScope((ICommonExpressionsScope) scope); + } + + @Override + public void visit(ASTLessThanExpression expr){ + expr.setEnclosingScope((ICommonExpressionsScope) scope); + } + + @Override + public void visit(ASTPlusExpression expr){ + expr.setEnclosingScope((ICommonExpressionsScope) scope); + } + + @Override + public void visit(ASTMinusExpression expr){ + expr.setEnclosingScope((ICommonExpressionsScope) scope); + } + + @Override + public void visit(ASTMultExpression expr){ + expr.setEnclosingScope((ICommonExpressionsScope) scope); + } + + @Override + public void visit(ASTDivideExpression expr){ + expr.setEnclosingScope((ICommonExpressionsScope) scope); + } + + @Override + public void visit(ASTModuloExpression expr){ + expr.setEnclosingScope((ICommonExpressionsScope) scope); + } + + @Override + public void visit(ASTEqualsExpression expr){ + expr.setEnclosingScope((ICommonExpressionsScope) scope); + } + + @Override + public void visit(ASTNotEqualsExpression expr){ + expr.setEnclosingScope((ICommonExpressionsScope) scope); + } + + @Override + public void visit(ASTFieldAccessExpression expr){ + expr.setEnclosingScope((ICommonExpressionsScope) scope); + } + + @Override + public void visit(ASTCallExpression expr){ + expr.setEnclosingScope((ICommonExpressionsScope) scope); + } + + @Override + public void visit(ASTLogicalNotExpression expr){ + expr.setEnclosingScope((ICommonExpressionsScope) scope); + } + + @Override + public void visit(ASTBooleanAndOpExpression expr){ + expr.setEnclosingScope((ICommonExpressionsScope) scope); + } + + @Override + public void visit(ASTBooleanOrOpExpression expr){ + expr.setEnclosingScope((ICommonExpressionsScope) scope); + } + + @Override + public void visit(ASTBooleanNotExpression expr){ + expr.setEnclosingScope((ICommonExpressionsScope) scope); + } + + @Override + public void visit(ASTBracketExpression expr){ + expr.setEnclosingScope((ICommonExpressionsScope) scope); + } + + @Override + public void visit(ASTConditionalExpression expr){ + expr.setEnclosingScope((ICommonExpressionsScope) scope); + } + + @Override + public void visit(ASTArguments expr){ + expr.setEnclosingScope((IExpressionsBasisScope) scope); + } + + /*************************************************BIT EXPRESSIONS****************************************************/ + + @Override + public void visit(ASTLogicalRightShiftExpression expr) { + expr.setEnclosingScope((IBitExpressionsScope) scope); + } + + @Override + public void visit(ASTRightShiftExpression expr) { + expr.setEnclosingScope((IBitExpressionsScope) scope); + } + + @Override + public void visit(ASTLeftShiftExpression expr) { + expr.setEnclosingScope((IBitExpressionsScope) scope); + } + + @Override + public void visit(ASTBinaryOrOpExpression expr){ + expr.setEnclosingScope((IBitExpressionsScope) scope); + } + + @Override + public void visit(ASTBinaryAndExpression expr){ + expr.setEnclosingScope((IBitExpressionsScope) scope); + } + + @Override + public void visit(ASTBinaryXorExpression expr){ + expr.setEnclosingScope((IBitExpressionsScope) scope); + } + + /*************************************************EXPRESSIONS BASIS****************************************************/ + + @Override + public void visit(ASTLiteralExpression expr){ + expr.setEnclosingScope((IExpressionsBasisScope) scope); + expr.getLiteral().setEnclosingScope((IExpressionsBasisScope) scope); + } + + + + @Override + public void visit(ASTNameExpression expr){ + expr.setEnclosingScope((IExpressionsBasisScope) scope); + } + + /*************************************************JAVA CLASS EXPRESSIONS****************************************************/ + + @Override + public void visit(ASTPrimarySuperExpression expr){ + expr.setEnclosingScope((IJavaClassExpressionsScope) scope); + } + + @Override + public void visit(ASTPrimaryThisExpression expr){ + expr.setEnclosingScope((IJavaClassExpressionsScope) scope); + } + + @Override + public void visit(ASTSuperExpression expr){ + expr.setEnclosingScope((IJavaClassExpressionsScope) scope); + } + + @Override + public void visit(ASTThisExpression expr){ + expr.setEnclosingScope((IJavaClassExpressionsScope) scope); + } + + @Override + public void visit(ASTArrayExpression expr){ + expr.setEnclosingScope((IJavaClassExpressionsScope) scope); + } + + @Override + public void visit(ASTInstanceofExpression expr){ + expr.setEnclosingScope((IJavaClassExpressionsScope) scope); + } + + @Override + public void visit(ASTTypeCastExpression expr){ + expr.setEnclosingScope((IJavaClassExpressionsScope) scope); + } + + @Override + public void visit(ASTPrimaryGenericInvocationExpression expr){ + expr.setEnclosingScope((IJavaClassExpressionsScope) scope); + } + + @Override + public void visit(ASTGenericInvocationExpression expr){ + expr.setEnclosingScope((IJavaClassExpressionsScope) scope); + } + + @Override + public void visit(ASTClassExpression expr){ + expr.setEnclosingScope((IJavaClassExpressionsScope) scope); + } + + /*************************************************LAMBDA EXPRESSIONS****************************************************/ + + @Override + public void visit(ASTLambdaExpression expr) { + expr.setEnclosingScope((ILambdaExpressionsScope) scope); + } + + /*************************************************MCBASICTYPES****************************************************/ + + @Override + public void visit(ASTMCQualifiedType type){ + type.setEnclosingScope((IMCBasicTypesScope) scope); + } + + @Override + public void visit(ASTMCQualifiedName name) { + name.setEnclosingScope((IMCBasicTypesScope) scope); + } + + @Override + public void visit(ASTMCReturnType type){ + type.setEnclosingScope((IMCBasicTypesScope) scope); + } + + @Override + public void visit(ASTMCPrimitiveType type) { + type.setEnclosingScope((IMCBasicTypesScope) scope); + } + + /*************************************************MCCOLLECTIONTYPES****************************************************/ + + @Override + public void visit(ASTMCMapType node) { + node.setEnclosingScope((IMCCollectionTypesScope) scope); + } + + @Override + public void visit(ASTMCSetType node) { + node.setEnclosingScope((IMCCollectionTypesScope) scope); + } + + @Override + public void visit(ASTMCListType node) { + node.setEnclosingScope((IMCCollectionTypesScope) scope); + } + + @Override + public void visit(ASTMCOptionalType node) { + node.setEnclosingScope((IMCCollectionTypesScope) scope); + } + + @Override + public void visit(ASTMCPrimitiveTypeArgument node){ + node.setEnclosingScope((IMCCollectionTypesScope) scope); + } + + @Override + public void visit(ASTMCBasicTypeArgument node) { + node.setEnclosingScope((IMCCollectionTypesScope) scope); + } + + /*************************************************MCSIMPLEGENERICTYPES****************************************************/ + + @Override + public void visit(ASTMCBasicGenericType type) { + type.setEnclosingScope((IMCSimpleGenericTypesScope) scope); + } + + @Override + public void visit(ASTMCCustomTypeArgument node) { + node.setEnclosingScope((IMCSimpleGenericTypesScope) scope); + } + + + /*************************************************MCFULLGENERICTYPES****************************************************/ + + @Override + public void visit(ASTMCMultipleGenericType type){ + type.setEnclosingScope((IMCFullGenericTypesScope) scope); + } + + @Override + public void visit(ASTMCWildcardTypeArgument node){ + node.setEnclosingScope((IMCFullGenericTypesScope) scope); + } + + @Override + public void visit(ASTMCInnerType node) { + node.setEnclosingScope((IMCFullGenericTypesScope) scope); + } + + /*************************************************MCFUNCTIONTYPES****************************************************/ + + @Override + public void visit(ASTMCFunctionType node) { + node.setEnclosingScope((IMCFunctionTypesScope) scope); + } + + /*************************************************MCARRAYTYPE****************************************************/ + + @Override + public void visit(ASTMCArrayType type) {type.setEnclosingScope((IMCArrayTypesScope) scope);} + + /*************************************************MCCommonLiterals****************************************************/ + @Override + public void visit(ASTStringLiteral lit){ + lit.setEnclosingScope((IMCCommonLiteralsScope) scope); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/check/FullDeriveFromCombineExpressionsWithLiterals.java b/monticore-grammar/src/test/java/de/monticore/types/check/FullDeriveFromCombineExpressionsWithLiterals.java new file mode 100644 index 0000000000..2ff9ab1e51 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/check/FullDeriveFromCombineExpressionsWithLiterals.java @@ -0,0 +1,90 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.expressions.combineexpressionswithliterals.CombineExpressionsWithLiteralsMill; +import de.monticore.expressions.combineexpressionswithliterals._visitor.CombineExpressionsWithLiteralsTraverser; + +/** + * Delegator Visitor to test the combination of the grammars + */ +public class FullDeriveFromCombineExpressionsWithLiterals extends AbstractDerive { + + private DeriveSymTypeOfAssignmentExpressions deriveSymTypeOfAssignmentExpressions; + + private DeriveSymTypeOfCommonExpressions deriveSymTypeOfCommonExpressions; + + private DeriveSymTypeOfBitExpressions deriveSymTypeOfBitExpressions; + + private DeriveSymTypeOfExpression deriveSymTypeOfExpression; + + private DeriveSymTypeOfJavaClassExpressions deriveSymTypeOfJavaClassExpressions; + + private DeriveSymTypeOfLambdaExpressions deriveSymTypeOfLambdaExpressions; + + private DeriveSymTypeOfLiterals deriveSymTypeOfLiterals; + + private DeriveSymTypeOfMCCommonLiterals deriveSymTypeOfMCCommonLiterals; + + private DeriveSymTypeOfCombineExpressions deriveSymTypeOfCombineExpressions; + + private FullSynthesizeFromCombineExpressionsWithLiterals synthesizer; + + public FullDeriveFromCombineExpressionsWithLiterals(){ + this(CombineExpressionsWithLiteralsMill.traverser()); + } + + public FullDeriveFromCombineExpressionsWithLiterals(CombineExpressionsWithLiteralsTraverser traverser){ + super(traverser); + init(traverser); + } + + /** + * set the last result of all calculators to the same object + */ + public void setTypeCheckResult(TypeCheckResult typeCheckResult){ + deriveSymTypeOfAssignmentExpressions.setTypeCheckResult(typeCheckResult); + deriveSymTypeOfMCCommonLiterals.setTypeCheckResult(typeCheckResult); + deriveSymTypeOfCommonExpressions.setTypeCheckResult(typeCheckResult); + deriveSymTypeOfExpression.setTypeCheckResult(typeCheckResult); + deriveSymTypeOfLiterals.setTypeCheckResult(typeCheckResult); + deriveSymTypeOfBitExpressions.setTypeCheckResult(typeCheckResult); + deriveSymTypeOfJavaClassExpressions.setTypeCheckResult(typeCheckResult); + deriveSymTypeOfLambdaExpressions.setTypeCheckResult(typeCheckResult); + deriveSymTypeOfCombineExpressions.setTypeCheckResult(typeCheckResult); + } + + /** + * initialize the typescalculator + */ + public void init(CombineExpressionsWithLiteralsTraverser traverser) { + deriveSymTypeOfCommonExpressions = new DeriveSymTypeOfCommonExpressions(); + deriveSymTypeOfAssignmentExpressions = new DeriveSymTypeOfAssignmentExpressions(); + deriveSymTypeOfMCCommonLiterals = new DeriveSymTypeOfMCCommonLiterals(); + deriveSymTypeOfExpression = new DeriveSymTypeOfExpression(); + deriveSymTypeOfLiterals = new DeriveSymTypeOfLiterals(); + deriveSymTypeOfBitExpressions = new DeriveSymTypeOfBitExpressions(); + deriveSymTypeOfJavaClassExpressions = new DeriveSymTypeOfJavaClassExpressions(); + synthesizer = new FullSynthesizeFromCombineExpressionsWithLiterals(); + deriveSymTypeOfLambdaExpressions = new DeriveSymTypeOfLambdaExpressions(); + deriveSymTypeOfLambdaExpressions.setSynthesize(synthesizer); + deriveSymTypeOfCombineExpressions = new DeriveSymTypeOfCombineExpressions(synthesizer); + setTypeCheckResult(getTypeCheckResult()); + + traverser.add4CommonExpressions(deriveSymTypeOfCommonExpressions); + traverser.setCommonExpressionsHandler(deriveSymTypeOfCommonExpressions); + traverser.add4AssignmentExpressions(deriveSymTypeOfAssignmentExpressions); + traverser.setAssignmentExpressionsHandler(deriveSymTypeOfAssignmentExpressions); + traverser.add4MCCommonLiterals(deriveSymTypeOfMCCommonLiterals); + traverser.add4ExpressionsBasis(deriveSymTypeOfExpression); + traverser.setExpressionsBasisHandler(deriveSymTypeOfExpression); + traverser.add4MCLiteralsBasis(deriveSymTypeOfLiterals); + traverser.add4BitExpressions(deriveSymTypeOfBitExpressions); + traverser.setBitExpressionsHandler(deriveSymTypeOfBitExpressions); + traverser.add4JavaClassExpressions(deriveSymTypeOfJavaClassExpressions); + traverser.setLambdaExpressionsHandler(deriveSymTypeOfLambdaExpressions); + traverser.add4LambdaExpressions(deriveSymTypeOfLambdaExpressions); + traverser.setJavaClassExpressionsHandler(deriveSymTypeOfJavaClassExpressions); + traverser.add4CombineExpressionsWithLiterals(deriveSymTypeOfCombineExpressions); + traverser.setCombineExpressionsWithLiteralsHandler(deriveSymTypeOfCombineExpressions); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/check/FullDeriveFromCombineExpressionsWithLiteralsAbstract.java b/monticore-grammar/src/test/java/de/monticore/types/check/FullDeriveFromCombineExpressionsWithLiteralsAbstract.java new file mode 100644 index 0000000000..4f9dc1951f --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/check/FullDeriveFromCombineExpressionsWithLiteralsAbstract.java @@ -0,0 +1,53 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.expressions.abstracttypechecktest.AbstractTypeCheckTestMill; +import de.monticore.expressions.abstracttypechecktest._visitor.AbstractTypeCheckTestTraverser; + +public class FullDeriveFromCombineExpressionsWithLiteralsAbstract extends AbstractDerive { + + private DeriveSymTypeOfBSCommonExpressions deriveSymTypeOfCommonExpressions; + + private DeriveSymTypeOfExpression deriveSymTypeOfExpression; + + private DeriveSymTypeOfLiterals deriveSymTypeOfLiterals; + + private DeriveSymTypeOfMCCommonLiterals deriveSymTypeOfMCCommonLiterals; + + public FullDeriveFromCombineExpressionsWithLiteralsAbstract(){ + this(AbstractTypeCheckTestMill.traverser()); + } + + public FullDeriveFromCombineExpressionsWithLiteralsAbstract(AbstractTypeCheckTestTraverser traverser){ + super(traverser); + init(traverser); + } + + /** + * set the last result of all calculators to the same object + */ + public void setTypeCheckResult(TypeCheckResult typeCheckResult){ + deriveSymTypeOfMCCommonLiterals.setTypeCheckResult(typeCheckResult); + deriveSymTypeOfCommonExpressions.setTypeCheckResult(typeCheckResult); + deriveSymTypeOfExpression.setTypeCheckResult(typeCheckResult); + deriveSymTypeOfLiterals.setTypeCheckResult(typeCheckResult); + } + + /** + * initialize the typescalculator + */ + public void init(AbstractTypeCheckTestTraverser traverser) { + deriveSymTypeOfCommonExpressions = new DeriveSymTypeOfBSCommonExpressions(); + deriveSymTypeOfMCCommonLiterals = new DeriveSymTypeOfMCCommonLiterals(); + deriveSymTypeOfExpression = new DeriveSymTypeOfExpression(); + deriveSymTypeOfLiterals = new DeriveSymTypeOfLiterals(); + setTypeCheckResult(getTypeCheckResult()); + + traverser.add4CommonExpressions(deriveSymTypeOfCommonExpressions); + traverser.setCommonExpressionsHandler(deriveSymTypeOfCommonExpressions); + traverser.add4MCCommonLiterals(deriveSymTypeOfMCCommonLiterals); + traverser.add4ExpressionsBasis(deriveSymTypeOfExpression); + traverser.setExpressionsBasisHandler(deriveSymTypeOfExpression); + traverser.add4MCLiteralsBasis(deriveSymTypeOfLiterals); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/check/FullSynthesizeFromCombineExpressionsWithLiterals.java b/monticore-grammar/src/test/java/de/monticore/types/check/FullSynthesizeFromCombineExpressionsWithLiterals.java new file mode 100644 index 0000000000..dc7b213714 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/check/FullSynthesizeFromCombineExpressionsWithLiterals.java @@ -0,0 +1,35 @@ +/*(c) https://github.com/MontiCore/monticore*/ +package de.monticore.types.check; + +import de.monticore.expressions.combineexpressionswithliterals.CombineExpressionsWithLiteralsMill; +import de.monticore.expressions.combineexpressionswithliterals._visitor.CombineExpressionsWithLiteralsTraverser; + +public class FullSynthesizeFromCombineExpressionsWithLiterals extends AbstractSynthesize { + + public FullSynthesizeFromCombineExpressionsWithLiterals(){ + this(CombineExpressionsWithLiteralsMill.traverser()); + } + + public FullSynthesizeFromCombineExpressionsWithLiterals(CombineExpressionsWithLiteralsTraverser traverser){ + super(traverser); + init(traverser); + } + + public void init(CombineExpressionsWithLiteralsTraverser traverser) { + SynthesizeSymTypeFromMCBasicTypes symTypeFromMCBasicTypes = new SynthesizeSymTypeFromMCBasicTypes(); + symTypeFromMCBasicTypes.setTypeCheckResult(getTypeCheckResult()); + traverser.add4MCBasicTypes(symTypeFromMCBasicTypes); + traverser.setMCBasicTypesHandler(symTypeFromMCBasicTypes); + + SynthesizeSymTypeFromMCCollectionTypes symTypeFromMCCollectionTypes = new SynthesizeSymTypeFromMCCollectionTypes(); + symTypeFromMCCollectionTypes.setTypeCheckResult(getTypeCheckResult()); + traverser.add4MCCollectionTypes(symTypeFromMCCollectionTypes); + traverser.setMCCollectionTypesHandler(symTypeFromMCCollectionTypes); + + SynthesizeSymTypeFromMCSimpleGenericTypes symTypeFromMCSimpleGenericTypes = new SynthesizeSymTypeFromMCSimpleGenericTypes(); + symTypeFromMCSimpleGenericTypes.setTypeCheckResult(getTypeCheckResult()); + traverser.add4MCSimpleGenericTypes(symTypeFromMCSimpleGenericTypes); + traverser.setMCSimpleGenericTypesHandler(symTypeFromMCSimpleGenericTypes); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/check/SymTypeExpressionDeSerTest.java b/monticore-grammar/src/test/java/de/monticore/types/check/SymTypeExpressionDeSerTest.java new file mode 100644 index 0000000000..d363a32391 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/check/SymTypeExpressionDeSerTest.java @@ -0,0 +1,483 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import com.google.common.collect.Lists; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.symbols.oosymbols.OOSymbolsMill; +import de.monticore.symbols.oosymbols._symboltable.IOOSymbolsArtifactScope; +import de.monticore.symbols.oosymbols._symboltable.IOOSymbolsScope; +import de.monticore.symbols.oosymbols._symboltable.OOTypeSymbol; +import de.monticore.symboltable.serialization.JsonParser; +import de.monticore.symboltable.serialization.JsonPrinter; +import de.monticore.symboltable.serialization.json.JsonObject; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import static de.monticore.types.check.SymTypeExpressionFactory.*; +import static org.junit.Assert.*; + +public class SymTypeExpressionDeSerTest { + // setup of objects (unchanged during tests) + // these should be the same as those of SymTypeExpressionText + SymTypePrimitive teDouble; + + SymTypePrimitive teInt; + + SymTypeVariable teVarA; + + SymTypeVariable teVarB; + + SymTypeOfObject teP; + + SymTypeOfObject teH; // on purpose: package missing + + SymTypeVoid teVoid; + + SymTypeOfNull teNull; + + SymTypeArray teArr1; + + SymTypeArray teArr3; + + SymTypeOfGenerics teSet; + + SymTypeOfGenerics teSetA; + + SymTypeOfGenerics teMap; + + SymTypeOfGenerics teFoo; + + SymTypeOfGenerics teDeep1; + + SymTypeOfGenerics teDeep2; + + SymTypeOfWildcard teLowerBound; + + SymTypeOfWildcard teUpperBound; + + SymTypeOfWildcard teWildcard; + + SymTypeOfGenerics teMap2; + + SymTypeOfFunction teFun1; + + SymTypeOfFunction teFun2; + + SymTypeOfFunction teFun3; + + SymTypeOfFunction teFun4; + + SymTypeOfUnion teUnion1; + + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + OOSymbolsMill.reset(); + OOSymbolsMill.init(); + BasicSymbolsMill.initializePrimitives(); + + IOOSymbolsScope scope = OOSymbolsMill.scope(); + + // setup of objects (unchanged during tests) + // these should be the same as those of SymTypeExpressionText + teDouble = createPrimitive("double"); + + teInt = createPrimitive("int"); + + teVarA = createTypeVariable("A", scope); + + teVarB = createTypeVariable("B", scope); + + teP = createTypeObject("de.x.Person", scope); + + teH = createTypeObject("Human", scope); // on purpose: package missing + + teVoid = createTypeVoid(); + + teNull = createTypeOfNull(); + + teArr1 = createTypeArray(teH.print(), scope, 1, teH); + + teArr3 = createTypeArray(teInt.print(), scope, 3, teInt); + + teSet = createGenerics("java.util.Set", scope, Lists.newArrayList(teP)); + + teSetA = createGenerics("java.util.Set", scope, Lists.newArrayList(teVarA)); + + teMap = createGenerics("Map", scope, + Lists.newArrayList(teInt, teP)); // no package! + + teFoo = createGenerics("x.Foo", scope, + Lists.newArrayList(teP, teDouble, teInt, teH)); + + teDeep1 = createGenerics("java.util.Set", scope, Lists.newArrayList(teMap)); + + teDeep2 = createGenerics("java.util.Map2", scope, + Lists.newArrayList(teInt, teDeep1)); + + teLowerBound = createWildcard(false, teInt); + + teUpperBound = createWildcard(true, teH); + + teWildcard = createWildcard(); + + teMap2 = createGenerics("Map", scope, + Lists.newArrayList(teUpperBound, teWildcard)); + + teFun1 = createFunction(teInt); + + teFun2 = createFunction(teInt, teInt); + + teFun3 = createFunction(teInt, teInt, teP); + + teFun4 = createFunction(teInt, List.of(teInt), true); + + teUnion1 = createUnion(teInt, teDouble); + + scope.add(new OOTypeSymbol("A")); + scope.add(new OOTypeSymbol("B")); + scope.add(new OOTypeSymbol("Human")); + scope.add(new OOTypeSymbol("Map")); + + IOOSymbolsArtifactScope javaUtilAS = OOSymbolsMill.artifactScope(); + javaUtilAS.add(new OOTypeSymbol("Map2")); + scope.addSubScope(javaUtilAS); + + IOOSymbolsArtifactScope deXAS = OOSymbolsMill.artifactScope(); + deXAS.add(new OOTypeSymbol("Person")); + scope.addSubScope(deXAS); + + IOOSymbolsArtifactScope xAS = OOSymbolsMill.artifactScope(); + xAS.add(new OOTypeSymbol("Foo")); + scope.addSubScope(xAS); + } + + @Test + public void testRoundtripSerialization() { + performRoundTripSerialization(teDouble); + performRoundTripSerialization(teInt); + performRoundTripSerialization(teVarA); + performRoundTripSerialization(teVarB); + performRoundTripSerialization(teP); + performRoundTripSerialization(teH); + performRoundTripSerialization(teVoid); + performRoundTripSerialization(teNull); + performRoundTripSerialization(teArr1); + performRoundTripSerialization(teArr3); + performRoundTripSerialization(teSet); + performRoundTripSerialization(teSetA); + performRoundTripSerialization(teMap); + performRoundTripSerialization(teFoo); + performRoundTripSerialization(teDeep1); + performRoundTripSerialization(teDeep2); + performRoundTripSerialization(teLowerBound); + performRoundTripSerialization(teUpperBound); + performRoundTripSerialization(teWildcard); + performRoundTripSerialization(teMap2); + performRoundTripSerialization(teFun1); + performRoundTripSerialization(teFun2); + performRoundTripSerialization(teFun3); + performRoundTripSerialization(teFun4); + performRoundTripSerialization(teUnion1); + + performRoundTripSerializationSymTypePrimitive(teDouble); + performRoundTripSerializationSymTypePrimitive(teInt); + performRoundTripSerializationSymTypeVariable(teVarA); + performRoundTripSerializationSymTypeVariable(teVarB); + performRoundTripSerializationSymTypeOfObject(teP); + performRoundTripSerializationSymTypeOfObject(teH); + performRoundTripSerializationSymTypeArray(teArr1); + performRoundTripSerializationSymTypeArray(teArr3); + performRoundTripSerializationSymTypeOfGenerics(teSet); + performRoundTripSerializationSymTypeOfGenerics(teSetA); + performRoundTripSerializationSymTypeOfGenerics(teMap); + performRoundTripSerializationSymTypeOfGenerics(teFoo); + performRoundTripSerializationSymTypeOfGenerics(teDeep1); + performRoundTripSerializationSymTypeOfGenerics(teDeep2); + performRoundTripSerializationSymTypeOfGenerics(teMap2); + performRoundTripSerializationSymTypeOfFunction(teFun1); + performRoundTripSerializationSymTypeOfFunction(teFun2); + performRoundTripSerializationSymTypeOfFunction(teFun3); + performRoundTripSerializationSymTypeOfFunction(teFun4); + performRoundTripSerializationSymTypeOfUnion(teUnion1); + } + + protected void performRoundTripSerialization(SymTypeExpression expr) { + SymTypeExpressionDeSer deser = SymTypeExpressionDeSer.getInstance(); + //first serialize the expression using the deser + String serialized = deser.serialize(expr); + // then deserialize it + SymTypeExpression deserialized = deser.deserialize(serialized); + assertNotNull(deserialized); + // and assert that the serialized and deserialized symtype expression equals the one before + assertEquals(expr.print(), deserialized.print()); + assertEquals(expr.printAsJson(), deserialized.printAsJson()); + if (!(deserialized instanceof SymTypeOfWildcard)) { + TypeSymbol expectedTS = deserialized.getTypeInfo(); + TypeSymbol actualTS = expr.getTypeInfo(); + assertEquals(expectedTS.getName(), actualTS.getName()); + } + } + + protected void performRoundTripSerializationSymTypeOfGenerics(SymTypeOfGenerics expr) { + SymTypeOfGenericsDeSer deser = new SymTypeOfGenericsDeSer(); + + String serialized = deser.serialize(expr); + + SymTypeExpression deserialized = deser.deserialize(serialized); + assertNotNull(deserialized); + + assertEquals(expr.print(), deserialized.print()); + assertEquals(expr.printAsJson(), deserialized.printAsJson()); + TypeSymbol expectedTS = deserialized.getTypeInfo(); + TypeSymbol actualTS = deserialized.getTypeInfo(); + assertEquals(expectedTS.getName(), actualTS.getName()); + } + + protected void performRoundTripSerializationSymTypeOfFunction(SymTypeOfFunction expr) { + SymTypeOfFunctionDeSer deser = new SymTypeOfFunctionDeSer(); + + String serialized = deser.serialize(expr); + + SymTypeExpression deserialized = deser.deserialize(serialized); + assertNotNull(deserialized); + + assertEquals(expr.print(), deserialized.print()); + assertEquals(expr.printAsJson(), deserialized.printAsJson()); + TypeSymbol expectedTS = deserialized.getTypeInfo(); + TypeSymbol actualTS = deserialized.getTypeInfo(); + assertEquals(expectedTS.getName(), actualTS.getName()); + } + + protected void performRoundTripSerializationSymTypeOfObject(SymTypeOfObject expr) { + SymTypeOfObjectDeSer deser = new SymTypeOfObjectDeSer(); + + String serialized = deser.serialize(expr); + + SymTypeExpression deserialized = deser.deserialize(serialized); + assertNotNull(deserialized); + + assertEquals(expr.print(), deserialized.print()); + assertEquals(expr.printAsJson(), deserialized.printAsJson()); + TypeSymbol expectedTS = deserialized.getTypeInfo(); + TypeSymbol actualTS = deserialized.getTypeInfo(); + assertEquals(expectedTS.getName(), actualTS.getName()); + } + + protected void performRoundTripSerializationSymTypeVariable(SymTypeVariable expr) { + SymTypeVariableDeSer deser = new SymTypeVariableDeSer(); + + String serialized = deser.serialize(expr); + + SymTypeExpression deserialized = deser.deserialize(serialized); + assertNotNull(deserialized); + + assertEquals(expr.print(), deserialized.print()); + assertEquals(expr.printAsJson(), deserialized.printAsJson()); + TypeSymbol expectedTS = deserialized.getTypeInfo(); + TypeSymbol actualTS = deserialized.getTypeInfo(); + assertEquals(expectedTS.getName(), actualTS.getName()); + } + + protected void performRoundTripSerializationSymTypeArray(SymTypeArray expr) { + SymTypeArrayDeSer deser = new SymTypeArrayDeSer(); + + String serialized = deser.serialize(expr); + + SymTypeExpression deserialized = deser.deserialize(serialized); + assertNotNull(deserialized); + + assertEquals(expr.print(), deserialized.print()); + assertEquals(expr.printAsJson(), deserialized.printAsJson()); + TypeSymbol expectedTS = deserialized.getTypeInfo(); + TypeSymbol actualTS = deserialized.getTypeInfo(); + assertEquals(expectedTS.getName(), actualTS.getName()); + + //assertTrue(Log.getFindings().isEmpty()); + } + + protected void performRoundTripSerializationSymTypePrimitive(SymTypePrimitive expr) { + SymTypePrimitiveDeSer deser = new SymTypePrimitiveDeSer(); + + String serialized = deser.serialize(expr); + + SymTypeExpression deserialized = deser.deserialize(serialized); + assertNotNull(deserialized); + + assertEquals(expr.print(), deserialized.print()); + assertEquals(expr.printAsJson(), deserialized.printAsJson()); + TypeSymbol expectedTS = deserialized.getTypeInfo(); + TypeSymbol actualTS = deserialized.getTypeInfo(); + assertEquals(expectedTS.getName(), actualTS.getName()); + } + + protected void performRoundTripSerializationSymTypeOfUnion(SymTypeOfUnion expr) { + SymTypeOfUnionDeSer deser = new SymTypeOfUnionDeSer(); + + String serialized = deser.serialize(expr); + + SymTypeExpression deserialized = deser.deserialize(serialized); + assertNotNull(deserialized); + + assertTrue(expr.deepEquals(deserialized)); + assertEquals(deser.serialize(expr), deser.serialize((SymTypeOfUnion) deserialized)); + TypeSymbol expectedTS = deserialized.getTypeInfo(); + TypeSymbol actualTS = deserialized.getTypeInfo(); + assertEquals(expectedTS.getName(), actualTS.getName()); + } + + @Test + public void testRoundtrip2() { + performRoundtrip2(teDouble); + performRoundtrip2(teInt); + performRoundtrip2(teVarA); + performRoundtrip2(teVarB); + performRoundtrip2(teP); + performRoundtrip2(teH); + performRoundtrip2(teVoid); + performRoundtrip2(teNull); + performRoundtrip2(teArr1); + performRoundtrip2(teArr3); + performRoundtrip2(teSet); + performRoundtrip2(teSetA); + performRoundtrip2(teMap); + performRoundtrip2(teFoo); + performRoundtrip2(teDeep1); + performRoundtrip2(teDeep2); + performRoundtrip2(teUpperBound); + performRoundtrip2(teLowerBound); + performRoundtrip2(teWildcard); + performRoundtrip2(teMap2); + performRoundtrip2(teFun1); + performRoundtrip2(teFun2); + performRoundtrip2(teFun3); + performRoundtrip2(teFun4); + performRoundtrip2(teUnion1); + } + + protected void performRoundtrip2(SymTypeExpression expr) { + SymTypeExpressionDeSer deser = SymTypeExpressionDeSer.getInstance(); + //first serialize the expression using the deser + String serialized = deser.serialize(expr); + + // then deserialize it + SymTypeExpression loaded = deser.deserialize(serialized); + assertNotNull(loaded); + // and assert that the serialized and deserialized symtype expression equals the one before + assertEquals(expr.print(), loaded.print()); + assertEquals(expr.printAsJson(), loaded.printAsJson()); + if (!(loaded instanceof SymTypeOfWildcard)) { + TypeSymbol expectedTS = loaded.getTypeInfo(); + TypeSymbol actualTS = expr.getTypeInfo(); + assertEquals(expectedTS.getName(), actualTS.getName()); + } + + // usual member + JsonPrinter printer = new JsonPrinter(); + printer.beginObject(); + SymTypeExpressionDeSer.serializeMember(printer, "foo", expr); + printer.endObject(); + //produce a fake JSON object from the serialized member and parse this + JsonObject json = JsonParser.parseJsonObject(printer.getContent()); + SymTypeExpression deserialized = SymTypeExpressionDeSer.deserializeMember("foo", json); + assertEquals(expr.print(), deserialized.print()); + + // optional member that is present + printer = new JsonPrinter(); + printer.beginObject(); + SymTypeExpressionDeSer.serializeMember(printer, "foo", Optional.ofNullable(expr)); + printer.endObject(); + json = JsonParser.parseJsonObject(printer.getContent()); + Optional deserializedOpt = SymTypeExpressionDeSer + .deserializeOptionalMember("foo", json); + assertTrue(deserializedOpt.isPresent()); + assertEquals(expr.print(), deserializedOpt.get().print()); + + // optional member that is empty + printer = new JsonPrinter(true); + printer.beginObject(); + SymTypeExpressionDeSer.serializeMember(printer, "foo", Optional.empty()); + printer.endObject(); + json = JsonParser.parseJsonObject(printer.getContent()); + deserializedOpt = SymTypeExpressionDeSer.deserializeOptionalMember("foo", json); + assertTrue(!deserializedOpt.isPresent()); + + // list member that is empty + printer = new JsonPrinter(true); + printer.beginObject(); + SymTypeExpressionDeSer.serializeMember(printer, "foo", new ArrayList<>()); + printer.endObject(); + json = JsonParser.parseJsonObject(printer.getContent()); + List deserializedList = SymTypeExpressionDeSer + .deserializeListMember("foo", json); + assertEquals(0, deserializedList.size()); + + // list member with single element + printer = new JsonPrinter(); + printer.beginObject(); + SymTypeExpressionDeSer.serializeMember(printer, "foo", Lists.newArrayList(expr)); + printer.endObject(); + json = JsonParser.parseJsonObject(printer.getContent()); + deserializedList = SymTypeExpressionDeSer.deserializeListMember("foo", json); + assertEquals(1, deserializedList.size()); + assertEquals(expr.print(), deserializedList.get(0).print()); + + // list member with two elements + printer = new JsonPrinter(); + printer.beginObject(); + SymTypeExpressionDeSer.serializeMember(printer, "foo", Lists.newArrayList(expr, expr)); + printer.endObject(); + json = JsonParser.parseJsonObject(printer.getContent()); + deserializedList = SymTypeExpressionDeSer.deserializeListMember("foo", json); + assertEquals(2, deserializedList.size()); + assertEquals(expr.print(), deserializedList.get(0).print()); + assertEquals(expr.print(), deserializedList.get(1).print()); + } + + @Test + public void testInvalidJsonForSerializingReturnsError() { + String invalidJsonForSerializing = "\"Foo\":\"bar\""; + String invalidJsonForSerializing2 = "{\n\t\"symTypeExpression\": {\n\t\t\"foo\":\"bar\", \n\t\t\"foo2\":\"bar2\"\n\t}\n}"; + + SymTypeExpressionDeSer.getInstance().deserialize(invalidJsonForSerializing); + assertTrue(Log.getFindings().get(Log.getFindings().size() - 1).getMsg().startsWith("0x823FE")); + + SymTypeExpressionDeSer.getInstance().deserialize(invalidJsonForSerializing2); + assertTrue(Log.getFindings().get(Log.getFindings().size() - 1).getMsg().startsWith("0x823FE")); + + SymTypeOfGenericsDeSer symTypeOfGenericsDeSer = new SymTypeOfGenericsDeSer(); + symTypeOfGenericsDeSer.deserialize(invalidJsonForSerializing2); + assertTrue(Log.getFindings().get(Log.getFindings().size() - 1).getMsg().startsWith("0x823F6")); + + SymTypeArrayDeSer symTypeArrayDeSer = new SymTypeArrayDeSer(); + symTypeArrayDeSer.deserialize(invalidJsonForSerializing2); + assertTrue(Log.getFindings().get(Log.getFindings().size() - 1).getMsg().startsWith("0x823F2")); + + SymTypeOfObjectDeSer symTypeOfObjectDeSer = new SymTypeOfObjectDeSer(); + symTypeOfObjectDeSer.deserialize(invalidJsonForSerializing2); + assertTrue(Log.getFindings().get(Log.getFindings().size() - 1).getMsg().startsWith("0x823F4")); + + SymTypeVariableDeSer symTypeVariableDeSer = new SymTypeVariableDeSer(); + symTypeVariableDeSer.deserialize(invalidJsonForSerializing2); + assertTrue(Log.getFindings().get(Log.getFindings().size() - 1).getMsg().startsWith("0x823F5")); + + SymTypePrimitiveDeSer symTypePrimitiveDeser = new SymTypePrimitiveDeSer(); + symTypePrimitiveDeser.deserialize(invalidJsonForSerializing2); + assertTrue(Log.getFindings().get(Log.getFindings().size() - 1).getMsg().startsWith("0x823F1")); + + SymTypeOfUnionDeSer symTypeOfUnionDeser = new SymTypeOfUnionDeSer(); + symTypeOfUnionDeser.deserialize(invalidJsonForSerializing2); + assertTrue(Log.getFindings().get(Log.getFindings().size() - 1).getMsg().startsWith("0x9E2F7")); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/check/SymTypeExpressionTest.java b/monticore-grammar/src/test/java/de/monticore/types/check/SymTypeExpressionTest.java new file mode 100644 index 0000000000..37ad3866f0 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/check/SymTypeExpressionTest.java @@ -0,0 +1,1068 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import com.google.common.collect.Lists; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.symbols.oosymbols.OOSymbolsMill; +import de.monticore.symbols.oosymbols._symboltable.IOOSymbolsScope; +import de.monticore.symbols.oosymbols._symboltable.OOTypeSymbol; +import de.monticore.symboltable.serialization.JsonParser; +import de.monticore.symboltable.serialization.json.JsonElement; +import de.monticore.symboltable.serialization.json.JsonObject; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.Spliterator; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static de.monticore.types.check.DefsTypeBasic._intSymType; +import static de.monticore.types.check.SymTypeExpressionFactory.*; +import static org.junit.Assert.*; + +public class SymTypeExpressionTest { + + private static IOOSymbolsScope scope = OOSymbolsMill.scope(); + + // setup of objects (unchanged during tests) + static SymTypeExpression teDouble; + + static SymTypeExpression teInt; + + static SymTypeExpression teVarA; + + static SymTypeExpression teIntA; + + static SymTypeExpression teVarB; + + static SymTypeExpression teP; + + static SymTypeExpression teH; // on purpose: package missing + + static SymTypeExpression teVoid; + + static SymTypeExpression teNull; + + static SymTypeExpression teArr1; + + static SymTypeExpression teArr3; + + static SymTypeExpression teSet; + + static SymTypeExpression teSetA; + + static SymTypeExpression teSetC; + + static SymTypeExpression teMap; // no package! + + static SymTypeExpression teFoo; + + static SymTypeExpression teMap2; + + static SymTypeExpression teMapA; + + static SymTypeExpression teSetB; + + static SymTypeExpression teDeep1; + + static SymTypeExpression teDeep2; + + static SymTypeExpression teUpperBound; + + static SymTypeExpression teLowerBound; + + static SymTypeExpression teWildcard; + + static SymTypeExpression teMap3; + + static SymTypeExpression teFunc1; + + static SymTypeExpression teFunc2; + + static SymTypeExpression teFunc3; + + static SymTypeExpression teFunc4; + + static SymTypeExpression teUnion1; + + static SymTypeExpression teInter1; + + static SymTypeExpression teObscure; + + @Before + public void init(){ + LogStub.init(); + Log.enableFailQuick(false); + OOSymbolsMill.reset(); + OOSymbolsMill.init(); + BasicSymbolsMill.initializePrimitives(); + scope.add(new OOTypeSymbol("long")); + scope.add(new OOTypeSymbol("Human")); + scope = OOSymbolsMill.scope(); + + // setup of objects (unchanged during tests) + teDouble = createPrimitive("double"); + + teInt = createPrimitive("int"); + + teVarA = createTypeVariable("A", scope); + + teIntA = createTypeObject("java.lang.Integer",scope); + + teVarB = createTypeVariable("B", scope); + + teP = createTypeObject("de.x.Person", scope); + + teH = createTypeObject("Human", + scope); // on purpose: package missing + + teVoid = createTypeVoid(); + + teNull = createTypeOfNull(); + + teArr1 = createTypeArray(teH.print(), scope, 1, teH); + + teArr3 = createTypeArray(teInt.print(), scope, 3, teInt); + + teSet = createGenerics("java.util.Set", scope, Lists.newArrayList(teP)); + + teSetA = createGenerics("java.util.Set", scope, Lists.newArrayList(teVarA)); + + teSetC = createGenerics("Set",scope,Lists.newArrayList(teInt)); + + teMap = createGenerics("Map", scope, Lists.newArrayList(teInt, teP)); // no package! + + teFoo = createGenerics("x.Foo", scope, Lists.newArrayList(teP, teDouble, teInt, teH)); + + teMap2 = createGenerics("Map",scope,Lists.newArrayList(teSetC,teFoo)); + + teMapA = createGenerics("java.util.Map",scope,Lists.newArrayList(teIntA,teP)); + + teSetB = createGenerics("java.util.Set",scope,Lists.newArrayList(teMapA)); + + teDeep1 = createGenerics("java.util.Set", scope, Lists.newArrayList(teMap)); + + teDeep2 = createGenerics("java.util.Map2", scope, Lists.newArrayList(teInt, teDeep1)); + + teUpperBound = createWildcard(true, teInt); + + teLowerBound = createWildcard(false, teH); + + teWildcard = createWildcard(); + + teMap3 = createGenerics("java.util.Map", scope, Lists.newArrayList(teUpperBound, teWildcard)); + + teFunc1 = createFunction(teVoid); + + teFunc2 = createFunction(teInt, Lists.newArrayList(teDouble, teInt)); + + teFunc3 = createFunction(teFunc1, Lists.newArrayList(teFunc2)); + + teFunc4 = createFunction(teVoid, Lists.newArrayList(teDouble, teInt), true); + + teUnion1 = createUnion(teInt, teDouble); + + teInter1 = createIntersection(teInt, teDouble); + + teObscure = createObscureType(); + + } + + @Test + public void subTypeTest() { + assertTrue(teInt.isPrimitive()); + assertTrue(teInt.isValidType()); + assertFalse(teInt.isGenericType()); + assertFalse(teInt.isTypeVariable()); + assertFalse(teInt.isArrayType()); + assertFalse(teInt.isVoidType()); + assertFalse(teInt.isNullType()); + assertFalse(teInt.isObjectType()); + assertFalse(teInt.isFunctionType()); + assertFalse(teInt.isObscureType()); + assertFalse(teInt.isWildcard()); + assertFalse(teInt.isUnionType()); + + assertTrue(teVarA.isTypeVariable()); + assertFalse(teVarA.isValidType()); + assertTrue(teP.isObjectType()); + assertTrue(teP.isValidType()); + assertTrue(teVoid.isVoidType()); + assertTrue(teVoid.isValidType()); + assertTrue(teNull.isNullType()); + assertTrue(teNull.isValidType()); + assertTrue(teArr1.isArrayType()); + assertTrue(teArr1.isValidType()); + assertTrue(teSet.isGenericType()); + assertTrue(teSet.isValidType()); + assertTrue(teUpperBound.isWildcard()); + assertFalse(teUpperBound.isValidType()); + assertTrue(teFunc1.isFunctionType()); + assertTrue(teFunc1.isValidType()); + assertTrue(teUnion1.isUnionType()); + assertTrue(teUnion1.isValidType()); + assertTrue(teInter1.isIntersectionType()); + assertTrue(teInter1.isValidType()); + assertTrue(teObscure.isObscureType()); + assertFalse(teObscure.isValidType()); + } + + @Test + public void printTest() { + assertEquals("double", teDouble.print()); + assertEquals("int", teInt.print()); + assertEquals("A", teVarA.print()); + assertEquals("de.x.Person", teP.print()); + assertEquals("void", teVoid.print()); + assertEquals("null", teNull.print()); + assertEquals("Human[]", teArr1.print()); + assertEquals("int[][][]", teArr3.print()); + assertEquals("java.util.Set", teSet.printFullName()); + assertEquals("java.util.Set", teSetA.printFullName()); + assertEquals("Map", teMap.printFullName()); + assertEquals("x.Foo", teFoo.printFullName()); + assertEquals("java.util.Set>", teDeep1.printFullName()); + assertEquals("java.util.Map2>>", teDeep2.printFullName()); + assertEquals("? extends int", teUpperBound.print()); + assertEquals("? super Human", teLowerBound.print()); + assertEquals("?",teWildcard.print()); + assertEquals("java.util.Map", teMap3.printFullName()); + assertEquals("() -> void", teFunc1.print()); + assertEquals("(double, int) -> int", teFunc2.print()); + assertEquals("((double, int) -> int) -> () -> void", teFunc3.print()); + assertEquals("(double, int...) -> void", teFunc4.print()); + assertEquals("(double | int)", teUnion1.print()); + assertEquals("(double & int)", teInter1.print()); + } + + @Test + public void printAsJsonTest() { + JsonElement result = JsonParser.parse(teDouble.printAsJson()); + assertTrue(result.isJsonObject()); + JsonObject teDoubleJson = result.getAsJsonObject(); + assertEquals("de.monticore.types.check.SymTypePrimitive", teDoubleJson.getStringMember("kind")); + assertEquals("double", teDoubleJson.getStringMember("primitiveName")); + + result = JsonParser.parse(teInt.printAsJson()); + assertTrue(result.isJsonObject()); + JsonObject teIntJson = result.getAsJsonObject(); + assertEquals("de.monticore.types.check.SymTypePrimitive", teIntJson.getStringMember("kind")); + assertEquals("int", teIntJson.getStringMember("primitiveName")); + + result = JsonParser.parse(teVarA.printAsJson()); + assertTrue(result.isJsonObject()); + JsonObject teVarAJson = result.getAsJsonObject(); + assertEquals("de.monticore.types.check.SymTypeVariable", teVarAJson.getStringMember("kind")); + assertEquals("A", teVarAJson.getStringMember("varName")); + + result = JsonParser.parse(teP.printAsJson()); + assertTrue(result.isJsonObject()); + JsonObject tePJson = result.getAsJsonObject(); + assertEquals("de.monticore.types.check.SymTypeOfObject", tePJson.getStringMember("kind")); + assertEquals("de.x.Person", tePJson.getStringMember("objName")); + + result = JsonParser.parse(teVoid.printAsJson()); + assertTrue(result.isJsonString()); + assertEquals("void", result.getAsJsonString().getValue()); + + result = JsonParser.parse(teNull.printAsJson()); + assertTrue(result.isJsonString()); + assertEquals("null", result.getAsJsonString().getValue()); + + result = JsonParser.parse(teArr1.printAsJson()); + assertTrue(result.isJsonObject()); + JsonObject teArr1Json = result.getAsJsonObject(); + assertEquals("de.monticore.types.check.SymTypeArray", teArr1Json.getStringMember("kind")); + assertEquals(1, teArr1Json.getIntegerMember("dim"), 0.01); + JsonObject teArr1ArgJson = teArr1Json.getObjectMember("argument"); + assertEquals("de.monticore.types.check.SymTypeOfObject", teArr1ArgJson.getStringMember("kind")); + assertEquals("Human", teArr1ArgJson.getStringMember("objName")); + + result = JsonParser.parse(teArr3.printAsJson()); + assertTrue(result.isJsonObject()); + JsonObject teArr3Json = result.getAsJsonObject(); + assertEquals("de.monticore.types.check.SymTypeArray", teArr3Json.getStringMember("kind")); + assertEquals(3, teArr3Json.getIntegerMember("dim"), 0.01); + JsonObject teArr3ArgJson = teArr3Json.getObjectMember("argument"); + assertEquals("de.monticore.types.check.SymTypePrimitive", teArr3ArgJson.getStringMember("kind")); + assertEquals("int", teArr3ArgJson.getStringMember("primitiveName")); + + result = JsonParser.parse(teSet.printAsJson()); + assertTrue(result.isJsonObject()); + JsonObject teSetJson = result.getAsJsonObject(); + assertEquals("de.monticore.types.check.SymTypeOfGenerics", teSetJson.getStringMember("kind")); + assertEquals("java.util.Set", teSetJson.getStringMember("typeConstructorFullName")); + List teSetArgsJson = teSetJson.getArrayMember("arguments"); + assertEquals(1, teSetArgsJson.size(), 0.01); + assertEquals("de.monticore.types.check.SymTypeOfObject", + teSetArgsJson.get(0).getAsJsonObject().getStringMember("kind")); + assertEquals("de.x.Person", teSetArgsJson.get(0).getAsJsonObject().getStringMember("objName")); + + result = JsonParser.parse(teSetA.printAsJson()); + assertTrue(result.isJsonObject()); + JsonObject teSetAJson = result.getAsJsonObject(); + assertEquals("de.monticore.types.check.SymTypeOfGenerics", teSetAJson.getStringMember("kind")); + assertEquals("java.util.Set", teSetAJson.getStringMember("typeConstructorFullName")); + List teSetAArgsJson = teSetAJson.getArrayMember("arguments"); + assertEquals(1, teSetAArgsJson.size(), 0.01); + assertEquals("de.monticore.types.check.SymTypeVariable", + teSetAArgsJson.get(0).getAsJsonObject().getStringMember("kind")); + assertEquals("A", teSetAArgsJson.get(0).getAsJsonObject().getStringMember("varName")); + + result = JsonParser.parse(teMap.printAsJson()); + assertTrue(result.isJsonObject()); + JsonObject teMapJson = result.getAsJsonObject(); + assertEquals("de.monticore.types.check.SymTypeOfGenerics", teMapJson.getStringMember("kind")); + assertEquals("Map", teMapJson.getStringMember("typeConstructorFullName")); + List teMapArgsJson = teMapJson.getArrayMember("arguments"); + assertEquals(2, teMapArgsJson.size(), 0.01); + assertEquals("de.monticore.types.check.SymTypePrimitive", + teMapArgsJson.get(0).getAsJsonObject().getStringMember("kind")); + assertEquals("int", teMapArgsJson.get(0).getAsJsonObject().getStringMember("primitiveName")); + assertEquals("de.monticore.types.check.SymTypeOfObject", + teMapArgsJson.get(1).getAsJsonObject().getStringMember("kind")); + assertEquals("de.x.Person", teMapArgsJson.get(1).getAsJsonObject().getStringMember("objName")); + + result = JsonParser.parse(teFoo.printAsJson()); + assertTrue(result.isJsonObject()); + JsonObject teFooJson = result.getAsJsonObject(); + assertEquals("de.monticore.types.check.SymTypeOfGenerics", teFooJson.getStringMember("kind")); + assertEquals("x.Foo", teFooJson.getStringMember("typeConstructorFullName")); + List teFooArgsJson = teFooJson.getArrayMember("arguments"); + assertEquals(4, teFooArgsJson.size(), 0.01); + assertEquals("de.monticore.types.check.SymTypeOfObject", + teFooArgsJson.get(0).getAsJsonObject().getStringMember("kind")); + assertEquals("de.x.Person", teFooArgsJson.get(0).getAsJsonObject().getStringMember("objName")); + assertEquals("de.monticore.types.check.SymTypePrimitive", + teFooArgsJson.get(1).getAsJsonObject().getStringMember("kind")); + assertEquals("double", teFooArgsJson.get(1).getAsJsonObject().getStringMember("primitiveName")); + assertEquals("de.monticore.types.check.SymTypePrimitive", + teFooArgsJson.get(2).getAsJsonObject().getStringMember("kind")); + assertEquals("int", teFooArgsJson.get(2).getAsJsonObject().getStringMember("primitiveName")); + assertEquals("de.monticore.types.check.SymTypeOfObject", + teFooArgsJson.get(3).getAsJsonObject().getStringMember("kind")); + assertEquals("Human", teFooArgsJson.get(3).getAsJsonObject().getStringMember("objName")); + + result = JsonParser.parse(teDeep1.printAsJson()); + assertTrue(result.isJsonObject()); + JsonObject teDeep1Json = result.getAsJsonObject(); + assertEquals("de.monticore.types.check.SymTypeOfGenerics", teDeep1Json.getStringMember("kind")); + assertEquals("java.util.Set", teDeep1Json.getStringMember("typeConstructorFullName")); + List teDeep1ArgsJson = teDeep1Json.getArrayMember("arguments"); + assertEquals(1, teDeep1ArgsJson.size(), 0.01); + assertEquals("de.monticore.types.check.SymTypeOfGenerics", teDeep1ArgsJson.get(0).getAsJsonObject().getStringMember( "kind")); + assertEquals("Map", teDeep1ArgsJson.get(0).getAsJsonObject().getStringMember( "typeConstructorFullName")); + List teDeep1teMapArgsJson = teDeep1ArgsJson.get(0).getAsJsonObject() + .getArrayMember("arguments"); + assertEquals(2, teDeep1teMapArgsJson.size(), 0.01); + assertEquals("de.monticore.types.check.SymTypePrimitive", teDeep1teMapArgsJson.get(0).getAsJsonObject().getStringMember( "kind")); + assertEquals("int", teDeep1teMapArgsJson.get(0).getAsJsonObject().getStringMember("primitiveName")); + assertEquals("de.monticore.types.check.SymTypeOfObject", teDeep1teMapArgsJson.get(1).getAsJsonObject().getStringMember( "kind")); + assertEquals("de.x.Person", teDeep1teMapArgsJson.get(1).getAsJsonObject().getStringMember( "objName")); + + result = JsonParser.parse(teDeep2.printAsJson()); + assertTrue(result.isJsonObject()); + JsonObject teDeep2Json = result.getAsJsonObject(); + assertEquals("de.monticore.types.check.SymTypeOfGenerics", teDeep2Json.getStringMember("kind")); + assertEquals("java.util.Map2", teDeep2Json.getStringMember("typeConstructorFullName")); + List teDeep2ArgsJson = teDeep2Json.getArrayMember("arguments"); + assertEquals(2, teDeep2ArgsJson.size(), 0.01); + assertEquals("de.monticore.types.check.SymTypePrimitive", teDeep2ArgsJson.get(0).getAsJsonObject().getStringMember( "kind")); + assertEquals("int", teDeep2ArgsJson.get(0).getAsJsonObject().getStringMember( "primitiveName")); + assertEquals("de.monticore.types.check.SymTypeOfGenerics", teDeep2ArgsJson.get(1).getAsJsonObject().getStringMember( "kind")); + assertEquals("java.util.Set", teDeep2ArgsJson.get(1).getAsJsonObject().getStringMember( "typeConstructorFullName")); + List teDeep2SetArgsJson = teDeep2ArgsJson.get(1).getAsJsonObject() + .getArrayMember("arguments"); + assertEquals(1, teDeep2SetArgsJson.size(), 0.01); + assertEquals("de.monticore.types.check.SymTypeOfGenerics", teDeep2SetArgsJson.get(0).getAsJsonObject().getStringMember( "kind")); + assertEquals("Map", teDeep2SetArgsJson.get(0).getAsJsonObject().getStringMember( "typeConstructorFullName")); + List teDeep2SetMapArgsJson = teDeep2SetArgsJson.get(0).getAsJsonObject() + .getArrayMember("arguments"); + assertEquals(2, teDeep2SetMapArgsJson.size(), 0.01); + assertEquals("de.monticore.types.check.SymTypePrimitive", teDeep2SetMapArgsJson.get(0).getAsJsonObject().getStringMember( "kind")); + assertEquals("int", teDeep2SetMapArgsJson.get(0).getAsJsonObject().getStringMember( "primitiveName")); + assertEquals("de.monticore.types.check.SymTypeOfObject", teDeep2SetMapArgsJson.get(1).getAsJsonObject().getStringMember( "kind")); + assertEquals("de.x.Person", teDeep2SetMapArgsJson.get(1).getAsJsonObject().getStringMember( "objName")); + + result = JsonParser.parse(teUpperBound.printAsJson()); + assertTrue(result.isJsonObject()); + JsonObject teUpperBound2Json = result.getAsJsonObject(); + assertEquals("de.monticore.types.check.SymTypeOfWildcard",teUpperBound2Json.getStringMember("kind")); + assertTrue(teUpperBound2Json.getBooleanMember("isUpper")); + JsonObject bound = teUpperBound2Json.getObjectMember("bound"); + assertEquals("de.monticore.types.check.SymTypePrimitive",bound.getStringMember("kind")); + assertEquals("int",bound.getStringMember("primitiveName")); + + result = JsonParser.parse(teFunc2.printAsJson()); + assertTrue(result.isJsonObject()); + JsonObject teFunc2Json = result.getAsJsonObject(); + assertEquals("de.monticore.types.check.SymTypeOfFunction",teFunc2Json.getStringMember("kind")); + JsonObject func2returnType = teFunc2Json.getObjectMember("returnType"); + assertEquals("de.monticore.types.check.SymTypePrimitive", func2returnType.getStringMember( "kind")); + assertEquals("int", func2returnType.getStringMember( "primitiveName")); + List func2Arguments = teFunc2Json.getArrayMember("argumentTypes"); + assertEquals(2, func2Arguments.size()); + assertEquals("de.monticore.types.check.SymTypePrimitive", func2Arguments.get(0).getAsJsonObject().getStringMember( "kind")); + assertEquals("double", func2Arguments.get(0).getAsJsonObject().getStringMember( "primitiveName")); + assertEquals("de.monticore.types.check.SymTypePrimitive", func2Arguments.get(1).getAsJsonObject().getStringMember( "kind")); + assertEquals("int", func2Arguments.get(1).getAsJsonObject().getStringMember( "primitiveName")); + assertFalse(teFunc2Json.getBooleanMember("elliptic")); + + result = JsonParser.parse(teFunc4.printAsJson()); + assertTrue(result.isJsonObject()); + JsonObject teFunc4Json = result.getAsJsonObject(); + assertTrue(teFunc4Json.getBooleanMember("elliptic")); + + result = JsonParser.parse(teUnion1.printAsJson()); + assertTrue(result.isJsonObject()); + JsonObject teUnion1Json = result.getAsJsonObject(); + assertEquals("de.monticore.types.check.SymTypeOfUnion", + teUnion1Json.getStringMember("kind")); + assertEquals(2, teUnion1Json.getArrayMember("unionizedTypes").size()); + List union1Types = teUnion1Json.getArrayMember("unionizedTypes"); + assertEquals("de.monticore.types.check.SymTypePrimitive", + union1Types.get(0).getAsJsonObject().getStringMember( "kind")); + assertEquals("double", + union1Types.get(0).getAsJsonObject().getStringMember( "primitiveName")); + assertEquals("de.monticore.types.check.SymTypePrimitive", + union1Types.get(1).getAsJsonObject().getStringMember( "kind")); + assertEquals("int", + union1Types.get(1).getAsJsonObject().getStringMember( "primitiveName")); + + result = JsonParser.parse(teInter1.printAsJson()); + assertTrue(result.isJsonObject()); + JsonObject teInter1Json = result.getAsJsonObject(); + assertEquals("de.monticore.types.check.SymTypeOfIntersection", + teInter1Json.getStringMember("kind")); + assertEquals(2, teInter1Json.getArrayMember("intersectedTypes").size()); + List intersected1Types = teInter1Json.getArrayMember("intersectedTypes"); + assertEquals("de.monticore.types.check.SymTypePrimitive", + intersected1Types.get(0).getAsJsonObject().getStringMember( "kind")); + assertEquals("double", + intersected1Types.get(0).getAsJsonObject().getStringMember( "primitiveName")); + assertEquals("de.monticore.types.check.SymTypePrimitive", + intersected1Types.get(1).getAsJsonObject().getStringMember( "kind")); + assertEquals("int", + intersected1Types.get(1).getAsJsonObject().getStringMember( "primitiveName")); + } + + @Test + public void baseNameTest() { + assertEquals("Person", ((SymTypeOfObject) (teP)).getBaseName()); + assertEquals("Human", ((SymTypeOfObject) (teH)).getBaseName()); + assertEquals("Map", ((SymTypeOfGenerics) (teMap)).getBaseName()); + assertEquals("Set", ((SymTypeOfGenerics) (teSet)).getBaseName()); + } + + @Test + public void unboxTest(){ + assertEquals("Set>",SymTypeOfGenerics.unbox((SymTypeOfGenerics)teSetB)); + assertEquals("Set",SymTypeOfGenerics.unbox((SymTypeOfGenerics)teSet)); + assertEquals("Set",SymTypeOfGenerics.unbox((SymTypeOfGenerics)teSetA)); + assertEquals("Map",SymTypeOfGenerics.unbox((SymTypeOfGenerics)teMap)); + assertEquals("Map,x.Foo>",SymTypeOfGenerics.unbox((SymTypeOfGenerics)teMap2)); + } + + @Test + public void boxTest() { + assertEquals("java.util.Set>", SymTypeOfGenerics.box((SymTypeOfGenerics) teSetB)); + assertEquals("java.util.Set", SymTypeOfGenerics.box((SymTypeOfGenerics) teSet)); + assertEquals("java.util.Set", SymTypeOfGenerics.box((SymTypeOfGenerics) teSetA)); + assertEquals("java.util.Map", SymTypeOfGenerics.box((SymTypeOfGenerics)teMap)); + assertEquals("java.util.Map,x.Foo>",SymTypeOfGenerics.box((SymTypeOfGenerics)teMap2)); + } + + @Test + public void testHasTypeInfo() { + assertTrue(teInt.hasTypeInfo()); + assertFalse(SymTypeExpressionFactory.createPrimitive((TypeSymbol) null).hasTypeInfo()); + assertTrue(teVarA.hasTypeInfo()); + assertTrue(teVarB.hasTypeInfo()); + assertTrue(teIntA.hasTypeInfo()); + assertFalse(SymTypeExpressionFactory.createTypeObject(null).hasTypeInfo()); + assertTrue(teP.hasTypeInfo()); + assertTrue(teH.hasTypeInfo()); + assertFalse(teVoid.hasTypeInfo()); + assertFalse(teNull.hasTypeInfo()); + assertFalse(teArr1.hasTypeInfo()); + assertFalse(teArr3.hasTypeInfo()); + assertTrue(teSetA.hasTypeInfo()); + assertTrue(teSetB.hasTypeInfo()); + assertTrue(teSetC.hasTypeInfo()); + assertTrue(teMap.hasTypeInfo()); + assertTrue(teMapA.hasTypeInfo()); + assertTrue(teMap3.hasTypeInfo()); + assertTrue(teFoo.hasTypeInfo()); + assertTrue(teDeep1.hasTypeInfo()); + assertTrue(teDeep2.hasTypeInfo()); + assertFalse(teUpperBound.hasTypeInfo()); + assertFalse(teLowerBound.hasTypeInfo()); + assertFalse(teWildcard.hasTypeInfo()); + assertFalse(teFunc1.hasTypeInfo()); + assertFalse(teFunc2.hasTypeInfo()); + assertFalse(teFunc3.hasTypeInfo()); + assertFalse(teFunc4.hasTypeInfo()); + assertFalse(teUnion1.hasTypeInfo()); + assertFalse(teObscure.hasTypeInfo()); + } + + @Test + public void deepCloneTest(){ + //SymTypeVoid + assertTrue(teVoid.deepClone() instanceof SymTypeVoid); + assertEquals(teVoid.getTypeInfo().getName(),teVoid.deepClone().getTypeInfo().getName()); + assertEquals(teVoid.print(),teVoid.deepClone().print()); + + //SymTypeOfNull + assertTrue(teNull.deepClone() instanceof SymTypeOfNull); + assertEquals(teNull.getTypeInfo().getName(),teNull.deepClone().getTypeInfo().getName()); + assertEquals(teNull.print(),teNull.deepClone().print()); + + //SymTypeVariable + assertTrue(teVarA.deepClone() instanceof SymTypeVariable); + assertFalse(teVarA.deepClone().isPrimitive()); + assertTrue(teVarA.deepClone().isTypeVariable()); + assertEquals(teVarA.print(),teVarA.deepClone().print()); + + //SymTypePrimitive + assertTrue(teInt.deepClone() instanceof SymTypePrimitive); + assertEquals(teInt.getTypeInfo().getName(), teInt.deepClone().getTypeInfo().getName()); + assertTrue(teInt.deepClone().isPrimitive()); + assertEquals(teInt.print(),teInt.deepClone().print()); + + //SymTypeOfObject + assertTrue(teH.deepClone() instanceof SymTypeOfObject); + assertEquals(teH.print(),teH.deepClone().print()); + + //SymTypeArray + assertTrue(teArr1.deepClone() instanceof SymTypeArray); + assertEquals(teArr1.print(),teArr1.deepClone().print()); + assertEquals(((SymTypeArray)teArr1).getDim(),((SymTypeArray)teArr1.deepClone()).getDim()); + assertEquals(((SymTypeArray)teArr1).getArgument().print(),((SymTypeArray)teArr1.deepClone()).getArgument().print()); + + //SymTypeOfGenerics + assertTrue(teDeep1.deepClone() instanceof SymTypeOfGenerics); + assertTrue(teDeep1.deepClone().isGenericType()); + assertEquals(teDeep1.print(),teDeep1.deepClone().print()); + + //SymTypeOfWildcard + assertTrue(teUpperBound.deepClone() instanceof SymTypeOfWildcard); + assertEquals(((SymTypeOfWildcard) teUpperBound).getBound().print(), ((SymTypeOfWildcard) teUpperBound.deepClone()).getBound().print()); + assertEquals(teUpperBound.print(), teUpperBound.deepClone().print()); + + //SymTypeOfFunction + assertTrue(teFunc3.deepClone() instanceof SymTypeOfFunction); + assertTrue(teFunc3.deepClone().isFunctionType()); + assertEquals(teFunc3.print(), teFunc3.deepClone().print()); + + //SymTypeOfUnion + assertTrue(teUnion1.deepClone() instanceof SymTypeOfUnion); + assertTrue(teUnion1.deepClone().isUnionType()); + assertEquals(teUnion1.print(), teUnion1.deepClone().print()); + + //SymTypeOfIntersection + assertTrue(teInter1.deepClone() instanceof SymTypeOfIntersection); + assertTrue(teInter1.deepClone().isIntersectionType()); + assertEquals(teInter1.print(), teInter1.deepClone().print()); + } + + @Test + public void testSymTypeExpressionFactory(){ + SymTypeVoid tVoid = SymTypeExpressionFactory.createTypeVoid(); + assertEquals("void",tVoid.print()); + + SymTypeOfNull tNull = SymTypeExpressionFactory.createTypeOfNull(); + assertEquals("null",tNull.print()); + + SymTypePrimitive tInt = SymTypeExpressionFactory.createPrimitive("int"); + assertEquals("int",tInt.print()); + assertTrue(tInt.isIntegralType()); + + SymTypeOfGenerics tA = SymTypeExpressionFactory.createGenerics("A",scope); + assertEquals("A<>",tA.print()); + assertTrue(tA.isEmptyArguments()); + + SymTypeOfGenerics tB = SymTypeExpressionFactory.createGenerics("B",scope,Lists.newArrayList(teArr1,teIntA)); + assertEquals("B",tB.printFullName()); + assertEquals(2,tB.sizeArguments()); + + SymTypeOfGenerics tC = SymTypeExpressionFactory.createGenerics("C",scope,teDeep1,teDeep2); + assertEquals("C>,java.util.Map2>>>",tC.printFullName()); + assertEquals(2,tC.sizeArguments()); + + SymTypeOfGenerics tD = SymTypeExpressionFactory.createGenerics("D",scope); + assertEquals("D<>",tD.printFullName()); + assertTrue(tD.isEmptyArguments()); + + SymTypeOfGenerics tE = SymTypeExpressionFactory.createGenerics("E",scope,Lists.newArrayList(teDouble,teMap)); + assertEquals("E>",tE.printFullName()); + assertEquals(2,tE.sizeArguments()); + + SymTypeOfGenerics tF = SymTypeExpressionFactory.createGenerics("F",scope,teH,teP); + assertEquals("F",tF.printFullName()); + assertEquals(2,tF.sizeArguments()); + + SymTypeArray tHuman = SymTypeExpressionFactory.createTypeArray("Human",scope,1,teH); + assertEquals("Human[]",tHuman.print()); + assertEquals(1,tHuman.getDim()); + assertEquals("Human",tHuman.getArgument().print()); + + SymTypeArray tPerson = SymTypeExpressionFactory.createTypeArray("de.x.Person",scope,2,teP); + assertEquals("de.x.Person[][]",tPerson.print()); + assertEquals(2,tPerson.getDim()); + assertEquals("de.x.Person",tPerson.getArgument().print()); + + SymTypeOfObject tG = SymTypeExpressionFactory.createTypeObject("G",scope); + assertEquals("G",tG.print()); + + SymTypeOfObject tH = SymTypeExpressionFactory.createTypeObject("H",scope); + assertEquals("H",tH.print()); + + SymTypeVariable tT = SymTypeExpressionFactory.createTypeVariable("T",scope); + assertEquals("T",tT.print()); + + SymTypeVariable tS = SymTypeExpressionFactory.createTypeVariable("S",scope); + assertEquals("S",tS.print()); + + SymTypeExpression tExpr = SymTypeExpressionFactory.createTypeExpression("void",scope); + assertTrue(tExpr instanceof SymTypeVoid); + assertEquals("void",tExpr.print()); + + SymTypeOfFunction tFunc1 = SymTypeExpressionFactory.createFunction(tVoid); + assertEquals("() -> void", tFunc1.print()); + + SymTypeOfFunction tFunc2 = SymTypeExpressionFactory.createFunction(tVoid, Lists.newArrayList(tFunc1, tFunc1)); + assertEquals("(() -> void, () -> void) -> void", tFunc2.print()); + + SymTypeOfFunction tFunc3 = SymTypeExpressionFactory.createFunction(tVoid, tFunc1, tFunc1); + assertEquals("(() -> void, () -> void) -> void", tFunc3.print()); + + SymTypeOfFunction tFunc4 = SymTypeExpressionFactory.createFunction(tVoid, Lists.newArrayList(teDouble, teInt), true); + assertEquals("(double, int...) -> void", tFunc4.print()); + + SymTypeOfUnion tUnion1 = createUnion(teInt, teDouble); + assertEquals("(double | int)", tUnion1.print()); + + SymTypeOfUnion tUnion2 = createUnion(Set.of(teInt, teDouble, teArr1)); + assertEquals("(Human[] | double | int)", tUnion2.print()); + + SymTypeOfIntersection tInter1 = createIntersection(teInt, teDouble); + assertEquals("(double & int)", tInter1.print()); + + SymTypeOfIntersection tInter2 = createIntersection(Set.of(teInt, teDouble, teArr1)); + assertEquals("(Human[] & double & int)", tInter2.print()); + } + + @Test + public void testGenericArguments(){ + SymTypeExpression teFoo = createGenerics("x.Foo", scope, Lists.newArrayList(teP, teDouble, teInt, teH)); + assertTrue(teFoo.isGenericType()); + SymTypeOfGenerics teFoo2 = (SymTypeOfGenerics) teFoo; + //getArgumentList & getArgument + assertEquals(4, teFoo2.getArgumentList().size()); + assertEquals(teP,teFoo2.getArgument(0)); + assertEquals(teDouble,teFoo2.getArgument(1)); + assertEquals(teInt,teFoo2.getArgument(2)); + assertEquals(teH,teFoo2.getArgument(3)); + List arguments = teFoo2.getArgumentList(); + + //toArrayArguments + Object[] args = teFoo2.toArrayArguments(); + assertEquals(teP,args[0]); + assertEquals(teDouble,args[1]); + assertEquals(teInt,args[2]); + assertEquals(teH,args[3]); + + //toArrayArguments2 + SymTypeExpression[] symArgs = teFoo2.toArrayArguments(new SymTypeExpression[4]); + assertEquals(teP,symArgs[0]); + assertEquals(teDouble,symArgs[1]); + assertEquals(teInt,symArgs[2]); + assertEquals(teH,symArgs[3]); + + //subListArguments + List subList = teFoo2.subListArguments(1,3); + assertEquals(2,subList.size()); + assertEquals(teDouble,subList.get(0)); + assertEquals(teInt,subList.get(1)); + + //containsArgument + assertTrue(teFoo2.containsArgument(teDouble)); + assertFalse(teFoo2.containsArgument(teDeep1)); + + //containsAllArguments + assertTrue(teFoo2.containsAllArguments(subList)); + + //indexOfArgument + assertEquals(0,teFoo2.indexOfArgument(teP)); + + //lastIndexOfArgument + assertEquals(0,teFoo2.lastIndexOfArgument(teP)); + + //equalsArguments + assertTrue(teFoo2.equalsArguments(teFoo2.getArgumentList())); + assertFalse(teFoo2.equalsArguments(subList)); + + //listIteratorArguments + Iterator it = teFoo2.listIteratorArguments(); + int i = 0; + while(it.hasNext()){ + assertEquals(symArgs[i],it.next()); + ++i; + } + assertEquals(4,i); + + //listIteratorArguments + Iterator it3 = teFoo2.listIteratorArguments(1); + i=0; + while(it3.hasNext()){ + assertEquals(symArgs[i+1],it3.next()); + ++i; + } + assertEquals(3,i); + + //iteratorArguments + Iterator it2 = teFoo2.iteratorArguments(); + i = 0; + while(it2.hasNext()){ + assertEquals(symArgs[i],it2.next()); + ++i; + } + assertEquals(4,i); + + //spliteratorArguments + Spliterator split = teFoo2.spliteratorArguments(); + assertEquals(4,split.getExactSizeIfKnown()); + split.forEachRemaining(SymTypeExpression::print); + + //sizeArguments + assertEquals(4,teFoo2.sizeArguments()); + + //streamArguments + Stream stream =teFoo2.streamArguments(); + List list = stream.filter(SymTypeExpression::isPrimitive) + .collect(Collectors.toList()); + assertEquals(2,list.size()); + assertEquals(teDouble,list.get(0)); + assertEquals(teInt,list.get(1)); + + //parallelStreamArguments + Stream parStream = teFoo2.parallelStreamArguments(); + List parList = parStream.filter(SymTypeExpression::isPrimitive) + .collect(Collectors.toList()); + assertEquals(2,parList.size()); + assertEquals(teDouble,parList.get(0)); + assertEquals(teInt,parList.get(1)); + + //hashCodeArguments + assertEquals(teFoo2.getArgumentList().hashCode(),teFoo2.hashCodeArguments()); + + //forEachArguments + teFoo2.forEachArguments(SymTypeExpression::deepClone); + assertEquals(teP,teFoo2.getArgument(0)); + + //setArgument + teFoo2.setArgument(2,teDeep2); + assertEquals(teDeep2,teFoo2.getArgument(2)); + + //addArgument + teFoo2.addArgument(teSetA); + assertEquals(5, teFoo2.sizeArguments()); + assertEquals(teSetA,teFoo2.getArgument(4)); + teFoo2.addArgument(3,teArr3); + assertEquals(6,teFoo2.sizeArguments()); + assertEquals(teArr3,teFoo2.getArgument(3)); + + //removeArgument + teFoo2.removeArgument(teArr3); + assertFalse(teFoo2.containsArgument(teArr3)); + assertEquals(5,teFoo2.sizeArguments()); + teFoo2.removeArgument(4); + assertFalse(teFoo2.containsArgument(teSetA)); + assertEquals(4,teFoo2.sizeArguments()); + + //clearArguments, isEmptyArguments + assertFalse(teFoo2.isEmptyArguments()); + teFoo2.clearArguments(); + assertEquals(0,teFoo2.sizeArguments()); + assertTrue(teFoo2.isEmptyArguments()); + + //setArgumentList + arguments = Lists.newArrayList(teP,teDouble,teInt,teH); + teFoo2.setArgumentList(arguments); + assertEquals(4, teFoo2.sizeArguments()); + assertEquals(teP,teFoo2.getArgument(0)); + assertEquals(teDouble,teFoo2.getArgument(1)); + assertEquals(teInt,teFoo2.getArgument(2)); + assertEquals(teH,teFoo2.getArgument(3)); + + //sortArguments + teFoo2.sortArguments((arg1,arg2) -> arg1.hashCode()+arg2.hashCode()); + assertEquals(4,teFoo2.sizeArguments()); + + //addAllArguments + teFoo2.setArgumentList(Lists.newArrayList()); + assertTrue(teFoo2.isEmptyArguments()); + arguments = Lists.newArrayList(teP,teDouble,teInt,teH); + teFoo2.addAllArguments(arguments); + assertEquals(4, teFoo2.getArgumentList().size()); + assertEquals(teP,teFoo2.getArgument(0)); + assertEquals(teDouble,teFoo2.getArgument(1)); + assertEquals(teInt,teFoo2.getArgument(2)); + assertEquals(teH,teFoo2.getArgument(3)); + + //retainAllArguments + subList = Lists.newArrayList(teP,teH); + teFoo2.retainAllArguments(subList); + assertEquals(2,teFoo2.sizeArguments()); + assertEquals(teP,teFoo2.getArgument(0)); + assertEquals(teH,teFoo2.getArgument(1)); + + //removeAllArguments + teFoo2.removeAllArguments(subList); + assertTrue(teFoo2.isEmptyArguments()); + + //replaceAllArguments + arguments = Lists.newArrayList(teP,teDouble,teInt,teH); + teFoo2.setArgumentList(arguments); + teFoo2.replaceAllArguments(SymTypeExpression::deepClone); + assertEquals(4,teFoo2.sizeArguments()); + assertTrue(teFoo2.equalsArguments(arguments)); + + //removeIfArgument + teFoo2.removeIfArgument(SymTypeExpression::isPrimitive); + assertEquals(2,teFoo2.sizeArguments()); + } + + @Test + public void symTypeArrayTest(){ + SymTypeArray array = SymTypeExpressionFactory.createTypeArray("int",scope,1,_intSymType); + assertEquals("int[]",array.print()); + array.setDim(2); + assertEquals("int[][]",array.print()); + } + + @Test + public void symTypeArrayCloneWithLessDimTest() { + SymTypeArray arr3 = (SymTypeArray) teArr3; + assertEquals("int[][][][]", arr3.cloneWithLessDim(-1).print()); + assertEquals("int[][][]", arr3.cloneWithLessDim(0).print()); + assertEquals("int[][]", arr3.cloneWithLessDim(1).print()); + assertEquals("int[]", arr3.cloneWithLessDim(2).print()); + assertEquals("int", arr3.cloneWithLessDim(3).print()); + assertFalse(arr3.cloneWithLessDim(3).isArrayType()); + } + + @Test + public void symTypePrimitiveTest(){ + SymTypePrimitive intType = SymTypeExpressionFactory.createPrimitive("int"); + assertEquals("int",intType.print()); + intType.setPrimitiveName("double"); + assertEquals("double",intType.print()); + intType.setPrimitiveName("int"); + + assertEquals("java.lang.Integer",intType.getBoxedPrimitiveName()); + assertEquals("Integer",intType.getBaseOfBoxedName()); + assertTrue(intType.isIntegralType()); + assertTrue(intType.isNumericType()); + } + + @Test + public void symTypeOfWildcardTest(){ + SymTypeOfWildcard upperBoundInt = (SymTypeOfWildcard) teUpperBound; + assertEquals("? extends int", upperBoundInt.print()); + assertEquals("int", upperBoundInt.getBound().print()); + assertTrue(upperBoundInt.isUpper()); + } + + @Test + public void testFunctionArguments() { + SymTypeExpression teFunExp = createFunction(teVoid, + Lists.newArrayList(teP, teDouble, teInt, teH)); + assertTrue(teFunExp.isFunctionType()); + SymTypeOfFunction teFun = (SymTypeOfFunction) teFunExp; + + //getArgumentTypeList & getArgumentType + assertEquals(4, teFun.getArgumentTypeList().size()); + assertEquals(teP, teFun.getArgumentType(0)); + assertEquals(teDouble, teFun.getArgumentType(1)); + assertEquals(teInt, teFun.getArgumentType(2)); + assertEquals(teH, teFun.getArgumentType(3)); + List arguments = teFun.getArgumentTypeList(); + + //toArrayArguments + Object[] args = teFun.toArrayArgumentTypes(); + assertEquals(teP, args[0]); + assertEquals(teDouble, args[1]); + assertEquals(teInt, args[2]); + assertEquals(teH, args[3]); + + //toArrayArguments2 + SymTypeExpression[] symArgs = teFun.toArrayArgumentTypes(new SymTypeExpression[4]); + assertEquals(teP, symArgs[0]); + assertEquals(teDouble, symArgs[1]); + assertEquals(teInt, symArgs[2]); + assertEquals(teH, symArgs[3]); + + //subListArguments + List subList = teFun.subListArgumentTypes(1, 3); + assertEquals(2, subList.size()); + assertEquals(teDouble, subList.get(0)); + assertEquals(teInt, subList.get(1)); + + //containsArgument + assertTrue(teFun.containsArgumentType(teDouble)); + assertFalse(teFun.containsArgumentType(teDeep1)); + + //containsAllArgumentTypes + assertTrue(teFun.containsAllArgumentTypes(subList)); + + //indexOfArgument + assertEquals(0, teFun.indexOfArgumentType(teP)); + + //lastIndexOfArgument + assertEquals(0, teFun.lastIndexOfArgumentType(teP)); + + //equalsArgumentTypes + assertTrue(teFun.equalsArgumentTypeTypes(teFun.getArgumentTypeList())); + assertFalse(teFun.equalsArgumentTypeTypes(subList)); + + //listIteratorArgumentTypes + Iterator it = teFun.listIteratorArgumentTypes(); + int i = 0; + while (it.hasNext()) { + assertEquals(symArgs[i], it.next()); + ++i; + } + assertEquals(4, i); + + //listIteratorArgumentTypes + Iterator it3 = teFun.listIteratorArgumentTypes(1); + i = 0; + while (it3.hasNext()) { + assertEquals(symArgs[i + 1], it3.next()); + ++i; + } + assertEquals(3, i); + + //iteratorArgumentTypes + Iterator it2 = teFun.iteratorArgumentTypes(); + i = 0; + while (it2.hasNext()) { + assertEquals(symArgs[i], it2.next()); + ++i; + } + assertEquals(4, i); + + //spliteratorArgumentTypes + Spliterator split = teFun.spliteratorArgumentTypes(); + assertEquals(4, split.getExactSizeIfKnown()); + split.forEachRemaining(SymTypeExpression::print); + + //sizeArgumentTypes + assertEquals(4, teFun.sizeArgumentTypes()); + + //streamArgumentTypes + Stream stream = teFun.streamArgumentTypes(); + List list = stream.filter(SymTypeExpression::isPrimitive) + .collect(Collectors.toList()); + assertEquals(2, list.size()); + assertEquals(teDouble, list.get(0)); + assertEquals(teInt, list.get(1)); + + //parallelStreamArgumentTypes + Stream parStream = teFun.parallelStreamArgumentTypes(); + List parList = parStream.filter(SymTypeExpression::isPrimitive) + .collect(Collectors.toList()); + assertEquals(2, parList.size()); + assertEquals(teDouble, parList.get(0)); + assertEquals(teInt, parList.get(1)); + + //hashCodeArgumentTypes + assertEquals(teFun.getArgumentTypeList().hashCode(), teFun.hashCodeArgumentTypes()); + + //forEachArgumentTypes + teFun.forEachArgumentTypes(SymTypeExpression::deepClone); + assertEquals(teP, teFun.getArgumentType(0)); + + //setArgument + teFun.setArgumentType(2, teDeep2); + assertEquals(teDeep2, teFun.getArgumentType(2)); + + //addArgument + teFun.addArgumentType(teSetA); + assertEquals(5, teFun.sizeArgumentTypes()); + assertEquals(teSetA, teFun.getArgumentType(4)); + teFun.addArgumentType(3, teArr3); + assertEquals(6, teFun.sizeArgumentTypes()); + assertEquals(teArr3, teFun.getArgumentType(3)); + + //removeArgument + teFun.removeArgumentType(teArr3); + assertFalse(teFun.containsArgumentType(teArr3)); + assertEquals(5, teFun.sizeArgumentTypes()); + teFun.removeArgumentType(4); + assertFalse(teFun.containsArgumentType(teSetA)); + assertEquals(4, teFun.sizeArgumentTypes()); + + //clearArgumentTypes, isEmptyArgumentTypes + assertFalse(teFun.isEmptyArgumentTypes()); + teFun.clearArgumentTypes(); + assertEquals(0, teFun.sizeArgumentTypes()); + assertTrue(teFun.isEmptyArgumentTypes()); + + //setArgumentList + arguments = Lists.newArrayList(teP, teDouble, teInt, teH); + teFun.setArgumentTypeList(arguments); + assertEquals(4, teFun.sizeArgumentTypes()); + assertEquals(teP, teFun.getArgumentType(0)); + assertEquals(teDouble, teFun.getArgumentType(1)); + assertEquals(teInt, teFun.getArgumentType(2)); + assertEquals(teH, teFun.getArgumentType(3)); + + //sortArgumentTypes + teFun.sortArgumentTypes((arg1, arg2) -> arg1.hashCode() + arg2.hashCode()); + assertEquals(4, teFun.sizeArgumentTypes()); + + //addAllArgumentTypes + teFun.setArgumentTypeList(Lists.newArrayList()); + assertTrue(teFun.isEmptyArgumentTypes()); + arguments = Lists.newArrayList(teP, teDouble, teInt, teH); + teFun.addAllArgumentTypes(arguments); + assertEquals(4, teFun.getArgumentTypeList().size()); + assertEquals(teP, teFun.getArgumentType(0)); + assertEquals(teDouble, teFun.getArgumentType(1)); + assertEquals(teInt, teFun.getArgumentType(2)); + assertEquals(teH, teFun.getArgumentType(3)); + + //retainAllArgumentTypes + subList = Lists.newArrayList(teP, teH); + teFun.retainAllArgumentTypes(subList); + assertEquals(2, teFun.sizeArgumentTypes()); + assertEquals(teP, teFun.getArgumentType(0)); + assertEquals(teH, teFun.getArgumentType(1)); + + //removeAllArgumentTypes + teFun.removeAllArgumentTypes(subList); + assertTrue(teFun.isEmptyArgumentTypes()); + + //replaceAllArgumentTypes + arguments = Lists.newArrayList(teP, teDouble, teInt, teH); + teFun.setArgumentTypeList(arguments); + teFun.replaceAllArgumentTypes(SymTypeExpression::deepClone); + assertEquals(4, teFun.sizeArgumentTypes()); + assertTrue(teFun.equalsArgumentTypeTypes(arguments)); + + //removeIfArgument + teFun.removeIfArgumentType(SymTypeExpression::isPrimitive); + assertEquals(2, teFun.sizeArgumentTypes()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/check/SynthesizeSymTypeFromMCArrayTypesTest.java b/monticore-grammar/src/test/java/de/monticore/types/check/SynthesizeSymTypeFromMCArrayTypesTest.java new file mode 100644 index 0000000000..0631eea4cd --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/check/SynthesizeSymTypeFromMCArrayTypesTest.java @@ -0,0 +1,162 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.expressions.combineexpressionswithliterals.CombineExpressionsWithLiteralsMill; +import de.monticore.expressions.combineexpressionswithliterals._parser.CombineExpressionsWithLiteralsParser; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.CombineExpressionsWithLiteralsSymbols2Json; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.ICombineExpressionsWithLiteralsArtifactScope; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.ICombineExpressionsWithLiteralsGlobalScope; +import de.monticore.expressions.combineexpressionswithliterals._visitor.CombineExpressionsWithLiteralsTraverser; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.types.mcarraytypes.MCArrayTypesMill; +import de.monticore.types.mcarraytypes._ast.ASTMCArrayType; +import de.monticore.types.mcarraytypes._visitor.MCArrayTypesTraverser; +import de.monticore.types.mcarraytypestest._parser.MCArrayTypesTestParser; +import de.monticore.types.mcbasictypes.MCBasicTypesMill; +import de.monticore.types.mcbasictypes._ast.ASTMCReturnType; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.mcbasictypes._ast.ASTMCVoidType; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; + +import static org.junit.Assert.assertEquals; + +public class SynthesizeSymTypeFromMCArrayTypesTest { + + /** + * Focus: Interplay between TypeCheck and the assisting visitors on the + * Basic configuration, i.e. + * i.e. for + * expressions/ExpressionsBasis.mc4 + * literals/MCLiteralsBasis.mc4 + * types/MCBasicTypes.mc4 + * types/MCArrayTypes.mc4 + */ + + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + CombineExpressionsWithLiteralsMill.reset(); + CombineExpressionsWithLiteralsMill.init(); + BasicSymbolsMill.initializePrimitives(); + + ICombineExpressionsWithLiteralsGlobalScope gs = CombineExpressionsWithLiteralsMill.globalScope(); + gs.add(DefsTypeBasic.type("A")); + gs.add(DefsTypeBasic.type("Person")); + + CombineExpressionsWithLiteralsSymbols2Json symbols2Json = new CombineExpressionsWithLiteralsSymbols2Json(); + ICombineExpressionsWithLiteralsArtifactScope as = symbols2Json.load("src/test/resources/de/monticore/types/check/Persondex.cesym"); + as.setEnclosingScope(gs); + } + + // Parer used for convenience: + CombineExpressionsWithLiteralsParser parser = new CombineExpressionsWithLiteralsParser(); + // This is the TypeChecker under Test: + TypeCalculator tc = new TypeCalculator(new FullSynthesizeFromMCArrayTypes(),null); + + FlatExpressionScopeSetter scopeSetter; + CombineExpressionsWithLiteralsTraverser traverser; + + @Before + public void initScope(){ + scopeSetter = new FlatExpressionScopeSetter(CombineExpressionsWithLiteralsMill.globalScope()); + traverser = CombineExpressionsWithLiteralsMill.traverser(); + traverser.add4MCBasicTypes(scopeSetter); + traverser.add4MCArrayTypes(scopeSetter); + } + + // ------------------------------------------------------ Tests for Function 1, 1b, 1c + + @Test + public void symTypeFromAST_Test1() throws IOException { + String s = "double"; + ASTMCType asttype = parser.parse_StringMCType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFromAST_Test2() throws IOException { + String s = "int"; + ASTMCType asttype = parser.parse_StringMCType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFromAST_Test3() throws IOException { + String s = "A"; + ASTMCType asttype = parser.parse_StringMCType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFromAST_Test4() throws IOException { + String s = "Person"; + ASTMCType asttype = parser.parse_StringMCType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFromAST_Test5() throws IOException { + String s = "de.x.Person"; + ASTMCType asttype = parser.parse_StringMCType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFromAST_VoidTest() throws IOException { + ASTMCVoidType v = MCBasicTypesMill.mCVoidTypeBuilder().build(); + assertEquals("void", tc.symTypeFromAST(v).printFullName()); + } + + @Test + public void symTypeFromAST_ReturnTest() throws IOException { + ASTMCVoidType v = MCBasicTypesMill.mCVoidTypeBuilder().build(); + ASTMCReturnType r = MCBasicTypesMill.mCReturnTypeBuilder().setMCVoidType(v).build(); + assertEquals("void", tc.symTypeFromAST(r).printFullName()); + } + + @Test + public void symTypeFromAST_ReturnTest2() throws IOException { + // im Prinzip dassselbe via Parser: + ASTMCReturnType r = parser.parse_StringMCReturnType("void").get(); + assertEquals("void", tc.symTypeFromAST(r).printFullName()); + } + + @Test + public void symTypeFromAST_ReturnTest3() throws IOException { + // und nochmal einen normalen Typ: + String s = "Person"; + ASTMCReturnType r = parser.parse_StringMCReturnType(s).get(); + r.accept(traverser); + assertEquals(s, tc.symTypeFromAST(r).printFullName()); + } + + @Test + public void symTypeFrom_AST_ArrayTest() throws IOException { + ASTMCType prim = parser.parse_StringMCType("int").get(); + ASTMCArrayType asttype = MCArrayTypesMill.mCArrayTypeBuilder().setMCType(prim).setDimensions(2).build(); + asttype.accept(traverser); + assertEquals("int[][]", tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFrom_AST_ArrayTest2() throws IOException { + ASTMCType person = parser.parse_StringMCType("Person").get(); + ASTMCArrayType asttype = MCArrayTypesMill.mCArrayTypeBuilder().setMCType(person).setDimensions(1).build(); + asttype.accept(traverser); + assertEquals("Person[]", tc.symTypeFromAST(asttype).printFullName()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/check/SynthesizeSymTypeFromMCBasicTypesTest.java b/monticore-grammar/src/test/java/de/monticore/types/check/SynthesizeSymTypeFromMCBasicTypesTest.java new file mode 100644 index 0000000000..30ee8aaf19 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/check/SynthesizeSymTypeFromMCBasicTypesTest.java @@ -0,0 +1,140 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.expressions.combineexpressionswithliterals.CombineExpressionsWithLiteralsMill; +import de.monticore.expressions.combineexpressionswithliterals._parser.CombineExpressionsWithLiteralsParser; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.*; +import de.monticore.expressions.combineexpressionswithliterals._visitor.CombineExpressionsWithLiteralsTraverser; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.types.mcbasictypes._ast.ASTMCReturnType; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.mcbasictypes._ast.ASTMCVoidType; +import de.monticore.types.mcbasictypes.MCBasicTypesMill; +import de.monticore.types.mcbasictypes._visitor.MCBasicTypesTraverser; +import de.monticore.types.mcbasictypestest._parser.MCBasicTypesTestParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; + +import static org.junit.Assert.assertEquals; + +public class SynthesizeSymTypeFromMCBasicTypesTest { + + /** + * Focus: Interplay between TypeCheck and the assisting visitors on the + * Basic configuration, i.e. + * i.e. for + * expressions/ExpressionsBasis.mc4 + * literals/MCLiteralsBasis.mc4 + * types/MCBasicTypes.mc4 + */ + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + + CombineExpressionsWithLiteralsMill.reset(); + CombineExpressionsWithLiteralsMill.init(); + BasicSymbolsMill.initializePrimitives(); + + ICombineExpressionsWithLiteralsGlobalScope gs = CombineExpressionsWithLiteralsMill.globalScope(); + gs.add(DefsTypeBasic.type("A")); + gs.add(DefsTypeBasic.type("Person")); + + CombineExpressionsWithLiteralsSymbols2Json symbols2Json = new CombineExpressionsWithLiteralsSymbols2Json(); + ICombineExpressionsWithLiteralsArtifactScope as = symbols2Json.load("src/test/resources/de/monticore/types/check/Persondex.cesym"); + as.setEnclosingScope(gs); + } + + // Parer used for convenience: + CombineExpressionsWithLiteralsParser parser = new CombineExpressionsWithLiteralsParser(); + // This is the TypeChecker under Test: + TypeCalculator tc = new TypeCalculator(new FullSynthesizeFromMCBasicTypes(),null); + + FlatExpressionScopeSetter scopeSetter; + CombineExpressionsWithLiteralsTraverser traverser; + + @Before + public void initScope(){ + scopeSetter = new FlatExpressionScopeSetter(CombineExpressionsWithLiteralsMill.globalScope()); + traverser = CombineExpressionsWithLiteralsMill.traverser(); + traverser.add4MCBasicTypes(scopeSetter); + } + + // ------------------------------------------------------ Tests for Function 1, 1b, 1c + + @Test + public void symTypeFromAST_Test1() throws IOException { + String s = "double"; + ASTMCType asttype = parser.parse_StringMCType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFromAST_Test2() throws IOException { + String s = "int"; + ASTMCType asttype = parser.parse_StringMCType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFromAST_Test3() throws IOException { + String s = "A"; + ASTMCType asttype = parser.parse_StringMCType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFromAST_Test4() throws IOException { + String s = "Person"; + ASTMCType asttype = parser.parse_StringMCType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFromAST_Test5() throws IOException { + String s = "de.x.Person"; + ASTMCType asttype = parser.parse_StringMCType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFromAST_VoidTest() throws IOException { + ASTMCVoidType v = MCBasicTypesMill.mCVoidTypeBuilder().build(); + assertEquals("void", tc.symTypeFromAST(v).printFullName()); + } + + @Test + public void symTypeFromAST_ReturnTest() throws IOException { + ASTMCVoidType v = MCBasicTypesMill.mCVoidTypeBuilder().build(); + ASTMCReturnType r = MCBasicTypesMill.mCReturnTypeBuilder().setMCVoidType(v).build(); + assertEquals("void", tc.symTypeFromAST(r).printFullName()); + } + + @Test + public void symTypeFromAST_ReturnTest2() throws IOException { + // im Prinzip dassselbe via Parser: + ASTMCReturnType r = parser.parse_StringMCReturnType("void").get(); + assertEquals("void", tc.symTypeFromAST(r).printFullName()); + } + + @Test + public void symTypeFromAST_ReturnTest3() throws IOException { + // und nochmal einen normalen Typ: + String s = "Person"; + ASTMCReturnType r = parser.parse_StringMCReturnType(s).get(); + r.accept(traverser); + assertEquals(s, tc.symTypeFromAST(r).printFullName()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/check/SynthesizeSymTypeFromMCCollectionTypesTest.java b/monticore-grammar/src/test/java/de/monticore/types/check/SynthesizeSymTypeFromMCCollectionTypesTest.java new file mode 100644 index 0000000000..4629033fe3 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/check/SynthesizeSymTypeFromMCCollectionTypesTest.java @@ -0,0 +1,241 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.expressions.combineexpressionswithliterals.CombineExpressionsWithLiteralsMill; +import de.monticore.expressions.combineexpressionswithliterals._parser.CombineExpressionsWithLiteralsParser; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.CombineExpressionsWithLiteralsSymbols2Json; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.ICombineExpressionsWithLiteralsArtifactScope; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.ICombineExpressionsWithLiteralsGlobalScope; +import de.monticore.expressions.combineexpressionswithliterals._visitor.CombineExpressionsWithLiteralsTraverser; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbolSurrogate; +import de.monticore.symbols.basicsymbols._symboltable.TypeVarSymbol; +import de.monticore.symbols.oosymbols._symboltable.OOTypeSymbolSurrogate; +import de.monticore.types.mcbasictypes._ast.ASTMCReturnType; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.mcbasictypes._ast.ASTMCVoidType; +import de.monticore.types.mcbasictypes.MCBasicTypesMill; +import de.monticore.types.mccollectiontypes._ast.ASTMCListType; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import static org.junit.Assert.*; + +public class SynthesizeSymTypeFromMCCollectionTypesTest { + + /** + * Focus: Interplay between TypeCheck and the assisting visitors on the + * extended configuration, + * i.e. for + * types/MCCollectionTypes.mc4 + */ + + + // Parer used for convenience: + CombineExpressionsWithLiteralsParser parser = new CombineExpressionsWithLiteralsParser(); + + // This is Visitor for Collection types under test: + FullSynthesizeFromMCCollectionTypes synt = new FullSynthesizeFromMCCollectionTypes(); + + // other arguments not used (and therefore deliberately null) + + // This is the TypeChecker under Test: + TypeCalculator tc = new TypeCalculator(synt,null); + + FlatExpressionScopeSetter scopeSetter; + CombineExpressionsWithLiteralsTraverser traverser; + + @Before + public void init(){ + + LogStub.init(); + Log.enableFailQuick(false); + CombineExpressionsWithLiteralsMill.reset(); + CombineExpressionsWithLiteralsMill.init(); + BasicSymbolsMill.initializePrimitives(); + + scopeSetter = new FlatExpressionScopeSetter(CombineExpressionsWithLiteralsMill.globalScope()); + traverser = CombineExpressionsWithLiteralsMill.traverser(); + traverser.add4MCCollectionTypes(scopeSetter); + traverser.add4MCBasicTypes(scopeSetter); + + ICombineExpressionsWithLiteralsGlobalScope gs = CombineExpressionsWithLiteralsMill.globalScope(); + gs.add(DefsTypeBasic.type("A")); + gs.add(DefsTypeBasic.type("Person")); + gs.add(DefsTypeBasic.type("Auto")); + gs.add(buildGeneric("Map", "K", "V")); + gs.add(buildGeneric("List", "T")); + gs.add(buildGeneric("Set", "T")); + gs.add(buildGeneric("Optional", "T")); + + CombineExpressionsWithLiteralsSymbols2Json symbols2Json = new CombineExpressionsWithLiteralsSymbols2Json(); + ICombineExpressionsWithLiteralsArtifactScope as = symbols2Json.load("src/test/resources/de/monticore/types/check/Persondex.cesym"); + as.setEnclosingScope(gs); + + ICombineExpressionsWithLiteralsArtifactScope as2 = symbols2Json.load("src/test/resources/de/monticore/types/check/Personaz.cesym"); + as2.setEnclosingScope(gs); + } + + protected static TypeSymbol buildGeneric(String rawName, String... typeParamNames) { + List typeParams = Arrays.stream(typeParamNames) + .map(DefsTypeBasic::typeVariable) + .collect(Collectors.toList()); + + return DefsTypeBasic.type(rawName, new ArrayList<>(), typeParams); + } + + // ------------------------------------------------------ Tests for Function 1, 1b, 1c + + // reuse some of the tests from MCBasicTypes (to check conformity) + + @Test + public void symTypeFromAST_Test1() throws IOException { + String s = "double"; + ASTMCType asttype = parser.parse_StringMCType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFromAST_Test4() throws IOException { + String s = "Person"; + ASTMCType asttype = parser.parse_StringMCType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFromAST_Test5() throws IOException { + String s = "de.x.Person"; + ASTMCType asttype = parser.parse_StringMCType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFromAST_ReturnTest() throws IOException { + ASTMCVoidType v = MCBasicTypesMill.mCVoidTypeBuilder().build(); + ASTMCReturnType r = MCBasicTypesMill.mCReturnTypeBuilder() + .setMCVoidType(v).build(); + assertEquals("void", tc.symTypeFromAST(r).printFullName()); + } + + @Test + public void symTypeFromAST_ReturnTest3() throws IOException { + // und nochmal einen normalen Typ: + String s = "Person"; + ASTMCReturnType r = parser.parse_StringMCReturnType(s).get(); + r.accept(traverser); + assertEquals(s, tc.symTypeFromAST(r).printFullName()); + } + + // new forms of Types coming from MCCollectionType + + @Test + public void symTypeFromAST_TestListQual() throws IOException { + // Given + String s = "List"; + ASTMCListType asttype = parser.parse_StringMCListType(s).get(); + + // When + asttype.accept(traverser); + SymTypeExpression result = tc.symTypeFromAST(asttype); + + // Then + assertEquals(s, result.printFullName()); + assertTrue(result instanceof SymTypeOfGenerics); + assertFalse(result.getTypeInfo() instanceof TypeSymbolSurrogate); + assertFalse(result.getTypeInfo() instanceof OOTypeSymbolSurrogate); + assertFalse(((SymTypeOfGenerics) result).getArgument(0).getTypeInfo() instanceof TypeSymbolSurrogate); + assertFalse(((SymTypeOfGenerics) result).getArgument(0).getTypeInfo() instanceof OOTypeSymbolSurrogate); + + } + + @Test + public void symTypeFromAST_TestListQual2() throws IOException { + // Given + String s = "Set"; + ASTMCType asttype = parser.parse_StringMCType(s).get(); + + // When + asttype.accept(traverser); + SymTypeExpression result = tc.symTypeFromAST(asttype); + + // Then + assertEquals(s, result.printFullName()); + assertTrue(result instanceof SymTypeOfGenerics); + assertFalse(result.getTypeInfo() instanceof TypeSymbolSurrogate); + assertFalse(result.getTypeInfo() instanceof OOTypeSymbolSurrogate); + assertFalse(((SymTypeOfGenerics) result).getArgument(0).getTypeInfo() instanceof TypeSymbolSurrogate); + assertFalse(((SymTypeOfGenerics) result).getArgument(0).getTypeInfo() instanceof OOTypeSymbolSurrogate); + } + + @Test + public void symTypeFromAST_TestListQual3() throws IOException { + // Given + String s = "Map"; + ASTMCType asttype = parser.parse_StringMCType(s).get(); + + // When + asttype.accept(traverser); + SymTypeExpression result = tc.symTypeFromAST(asttype); + + // Then + assertEquals(s, result.printFullName()); + assertTrue(result instanceof SymTypeOfGenerics); + assertFalse(result.getTypeInfo() instanceof TypeSymbolSurrogate); + assertFalse(result.getTypeInfo() instanceof OOTypeSymbolSurrogate); + assertFalse(((SymTypeOfGenerics) result).getArgument(0).getTypeInfo() instanceof TypeSymbolSurrogate); + assertFalse(((SymTypeOfGenerics) result).getArgument(0).getTypeInfo() instanceof TypeSymbolSurrogate); + assertFalse(((SymTypeOfGenerics) result).getArgument(1).getTypeInfo() instanceof OOTypeSymbolSurrogate); + assertFalse(((SymTypeOfGenerics) result).getArgument(1).getTypeInfo() instanceof OOTypeSymbolSurrogate); + } + + @Test + public void symTypeFromAST_TestListQual4() throws IOException { + // Given + String s = "Set"; + ASTMCType asttype = parser.parse_StringMCType(s).get(); + + // When + asttype.accept(traverser); + SymTypeExpression result = tc.symTypeFromAST(asttype); + + // Then + assertEquals(s, result.printFullName()); + assertTrue(result instanceof SymTypeOfGenerics); + assertFalse(result.getTypeInfo() instanceof TypeSymbolSurrogate); + assertFalse(result.getTypeInfo() instanceof OOTypeSymbolSurrogate); + assertFalse(((SymTypeOfGenerics) result).getArgument(0).getTypeInfo() instanceof TypeSymbolSurrogate); + assertFalse(((SymTypeOfGenerics) result).getArgument(0).getTypeInfo() instanceof OOTypeSymbolSurrogate); + } + + @Test + public void symTypeFromAST_TestListQual5() throws IOException { + // Given + String s = "Optional"; + ASTMCType asttype = parser.parse_StringMCType(s).get(); + + // When + asttype.accept(traverser); + SymTypeExpression result = tc.symTypeFromAST(asttype); + + // Then + assertEquals(s, result.printFullName()); + assertTrue(result instanceof SymTypeOfGenerics); + assertFalse(result.getTypeInfo() instanceof TypeSymbolSurrogate); + assertFalse(result.getTypeInfo() instanceof OOTypeSymbolSurrogate); + assertFalse(((SymTypeOfGenerics) result).getArgument(0).getTypeInfo() instanceof TypeSymbolSurrogate); + assertFalse(((SymTypeOfGenerics) result).getArgument(0).getTypeInfo() instanceof OOTypeSymbolSurrogate); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/check/SynthesizeSymTypeFromMCFullGenericTypesTest.java b/monticore-grammar/src/test/java/de/monticore/types/check/SynthesizeSymTypeFromMCFullGenericTypesTest.java new file mode 100644 index 0000000000..e6a396f509 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/check/SynthesizeSymTypeFromMCFullGenericTypesTest.java @@ -0,0 +1,250 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import com.google.common.collect.Lists; +import de.monticore.expressions.combineexpressionswithliterals.CombineExpressionsWithLiteralsMill; +import de.monticore.expressions.combineexpressionswithliterals._parser.CombineExpressionsWithLiteralsParser; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.CombineExpressionsWithLiteralsSymbols2Json; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.ICombineExpressionsWithLiteralsArtifactScope; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.ICombineExpressionsWithLiteralsGlobalScope; +import de.monticore.expressions.combineexpressionswithliterals._visitor.CombineExpressionsWithLiteralsTraverser; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.oosymbols._symboltable.OOTypeSymbol; +import de.monticore.types.mcbasictypes.MCBasicTypesMill; +import de.monticore.types.mcbasictypes._ast.ASTMCReturnType; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.mcbasictypes._ast.ASTMCVoidType; +import de.monticore.types.mccollectiontypes._ast.ASTMCListType; +import de.monticore.types.mcfullgenerictypes.MCFullGenericTypesMill; +import de.monticore.types.mcfullgenerictypes._visitor.MCFullGenericTypesTraverser; +import de.monticore.types.mcfullgenerictypestest._parser.MCFullGenericTypesTestParser; +import de.monticore.types.mcsimplegenerictypes._ast.ASTMCBasicGenericType; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; + +import static org.junit.Assert.assertEquals; + +public class SynthesizeSymTypeFromMCFullGenericTypesTest { + + /** + * Focus: Interplay between TypeCheck and the assisting visitors on the + * extended configuration, + * i.e. for + * types/MCFullGenericTypes.mc4 + */ + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + CombineExpressionsWithLiteralsMill.reset(); + CombineExpressionsWithLiteralsMill.init(); + BasicSymbolsMill.initializePrimitives(); + + ICombineExpressionsWithLiteralsGlobalScope gs = CombineExpressionsWithLiteralsMill.globalScope(); + OOTypeSymbol a2 = DefsTypeBasic.type("A2", Lists.newArrayList(), Lists.newArrayList(), Lists.newArrayList(), Lists.newArrayList(DefsTypeBasic.typeVariable("T")), gs); + gs.add(a2); + a2.getSpannedScope().add(DefsTypeBasic.type("B", Lists.newArrayList(), Lists.newArrayList(), Lists.newArrayList(), Lists.newArrayList(), a2.getSpannedScope())); + gs.add(DefsTypeBasic.type("A")); + gs.add(DefsTypeBasic.type("Person")); + gs.add(DefsTypeBasic.type("Auto")); + gs.add(DefsTypeBasic.type("Map")); + gs.add(DefsTypeBasic.type("List")); + gs.add(DefsTypeBasic.type("Set")); + gs.add(DefsTypeBasic.type("Iterator")); + gs.add(DefsTypeBasic.type("Collection")); + gs.add(DefsTypeBasic.type("Void")); + + CombineExpressionsWithLiteralsSymbols2Json symbols2Json = new CombineExpressionsWithLiteralsSymbols2Json(); + ICombineExpressionsWithLiteralsArtifactScope as = symbols2Json.load("src/test/resources/de/monticore/types/check/Persondex.cesym"); + as.setEnclosingScope(gs); + + ICombineExpressionsWithLiteralsArtifactScope as2 = symbols2Json.load("src/test/resources/de/monticore/types/check/Personaz.cesym"); + as2.setEnclosingScope(gs); + + ICombineExpressionsWithLiteralsArtifactScope as3 = symbols2Json.load("src/test/resources/de/monticore/types/check/Iterator.cesym"); + as3.setEnclosingScope(gs); + + ICombineExpressionsWithLiteralsArtifactScope as4 = symbols2Json.load("src/test/resources/de/monticore/types/check/String.cesym"); + as4.setEnclosingScope(gs); + + ICombineExpressionsWithLiteralsArtifactScope as5 = symbols2Json.load("src/test/resources/de/monticore/types/check/Personjl.cesym"); + as5.setEnclosingScope(gs); + } + + // Parer used for convenience: + CombineExpressionsWithLiteralsParser parser = new CombineExpressionsWithLiteralsParser(); + + // This is Visitor for SimpleGeneric types under test: + ISynthesize synt = new FullSynthesizeFromMCFullGenericTypes(); + + // other arguments not used (and therefore deliberately null) + + // This is the TypeChecker under Test: + TypeCalculator tc = new TypeCalculator(synt,null); + + FlatExpressionScopeSetter scopeSetter; + CombineExpressionsWithLiteralsTraverser traverser; + + @Before + public void initScope(){ + scopeSetter = new FlatExpressionScopeSetter(CombineExpressionsWithLiteralsMill.globalScope()); + traverser = CombineExpressionsWithLiteralsMill.traverser(); + traverser.add4MCFullGenericTypes(scopeSetter); + traverser.add4MCSimpleGenericTypes(scopeSetter); + traverser.add4MCCollectionTypes(scopeSetter); + traverser.add4MCBasicTypes(scopeSetter); + } + + // ------------------------------------------------------ Tests for Function 1, 1b, 1c + + // reuse some of the tests from MCBasicTypes (to check conformity) + + @Test + public void symTypeFromAST_Test1() throws IOException { + String s = "double"; + parser = new CombineExpressionsWithLiteralsParser(); + ASTMCType asttype = parser.parse_StringMCType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFromAST_Test4() throws IOException { + String s = "Person"; + ASTMCType asttype = parser.parse_StringMCType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFromAST_Test5() throws IOException { + String s = "de.x.Person"; + ASTMCType asttype = parser.parse_StringMCType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFromAST_ReturnTest() throws IOException { + ASTMCVoidType v = MCBasicTypesMill.mCVoidTypeBuilder().build(); + ASTMCReturnType r = MCBasicTypesMill.mCReturnTypeBuilder() + .setMCVoidType(v).build(); + assertEquals("void", tc.symTypeFromAST(r).printFullName()); + } + + @Test + public void symTypeFromAST_ReturnTest3() throws IOException { + // und nochmal einen normalen Typ: + String s = "Person"; + ASTMCReturnType r = parser.parse_StringMCReturnType(s).get(); + r.accept(traverser); + assertEquals(s, tc.symTypeFromAST(r).printFullName()); + } + + // reuse some of the tests from MCCollectionType + + @Test + public void symTypeFromAST_TestListQual() throws IOException { + String s = "List"; + ASTMCListType asttype = parser.parse_StringMCListType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFromAST_TestListQual2() throws IOException { + String s = "Set"; + ASTMCType asttype = parser.parse_StringMCType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFromAST_TestListQual3() throws IOException { + String s = "Map"; + ASTMCType asttype = parser.parse_StringMCType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFromAST_TestListQual4() throws IOException { + String s = "Set"; + ASTMCType asttype = parser.parse_StringMCType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + //new tests coming from MCSimpleGenericTypes + + @Test + public void symTypeFromAST_TestGeneric() throws IOException { + String s = "Iterator"; + ASTMCType asttype = parser.parse_StringMCType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFromAST_TestGeneric2() throws IOException { + String s = "java.util.Iterator"; + ASTMCBasicGenericType asttype = parser.parse_StringMCBasicGenericType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFromAST_TestGeneric3() throws IOException { + String s = "Collection"; + ASTMCType asttype = parser.parse_StringMCType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFromAST_TestGeneric4() throws IOException { + String s = "java.util.Iterator"; + ASTMCBasicGenericType asttype = parser.parse_StringMCBasicGenericType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFromAST_TestGeneric5() throws IOException { + String s = "java.util.Iterator"; + ASTMCBasicGenericType asttype = parser.parse_StringMCBasicGenericType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFromAST_TestGeneric6() throws IOException { + String s = "java.util.Iterator>>"; + ASTMCBasicGenericType asttype = parser.parse_StringMCBasicGenericType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFromAST_TestGeneric7() throws IOException { + String s = "java.util.Iterator>>"; + ASTMCBasicGenericType asttype = parser.parse_StringMCBasicGenericType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFromAST_TestGeneric8() throws IOException { + String s = "List"; + ASTMCBasicGenericType asttype = parser.parse_StringMCBasicGenericType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/check/SynthesizeSymTypeFromMCSimpleGenericTypesTest.java b/monticore-grammar/src/test/java/de/monticore/types/check/SynthesizeSymTypeFromMCSimpleGenericTypesTest.java new file mode 100644 index 0000000000..115f5d0c21 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/check/SynthesizeSymTypeFromMCSimpleGenericTypesTest.java @@ -0,0 +1,236 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.expressions.combineexpressionswithliterals.CombineExpressionsWithLiteralsMill; +import de.monticore.expressions.combineexpressionswithliterals._parser.CombineExpressionsWithLiteralsParser; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.CombineExpressionsWithLiteralsSymbols2Json; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.ICombineExpressionsWithLiteralsArtifactScope; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.ICombineExpressionsWithLiteralsGlobalScope; +import de.monticore.expressions.combineexpressionswithliterals._visitor.CombineExpressionsWithLiteralsTraverser; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.types.mcbasictypes._ast.ASTMCReturnType; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.mcbasictypes._ast.ASTMCVoidType; +import de.monticore.types.mcbasictypes.MCBasicTypesMill; +import de.monticore.types.mccollectiontypes._ast.ASTMCListType; +import de.monticore.types.mcsimplegenerictypes.MCSimpleGenericTypesMill; +import de.monticore.types.mcsimplegenerictypes._ast.ASTMCBasicGenericType; +import de.monticore.types.mcsimplegenerictypes._visitor.MCSimpleGenericTypesTraverser; +import de.monticore.types.mcsimplegenerictypestest._parser.MCSimpleGenericTypesTestParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; + +import static org.junit.Assert.assertEquals; + +public class SynthesizeSymTypeFromMCSimpleGenericTypesTest { + + /** + * Focus: Interplay between TypeCheck and the assisting visitors on the + * extended configuration, + * i.e. for + * types/MCSimpleGenericTypes.mc4 + */ + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + CombineExpressionsWithLiteralsMill.reset(); + CombineExpressionsWithLiteralsMill.init(); + BasicSymbolsMill.initializePrimitives(); + + ICombineExpressionsWithLiteralsGlobalScope gs = CombineExpressionsWithLiteralsMill.globalScope(); + gs.add(DefsTypeBasic.type("A")); + gs.add(DefsTypeBasic.type("Person")); + gs.add(DefsTypeBasic.type("Auto")); + gs.add(DefsTypeBasic.type("Map")); + gs.add(DefsTypeBasic.type("List")); + gs.add(DefsTypeBasic.type("Set")); + gs.add(DefsTypeBasic.type("Iterator")); + gs.add(DefsTypeBasic.type("Collection")); + gs.add(DefsTypeBasic.type("Void")); + + CombineExpressionsWithLiteralsSymbols2Json symbols2Json = new CombineExpressionsWithLiteralsSymbols2Json(); + ICombineExpressionsWithLiteralsArtifactScope as = symbols2Json.load("src/test/resources/de/monticore/types/check/Persondex.cesym"); + as.setEnclosingScope(gs); + + ICombineExpressionsWithLiteralsArtifactScope as2 = symbols2Json.load("src/test/resources/de/monticore/types/check/Personaz.cesym"); + as2.setEnclosingScope(gs); + + ICombineExpressionsWithLiteralsArtifactScope as3 = symbols2Json.load("src/test/resources/de/monticore/types/check/Iterator.cesym"); + as3.setEnclosingScope(gs); + + ICombineExpressionsWithLiteralsArtifactScope as4 = symbols2Json.load("src/test/resources/de/monticore/types/check/String.cesym"); + as4.setEnclosingScope(gs); + + ICombineExpressionsWithLiteralsArtifactScope as5 = symbols2Json.load("src/test/resources/de/monticore/types/check/Personjl.cesym"); + as5.setEnclosingScope(gs); + } + + // Parer used for convenience: + CombineExpressionsWithLiteralsParser parser = new CombineExpressionsWithLiteralsParser(); + + // This is Visitor for SimpleGeneric types under test: + ISynthesize synt = new FullSynthesizeFromMCSimpleGenericTypes(); + + // other arguments not used (and therefore deliberately null) + + // This is the TypeChecker under Test: + TypeCalculator tc = new TypeCalculator(synt,null); + + FlatExpressionScopeSetter scopeSetter; + CombineExpressionsWithLiteralsTraverser traverser; + + @Before + public void initScope(){ + scopeSetter = new FlatExpressionScopeSetter(CombineExpressionsWithLiteralsMill.globalScope()); + traverser = CombineExpressionsWithLiteralsMill.traverser(); + traverser.add4MCSimpleGenericTypes(scopeSetter); + traverser.add4MCCollectionTypes(scopeSetter); + traverser.add4MCBasicTypes(scopeSetter); + } + + // ------------------------------------------------------ Tests for Function 1, 1b, 1c + + // reuse some of the tests from MCBasicTypes (to check conformity) + + @Test + public void symTypeFromAST_Test1() throws IOException { + String s = "double"; + parser = new CombineExpressionsWithLiteralsParser(); + ASTMCType asttype = parser.parse_StringMCType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFromAST_Test4() throws IOException { + String s = "Person"; + ASTMCType asttype = parser.parse_StringMCType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFromAST_Test5() throws IOException { + String s = "de.x.Person"; + ASTMCType asttype = parser.parse_StringMCType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFromAST_ReturnTest() throws IOException { + ASTMCVoidType v = MCBasicTypesMill.mCVoidTypeBuilder().build(); + ASTMCReturnType r = MCBasicTypesMill.mCReturnTypeBuilder() + .setMCVoidType(v).build(); + assertEquals("void", tc.symTypeFromAST(r).printFullName()); + } + + @Test + public void symTypeFromAST_ReturnTest3() throws IOException { + // und nochmal einen normalen Typ: + String s = "Person"; + ASTMCReturnType r = parser.parse_StringMCReturnType(s).get(); + r.accept(traverser); + assertEquals(s, tc.symTypeFromAST(r).printFullName()); + } + + // reuse some of the tests from MCCollectionType + + @Test + public void symTypeFromAST_TestListQual() throws IOException { + String s = "List"; + ASTMCListType asttype = parser.parse_StringMCListType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFromAST_TestListQual2() throws IOException { + String s = "Set"; + ASTMCType asttype = parser.parse_StringMCType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFromAST_TestListQual3() throws IOException { + String s = "Map"; + ASTMCType asttype = parser.parse_StringMCType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFromAST_TestListQual4() throws IOException { + String s = "Set"; + ASTMCType asttype = parser.parse_StringMCType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + //new tests coming from MCSimpleGenericTypes + + @Test + public void symTypeFromAST_TestGeneric() throws IOException { + String s = "Iterator"; + ASTMCType asttype = parser.parse_StringMCType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFromAST_TestGeneric2() throws IOException { + String s = "java.util.Iterator"; + ASTMCBasicGenericType asttype = parser.parse_StringMCBasicGenericType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFromAST_TestGeneric3() throws IOException { + String s = "Collection"; + ASTMCType asttype = parser.parse_StringMCType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFromAST_TestGeneric4() throws IOException { + String s = "java.util.Iterator"; + ASTMCBasicGenericType asttype = parser.parse_StringMCBasicGenericType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFromAST_TestGeneric5() throws IOException { + String s = "java.util.Iterator"; + ASTMCBasicGenericType asttype = parser.parse_StringMCBasicGenericType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFromAST_TestGeneric6() throws IOException { + String s = "java.util.Iterator>>"; + ASTMCBasicGenericType asttype = parser.parse_StringMCBasicGenericType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + + @Test + public void symTypeFromAST_TestGeneric7() throws IOException { + String s = "java.util.Iterator>>"; + ASTMCBasicGenericType asttype = parser.parse_StringMCBasicGenericType(s).get(); + asttype.accept(traverser); + assertEquals(s, tc.symTypeFromAST(asttype).printFullName()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/check/SynthesizeSymTypeFromMCcFunctionTypesTest.java b/monticore-grammar/src/test/java/de/monticore/types/check/SynthesizeSymTypeFromMCcFunctionTypesTest.java new file mode 100644 index 0000000000..a704a56c20 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/check/SynthesizeSymTypeFromMCcFunctionTypesTest.java @@ -0,0 +1,113 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check; + +import de.monticore.expressions.combineexpressionswithliterals.CombineExpressionsWithLiteralsMill; +import de.monticore.expressions.combineexpressionswithliterals._visitor.CombineExpressionsWithLiteralsTraverser; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.types.mcfunctiontypes._ast.ASTMCFunctionType; +import de.monticore.types.mcfunctiontypestest._parser.MCFunctionTypesTestParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class SynthesizeSymTypeFromMCcFunctionTypesTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + CombineExpressionsWithLiteralsMill.reset(); + CombineExpressionsWithLiteralsMill.init(); + BasicSymbolsMill.initializePrimitives(); + } + + @Test + public void symTypeFromAST_TestRunnable() throws IOException { + testSynthesizePrintCompare("() -> void"); + } + + @Test + public void symTypeFromAST_TestSimpleFunction1() throws IOException { + testSynthesizePrintCompare("(int) -> int"); + } + + @Test + public void symTypeFromAST_TestSimpleFunction2() throws IOException { + testSynthesizePrintCompare("(long, int) -> int"); + } + + @Test + public void symTypeFromAST_TestEllipticFunction1() throws IOException { + testSynthesizePrintCompare("(int...) -> int"); + } + + @Test + public void symTypeFromAST_TestEllipticFunction2() throws IOException { + testSynthesizePrintCompare("(long, int...) -> void"); + } + + @Test + public void symTypeFromAST_TestHigherOrderFunction1() throws IOException { + testSynthesizePrintCompare("((int) -> void) -> () -> int"); + } + + @Test + public void symTypeFromAST_TestHigherOrderEllipticFunction() throws IOException { + testSynthesizePrintCompare("(int) -> (() -> (int, long...) -> int...) -> void"); + } + + protected ASTMCFunctionType parse(String mcTypeStr) throws IOException { + MCFunctionTypesTestParser parser = new MCFunctionTypesTestParser(); + Optional typeOpt = parser.parse_StringMCFunctionType(mcTypeStr); + assertNotNull(typeOpt); + assertTrue(typeOpt.isPresent()); + assertEquals(0, Log.getFindingsCount()); + return typeOpt.get(); + } + + protected SymTypeOfFunction synthesizeType(ASTMCFunctionType mcType) { + ISynthesize synthesize = new FullSynthesizeFromMCFunctionTypes(); + TypeCalculator tc = new TypeCalculator(synthesize, null); + + CombineExpressionsWithLiteralsTraverser traverser = CombineExpressionsWithLiteralsMill.traverser(); + FlatExpressionScopeSetter scopeSetter = new FlatExpressionScopeSetter( + CombineExpressionsWithLiteralsMill.globalScope()); + traverser.add4MCFunctionTypes(scopeSetter); + traverser.add4MCBasicTypes(scopeSetter); + mcType.accept(traverser); + + SymTypeExpression symType = tc.symTypeFromAST(mcType); + assertTrue(symType.isFunctionType()); + SymTypeOfFunction funcType = (SymTypeOfFunction) symType; + assertNotNull(funcType.getTypeInfo()); + assertEquals(SymTypeOfFunction.TYPESYMBOL_NAME, funcType.getTypeInfo().getName()); + assertNotNull(funcType.getType()); + assertFalse(funcType.getType().isObscureType()); + assertNotNull(funcType.getArgumentTypeList()); + for (SymTypeExpression argType : funcType.getArgumentTypeList()) { + assertNotNull(argType); + assertFalse(argType.isObscureType()); + } + assertTrue(mcType.getDefiningSymbol().isPresent()); + assertEquals(SymTypeOfFunction.TYPESYMBOL_NAME, mcType.getDefiningSymbol().get().getName()); + + return funcType; + } + + protected void testSynthesizePrintCompare(String mcTypeStr) throws IOException { + // The symTypeExpression is to be printed the same way as the MCType + ASTMCFunctionType mcType = parse(mcTypeStr); + SymTypeOfFunction symType = synthesizeType(mcType); + assertEquals(mcTypeStr, symType.printFullName()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/check/TypeCalculatorTest.java b/monticore-grammar/src/test/java/de/monticore/types/check/TypeCalculatorTest.java new file mode 100644 index 0000000000..ce4e1abb73 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/check/TypeCalculatorTest.java @@ -0,0 +1,372 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.types.check; + +import com.google.common.collect.Lists; +import de.monticore.expressions.combineexpressionswithliterals.CombineExpressionsWithLiteralsMill; +import de.monticore.expressions.combineexpressionswithliterals._parser.CombineExpressionsWithLiteralsParser; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.ICombineExpressionsWithLiteralsScope; +import de.monticore.expressions.combineexpressionswithliterals._visitor.CombineExpressionsWithLiteralsTraverser; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.TypeVarSymbol; +import de.monticore.symbols.oosymbols.OOSymbolsMill; +import de.monticore.symbols.oosymbols._symboltable.OOTypeSymbol; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import static de.monticore.types.check.DefsTypeBasic.*; +import static de.monticore.types.check.SymTypeExpressionFactory.createPrimitive; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Test Class for {@link TypeCalculator} + */ +public class TypeCalculatorTest { + + private ICombineExpressionsWithLiteralsScope scope; + private TypeCalculator tc = new TypeCalculator( + new FullSynthesizeFromCombineExpressionsWithLiterals(), + new FullDeriveFromCombineExpressionsWithLiterals(), + new TypeRelations() + ); + private CombineExpressionsWithLiteralsParser p = new CombineExpressionsWithLiteralsParser(); + private FlatExpressionScopeSetter flatExpressionScopeSetter; + + @Before + public void init() { + LogStub.init(); // replace log by a sideffect free variant + Log.enableFailQuick(false); + + // Setting up a Scope Infrastructure (without a global Scope) + CombineExpressionsWithLiteralsMill.reset(); + CombineExpressionsWithLiteralsMill.init(); + BasicSymbolsMill.initializePrimitives(); + DefsTypeBasic.setup(); + scope = CombineExpressionsWithLiteralsMill.scope(); + scope.setEnclosingScope(null); // No enclosing Scope: Search ending here + scope.setExportingSymbols(true); + scope.setAstNode(null); // hopefully unused + // we add a variety of TypeSymbols to the same scope (which in reality doesn't happen) + + add2scope(scope, DefsTypeBasic._array); + add2scope(scope, DefsTypeBasic._Object); + add2scope(scope, DefsTypeBasic._String); + + // some FieldSymbols (ie. Variables, Attributes) + OOTypeSymbol p = new OOTypeSymbol("Person"); + p.setEnclosingScope(scope); + add2scope(scope, p); + OOTypeSymbol s = new OOTypeSymbol("Student"); + s.setEnclosingScope(scope); + add2scope(scope,s); + s.setSuperTypesList(Lists.newArrayList(SymTypeExpressionFactory.createTypeObject("Person", scope))); + OOTypeSymbol f = new OOTypeSymbol("FirstSemesterStudent"); + f.setEnclosingScope(scope); + add2scope(scope,f); + f.setSuperTypesList(Lists.newArrayList(SymTypeExpressionFactory.createTypeObject("Student", scope))); + add2scope(scope, field("foo", _intSymType)); + add2scope(scope, field("bar2", _booleanSymType)); + add2scope(scope, field("vardouble", _doubleSymType)); + add2scope(scope, field("varchar", _charSymType)); + add2scope(scope, field("varfloat", _floatSymType)); + add2scope(scope, field("varlong", _longSymType)); + add2scope(scope, field("varint", _intSymType)); + add2scope(scope, field("varString", SymTypeExpressionFactory.createTypeObject("String", scope))); + add2scope(scope, field("person1", SymTypeExpressionFactory.createTypeObject("Person", scope))); + add2scope(scope, field("person2", SymTypeExpressionFactory.createTypeObject("Person", scope))); + add2scope(scope, field("student1", SymTypeExpressionFactory.createTypeObject("Student", scope))); + add2scope(scope, field("student2", SymTypeExpressionFactory.createTypeObject("Student", scope))); + add2scope(scope, field("firstsemester", SymTypeExpressionFactory.createTypeObject("FirstSemesterStudent", scope))); + flatExpressionScopeSetter = new FlatExpressionScopeSetter(scope); + LogStub.init(); + } + + @Test + public void testIsOfTypeForAssign() throws IOException { + //primitives + CombineExpressionsWithLiteralsTraverser traverser = getTraverser(flatExpressionScopeSetter); + ASTExpression bool1 = p.parse_StringExpression("true").get(); + bool1.accept(traverser); + ASTExpression bool2 = p.parse_StringExpression("false").get(); + bool2.accept(traverser); + ASTExpression float1 = p.parse_StringExpression("3.4f").get(); + float1.accept(traverser); + ASTExpression int1 = p.parse_StringExpression("3").get(); + int1.accept(traverser); + ASTExpression double1 = p.parse_StringExpression("3.46").get(); + double1.accept(traverser); + ASTExpression long1 = p.parse_StringExpression("5L").get(); + long1.accept(traverser); + ASTExpression char1 = p.parse_StringExpression("\'a\'").get(); + char1.accept(traverser); + + assertTrue(tc.isOfTypeForAssign(tc.typeOf(bool1), bool2)); + assertTrue(tc.isOfTypeForAssign(tc.typeOf(double1), int1)); + assertFalse(tc.isOfTypeForAssign(tc.typeOf(bool1), int1)); + assertTrue(tc.isOfTypeForAssign(tc.typeOf(float1), int1)); + assertTrue(tc.isOfTypeForAssign(tc.typeOf(long1), int1)); + assertTrue(tc.isOfTypeForAssign(tc.typeOf(char1), char1)); + assertFalse(tc.isOfTypeForAssign(tc.typeOf(char1), int1)); + assertFalse(tc.isOfTypeForAssign(tc.typeOf(double1), bool1)); + assertFalse(tc.isOfTypeForAssign(tc.typeOf(long1), float1)); + assertTrue(tc.isOfTypeForAssign(tc.typeOf(float1), int1)); + + //non-primitives + ASTExpression pers = p.parse_StringExpression("Person").get(); + pers.accept(traverser); + ASTExpression stud = p.parse_StringExpression("Student").get(); + stud.accept(traverser); + ASTExpression fstud = p.parse_StringExpression("FirstSemesterStudent").get(); + fstud.accept(traverser); + + assertTrue(tc.isOfTypeForAssign(tc.typeOf(pers), stud)); + assertTrue(tc.isOfTypeForAssign(tc.typeOf(pers), fstud)); + assertTrue(tc.isOfTypeForAssign(tc.typeOf(stud), fstud)); + assertFalse(tc.isOfTypeForAssign(tc.typeOf(stud), pers)); + assertFalse(tc.isOfTypeForAssign(tc.typeOf(fstud), pers)); + assertFalse(tc.isOfTypeForAssign(tc.typeOf(fstud), stud)); + assertTrue(tc.isOfTypeForAssign(tc.typeOf(pers), pers)); + + assertFalse(tc.isOfTypeForAssign(tc.typeOf(int1), pers)); + } + + @Test + public void testIsSubtype() throws IOException { + //primitives + CombineExpressionsWithLiteralsTraverser traverser = getTraverser(flatExpressionScopeSetter); + ASTExpression bool1 = p.parse_StringExpression("true").get(); + bool1.accept(traverser); + ASTExpression bool2 = p.parse_StringExpression("false").get(); + bool2.accept(traverser); + ASTExpression float1 = p.parse_StringExpression("3.4f").get(); + float1.accept(traverser); + ASTExpression int1 = p.parse_StringExpression("3").get(); + int1.accept(traverser); + ASTExpression double1 = p.parse_StringExpression("3.46").get(); + double1.accept(traverser); + ASTExpression long1 = p.parse_StringExpression("5L").get(); + long1.accept(traverser); + ASTExpression char1 = p.parse_StringExpression("\'a\'").get(); + char1.accept(traverser); + + + assertTrue(tc.isSubtypeOf(tc.typeOf(bool1), tc.typeOf(bool2))); + assertTrue(tc.isSubtypeOf(tc.typeOf(int1), tc.typeOf(double1))); + assertFalse(tc.isSubtypeOf(tc.typeOf(int1), tc.typeOf(bool1))); + assertTrue(tc.isSubtypeOf(tc.typeOf(int1), tc.typeOf(float1))); + assertTrue(tc.isSubtypeOf(tc.typeOf(int1), tc.typeOf(long1))); + assertTrue(tc.isSubtypeOf(tc.typeOf(char1), tc.typeOf(char1))); + assertFalse(tc.isSubtypeOf(tc.typeOf(int1), tc.typeOf(char1))); + assertFalse(tc.isSubtypeOf(tc.typeOf(bool1), tc.typeOf(double1))); + assertFalse(tc.isSubtypeOf(tc.typeOf(float1), tc.typeOf(long1))); + assertTrue(tc.isSubtypeOf(tc.typeOf(int1), tc.typeOf(float1))); + + //non-primitives + ASTExpression pers = p.parse_StringExpression("Person").get(); + pers.accept(traverser); + ASTExpression stud = p.parse_StringExpression("Student").get(); + stud.accept(traverser); + ASTExpression fstud = p.parse_StringExpression("FirstSemesterStudent").get(); + fstud.accept(traverser); + + assertTrue(tc.isSubtypeOf(tc.typeOf(stud), tc.typeOf(pers))); + assertTrue(tc.isSubtypeOf(tc.typeOf(fstud), tc.typeOf(pers))); + assertTrue(tc.isSubtypeOf(tc.typeOf(fstud), tc.typeOf(stud))); + assertFalse(tc.isSubtypeOf(tc.typeOf(pers), tc.typeOf(stud))); + assertFalse(tc.isSubtypeOf(tc.typeOf(pers), tc.typeOf(fstud))); + assertFalse(tc.isSubtypeOf(tc.typeOf(stud), tc.typeOf(fstud))); + assertTrue(tc.isSubtypeOf(tc.typeOf(pers), tc.typeOf(pers))); + + assertFalse(tc.isSubtypeOf(tc.typeOf(int1), tc.typeOf(pers))); + } + + @Test + public void testCompatibilityForPrimitives() { + SymTypeExpression booleanT = createPrimitive(BasicSymbolsMill.BOOLEAN); + SymTypeExpression byteT = createPrimitive(BasicSymbolsMill.BYTE); + SymTypeExpression shortT = createPrimitive(BasicSymbolsMill.SHORT); + SymTypeExpression charT = createPrimitive(BasicSymbolsMill.CHAR); + SymTypeExpression intT = createPrimitive(BasicSymbolsMill.INT); + SymTypeExpression longT = createPrimitive(BasicSymbolsMill.LONG); + SymTypeExpression floatT = createPrimitive(BasicSymbolsMill.FLOAT); + SymTypeExpression doubleT = createPrimitive(BasicSymbolsMill.DOUBLE); + + assertTrue(tc.compatible(booleanT, booleanT)); + assertTrue(tc.compatible(byteT, byteT)); + assertTrue(tc.compatible(shortT, byteT)); + assertTrue(tc.compatible(shortT, shortT)); + assertTrue(tc.compatible(charT, charT)); + assertTrue(tc.compatible(intT, byteT)); + assertTrue(tc.compatible(intT, shortT)); + assertTrue(tc.compatible(intT, charT)); + assertTrue(tc.compatible(intT, intT)); + assertTrue(tc.compatible(longT, byteT)); + assertTrue(tc.compatible(longT, shortT)); + assertTrue(tc.compatible(longT, charT)); + assertTrue(tc.compatible(longT, intT)); + assertTrue(tc.compatible(longT, longT)); + assertTrue(tc.compatible(floatT, byteT)); + assertTrue(tc.compatible(floatT, shortT)); + assertTrue(tc.compatible(floatT, charT)); + assertTrue(tc.compatible(floatT, intT)); + assertTrue(tc.compatible(floatT, longT)); + assertTrue(tc.compatible(floatT, floatT)); + assertTrue(tc.compatible(doubleT, byteT)); + assertTrue(tc.compatible(doubleT, shortT)); + assertTrue(tc.compatible(doubleT, charT)); + assertTrue(tc.compatible(doubleT, intT)); + assertTrue(tc.compatible(doubleT, longT)); + assertTrue(tc.compatible(doubleT, floatT)); + assertTrue(tc.compatible(doubleT, doubleT)); + } + + @Test + public void testIncompatibilityForPrimitives() { + SymTypeExpression booleanT = createPrimitive(BasicSymbolsMill.BOOLEAN); + SymTypeExpression byteT = createPrimitive(BasicSymbolsMill.BYTE); + SymTypeExpression shortT = createPrimitive(BasicSymbolsMill.SHORT); + SymTypeExpression charT = createPrimitive(BasicSymbolsMill.CHAR); + SymTypeExpression intT = createPrimitive(BasicSymbolsMill.INT); + SymTypeExpression longT = createPrimitive(BasicSymbolsMill.LONG); + SymTypeExpression floatT = createPrimitive(BasicSymbolsMill.FLOAT); + SymTypeExpression doubleT = createPrimitive(BasicSymbolsMill.DOUBLE); + + assertFalse(tc.compatible(booleanT, byteT)); + assertFalse(tc.compatible(booleanT, shortT)); + assertFalse(tc.compatible(booleanT, charT)); + assertFalse(tc.compatible(booleanT, intT)); + assertFalse(tc.compatible(booleanT, longT)); + assertFalse(tc.compatible(booleanT, floatT)); + assertFalse(tc.compatible(booleanT, doubleT)); + assertFalse(tc.compatible(byteT, booleanT)); + assertFalse(tc.compatible(byteT, shortT)); + assertFalse(tc.compatible(byteT, charT)); + assertFalse(tc.compatible(byteT, intT)); + assertFalse(tc.compatible(byteT, longT)); + assertFalse(tc.compatible(byteT, floatT)); + assertFalse(tc.compatible(byteT, doubleT)); + assertFalse(tc.compatible(shortT, booleanT)); + assertFalse(tc.compatible(shortT, charT)); + assertFalse(tc.compatible(shortT, intT)); + assertFalse(tc.compatible(shortT, longT)); + assertFalse(tc.compatible(shortT, floatT)); + assertFalse(tc.compatible(shortT, doubleT)); + assertFalse(tc.compatible(charT, booleanT)); + assertFalse(tc.compatible(charT, byteT)); + assertFalse(tc.compatible(charT, shortT)); + assertFalse(tc.compatible(charT, intT)); + assertFalse(tc.compatible(charT, longT)); + assertFalse(tc.compatible(charT, floatT)); + assertFalse(tc.compatible(charT, doubleT)); + assertFalse(tc.compatible(intT, booleanT)); + assertFalse(tc.compatible(intT, longT)); + assertFalse(tc.compatible(intT, floatT)); + assertFalse(tc.compatible(intT, doubleT)); + assertFalse(tc.compatible(longT, booleanT)); + assertFalse(tc.compatible(longT, floatT)); + assertFalse(tc.compatible(longT, doubleT)); + assertFalse(tc.compatible(floatT, booleanT)); + assertFalse(tc.compatible(floatT, doubleT)); + assertFalse(tc.compatible(doubleT, booleanT)); + } + + @Test + public void testCompatibilityForGenerics() { + // Given + // Building ootype List; Instantiating List + OOTypeSymbol listSym = provideGeneric("List", "T"); + OOSymbolsMill.globalScope().add(listSym); + OOSymbolsMill.globalScope().addSubScope(listSym.getSpannedScope()); + SymTypeExpression personExpr = SymTypeExpressionFactory.createTypeObject(scope.resolveOOType("Person").get()); + SymTypeExpression listOfPersonExpr = SymTypeExpressionFactory.createGenerics(listSym, personExpr); + + // Building ootype PersonList extends List; Instantiating PersonList + OOTypeSymbol personListSym = provideOOType("PersonList"); + personListSym.addSuperTypes(listOfPersonExpr); + OOSymbolsMill.globalScope().add(personListSym); + OOSymbolsMill.globalScope().addSubScope(personListSym.getSpannedScope()); + SymTypeExpression personListExpr = SymTypeExpressionFactory.createTypeObject(personListSym); + + // Building ootype LinkedList extends List; Instantiating LinkedList + OOTypeSymbol linkedListSym = provideGeneric("LinkedList", "U"); + OOSymbolsMill.globalScope().add(linkedListSym); + OOSymbolsMill.globalScope().addSubScope(linkedListSym.getSpannedScope()); + SymTypeVariable linkedlistTypeVar = SymTypeExpressionFactory.createTypeVariable(linkedListSym.getTypeParameterList().get(0)); + SymTypeExpression linkedListParExpr = SymTypeExpressionFactory.createGenerics(listSym, linkedlistTypeVar); + linkedListSym.addSuperTypes(linkedListParExpr); + SymTypeExpression linkedlistOfPersonExpr = SymTypeExpressionFactory.createGenerics(linkedListSym, personExpr); + + // When & Then + Assert.assertTrue(tc.compatible(listOfPersonExpr, listOfPersonExpr)); + Assert.assertTrue(tc.compatible(listOfPersonExpr, personListExpr)); + Assert.assertTrue(tc.compatible(listOfPersonExpr, linkedlistOfPersonExpr)); + } + + @Test + public void testIncompatibilityForGenerics() { + // Given + // Building ootype List; Instantiating List, List, List + OOTypeSymbol listSym = provideGeneric("List", "T"); + OOSymbolsMill.globalScope().add(listSym); + OOSymbolsMill.globalScope().addSubScope(listSym.getSpannedScope()); + SymTypeExpression personExpr = SymTypeExpressionFactory.createTypeObject(scope.resolveOOType("Person").get()); + SymTypeExpression listOfPersonExpr = SymTypeExpressionFactory.createGenerics(listSym, personExpr); + SymTypeExpression listOfIntExpr = SymTypeExpressionFactory.createGenerics(listSym, _intSymType); + SymTypeExpression listOfBoolExpr = SymTypeExpressionFactory.createGenerics(listSym, _booleanSymType); + + // Building ootype PersonList extends List; Instantiating PersonList + OOTypeSymbol personListSym = provideOOType("PersonList"); + personListSym.addSuperTypes(listOfPersonExpr); + OOSymbolsMill.globalScope().add(personListSym); + OOSymbolsMill.globalScope().addSubScope(personListSym.getSpannedScope()); + SymTypeExpression personListExpr = SymTypeExpressionFactory.createTypeObject(personListSym); + + // When & Then + Assert.assertFalse(tc.compatible(listOfIntExpr, _intSymType)); + Assert.assertFalse(tc.compatible(listOfIntExpr, listOfBoolExpr)); + Assert.assertFalse(tc.compatible(listOfBoolExpr, listOfIntExpr)); + Assert.assertFalse(tc.compatible(listOfBoolExpr, listOfPersonExpr)); + Assert.assertFalse(tc.compatible(listOfPersonExpr, listOfBoolExpr)); + Assert.assertFalse(tc.compatible(listOfBoolExpr, personListExpr)); + Assert.assertFalse(tc.compatible(personListExpr, listOfBoolExpr)); + } + + public CombineExpressionsWithLiteralsTraverser getTraverser(FlatExpressionScopeSetter flatExpressionScopeSetter){ + CombineExpressionsWithLiteralsTraverser traverser = CombineExpressionsWithLiteralsMill.traverser(); + traverser.add4AssignmentExpressions(flatExpressionScopeSetter); + traverser.add4BitExpressions(flatExpressionScopeSetter); + traverser.add4CommonExpressions(flatExpressionScopeSetter); + traverser.add4ExpressionsBasis(flatExpressionScopeSetter); + traverser.add4JavaClassExpressions(flatExpressionScopeSetter); + traverser.add4MCBasicTypes(flatExpressionScopeSetter); + traverser.add4MCCommonLiterals(flatExpressionScopeSetter); + return traverser; + } + + + protected static OOTypeSymbol provideOOType(String name) { + return DefsTypeBasic.type(name); + } + + protected static OOTypeSymbol provideGeneric(String rawName, String... typeVarNames) { + List typeVars = Arrays.stream(typeVarNames) + .map(tVarName -> OOSymbolsMill.typeVarSymbolBuilder() + .setName(tVarName) + .setSpannedScope(OOSymbolsMill.scope()) + .build() + ).collect(Collectors.toList()); + + return DefsTypeBasic.type(rawName, new ArrayList<>(), typeVars); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/check/helpers/DefiningSymbolSetter4CommonExpressionsTest.java b/monticore-grammar/src/test/java/de/monticore/types/check/helpers/DefiningSymbolSetter4CommonExpressionsTest.java new file mode 100644 index 0000000000..74647c43d6 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/check/helpers/DefiningSymbolSetter4CommonExpressionsTest.java @@ -0,0 +1,108 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check.helpers; + +import de.monticore.expressions.combineexpressionswithliterals.CombineExpressionsWithLiteralsMill; +import de.monticore.expressions.commonexpressions._ast.ASTCallExpression; +import de.monticore.expressions.commonexpressions._ast.ASTFieldAccessExpression; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.expressionsbasis._ast.ASTNameExpression; +import de.monticore.symboltable.ISymbol; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class DefiningSymbolSetter4CommonExpressionsTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + CombineExpressionsWithLiteralsMill.reset(); + CombineExpressionsWithLiteralsMill.init(); + } + + @Test + public void setDefiningSymbolForNameExprTest() { + // Given + DefiningSymbolSetter4CommonExpressions definingSymbolSetter = new DefiningSymbolSetter4CommonExpressions(); + + ASTNameExpression expr = CombineExpressionsWithLiteralsMill + .nameExpressionBuilder() + .setName("Foo") + .build(); + ISymbol symbol = CombineExpressionsWithLiteralsMill + .typeSymbolBuilder() + .setName("Foo") + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + + // When + definingSymbolSetter.setDefiningSymbol((ASTExpression) expr, symbol); + + // Then + Assert.assertTrue(expr.getDefiningSymbol().isPresent()); + Assert.assertSame(symbol, expr.getDefiningSymbol().get()); + + } + + @Test + public void setDefiningSymbolForFieldAccessExprTest() { + // Given + DefiningSymbolSetter4CommonExpressions definingSymbolSetter = new DefiningSymbolSetter4CommonExpressions(); + + ASTNameExpression qualExpr = CombineExpressionsWithLiteralsMill + .nameExpressionBuilder() + .setName("paccage") + .build(); + ASTFieldAccessExpression fieldAccessExpr = CombineExpressionsWithLiteralsMill + .fieldAccessExpressionBuilder() + .setName("Foo") + .setExpression(qualExpr) + .build(); + ISymbol symbol = CombineExpressionsWithLiteralsMill + .typeSymbolBuilder() + .setName("Foo") + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .build(); + + // When + definingSymbolSetter.setDefiningSymbol((ASTExpression) fieldAccessExpr, symbol); + + // Then + Assert.assertTrue(fieldAccessExpr.getDefiningSymbol().isPresent()); + Assert.assertSame(symbol, fieldAccessExpr.getDefiningSymbol().get()); + + Assert.assertFalse(qualExpr.getDefiningSymbol().isPresent()); + } + + @Test + public void setDefiningSymbolForCallExprTest() { + // Given + DefiningSymbolSetter4CommonExpressions definingSymbolSetter = new DefiningSymbolSetter4CommonExpressions(); + + ASTNameExpression methodNameExpr = CombineExpressionsWithLiteralsMill + .nameExpressionBuilder() + .setName("foo") + .build(); + ASTCallExpression callExpr = CombineExpressionsWithLiteralsMill + .callExpressionBuilder() + .setExpression(methodNameExpr) + .uncheckedBuild(); + ISymbol symbol = CombineExpressionsWithLiteralsMill + .functionSymbolBuilder() + .setName("foo") + .build(); + + // When + definingSymbolSetter.setDefiningSymbol((ASTExpression) callExpr, symbol); + + // Then + Assert.assertTrue(callExpr.getDefiningSymbol().isPresent()); + Assert.assertSame(symbol, callExpr.getDefiningSymbol().get()); + + Assert.assertFalse(methodNameExpr.getDefiningSymbol().isPresent()); + } +} + diff --git a/monticore-grammar/src/test/java/de/monticore/types/check/helpers/SubExprNameExtractionResultTest.java b/monticore-grammar/src/test/java/de/monticore/types/check/helpers/SubExprNameExtractionResultTest.java new file mode 100644 index 0000000000..f686f6894a --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/check/helpers/SubExprNameExtractionResultTest.java @@ -0,0 +1,450 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check.helpers; + +import de.monticore.expressions.expressionsbasis._ast.ASTNameExpression; +import de.monticore.expressions.testcommonexpressions.TestCommonExpressionsMill; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; + +public class SubExprNameExtractionResultTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + TestCommonExpressionsMill.reset(); + TestCommonExpressionsMill.init(); + } + + protected static ASTNameExpression buildNameExpression(String name) { + return TestCommonExpressionsMill.nameExpressionBuilder().setName(name).build(); + } + + @Test + public void resetCreatesNewList() { + // Given + SubExprNameExtractionResult extractionResult = new SubExprNameExtractionResult(); + extractionResult.putNameAtStart(buildNameExpression("a"), "a"); + extractionResult.maybeAppendInvalidExprAtStart(buildNameExpression("b")); + List subExprListBefore = extractionResult.getNamePartsRaw(); + + // When + extractionResult.reset(); + extractionResult.putNameAtStart(buildNameExpression("c"), "c"); + + // Then + List subExprListAfter = extractionResult.getNamePartsRaw(); + Assert.assertEquals(1, subExprListAfter.size()); + Assert.assertNotEquals(subExprListBefore, subExprListAfter); + } + + @Test + public void testSetSubExpressions() { + // Given + SubExprNameExtractionResult extractionResult = new SubExprNameExtractionResult(); + List subExprList = new LinkedList<>(); + ASTNameExpression nameToInsert = buildNameExpression("a"); + + // When + extractionResult.setSubExpressions(subExprList); + subExprList.add(0, ExprToOptNamePair.of(nameToInsert, Optional.of("a"))); + + // Then + List returnedSubExprList = extractionResult.getNamePartsRaw(); + Assert.assertEquals(1, returnedSubExprList.size()); + Assert.assertEquals(nameToInsert, returnedSubExprList.get(0).getExpression()); + Assert.assertEquals("a", returnedSubExprList.get(0).getName().orElse("no name inserted")); + Assert.assertEquals(subExprList, returnedSubExprList); + } + + @Test + public void maybeAppendInvalidSubExprToEmptyList() { + // Given + SubExprNameExtractionResult extractionResult = new SubExprNameExtractionResult(); + ASTNameExpression expr = buildNameExpression("a"); + + // When + extractionResult.maybeAppendInvalidExprAtStart(expr); + + // Then + List subExprList = extractionResult.getNamePartsRaw(); + Assert.assertEquals(1, subExprList.size()); + Assert.assertEquals(expr, subExprList.get(0).getExpression()); + Assert.assertFalse(subExprList.get(0).getName().isPresent()); + } + + @Test + public void maybeAppendInvalidSubExprToFilledListContainingDifferentValidNameExpressions() { + SubExprNameExtractionResult extractionResult = new SubExprNameExtractionResult(); + ASTNameExpression oldExpr = buildNameExpression("a"); + ASTNameExpression newExpr = buildNameExpression("b"); + extractionResult.putNameAtStart(oldExpr, "a"); + + // When + extractionResult.maybeAppendInvalidExprAtStart(newExpr); + + // Then + List subExprList = extractionResult.getNamePartsRaw(); + Assert.assertEquals(2, subExprList.size()); + + Assert.assertEquals(newExpr, subExprList.get(0).getExpression()); + Assert.assertFalse(subExprList.get(0).getName().isPresent()); + + Assert.assertEquals(oldExpr, subExprList.get(1).getExpression()); + Assert.assertEquals("a", subExprList.get(1).getName().orElse("no name inserted")); + } + + @Test + public void maybeAppendInvalidSubExprToFilledListContainingDifferentInvalidNameExpressions() { + SubExprNameExtractionResult extractionResult = new SubExprNameExtractionResult(); + ASTNameExpression oldExpr = buildNameExpression("a"); + extractionResult.maybeAppendInvalidExprAtStart(oldExpr); + + // When + ASTNameExpression newExpr = buildNameExpression("b"); + extractionResult.maybeAppendInvalidExprAtStart(newExpr); + + // Then + List subExprList = extractionResult.getNamePartsRaw(); + Assert.assertEquals(2, subExprList.size()); + + Assert.assertEquals(newExpr, subExprList.get(0).getExpression()); + Assert.assertFalse(subExprList.get(0).getName().isPresent()); + + Assert.assertEquals(oldExpr, subExprList.get(1).getExpression()); + Assert.assertFalse(subExprList.get(1).getName().isPresent()); + } + + @Test + public void maybeAppendInvalidSubExprDoesNotOverwriteSameExpression() { + // Given + SubExprNameExtractionResult extractionResult = new SubExprNameExtractionResult(); + ASTNameExpression expr = buildNameExpression("a"); + extractionResult.putNameAtStart(expr, "a"); + + // When + extractionResult.maybeAppendInvalidExprAtStart(expr); + + // Then + List subExprList = extractionResult.getNamePartsRaw(); + Assert.assertEquals(1, subExprList.size()); + Assert.assertEquals(expr, subExprList.get(0).getExpression()); + Assert.assertEquals("a", subExprList.get(0).getName().orElse("no name inserted")); + } + + @Test + public void putNameAtStartOfEmptyList() { + // Given + SubExprNameExtractionResult extractionResult = new SubExprNameExtractionResult(); + ASTNameExpression expr = buildNameExpression("a"); + + // When + extractionResult.putNameAtStart(expr, "a"); + + // Then + List subExprList = extractionResult.getNamePartsRaw(); + Assert.assertEquals(1, subExprList.size()); + Assert.assertEquals(expr, subExprList.get(0).getExpression()); + Assert.assertEquals("a", subExprList.get(0).getName().orElse("no name inserted")); + } + + @Test + public void putNameAtStartOfFilledListContainingDifferentValidExpressions() { + // Given + SubExprNameExtractionResult extractionResult = new SubExprNameExtractionResult(); + ASTNameExpression oldExpr = buildNameExpression("a"); + + extractionResult.putNameAtStart(oldExpr, "a"); + + // When + ASTNameExpression newExpr = buildNameExpression("b"); + extractionResult.putNameAtStart(newExpr, "b"); + + // Then + List subExprList = extractionResult.getNamePartsRaw(); + Assert.assertEquals(2, subExprList.size()); + + Assert.assertEquals(newExpr, subExprList.get(0).getExpression()); + Assert.assertEquals("b", subExprList.get(0).getName().orElse("no name inserted")); + + Assert.assertEquals(oldExpr, subExprList.get(1).getExpression()); + Assert.assertEquals("a", subExprList.get(1).getName().orElse("no name inserted")); + } + + @Test + public void putNameAtStartOverwritesSameExpressionThatHasNoNameYet() { + // Given + SubExprNameExtractionResult extractionResult = new SubExprNameExtractionResult(); + ASTNameExpression dummyExprNotToAlter = buildNameExpression("dummy already in list"); + ASTNameExpression expr = buildNameExpression("b"); + extractionResult.maybeAppendInvalidExprAtStart(dummyExprNotToAlter); + extractionResult.maybeAppendInvalidExprAtStart(expr); + + // When + extractionResult.putNameAtStart(expr, "b"); + + // Then + List subExprList = extractionResult.getNamePartsRaw(); + Assert.assertEquals(2, subExprList.size()); + + Assert.assertEquals(expr, subExprList.get(0).getExpression()); + Assert.assertEquals("b", subExprList.get(0).getName().orElse("no name inserted")); + + Assert.assertEquals(dummyExprNotToAlter, subExprList.get(1).getExpression()); + Assert.assertFalse(subExprList.get(1).getName().isPresent()); + } + + @Test + public void putNameAtStartOverwritesSameExpressionThatAlreadyHasAName() { + // Given + SubExprNameExtractionResult extractionResult = new SubExprNameExtractionResult(); + ASTNameExpression dummyExprNotToAlter = buildNameExpression("dummy already in list"); + ASTNameExpression expr = buildNameExpression("b"); + extractionResult.maybeAppendInvalidExprAtStart(dummyExprNotToAlter); + extractionResult.putNameAtStart(expr, "nameToBeOverwritten"); + + // When + extractionResult.putNameAtStart(expr, "b"); + + // Then + List subExprList = extractionResult.getNamePartsRaw(); + Assert.assertEquals(2, subExprList.size()); + + Assert.assertEquals(expr, subExprList.get(0).getExpression()); + Assert.assertEquals("b", subExprList.get(0).getName().orElse("no name inserted")); + + Assert.assertEquals(dummyExprNotToAlter, subExprList.get(1).getExpression()); + Assert.assertFalse(subExprList.get(1).getName().isPresent()); + } + + @Test + public void getNamePartsIfValidOnEmptyNameList() { + // Given + SubExprNameExtractionResult extractionResult = new SubExprNameExtractionResult(); + + // When + Optional> subExprList = extractionResult.getNamePartsIfValid(); + + // Then + Assert.assertFalse(subExprList.isPresent()); + } + + @Test + public void getNamePartsIfValidOnFilledInvalidNameList() { + // Given + SubExprNameExtractionResult extractionResult = new SubExprNameExtractionResult(); + + ASTNameExpression aExpr = buildNameExpression("a"); + ASTNameExpression bExpr = buildNameExpression("b"); + ASTNameExpression cExpr = buildNameExpression("c"); + + extractionResult.putNameAtStart(aExpr, "a"); + extractionResult.maybeAppendInvalidExprAtStart(bExpr); + extractionResult.putNameAtStart(cExpr, "c"); + + // When + Optional> subExprList = extractionResult.getNamePartsIfValid(); + + // Then + Assert.assertFalse(subExprList.isPresent()); + } + + @Test + public void getNamePartsIfValidOnValidNameList() { + // Given + SubExprNameExtractionResult extractionResult = new SubExprNameExtractionResult(); + + ASTNameExpression aExpr = buildNameExpression("a"); + ASTNameExpression bExpr = buildNameExpression("b"); + + extractionResult.putNameAtStart(aExpr, "a"); + extractionResult.putNameAtStart(bExpr, "b"); + + // When + Optional> subExprList = extractionResult.getNamePartsIfValid(); + + // Then + Assert.assertTrue(subExprList.isPresent()); + Assert.assertEquals(2, subExprList.get().size()); + + Assert.assertEquals(bExpr, subExprList.get().get(0).getExpression()); + Assert.assertEquals("b", subExprList.get().get(0).getName()); + + Assert.assertEquals(aExpr, subExprList.get().get(1).getExpression()); + Assert.assertEquals("a", subExprList.get().get(1).getName()); + } + + @Test + public void getNamePartsRawOnEmptyList() { + // Given + SubExprNameExtractionResult extractionResult = new SubExprNameExtractionResult(); + + // When + List subExprList = extractionResult.getNamePartsRaw(); + + // Then + Assert.assertTrue(subExprList.isEmpty()); + } + + @Test + public void getNamePartsRawOnFilledList() { + // Given + SubExprNameExtractionResult extractionResult = new SubExprNameExtractionResult(); + ASTNameExpression aExpr = buildNameExpression("a"); + ASTNameExpression bExpr = buildNameExpression("b"); + ASTNameExpression cExpr = buildNameExpression("c"); + + extractionResult.putNameAtStart(aExpr, "a"); + extractionResult.maybeAppendInvalidExprAtStart(bExpr); + extractionResult.putNameAtStart(cExpr, "c"); + + // When + List subExprList = extractionResult.getNamePartsRaw(); + + // Then + Assert.assertEquals(3, subExprList.size()); + + Assert.assertEquals(cExpr, subExprList.get(0).getExpression()); + Assert.assertEquals(bExpr, subExprList.get(1).getExpression()); + Assert.assertEquals(aExpr, subExprList.get(2).getExpression()); + + Assert.assertEquals("c", subExprList.get(0).getName().orElse("no name was inserted")); + Assert.assertFalse(subExprList.get(1).getName().isPresent()); + Assert.assertEquals("a", subExprList.get(2).getName().orElse("no name was inserted")); + } + + @Test + public void getLastNameOnEmptyList() { + // Given + SubExprNameExtractionResult extractionResult = new SubExprNameExtractionResult(); + + // When + Optional lastName = extractionResult.getLastName(); + + // Then + Assert.assertFalse(lastName.isPresent()); + } + + @Test + public void getLastNameOnAbsentName() { + // Given + SubExprNameExtractionResult extractionResult = new SubExprNameExtractionResult(); + ASTNameExpression aExpr = buildNameExpression("a"); + ASTNameExpression bExpr = buildNameExpression("b"); + + extractionResult.maybeAppendInvalidExprAtStart(aExpr); + extractionResult.putNameAtStart(bExpr, "b"); + + // When + Optional lastName = extractionResult.getLastName(); + + // Then + Assert.assertFalse(lastName.isPresent()); + } + + @Test + public void getLastNameOnValidLastName() { + // Given + SubExprNameExtractionResult extractionResult = new SubExprNameExtractionResult(); + ASTNameExpression aExpr = buildNameExpression("a"); + ASTNameExpression bExpr = buildNameExpression("b"); + + extractionResult.putNameAtStart(aExpr, "a"); + extractionResult.maybeAppendInvalidExprAtStart(bExpr); + + // When + Optional lastName = extractionResult.getLastName(); + + // Then + Assert.assertTrue(lastName.isPresent()); + Assert.assertEquals("a", lastName.get()); + } + + @Test + public void resultIsValidNameOnEmptyNameList() { + // Given + SubExprNameExtractionResult extractionResult = new SubExprNameExtractionResult(); + + // When + boolean resultIsValidName = extractionResult.resultIsValidName(); + + // Then + Assert.assertFalse(resultIsValidName); + } + + @Test + public void resultIsValidNameOnInvalidNameList() { + // Given + SubExprNameExtractionResult extractionResult = new SubExprNameExtractionResult(); + ASTNameExpression aExpr = buildNameExpression("a"); + ASTNameExpression bExpr = buildNameExpression("b"); + ASTNameExpression cExpr = buildNameExpression("c"); + + extractionResult.putNameAtStart(aExpr, "a"); + extractionResult.maybeAppendInvalidExprAtStart(bExpr); + extractionResult.putNameAtStart(cExpr, "c"); + + // When + boolean resultIsValidName = extractionResult.resultIsValidName(); + + // Then + Assert.assertFalse(resultIsValidName); + } + + @Test + public void resultIsValidOnValidNameList() { + // Given + SubExprNameExtractionResult extractionResult = new SubExprNameExtractionResult(); + + ASTNameExpression aExpr = buildNameExpression("a"); + ASTNameExpression bExpr = buildNameExpression("b"); + + extractionResult.putNameAtStart(aExpr, "a"); + extractionResult.putNameAtStart(bExpr, "b"); + + // When + boolean resultIsValidName = extractionResult.resultIsValidName(); + + // Then + Assert.assertTrue(resultIsValidName); + } + + @Test + public void testCopy() { + // Given + SubExprNameExtractionResult originalResult = new SubExprNameExtractionResult(); + ASTNameExpression aExpr = buildNameExpression("a"); + ASTNameExpression bExpr = buildNameExpression("b"); + ASTNameExpression cExpr = buildNameExpression("c"); + + originalResult.putNameAtStart(aExpr, "a"); + originalResult.maybeAppendInvalidExprAtStart(bExpr); + originalResult.putNameAtStart(cExpr, "c"); + + // When + SubExprNameExtractionResult copiedResult = originalResult.copy(); + originalResult.reset(); + ASTNameExpression dExpr = buildNameExpression("d"); + originalResult.putNameAtStart(dExpr, "d"); + + // Then + List originalExpressions = originalResult.getNamePartsRaw(); + List copiedSubExpressions = copiedResult.getNamePartsRaw(); + Assert.assertNotEquals(originalExpressions, copiedSubExpressions); + + Assert.assertEquals(3, copiedSubExpressions.size()); + Assert.assertEquals(cExpr, copiedSubExpressions.get(0).getExpression()); + Assert.assertEquals(bExpr, copiedSubExpressions.get(1).getExpression()); + Assert.assertEquals(aExpr, copiedSubExpressions.get(2).getExpression()); + + Assert.assertEquals(1, originalExpressions.size()); + Assert.assertEquals(dExpr, originalExpressions.get(0).getExpression()); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/check/helpers/SubExprNameExtractor4CommonExpressionsTest.java b/monticore-grammar/src/test/java/de/monticore/types/check/helpers/SubExprNameExtractor4CommonExpressionsTest.java new file mode 100644 index 0000000000..f46c5dc8c9 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/check/helpers/SubExprNameExtractor4CommonExpressionsTest.java @@ -0,0 +1,108 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.check.helpers; + +import de.monticore.expressions.combineexpressionswithliterals.CombineExpressionsWithLiteralsMill; +import de.monticore.expressions.combineexpressionswithliterals._parser.CombineExpressionsWithLiteralsParser; +import de.monticore.expressions.commonexpressions._ast.ASTCallExpression; +import de.monticore.expressions.commonexpressions._ast.ASTFieldAccessExpression; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.List; + +import static org.junit.Assert.*; + +public class SubExprNameExtractor4CommonExpressionsTest { + + private CombineExpressionsWithLiteralsParser parser = new CombineExpressionsWithLiteralsParser(); + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + CombineExpressionsWithLiteralsMill.reset(); + CombineExpressionsWithLiteralsMill.init(); + } + + /** + * test if the name calculator extracts the name of the name expression correctly + */ + @Test + public void nameTest() throws IOException { + // Given + ASTExpression astExpr = parser.parse_StringExpression("test()").get(); + ASTExpression methodNameExpr = ((ASTCallExpression) astExpr).getExpression(); + SubExprNameExtractor4CommonExpressions nameCalculator = new SubExprNameExtractor4CommonExpressions(); + + // When + SubExprNameExtractionResult result = nameCalculator.calculateNameParts(methodNameExpr); + + // Then + assertTrue(result.resultIsValidName()); + assertEquals(1, result.getNamePartsRaw().size()); + assertEquals("test", result.getLastName().get()); + assertEquals(methodNameExpr, result.getNamePartsIfValid().get().get(0).getExpression()); + + assertTrue(Log.getFindings().isEmpty()); + } + + /** + * test if the visitor transforms a call expression with inner field access expression correctly + */ + @Test + public void fieldAccessTest() throws IOException { + // Given + ASTExpression astExpr = parser.parse_StringExpression("Foo.b.test()").get(); + ASTExpression methodNameExpr = ((ASTCallExpression) astExpr).getExpression(); + SubExprNameExtractor4CommonExpressions nameCalculator = new SubExprNameExtractor4CommonExpressions(); + + // When + SubExprNameExtractionResult result = nameCalculator.calculateNameParts(methodNameExpr); + + // Then + assertTrue(result.resultIsValidName()); + List nameParts = result.getNamePartsIfValid().get(); + + assertEquals(3, result.getNamePartsRaw().size()); + assertEquals("test", result.getLastName().get()); + assertEquals(methodNameExpr, nameParts.get(nameParts.size() - 1).getExpression()); + assertEquals( + ((ASTFieldAccessExpression) methodNameExpr).getExpression(), + nameParts.get(nameParts.size() - 2).getExpression() + ); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void methodChainTest() throws IOException { + // Given + ASTExpression astExpr = parser.parse_StringExpression("tezt().test()").get(); + ASTExpression methodNameExpr = ((ASTCallExpression) astExpr).getExpression(); + SubExprNameExtractor4CommonExpressions nameCalculator = new SubExprNameExtractor4CommonExpressions(); + + // When + SubExprNameExtractionResult result = nameCalculator.calculateNameParts(methodNameExpr); + + // Then + assertFalse(result.resultIsValidName()); + assertTrue(result.getLastName().isPresent()); + + List subExprs = result.getNamePartsRaw(); + + assertEquals(3, result.getNamePartsRaw().size()); // tezt NameExpr, tezt() CallExpr, tezt().test fAccExpr + assertEquals("test", result.getLastName().get()); + assertEquals(methodNameExpr, subExprs.get(subExprs.size() - 1).getExpression()); + assertEquals( + ((ASTFieldAccessExpression) methodNameExpr).getExpression(), + subExprs.get(subExprs.size() - 2).getExpression() + ); + + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/helper/MCArrayTypesNodeIdentHelperTest.java b/monticore-grammar/src/test/java/de/monticore/types/helper/MCArrayTypesNodeIdentHelperTest.java new file mode 100644 index 0000000000..3d6f9d1813 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/helper/MCArrayTypesNodeIdentHelperTest.java @@ -0,0 +1,47 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.types.helper; + +import de.monticore.types.MCArrayTypesNodeIdentHelper; +import de.monticore.types.mcarraytypes.MCArrayTypesMill; +import de.monticore.types.mcarraytypes._ast.ASTMCArrayType; +import de.monticore.types.mcarraytypestest._parser.MCArrayTypesTestParser; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class MCArrayTypesNodeIdentHelperTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + MCArrayTypesMill.reset(); + MCArrayTypesMill.init(); + } + + @Test + public void testGetIdent() throws IOException { + MCArrayTypesTestParser p = new MCArrayTypesTestParser(); + MCArrayTypesNodeIdentHelper identHelper = new MCArrayTypesNodeIdentHelper(); + + Optional astmcArrayType = p.parse_StringMCType("A[]"); + + assertTrue(astmcArrayType.isPresent()); + assertTrue(astmcArrayType.get() instanceof ASTMCArrayType); + + assertEquals("@A!MCArrayType", identHelper.getIdent((ASTMCArrayType)astmcArrayType.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/helper/MCBasicTypesHelperTest.java b/monticore-grammar/src/test/java/de/monticore/types/helper/MCBasicTypesHelperTest.java new file mode 100644 index 0000000000..5d8773bdab --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/helper/MCBasicTypesHelperTest.java @@ -0,0 +1,41 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.helper; + +import de.monticore.types.MCBasicTypesHelper; +import de.monticore.types.mcbasictypes.MCBasicTypesMill; +import de.monticore.types.mcbasictypes._ast.ASTConstantsMCBasicTypes; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class MCBasicTypesHelperTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + MCBasicTypesMill.reset(); + MCBasicTypesMill.init(); + } + + @Test + public void testGetPrimitive(){ + assertEquals(ASTConstantsMCBasicTypes.BOOLEAN,MCBasicTypesHelper.primitiveName2Const("boolean")); + assertEquals(-1,MCBasicTypesHelper.primitiveName2Const(null)); + assertEquals(-1,MCBasicTypesHelper.primitiveName2Const("")); + assertEquals(ASTConstantsMCBasicTypes.BYTE,MCBasicTypesHelper.primitiveName2Const("byte")); + assertEquals(ASTConstantsMCBasicTypes.CHAR,MCBasicTypesHelper.primitiveName2Const("char")); + assertEquals(ASTConstantsMCBasicTypes.DOUBLE,MCBasicTypesHelper.primitiveName2Const("double")); + assertEquals(ASTConstantsMCBasicTypes.FLOAT,MCBasicTypesHelper.primitiveName2Const("float")); + assertEquals(ASTConstantsMCBasicTypes.INT,MCBasicTypesHelper.primitiveName2Const("int")); + assertEquals(ASTConstantsMCBasicTypes.LONG,MCBasicTypesHelper.primitiveName2Const("long")); + assertEquals(ASTConstantsMCBasicTypes.SHORT,MCBasicTypesHelper.primitiveName2Const("short")); + + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/helper/MCBasicTypesNodeIdentHelperTest.java b/monticore-grammar/src/test/java/de/monticore/types/helper/MCBasicTypesNodeIdentHelperTest.java new file mode 100644 index 0000000000..076ae75917 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/helper/MCBasicTypesNodeIdentHelperTest.java @@ -0,0 +1,58 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.helper; + +import de.monticore.types.MCBasicTypesNodeIdentHelper; +import de.monticore.types.mcbasictypes.MCBasicTypesMill; +import de.monticore.types.mcbasictypes._ast.*; +import de.monticore.types.mcbasictypestest._parser.MCBasicTypesTestParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.*; + +public class MCBasicTypesNodeIdentHelperTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + MCBasicTypesMill.reset(); + MCBasicTypesMill.init(); + } + + @Test + public void testGetIdent() throws IOException { + MCBasicTypesTestParser parser = new MCBasicTypesTestParser(); + Optional astmcQualifiedName = parser.parse_StringMCQualifiedName("java.util.List"); + Optional astmcPrimitiveType = parser.parse_StringMCPrimitiveType("int"); + Optional astmcQualifiedType = parser.parse_StringMCQualifiedType("java.util.Set"); + Optional astmcVoidType = parser.parse_StringMCVoidType("void"); + Optional astmcReturnType = parser.parse_StringMCReturnType("float"); + Optional astmcReturnTypeVoid = parser.parse_StringMCReturnType("void"); + + assertFalse(parser.hasErrors()); + assertTrue(astmcQualifiedName.isPresent()); + assertTrue(astmcPrimitiveType.isPresent()); + assertTrue(astmcQualifiedType.isPresent()); + assertTrue(astmcVoidType.isPresent()); + assertTrue(astmcReturnType.isPresent()); + assertTrue(astmcReturnTypeVoid.isPresent()); + + MCBasicTypesNodeIdentHelper helper = new MCBasicTypesNodeIdentHelper(); + assertEquals("@java.util.List!MCQualifiedName",helper.getIdent(astmcQualifiedName.get())); + assertEquals("@int!MCPrimitiveType",helper.getIdent(astmcPrimitiveType.get())); + assertEquals("@java.util.Set!MCQualifiedType",helper.getIdent(astmcQualifiedType.get())); + assertEquals("@void!MCVoidType",helper.getIdent(astmcVoidType.get())); + assertEquals("@float!MCPrimitiveType",helper.getIdent(astmcReturnType.get())); + assertEquals("@void!MCVoidType",helper.getIdent(astmcReturnTypeVoid.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/helper/MCCollectionTypesNodeIdentHelperTest.java b/monticore-grammar/src/test/java/de/monticore/types/helper/MCCollectionTypesNodeIdentHelperTest.java new file mode 100644 index 0000000000..9115de8c6e --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/helper/MCCollectionTypesNodeIdentHelperTest.java @@ -0,0 +1,58 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.helper; + +import de.monticore.types.MCCollectionTypesNodeIdentHelper; +import de.monticore.types.mcbasictypes._ast.ASTMCPrimitiveType; +import de.monticore.types.mccollectiontypes._ast.ASTMCBasicTypeArgument; +import de.monticore.types.mccollectiontypes._ast.ASTMCGenericType; +import de.monticore.types.mccollectiontypes._ast.ASTMCPrimitiveTypeArgument; +import de.monticore.types.mccollectiontypestest.MCCollectionTypesTestMill; +import de.monticore.types.mccollectiontypestest._parser.MCCollectionTypesTestParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.*; + +public class MCCollectionTypesNodeIdentHelperTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + MCCollectionTypesTestMill.reset(); + MCCollectionTypesTestMill.init(); + } + + @Test + public void testGetIdent() throws IOException { + MCCollectionTypesTestParser parser = new MCCollectionTypesTestParser(); + Optional astmcGenericType = parser.parse_StringMCGenericType("Map"); + Optional astmcGenericType1 = parser.parse_StringMCGenericType("List"); + Optional astmcGenericType2 = parser.parse_StringMCGenericType("Set"); + Optional astmcGenericType3 = parser.parse_StringMCGenericType("Optional"); + Optional astmcBasicTypeArgument = parser.parse_StringMCBasicTypeArgument("a.B.C"); + Optional astmcPrimitiveTypeArgument = parser.parse_StringMCPrimitiveTypeArgument("boolean"); + + assertFalse(parser.hasErrors()); + assertTrue(astmcGenericType.isPresent()); + assertTrue(astmcGenericType1.isPresent()); + assertTrue(astmcGenericType2.isPresent()); + assertTrue(astmcGenericType3.isPresent()); + + MCCollectionTypesNodeIdentHelper helper = new MCCollectionTypesNodeIdentHelper(); + assertEquals("@Map!MCMapType", helper.getIdent(astmcGenericType.get())); + assertEquals("@List!MCListType", helper.getIdent(astmcGenericType1.get())); + assertEquals("@Set!MCSetType", helper.getIdent(astmcGenericType2.get())); + assertEquals("@Optional!MCOptionalType",helper.getIdent(astmcGenericType3.get())); + assertEquals("@a.B.C!MCBasicTypeArgument", helper.getIdent(astmcBasicTypeArgument.get())); + assertEquals("@boolean!MCPrimitiveTypeArgument",helper.getIdent(astmcPrimitiveTypeArgument.get())); + + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/helper/MCFullGenericTypesNodeIdentHelperTest.java b/monticore-grammar/src/test/java/de/monticore/types/helper/MCFullGenericTypesNodeIdentHelperTest.java new file mode 100644 index 0000000000..73777a8879 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/helper/MCFullGenericTypesNodeIdentHelperTest.java @@ -0,0 +1,44 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.types.helper; + +import de.monticore.types.MCFullGenericTypesNodeIdentHelper; +import de.monticore.types.mcfullgenerictypes._ast.ASTMCMultipleGenericType; +import de.monticore.types.mcfullgenerictypestest.MCFullGenericTypesTestMill; +import de.monticore.types.mcfullgenerictypestest._parser.MCFullGenericTypesTestParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class MCFullGenericTypesNodeIdentHelperTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + MCFullGenericTypesTestMill.reset(); + MCFullGenericTypesTestMill.init(); + } + + @Test + public void testGetIdent() throws IOException { + MCFullGenericTypesTestParser p = new MCFullGenericTypesTestParser(); + MCFullGenericTypesNodeIdentHelper identHelper = new MCFullGenericTypesNodeIdentHelper(); + Optional astmcMultipleGenericType = p.parse_StringMCMultipleGenericType("a.b.D.d.E"); + + assertTrue(astmcMultipleGenericType.isPresent()); + + assertEquals("@a.b.D.d.E!MCMultipleGenericType", identHelper.getIdent(astmcMultipleGenericType.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/helper/MCSimpleGenericTypesNodeIdentHelperTest.java b/monticore-grammar/src/test/java/de/monticore/types/helper/MCSimpleGenericTypesNodeIdentHelperTest.java new file mode 100644 index 0000000000..b3dcddbf0a --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/helper/MCSimpleGenericTypesNodeIdentHelperTest.java @@ -0,0 +1,56 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.helper; + +import de.monticore.types.MCSimpleGenericTypesNodeIdentHelper; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.mccollectiontypes._ast.ASTMCGenericType; +import de.monticore.types.mcsimplegenerictypes.MCSimpleGenericTypesMill; +import de.monticore.types.mcsimplegenerictypes._ast.ASTMCBasicGenericType; +import de.monticore.types.mcsimplegenerictypestest._parser.MCSimpleGenericTypesTestParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; + +public class MCSimpleGenericTypesNodeIdentHelperTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + MCSimpleGenericTypesMill.reset(); + MCSimpleGenericTypesMill.init(); + } + + @Test + public void testGetIdent() throws IOException { + MCSimpleGenericTypesTestParser parser = new MCSimpleGenericTypesTestParser(); + Optional astmcType = parser.parse_StringMCBasicGenericType("List>"); + Optional astmcType1 = parser.parse_StringMCBasicGenericType("java.util.List"); + Optional astmcType2 = parser.parse_StringMCBasicGenericType("Optional>"); + Optional astmcType3 = parser.parse_StringMCBasicGenericType("java.util.Optional"); + Optional astmcType4 = parser.parse_StringMCBasicGenericType("a.b.c.D"); + + assertFalse(parser.hasErrors()); + assertTrue(astmcType.isPresent()); + assertTrue(astmcType1.isPresent()); + assertTrue(astmcType2.isPresent()); + assertTrue(astmcType3.isPresent()); + + MCSimpleGenericTypesNodeIdentHelper helper = new MCSimpleGenericTypesNodeIdentHelper(); + assertEquals("@List!MCBasicGenericType", helper.getIdent(astmcType.get())); + assertEquals("@java.util.List!MCBasicGenericType", helper.getIdent(astmcType1.get())); + assertEquals("@Optional!MCBasicGenericType", helper.getIdent(astmcType2.get())); + assertEquals("@java.util.Optional!MCBasicGenericType",helper.getIdent(astmcType3.get())); + assertEquals("@a.b.c.D!MCBasicGenericType",helper.getIdent(astmcType4.get())); + + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/helper/MCType2SymTypeExpressionTest.java b/monticore-grammar/src/test/java/de/monticore/types/helper/MCType2SymTypeExpressionTest.java new file mode 100644 index 0000000000..7089cf9619 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/helper/MCType2SymTypeExpressionTest.java @@ -0,0 +1,427 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.helper; + +import de.monticore.expressions.combineexpressionswithliterals.CombineExpressionsWithLiteralsMill; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.CombineExpressionsWithLiteralsSymbols2Json; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.ICombineExpressionsWithLiteralsArtifactScope; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.ICombineExpressionsWithLiteralsGlobalScope; +import de.monticore.expressions.combineexpressionswithliterals._visitor.CombineExpressionsWithLiteralsTraverser; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.types.check.*; +import de.monticore.types.mcbasictypes._ast.*; +import de.monticore.types.mccollectiontypes._ast.ASTMCListType; +import de.monticore.types.mccollectiontypes._ast.ASTMCMapType; +import de.monticore.types.mccollectiontypes._ast.ASTMCOptionalType; +import de.monticore.types.mccollectiontypes._ast.ASTMCSetType; +import de.monticore.types.mccollectiontypestest._parser.MCCollectionTypesTestParser; +import de.monticore.types.mcfullgenerictypestest._parser.MCFullGenericTypesTestParser; +import de.monticore.types.mcsimplegenerictypes._ast.ASTMCBasicGenericType; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class MCType2SymTypeExpressionTest { + + protected FlatExpressionScopeSetter scopeSetter; + protected CombineExpressionsWithLiteralsTraverser traverser; + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + CombineExpressionsWithLiteralsMill.reset(); + CombineExpressionsWithLiteralsMill.init(); + BasicSymbolsMill.initializePrimitives(); + + ICombineExpressionsWithLiteralsGlobalScope gs = CombineExpressionsWithLiteralsMill.globalScope(); + gs.add(DefsTypeBasic.type("Person")); + gs.add(DefsTypeBasic.type("Map")); + gs.add(DefsTypeBasic.type("List")); + gs.add(DefsTypeBasic.type("Set")); + gs.add(DefsTypeBasic.type("Optional")); + gs.add(DefsTypeBasic.type("PersonKey")); + gs.add(DefsTypeBasic.type("PersonValue")); + + CombineExpressionsWithLiteralsSymbols2Json symbols2Json = new CombineExpressionsWithLiteralsSymbols2Json(); + ICombineExpressionsWithLiteralsArtifactScope as = symbols2Json.load("src/test/resources/de/monticore/types/check/PairA.cesym"); + as.setEnclosingScope(gs); + + ICombineExpressionsWithLiteralsArtifactScope as2 = symbols2Json.load("src/test/resources/de/monticore/types/check/PairB.cesym"); + as2.setEnclosingScope(gs); + + ICombineExpressionsWithLiteralsArtifactScope as3 = symbols2Json.load("src/test/resources/de/monticore/types/check/PairC.cesym"); + as3.setEnclosingScope(gs); + + ICombineExpressionsWithLiteralsArtifactScope as4 = symbols2Json.load("src/test/resources/de/monticore/types/check/Persondemc.cesym"); + as4.setEnclosingScope(gs); + + ICombineExpressionsWithLiteralsArtifactScope as5 = symbols2Json.load("src/test/resources/de/monticore/types/check/PersonKey.cesym"); + as5.setEnclosingScope(gs); + + ICombineExpressionsWithLiteralsArtifactScope as6 = symbols2Json.load("src/test/resources/de/monticore/types/check/PersonValue.cesym"); + as6.setEnclosingScope(gs); + + ICombineExpressionsWithLiteralsArtifactScope as7 = symbols2Json.load("src/test/resources/de/monticore/types/check/Pair.cesym"); + as7.setEnclosingScope(gs); + + ICombineExpressionsWithLiteralsArtifactScope as8 = symbols2Json.load("src/test/resources/de/monticore/types/check/Pair2.cesym"); + as8.setEnclosingScope(gs); + } + + + + @Before + public void initScope(){ + scopeSetter = new FlatExpressionScopeSetter(CombineExpressionsWithLiteralsMill.globalScope()); + traverser = CombineExpressionsWithLiteralsMill.traverser(); + traverser.add4MCSimpleGenericTypes(scopeSetter); + traverser.add4MCCollectionTypes(scopeSetter); + traverser.add4MCBasicTypes(scopeSetter); + } + + List primitiveTypes = Arrays + .asList("boolean", "byte", "char", "short", "int", "long", "float", "double"); + + private SymTypeExpression mcType2TypeExpression(ASTMCType type) { + type.accept(traverser); + FullSynthesizeFromCombineExpressionsWithLiterals visitor = new FullSynthesizeFromCombineExpressionsWithLiterals(); + return visitor.synthesizeType(type).getResult(); + } + + private SymTypeExpression mcType2TypeExpression(ASTMCVoidType type){ + type.accept(traverser); + FullSynthesizeFromCombineExpressionsWithLiterals visitor = new FullSynthesizeFromCombineExpressionsWithLiterals(); + TypeCalculator tc = new TypeCalculator(visitor, null); + return tc.symTypeFromAST(type); + } + + private SymTypeExpression mcType2TypeExpression(ASTMCQualifiedName qName){ + qName.accept(traverser); + FullSynthesizeFromCombineExpressionsWithLiterals visitor = new FullSynthesizeFromCombineExpressionsWithLiterals(); + return visitor.synthesizeType(qName).getResult(); + } + + @Test + public void testBasicGeneric() throws IOException { + Optional type = new MCFullGenericTypesTestParser().parse_StringMCBasicGenericType("de.util.Pair"); + assertTrue(type.isPresent()); + SymTypeExpression listSymTypeExpression = mcType2TypeExpression(type.get()); + assertTrue(listSymTypeExpression instanceof SymTypeOfGenerics); + assertEquals("de.util.Pair", ((SymTypeOfGenerics) listSymTypeExpression).getTypeConstructorFullName()); + SymTypeExpression keyTypeArgument = ((SymTypeOfGenerics) listSymTypeExpression).getArgumentList().get(0); + assertTrue(keyTypeArgument instanceof SymTypeOfObject); + assertEquals("de.mc.PairA", keyTypeArgument.printFullName()); + + SymTypeExpression valueTypeArgument = ((SymTypeOfGenerics) listSymTypeExpression).getArgumentList().get(1); + assertTrue(valueTypeArgument instanceof SymTypeOfObject); + assertEquals("de.mc.PairB", valueTypeArgument.printFullName()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testBasicGenericRekursiv() throws IOException { + Optional type = new MCFullGenericTypesTestParser().parse_StringMCBasicGenericType("de.util.Pair>"); + assertTrue(type.isPresent()); + SymTypeExpression listSymTypeExpression = mcType2TypeExpression(type.get()); + assertTrue(listSymTypeExpression instanceof SymTypeOfGenerics); + assertEquals("de.util.Pair", ((SymTypeOfGenerics) listSymTypeExpression).getTypeConstructorFullName()); + SymTypeExpression keyTypeArgument = ((SymTypeOfGenerics) listSymTypeExpression).getArgumentList().get(0); + assertTrue(keyTypeArgument instanceof SymTypeOfObject); + assertEquals("de.mc.PairA", keyTypeArgument.printFullName()); + + SymTypeExpression valueTypeArgument = ((SymTypeOfGenerics) listSymTypeExpression).getArgumentList().get(1); + assertTrue(valueTypeArgument instanceof SymTypeOfGenerics); + assertEquals("de.util.Pair2", ((SymTypeOfGenerics) valueTypeArgument).getTypeConstructorFullName()); + + SymTypeOfGenerics valueTypeArg = (SymTypeOfGenerics) valueTypeArgument; + + SymTypeExpression argument1 = valueTypeArg.getArgumentList().get(0); + assertEquals("de.mc.PairB", argument1.printFullName()); + + SymTypeExpression argument2 = valueTypeArg.getArgumentList().get(1); + assertEquals("de.mc.PairC", argument2.printFullName()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMap() throws IOException { + Optional type = new MCCollectionTypesTestParser().parse_StringMCMapType("Map"); + assertTrue(type.isPresent()); + SymTypeExpression listSymTypeExpression = mcType2TypeExpression(type.get()); + assertTrue(listSymTypeExpression instanceof SymTypeOfGenerics); + assertEquals("Map", listSymTypeExpression.printFullName()); + SymTypeExpression keyTypeArgument = ((SymTypeOfGenerics) listSymTypeExpression).getArgumentList().get(0); + assertTrue(keyTypeArgument instanceof SymTypeOfObject); + assertEquals("de.mc.PersonKey", keyTypeArgument.printFullName()); + + SymTypeExpression valueTypeArgument = ((SymTypeOfGenerics) listSymTypeExpression).getArgumentList().get(1); + assertTrue(valueTypeArgument instanceof SymTypeOfObject); + assertEquals("de.mc.PersonValue", valueTypeArgument.printFullName()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMapUnqualified() throws IOException { + Optional type = new MCCollectionTypesTestParser().parse_StringMCMapType("Map"); + assertTrue(type.isPresent()); + SymTypeExpression listSymTypeExpression = mcType2TypeExpression(type.get()); + assertTrue(listSymTypeExpression instanceof SymTypeOfGenerics); + assertEquals("Map", listSymTypeExpression.printFullName()); + SymTypeExpression keyTypeArgument = ((SymTypeOfGenerics) listSymTypeExpression).getArgumentList().get(0); + assertTrue(keyTypeArgument instanceof SymTypeOfObject); + assertEquals("PersonKey", keyTypeArgument.printFullName()); + + SymTypeExpression valueTypeArgument = ((SymTypeOfGenerics) listSymTypeExpression).getArgumentList().get(1); + assertTrue(valueTypeArgument instanceof SymTypeOfObject); + assertEquals("PersonValue", valueTypeArgument.printFullName()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMapPrimitives() throws IOException { + for (String primitiveKey : primitiveTypes) { + for (String primitiveValue : primitiveTypes) { + Optional type = new MCCollectionTypesTestParser().parse_StringMCMapType("Map<" + primitiveKey + "," + primitiveValue + ">"); + assertTrue(type.isPresent()); + SymTypeExpression listSymTypeExpression = mcType2TypeExpression(type.get()); + assertTrue(listSymTypeExpression instanceof SymTypeOfGenerics); + assertEquals(("Map<" + primitiveKey + "," + primitiveValue + ">"), listSymTypeExpression.printFullName()); + + SymTypeExpression keyTypeArgument = ((SymTypeOfGenerics) listSymTypeExpression).getArgumentList().get(0); + assertTrue(keyTypeArgument instanceof SymTypePrimitive); + assertEquals(primitiveKey, keyTypeArgument.printFullName()); + + SymTypeExpression valueTypeArgument = ((SymTypeOfGenerics) listSymTypeExpression).getArgumentList().get(1); + assertTrue(valueTypeArgument instanceof SymTypePrimitive); + assertEquals(primitiveValue, valueTypeArgument.printFullName()); + } + } + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testOptional() throws IOException { + Optional type = new MCCollectionTypesTestParser().parse_StringMCOptionalType("Optional"); + assertTrue(type.isPresent()); + SymTypeExpression optSymTypeExpression = mcType2TypeExpression(type.get()); + assertTrue(optSymTypeExpression instanceof SymTypeOfGenerics); + assertEquals("Optional", ((SymTypeOfGenerics) optSymTypeExpression).getTypeConstructorFullName()); + SymTypeExpression listTypeArgument = ((SymTypeOfGenerics) optSymTypeExpression).getArgumentList().get(0); + assertTrue(listTypeArgument instanceof SymTypeOfObject); + assertEquals("de.mc.Person", listTypeArgument.printFullName()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testOptionalUnqualified() throws IOException { + Optional type = new MCCollectionTypesTestParser().parse_StringMCOptionalType("Optional"); + assertTrue(type.isPresent()); + SymTypeExpression optSymTypeExpression = mcType2TypeExpression(type.get()); + assertTrue(optSymTypeExpression instanceof SymTypeOfGenerics); + assertEquals("Optional", ((SymTypeOfGenerics) optSymTypeExpression).getTypeConstructorFullName()); + SymTypeExpression listTypeArgument = ((SymTypeOfGenerics) optSymTypeExpression).getArgumentList().get(0); + assertTrue(listTypeArgument instanceof SymTypeOfObject); + assertEquals("Person", listTypeArgument.printFullName()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testOptionalPrimitive() throws IOException { + for (String primitive : primitiveTypes) { + Optional type = new MCCollectionTypesTestParser().parse_StringMCOptionalType("Optional<" + primitive + ">"); + assertTrue(type.isPresent()); + SymTypeExpression optSymTypeExpression = mcType2TypeExpression(type.get()); + assertTrue(optSymTypeExpression instanceof SymTypeOfGenerics); + assertEquals("Optional", ((SymTypeOfGenerics) optSymTypeExpression).getTypeConstructorFullName()); + SymTypeExpression listTypeArgument = ((SymTypeOfGenerics) optSymTypeExpression).getArgumentList().get(0); + assertTrue(listTypeArgument instanceof SymTypePrimitive); + assertEquals(primitive, listTypeArgument.printFullName()); + } + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void testSet() throws IOException { + Optional type = new MCCollectionTypesTestParser().parse_StringMCSetType("Set"); + assertTrue(type.isPresent()); + SymTypeExpression setSymTypeExpression = mcType2TypeExpression(type.get()); + assertTrue(setSymTypeExpression instanceof SymTypeOfGenerics); + assertEquals("Set", ((SymTypeOfGenerics) setSymTypeExpression).getTypeConstructorFullName()); + SymTypeExpression listTypeArgument = ((SymTypeOfGenerics) setSymTypeExpression).getArgumentList().get(0); + assertTrue(listTypeArgument instanceof SymTypeOfObject); + assertEquals("de.mc.Person", listTypeArgument.printFullName()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testSetUnqualified() throws IOException { + Optional type = new MCCollectionTypesTestParser().parse_StringMCSetType("Set"); + assertTrue(type.isPresent()); + SymTypeExpression setSymTypeExpression = mcType2TypeExpression(type.get()); + assertTrue(setSymTypeExpression instanceof SymTypeOfGenerics); + assertEquals("Set", ((SymTypeOfGenerics) setSymTypeExpression).getTypeConstructorFullName()); + SymTypeExpression listTypeArgument = ((SymTypeOfGenerics) setSymTypeExpression).getArgumentList().get(0); + assertTrue(listTypeArgument instanceof SymTypeOfObject); + assertEquals("Person", listTypeArgument.printFullName()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testSetPrimitives() throws IOException { + for (String primitive : primitiveTypes) { + Optional type = new MCCollectionTypesTestParser().parse_StringMCSetType("Set<" + primitive + ">"); + assertTrue(type.isPresent()); + SymTypeExpression setSymTypeExpression = + mcType2TypeExpression(type.get()); + assertTrue(setSymTypeExpression instanceof SymTypeOfGenerics); + assertEquals("Set", ((SymTypeOfGenerics) setSymTypeExpression).getTypeConstructorFullName()); + SymTypeExpression listTypeArgument = ((SymTypeOfGenerics) setSymTypeExpression).getArgumentList().get(0); + assertTrue(listTypeArgument instanceof SymTypePrimitive); + assertEquals(primitive, listTypeArgument.printFullName()); + } + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testList() throws IOException { + Optional type = new MCCollectionTypesTestParser().parse_StringMCListType("List"); + assertTrue(type.isPresent()); + SymTypeExpression listSymTypeExpression = mcType2TypeExpression(type.get()); + assertTrue(listSymTypeExpression instanceof SymTypeOfGenerics); + assertEquals("List", ((SymTypeOfGenerics) listSymTypeExpression).getTypeConstructorFullName()); + SymTypeExpression listTypeArgument = ((SymTypeOfGenerics) listSymTypeExpression).getArgumentList().get(0); + assertTrue(listTypeArgument instanceof SymTypeOfObject); + assertEquals("de.mc.Person", listTypeArgument.printFullName()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testListUnqualified() throws IOException { + Optional type = new MCCollectionTypesTestParser().parse_StringMCListType("List"); + assertTrue(type.isPresent()); + SymTypeExpression listSymTypeExpression = mcType2TypeExpression(type.get()); + assertTrue(listSymTypeExpression instanceof SymTypeOfGenerics); + assertEquals("List", ((SymTypeOfGenerics) listSymTypeExpression).getTypeConstructorFullName()); + SymTypeExpression listTypeArgument = ((SymTypeOfGenerics) listSymTypeExpression).getArgumentList().get(0); + assertTrue(listTypeArgument instanceof SymTypeOfObject); + assertEquals("Person", listTypeArgument.printFullName()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testListPrimitive() throws IOException { + for (String primitive : primitiveTypes) { + Optional type = new MCCollectionTypesTestParser().parse_StringMCListType("List<" + primitive + ">"); + assertTrue(type.isPresent()); + SymTypeExpression listSymTypeExpression = mcType2TypeExpression(type.get()); + assertTrue(listSymTypeExpression instanceof SymTypeOfGenerics); + assertEquals("List", ((SymTypeOfGenerics) listSymTypeExpression).getTypeConstructorFullName()); + SymTypeExpression listTypeArgument = ((SymTypeOfGenerics) listSymTypeExpression).getArgumentList().get(0); + assertTrue(listTypeArgument instanceof SymTypePrimitive); + assertEquals(primitive, listTypeArgument.printFullName()); + } + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void testPrimitives() throws IOException { + for (String primitive : primitiveTypes) { + Optional type = new MCCollectionTypesTestParser().parse_StringMCPrimitiveType(primitive); + assertTrue(type.isPresent()); + ASTMCPrimitiveType booleanType = type.get(); + SymTypeExpression symTypeExpression = mcType2TypeExpression(booleanType); + assertTrue(symTypeExpression instanceof SymTypePrimitive); + assertEquals(primitive, symTypeExpression.printFullName()); + } + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testVoid() throws IOException { + Optional type = new MCCollectionTypesTestParser().parse_StringMCVoidType("void"); + assertTrue(type.isPresent()); + ASTMCVoidType booleanType = type.get(); + SymTypeExpression symTypeExpression = mcType2TypeExpression(booleanType); + assertTrue(symTypeExpression instanceof SymTypeVoid); + assertEquals("void", symTypeExpression.printFullName()); + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void testQualifiedType() throws IOException { + Optional type = new MCCollectionTypesTestParser().parse_StringMCQualifiedType("de.mc.Person"); + assertTrue(type.isPresent()); + ASTMCQualifiedType qualifiedType = type.get(); + SymTypeExpression symTypeExpression = mcType2TypeExpression(qualifiedType); + assertTrue(symTypeExpression instanceof SymTypeOfObject); + assertEquals("de.mc.Person", symTypeExpression.printFullName()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testQualifiedTypeUnqualified() throws IOException { + Optional type = new MCCollectionTypesTestParser().parse_StringMCQualifiedType("Person"); + assertTrue(type.isPresent()); + ASTMCQualifiedType qualifiedType = type.get(); + SymTypeExpression symTypeExpression = mcType2TypeExpression(qualifiedType); + assertTrue(symTypeExpression instanceof SymTypeOfObject); + assertEquals("Person", symTypeExpression.printFullName()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testQualifiedName() throws IOException { + Optional type = new MCCollectionTypesTestParser().parse_StringMCQualifiedName("de.mc.Person"); + assertTrue(type.isPresent()); + ASTMCQualifiedName qualifiedName = type.get(); + SymTypeExpression symTypeExpression = mcType2TypeExpression(qualifiedName); + assertTrue(symTypeExpression instanceof SymTypeOfObject); + assertEquals("de.mc.Person", symTypeExpression.printFullName()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testQualifiedNameUnqualified() throws IOException { + Optional type = new MCCollectionTypesTestParser().parse_StringMCQualifiedName("Person"); + assertTrue(type.isPresent()); + ASTMCQualifiedName qualifiedName = type.get(); + SymTypeExpression symTypeExpression = mcType2TypeExpression(qualifiedName); + assertTrue(symTypeExpression instanceof SymTypeOfObject); + assertEquals("Person", symTypeExpression.printFullName()); + + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/mccollectiontypes/types3/util/MCCollectionSymTypeFactoryTest.java b/monticore-grammar/src/test/java/de/monticore/types/mccollectiontypes/types3/util/MCCollectionSymTypeFactoryTest.java new file mode 100644 index 0000000000..ac01d17615 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/mccollectiontypes/types3/util/MCCollectionSymTypeFactoryTest.java @@ -0,0 +1,140 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.types.mccollectiontypes.types3.util; + +import de.monticore.expressions.combineexpressionswithliterals.CombineExpressionsWithLiteralsMill; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.IBasicSymbolsGlobalScope; +import de.monticore.types.check.SymTypeOfGenerics; +import de.monticore.types3.AbstractTypeTest; +import de.monticore.types3.util.DefsTypesForTests; +import org.junit.Before; +import org.junit.Test; + +import static de.monticore.types3.util.DefsTypesForTests._boxedListSymType; +import static de.monticore.types3.util.DefsTypesForTests._boxedMapSymType; +import static de.monticore.types3.util.DefsTypesForTests._boxedOptionalSymType; +import static de.monticore.types3.util.DefsTypesForTests._boxedSetSymType; +import static de.monticore.types3.util.DefsTypesForTests._floatSymType; +import static de.monticore.types3.util.DefsTypesForTests._intSymType; +import static de.monticore.types3.util.DefsTypesForTests._unboxedListSymType; +import static de.monticore.types3.util.DefsTypesForTests._unboxedMapSymType; +import static de.monticore.types3.util.DefsTypesForTests._unboxedOptionalSymType; +import static de.monticore.types3.util.DefsTypesForTests._unboxedSetSymType; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +public class MCCollectionSymTypeFactoryTest extends AbstractTypeTest { + + MCCollectionTypeRelations collectionTypeRelations; + + @Before + public void setup() { + BasicSymbolsMill.reset(); + BasicSymbolsMill.init(); + // make collection types available in unboxed AND boxed form + DefsTypesForTests.setup(); + collectionTypeRelations = new MCCollectionTypeRelations(); + } + + @Test + public void createsList() { + IBasicSymbolsGlobalScope gs = BasicSymbolsMill.globalScope(); + + SymTypeOfGenerics intList = MCCollectionSymTypeFactory.createList(_intSymType); + assertTrue(intList.hasTypeInfo()); + assertSame(_unboxedListSymType.getTypeInfo(), intList.getTypeInfo()); + assertEquals("List", intList.getTypeConstructorFullName()); + assertEquals(1, intList.sizeArguments()); + assertTrue(_intSymType.deepEquals(intList.getArgument(0))); + assertTrue(getCollectionTypeRelations().isList(intList)); + + // again, but with the unboxed "List" not being available + gs.remove(gs.resolveType("List").get()); + intList = MCCollectionSymTypeFactory.createList(_intSymType); + assertTrue(intList.hasTypeInfo()); + assertSame(_boxedListSymType.getTypeInfo(), intList.getTypeInfo()); + assertEquals("java.util.List", intList.getTypeConstructorFullName()); + assertEquals(1, intList.sizeArguments()); + assertTrue(_intSymType.deepEquals(intList.getArgument(0))); + assertTrue(getCollectionTypeRelations().isList(intList)); + } + + @Test + public void createsSet() { + IBasicSymbolsGlobalScope gs = BasicSymbolsMill.globalScope(); + + SymTypeOfGenerics intSet = MCCollectionSymTypeFactory.createSet(_intSymType); + assertTrue(intSet.hasTypeInfo()); + assertSame(_unboxedSetSymType.getTypeInfo(), intSet.getTypeInfo()); + assertEquals("Set", intSet.getTypeConstructorFullName()); + assertEquals(1, intSet.sizeArguments()); + assertTrue(_intSymType.deepEquals(intSet.getArgument(0))); + assertTrue(getCollectionTypeRelations().isSet(intSet)); + + // again, but with the unboxed "Set" not being available + gs.remove(gs.resolveType("Set").get()); + intSet = MCCollectionSymTypeFactory.createSet(_intSymType); + assertTrue(intSet.hasTypeInfo()); + assertSame(_boxedSetSymType.getTypeInfo(), intSet.getTypeInfo()); + assertEquals("java.util.Set", intSet.getTypeConstructorFullName()); + assertEquals(1, intSet.sizeArguments()); + assertTrue(_intSymType.deepEquals(intSet.getArgument(0))); + assertTrue(getCollectionTypeRelations().isSet(intSet)); + } + + @Test + public void createsOptional() { + IBasicSymbolsGlobalScope gs = BasicSymbolsMill.globalScope(); + + SymTypeOfGenerics intOptional = MCCollectionSymTypeFactory.createOptional(_intSymType); + assertTrue(intOptional.hasTypeInfo()); + assertSame(_unboxedOptionalSymType.getTypeInfo(), intOptional.getTypeInfo()); + assertEquals("Optional", intOptional.getTypeConstructorFullName()); + assertEquals(1, intOptional.sizeArguments()); + assertTrue(_intSymType.deepEquals(intOptional.getArgument(0))); + assertTrue(getCollectionTypeRelations().isOptional(intOptional)); + + // again, but with the unboxed "Optional" not being available + gs.remove(gs.resolveType("Optional").get()); + intOptional = MCCollectionSymTypeFactory.createOptional(_intSymType); + assertTrue(intOptional.hasTypeInfo()); + assertSame(_boxedOptionalSymType.getTypeInfo(), intOptional.getTypeInfo()); + assertEquals("java.util.Optional", intOptional.getTypeConstructorFullName()); + assertEquals(1, intOptional.sizeArguments()); + assertTrue(_intSymType.deepEquals(intOptional.getArgument(0))); + assertTrue(getCollectionTypeRelations().isOptional(intOptional)); + } + + @Test + public void createsMap() { + IBasicSymbolsGlobalScope gs = BasicSymbolsMill.globalScope(); + + SymTypeOfGenerics intMap = + MCCollectionSymTypeFactory.createMap(_intSymType, _floatSymType); + assertTrue(intMap.hasTypeInfo()); + assertSame(_unboxedMapSymType.getTypeInfo(), intMap.getTypeInfo()); + assertEquals("Map", intMap.getTypeConstructorFullName()); + assertEquals(2, intMap.sizeArguments()); + assertTrue(_intSymType.deepEquals(intMap.getArgument(0))); + assertTrue(_floatSymType.deepEquals(intMap.getArgument(1))); + assertTrue(getCollectionTypeRelations().isMap(intMap)); + + // again, but with the unboxed "Map" not being available + gs.remove(gs.resolveType("Map").get()); + intMap = MCCollectionSymTypeFactory.createMap(_intSymType, _floatSymType); + assertTrue(intMap.hasTypeInfo()); + assertSame(_boxedMapSymType.getTypeInfo(), intMap.getTypeInfo()); + assertEquals("java.util.Map", intMap.getTypeConstructorFullName()); + assertEquals(2, intMap.sizeArguments()); + assertTrue(_intSymType.deepEquals(intMap.getArgument(0))); + assertTrue(_floatSymType.deepEquals(intMap.getArgument(1))); + assertTrue(getCollectionTypeRelations().isMap(intMap)); + } + + // Helper + + protected MCCollectionTypeRelations getCollectionTypeRelations() { + return collectionTypeRelations; + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/mccollectiontypes/types3/util/MCCollectionTypeRelationsTest.java b/monticore-grammar/src/test/java/de/monticore/types/mccollectiontypes/types3/util/MCCollectionTypeRelationsTest.java new file mode 100644 index 0000000000..587bc1b522 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/mccollectiontypes/types3/util/MCCollectionTypeRelationsTest.java @@ -0,0 +1,207 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.types.mccollectiontypes.types3.util; + +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types.mccollectiontypes.types3.IMCCollectionTypeRelations; +import de.monticore.types3.AbstractTypeTest; +import de.monticore.types3.util.DefsTypesForTests; +import org.junit.Before; +import org.junit.Test; + +import java.util.Collections; +import java.util.List; + +import static de.monticore.types3.util.DefsTypesForTests._boxedListSymType; +import static de.monticore.types3.util.DefsTypesForTests._boxedMapSymType; +import static de.monticore.types3.util.DefsTypesForTests._boxedOptionalSymType; +import static de.monticore.types3.util.DefsTypesForTests._boxedSetSymType; +import static de.monticore.types3.util.DefsTypesForTests._intSymType; +import static de.monticore.types3.util.DefsTypesForTests._personSymType; +import static de.monticore.types3.util.DefsTypesForTests._unboxedListSymType; +import static de.monticore.types3.util.DefsTypesForTests._unboxedMapSymType; +import static de.monticore.types3.util.DefsTypesForTests._unboxedOptionalSymType; +import static de.monticore.types3.util.DefsTypesForTests._unboxedSetSymType; +import static de.monticore.types3.util.DefsTypesForTests._unboxedString; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +public class MCCollectionTypeRelationsTest extends AbstractTypeTest { + + @Before + public void init() { + DefsTypesForTests.setup(); + } + + @Test + public void recognizeUnboxedCollectionTypes() { + assertTrue(getRel().isCollection(_unboxedListSymType)); + assertTrue(getRel().isCollection(_unboxedSetSymType)); + assertTrue(getRel().isCollection(_unboxedOptionalSymType)); + assertTrue(getRel().isCollection(_unboxedMapSymType)); + } + + @Test + public void recognizeBoxedCollectionTypes() { + assertTrue(getRel().isCollection(_boxedListSymType)); + assertTrue(getRel().isCollection(_boxedSetSymType)); + assertTrue(getRel().isCollection(_boxedOptionalSymType)); + assertTrue(getRel().isCollection(_boxedMapSymType)); + } + + @Test + public void recognizeNonCollectionTypes() { + assertFalse(getRel().isCollection(_intSymType)); + assertFalse(getRel().isCollection(_personSymType)); + assertFalse(getRel().isCollection(_unboxedString)); + assertFalse(getRel().isCollection(SymTypeExpressionFactory.createGenerics( + "noList", BasicSymbolsMill.scope(), _intSymType + ))); + } + + @Test + public void recognizeLists() { + assertTrue(getRel().isList(_unboxedListSymType)); + assertTrue(getRel().isList(_boxedListSymType)); + } + + @Test + public void recognizeNonLists() { + assertFalse(getRel().isList(_unboxedMapSymType)); + assertFalse(getRel().isList(_unboxedSetSymType)); + assertFalse(getRel().isList(_unboxedOptionalSymType)); + assertFalse(getRel().isList(_boxedMapSymType)); + assertFalse(getRel().isList(_boxedSetSymType)); + assertFalse(getRel().isList(_boxedOptionalSymType)); + + // incorrect number of arguments + _unboxedListSymType.setArgumentList(Collections.emptyList()); + _boxedListSymType.setArgumentList(Collections.emptyList()); + assertFalse(getRel().isList(_unboxedListSymType)); + assertFalse(getRel().isList(_boxedListSymType)); + _unboxedListSymType.setArgumentList(List.of(_intSymType, _intSymType)); + _boxedListSymType.setArgumentList(List.of(_intSymType, _intSymType)); + assertFalse(getRel().isList(_unboxedListSymType)); + assertFalse(getRel().isList(_boxedListSymType)); + } + + @Test + public void recognizeSets() { + assertTrue(getRel().isSet(_unboxedSetSymType)); + assertTrue(getRel().isSet(_boxedSetSymType)); + } + + @Test + public void recognizeNonSets() { + assertFalse(getRel().isSet(_unboxedMapSymType)); + assertFalse(getRel().isSet(_unboxedListSymType)); + assertFalse(getRel().isSet(_unboxedOptionalSymType)); + assertFalse(getRel().isSet(_boxedMapSymType)); + assertFalse(getRel().isSet(_boxedListSymType)); + assertFalse(getRel().isSet(_boxedOptionalSymType)); + + // incorrect number of arguments + _unboxedSetSymType.setArgumentList(Collections.emptyList()); + _boxedSetSymType.setArgumentList(Collections.emptyList()); + assertFalse(getRel().isSet(_unboxedSetSymType)); + assertFalse(getRel().isSet(_boxedSetSymType)); + _unboxedSetSymType.setArgumentList(List.of(_intSymType, _intSymType)); + _boxedSetSymType.setArgumentList(List.of(_intSymType, _intSymType)); + assertFalse(getRel().isSet(_unboxedSetSymType)); + assertFalse(getRel().isSet(_boxedSetSymType)); + } + + @Test + public void recognizeOptionals() { + assertTrue(getRel().isOptional(_unboxedOptionalSymType)); + assertTrue(getRel().isOptional(_boxedOptionalSymType)); + } + + @Test + public void recognizeNonOptionals() { + assertFalse(getRel().isOptional(_unboxedMapSymType)); + assertFalse(getRel().isOptional(_unboxedListSymType)); + assertFalse(getRel().isOptional(_unboxedSetSymType)); + assertFalse(getRel().isOptional(_boxedMapSymType)); + assertFalse(getRel().isOptional(_boxedListSymType)); + assertFalse(getRel().isOptional(_boxedSetSymType)); + + // incorrect number of arguments + _unboxedOptionalSymType.setArgumentList(Collections.emptyList()); + _boxedOptionalSymType.setArgumentList(Collections.emptyList()); + assertFalse(getRel().isOptional(_unboxedOptionalSymType)); + assertFalse(getRel().isOptional(_boxedOptionalSymType)); + _unboxedOptionalSymType.setArgumentList(List.of(_intSymType, _intSymType)); + _boxedOptionalSymType.setArgumentList(List.of(_intSymType, _intSymType)); + assertFalse(getRel().isOptional(_unboxedOptionalSymType)); + assertFalse(getRel().isOptional(_boxedOptionalSymType)); + } + + @Test + public void recognizeMaps() { + assertTrue(getRel().isMap(_unboxedMapSymType)); + assertTrue(getRel().isMap(_boxedMapSymType)); + } + + @Test + public void recognizeNonMaps() { + assertFalse(getRel().isMap(_unboxedListSymType)); + assertFalse(getRel().isMap(_unboxedSetSymType)); + assertFalse(getRel().isMap(_unboxedOptionalSymType)); + assertFalse(getRel().isMap(_boxedListSymType)); + assertFalse(getRel().isMap(_boxedSetSymType)); + assertFalse(getRel().isMap(_boxedOptionalSymType)); + + // incorrect number of arguments + _unboxedMapSymType.setArgumentList(Collections.emptyList()); + _boxedMapSymType.setArgumentList(Collections.emptyList()); + assertFalse(getRel().isMap(_unboxedMapSymType)); + assertFalse(getRel().isMap(_boxedMapSymType)); + _unboxedMapSymType.setArgumentList(List.of(_intSymType)); + _boxedMapSymType.setArgumentList(List.of(_intSymType)); + assertFalse(getRel().isMap(_unboxedMapSymType)); + assertFalse(getRel().isMap(_boxedMapSymType)); + _unboxedMapSymType.setArgumentList( + List.of(_intSymType, _intSymType, _intSymType)); + _boxedMapSymType.setArgumentList( + List.of(_intSymType, _intSymType, _intSymType)); + assertFalse(getRel().isMap(_unboxedMapSymType)); + assertFalse(getRel().isMap(_boxedMapSymType)); + } + + @Test + public void getCollectionElementTypeTest() { + assertSame(_unboxedListSymType.getArgument(0), + getRel().getCollectionElementType(_unboxedListSymType)); + assertSame(_unboxedSetSymType.getArgument(0), + getRel().getCollectionElementType(_unboxedSetSymType)); + assertSame(_unboxedOptionalSymType.getArgument(0), + getRel().getCollectionElementType(_unboxedOptionalSymType)); + assertSame(_unboxedMapSymType.getArgument(1), + getRel().getCollectionElementType(_unboxedMapSymType)); + assertSame(_boxedListSymType.getArgument(0), + getRel().getCollectionElementType(_boxedListSymType)); + assertSame(_boxedSetSymType.getArgument(0), + getRel().getCollectionElementType(_boxedSetSymType)); + assertSame(_boxedOptionalSymType.getArgument(0), + getRel().getCollectionElementType(_boxedOptionalSymType)); + assertSame(_boxedMapSymType.getArgument(1), + getRel().getCollectionElementType(_boxedMapSymType)); + } + + @Test + public void getMapKeyTest() { + assertSame(_unboxedMapSymType.getArgument(0), + getRel().getMapKeyType(_unboxedMapSymType)); + assertSame(_boxedMapSymType.getArgument(0), + getRel().getMapKeyType(_boxedMapSymType)); + } + + // Helper + + protected IMCCollectionTypeRelations getRel() { + return new MCCollectionTypeRelations(); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/prettyprint/MCArrayTypesPrettyPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/types/prettyprint/MCArrayTypesPrettyPrinterTest.java new file mode 100644 index 0000000000..81169ac08b --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/prettyprint/MCArrayTypesPrettyPrinterTest.java @@ -0,0 +1,52 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.prettyprint; + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.types.mcarraytypes.MCArrayTypesMill; +import de.monticore.types.mcarraytypes._ast.ASTMCArrayType; +import de.monticore.types.mcarraytypes._prettyprint.MCArrayTypesFullPrettyPrinter; +import de.monticore.types.mcarraytypestest.MCArrayTypesTestMill; +import de.monticore.types.mcarraytypestest._parser.MCArrayTypesTestParser; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.mcfullgenerictypestest._parser.MCFullGenericTypesTestParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class MCArrayTypesPrettyPrinterTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + MCArrayTypesTestMill.reset(); + MCArrayTypesTestMill.init(); + } + + @Test + public void testMCArrayType() throws IOException { + //have to use ASTMCType because of left recursion in ASTMCArrayType there is no parse Method + MCArrayTypesTestParser parser = new MCArrayTypesTestParser(); + Optional ast = parser.parse_StringMCType("String[][]"); + assertTrue(ast.isPresent()); + assertFalse(parser.hasErrors()); + assertTrue(ast.get() instanceof ASTMCArrayType); + ASTMCArrayType type = (ASTMCArrayType) ast.get(); + MCArrayTypesFullPrettyPrinter printer = new MCArrayTypesFullPrettyPrinter(new IndentPrinter()); + String output = printer.prettyprint(type); + ast = parser.parse_StringMCType(output); + assertFalse(parser.hasErrors()); + assertTrue(ast.isPresent()); + assertTrue(type.deepEquals(ast.get())); + + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/prettyprint/MCBasicTypesPrettyPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/types/prettyprint/MCBasicTypesPrettyPrinterTest.java new file mode 100644 index 0000000000..694aa55fe4 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/prettyprint/MCBasicTypesPrettyPrinterTest.java @@ -0,0 +1,210 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.prettyprint; + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.types.mcbasictypes.MCBasicTypesMill; +import de.monticore.types.mcbasictypes._prettyprint.MCBasicTypesFullPrettyPrinter; +import de.monticore.types.mcbasictypes._ast.*; +import de.monticore.types.mcbasictypestest.MCBasicTypesTestMill; +import de.monticore.types.mcbasictypestest._parser.MCBasicTypesTestParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.*; + +public class MCBasicTypesPrettyPrinterTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + MCBasicTypesTestMill.reset(); + MCBasicTypesTestMill.init(); + } + + + + @Test + public void testMCQualifiedName() throws IOException { + MCBasicTypesTestParser parser = new MCBasicTypesTestParser(); + Optional ast = parser.parse_StringMCQualifiedName("Name1.Name2.Name3"); + assertTrue(ast.isPresent()); + assertFalse(parser.hasErrors()); + ASTMCQualifiedName qualifiedName = ast.get(); + MCBasicTypesFullPrettyPrinter printer = new MCBasicTypesFullPrettyPrinter(new IndentPrinter()); + String output = printer.prettyprint(ast.get()); + ast = parser.parse_StringMCQualifiedName(output); + assertFalse(parser.hasErrors()); + assertTrue(ast.isPresent()); + assertTrue(qualifiedName.deepEquals(ast.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMcImportStatement() throws IOException { + MCBasicTypesTestParser parser = new MCBasicTypesTestParser(); + Optional ast = parser.parse_StringMCImportStatement("import de.monticore.types.*;"); + assertTrue(ast.isPresent()); + assertFalse(parser.hasErrors()); + ASTMCImportStatement importStatement = ast.get(); + MCBasicTypesFullPrettyPrinter printer = new MCBasicTypesFullPrettyPrinter(new IndentPrinter()); + String output = printer.prettyprint(ast.get()); + ast = parser.parse_StringMCImportStatement(output); + assertFalse(parser.hasErrors()); + assertTrue(ast.isPresent()); + assertTrue(importStatement.deepEquals(ast.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMcPrimitiveType() throws IOException { + MCBasicTypesTestParser parser = new MCBasicTypesTestParser(); + Optional ast = parser.parse_StringMCPrimitiveType("long"); + assertTrue(ast.isPresent()); + assertFalse(parser.hasErrors()); + ASTMCPrimitiveType primitiveType = ast.get(); + MCBasicTypesFullPrettyPrinter printer = new MCBasicTypesFullPrettyPrinter(new IndentPrinter()); + String output = printer.prettyprint(ast.get()); + ast = parser.parse_StringMCPrimitiveType(output); + assertFalse(parser.hasErrors()); + assertTrue(ast.isPresent()); + assertTrue(primitiveType.deepEquals(ast.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCVoidType() throws IOException { + MCBasicTypesTestParser parser = new MCBasicTypesTestParser(); + Optional ast = parser.parse_StringMCVoidType("void"); + assertTrue(ast.isPresent()); + assertFalse(parser.hasErrors()); + ASTMCVoidType voidType = ast.get(); + MCBasicTypesFullPrettyPrinter printer = new MCBasicTypesFullPrettyPrinter(new IndentPrinter()); + String output = printer.prettyprint(ast.get()); + ast = parser.parse_StringMCVoidType(output); + assertFalse(parser.hasErrors()); + assertTrue(ast.isPresent()); + assertTrue(voidType.deepEquals(ast.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCReturnTypeVoid() throws IOException { + MCBasicTypesTestParser parser = new MCBasicTypesTestParser(); + Optional ast = parser.parse_StringMCReturnType("void"); + assertTrue(ast.isPresent()); + assertFalse(parser.hasErrors()); + ASTMCReturnType voidType = ast.get(); + MCBasicTypesFullPrettyPrinter printer = new MCBasicTypesFullPrettyPrinter(new IndentPrinter()); + String output = printer.prettyprint(ast.get()); + ast = parser.parse_StringMCReturnType(output); + assertFalse(parser.hasErrors()); + assertTrue(ast.isPresent()); + assertTrue(voidType.deepEquals(ast.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCReturnType() throws IOException { + MCBasicTypesTestParser parser = new MCBasicTypesTestParser(); + Optional ast = parser.parse_StringMCReturnType("boolean"); + assertTrue(ast.isPresent()); + assertFalse(parser.hasErrors()); + ASTMCReturnType voidType = ast.get(); + MCBasicTypesFullPrettyPrinter printer = new MCBasicTypesFullPrettyPrinter(new IndentPrinter()); + String output = printer.prettyprint(ast.get()); + ast = parser.parse_StringMCReturnType(output); + assertFalse(parser.hasErrors()); + assertTrue(ast.isPresent()); + assertTrue(voidType.deepEquals(ast.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCQualifiedType() throws IOException { + MCBasicTypesTestParser parser = new MCBasicTypesTestParser(); + Optional ast = parser.parse_StringMCQualifiedType("a.b.c.d"); + assertTrue(ast.isPresent()); + assertFalse(parser.hasErrors()); + ASTMCQualifiedType qualifiedReferenceType = ast.get(); + MCBasicTypesFullPrettyPrinter printer = new MCBasicTypesFullPrettyPrinter(new IndentPrinter()); + String output = printer.prettyprint(ast.get()); + ast = parser.parse_StringMCQualifiedType(output); + assertFalse(parser.hasErrors()); + assertTrue(ast.isPresent()); + assertTrue(qualifiedReferenceType.deepEquals(ast.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCPackageDeclaration() throws IOException { + MCBasicTypesTestParser parser = MCBasicTypesTestMill.parser(); + Optional ast = parser.parse_StringMCPackageDeclaration("package a.b.c.d;"); + assertTrue(ast.isPresent()); + assertFalse(parser.hasErrors()); + ASTMCPackageDeclaration packageDeclaration = ast.get(); + MCBasicTypesFullPrettyPrinter printer = new MCBasicTypesFullPrettyPrinter(new IndentPrinter()); + String output = printer.prettyprint(ast.get()); + ast = parser.parse_StringMCPackageDeclaration(output); + assertFalse(parser.hasErrors()); + assertTrue(ast.isPresent()); + assertTrue(packageDeclaration.deepEquals(ast.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void primitivesTest(){ + Class foo = boolean.class; + MCBasicTypesFullPrettyPrinter prettyprinter = new MCBasicTypesFullPrettyPrinter(new IndentPrinter()); + + String[] primitives = new String[]{"boolean", "byte", "char", "short", "int", "long", + "float", "double"}; + try { + for (String primitive : primitives) { + prettyprinter.getPrinter().clearBuffer(); + MCBasicTypesTestParser mcBasicTypesParser = new MCBasicTypesTestParser(); + // .parseType(primitive); + + Optional type = mcBasicTypesParser.parse_StringMCPrimitiveType(primitive); + assertTrue(type.isPresent()); + assertEquals(primitive,prettyprinter.prettyprint(type.get())); + assertTrue(type.get() instanceof ASTMCPrimitiveType); + } + } catch (IOException e) { + e.printStackTrace(); + } + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void simpleReferenceTest(){ + MCBasicTypesFullPrettyPrinter prettyprinter = new MCBasicTypesFullPrettyPrinter(new IndentPrinter()); + String simpleReference = "de.monticore.types.prettyprint"; + try{ + MCBasicTypesTestParser mcBasicTypesParser= new MCBasicTypesTestParser(); + Optional type = mcBasicTypesParser.parse_StringMCQualifiedType(simpleReference); + assertTrue(type.isPresent()); + assertEquals(simpleReference,prettyprinter.prettyprint(type.get())); + assertTrue(type.get() instanceof ASTMCQualifiedType); + }catch(IOException e){ + e.printStackTrace(); + } + + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/prettyprint/MCCollectionTypesPrettyPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/types/prettyprint/MCCollectionTypesPrettyPrinterTest.java new file mode 100644 index 0000000000..8cfcceb9c6 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/prettyprint/MCCollectionTypesPrettyPrinterTest.java @@ -0,0 +1,131 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.prettyprint; + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.types.mccollectiontypes._ast.*; +import de.monticore.types.mccollectiontypestest.MCCollectionTypesTestMill; +import de.monticore.types.mccollectiontypestest._parser.MCCollectionTypesTestParser; +import de.monticore.types.mccollectiontypes._prettyprint.MCCollectionTypesFullPrettyPrinter; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class MCCollectionTypesPrettyPrinterTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + MCCollectionTypesTestMill.reset(); + MCCollectionTypesTestMill.init(); + } + @Test + public void testMCPrimitiveTypeArgument() throws IOException { + MCCollectionTypesTestParser parser = new MCCollectionTypesTestParser(); + Optional ast = parser.parse_StringMCPrimitiveTypeArgument("boolean"); + assertTrue(ast.isPresent()); + assertFalse(parser.hasErrors()); + ASTMCPrimitiveTypeArgument typeArgument = ast.get(); + MCCollectionTypesFullPrettyPrinter printer = new MCCollectionTypesFullPrettyPrinter(new IndentPrinter()); + String output = printer.prettyprint(ast.get()); + ast = parser.parse_StringMCPrimitiveTypeArgument(output); + assertFalse(parser.hasErrors()); + assertTrue(ast.isPresent()); + assertTrue(typeArgument.deepEquals(ast.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCBasicTypeArgument() throws IOException { + MCCollectionTypesTestParser parser = new MCCollectionTypesTestParser(); + Optional ast = parser.parse_StringMCBasicTypeArgument("a.b.c.d"); + assertTrue(ast.isPresent()); + assertFalse(parser.hasErrors()); + ASTMCBasicTypeArgument typeArgument = ast.get(); + MCCollectionTypesFullPrettyPrinter printer = new MCCollectionTypesFullPrettyPrinter(new IndentPrinter()); + String output = printer.prettyprint(ast.get()); + ast = parser.parse_StringMCBasicTypeArgument(output); + assertFalse(parser.hasErrors()); + assertTrue(ast.isPresent()); + assertTrue(typeArgument.deepEquals(ast.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCListType() throws IOException { + MCCollectionTypesTestParser parser = new MCCollectionTypesTestParser(); + Optional ast = parser.parse_StringMCListType("List"); + assertTrue(ast.isPresent()); + assertFalse(parser.hasErrors()); + ASTMCListType listType = ast.get(); + MCCollectionTypesFullPrettyPrinter printer = new MCCollectionTypesFullPrettyPrinter(new IndentPrinter()); + String output = printer.prettyprint(ast.get()); + ast = parser.parse_StringMCListType(output); + assertFalse(parser.hasErrors()); + assertTrue(ast.isPresent()); + assertTrue(listType.deepEquals(ast.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCOptionalType() throws IOException { + MCCollectionTypesTestParser parser = new MCCollectionTypesTestParser(); + Optional ast = parser.parse_StringMCOptionalType("Optional"); + assertTrue(ast.isPresent()); + assertFalse(parser.hasErrors()); + ASTMCOptionalType optionalType = ast.get(); + MCCollectionTypesFullPrettyPrinter printer = new MCCollectionTypesFullPrettyPrinter(new IndentPrinter()); + String output = printer.prettyprint(ast.get()); + ast = parser.parse_StringMCOptionalType(output); + assertFalse(parser.hasErrors()); + assertTrue(ast.isPresent()); + assertTrue(optionalType.deepEquals(ast.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCMapType() throws IOException { + MCCollectionTypesTestParser parser = new MCCollectionTypesTestParser(); + Optional ast = parser.parse_StringMCMapType("Map"); + assertTrue(ast.isPresent()); + assertFalse(parser.hasErrors()); + ASTMCMapType mapType = ast.get(); + MCCollectionTypesFullPrettyPrinter printer = new MCCollectionTypesFullPrettyPrinter(new IndentPrinter()); + String output = printer.prettyprint(ast.get()); + ast = parser.parse_StringMCMapType(output); + assertFalse(parser.hasErrors()); + assertTrue(ast.isPresent()); + assertTrue(mapType.deepEquals(ast.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCSetType() throws IOException { + MCCollectionTypesTestParser parser = new MCCollectionTypesTestParser(); + Optional ast = parser.parse_StringMCSetType("Set"); + assertTrue(ast.isPresent()); + assertFalse(parser.hasErrors()); + ASTMCSetType setType = ast.get(); + MCCollectionTypesFullPrettyPrinter printer = new MCCollectionTypesFullPrettyPrinter(new IndentPrinter()); + String output = printer.prettyprint(ast.get()); + ast = parser.parse_StringMCSetType(output); + assertFalse(parser.hasErrors()); + assertTrue(ast.isPresent()); + assertTrue(setType.deepEquals(ast.get())); + + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/prettyprint/MCFullGenericTypesPrettyPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/types/prettyprint/MCFullGenericTypesPrettyPrinterTest.java new file mode 100644 index 0000000000..e0863a225d --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/prettyprint/MCFullGenericTypesPrettyPrinterTest.java @@ -0,0 +1,83 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.prettyprint; + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.types.mcfullgenerictypes._ast.ASTMCMultipleGenericType; +import de.monticore.types.mcfullgenerictypes._ast.ASTMCWildcardTypeArgument; +import de.monticore.types.mcfullgenerictypestest.MCFullGenericTypesTestMill; +import de.monticore.types.mcfullgenerictypestest._parser.MCFullGenericTypesTestParser; +import de.monticore.types.mcfullgenerictypes._prettyprint.MCFullGenericTypesFullPrettyPrinter; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class MCFullGenericTypesPrettyPrinterTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + MCFullGenericTypesTestMill.reset(); + MCFullGenericTypesTestMill.init(); + } + + @Test + public void testMCWildcardTypeArgumentExtends() throws IOException { + MCFullGenericTypesTestParser parser = new MCFullGenericTypesTestParser(); + Optional ast = parser.parse_StringMCWildcardTypeArgument("? extends java.util.List"); + assertTrue(ast.isPresent()); + assertFalse(parser.hasErrors()); + ASTMCWildcardTypeArgument wildcardType = ast.get(); + MCFullGenericTypesFullPrettyPrinter printer = new MCFullGenericTypesFullPrettyPrinter(new IndentPrinter()); + String output = printer.prettyprint(ast.get()); + ast = parser.parse_StringMCWildcardTypeArgument(output); + assertFalse(parser.hasErrors()); + assertTrue(ast.isPresent()); + assertTrue(wildcardType.deepEquals(ast.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCWildcardTypeArgumentSuper() throws IOException { + MCFullGenericTypesTestParser parser = new MCFullGenericTypesTestParser(); + Optional ast = parser.parse_StringMCWildcardTypeArgument("? super de.monticore.ASTNode"); + assertTrue(ast.isPresent()); + assertFalse(parser.hasErrors()); + ASTMCWildcardTypeArgument wildcardType = ast.get(); + MCFullGenericTypesFullPrettyPrinter printer = new MCFullGenericTypesFullPrettyPrinter(new IndentPrinter()); + String output = printer.prettyprint(ast.get()); + ast = parser.parse_StringMCWildcardTypeArgument(output); + assertFalse(parser.hasErrors()); + assertTrue(ast.isPresent()); + assertTrue(wildcardType.deepEquals(ast.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCMultipleGenericType() throws IOException { + MCFullGenericTypesTestParser parser = new MCFullGenericTypesTestParser(); + Optional ast = parser.parse_StringMCMultipleGenericType("java.util.List.some.util.Set.Opt>"); + assertTrue(ast.isPresent()); + assertFalse(parser.hasErrors()); + ASTMCMultipleGenericType complexReferenceType = ast.get(); + MCFullGenericTypesFullPrettyPrinter printer = new MCFullGenericTypesFullPrettyPrinter(new IndentPrinter()); + String output = printer.prettyprint(ast.get()); + ast = parser.parse_StringMCMultipleGenericType(output); + assertFalse(parser.hasErrors()); + assertTrue(ast.isPresent()); + assertTrue(complexReferenceType.deepEquals(ast.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/prettyprint/MCFunctionTypesPrettyPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/types/prettyprint/MCFunctionTypesPrettyPrinterTest.java new file mode 100644 index 0000000000..e93b8f9f86 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/prettyprint/MCFunctionTypesPrettyPrinterTest.java @@ -0,0 +1,91 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.prettyprint; + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.types.mcfunctiontypes._ast.ASTMCFunctionType; +import de.monticore.types.mcfunctiontypes._prettyprint.MCFunctionTypesFullPrettyPrinter; +import de.monticore.types.mcfunctiontypestest.MCFunctionTypesTestMill; +import de.monticore.types.mcfunctiontypestest._parser.MCFunctionTypesTestParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class MCFunctionTypesPrettyPrinterTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + MCFunctionTypesTestMill.reset(); + MCFunctionTypesTestMill.init(); + } + + @Test + public void testMCBasicTypeArgument() throws IOException { + testPrintParseCompare("(int) -> int"); + } + + @Test + public void testMCBasicTypeArgument2() throws IOException { + testPrintParseCompare("(long, int) -> void"); + } + + @Test + public void testSupplier() throws IOException { + testPrintParseCompare("() -> int"); + } + + @Test + public void testElliptic1() throws IOException { + testPrintParseCompare("(int, int...) -> int"); + } + + @Test + public void testElliptic2() throws IOException { + testPrintParseCompare("(int...) -> void"); + } + + @Test + public void testHigherOrderFunction1() throws IOException { + testPrintParseCompare("((int) -> void) -> (int) -> long"); + } + + @Test + public void testHigherOrderFunction2() throws IOException { + testPrintParseCompare("(long, ((long) -> int) -> int) -> ((long) -> void) -> int"); + } + + protected ASTMCFunctionType parse(String mcTypeStr) throws IOException { + MCFunctionTypesTestParser parser = new MCFunctionTypesTestParser(); + Optional typeOpt = parser.parse_StringMCFunctionType(mcTypeStr); + assertNotNull(typeOpt); + assertTrue(typeOpt.isPresent()); + assertEquals(0, Log.getFindingsCount()); + return typeOpt.get(); + } + + protected String print(ASTMCFunctionType type) { + MCFunctionTypesFullPrettyPrinter printer = new MCFunctionTypesFullPrettyPrinter( + new IndentPrinter()); + String typeStr = printer.prettyprint(type); + assertEquals(0, Log.getFindingsCount()); + return typeStr; + } + + protected void testPrintParseCompare(String typeStr) throws IOException { + ASTMCFunctionType type = parse(typeStr); + String printed = print(type); + ASTMCFunctionType typeOfPrinted = parse(printed); + assertTrue(typeOfPrinted.deepEquals(type)); + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/prettyprint/MCSimpleGenericTypesPrettyPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/types/prettyprint/MCSimpleGenericTypesPrettyPrinterTest.java new file mode 100644 index 0000000000..312841e79e --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/prettyprint/MCSimpleGenericTypesPrettyPrinterTest.java @@ -0,0 +1,106 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.prettyprint; + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.mcsimplegenerictypes._ast.ASTMCBasicGenericType; +import de.monticore.types.mcsimplegenerictypes._ast.ASTMCCustomTypeArgument; +import de.monticore.types.mcsimplegenerictypes._prettyprint.MCSimpleGenericTypesFullPrettyPrinter; +import de.monticore.types.mcsimplegenerictypestest.MCSimpleGenericTypesTestMill; +import de.monticore.types.mcsimplegenerictypestest._parser.MCSimpleGenericTypesTestParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.*; + +public class MCSimpleGenericTypesPrettyPrinterTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + MCSimpleGenericTypesTestMill.reset(); + MCSimpleGenericTypesTestMill.init(); + } + + + + @Test + public void testMCBasicTypeArgument() throws IOException { + MCSimpleGenericTypesTestParser parser = new MCSimpleGenericTypesTestParser(); + Optional ast = parser.parse_StringMCBasicGenericType("java.util.List>>"); + assertTrue(ast.isPresent()); + assertFalse(parser.hasErrors()); + ASTMCBasicGenericType typeArgument = ast.get(); + MCSimpleGenericTypesFullPrettyPrinter printer = new MCSimpleGenericTypesFullPrettyPrinter(new IndentPrinter()); + String output = printer.prettyprint(ast.get()); + ast = parser.parse_StringMCBasicGenericType(output); + assertFalse(parser.hasErrors()); + assertTrue(ast.isPresent()); + assertTrue(typeArgument.deepEquals(ast.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCBasicTypeArgument2() throws IOException { + MCSimpleGenericTypesTestParser parser = new MCSimpleGenericTypesTestParser(); + Optional ast = parser.parse_StringMCBasicGenericType("some.randomObject>>>>"); + assertTrue(ast.isPresent()); + assertFalse(parser.hasErrors()); + ASTMCBasicGenericType typeArgument = ast.get(); + MCSimpleGenericTypesFullPrettyPrinter printer = new MCSimpleGenericTypesFullPrettyPrinter(new IndentPrinter()); + String output = printer.prettyprint(ast.get()); + ast = parser.parse_StringMCBasicGenericType(output); + assertFalse(parser.hasErrors()); + assertTrue(ast.isPresent()); + assertTrue(typeArgument.deepEquals(ast.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMCCustomTypeArgument() throws IOException { + MCSimpleGenericTypesTestParser parser = new MCSimpleGenericTypesTestParser(); + Optional ast = parser.parse_StringMCCustomTypeArgument("some.randomObject>>>>"); + assertTrue(ast.isPresent()); + assertFalse(parser.hasErrors()); + ASTMCCustomTypeArgument typeArgument = ast.get(); + MCSimpleGenericTypesFullPrettyPrinter printer = new MCSimpleGenericTypesFullPrettyPrinter(new IndentPrinter()); + String output = printer.prettyprint(ast.get()); + ast = parser.parse_StringMCCustomTypeArgument(output); + assertFalse(parser.hasErrors()); + assertTrue(ast.isPresent()); + assertTrue(typeArgument.deepEquals(ast.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testMultipleMCCustomTypeArgument() throws IOException { + String type = "java.util.List,List>"; + MCSimpleGenericTypesTestParser parser = new MCSimpleGenericTypesTestParser(); + Optional ast = parser.parse_StringMCBasicGenericType(type); + assertTrue(ast.isPresent()); + assertFalse(parser.hasErrors()); + Optional astBefore = ast; + MCSimpleGenericTypesFullPrettyPrinter printer = new MCSimpleGenericTypesFullPrettyPrinter(new IndentPrinter()); + String output = printer.prettyprint(ast.get()); + + assertEquals(type,output); + + ast = parser.parse_StringMCBasicGenericType(output); + assertFalse(parser.hasErrors()); + assertTrue(ast.isPresent()); + assertTrue(astBefore.get().deepEquals(ast.get())); + + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/prettyprint/PrintTypeAstExtensionTests.java b/monticore-grammar/src/test/java/de/monticore/types/prettyprint/PrintTypeAstExtensionTests.java new file mode 100644 index 0000000000..23df1c5c6b --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/prettyprint/PrintTypeAstExtensionTests.java @@ -0,0 +1,289 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.prettyprint; + +import de.monticore.types.mcbasictypes._ast.ASTMCImportStatement; +import de.monticore.types.mcbasictypes._ast.ASTMCReturnType; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.mcbasictypestest._parser.MCBasicTypesTestParser; +import de.monticore.types.mccollectiontypestest._parser.MCCollectionTypesTestParser; +import de.monticore.types.mcfullgenerictypestest.MCFullGenericTypesTestMill; +import de.monticore.types.mcfullgenerictypestest._parser.MCFullGenericTypesTestParser; +import de.se_rwth.commons.logging.Finding; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class PrintTypeAstExtensionTests { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + MCFullGenericTypesTestMill.reset(); + MCFullGenericTypesTestMill.init(); + } + + + @Test + public void printTypeMethodPrimitiveBooleanTest() { + MCFullGenericTypesTestParser parser= new MCFullGenericTypesTestParser(); + + String[] primitives = {"boolean","byte","short","int","char","float","long","double"}; + + for(String simpleReference:primitives) { + try { + Optional type = parser.parse_StringMCType(simpleReference); + + for (Finding f : Log.getFindings()) { + System.out.println(f.getMsg()); + } + + assertEquals(simpleReference.trim(), type.get().printType().trim()); + + + } catch (IOException e) { + e.printStackTrace(); + } + } + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void printTypeMethodObjectTypeTest() { + MCBasicTypesTestParser mcBasicTypesParser= new MCBasicTypesTestParser(); + String simpleReference = "de.monticore.types.prettyprint"; + try { + Optional type = mcBasicTypesParser.parse_StringMCObjectType(simpleReference); + assertEquals(simpleReference.trim(),type.get().printType().trim()); + } catch (IOException e) { + e.printStackTrace(); + } + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void printTypeMethodQualifiedTypeTest() { + MCBasicTypesTestParser mcBasicTypesParser= new MCBasicTypesTestParser(); + String simpleReference = "de.monticore.types.prettyprint"; + try { + Optional type = mcBasicTypesParser.parse_StringMCQualifiedType(simpleReference); + assertEquals(simpleReference.trim(),type.get().printType().trim()); + } catch (IOException e) { + e.printStackTrace(); + } + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void printTypeMethodReturnTypeTest() { + MCBasicTypesTestParser mcBasicTypesParser= new MCBasicTypesTestParser(); + String simpleReference = "de.monticore.types.Prettyprint"; + try { + Optional type = mcBasicTypesParser.parse_StringMCReturnType(simpleReference); + assertEquals(simpleReference.trim(),type.get().printType().trim()); + } catch (IOException e) { + e.printStackTrace(); + } + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void printTypeMethodReturnTypeVoidTest() { + MCBasicTypesTestParser mcBasicTypesParser= new MCBasicTypesTestParser(); + String simpleReference = "void"; + try { + Optional type = mcBasicTypesParser.parse_StringMCReturnType(simpleReference); + assertEquals(simpleReference.trim(),type.get().printType().trim()); + } catch (IOException e) { + e.printStackTrace(); + } + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void printTypeMethodTullGenericTypeTest() { + MCFullGenericTypesTestParser parser= new MCFullGenericTypesTestParser(); + String simpleReference = "de.monticore.types.prettyprint"; + try { + Optional type = parser.parse_StringMCType(simpleReference); + assertEquals(simpleReference.trim(),type.get().printType().trim()); + + + } catch (IOException e) { + e.printStackTrace(); + } + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void printTypeMethodTullGenericType2Test() { + MCFullGenericTypesTestParser parser= new MCFullGenericTypesTestParser(); + String simpleReference = "de.monticore.types.prettyprint"; + try { + Optional type = parser.parse_StringMCType(simpleReference); + assertEquals(simpleReference.trim(),type.get().printType().trim()); + + + } catch (IOException e) { + e.printStackTrace(); + } + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void printTypeMethodTSimpleGenericsArrayTest() { + MCFullGenericTypesTestParser parser= new MCFullGenericTypesTestParser(); + String[] types = {"Person","java.util.List,List>"}; + for(String simpleReference:types) { + try { + Optional type = parser.parse_StringMCType(simpleReference); + assertEquals(simpleReference.trim(), type.get().printType().trim()); + + + } catch (IOException e) { + e.printStackTrace(); + } + } + + assertTrue(Log.getFindings().isEmpty()); + } + + + + @Test + public void printTypeMethodCollectionTypesTest() { + MCCollectionTypesTestParser parser= new MCCollectionTypesTestParser(); + + String[] collectionTypes = {"List","Optional","Map","Set"}; + for(String simpleReference:collectionTypes) { + try { + Optional type = parser.parse_StringMCType(simpleReference); + assertEquals(simpleReference.trim(), type.get().printType().trim()); + + + } catch (IOException e) { + e.printStackTrace(); + } + } + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void printTypeMethodTullGenericTypeWildcardExtendsTest() { + MCFullGenericTypesTestParser parser= new MCFullGenericTypesTestParser(); + String simpleReference = "de.monticore.types.prettyprint"; + try { + Optional type = parser.parse_StringMCType(simpleReference); + assertEquals(simpleReference.trim(),type.get().printType().trim()); + + + } catch (IOException e) { + e.printStackTrace(); + } + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void printTypeMethodTullGenericTypeExtendsTest() { + MCFullGenericTypesTestParser parser= new MCFullGenericTypesTestParser(); + String simpleReference = "de.monticore.types.prettyprint"; + try { + Optional type = parser.parse_StringMCType(simpleReference); + + for(Finding f : Log.getFindings()) { + System.out.println(f.getMsg()); + } + + assertEquals(simpleReference.trim(),type.get().printType().trim()); + + + } catch (IOException e) { + e.printStackTrace(); + } + + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void printTypeMethodTullGenericTypeWildcardSuperTest() { + MCFullGenericTypesTestParser parser= new MCFullGenericTypesTestParser(); + String simpleReference = "de.monticore.types.prettyprint>"; + try { + Optional type = parser.parse_StringMCType(simpleReference); + + assertEquals(simpleReference.trim(),type.get().printType().trim()); + + + } catch (IOException e) { + e.printStackTrace(); + } + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void printTypeMethodImportStatementTest() { + MCFullGenericTypesTestParser parser= new MCFullGenericTypesTestParser(); + String simpleReference = "import a.b.C;"; + try { + Optional type = parser.parse_StringMCImportStatement(simpleReference); + + for(Finding f : Log.getFindings()) { + System.out.println(f.getMsg()); + } + + assertEquals(simpleReference.trim(),type.get().printType().trim()); + + + } catch (IOException e) { + e.printStackTrace(); + } + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void printTypeMethodStarImportStatementTest() { + MCFullGenericTypesTestParser parser= new MCFullGenericTypesTestParser(); + String simpleReference = "import a.b.c.*;"; + try { + Optional type = parser.parse_StringMCImportStatement(simpleReference); + + for(Finding f : Log.getFindings()) { + System.out.println(f.getMsg()); + } + + assertEquals(simpleReference.trim(),type.get().printType().trim()); + + + } catch (IOException e) { + e.printStackTrace(); + } + + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/printer/BasicTypesPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/types/printer/BasicTypesPrinterTest.java new file mode 100644 index 0000000000..4fe2a2c70d --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/printer/BasicTypesPrinterTest.java @@ -0,0 +1,56 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.printer; + +import de.monticore.types.mcbasictypes.MCBasicTypesMill; +import de.monticore.types.mcbasictypes._ast.*; +import de.monticore.types.mcbasictypestest.MCBasicTypesTestMill; +import de.monticore.types.mcbasictypestest._parser.MCBasicTypesTestParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.*; + +public class BasicTypesPrinterTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + MCBasicTypesTestMill.reset(); + MCBasicTypesTestMill.init(); + } + + @Test + public void testPrintType() throws IOException{ + MCBasicTypesTestParser parser = new MCBasicTypesTestParser(); + Optional astmcImportStatement = parser.parse_StringMCImportStatement("import java.util.List;"); + Optional astmcImportStatement1 = parser.parse_StringMCImportStatement("import a.b.c.d.*;"); + Optional astmcQualifiedName = parser.parse_StringMCQualifiedName("java.util.List"); + Optional astmcReturnType = parser.parse_StringMCReturnType("String"); + Optional astmcVoidType = parser.parse_StringMCVoidType("void"); + Optional astmcPrimitiveType = parser.parse_StringMCPrimitiveType("int"); + Optional astmcQualifiedType = parser.parse_StringMCQualifiedType("java.util.List"); + + assertFalse(parser.hasErrors()); + assertTrue(astmcImportStatement.isPresent()); + assertTrue(astmcImportStatement.isPresent()); + assertTrue(astmcImportStatement1.isPresent()); + assertTrue(astmcQualifiedName.isPresent()); + assertTrue(astmcReturnType.isPresent()); + assertTrue(astmcVoidType.isPresent()); + assertTrue(astmcPrimitiveType.isPresent()); + assertTrue(astmcQualifiedType.isPresent()); + + assertEquals("String", MCBasicTypesMill.prettyPrint(astmcReturnType.get(), true)); + assertEquals("void", MCBasicTypesMill.prettyPrint(astmcVoidType.get(), true)); + assertEquals("int", MCBasicTypesMill.prettyPrint(astmcPrimitiveType.get(), true)); + assertEquals("java.util.List", MCBasicTypesMill.prettyPrint(astmcQualifiedType.get(), true)); + + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/printer/CollectionTypesPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/types/printer/CollectionTypesPrinterTest.java new file mode 100644 index 0000000000..2d7b7ed050 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/printer/CollectionTypesPrinterTest.java @@ -0,0 +1,56 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.printer; + +import de.monticore.types.mccollectiontypes.MCCollectionTypesMill; +import de.monticore.types.mccollectiontypes._ast.*; +import de.monticore.types.mccollectiontypes._prettyprint.MCCollectionTypesFullPrettyPrinter; +import de.monticore.types.mccollectiontypestest.MCCollectionTypesTestMill; +import de.monticore.types.mccollectiontypestest._parser.MCCollectionTypesTestParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.*; + +public class CollectionTypesPrinterTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + MCCollectionTypesTestMill.reset(); + MCCollectionTypesTestMill.init(); + } + + @Test + public void testPrintType() throws IOException { + MCCollectionTypesTestParser parser = new MCCollectionTypesTestParser(); + Optional astmcBasicTypeArgument = parser.parse_StringMCBasicTypeArgument("java.util.List"); + Optional astmcPrimitiveTypeArgument = parser.parse_StringMCPrimitiveTypeArgument("int"); + Optional astmcListType = parser.parse_StringMCListType("List"); + Optional astmcSetType = parser.parse_StringMCSetType("Set"); + Optional astmcOptionalType = parser.parse_StringMCOptionalType("Optional"); + Optional astmcMapType = parser.parse_StringMCMapType("Map"); + + assertFalse(parser.hasErrors()); + assertTrue(astmcBasicTypeArgument.isPresent()); + assertTrue(astmcPrimitiveTypeArgument.isPresent()); + assertTrue(astmcListType.isPresent()); + assertTrue(astmcSetType.isPresent()); + assertTrue(astmcOptionalType.isPresent()); + assertTrue(astmcMapType.isPresent()); + + assertEquals("java.util.List", MCCollectionTypesMill.prettyPrint(astmcBasicTypeArgument.get(), true)); + assertEquals("int", MCCollectionTypesMill.prettyPrint(astmcPrimitiveTypeArgument.get(), true)); + assertEquals("List", MCCollectionTypesMill.prettyPrint(astmcListType.get(), true)); + assertEquals("Set", MCCollectionTypesMill.prettyPrint(astmcSetType.get(), true)); + assertEquals("Optional", MCCollectionTypesMill.prettyPrint(astmcOptionalType.get(), true)); + assertEquals("Map", MCCollectionTypesMill.prettyPrint(astmcMapType.get(), true)); + + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/printer/FullGenericTypesPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/types/printer/FullGenericTypesPrinterTest.java new file mode 100644 index 0000000000..32a4bd4410 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/printer/FullGenericTypesPrinterTest.java @@ -0,0 +1,57 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.printer; + +import de.monticore.types.mcfullgenerictypes.MCFullGenericTypesMill; +import de.monticore.types.mcfullgenerictypes._ast.ASTMCMultipleGenericType; +import de.monticore.types.mcfullgenerictypes._ast.ASTMCWildcardTypeArgument; +import de.monticore.types.mcfullgenerictypestest.MCFullGenericTypesTestMill; +import de.monticore.types.mcfullgenerictypestest._parser.MCFullGenericTypesTestParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.*; + +public class FullGenericTypesPrinterTest { + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + MCFullGenericTypesTestMill.reset(); + MCFullGenericTypesTestMill.init(); + } + + @Test + public void testPrintType() throws IOException { + MCFullGenericTypesTestParser parser = new MCFullGenericTypesTestParser(); + Optional astmcWildcardTypeArgument = parser.parse_StringMCWildcardTypeArgument("?"); + Optional astmcWildcardTypeArgument1 = parser.parse_StringMCWildcardTypeArgument("? extends List"); + Optional astmcWildcardTypeArgument2 = parser.parse_StringMCWildcardTypeArgument("? super Stream"); + Optional astmcMultipleGenericType = parser.parse_StringMCMultipleGenericType("java.util.List>.c.d"); +// Optional astmcTypeVariableDeclaration = parser.parse_StringMCTypeVariableDeclaration("a extends b&c&d"); +// Optional astmcTypeParameters = parser.parse_StringMCTypeParameters(""); + + assertFalse(parser.hasErrors()); + assertTrue(astmcWildcardTypeArgument.isPresent()); + assertTrue(astmcWildcardTypeArgument1.isPresent()); + assertTrue(astmcWildcardTypeArgument2.isPresent()); + assertTrue(astmcMultipleGenericType.isPresent()); +// assertTrue(astmcTypeVariableDeclaration.isPresent()); +// assertTrue(astmcTypeParameters.isPresent()); + + assertEquals("?", MCFullGenericTypesMill.prettyPrint(astmcWildcardTypeArgument.get(), true)); + assertEquals("? extends List", MCFullGenericTypesMill.prettyPrint(astmcWildcardTypeArgument1.get(), true)); + assertEquals("? super Stream", MCFullGenericTypesMill.prettyPrint(astmcWildcardTypeArgument2.get(), true)); + assertEquals("java.util.List>.c.d", MCFullGenericTypesMill.prettyPrint(astmcMultipleGenericType.get(), true)); +// assertEquals("", FullGenericTypesPrinter.printType(astmcTypeParameters.get())); +// assertEquals("a extends b &c &d", FullGenericTypesPrinter.printType(astmcTypeVariableDeclaration.get())); + + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/types/printer/SimpleGenericTypesPrinterTest.java b/monticore-grammar/src/test/java/de/monticore/types/printer/SimpleGenericTypesPrinterTest.java new file mode 100644 index 0000000000..cf6b89ff07 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types/printer/SimpleGenericTypesPrinterTest.java @@ -0,0 +1,44 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types.printer; + +import de.monticore.types.mcsimplegenerictypes._ast.ASTMCBasicGenericType; +import de.monticore.types.mcsimplegenerictypes._ast.ASTMCCustomTypeArgument; +import de.monticore.types.mcsimplegenerictypes.MCSimpleGenericTypesMill; +import de.monticore.types.mcsimplegenerictypestest.MCSimpleGenericTypesTestMill; +import de.monticore.types.mcsimplegenerictypestest._parser.MCSimpleGenericTypesTestParser; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.*; + +public class SimpleGenericTypesPrinterTest { + + @Before + public void before() { + LogStub.init(); + Log.enableFailQuick(false); + MCSimpleGenericTypesTestMill.reset(); + MCSimpleGenericTypesTestMill.init(); + } + + @Test + public void testPrintType() throws IOException { + MCSimpleGenericTypesTestParser parser = new MCSimpleGenericTypesTestParser(); + Optional astmcCustomTypeArgument = parser.parse_StringMCCustomTypeArgument("List"); + Optional astmcBasicGenericType = parser.parse_StringMCBasicGenericType("java.util.List>"); + + assertFalse(parser.hasErrors()); + assertTrue(astmcBasicGenericType.isPresent()); + assertTrue(astmcCustomTypeArgument.isPresent()); + + assertEquals("List", MCSimpleGenericTypesMill.prettyPrint(astmcCustomTypeArgument.get(), false)); + assertEquals("java.util.List>", MCSimpleGenericTypesMill.prettyPrint(astmcBasicGenericType.get(), false)); + + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/types3/AbstractTypeTest.java b/monticore-grammar/src/test/java/de/monticore/types3/AbstractTypeTest.java new file mode 100644 index 0000000000..715fd58a02 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types3/AbstractTypeTest.java @@ -0,0 +1,29 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.types3; + +import de.se_rwth.commons.logging.Finding; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; + +import java.util.stream.Collectors; + +import static org.junit.Assert.assertTrue; + +public class AbstractTypeTest { + + @Before + public void initLog() { + LogStub.init(); + Log.enableFailQuick(false); + } + + protected static void assertNoFindings() { + assertTrue(Log.getFindings().stream() + .map(Finding::buildMsg) + .collect(Collectors.joining(System.lineSeparator())), + Log.getFindings().isEmpty() + ); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types3/AbstractTypeVisitorTest.java b/monticore-grammar/src/test/java/de/monticore/types3/AbstractTypeVisitorTest.java new file mode 100644 index 0000000000..1111fa8799 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types3/AbstractTypeVisitorTest.java @@ -0,0 +1,314 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types3; + +import de.monticore.expressions.combineexpressionswithliterals.CombineExpressionsWithLiteralsMill; +import de.monticore.expressions.combineexpressionswithliterals._ast.ASTFoo; +import de.monticore.expressions.combineexpressionswithliterals._parser.CombineExpressionsWithLiteralsParser; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.ICombineExpressionsWithLiteralsArtifactScope; +import de.monticore.expressions.combineexpressionswithliterals._visitor.CombineExpressionsWithLiteralsTraverser; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.lambdaexpressions._ast.ASTLambdaExpression; +import de.monticore.expressions.lambdaexpressions._symboltable.LambdaExpressionsSTCompleteTypes2; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.FunctionSymbol; +import de.monticore.symbols.basicsymbols._symboltable.IBasicSymbolsScope; +import de.monticore.symbols.basicsymbols._symboltable.TypeVarSymbol; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types3.util.CombineExpressionsWithLiteralsTypeTraverserFactory; +import de.monticore.types3.util.DefsTypesForTests; +import de.monticore.types3.util.DefsVariablesForTests; +import de.monticore.visitor.ITraverser; +import de.se_rwth.commons.logging.Finding; +import de.se_rwth.commons.logging.Log; +import org.junit.Before; + +import java.io.IOException; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import static de.monticore.types3.util.DefsTypesForTests.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * used to provide facilities to test type derivers. + * main extensions point are the methods + * setup, setupValues, parseString*, generateScopes, calculateTypes + */ +public class AbstractTypeVisitorTest extends AbstractTypeTest { + + // Parser, etc. used for convenience: + // (may be any other Parser that understands CommonExpressions) + protected CombineExpressionsWithLiteralsParser parser; + + // we can use our own type4Ast instance to try to find occurrences of + // Type Visitors using the map from the mill instead of the provided one + protected Type4Ast type4Ast; + + protected ITraverser typeMapTraverser; + + protected ITraverser scopeGenitor; + + protected ITraverser getScopeGenitor() { + return scopeGenitor; + } + + protected ITraverser getTypeMapTraverser() { + return typeMapTraverser; + } + + @Before + public void setupDefaultMill() { + CombineExpressionsWithLiteralsMill.reset(); + CombineExpressionsWithLiteralsMill.init(); + BasicSymbolsMill.initializePrimitives(); + DefsTypesForTests.setup(); + parser = CombineExpressionsWithLiteralsMill.parser(); + type4Ast = new Type4Ast(); + typeMapTraverser = new CombineExpressionsWithLiteralsTypeTraverserFactory() + .createTraverser(type4Ast); + CombineExpressionsWithLiteralsTraverser combinedScopesGenitor = + CombineExpressionsWithLiteralsMill.traverser(); + combinedScopesGenitor.add4LambdaExpressions( + new LambdaExpressionsSTCompleteTypes2( + typeMapTraverser, + getType4Ast() + ) + ); + scopeGenitor = combinedScopesGenitor; + } + + /** + * adds a set of variables, functions, etc. to the global scope, + * useful for most non-specific tests + */ + protected void setupValues() { + IBasicSymbolsScope gs = + BasicSymbolsMill.globalScope(); + DefsVariablesForTests.setup(gs); + // deprecated + inScope(gs, variable("person1", _personSymType)); + inScope(gs, variable("person2", _personSymType)); + inScope(gs, variable("student1", _studentSymType)); + inScope(gs, variable("student2", _studentSymType)); + inScope(gs, variable("csStudent1", _csStudentSymType)); + inScope(gs, variable("intList", + SymTypeExpressionFactory.createGenerics( + _boxedListSymType.getTypeInfo(), _intSymType)) + ); + inScope(gs, variable("intLinkedList", + SymTypeExpressionFactory.createGenerics( + _linkedListSymType.getTypeInfo(), _intSymType)) + ); + // non-member functions + inScope(gs, function("runnable", _voidSymType)); + inScope(gs, function("intProvider", _intSymType)); + FunctionSymbol intConsumer = function("intConsumer", _voidSymType, _intSymType); + inScope(gs, intConsumer); + inScope(gs, function("intConsumerProvider", intConsumer.getFunctionType())); + inScope(gs, function("intEllipticConsumer", _voidSymType, + List.of(_intSymType), true)); + inScope(gs, function("int2int", _intSymType, _intSymType)); + TypeVarSymbol genericConsumerVar = typeVariable("T"); + FunctionSymbol genericConsumer + = inScope(gs, function("genericConsumer", _voidSymType, + SymTypeExpressionFactory.createTypeVariable(genericConsumerVar)) + ); + genericConsumer.getSpannedScope().add(genericConsumerVar); + inScope(gs, function("overloadedFunc1", _booleanSymType, _intSymType)); + inScope(gs, function("overloadedFunc1", _intSymType, _booleanSymType)); + } + + // Parse a String expression of the according language + protected Optional parseStringExpr(String exprStr) + throws IOException { + return parser.parse_StringExpression(exprStr); + } + + // Parse a String type identifier of the according language + protected Optional parseStringMCType(String mcTypeStr) + throws IOException { + return parser.parse_StringMCType(mcTypeStr); + } + + protected void generateScopes(ASTExpression expr) { + // create a root + ASTFoo rootNode = CombineExpressionsWithLiteralsMill.fooBuilder() + .setExpression(expr) + .build(); + ICombineExpressionsWithLiteralsArtifactScope rootScope = + CombineExpressionsWithLiteralsMill.scopesGenitorDelegator() + .createFromAST(rootNode); + rootScope.setName("fooRoot"); + // complete the symbol table + expr.accept(getScopeGenitor()); + } + + protected void generateScopes(ASTMCType mcType) { + // create an expression to contain the type + // currently (MC 7.5) lambda expressions are the only expressions + // which can directly contain MCTypes + ASTLambdaExpression lambda = CombineExpressionsWithLiteralsMill + .lambdaExpressionBuilder() + .setLambdaParameters( + CombineExpressionsWithLiteralsMill.lambdaParametersBuilder() + .setLambdaParametersList(List.of( + CombineExpressionsWithLiteralsMill.lambdaParameterBuilder() + .setName("parameter") + .setMCType(mcType) + .build() + )) + .build() + ) + .setLambdaBody( + CombineExpressionsWithLiteralsMill.lambdaExpressionBodyBuilder() + .setExpression( + CombineExpressionsWithLiteralsMill.literalExpressionBuilder() + .setLiteral( + CombineExpressionsWithLiteralsMill + .natLiteralBuilder() + .setDigits("8243721") + .build() + ) + .build() + ) + .setType(SymTypeExpressionFactory.createPrimitive("int")) + .build() + ) + .build(); + // create a root + ASTFoo rootNode = CombineExpressionsWithLiteralsMill.fooBuilder() + .setExpression(lambda) + .build(); + ICombineExpressionsWithLiteralsArtifactScope rootScope = + CombineExpressionsWithLiteralsMill.scopesGenitorDelegator() + .createFromAST(rootNode); + rootScope.setName("fooRoot"); + } + + protected void calculateTypes(ASTExpression expr) { + expr.accept(typeMapTraverser); + } + + protected void calculateTypes(ASTMCType mcType) { + mcType.accept(typeMapTraverser); + } + + protected ASTExpression parseExpr(String exprStr) throws IOException { + Optional astExpression = parseStringExpr(exprStr); + assertTrue(astExpression.isPresent()); + return astExpression.get(); + } + + protected ASTMCType parseMCType(String typeStr) throws IOException { + Optional mcType = parseStringMCType(typeStr); + assertTrue(mcType.isPresent()); + return mcType.get(); + } + + protected void checkExpr(String exprStr, String expectedType) + throws IOException { + ASTExpression astexpr = parseExpr(exprStr); + generateScopes(astexpr); + calculateTypes(astexpr); + assertNoFindings(); + assertTrue("No type calculated for expression " + exprStr, + getType4Ast().hasTypeOfExpression(astexpr)); + SymTypeExpression type = getType4Ast().getTypeOfExpression(astexpr); + assertNoFindings(); + assertEquals("Wrong type for expression " + exprStr, + expectedType, + type.printFullName() + ); + } + + protected void checkType(String typeStr, String expectedType) + throws IOException { + ASTMCType astType = parseMCType(typeStr); + generateScopes(astType); + calculateTypes(astType); + SymTypeExpression type = getType4Ast().getTypeOfTypeIdentifier(astType); + assertNoFindings(); + assertEquals("Wrong type for type identifier " + typeStr, + expectedType, + type.printFullName() + ); + } + + /** + * roundtrip test: parse, calculate type, print, compare + */ + protected void checkTypeRoundTrip(String typeStr) throws IOException { + checkType(typeStr, typeStr); + } + + protected void checkErrorExpr(String exprStr, String expectedError) + throws IOException { + ASTExpression astExpr = parseExpr(exprStr); + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + SymTypeExpression type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue("expected Obscure for expression \"" + exprStr + + "\" but got " + type.printFullName(), type.isObscureType()); + assertHasErrorCode(expectedError); + Log.getFindings().clear(); + } + + protected void checkErrorMCType(String typeStr, String expectedError) + throws IOException { + ASTMCType astType = parseMCType(typeStr); + generateScopes(astType); + assertNoFindings(); + Log.getFindings().clear(); + calculateTypes(astType); + SymTypeExpression type = getType4Ast().getPartialTypeOfTypeId(astType); + assertTrue("expected Obscure for expression \"" + typeStr + + "\" but got " + type.printFullName(), type.isObscureType()); + assertHasErrorCode(expectedError); + } + + protected List getFirstErrorCodes(long n) { + List errorsInLog = Log.getFindings().stream() + .filter(Finding::isError) + .map(err -> err.getMsg().split(" ")[0]) + .limit(n) + .collect(Collectors.toList()); + List errorsToReturn; + + if (errorsInLog.size() < n) { + errorsToReturn = errorsInLog; + for (int i = 0; i < n - errorsInLog.size(); i++) { + errorsToReturn.add(""); + } + } + else { + errorsToReturn = errorsInLog.subList(0, (int) n); + } + return errorsToReturn; + } + + protected List getAllErrorCodes() { + return getFirstErrorCodes(Log.getErrorCount()); + } + + protected void assertHasErrorCode(String code) { + assertTrue( + "Error \"" + code + "\" expected, " + + "but instead the errors are:" + + System.lineSeparator() + + Log.getFindings().stream() + .map(Finding::buildMsg) + .collect(Collectors.joining(System.lineSeparator())) + + System.lineSeparator(), + getAllErrorCodes().stream().anyMatch(code::equals) + ); + } + + protected Type4Ast getType4Ast() { + return type4Ast; + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/types3/AssignmentExpressionTypeVisitorTest.java b/monticore-grammar/src/test/java/de/monticore/types3/AssignmentExpressionTypeVisitorTest.java new file mode 100644 index 0000000000..0f0f92b0c4 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types3/AssignmentExpressionTypeVisitorTest.java @@ -0,0 +1,894 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types3; + +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; + +public class AssignmentExpressionTypeVisitorTest extends AbstractTypeVisitorTest { + + @Before + public void init() { + setupValues(); + } + + @Test + public void deriveFromIncSuffixExpression() throws IOException { + checkExpr("varchar++", "char"); // ++ applicable to char + checkExpr("varbyte++", "byte"); // ++ applicable to byte + checkExpr("varshort++", "short"); // ++ applicable to short + checkExpr("varint++", "int"); // ++ applicable to int + checkExpr("varlong++", "long"); // ++ applicable to long + checkExpr("varfloat++", "float"); // ++ applicable to float + checkExpr("vardouble++", "double"); // ++ applicable to double + checkExpr("varchar = varchar++", "char"); // ++ applicable to char, result is char + checkExpr("varbyte = varbyte++", "byte"); // ++ applicable to byte, result is byte + checkExpr("varshort = varshort++", "short"); // ++ applicable to short, result is short + checkExpr("varint = varint++", "int"); // ++ applicable to int, result is int + checkExpr("varlong = varlong++", "long"); // ++ applicable to long, result is long + checkExpr("varfloat = varfloat++", "float"); // ++ applicable to float, result is float + checkExpr("vardouble = vardouble++", "double"); // ++ applicable to double, result is double + } + + @Test + public void testInvalidIncSuffixExpression() throws IOException { + checkErrorExpr("varboolean++", "0xA0184"); // ++ not applicable to boolean + checkErrorExpr("varString++", "0xA0184"); // not applicable to String + } + + @Test + public void deriveFromDecSuffixExpression() throws IOException { + checkExpr("varchar--", "char"); // -- applicable to char + checkExpr("varbyte--", "byte"); // -- applicable to byte + checkExpr("varshort--", "short"); // -- applicable to short + checkExpr("varint--", "int"); // -- applicable to int + checkExpr("varlong--", "long"); // -- applicable to long + checkExpr("varfloat--", "float"); // -- applicable to float + checkExpr("vardouble--", "double"); // -- applicable to double + + checkExpr("varchar = varchar--", "char"); // -- applicable to char, result is char + checkExpr("varbyte = varbyte--", "byte"); // -- applicable to byte, result is byte + checkExpr("varshort = varshort--", "short"); // -- applicable to short, result is short + checkExpr("varint = varint--", "int"); // -- applicable to int, result is int + checkExpr("varlong = varlong--", "long"); // -- applicable to long, result is long + checkExpr("varfloat = varfloat--", "float"); // -- applicable to float, result is float + checkExpr("vardouble = vardouble--", "double"); // -- applicable to double, result is double + } + + @Test + public void testInvalidDecSuffixExpression() throws IOException { + checkErrorExpr("varboolean--", "0xA0184"); // -- not applicable to boolean + checkErrorExpr("varString--", "0xA0184"); //not applicable to Strings + } + + @Test + public void deriveFromIncPrefixExpression() throws IOException { + checkExpr("++varchar", "char"); // ++ applicable to char + checkExpr("++varbyte", "byte"); // ++ applicable to byte + checkExpr("++varshort", "short"); // ++ applicable to short + checkExpr("++varint", "int"); // ++ applicable to int + checkExpr("++varlong", "long"); // ++ applicable to long + checkExpr("++varfloat", "float"); // ++ applicable to float + checkExpr("++vardouble", "double"); // ++ applicable to double + + checkExpr("varchar = ++varchar", "char"); // ++ applicable to char, result is char + checkExpr("varbyte = ++varbyte", "byte"); // ++ applicable to byte, result is byte + checkExpr("varshort = ++varshort", "short"); // ++ applicable to short, result is short + checkExpr("varint = ++varint", "int"); // ++ applicable to int, result is int + checkExpr("varlong = ++varlong", "long"); // ++ applicable to long, result is long + checkExpr("varfloat = ++varfloat", "float"); // ++ applicable to float, result is float + checkExpr("vardouble = ++vardouble", "double"); // ++ applicable to double, result is double + } + + @Test + public void testInvalidIncPrefixExpression() throws IOException { + checkErrorExpr("++varboolean", "0xA0184"); // ++ not applicable to boolean + checkErrorExpr("++varString", "0xA0184"); //not applicable to Strings + } + + @Test + public void deriveFromDecPrefixExpression() throws IOException { + checkExpr("--varchar", "char"); // -- applicable to char + checkExpr("--varbyte", "byte"); // -- applicable to byte + checkExpr("--varshort", "short"); // -- applicable to short + checkExpr("--varint", "int"); // -- applicable to int + checkExpr("--varlong", "long"); // -- applicable to long + checkExpr("--varfloat", "float"); // -- applicable to float + checkExpr("--vardouble", "double"); // -- applicable to double + + checkExpr("varchar = --varchar", "char"); // -- applicable to char, result is char + checkExpr("varbyte = --varbyte", "byte"); // -- applicable to byte, result is byte + checkExpr("varshort = --varshort", "short"); // -- applicable to short, result is short + checkExpr("varint = --varint", "int"); // -- applicable to int, result is int + checkExpr("varlong = --varlong", "long"); // -- applicable to long, result is long + checkExpr("varfloat = --varfloat", "float"); // -- applicable to float, result is float + checkExpr("vardouble = --vardouble", "double"); // -- applicable to double, result is double + } + + @Test + public void testInvalidDecPrefixExpression() throws IOException { + checkErrorExpr("--varboolean", "0xA0184"); // -- not applicable to boolean + checkErrorExpr("--varString", "0xA0184"); // not applicable to Strings + } + + @Test + public void deriveFromMinusPrefixExpression() throws IOException { + //example with int + checkExpr("-5", "int"); + + //example with double + checkExpr("-15.7", "double"); + } + + @Test + public void testInvalidMinusPrefixExpression() throws IOException { + checkErrorExpr("-aBoolean", "0xFD118"); // - not applicable to boolean + checkErrorExpr("-\"Hello\"", "0xA017D"); //only possible with numeric types + } + + @Test + public void deriveFromPlusPrefixExpression() throws IOException { + //example with int + checkExpr("+34", "int"); + + //example with long + checkExpr("+4L", "long"); + } + + @Test + public void testInvalidPlusPrefixExpression() throws IOException { + //only possible with numeric types + checkErrorExpr("+\"Hello\"", "0xA017D"); + } + + @Test + public void deriveFromPlusAssignmentExpression() throws IOException { + checkExpr("varchar += varchar", "char"); // += applicable to char, char + checkExpr("varchar += varbyte", "char"); // += applicable to char, byte + checkExpr("varchar += varshort", "char"); // += applicable to char, short + checkExpr("varchar += varint", "char"); // += applicable to char, int + checkExpr("varchar += varfloat", "char"); // += applicable to char, float + checkExpr("varchar += vardouble", "char"); // += applicable to char, double + checkExpr("varshort += varchar", "short"); // += applicable to short, char + checkExpr("varshort += varbyte", "short"); // += applicable to short, byte + checkExpr("varshort += varshort", "short"); // += applicable to short, short + checkExpr("varshort += varint", "short"); // += applicable to short, int + checkExpr("varshort += varlong", "short"); // += applicable to short, long + checkExpr("varshort += varfloat", "short"); // += applicable to short, float + checkExpr("varshort += vardouble", "short"); // += applicable to short, double + checkExpr("varint += varchar", "int"); // += applicable to int, char + checkExpr("varint += varbyte", "int"); // += applicable to int, byte + checkExpr("varint += varshort", "int"); // += applicable to int, short + checkExpr("varint += varint", "int"); // += applicable to int, int + checkExpr("varint += varlong", "int"); // += applicable to int, long + checkExpr("varint += varfloat", "int"); // += applicable to int, float + checkExpr("varint += vardouble", "int"); // += applicable to int, double + checkExpr("varfloat += varchar", "float"); // += applicable to float, char + checkExpr("varfloat += varbyte", "float"); // += applicable to float, byte + checkExpr("varfloat += varshort", "float"); // += applicable to float, short + checkExpr("varfloat += varint", "float"); // += applicable to float, int + checkExpr("varfloat += varlong", "float"); // += applicable to float, long + checkExpr("varfloat += varfloat", "float"); // += applicable to float, float + checkExpr("varfloat += vardouble", "float"); // += applicable to float, double + checkExpr("vardouble += varchar", "double"); // += applicable to double, char + checkExpr("vardouble += varbyte", "double"); // += applicable to double, byte + checkExpr("vardouble += varshort", "double"); // += applicable to double, short + checkExpr("vardouble += varint", "double"); // += applicable to double, int + checkExpr("vardouble += varlong", "double"); // += applicable to double, long + checkExpr("vardouble += varfloat", "double"); // += applicable to double, float + checkExpr("vardouble += vardouble", "double"); // += applicable to double, double + checkExpr("varString+=person1", "String"); // example with String - Person + } + + @Test + public void testInvalidPlusAssignmentExpression() throws IOException { + checkErrorExpr("varboolean += varboolean", "0xA0178"); // += not applicable to boolean, boolean + checkErrorExpr("varboolean += varchar", "0xA0178"); // += not applicable to boolean, char + checkErrorExpr("varboolean += varbyte", "0xA0178"); // += not applicable to boolean, byte + checkErrorExpr("varboolean += varshort", "0xA0178"); // += not applicable to boolean, short + checkErrorExpr("varboolean += varint", "0xA0178"); // += not applicable to boolean, int + checkErrorExpr("varboolean += varlong", "0xA0178"); // += not applicable to boolean, long + checkErrorExpr("varboolean += varfloat", "0xA0178"); // += not applicable to boolean, float + checkErrorExpr("varboolean += vardouble", "0xA0178"); // += not applicable to boolean, double + checkErrorExpr("varchar += varboolean", "0xA0178"); // += not applicable to char, boolean + checkErrorExpr("varbyte += varboolean", "0xA0178"); // += not applicable to byte, boolean + checkErrorExpr("varshort += varboolean", "0xA0178"); // += not applicable to short, boolean + checkErrorExpr("varint += varboolean", "0xA0178"); // += not applicable to int, boolean + checkErrorExpr("varlong += varboolean", "0xA0178"); // += not applicable to long, boolean + checkErrorExpr("varfloat += varboolean", "0xA0178"); // += not applicable to float, boolean + checkErrorExpr("vardouble += varboolean", "0xA0178"); // += not applicable to double, boolean + checkErrorExpr("varint+=\"Hello\"", "0xA0178"); // not possible because int = int + (int) String returns a casting error + } + + @Test + public void deriveFromMinusAssignmentExpression() throws IOException { + checkExpr("varchar -= varchar", "char"); // -= applicable to char, char + checkExpr("varchar -= varbyte", "char"); // -= applicable to char, byte + checkExpr("varchar -= varshort", "char"); // -= applicable to char, short + checkExpr("varchar -= varint", "char"); // -= applicable to char, int + checkExpr("varchar -= varlong", "char"); // -= applicable to char, long + checkExpr("varchar -= varfloat", "char"); // -= applicable to char, float + checkExpr("varchar -= vardouble", "char"); // -= applicable to char, double + checkExpr("varshort -= varchar", "short"); // -= applicable to short, char + checkExpr("varshort -= varbyte", "short"); // -= applicable to short, byte + checkExpr("varshort -= varshort", "short"); // -= applicable to short, short + checkExpr("varshort -= varint", "short"); // -= applicable to short, int + checkExpr("varshort -= varlong", "short"); // -= applicable to short, long + checkExpr("varshort -= varfloat", "short"); // -= applicable to short, float + checkExpr("varshort -= vardouble", "short"); // -= applicable to short, double + checkExpr("varint -= varchar", "int"); // -= applicable to int, char + checkExpr("varint -= varbyte", "int"); // -= applicable to int, byte + checkExpr("varint -= varshort", "int"); // -= applicable to int, short + checkExpr("varint -= varint", "int"); // -= applicable to int, int + checkExpr("varint -= varlong", "int"); // -= applicable to int, long + checkExpr("varint -= varfloat", "int"); // -= applicable to int, float + checkExpr("varint -= vardouble", "int"); // -= applicable to int, double + checkExpr("varfloat -= varchar", "float"); // -= applicable to float, char + checkExpr("varfloat -= varbyte", "float"); // -= applicable to float, byte + checkExpr("varfloat -= varshort", "float"); // -= applicable to float, short + checkExpr("varfloat -= varint", "float"); // -= applicable to float, int + checkExpr("varfloat -= varlong", "float"); // -= applicable to float, long + checkExpr("varfloat -= varfloat", "float"); // -= applicable to float, float + checkExpr("varfloat -= vardouble", "float"); // -= applicable to float, double + checkExpr("vardouble -= varchar", "double"); // -= applicable to double, char + checkExpr("vardouble -= varbyte", "double"); // -= applicable to double, byte + checkExpr("vardouble -= varshort", "double"); // -= applicable to double, short + checkExpr("vardouble -= varint", "double"); // -= applicable to double, int + checkExpr("vardouble -= varlong", "double"); // -= applicable to double, long + checkExpr("vardouble -= varfloat", "double"); // -= applicable to double, float + checkExpr("vardouble -= vardouble", "double"); // -= applicable to double, double + } + + @Test + public void testInvalidMinusAssignmentExpression() throws IOException { + checkErrorExpr("varBoolean -= varBoolean", "0xA0178"); // -= not applicable to boolean, boolean + checkErrorExpr("varBoolean -= varchar", "0xA0178"); // -= not applicable to boolean, char + checkErrorExpr("varBoolean -= varbyte", "0xA0178"); // -= not applicable to boolean, byte + checkErrorExpr("varBoolean -= varshort", "0xA0178"); // -= not applicable to boolean, short + checkErrorExpr("varBoolean -= varint", "0xA0178"); // -= not applicable to boolean, int + checkErrorExpr("varBoolean -= varlong", "0xA0178"); // -= not applicable to boolean, long + checkErrorExpr("varBoolean -= varfloat", "0xA0178"); // -= not applicable to boolean, float + checkErrorExpr("varBoolean -= vardouble", "0xA0178"); // -= not applicable to boolean, double + checkErrorExpr("varint-=\"Hello\"", "0xA0178"); //not possible because int = int - (int) String returns a casting error + } + + @Test + public void deriveFromMultAssignmentExpression() throws IOException { + checkExpr("varchar *= varchar", "char"); // *= applicable to char, char + checkExpr("varchar *= varbyte", "char"); // *= applicable to char, byte + checkExpr("varchar *= varshort", "char"); // *= applicable to char, short + checkExpr("varchar *= varint", "char"); // *= applicable to char, int + checkExpr("varchar *= varlong", "char"); // *= applicable to char, long + checkExpr("varchar *= varfloat", "char"); // *= applicable to char, float + checkExpr("varchar *= vardouble", "char"); // *= applicable to char, double + checkExpr("varshort *= varchar", "short"); // *= applicable to short, char + checkExpr("varshort *= varbyte", "short"); // *= applicable to short, byte + checkExpr("varshort *= varshort", "short"); // *= applicable to short, short + checkExpr("varshort *= varint", "short"); // *= applicable to short, int + checkExpr("varshort *= varlong", "short"); // *= applicable to short, long + checkExpr("varshort *= varfloat", "short"); // *= applicable to short, float + checkExpr("varshort *= vardouble", "short"); // *= applicable to short, double + checkExpr("varint *= varchar", "int"); // *= applicable to int, char + checkExpr("varint *= varbyte", "int"); // *= applicable to int, byte + checkExpr("varint *= varshort", "int"); // *= applicable to int, short + checkExpr("varint *= varint", "int"); // *= applicable to int, int + checkExpr("varint *= varlong", "int"); // *= applicable to int, long + checkExpr("varint *= varfloat", "int"); // *= applicable to int, float + checkExpr("varint *= vardouble", "int"); // *= applicable to int, double + checkExpr("varfloat *= varchar", "float"); // *= applicable to float, char + checkExpr("varfloat *= varbyte", "float"); // *= applicable to float, byte + checkExpr("varfloat *= varshort", "float"); // *= applicable to float, short + checkExpr("varfloat *= varint", "float"); // *= applicable to float, int + checkExpr("varfloat *= varlong", "float"); // *= applicable to float, long + checkExpr("varfloat *= varfloat", "float"); // *= applicable to float, float + checkExpr("varfloat *= vardouble", "float"); // *= applicable to float, double + checkExpr("vardouble *= varchar", "double"); // *= applicable to double, char + checkExpr("vardouble *= varbyte", "double"); // *= applicable to double, byte + checkExpr("vardouble *= varshort", "double"); // *= applicable to double, short + checkExpr("vardouble *= varint", "double"); // *= applicable to double, int + checkExpr("vardouble *= varlong", "double"); // *= applicable to double, long + checkExpr("vardouble *= varfloat", "double"); // *= applicable to double, float + checkExpr("vardouble *= vardouble", "double"); // *= applicable to double, double + } + + @Test + public void testInvalidMultAssignmentExpression() throws IOException { + checkErrorExpr("varboolean *= varboolean", "0xA0178"); // *= not applicable to boolean, boolean + checkErrorExpr("varboolean *= varchar", "0xA0178"); // *= not applicable to boolean, char + checkErrorExpr("varboolean *= varbyte", "0xA0178"); // *= not applicable to boolean, byte + checkErrorExpr("varboolean *= varshort", "0xA0178"); // *= not applicable to boolean, short + checkErrorExpr("varboolean *= varint", "0xA0178"); // *= not applicable to boolean, int + checkErrorExpr("varboolean *= varlong", "0xA0178"); // *= not applicable to boolean, long + checkErrorExpr("varboolean *= varfloat", "0xA0178"); // *= not applicable to boolean, float + checkErrorExpr("varboolean *= vardouble", "0xA0178"); // *= not applicable to boolean, double + checkErrorExpr("varint*=\"Hello\"", "0xA0178"); /// not possible because int = int * (int) String returns a casting error + } + + @Test + public void deriveFromDivideAssignmentExpression() throws IOException { + checkExpr("varchar /= varchar", "char"); // /= applicable to char, char + checkExpr("varchar /= varbyte", "char"); // /= applicable to char, byte + checkExpr("varchar /= varshort", "char"); // /= applicable to char, short + checkExpr("varchar /= varint", "char"); // /= applicable to char, int + checkExpr("varchar /= varlong", "char"); // /= applicable to char, long + checkExpr("varchar /= varfloat", "char"); // /= applicable to char, float + checkExpr("varchar /= vardouble", "char"); // /= applicable to char, double + checkExpr("varshort /= varchar", "short"); // /= applicable to short, char + checkExpr("varshort /= varbyte", "short"); // /= applicable to short, byte + checkExpr("varshort /= varshort", "short"); // /= applicable to short, short + checkExpr("varshort /= varint", "short"); // /= applicable to short, int + checkExpr("varshort /= varlong", "short"); // /= applicable to short, long + checkExpr("varshort /= varfloat", "short"); // /= applicable to short, float + checkExpr("varshort /= vardouble", "short"); // /= applicable to short, double + checkExpr("varint /= varchar", "int"); // /= applicable to int, char + checkExpr("varint /= varbyte", "int"); // /= applicable to int, byte + checkExpr("varint /= varshort", "int"); // /= applicable to int, short + checkExpr("varint /= varint", "int"); // /= applicable to int, int + checkExpr("varint /= varlong", "int"); // /= applicable to int, long + checkExpr("varint /= varfloat", "int"); // /= applicable to int, float + checkExpr("varint /= vardouble", "int"); // /= applicable to int, double + checkExpr("varfloat /= varchar", "float"); // /= applicable to float, char + checkExpr("varfloat /= varbyte", "float"); // /= applicable to float, byte + checkExpr("varfloat /= varshort", "float"); // /= applicable to float, short + checkExpr("varfloat /= varint", "float"); // /= applicable to float, int + checkExpr("varfloat /= varlong", "float"); // /= applicable to float, long + checkExpr("varfloat /= varfloat", "float"); // /= applicable to float, float + checkExpr("varfloat /= vardouble", "float"); // /= applicable to float, double + checkExpr("vardouble /= varchar", "double"); // /= applicable to double, char + checkExpr("vardouble /= varbyte", "double"); // /= applicable to double, byte + checkExpr("vardouble /= varshort", "double"); // /= applicable to double, short + checkExpr("vardouble /= varint", "double"); // /= applicable to double, int + checkExpr("vardouble /= varlong", "double"); // /= applicable to double, long + checkExpr("vardouble /= varfloat", "double"); // /= applicable to double, float + checkExpr("vardouble /= vardouble", "double"); // /= applicable to double, double + + } + + @Test + public void testInvalidDivideAssignmentExpression() throws IOException { + checkErrorExpr("varboolean /= varboolean", "0xA0178"); // /= not applicable to boolean, boolean + checkErrorExpr("varboolean /= varchar", "0xA0178"); // /= not applicable to boolean, char + checkErrorExpr("varboolean /= varbyte", "0xA0178"); // /= not applicable to boolean, byte + checkErrorExpr("varboolean /= varshort", "0xA0178"); // /= not applicable to boolean, short + checkErrorExpr("varboolean /= varint", "0xA0178"); // /= not applicable to boolean, int + checkErrorExpr("varboolean /= varlong", "0xA0178"); // /= not applicable to boolean, long + checkErrorExpr("varboolean /= varfloat", "0xA0178"); // /= not applicable to boolean, float + checkErrorExpr("varboolean /= vardouble", "0xA0178"); // /= not applicable to boolean, double + checkErrorExpr("varint/=\"Hello\"", "0xA0178"); // not possible because int = int / (int) String returns a casting error + } + + @Test + public void deriveFromModuloAssignmentExpression() throws IOException { + checkExpr("varchar %= varchar", "char"); // %= applicable to char, char + checkExpr("varchar %= varbyte", "char"); // %= applicable to char, byte + checkExpr("varchar %= varshort", "char"); // %= applicable to char, short + checkExpr("varchar %= varint", "char"); // %= applicable to char, int + checkExpr("varchar %= varlong", "char"); // %= applicable to char, long + checkExpr("varchar %= varfloat", "char"); // %= applicable to char, float + checkExpr("varchar %= vardouble", "char"); // %= applicable to char, double + checkExpr("varshort %= varchar", "short"); // %= applicable to short, char + checkExpr("varshort %= varbyte", "short"); // %= applicable to short, byte + checkExpr("varshort %= varshort", "short"); // %= applicable to short, short + checkExpr("varshort %= varint", "short"); // %= applicable to short, int + checkExpr("varshort %= varlong", "short"); // %= applicable to short, long + checkExpr("varshort %= varfloat", "short"); // %= applicable to short, float + checkExpr("varshort %= vardouble", "short"); // %= applicable to short, double + checkExpr("varint %= varchar", "int"); // %= applicable to int, char + checkExpr("varint %= varbyte", "int"); // %= applicable to int, byte + checkExpr("varint %= varshort", "int"); // %= applicable to int, short + checkExpr("varint %= varint", "int"); // %= applicable to int, int + checkExpr("varint %= varlong", "int"); // %= applicable to int, long + checkExpr("varint %= varfloat", "int"); // %= applicable to int, float + checkExpr("varint %= vardouble", "int"); // %= applicable to int, double + checkExpr("varfloat %= varchar", "float"); // %= applicable to float, char + checkExpr("varfloat %= varbyte", "float"); // %= applicable to float, byte + checkExpr("varfloat %= varshort", "float"); // %= applicable to float, short + checkExpr("varfloat %= varint", "float"); // %= applicable to float, int + checkExpr("varfloat %= varlong", "float"); // %= applicable to float, long + checkExpr("varfloat %= varfloat", "float"); // %= applicable to float, float + checkExpr("varfloat %= vardouble", "float"); // %= applicable to float, double + checkExpr("vardouble %= varchar", "double"); // %= applicable to double, char + checkExpr("vardouble %= varbyte", "double"); // %= applicable to double, byte + checkExpr("vardouble %= varshort", "double"); // %= applicable to double, short + checkExpr("vardouble %= varint", "double"); // %= applicable to double, int + checkExpr("vardouble %= varlong", "double"); // %= applicable to double, long + checkExpr("vardouble %= varfloat", "double"); // %= applicable to double, float + checkExpr("vardouble %= vardouble", "double"); // %= applicable to double, double + } + + @Test + public void testInvalidModuloAssignmentExpression() throws IOException { + checkErrorExpr("varboolean %= varboolean", "0xA0178"); // %= not applicable to boolean, boolean + checkErrorExpr("varboolean %= varchar", "0xA0178"); // %= not applicable to boolean, char + checkErrorExpr("varboolean %= varbyte", "0xA0178"); // %= not applicable to boolean, byte + checkErrorExpr("varboolean %= varshort", "0xA0178"); // %= not applicable to boolean, short + checkErrorExpr("varboolean %= varint", "0xA0178"); // %= not applicable to boolean, int + checkErrorExpr("varboolean %= varlong", "0xA0178"); // %= not applicable to boolean, long + checkErrorExpr("varboolean %= varfloat", "0xA0178"); // %= not applicable to boolean, float + checkErrorExpr("varboolean %= vardouble", "0xA0178"); // %= not applicable to boolean, double + checkErrorExpr("varint%=\"Hello\"", "0xA0178"); // not possible because int = int % (int) String returns a casting error + } + + @Test + public void deriveFromAndAssignmentExpression() throws IOException { + checkExpr("varboolean &= varboolean", "boolean"); // &= applicable to boolean, boolean + checkExpr("varchar &= varchar", "char"); // &= applicable to char, char + checkExpr("varchar &= varbyte", "char"); // &= applicable to char, byte + checkExpr("varchar &= varshort", "char"); // &= applicable to char, short + checkExpr("varchar &= varint", "char"); // &= applicable to char, int + checkExpr("varchar &= varlong", "char"); // &= applicable to char, long + checkExpr("varbyte &= varchar", "byte"); // &= applicable to byte, char + checkExpr("varbyte &= varbyte", "byte"); // &= applicable to byte, byte + checkExpr("varbyte &= varshort", "byte"); // &= applicable to byte, short + checkExpr("varbyte &= varint", "byte"); // &= applicable to byte, int + checkExpr("varbyte &= varlong", "byte"); // &= applicable to byte, long + checkExpr("varshort &= varchar", "short"); // &= applicable to short, char + checkExpr("varshort &= varbyte", "short"); // &= applicable to short, byte + checkExpr("varshort &= varshort", "short"); // &= applicable to short, short + checkExpr("varshort &= varint", "short"); // &= applicable to short, int + checkExpr("varshort &= varlong", "short"); // &= applicable to short, long + checkExpr("varint &= varchar", "int"); // &= applicable to int, char + checkExpr("varint &= varbyte", "int"); // &= applicable to int, byte + checkExpr("varint &= varshort", "int"); // &= applicable to int, short + checkExpr("varint &= varint", "int"); // &= applicable to int, int + checkExpr("varint &= varlong", "int"); // &= applicable to int, long + checkExpr("varlong &= varchar", "long"); // &= applicable to long, char + checkExpr("varlong &= varbyte", "long"); // &= applicable to long, byte + checkExpr("varlong &= varshort", "long"); // &= applicable to long, short + checkExpr("varlong &= varint", "long"); // &= applicable to long, int + checkExpr("varlong &= varlong", "long"); // &= applicable to long, long + } + + @Test + public void testInvalidAndAssignmentExpression() throws IOException { + checkErrorExpr("varboolean &= varchar", "0xA0176"); // &= not applicable to boolean, char + checkErrorExpr("varboolean &= varbyte", "0xA0176"); // &= not applicable to boolean, byte + checkErrorExpr("varboolean &= varshort", "0xA0176"); // &= not applicable to boolean, short + checkErrorExpr("varboolean &= varint", "0xA0176"); // &= not applicable to boolean, int + checkErrorExpr("varboolean &= varlong", "0xA0176"); // &= not applicable to boolean, long + checkErrorExpr("varboolean &= varfloat", "0xA0176"); // &= not applicable to boolean, float + checkErrorExpr("varboolean &= vardouble", "0xA0176"); // &= not applicable to boolean, double + checkErrorExpr("varchar &= varboolean", "0xA0176"); // &= not applicable to char, boolean + checkErrorExpr("varchar &= varfloat", "0xA0176"); // &= not applicable to char, float + checkErrorExpr("varchar &= vardouble", "0xA0176"); // &= not applicable to char, double + checkErrorExpr("varbyte &= varboolean", "0xA0176"); // &= not applicable to byte, boolean + checkErrorExpr("varbyte &= varfloat", "0xA0176"); // &= not applicable to byte, float + checkErrorExpr("varbyte &= vardouble", "0xA0176"); // &= not applicable to byte, double + checkErrorExpr("varshort &= varboolean", "0xA0176"); // &= not applicable to short, boolean + checkErrorExpr("varshort &= varfloat", "0xA0176"); // &= not applicable to short, float + checkErrorExpr("varshort &= vardouble", "0xA0176"); // &= not applicable to short, double + checkErrorExpr("varint &= varboolean", "0xA0176"); // &= not applicable to int, boolean + checkErrorExpr("varint &= varfloat", "0xA0176"); // &= not applicable to int, float + checkErrorExpr("varint &= vardouble", "0xA0176"); // &= not applicable to int, double + checkErrorExpr("varlong &= varboolean", "0xA0176"); // &= not applicable to long, boolean + checkErrorExpr("varlong &= varfloat", "0xA0176"); // &= not applicable to long, float + checkErrorExpr("varlong &= vardouble", "0xA0176"); // &= not applicable to long, double + checkErrorExpr("varfloat &= varboolean", "0xA0176"); // &= not applicable to float, boolean + checkErrorExpr("varfloat &= varchar", "0xA0176"); // &= not applicable to float, char + checkErrorExpr("varfloat &= varbyte", "0xA0176"); // &= not applicable to float, byte + checkErrorExpr("varfloat &= varshort", "0xA0176"); // &= not applicable to float, short + checkErrorExpr("varfloat &= varint", "0xA0176"); // &= not applicable to float, int + checkErrorExpr("varfloat &= varlong", "0xA0176"); // &= not applicable to float, long + checkErrorExpr("varfloat &= varfloat", "0xA0176"); // &= not applicable to float, float + checkErrorExpr("varfloat &= vardouble", "0xA0176"); // &= not applicable to float, double + checkErrorExpr("vardouble &= varboolean", "0xA0176"); // &= not applicable to double, boolean + checkErrorExpr("vardouble &= varchar", "0xA0176"); // &= not applicable to double, char + checkErrorExpr("vardouble &= varbyte", "0xA0176"); // &= not applicable to double, byte + checkErrorExpr("vardouble &= varshort", "0xA0176"); // &= not applicable to double, short + checkErrorExpr("vardouble &= varint", "0xA0176"); // &= not applicable to double, int + checkErrorExpr("vardouble &= varlong", "0xA0176"); // &= not applicable to double, long + checkErrorExpr("vardouble &= varfloat", "0xA0176"); // &= not applicable to double, float + checkErrorExpr("vardouble &= vardouble", "0xA0176"); // &= not applicable double, double + checkErrorExpr("varint&=\"Hello\"", "0xA0176"); // not possible because int = int & (int) String returns a casting error + } + + @Test + public void deriveFromOrAssignmentExpression() throws IOException { + checkExpr("varboolean |= varboolean", "boolean"); // |= applicable to boolean, boolean + checkExpr("varchar |= varchar", "char"); // |= applicable to char, char + checkExpr("varchar |= varbyte", "char"); // |= applicable to char, byte + checkExpr("varchar |= varshort", "char"); // |= applicable to char, short + checkExpr("varchar |= varint", "char"); // |= applicable to char, int + checkExpr("varchar |= varlong", "char"); // |= applicable to char, long + checkExpr("varbyte |= varchar", "byte"); // |= applicable to byte, char + checkExpr("varbyte |= varbyte", "byte"); // |= applicable to byte, byte + checkExpr("varbyte |= varshort", "byte"); // |= applicable to byte, short + checkExpr("varbyte |= varint", "byte"); // |= applicable to byte, int + checkExpr("varbyte |= varlong", "byte"); // |= applicable to byte, long + checkExpr("varshort |= varchar", "short"); // |= applicable to short, char + checkExpr("varshort |= varbyte", "short"); // |= applicable to short, byte + checkExpr("varshort |= varshort", "short"); // |= applicable to short, short + checkExpr("varshort |= varint", "short"); // |= applicable to short, int + checkExpr("varshort |= varlong", "short"); // |= applicable to short, long + checkExpr("varint |= varchar", "int"); // |= applicable to int, char + checkExpr("varint |= varbyte", "int"); // |= applicable to int, byte + checkExpr("varint |= varshort", "int"); // |= applicable to int, short + checkExpr("varint |= varint", "int"); // |= applicable to int, int + checkExpr("varint |= varlong", "int"); // |= applicable to int, long + checkExpr("varlong |= varchar", "long"); // |= applicable to long, char + checkExpr("varlong |= varbyte", "long"); // |= applicable to long, byte + checkExpr("varlong |= varshort", "long"); // |= applicable to long, short + checkExpr("varlong |= varint", "long"); // |= applicable to long, int + checkExpr("varlong |= varlong", "long"); // |= applicable to long, long + } + + @Test + public void testInvalidOrAssignmentExpression() throws IOException { + checkErrorExpr("varboolean |= varchar", "0xA0176"); // |= not applicable to boolean, char + checkErrorExpr("varboolean |= varbyte", "0xA0176"); // |= not applicable to boolean, byte + checkErrorExpr("varboolean |= varshort", "0xA0176"); // |= not applicable to boolean, short + checkErrorExpr("varboolean |= varint", "0xA0176"); // |= not applicable to boolean, int + checkErrorExpr("varboolean |= varlong", "0xA0176"); // |= not applicable to boolean, long + checkErrorExpr("varboolean |= varfloat", "0xA0176"); // |= not applicable to boolean, float + checkErrorExpr("varboolean |= vardouble", "0xA0176"); // |= not applicable to boolean, double + checkErrorExpr("varchar |= varboolean", "0xA0176"); // |= not applicable to char, boolean + checkErrorExpr("varchar |= varfloat", "0xA0176"); // |= not applicable to char, float + checkErrorExpr("varchar |= vardouble", "0xA0176"); // |= not applicable to char, double + checkErrorExpr("varbyte |= varboolean", "0xA0176"); // |= not applicable to byte, boolean + checkErrorExpr("varbyte |= varfloat", "0xA0176"); // |= not applicable to byte, float + checkErrorExpr("varbyte |= vardouble", "0xA0176"); // |= not applicable to byte, double + checkErrorExpr("varshort |= varboolean", "0xA0176"); // |= not applicable to short, boolean + checkErrorExpr("varshort |= varfloat", "0xA0176"); // |= not applicable to short, float + checkErrorExpr("varshort |= vardouble", "0xA0176"); // |= not applicable to short, double + checkErrorExpr("varint |= varboolean", "0xA0176"); // |= not applicable to int, boolean + checkErrorExpr("varint |= varfloat", "0xA0176"); // |= not applicable to int, float + checkErrorExpr("varint |= vardouble", "0xA0176"); // |= not applicable to int, double + checkErrorExpr("varlong |= varboolean", "0xA0176"); // |= not applicable to long, boolean + checkErrorExpr("varlong |= varfloat", "0xA0176"); // |= not applicable to long, float + checkErrorExpr("varlong |= vardouble", "0xA0176"); // |= not applicable to long, double + checkErrorExpr("varfloat |= varboolean", "0xA0176"); // |= not applicable to float, boolean + checkErrorExpr("varfloat |= varchar", "0xA0176"); // |= not applicable to float, char + checkErrorExpr("varfloat |= varbyte", "0xA0176"); // |= not applicable to float, byte + checkErrorExpr("varfloat |= varshort", "0xA0176"); // |= not applicable to float, short + checkErrorExpr("varfloat |= varint", "0xA0176"); // |= not applicable to float, int + checkErrorExpr("varfloat |= varlong", "0xA0176"); // |= not applicable to float, long + checkErrorExpr("varfloat |= varfloat", "0xA0176"); // |= not applicable to float, float + checkErrorExpr("varfloat |= vardouble", "0xA0176"); // |= not applicable to float, double + checkErrorExpr("vardouble |= varboolean", "0xA0176"); // |= not applicable to double, boolean + checkErrorExpr("vardouble |= varchar", "0xA0176"); // |= not applicable to double, char + checkErrorExpr("vardouble |= varbyte", "0xA0176"); // |= not applicable to double, byte + checkErrorExpr("vardouble |= varshort", "0xA0176"); // |= not applicable to double, short + checkErrorExpr("vardouble |= varint", "0xA0176"); // |= not applicable to double, int + checkErrorExpr("vardouble |= varlong", "0xA0176"); // |= not applicable to double, long + checkErrorExpr("vardouble |= varfloat", "0xA0176"); // |= not applicable to double, float + checkErrorExpr("vardouble |= vardouble", "0xA0176"); // |= not applicable double, double + checkErrorExpr("varint|=\"Hello\"", "0xA0176"); // not possible because int = int | (int) String returns a casting error + } + @Test + public void deriveFromBinaryXorAssignmentExpression() throws IOException { + checkExpr("varboolean ^= varboolean", "boolean"); // ^= applicable to boolean, boolean + checkExpr("varchar ^= varchar", "char"); // ^= applicable to char, char + checkExpr("varchar ^= varbyte", "char"); // ^= applicable to char, byte + checkExpr("varchar ^= varshort", "char"); // ^= applicable to char, short + checkExpr("varchar ^= varint", "char"); // ^= applicable to char, int + checkExpr("varchar ^= varlong", "char"); // ^= applicable to char, long + checkExpr("varbyte ^= varchar", "byte"); // ^= applicable to byte, char + checkExpr("varbyte ^= varbyte", "byte"); // ^= applicable to byte, byte + checkExpr("varbyte ^= varshort", "byte"); // ^= applicable to byte, short + checkExpr("varbyte ^= varint", "byte"); // ^= applicable to byte, int + checkExpr("varbyte ^= varlong", "byte"); // ^= applicable to byte, long + checkExpr("varshort ^= varchar", "short"); // ^= applicable to short, char + checkExpr("varshort ^= varbyte", "short"); // ^= applicable to short, byte + checkExpr("varshort ^= varshort", "short"); // ^= applicable to short, short + checkExpr("varshort ^= varint", "short"); // ^= applicable to short, int + checkExpr("varshort ^= varlong", "short"); // ^= applicable to short, long + checkExpr("varint ^= varchar", "int"); // ^= applicable to int, char + checkExpr("varint ^= varbyte", "int"); // ^= applicable to int, byte + checkExpr("varint ^= varshort", "int"); // ^= applicable to int, short + checkExpr("varint ^= varint", "int"); // ^= applicable to int, int + checkExpr("varint ^= varlong", "int"); // ^= applicable to int, long + checkExpr("varlong ^= varchar", "long"); // ^= applicable to long, char + checkExpr("varlong ^= varbyte", "long"); // ^= applicable to long, byte + checkExpr("varlong ^= varshort", "long"); // ^= applicable to long, short + checkExpr("varlong ^= varint", "long"); // ^= applicable to long, int + checkExpr("varlong ^= varlong", "long"); // ^= applicable to long, long + } + + @Test + public void testInvalidBinaryXorAssignmentExpression() throws IOException { + checkErrorExpr("varboolean ^= varchar", "0xA0176"); // ^= not applicable to boolean, char + checkErrorExpr("varboolean ^= varbyte", "0xA0176"); // ^= not applicable to boolean, byte + checkErrorExpr("varboolean ^= varshort", "0xA0176"); // ^= not applicable to boolean, short + checkErrorExpr("varboolean ^= varint", "0xA0176"); // ^= not applicable to boolean, int + checkErrorExpr("varboolean ^= varlong", "0xA0176"); // ^= not applicable to boolean, long + checkErrorExpr("varboolean ^= varfloat", "0xA0176"); // ^= not applicable to boolean, float + checkErrorExpr("varboolean ^= vardouble", "0xA0176"); // ^= not applicable to boolean, double + checkErrorExpr("varchar ^= varboolean", "0xA0176"); // ^= not applicable to char, boolean + checkErrorExpr("varchar ^= varfloat", "0xA0176"); // ^= not applicable to char, float + checkErrorExpr("varchar ^= vardouble", "0xA0176"); // ^= not applicable to char, double + checkErrorExpr("varbyte ^= varboolean", "0xA0176"); // ^= not applicable to byte, boolean + checkErrorExpr("varbyte ^= varfloat", "0xA0176"); // ^= not applicable to byte, float + checkErrorExpr("varbyte ^= vardouble", "0xA0176"); // ^= not applicable to byte, double + checkErrorExpr("varshort ^= varboolean", "0xA0176"); // ^= not applicable to short, boolean + checkErrorExpr("varshort ^= varfloat", "0xA0176"); // ^= not applicable to short, float + checkErrorExpr("varshort ^= vardouble", "0xA0176"); // ^= not applicable to short, double + checkErrorExpr("varint ^= varboolean", "0xA0176"); // ^= not applicable to int, boolean + checkErrorExpr("varint ^= varfloat", "0xA0176"); // ^= not applicable to int, float + checkErrorExpr("varint ^= vardouble", "0xA0176"); // ^= not applicable to int, double + checkErrorExpr("varlong ^= varboolean", "0xA0176"); // ^= not applicable to long, boolean + checkErrorExpr("varlong ^= varfloat", "0xA0176"); // ^= not applicable to long, float + checkErrorExpr("varlong ^= vardouble", "0xA0176"); // ^= not applicable to long, double + checkErrorExpr("varfloat ^= varboolean", "0xA0176"); // ^= not applicable to float, boolean + checkErrorExpr("varfloat ^= varchar", "0xA0176"); // ^= not applicable to float, char + checkErrorExpr("varfloat ^= varbyte", "0xA0176"); // ^= not applicable to float, byte + checkErrorExpr("varfloat ^= varshort", "0xA0176"); // ^= not applicable to float, short + checkErrorExpr("varfloat ^= varint", "0xA0176"); // ^= not applicable to float, int + checkErrorExpr("varfloat ^= varlong", "0xA0176"); // ^= not applicable to float, long + checkErrorExpr("varfloat ^= varfloat", "0xA0176"); // ^= not applicable to float, float + checkErrorExpr("varfloat ^= vardouble", "0xA0176"); // ^= not applicable to float, double + checkErrorExpr("vardouble ^= varboolean", "0xA0176"); // ^= not applicable to double, boolean + checkErrorExpr("vardouble ^= varchar", "0xA0176"); // ^= not applicable to double, char + checkErrorExpr("vardouble ^= varbyte", "0xA0176"); // ^= not applicable to double, byte + checkErrorExpr("vardouble ^= varshort", "0xA0176"); // ^= not applicable to double, short + checkErrorExpr("vardouble ^= varint", "0xA0176"); // ^= not applicable to double, int + checkErrorExpr("vardouble ^= varlong", "0xA0176"); // ^= not applicable to double, long + checkErrorExpr("vardouble ^= varfloat", "0xA0176"); // ^= not applicable to double, float + checkErrorExpr("vardouble ^= vardouble", "0xA0176"); // ^= not applicable to double, double + checkErrorExpr("varint^=\"Hello\"", "0xA0176"); // not possible because int = int ^ (int) String returns a casting error + } + + @Test + public void deriveFromDoubleLeftAssignmentExpression() throws IOException { + checkExpr("varchar <<= varchar", "char"); // <<= applicable to char, char + checkExpr("varchar <<= varbyte", "char"); // <<= applicable to char, byte + checkExpr("varchar <<= varshort", "char"); // <<= applicable to char, short + checkExpr("varchar <<= varint", "char"); // <<= applicable to char, int + checkExpr("varchar <<= varlong", "char"); // <<= applicable to char, long + checkExpr("varbyte <<= varchar", "byte"); // <<= applicable to byte, char + checkExpr("varbyte <<= varbyte", "byte"); // <<= applicable to byte, byte + checkExpr("varbyte <<= varshort", "byte"); // <<= applicable to byte, short + checkExpr("varbyte <<= varint", "byte"); // <<= applicable to byte, int + checkExpr("varbyte <<= varlong", "byte"); // <<= applicable to byte, long + checkExpr("varshort <<= varchar", "short"); // <<= applicable to short, char + checkExpr("varshort <<= varbyte", "short"); // <<= applicable to short, byte + checkExpr("varshort <<= varshort", "short"); // <<= applicable to short, short + checkExpr("varshort <<= varint", "short"); // <<= applicable to short, int + checkExpr("varshort <<= varlong", "short"); // <<= applicable to short, long + checkExpr("varint <<= varchar", "int"); // <<= applicable to int, char + checkExpr("varint <<= varbyte", "int"); // <<= applicable to int, byte + checkExpr("varint <<= varshort", "int"); // <<= applicable to int, short + checkExpr("varint <<= varint", "int"); // <<= applicable to int, int + checkExpr("varint <<= varlong", "int"); // <<= applicable to int, long + checkExpr("varlong <<= varchar", "long"); // <<= applicable to long, char + checkExpr("varlong <<= varbyte", "long"); // <<= applicable to long, byte + checkExpr("varlong <<= varshort", "long"); // <<= applicable to long, short + checkExpr("varlong <<= varint", "long"); // <<= applicable to long, int + checkExpr("varlong <<= varlong", "long"); // <<= applicable to long, long + } + + @Test + public void testInvvarlidDoubleLeftvarssignmentExpression() throws IOException { + checkErrorExpr("varboolean <<= varboolean", "0xA0177"); // <<= not applicable to boolean, boolean + checkErrorExpr("varboolean <<= varchar", "0xA0177"); // <<= not applicable to boolean, char + checkErrorExpr("varboolean <<= varbyte", "0xA0177"); // <<= not applicable to boolean, byte + checkErrorExpr("varboolean <<= varshort", "0xA0177"); // <<= not applicable to boolean, short + checkErrorExpr("varboolean <<= varint", "0xA0177"); // <<= not applicable to boolean, int + checkErrorExpr("varboolean <<= varlong", "0xA0177"); // <<= not applicable to boolean, long + checkErrorExpr("varboolean <<= varfloat", "0xA0177"); // <<= not applicable to boolean, float + checkErrorExpr("varboolean <<= vardouble", "0xA0177"); // <<= not applicable to boolean, double + checkErrorExpr("varfloat <<= varboolean", "0xA0177"); // <<= not applicable to float, boolean + checkErrorExpr("varfloat <<= varchar", "0xA0177"); // <<= not applicable to float, char + checkErrorExpr("varfloat <<= varbyte", "0xA0177"); // <<= not applicable to float, byte + checkErrorExpr("varfloat <<= varshort", "0xA0177"); // <<= not applicable to float, short + checkErrorExpr("varfloat <<= varint", "0xA0177"); // <<= not applicable to float, int + checkErrorExpr("varfloat <<= varlong", "0xA0177"); // <<= not applicable to float, long + checkErrorExpr("varfloat <<= varfloat", "0xA0177"); // <<= not applicable to float, float + checkErrorExpr("varfloat <<= vardouble", "0xA0177"); // <<= not applicable to float, double + checkErrorExpr("vardouble <<= varboolean", "0xA0177"); // <<= not applicable to double, boolean + checkErrorExpr("vardouble <<= varchar", "0xA0177"); // <<= not applicable to double, char + checkErrorExpr("vardouble <<= varbyte", "0xA0177"); // <<= not applicable to double, byte + checkErrorExpr("vardouble <<= varshort", "0xA0177"); // <<= not applicable to double, short + checkErrorExpr("vardouble <<= varint", "0xA0177"); // <<= not applicable to double, int + checkErrorExpr("vardouble <<= varlong", "0xA0177"); // <<= not applicable to double, long + checkErrorExpr("vardouble <<= varfloat", "0xA0177"); // <<= not applicable to double, float + checkErrorExpr("vardouble <<= vardouble", "0xA0177"); // <<= not applicable to double, double + checkErrorExpr("varint<<=\"Hello\"", "0xA0177"); // not possible because int = int << (int) String returns a cvarsting error + } + + @Test + public void deriveFromDoubleRightAssignmentExpression() throws IOException { + checkExpr("varchar >>= varchar", "char"); // >>= applicable to char, char + checkExpr("varchar >>= varbyte", "char"); // >>= applicable to char, byte + checkExpr("varchar >>= varshort", "char"); // >>= applicable to char, short + checkExpr("varchar >>= varint", "char"); // >>= applicable to char, int + checkExpr("varchar >>= varlong", "char"); // >>= applicable to char, long + checkExpr("varbyte >>= varchar", "byte"); // >>= applicable to byte, char + checkExpr("varbyte >>= varbyte", "byte"); // >>= applicable to byte, byte + checkExpr("varbyte >>= varshort", "byte"); // >>= applicable to byte, short + checkExpr("varbyte >>= varint", "byte"); // >>= applicable to byte, int + checkExpr("varbyte >>= varlong", "byte"); // >>= applicable to byte, long + checkExpr("varshort >>= varchar", "short"); // >>= applicable to short, char + checkExpr("varshort >>= varbyte", "short"); // >>= applicable to short, byte + checkExpr("varshort >>= varshort", "short"); // >>= applicable to short, short + checkExpr("varshort >>= varint", "short"); // >>= applicable to short, int + checkExpr("varshort >>= varlong", "short"); // >>= applicable to short, long + checkExpr("varint >>= varchar", "int"); // >>= applicable to int, char + checkExpr("varint >>= varbyte", "int"); // >>= applicable to int, byte + checkExpr("varint >>= varshort", "int"); // >>= applicable to int, short + checkExpr("varint >>= varint", "int"); // >>= applicable to int, int + checkExpr("varint >>= varlong", "int"); // >>= applicable to int, long + checkExpr("varlong >>= varchar", "long"); // >>= applicable to long, char + checkExpr("varlong >>= varbyte", "long"); // >>= applicable to long, byte + checkExpr("varlong >>= varshort", "long"); // >>= applicable to long, short + checkExpr("varlong >>= varint", "long"); // >>= applicable to long, int + checkExpr("varlong >>= varlong", "long"); // >>= applicable to long, long + } + + @Test + public void testInvalidDoubleRightAssignmentExpression() throws IOException { + checkErrorExpr("varboolean >>= varboolean", "0xA0177"); // >>= not applicable to boolean, boolean + checkErrorExpr("varboolean >>= varchar", "0xA0177"); // >>= not applicable to boolean, char + checkErrorExpr("varboolean >>= varbyte", "0xA0177"); // >>= not applicable to boolean, byte + checkErrorExpr("varboolean >>= varshort", "0xA0177"); // >>= not applicable to boolean, short + checkErrorExpr("varboolean >>= varint", "0xA0177"); // >>= not applicable to boolean, int + checkErrorExpr("varboolean >>= varlong", "0xA0177"); // >>= not applicable to boolean, long + checkErrorExpr("varboolean >>= varfloat", "0xA0177"); // >>= not applicable to boolean, float + checkErrorExpr("varboolean >>= vardouble", "0xA0177"); // >>= not applicable to boolean, double + checkErrorExpr("varfloat >>= varboolean", "0xA0177"); // >>= not applicable to float, boolean + checkErrorExpr("varfloat >>= varchar", "0xA0177"); // >>= not applicable to float, char + checkErrorExpr("varfloat >>= varbyte", "0xA0177"); // >>= not applicable to float, byte + checkErrorExpr("varfloat >>= varshort", "0xA0177"); // >>= not applicable to float, short + checkErrorExpr("varfloat >>= varint", "0xA0177"); // >>= not applicable to float, int + checkErrorExpr("varfloat >>= varlong", "0xA0177"); // >>= not applicable to float, long + checkErrorExpr("varfloat >>= varfloat", "0xA0177"); // >>= not applicable to float, float + checkErrorExpr("varfloat >>= vardouble", "0xA0177"); // >>= not applicable to float, double + checkErrorExpr("vardouble >>= varboolean", "0xA0177"); // >>= not applicable to double, boolean + checkErrorExpr("vardouble >>= varchar", "0xA0177"); // >>= not applicable to double, char + checkErrorExpr("vardouble >>= varbyte", "0xA0177"); // >>= not applicable to double, byte + checkErrorExpr("vardouble >>= varshort", "0xA0177"); // >>= not applicable to double, short + checkErrorExpr("vardouble >>= varint", "0xA0177"); // >>= not applicable to double, int + checkErrorExpr("vardouble >>= varlong", "0xA0177"); // >>= not applicable to double, long + checkErrorExpr("vardouble >>= varfloat", "0xA0177"); // >>= not applicable to double, float + checkErrorExpr("vardouble >>= vardouble", "0xA0177"); // >>= not applicable to double, double + checkErrorExpr("varint>>=\"Hello\"", "0xA0177"); // not possible because int = int >> (int) String returns a cvarsting error + } + + @Test + public void deriveFromLogicalRightAssignmentExpression() throws IOException { + checkExpr("varchar >>>= varchar", "char"); // >>>= applicable to char, char + checkExpr("varchar >>>= varbyte", "char"); // >>>= applicable to char, byte + checkExpr("varchar >>>= varshort", "char"); // >>>= applicable to char, short + checkExpr("varchar >>>= varint", "char"); // >>>= applicable to char, int + checkExpr("varchar >>>= varlong", "char"); // >>>= applicable to char, long + checkExpr("varbyte >>>= varchar", "byte"); // >>>= applicable to byte, char + checkExpr("varbyte >>>= varbyte", "byte"); // >>>= applicable to byte, byte + checkExpr("varbyte >>>= varshort", "byte"); // >>>= applicable to byte, short + checkExpr("varbyte >>>= varint", "byte"); // >>>= applicable to byte, int + checkExpr("varbyte >>>= varlong", "byte"); // >>>= applicable to byte, long + checkExpr("varshort >>>= varchar", "short"); // >>>= applicable to short, char + checkExpr("varshort >>>= varbyte", "short"); // >>>= applicable to short, byte + checkExpr("varshort >>>= varshort", "short"); // >>>= applicable to short, short + checkExpr("varshort >>>= varint", "short"); // >>>= applicable to short, int + checkExpr("varshort >>>= varlong", "short"); // >>>= applicable to short, long + checkExpr("varint >>>= varchar", "int"); // >>>= applicable to int, char + checkExpr("varint >>>= varbyte", "int"); // >>>= applicable to int, byte + checkExpr("varint >>>= varshort", "int"); // >>>= applicable to int, short + checkExpr("varint >>>= varint", "int"); // >>>= applicable to int, int + checkExpr("varint >>>= varlong", "int"); // >>>= applicable to int, long + checkExpr("varlong >>>= varchar", "long"); // >>>= applicable to long, char + checkExpr("varlong >>>= varbyte", "long"); // >>>= applicable to long, byte + checkExpr("varlong >>>= varshort", "long"); // >>>= applicable to long, short + checkExpr("varlong >>>= varint", "long"); // >>>= applicable to long, int + checkExpr("varlong >>>= varlong", "long"); // >>>= applicable to long, long + } + + @Test + public void testInvalidLogicalRightAssignmentExpression() throws IOException { + checkErrorExpr("varboolean >>>= varboolean", "0xA0177"); // >>>= not applicable to boolean, boolean + checkErrorExpr("varboolean >>>= varchar", "0xA0177"); // >>>= not applicable to boolean, char + checkErrorExpr("varboolean >>>= varbyte", "0xA0177"); // >>>= not applicable to boolean, byte + checkErrorExpr("varboolean >>>= varshort", "0xA0177"); // >>>= not applicable to boolean, short + checkErrorExpr("varboolean >>>= varint", "0xA0177"); // >>>= not applicable to boolean, int + checkErrorExpr("varboolean >>>= varlong", "0xA0177"); // >>>= not applicable to boolean, long + checkErrorExpr("varboolean >>>= varfloat", "0xA0177"); // >>>= not applicable to boolean, float + checkErrorExpr("varboolean >>>= vardouble", "0xA0177"); // >>>= not applicable to boolean, double + checkErrorExpr("varfloat >>>= varboolean", "0xA0177"); // >>>= not applicable to float, boolean + checkErrorExpr("varfloat >>>= varchar", "0xA0177"); // >>>= not applicable to float, char + checkErrorExpr("varfloat >>>= varbyte", "0xA0177"); // >>>= not applicable to float, byte + checkErrorExpr("varfloat >>>= varshort", "0xA0177"); // >>>= not applicable to float, short + checkErrorExpr("varfloat >>>= varint", "0xA0177"); // >>>= not applicable to float, int + checkErrorExpr("varfloat >>>= varlong", "0xA0177"); // >>>= not applicable to float, long + checkErrorExpr("varfloat >>>= varfloat", "0xA0177"); // >>>= not applicable to float, float + checkErrorExpr("varfloat >>>= vardouble", "0xA0177"); // >>>= not applicable to float, double + checkErrorExpr("vardouble >>>= varboolean", "0xA0177"); // >>>= not applicable to double, boolean + checkErrorExpr("vardouble >>>= varchar", "0xA0177"); // >>>= not applicable to double, char + checkErrorExpr("vardouble >>>= varbyte", "0xA0177"); // >>>= not applicable to double, byte + checkErrorExpr("vardouble >>>= varshort", "0xA0177"); // >>>= not applicable to double, short + checkErrorExpr("vardouble >>>= varint", "0xA0177"); // >>>= not applicable to double, int + checkErrorExpr("vardouble >>>= varlong", "0xA0177"); // >>>= not applicable to double, long + checkErrorExpr("vardouble >>>= varfloat", "0xA0177"); // >>>= not applicable to double, float + checkErrorExpr("vardouble >>>= vardouble", "0xA0177"); // >>>= not applicable to double, double + checkErrorExpr("varint>>>=\"Hello\"", "0xA0177"); // not possible because int = int >>> (int) String returns a casting error + } + + @Test + public void deriveFromRegularAssignmentExpression() throws IOException { + checkExpr("varlong = 0l", "long"); // expected long and provided long + checkExpr("varlong = +1l", "long"); // expected long and provided long + checkExpr("varlong = -1l", "long"); // expected long and provided long + checkExpr("varlong = 9223372036854775807l", "long"); // expected long and provided long (max value)) + checkExpr("varlong = -9223372036854775808l", "long"); // expected long and provided long (min value)) + checkExpr("varlong = varchar", "long"); // expected long and provided long (char conversion) + checkExpr("varlong = varbyte", "long"); // expected long and provided long (byte conversion) + checkExpr("varlong = varshort", "long"); // expected long and provided long (short conversion) + checkExpr("varlong = varint", "long"); // expected long and provided long (int conversion) + checkExpr("varlong = varlong", "long"); // expected long and provided long + checkExpr("varfloat = 'a'", "float"); // expected float and provided float (char conversion) + checkExpr("varfloat = 0", "float"); // expected float and provided float (int conversion) + checkExpr("varfloat = 0l", "float"); // expected float and provided float (long conversion) + checkExpr("varfloat = 0.0f", "float"); // expected float and provided float + checkExpr("varfloat = +0.1f", "float"); // expected float and provided float + checkExpr("varfloat = -0.1f", "float"); // expected float and provided float + checkExpr("varfloat = varchar", "float"); // expected float and provided float (char conversion) + checkExpr("varfloat = varbyte", "float"); // expected float and provided float (byte conversion) + checkExpr("varfloat = varshort", "float"); // expected float and provided float (short conversion) + checkExpr("varfloat = varint", "float"); // expected float and provided float (int conversion) + checkExpr("varfloat = varlong", "float"); // expected float and provided float (long conversion) + checkExpr("varfloat = varfloat", "float"); // expected float and provided float + checkExpr("vardouble = 'a'", "double"); // expected double and provided double (char conversion) + checkExpr("vardouble = 0", "double"); // expected double and provided double (int conversion) + checkExpr("vardouble = 0l", "double"); // expected double and provided double (long conversion) + checkExpr("vardouble = 0.0f", "double"); // expected double and provided double (float conversion) + checkExpr("vardouble = 0.0", "double"); // expected double and provided double + checkExpr("vardouble = +0.1", "double"); // expected double and provided double + checkExpr("vardouble = -0.1", "double"); // expected double and provided double + checkExpr("vardouble = varchar", "double"); // expected double and provided double (char conversion) + checkExpr("vardouble = varbyte", "double"); // expected double and provided double (byte conversion) + checkExpr("vardouble = varshort", "double"); // expected double and provided double (short conversion) + checkExpr("vardouble = varint", "double"); // expected double and provided double (int conversion) + checkExpr("vardouble = varlong", "double"); // expected double and provided double (long conversion) + checkExpr("vardouble = varfloat", "double"); // expected double and provided double (float conversion) + checkExpr("vardouble = vardouble", "double"); // expected double and provided double + checkExpr("person1 = student2", "Person"); // example with person - student + checkExpr("person2 = csStudent1", "Person"); // example with person - firstsemesterstudent + checkExpr("varBoolean = varboolean", "java.lang.Boolean"); // example with Boolean - boolean + checkExpr("varboolean = varBoolean", "boolean"); // example with boolean - Boolean + } + + @Test + public void testInvalidRegularAssignmentExpression() throws IOException { + checkErrorExpr("varbyte = varlong", "0xA0179"); // expected byte but provided long + checkErrorExpr("varbyte = varfloat", "0xA0179"); // expected byte but provided float + checkErrorExpr("varbyte = vardouble", "0xA0179"); // expected byte but provided double + checkErrorExpr("varshort = true", "0xA0179"); // expected short but provided boolean + checkErrorExpr("varshort = false", "0xA0179"); // expected short but provided boolean + checkErrorExpr("varshort = 32768", "0xA0179"); // expected short but provided int + checkErrorExpr("varshort = -32769", "0xA0179"); // expected short but provided int + checkErrorExpr("varshort = 1l", "0xA0179"); // expected short but provided long + checkErrorExpr("varshort = 0.1f", "0xA0179"); // expected short but provided float + checkErrorExpr("varshort = 0.1", "0xA0179"); // expected short but provided double + checkErrorExpr("varshort = varboolean", "0xA0179"); // expected short but provided boolean + checkErrorExpr("varshort = varchar", "0xA0179"); // expected short but provided char + checkErrorExpr("varshort = varint", "0xA0179"); // expected short but provided int + checkErrorExpr("varshort = varlong", "0xA0179"); // expected short but provided long + checkErrorExpr("varshort = varfloat", "0xA0179"); // expected short but provided float + checkErrorExpr("varshort = vardouble", "0xA0179"); // expected short but provided double + checkErrorExpr("varint = true", "0xA0179"); // expected int but provided boolean + checkErrorExpr("varint = false", "0xA0179"); // expected int but provided boolean + checkErrorExpr("varint = 1l", "0xA0179"); // expected int but provided long + checkErrorExpr("varint = 0.1f", "0xA0179"); // expected int but provided float + checkErrorExpr("varint = 0.1", "0xA0179"); // expected int but provided double + checkErrorExpr("varint = varboolean", "0xA0179"); // expected int but provided boolean + checkErrorExpr("varint = varlong", "0xA0179"); // expected int but provided long + checkErrorExpr("varint = varfloat", "0xA0179"); // expected int but provided float + checkErrorExpr("varint = vardouble", "0xA0179"); // expected int but provided double + checkErrorExpr("varlong = true", "0xA0179"); // expected long but provided boolean + checkErrorExpr("varlong = false", "0xA0179"); // expected long but provided boolean + checkErrorExpr("varlong = 0.1f", "0xA0179"); // expected long but provided float + checkErrorExpr("varlong = 0.1", "0xA0179"); // expected long but provided double + checkErrorExpr("varlong = varboolean", "0xA0179"); // expected long but provided boolean + checkErrorExpr("varlong = varfloat", "0xA0179"); // expected long but provided float + checkErrorExpr("varlong = vardouble", "0xA0179"); // expected long but provided double + checkErrorExpr("varfloat = true", "0xA0179"); // expected float but provided boolean + checkErrorExpr("varfloat = false", "0xA0179"); // expected float but provided boolean + checkErrorExpr("varfloat = 0.1", "0xA0179"); // expected float but provided double + checkErrorExpr("varfloat = varboolean", "0xA0179"); // expected float but provided boolean + checkErrorExpr("varfloat = vardouble", "0xA0179"); // expected float but provided double + checkErrorExpr("vardouble = true", "0xA0179"); // expected double but provided boolean + checkErrorExpr("vardouble = false", "0xA0179"); // expected double but provided boolean + checkErrorExpr("vardouble = varboolean", "0xA0179"); // expected double but provided boolean + checkErrorExpr("varint=\"Hello\"", "0xA0179"); // not possible because int = (int) String returns a cvarsting error + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types3/BitExpressionsTypeVisitorTest.java b/monticore-grammar/src/test/java/de/monticore/types3/BitExpressionsTypeVisitorTest.java new file mode 100644 index 0000000000..748a171acb --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types3/BitExpressionsTypeVisitorTest.java @@ -0,0 +1,123 @@ +package de.monticore.types3; + +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; + +public class BitExpressionsTypeVisitorTest extends AbstractTypeVisitorTest { + + @Before + public void init() { + setupValues(); + } + + /** + * test LeftShiftExpression + */ + @Test + public void deriveFromLeftShiftExpressionTest() throws IOException { + //example with int - int + checkExpr("3<<5", "int"); + + //example with char - long + checkExpr("\'a\'<<4L", "int"); + } + + @Test + public void testInvalidLeftShiftExpression() throws IOException { + //only possible with integral types + checkErrorExpr("3<<4.5", "0xC0201"); + } + + /** + * test rightShiftExpression + */ + @Test + public void deriveFromRightShiftExpression() throws IOException { + //example with int - int + checkExpr("3>>5", "int"); + + //example with long - long + checkExpr("6L>>4L", "long"); + } + + @Test + public void testInvalidRightShiftExpression() throws IOException { + //only possible with integral types + checkErrorExpr("3>>4.5", "0xC0201"); + } + + /** + * test LogicalRightExpression + */ + @Test + public void deriveFromLogicalRightExpression() throws IOException { + //example with int - int + checkExpr("3>>>5", "int"); + + //example with int - long + checkExpr("12>>>4L", "int"); + } + + @Test + public void testInvalidLogicalRightExpression() throws IOException { + //only possible with integral types + checkErrorExpr("3>>>4.5", "0xC0201"); + } + + /** + * test BinaryOrOpExpression + */ + @Test + public void deriveFromBinaryOrOpExpression() throws IOException { + //example with int - int + checkExpr("3|5", "int"); + + //example with char - long + checkExpr("\'a\'|4L", "long"); + } + + @Test + public void testInvalidBinaryOrOpExpression() throws IOException { + //only possible with integral types + checkErrorExpr("3|4.5", "0xC0203"); + } + + /** + * test BinaryAndExpression + */ + @Test + public void deriveFromBinaryAndExpression() throws IOException { + //example with int - int + checkExpr("3&5", "int"); + + //example with long - long + checkExpr("4L&12L", "long"); + } + + @Test + public void testInvalidBinaryAndExpression() throws IOException { + //only possible with integral types + checkErrorExpr("3&4.5", "0xC0203"); + } + + /** + * test BinaryXorExpression + */ + @Test + public void deriveFromBinaryXorExpression() throws IOException { + //example with int - int + checkExpr("3^5", "int"); + + //example with boolean - boolean + checkExpr("true^false", "boolean"); + } + + @Test + public void testInvalidBinaryXorExpression() throws IOException { + //only possible with integral types + checkErrorExpr("3^4.5", "0xC0203"); + } +} + diff --git a/monticore-grammar/src/test/java/de/monticore/types3/CommonExpressionTypeVisitorTest.java b/monticore-grammar/src/test/java/de/monticore/types3/CommonExpressionTypeVisitorTest.java new file mode 100644 index 0000000000..aee339ec4b --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types3/CommonExpressionTypeVisitorTest.java @@ -0,0 +1,2760 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types3; + +import com.google.common.collect.Lists; +import de.monticore.expressions.combineexpressionswithliterals.CombineExpressionsWithLiteralsMill; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.ICombineExpressionsWithLiteralsArtifactScope; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.ICombineExpressionsWithLiteralsGlobalScope; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.ICombineExpressionsWithLiteralsScope; +import de.monticore.expressions.expressionsbasis.ExpressionsBasisMill; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.expressionsbasis._visitor.ExpressionsBasisTraverser; +import de.monticore.expressions.expressionsbasis._visitor.ExpressionsBasisVisitor2; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.FunctionSymbol; +import de.monticore.symbols.basicsymbols._symboltable.IBasicSymbolsGlobalScope; +import de.monticore.symbols.basicsymbols._symboltable.IBasicSymbolsScope; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.symbols.basicsymbols._symboltable.TypeVarSymbol; +import de.monticore.symbols.basicsymbols._symboltable.VariableSymbol; +import de.monticore.symbols.oosymbols.OOSymbolsMill; +import de.monticore.symbols.oosymbols._symboltable.FieldSymbol; +import de.monticore.symbols.oosymbols._symboltable.IOOSymbolsGlobalScope; +import de.monticore.symbols.oosymbols._symboltable.IOOSymbolsScope; +import de.monticore.symbols.oosymbols._symboltable.MethodSymbol; +import de.monticore.symbols.oosymbols._symboltable.OOTypeSymbol; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types3.util.SymTypeRelations; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +import static de.monticore.types.check.SymTypeExpressionFactory.createGenerics; +import static de.monticore.types.check.SymTypeExpressionFactory.createIntersection; +import static de.monticore.types.check.SymTypeExpressionFactory.createTypeArray; +import static de.monticore.types.check.SymTypeExpressionFactory.createTypeObject; +import static de.monticore.types.check.SymTypeExpressionFactory.createTypeVariable; +import static de.monticore.types.check.SymTypeExpressionFactory.createUnion; +import static de.monticore.types3.util.DefsTypesForTests.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class CommonExpressionTypeVisitorTest + extends AbstractTypeVisitorTest { + + @Before + public void init() { + setupValues(); + } + + @Test + public void deriveFromPlusExpression() throws IOException { + checkExpr("varbyte + varbyte", "int"); // + applicable to byte, byte, result is int + checkExpr("varshort + varshort", "int"); // + applicable to short, short, result is int + checkExpr("varint + varint", "int"); // + applicable to int, int, result is int + checkExpr("varlong + varlong", "long"); // + applicable to long, long, result is long + checkExpr("varfloat + varfloat", "float"); // + applicable to float, float, result is float + checkExpr("vardouble + vardouble", "double"); // + applicable to double, double, result is double + checkExpr("0 + 0", "int"); // expected int and provided int + checkExpr("127 + 1", "int"); // expected int and provided int + checkExpr("32767 + 1", "int"); // expected int and provided int + checkExpr("65536 + 1", "int"); // expected int and provided int + checkExpr("2147483647 + 0", "int"); // expected int and provided int + checkExpr("varchar + varchar", "int"); // + applicable to char, char, result is int + checkExpr("3 + \"Hallo\"", "String"); // example with String + } + + @Test + public void testInvalidPlusExpression() throws IOException { + checkErrorExpr("varchar = 65535 + 1", "0xA0179"); // expected char but provided int + checkErrorExpr("varchar = 1 + 65535", "0xA0179"); // expected char but provided int + checkErrorExpr("varchar = varchar + varchar", "0xA0179"); // expected char but provided int + checkErrorExpr("varboolean + varboolean", "0xB0163"); // + not applicable to boolean, boolean + checkErrorExpr("varboolean + varchar", "0xB0163"); // + not applicable to boolean, char + checkErrorExpr("varboolean + varbyte", "0xB0163"); // + not applicable to boolean, byte + checkErrorExpr("varboolean + varshort", "0xB0163"); // + not applicable to boolean, short + checkErrorExpr("varboolean + varint", "0xB0163"); // + not applicable to boolean, int + checkErrorExpr("varboolean + varlong", "0xB0163"); // + not applicable to boolean, long + checkErrorExpr("varboolean + varfloat", "0xB0163"); // + not applicable to boolean, float + checkErrorExpr("varboolean + vardouble", "0xB0163"); // + not applicable to boolean, double + checkErrorExpr("varchar + varboolean", "0xB0163"); // + not applicable to char, boolean + checkErrorExpr("varbyte + varboolean", "0xB0163"); // + not applicable to byte, boolean + checkErrorExpr("varshort + varboolean", "0xB0163"); // + not applicable to short, boolean + checkErrorExpr("varint + varboolean", "0xB0163"); // + not applicable to int, boolean + checkErrorExpr("varlong + varboolean", "0xB0163"); // + not applicable to long, boolean + checkErrorExpr("varfloat + varboolean", "0xB0163"); // + not applicable to float, boolean + checkErrorExpr("vardouble + varboolean", "0xB0163"); // + not applicable to double, boolean + checkErrorExpr("varchar = 1 + 1l", "0xA0179"); // expected char but provided long + checkErrorExpr("varchar = 1l + 1", "0xA0179"); // expected char but provided long + checkErrorExpr("varchar = 1 + 0.1f", "0xA0179"); // expected char but provided float + checkErrorExpr("varchar = 0.1f + 1", "0xA0179"); // expected char but provided float + checkErrorExpr("varchar = 1 + 0.1", "0xA0179"); // expected char but provided double + checkErrorExpr("varchar = 0.1 + 1", "0xA0179"); // expected char but provided double + checkErrorExpr("varbyte = 127 + 1", "0xA0179"); // expected byte but provided int + checkErrorExpr("varbyte = 1 + 127", "0xA0179"); // expected byte but provided int + checkErrorExpr("varbyte = varbyte + varbyte", "0xA0179"); // expected byte but provided int + checkErrorExpr("varbyte = 1 + 1l", "0xA0179"); // expected byte but provided long + checkErrorExpr("varbyte = 1l + 1", "0xA0179"); // expected byte but provided long + checkErrorExpr("varbyte = 1 + 0.1f", "0xA0179"); // expected byte but provided float + checkErrorExpr("varbyte = 0.1f + 1", "0xA0179"); // expected byte but provided float + checkErrorExpr("varbyte = 1 + 0.1", "0xA0179"); // expected byte but provided double + checkErrorExpr("varbyte = 0.1 + 1", "0xA0179"); // expected byte but provided double + checkErrorExpr("varshort = 32767 + 1", "0xA0179"); // expected short but provided int + checkErrorExpr("varshort = 1 + 32767", "0xA0179"); // expected short but provided int + checkErrorExpr("varshort = varshort + varshort", "0xA0179"); // expected short but provided int + checkErrorExpr("varshort = 1 + 1l", "0xA0179"); // expected short but provided long + checkErrorExpr("varshort = 1l + 1", "0xA0179"); // expected short but provided long + checkErrorExpr("varshort = 1 + 0.1f", "0xA0179"); // expected short but provided float + checkErrorExpr("varshort = 0.1f + 1", "0xA0179"); // expected short but provided float + checkErrorExpr("varshort = 1 + 0.1", "0xA0179"); // expected short but provided double + checkErrorExpr("varshort = 0.1 + 1", "0xA0179"); // expected short but provided double + checkErrorExpr("varint = 1 + 1l", "0xA0179"); // expected int but provided long + checkErrorExpr("varint = 1l + 1", "0xA0179"); // expected int but provided long + checkErrorExpr("varint = 1 + 0.1f", "0xA0179"); // expected int but provided float + checkErrorExpr("varint = 0.1f + 1", "0xA0179"); // expected int but provided float + checkErrorExpr("varint = 1 + 0.1", "0xA0179"); // expected int but provided double + checkErrorExpr("varint = 0.1 + 1", "0xA0179"); // expected int but provided double + checkErrorExpr("varlong = 1l + 0.1f", "0xA0179"); // expected long but provided float + checkErrorExpr("varlong = 0.1f + 1l", "0xA0179"); // expected long but provided float + checkErrorExpr("varlong = 1l + 0.1", "0xA0179"); // expected long but provided double + checkErrorExpr("varlong = 0.1 + 1l", "0xA0179"); // expected long but provided double + checkErrorExpr("varfloat = 0.1f + 0.1", "0xA0179"); // expected float but provided double + checkErrorExpr("varfloat = 0.1 + 0.1f", "0xA0179"); // expected float but provided double + } + + @Test + public void deriveFromMinusExpression() throws IOException { + checkExpr("1 - 1", "int"); // expected int and provided int + checkExpr("-128 - 1", "int"); // expected int and provided int + checkExpr("-32768 - 1", "int"); // expected int and provided int + checkExpr("-2147483648 - 0", "int"); // expected int and provided int + checkExpr("varchar - varchar", "int"); // - applicable to char, char, result is int + checkExpr("varbyte - varbyte", "int"); // - applicable to byte, byte, result is int + checkExpr("varshort - varshort", "int"); // - applicable to short, short, result is int + checkExpr("varint - varint", "int"); // - applicable to int, int, result is int + checkExpr("varlong - varlong", "long"); // - applicable to long, long, result is long + checkExpr("varfloat - varfloat", "float"); // - applicable to float, float, result is float + checkExpr("vardouble - vardouble", "double"); // - applicable to double, double, result is double + } + + @Test + public void testInvalidMinusExpression() throws IOException { + checkErrorExpr("varboolean - varboolean", "0xB0163"); // - not applicable to boolean, boolean + checkErrorExpr("varboolean - varchar", "0xB0163"); // - not applicable to boolean, char + checkErrorExpr("varboolean - varbyte", "0xB0163"); // - not applicable to boolean, byte + checkErrorExpr("varboolean - varshort", "0xB0163"); // - not applicable to boolean, short + checkErrorExpr("varboolean - varint", "0xB0163"); // - not applicable to boolean, int + checkErrorExpr("varboolean - varlong", "0xB0163"); // - not applicable to boolean, long + checkErrorExpr("varboolean - varfloat", "0xB0163"); // - not applicable to boolean, float + checkErrorExpr("varboolean - vardouble", "0xB0163"); // - not applicable to boolean, double + checkErrorExpr("varchar - varboolean", "0xB0163"); // - not applicable to char, boolean + checkErrorExpr("varbyte - varboolean", "0xB0163"); // - not applicable to byte, boolean + checkErrorExpr("varshort - varboolean", "0xB0163"); // - not applicable to short, boolean + checkErrorExpr("varint - varboolean", "0xB0163"); // - not applicable to int, boolean + checkErrorExpr("varlong - varboolean", "0xB0163"); // - not applicable to long, boolean + checkErrorExpr("varfloat - varboolean", "0xB0163"); // - not applicable to float, boolean + checkErrorExpr("vardouble - varboolean", "0xB0163"); // - not applicable to double, boolean + checkErrorExpr("varchar = varchar - varchar", "0xA0179"); // expected char but provided int + checkErrorExpr("varchar = 1 - 1l", "0xA0179"); // expected char but provided long + checkErrorExpr("varchar = 1l - 1", "0xA0179"); // expected char but provided long + checkErrorExpr("varchar = 1 - 0.1f", "0xA0179"); // expected char but provided float + checkErrorExpr("varchar = 0.1f - 1", "0xA0179"); // expected char but provided float + checkErrorExpr("varchar = 1 - 0.1", "0xA0179"); // expected char but provided double + checkErrorExpr("varchar = 0.1 - 1", "0xA0179"); // expected char but provided double + checkErrorExpr("varbyte = 1 - 1l", "0xA0179"); // expected byte but provided long + checkErrorExpr("varbyte = 1l - 1", "0xA0179"); // expected byte but provided long + checkErrorExpr("varbyte = 1 - 0.1f", "0xA0179"); // expected byte but provided float + checkErrorExpr("varbyte = 0.1f - 1", "0xA0179"); // expected byte but provided float + checkErrorExpr("varbyte = 1 - 0.1", "0xA0179"); // expected byte but provided double + checkErrorExpr("varbyte = 0.1 - 1", "0xA0179"); // expected byte but provided double + checkErrorExpr("varshort = varshort - varshort", "0xA0179"); // expected short but provided int + checkErrorExpr("varshort = 1 - 1l", "0xA0179"); // expected short but provided long + checkErrorExpr("varshort = 1l - 1", "0xA0179"); // expected short but provided long + checkErrorExpr("varshort = 1 - 0.1f", "0xA0179"); // expected short but provided float + checkErrorExpr("varshort = 0.1f - 1", "0xA0179"); // expected short but provided float + checkErrorExpr("varshort = 1 - 0.1", "0xA0179"); // expected short but provided double + checkErrorExpr("varshort = 0.1 - 1", "0xA0179"); // expected short but provided double + checkErrorExpr("varint = 1 - 1l", "0xA0179"); // expected int but provided long + checkErrorExpr("varint = 1l - 1", "0xA0179"); // expected int but provided long + checkErrorExpr("varint = 1 - 0.1f", "0xA0179"); // expected int but provided float + checkErrorExpr("varint = 0.1f - 1", "0xA0179"); // expected int but provided float + checkErrorExpr("varint = 1 - 0.1", "0xA0179"); // expected int but provided double + checkErrorExpr("varint = 0.1 - 1", "0xA0179"); // expected int but provided double + checkErrorExpr("varlong = 1l - 0.1f", "0xA0179"); // expected long but provided float + checkErrorExpr("varlong = 0.1f - 1l", "0xA0179"); // expected long but provided float + checkErrorExpr("varlong = 1l - 0.1", "0xA0179"); // expected long but provided double + checkErrorExpr("varlong = 0.1 - 1l", "0xA0179"); // expected long but provided double + checkErrorExpr("varfloat = 0.1f - 0.1", "0xA0179"); // expected float but provided double + checkErrorExpr("varfloat = 0.1 - 0.1f", "0xA0179"); // expected float but provided double + } + + @Test + public void deriveFromMultExpression() throws IOException { + checkExpr("0 * 0", "int"); // expected int and provided int + checkExpr("2147483647 * 1", "int"); // expected int and provided int + checkExpr("1 * 2147483647", "int"); // expected int and provided int + checkExpr("varchar * varchar", "int"); // * applicable to char, char, result is int + checkExpr("varbyte * varbyte", "int"); // * applicable to byte, byte, result is int + checkExpr("varshort * varshort", "int"); // * applicable to short, short, result is int + checkExpr("varint * varint", "int"); // * applicable to int, int, result is int + checkExpr("varlong * varlong", "long"); // * applicable to long, long, result is long + checkExpr("varfloat * varfloat", "float"); // * applicable to float, float, result is float + checkExpr("vardouble * vardouble", "double"); // * applicable to double, double, result is double + } + + @Test + public void testInvalidMultExpression() throws IOException { + checkErrorExpr("varboolean * varboolean", "0xB0163"); // * not applicable to boolean, boolean + checkErrorExpr("varboolean * varchar", "0xB0163"); // * not applicable to boolean, char + checkErrorExpr("varboolean * varbyte", "0xB0163"); // * not applicable to boolean, byte + checkErrorExpr("varboolean * varshort", "0xB0163"); // * not applicable to boolean, short + checkErrorExpr("varboolean * varint", "0xB0163"); // * not applicable to boolean, int + checkErrorExpr("varboolean * varlong", "0xB0163"); // * not applicable to boolean, long + checkErrorExpr("varboolean * varfloat", "0xB0163"); // * not applicable to boolean, float + checkErrorExpr("varboolean * vardouble", "0xB0163"); // * not applicable to boolean, double + checkErrorExpr("varchar * varboolean", "0xB0163"); // * not applicable to char, boolean + checkErrorExpr("varbyte * varboolean", "0xB0163"); // * not applicable to byte, boolean + checkErrorExpr("varshort * varboolean", "0xB0163"); // * not applicable to short, boolean + checkErrorExpr("varint * varboolean", "0xB0163"); // * not applicable to int, boolean + checkErrorExpr("varlong * varboolean", "0xB0163"); // * not applicable to long, boolean + checkErrorExpr("varfloat * varboolean", "0xB0163"); // * not applicable to float, boolean + checkErrorExpr("vardouble * varboolean", "0xB0163"); // * not applicable to double, boolean + checkErrorExpr("varbyte = 64 * 2", "0xA0179"); // expected byte but provided int + checkErrorExpr("varbyte = varbyte * varbyte", "0xA0179"); // expected byte but provided int + checkErrorExpr("varbyte = 1 * 1l", "0xA0179"); // expected byte but provided long + checkErrorExpr("varbyte = 1l * 1", "0xA0179"); // expected byte but provided long + checkErrorExpr("varbyte = 1 * 0.1f", "0xA0179"); // expected byte but provided float + checkErrorExpr("varbyte = 0.1f * 1", "0xA0179"); // expected byte but provided float + checkErrorExpr("varbyte = 1 * 0.1", "0xA0179"); // expected byte but provided double + checkErrorExpr("varbyte = 0.1 * 1", "0xA0179"); // expected byte but provided double + checkErrorExpr("varshort = 16384 * 2", "0xA0179"); // expected short but provided int + checkErrorExpr("varshort = varshort * varshort", "0xA0179"); // expected short but provided int + checkErrorExpr("varshort = 1 * 1l", "0xA0179"); // expected short but provided long + checkErrorExpr("varshort = 1l * 1", "0xA0179"); // expected short but provided long + checkErrorExpr("varshort = 1 * 0.1f", "0xA0179"); // expected short but provided float + checkErrorExpr("varshort = 0.1f * 1", "0xA0179"); // expected short but provided float + checkErrorExpr("varshort = 1 * 0.1", "0xA0179"); // expected short but provided double + checkErrorExpr("varshort = 0.1 * 1", "0xA0179"); // expected short but provided double + checkErrorExpr("varint = 1 * 1l", "0xA0179"); // expected int but provided long + checkErrorExpr("varint = 1l * 1", "0xA0179"); // expected int but provided long + checkErrorExpr("varint = 1 * 0.1f", "0xA0179"); // expected int but provided float + checkErrorExpr("varint = 0.1f * 1", "0xA0179"); // expected int but provided float + checkErrorExpr("varint = 1 * 0.1", "0xA0179"); // expected int but provided double + checkErrorExpr("varint = 0.1 * 1", "0xA0179"); // expected int but provided double + checkErrorExpr("varlong = 1l * 0.1f", "0xA0179"); // expected long but provided float + checkErrorExpr("varlong = 0.1f * 1l", "0xA0179"); // expected long but provided float + checkErrorExpr("varlong = 1l * 0.1", "0xA0179"); // expected long but provided double + checkErrorExpr("varlong = 0.1 * 1l", "0xA0179"); // expected long but provided double + checkErrorExpr("varfloat = 0.1f * 0.1", "0xA0179"); // expected float but provided double + checkErrorExpr("varfloat = 0.1 * 0.1f", "0xA0179"); // expected float but provided double + checkErrorExpr("varchar = 32768 * 2", "0xA0179"); // expected char but provided int + checkErrorExpr("varchar = varchar * varchar", "0xA0179"); // expected char but provided int + checkErrorExpr("varchar = 1 * 1l", "0xA0179"); // expected char but provided long + checkErrorExpr("varchar = 1l * 1", "0xA0179"); // expected char but provided long + checkErrorExpr("varchar = 1 * 0.1f", "0xA0179"); // expected char but provided float + checkErrorExpr("varchar = 0.1f * 1", "0xA0179"); // expected char but provided float + checkErrorExpr("varchar = 1 * 0.1", "0xA0179"); // expected char but provided double + checkErrorExpr("varchar = 0.1 * 1", "0xA0179"); // expected char but provided double + } + + @Test + public void deriveFromDivideExpression() throws IOException { + checkExpr("2147483647 / 1", "int"); // expected int and provided int + checkExpr("1 / 2147483647", "int"); // expected int and provided int + checkExpr("varchar / varchar", "int"); // / applicable to char, char, result is int + checkExpr("varbyte / varbyte", "int"); // / applicable to byte, byte, result is int + checkExpr("varshort / varshort", "int"); // / applicable to short, short, result is int + checkExpr("varint / varint", "int"); // / applicable to int, int, result is int + checkExpr("varlong / varlong", "long"); // / applicable to long, long, result is long + checkExpr("varfloat / varfloat", "float"); // / applicable to float, float, result is float + checkExpr("vardouble / vardouble", "double"); // / applicable to double, double, result is double + } + + @Test + public void testInvalidDivideExpression() throws IOException { + checkErrorExpr("varboolean / varboolean", "0xB0163"); // / not applicable to boolean, boolean + checkErrorExpr("varboolean / varchar", "0xB0163"); // / not applicable to boolean, char + checkErrorExpr("varboolean / varbyte", "0xB0163"); // / not applicable to boolean, byte + checkErrorExpr("varboolean / varshort", "0xB0163"); // / not applicable to boolean, short + checkErrorExpr("varboolean / varint", "0xB0163"); // / not applicable to boolean, int + checkErrorExpr("varboolean / varlong", "0xB0163"); // / not applicable to boolean, long + checkErrorExpr("varboolean / varfloat", "0xB0163"); // / not applicable to boolean, float + checkErrorExpr("varboolean / vardouble", "0xB0163"); // / not applicable to boolean, double + checkErrorExpr("varchar / varboolean", "0xB0163"); // / not applicable to char, boolean + checkErrorExpr("varbyte / varboolean", "0xB0163"); // / not applicable to byte, boolean + checkErrorExpr("varshort / varboolean", "0xB0163"); // / not applicable to short, boolean + checkErrorExpr("varint / varboolean", "0xB0163"); // / not applicable to int, boolean + checkErrorExpr("varlong / varboolean", "0xB0163"); // / not applicable to long, boolean + checkErrorExpr("varfloat / varboolean", "0xB0163"); // / not applicable to float, boolean + checkErrorExpr("vardouble / varboolean", "0xB0163"); // / not applicable to double, boolean + checkErrorExpr("varchar = 65536 / 1", "0xA0179"); // expected char but provided int + checkErrorExpr("varchar = varchar / varchar", "0xA0179"); // expected char but provided int + checkErrorExpr("varchar = 1 / 1l", "0xA0179"); // expected char but provided long + checkErrorExpr("varchar = 1l / 1", "0xA0179"); // expected char but provided long + checkErrorExpr("varchar = 1 / 0.1f", "0xA0179"); // expected char but provided float + checkErrorExpr("varchar = 0.1f / 1", "0xA0179"); // expected char but provided float + checkErrorExpr("varchar = 1 / 0.1", "0xA0179"); // expected char but provided double + checkErrorExpr("varchar = 0.1 / 1", "0xA0179"); // expected char but provided double + checkErrorExpr("varbyte = 128 / 1", "0xA0179"); // expected byte but provided int + checkErrorExpr("varbyte = varbyte / varbyte", "0xA0179"); // expected byte but provided int + checkErrorExpr("varbyte = 1 / 1l", "0xA0179"); // expected byte but provided long + checkErrorExpr("varbyte = 1l / 1", "0xA0179"); // expected byte but provided long + checkErrorExpr("varbyte = 1 / 0.1f", "0xA0179"); // expected byte but provided float + checkErrorExpr("varbyte = 0.1f / 1", "0xA0179"); // expected byte but provided float + checkErrorExpr("varbyte = 1 / 0.1", "0xA0179"); // expected byte but provided double + checkErrorExpr("varbyte = 0.1 / 1", "0xA0179"); // expected byte but provided double + checkErrorExpr("varshort = 32768 / 1", "0xA0179"); // expected short but provided int + checkErrorExpr("varshort = varshort / varshort", "0xA0179"); // expected short but provided int + checkErrorExpr("varshort = 1 / 1l", "0xA0179"); // expected short but provided long + checkErrorExpr("varshort = 1l / 1", "0xA0179"); // expected short but provided long + checkErrorExpr("varshort = 1 / 0.1f", "0xA0179"); // expected short but provided float + checkErrorExpr("varshort = 0.1f / 1", "0xA0179"); // expected short but provided float + checkErrorExpr("varshort = 1 / 0.1", "0xA0179"); // expected short but provided double + checkErrorExpr("varshort = 0.1 / 1", "0xA0179"); // expected short but provided double + checkErrorExpr("varint = 1 / 1l", "0xA0179"); // expected int but provided long + checkErrorExpr("varint = 1l / 1", "0xA0179"); // expected int but provided long + checkErrorExpr("varint = 1 / 0.1f", "0xA0179"); // expected int but provided float + checkErrorExpr("varint = 0.1f / 1", "0xA0179"); // expected int but provided float + checkErrorExpr("varint = 1 / 0.1", "0xA0179"); // expected int but provided double + checkErrorExpr("varint = 0.1 / 1", "0xA0179"); // expected int but provided double + checkErrorExpr("varlong = 1l / 0.1f", "0xA0179"); // expected long but provided float + checkErrorExpr("varlong = 0.1f / 1l", "0xA0179"); // expected long but provided float + checkErrorExpr("varlong = 1l / 0.1", "0xA0179"); // expected long but provided double + checkErrorExpr("varlong = 0.1 / 1l", "0xA0179"); // expected long but provided double + checkErrorExpr("varfloat = 0.1f / 0.1", "0xA0179"); // expected float but provided double + checkErrorExpr("varfloat = 0.1 / 0.1f", "0xA0179"); // expected float but provided double + } + + @Test + public void deriveFromModuloExpression() throws IOException { + checkExpr("2147483647 % 1", "int"); // expected int and provided int + checkExpr("1 % 2147483647", "int"); // expected int and provided int + checkExpr("varchar % varchar", "int"); // % applicable to char, char, result is int + checkExpr("varbyte % varbyte", "int"); // % applicable to byte, byte, result is int + checkExpr("varshort % varshort", "int"); // % applicable to short, short, result is int + checkExpr("varint % varint", "int"); // % applicable to int, int, result is int + checkExpr("varlong % varlong", "long"); // % applicable to long, long, result is long + checkExpr("varfloat % varfloat", "float"); // % applicable to float, float, result is float + checkExpr("vardouble % vardouble", "double"); // % applicable to double, double, result is double + } + + @Test + public void testInvalidModuloExpression() throws IOException { + checkErrorExpr("varboolean % varboolean", "0xB0163"); // % not applicable to boolean, boolean + checkErrorExpr("varboolean % varchar", "0xB0163"); // % not applicable to boolean, char + checkErrorExpr("varboolean % varbyte", "0xB0163"); // % not applicable to boolean, byte + checkErrorExpr("varboolean % varshort", "0xB0163"); // % not applicable to boolean, short + checkErrorExpr("varboolean % varint", "0xB0163"); // % not applicable to boolean, int + checkErrorExpr("varboolean % varlong", "0xB0163"); // % not applicable to boolean, long + checkErrorExpr("varboolean % varfloat", "0xB0163"); // % not applicable to boolean, float + checkErrorExpr("varboolean % vardouble", "0xB0163"); // % not applicable to boolean, double + checkErrorExpr("varchar % varboolean", "0xB0163"); // % not applicable to char, boolean + checkErrorExpr("varbyte % varboolean", "0xB0163"); // % not applicable to byte, boolean + checkErrorExpr("varshort % varboolean", "0xB0163"); // % not applicable to short, boolean + checkErrorExpr("varint % varboolean", "0xB0163"); // % not applicable to int, boolean + checkErrorExpr("varlong % varboolean", "0xB0163"); // % not applicable to long, boolean + checkErrorExpr("varfloat % varboolean", "0xB0163"); // % not applicable to float, boolean + checkErrorExpr("vardouble % varboolean", "0xB0163"); // % not applicable to double, boolean + checkErrorExpr("varchar = varchar % varchar", "0xA0179"); // expected char but provided int + checkErrorExpr("varchar = 1 % 1l", "0xA0179"); // expected char but provided long + checkErrorExpr("varchar = 1l % 1", "0xA0179"); // expected char but provided long + checkErrorExpr("varchar = 1 % 0.1f", "0xA0179"); // expected char but provided float + checkErrorExpr("varchar = 0.1f % 1", "0xA0179"); // expected char but provided float + checkErrorExpr("varchar = 1 % 0.1", "0xA0179"); // expected char but provided double + checkErrorExpr("varchar = 0.1 % 1", "0xA0179"); // expected char but provided double + checkErrorExpr("varbyte = varbyte % varbyte", "0xA0179"); // expected byte but provided int + checkErrorExpr("varbyte = 1 % 1l", "0xA0179"); // expected byte but provided long + checkErrorExpr("varbyte = 1l % 1", "0xA0179"); // expected byte but provided long + checkErrorExpr("varbyte = 1 % 0.1f", "0xA0179"); // expected byte but provided float + checkErrorExpr("varbyte = 0.1f % 1", "0xA0179"); // expected byte but provided float + checkErrorExpr("varbyte = 1 % 0.1", "0xA0179"); // expected byte but provided double + checkErrorExpr("varbyte = 0.1 % 1", "0xA0179"); // expected byte but provided double + checkErrorExpr("varshort = varshort % varshort", "0xA0179"); // expected short but provided int + checkErrorExpr("varshort = 1 % 1l", "0xA0179"); // expected short but provided long + checkErrorExpr("varshort = 1l % 1", "0xA0179"); // expected short but provided long + checkErrorExpr("varshort = 1 % 0.1f", "0xA0179"); // expected short but provided float + checkErrorExpr("varshort = 0.1f % 1", "0xA0179"); // expected short but provided float + checkErrorExpr("varshort = 1 % 0.1", "0xA0179"); // expected short but provided double + checkErrorExpr("varshort = 0.1 % 1", "0xA0179"); // expected short but provided double + checkErrorExpr("varint = 1 % 1l", "0xA0179"); // expected int but provided long + checkErrorExpr("varint = 1l % 1", "0xA0179"); // expected int but provided long + checkErrorExpr("varint = 1 % 0.1f", "0xA0179"); // expected int but provided float + checkErrorExpr("varint = 0.1f % 1", "0xA0179"); // expected int but provided float + checkErrorExpr("varint = 1 % 0.1", "0xA0179"); // expected int but provided double + checkErrorExpr("varint = 0.1 % 1", "0xA0179"); // expected int but provided double + checkErrorExpr("varlong = 1l % 0.1f", "0xA0179"); // expected long but provided float + checkErrorExpr("varlong = 0.1f % 1l", "0xA0179"); // expected long but provided float + checkErrorExpr("varlong = 1l % 0.1", "0xA0179"); // expected long but provided double + checkErrorExpr("varlong = 0.1 % 1l", "0xA0179"); // expected long but provided double + checkErrorExpr("varfloat = 0.1f % 0.1", "0xA0179"); // expected float but provided double + checkErrorExpr("varfloat = 0.1 % 0.1f", "0xA0179"); // expected float but provided double + } + + @Test + public void deriveFromLessEqualExpression() throws IOException { + checkExpr("varbyte <= varbyte", "boolean"); // <= applicable to byte, byte, result is boolean + checkExpr("varbyte <= varshort", "boolean"); // <= applicable to byte, short, result is boolean + checkExpr("varbyte <= varchar", "boolean"); // <= applicable to byte, char, result is boolean + checkExpr("varbyte <= varint", "boolean"); // <= applicable to byte, int, result is boolean + checkExpr("varbyte <= varlong", "boolean"); // <= applicable to byte, long, result is boolean + checkExpr("varbyte <= varfloat", "boolean"); // <= applicable to byte, float, result is boolean + checkExpr("varbyte <= vardouble", "boolean"); // <= applicable to byte, double, result is boolean + checkExpr("varshort <= varbyte", "boolean"); // <= applicable to short, byte, result is boolean + checkExpr("varshort <= varshort", "boolean"); // <= applicable to short, short, result is boolean + checkExpr("varshort <= varchar", "boolean"); // <= applicable to short, char, result is boolean + checkExpr("varshort <= varint", "boolean"); // <= applicable to short, int, result is boolean + checkExpr("varshort <= varlong", "boolean"); // <= applicable to short, long, result is boolean + checkExpr("varshort <= varfloat", "boolean"); // <= applicable to short, float, result is boolean + checkExpr("varshort <= vardouble", "boolean"); // <= applicable to short, double, result is boolean + checkExpr("varchar <= varbyte", "boolean"); // <= applicable to char, byte, result is boolean + checkExpr("varchar <= varshort", "boolean"); // <= applicable to char, short, result is boolean + checkExpr("varchar <= varchar", "boolean"); // <= applicable to char, char, result is boolean + checkExpr("varchar <= varint", "boolean"); // <= applicable to char, int, result is boolean + checkExpr("varchar <= varlong", "boolean"); // <= applicable to char, long, result is boolean + checkExpr("varchar <= varfloat", "boolean"); // <= applicable to char, float, result is boolean + checkExpr("varchar <= vardouble", "boolean"); // <= applicable to char, double, result is boolean + checkExpr("varint <= varbyte", "boolean"); // <= applicable to int, byte, result is boolean + checkExpr("varint <= varshort", "boolean"); // <= applicable to int, short, result is boolean + checkExpr("varint <= varchar", "boolean"); // <= applicable to int, char, result is boolean + checkExpr("varint <= varint", "boolean"); // <= applicable to int, int, result is boolean + checkExpr("varint <= varlong", "boolean"); // <= applicable to int, long, result is boolean + checkExpr("varint <= varfloat", "boolean"); // <= applicable to int, float, result is boolean + checkExpr("varint <= vardouble", "boolean"); // <= applicable to int, double, result is boolean + checkExpr("varlong <= varbyte", "boolean"); // <= applicable to long, byte, result is boolean + checkExpr("varlong <= varshort", "boolean"); // <= applicable to long, short, result is boolean + checkExpr("varlong <= varchar", "boolean"); // <= applicable to long, char, result is boolean + checkExpr("varlong <= varint", "boolean"); // <= applicable to long, int, result is boolean + checkExpr("varlong <= varlong", "boolean"); // <= applicable to long, long, result is boolean + checkExpr("varlong <= varfloat", "boolean"); // <= applicable to long, float, result is boolean + checkExpr("varlong <= vardouble", "boolean"); // <= applicable to long, double, result is boolean + checkExpr("varfloat <= varbyte", "boolean"); // <= applicable to float, byte, result is boolean + checkExpr("varfloat <= varshort", "boolean"); // <= applicable to float, short, result is boolean + checkExpr("varfloat <= varchar", "boolean"); // <= applicable to float, char, result is boolean + checkExpr("varfloat <= varint", "boolean"); // <= applicable to float, int, result is boolean + checkExpr("varfloat <= varlong", "boolean"); // <= applicable to float, long, result is boolean + checkExpr("varfloat <= varfloat", "boolean"); // <= applicable to float, float, result is boolean + checkExpr("varfloat <= vardouble", "boolean"); // <= applicable to float, double, result is boolean + checkExpr("vardouble <= varbyte", "boolean"); // <= applicable to double, byte, result is boolean + checkExpr("vardouble <= varshort", "boolean"); // <= applicable to double, short, result is boolean + checkExpr("vardouble <= varchar", "boolean"); // <= applicable to double, char, result is boolean + checkExpr("vardouble <= varint", "boolean"); // <= applicable to double, int, result is boolean + checkExpr("vardouble <= varlong", "boolean"); // <= applicable to double, long, result is boolean + checkExpr("vardouble <= varfloat", "boolean"); // <= applicable to double, float, result is boolean + checkExpr("vardouble <= vardouble", "boolean"); // <= applicable to double, double, result is boolean + } + + @Test + public void testInvalidLessEqualExpression() throws IOException { + checkErrorExpr("varboolean <= varchar", "0xB0167"); // <= not applicable to boolean, char + checkErrorExpr("varboolean <= varbyte", "0xB0167"); // <= not applicable to boolean, byte + checkErrorExpr("varboolean <= varshort", "0xB0167"); // <= not applicable to boolean, short + checkErrorExpr("varboolean <= varint", "0xB0167"); // <= not applicable to boolean, int + checkErrorExpr("varboolean <= varlong", "0xB0167"); // <= not applicable to boolean, long + checkErrorExpr("varboolean <= varfloat", "0xB0167"); // <= not applicable to boolean, float + checkErrorExpr("varboolean <= vardouble", "0xB0167"); // <= not applicable to boolean, double + checkErrorExpr("varchar <= varboolean", "0xB0167"); // <= not applicable to char, boolean + checkErrorExpr("varbyte <= varboolean", "0xB0167"); // <= not applicable to byte, boolean + checkErrorExpr("varshort <= varboolean", "0xB0167"); // <= not applicable to short, boolean + checkErrorExpr("varint <= varboolean", "0xB0167"); // <= not applicable to int, boolean + checkErrorExpr("varlong <= varboolean", "0xB0167"); // <= not applicable to long, boolean + checkErrorExpr("varfloat <= varboolean", "0xB0167"); // <= not applicable to float, boolean + checkErrorExpr("vardouble <= varboolean", "0xB0167"); // <= not applicable to double, boolean + checkErrorExpr("varchar = varchar <= varchar", "0xA0179"); // expected char but provided boolean + checkErrorExpr("varbyte = varbyte <= varbyte", "0xA0179"); // expected byte but provided boolean + checkErrorExpr("varshort = varshort <= varshort", "0xA0179"); // expected short but provided boolean + checkErrorExpr("varint = varint <= varint", "0xA0179"); // expected int but provided boolean + checkErrorExpr("varlong = varlong <= varlong", "0xA0179"); // expected long but provided boolean + checkErrorExpr("varfloat = varfloat <= varfloat", "0xA0179"); // expected float but provided boolean + checkErrorExpr("vardouble = vardouble <= vardouble", "0xA0179"); // expected double but provided boolean + } + + @Test + public void deriveFromGreaterEqualExpression() throws IOException { + checkExpr("varbyte >= varbyte", "boolean"); // >= applicable to byte, byte, result is boolean + checkExpr("varbyte >= varshort", "boolean"); // >= applicable to byte, short, result is boolean + checkExpr("varbyte >= varchar", "boolean"); // >= applicable to byte, char, result is boolean + checkExpr("varbyte >= varint", "boolean"); // >= applicable to byte, int, result is boolean + checkExpr("varbyte >= varlong", "boolean"); // >= applicable to byte, long, result is boolean + checkExpr("varbyte >= varfloat", "boolean"); // >= applicable to byte, float, result is boolean + checkExpr("varbyte >= vardouble", "boolean"); // >= applicable to byte, double, result is boolean + checkExpr("varshort >= varbyte", "boolean"); // >= applicable to short, byte, result is boolean + checkExpr("varshort >= varshort", "boolean"); // >= applicable to short, short, result is boolean + checkExpr("varshort >= varchar", "boolean"); // >= applicable to short, char, result is boolean + checkExpr("varshort >= varint", "boolean"); // >= applicable to short, int, result is boolean + checkExpr("varshort >= varlong", "boolean"); // >= applicable to short, long, result is boolean + checkExpr("varshort >= varfloat", "boolean"); // >= applicable to short, float, result is boolean + checkExpr("varshort >= vardouble", "boolean"); // >= applicable to short, double, result is boolean + checkExpr("varchar >= varbyte", "boolean"); // >= applicable to char, byte, result is boolean + checkExpr("varchar >= varshort", "boolean"); // >= applicable to char, short, result is boolean + checkExpr("varchar >= varchar", "boolean"); // >= applicable to char, char, result is boolean + checkExpr("varchar >= varint", "boolean"); // >= applicable to char, int, result is boolean + checkExpr("varchar >= varlong", "boolean"); // >= applicable to char, long, result is boolean + checkExpr("varchar >= varfloat", "boolean"); // >= applicable to char, float, result is boolean + checkExpr("varchar >= vardouble", "boolean"); // >= applicable to char, double, result is boolean + checkExpr("varint >= varbyte", "boolean"); // >= applicable to int, byte, result is boolean + checkExpr("varint >= varshort", "boolean"); // >= applicable to int, short, result is boolean + checkExpr("varint >= varchar", "boolean"); // >= applicable to int, char, result is boolean + checkExpr("varint >= varint", "boolean"); // >= applicable to int, int, result is boolean + checkExpr("varint >= varlong", "boolean"); // >= applicable to int, long, result is boolean + checkExpr("varint >= varfloat", "boolean"); // >= applicable to int, float, result is boolean + checkExpr("varint >= vardouble", "boolean"); // >= applicable to int, double, result is boolean + checkExpr("varlong >= varbyte", "boolean"); // >= applicable to long, byte, result is boolean + checkExpr("varlong >= varshort", "boolean"); // >= applicable to long, short, result is boolean + checkExpr("varlong >= varchar", "boolean"); // >= applicable to long, char, result is boolean + checkExpr("varlong >= varint", "boolean"); // >= applicable to long, int, result is boolean + checkExpr("varlong >= varlong", "boolean"); // >= applicable to long, long, result is boolean + checkExpr("varlong >= varfloat", "boolean"); // >= applicable to long, float, result is boolean + checkExpr("varlong >= vardouble", "boolean"); // >= applicable to long, double, result is boolean + checkExpr("varfloat >= varbyte", "boolean"); // >= applicable to float, byte, result is boolean + checkExpr("varfloat >= varshort", "boolean"); // >= applicable to float, short, result is boolean + checkExpr("varfloat >= varchar", "boolean"); // >= applicable to float, char, result is boolean + checkExpr("varfloat >= varint", "boolean"); // >= applicable to float, int, result is boolean + checkExpr("varfloat >= varlong", "boolean"); // >= applicable to float, long, result is boolean + checkExpr("varfloat >= varfloat", "boolean"); // >= applicable to float, float, result is boolean + checkExpr("varfloat >= vardouble", "boolean"); // >= applicable to float, double, result is boolean + checkExpr("vardouble >= varbyte", "boolean"); // >= applicable to double, byte, result is boolean + checkExpr("vardouble >= varshort", "boolean"); // >= applicable to double, short, result is boolean + checkExpr("vardouble >= varchar", "boolean"); // >= applicable to double, char, result is boolean + checkExpr("vardouble >= varint", "boolean"); // >= applicable to double, int, result is boolean + checkExpr("vardouble >= varlong", "boolean"); // >= applicable to double, long, result is boolean + checkExpr("vardouble >= varfloat", "boolean"); // >= applicable to double, float, result is boolean + checkExpr("vardouble >= vardouble", "boolean"); // >= applicable to double, double, result is boolean + } + + @Test + public void testInvalidGreaterEqualExpression() throws IOException { + checkErrorExpr("varboolean >= varchar", "0xB0167"); // >= not applicable to boolean, char + checkErrorExpr("varboolean >= varbyte", "0xB0167"); // >= not applicable to boolean, byte + checkErrorExpr("varboolean >= varshort", "0xB0167"); // >= not applicable to boolean, short + checkErrorExpr("varboolean >= varint", "0xB0167"); // >= not applicable to boolean, int + checkErrorExpr("varboolean >= varlong", "0xB0167"); // >= not applicable to boolean, long + checkErrorExpr("varboolean >= varfloat", "0xB0167"); // >= not applicable to boolean, float + checkErrorExpr("varboolean >= vardouble", "0xB0167"); // >= not applicable to boolean, double + checkErrorExpr("varchar >= varboolean", "0xB0167"); // >= not applicable to char, boolean + checkErrorExpr("varbyte >= varboolean", "0xB0167"); // >= not applicable to byte, boolean + checkErrorExpr("varshort >= varboolean", "0xB0167"); // >= not applicable to short, boolean + checkErrorExpr("varint >= varboolean", "0xB0167"); // >= not applicable to int, boolean + checkErrorExpr("varlong >= varboolean", "0xB0167"); // >= not applicable to long, boolean + checkErrorExpr("varfloat >= varboolean", "0xB0167"); // >= not applicable to float, boolean + checkErrorExpr("vardouble >= varboolean", "0xB0167"); // >= not applicable to double, boolean + checkErrorExpr("varchar = varchar >= varchar", "0xA0179"); // expected char but provided boolean + checkErrorExpr("varbyte = varbyte >= varbyte", "0xA0179"); // expected byte but provided boolean + checkErrorExpr("varshort = varshort >= varshort", "0xA0179"); // expected short but provided boolean + checkErrorExpr("varint = varint >= varint", "0xA0179"); // expected int but provided boolean + checkErrorExpr("varlong = varlong >= varlong", "0xA0179"); // expected long but provided boolean + checkErrorExpr("varfloat = varfloat >= varfloat", "0xA0179"); // expected float but provided boolean + checkErrorExpr("vardouble = vardouble >= vardouble", "0xA0179"); // expected double but provided boolean + } + + @Test + public void deriveFromLessThanExpression() throws IOException { + checkExpr("varbyte < varbyte", "boolean"); // < applicable to byte, byte, result is boolean + checkExpr("varbyte < varshort", "boolean"); // < applicable to byte, short, result is boolean + checkExpr("varbyte < varchar", "boolean"); // < applicable to byte, char, result is boolean + checkExpr("varbyte < varint", "boolean"); // < applicable to byte, int, result is boolean + checkExpr("varbyte < varlong", "boolean"); // < applicable to byte, long, result is boolean + checkExpr("varbyte < varfloat", "boolean"); // < applicable to byte, float, result is boolean + checkExpr("varbyte < vardouble", "boolean"); // < applicable to byte, double, result is boolean + checkExpr("varshort < varbyte", "boolean"); // < applicable to short, byte, result is boolean + checkExpr("varshort < varshort", "boolean"); // < applicable to short, short, result is boolean + checkExpr("varshort < varchar", "boolean"); // < applicable to short, char, result is boolean + checkExpr("varshort < varint", "boolean"); // < applicable to short, int, result is boolean + checkExpr("varshort < varlong", "boolean"); // < applicable to short, long, result is boolean + checkExpr("varshort < varfloat", "boolean"); // < applicable to short, float, result is boolean + checkExpr("varshort < vardouble", "boolean"); // < applicable to short, double, result is boolean + checkExpr("varchar < varbyte", "boolean"); // < applicable to char, byte, result is boolean + checkExpr("varchar < varshort", "boolean"); // < applicable to char, short, result is boolean + checkExpr("varchar < varchar", "boolean"); // < applicable to char, char, result is boolean + checkExpr("varchar < varint", "boolean"); // < applicable to char, int, result is boolean + checkExpr("varchar < varlong", "boolean"); // < applicable to char, long, result is boolean + checkExpr("varchar < varfloat", "boolean"); // < applicable to char, float, result is boolean + checkExpr("varchar < vardouble", "boolean"); // < applicable to char, double, result is boolean + checkExpr("varint < varbyte", "boolean"); // < applicable to int, byte, result is boolean + checkExpr("varint < varshort", "boolean"); // < applicable to int, short, result is boolean + checkExpr("varint < varchar", "boolean"); // < applicable to int, char, result is boolean + checkExpr("varint < varint", "boolean"); // < applicable to int, int, result is boolean + checkExpr("varint < varlong", "boolean"); // < applicable to int, long, result is boolean + checkExpr("varint < varfloat", "boolean"); // < applicable to int, float, result is boolean + checkExpr("varint < vardouble", "boolean"); // < applicable to int, double, result is boolean + checkExpr("varlong < varbyte", "boolean"); // < applicable to long, byte, result is boolean + checkExpr("varlong < varshort", "boolean"); // < applicable to long, short, result is boolean + checkExpr("varlong < varchar", "boolean"); // < applicable to long, char, result is boolean + checkExpr("varlong < varint", "boolean"); // < applicable to long, int, result is boolean + checkExpr("varlong < varlong", "boolean"); // < applicable to long, long, result is boolean + checkExpr("varlong < varfloat", "boolean"); // < applicable to long, float, result is boolean + checkExpr("varlong < vardouble", "boolean"); // < applicable to long, double, result is boolean + checkExpr("varfloat < varbyte", "boolean"); // < applicable to float, byte, result is boolean + checkExpr("varfloat < varshort", "boolean"); // < applicable to float, short, result is boolean + checkExpr("varfloat < varchar", "boolean"); // < applicable to float, char, result is boolean + checkExpr("varfloat < varint", "boolean"); // < applicable to float, int, result is boolean + checkExpr("varfloat < varlong", "boolean"); // < applicable to float, long, result is boolean + checkExpr("varfloat < varfloat", "boolean"); // < applicable to float, float, result is boolean + checkExpr("varfloat < vardouble", "boolean"); // < applicable to float, double, result is boolean + checkExpr("vardouble < varbyte", "boolean"); // < applicable to double, byte, result is boolean + checkExpr("vardouble < varshort", "boolean"); // < applicable to double, short, result is boolean + checkExpr("vardouble < varchar", "boolean"); // < applicable to double, char, result is boolean + checkExpr("vardouble < varint", "boolean"); // < applicable to double, int, result is boolean + checkExpr("vardouble < varlong", "boolean"); // < applicable to double, long, result is boolean + checkExpr("vardouble < varfloat", "boolean"); // < applicable to double, float, result is boolean + checkExpr("vardouble < vardouble", "boolean"); // < applicable to double, double, result is boolean + } + + @Test + public void testInvalidLessThanExpression() throws IOException { + checkErrorExpr("varboolean < varchar", "0xB0167"); // < not applicable to boolean, char + checkErrorExpr("varboolean < varbyte", "0xB0167"); // < not applicable to boolean, byte + checkErrorExpr("varboolean < varshort", "0xB0167"); // < not applicable to boolean, short + checkErrorExpr("varboolean < varint", "0xB0167"); // < not applicable to boolean, int + checkErrorExpr("varboolean < varlong", "0xB0167"); // < not applicable to boolean, long + checkErrorExpr("varboolean < varfloat", "0xB0167"); // < not applicable to boolean, float + checkErrorExpr("varboolean < vardouble", "0xB0167"); // < not applicable to boolean, double + checkErrorExpr("varchar < varboolean", "0xB0167"); // < not applicable to char, boolean + checkErrorExpr("varbyte < varboolean", "0xB0167"); // < not applicable to byte, boolean + checkErrorExpr("varshort < varboolean", "0xB0167"); // < not applicable to short, boolean + checkErrorExpr("varint < varboolean", "0xB0167"); // < not applicable to int, boolean + checkErrorExpr("varlong < varboolean", "0xB0167"); // < not applicable to long, boolean + checkErrorExpr("varfloat < varboolean", "0xB0167"); // < not applicable to float, boolean + checkErrorExpr("vardouble < varboolean", "0xB0167"); // < not applicable to double, boolean + checkErrorExpr("varchar = varchar < varchar", "0xA0179"); // expected char but provided boolean + checkErrorExpr("varbyte = varbyte < varbyte", "0xA0179"); // expected byte but provided boolean + checkErrorExpr("varshort = varshort < varshort", "0xA0179"); // expected short but provided boolean + checkErrorExpr("varint = varint < varint", "0xA0179"); // expected int but provided boolean + checkErrorExpr("varlong = varlong < varlong", "0xA0179"); // expected long but provided boolean + checkErrorExpr("varfloat = varfloat < varfloat", "0xA0179"); // expected float but provided boolean + checkErrorExpr("vardouble = vardouble < vardouble", "0xA0179"); // expected double but provided boolean + } + + @Test + public void deriveFromGreaterThanExpression() throws IOException { + checkExpr("varbyte > varbyte", "boolean"); // > applicable to byte, byte, result is boolean + checkExpr("varbyte > varshort", "boolean"); // > applicable to byte, short, result is boolean + checkExpr("varbyte > varchar", "boolean"); // > applicable to byte, char, result is boolean + checkExpr("varbyte > varint", "boolean"); // > applicable to byte, int, result is boolean + checkExpr("varbyte > varlong", "boolean"); // > applicable to byte, long, result is boolean + checkExpr("varbyte > varfloat", "boolean"); // > applicable to byte, float, result is boolean + checkExpr("varbyte > vardouble", "boolean"); // > applicable to byte, double, result is boolean + checkExpr("varshort > varbyte", "boolean"); // > applicable to short, byte, result is boolean + checkExpr("varshort > varshort", "boolean"); // > applicable to short, short, result is boolean + checkExpr("varshort > varchar", "boolean"); // > applicable to short, char, result is boolean + checkExpr("varshort > varint", "boolean"); // > applicable to short, int, result is boolean + checkExpr("varshort > varlong", "boolean"); // > applicable to short, long, result is boolean + checkExpr("varshort > varfloat", "boolean"); // > applicable to short, float, result is boolean + checkExpr("varshort > vardouble", "boolean"); // > applicable to short, double, result is boolean + checkExpr("varchar > varbyte", "boolean"); // > applicable to char, byte, result is boolean + checkExpr("varchar > varshort", "boolean"); // > applicable to char, short, result is boolean + checkExpr("varchar > varchar", "boolean"); // > applicable to char, char, result is boolean + checkExpr("varchar > varint", "boolean"); // > applicable to char, int, result is boolean + checkExpr("varchar > varlong", "boolean"); // > applicable to char, long, result is boolean + checkExpr("varchar > varfloat", "boolean"); // > applicable to char, float, result is boolean + checkExpr("varchar > vardouble", "boolean"); // > applicable to char, double, result is boolean + checkExpr("varint > varbyte", "boolean"); // > applicable to int, byte, result is boolean + checkExpr("varint > varshort", "boolean"); // > applicable to int, short, result is boolean + checkExpr("varint > varchar", "boolean"); // > applicable to int, char, result is boolean + checkExpr("varint > varint", "boolean"); // > applicable to int, int, result is boolean + checkExpr("varint > varlong", "boolean"); // > applicable to int, long, result is boolean + checkExpr("varint > varfloat", "boolean"); // > applicable to int, float, result is boolean + checkExpr("varint > vardouble", "boolean"); // > applicable to int, double, result is boolean + checkExpr("varlong > varbyte", "boolean"); // > applicable to long, byte, result is boolean + checkExpr("varlong > varshort", "boolean"); // > applicable to long, short, result is boolean + checkExpr("varlong > varchar", "boolean"); // > applicable to long, char, result is boolean + checkExpr("varlong > varint", "boolean"); // > applicable to long, int, result is boolean + checkExpr("varlong > varlong", "boolean"); // > applicable to long, long, result is boolean + checkExpr("varlong > varfloat", "boolean"); // > applicable to long, float, result is boolean + checkExpr("varlong > vardouble", "boolean"); // > applicable to long, double, result is boolean + checkExpr("varfloat > varbyte", "boolean"); // > applicable to float, byte, result is boolean + checkExpr("varfloat > varshort", "boolean"); // > applicable to float, short, result is boolean + checkExpr("varfloat > varchar", "boolean"); // > applicable to float, char, result is boolean + checkExpr("varfloat > varint", "boolean"); // > applicable to float, int, result is boolean + checkExpr("varfloat > varlong", "boolean"); // > applicable to float, long, result is boolean + checkExpr("varfloat > varfloat", "boolean"); // > applicable to float, float, result is boolean + checkExpr("varfloat > vardouble", "boolean"); // > applicable to float, double, result is boolean + checkExpr("vardouble > varbyte", "boolean"); // > applicable to double, byte, result is boolean + checkExpr("vardouble > varshort", "boolean"); // > applicable to double, short, result is boolean + checkExpr("vardouble > varchar", "boolean"); // > applicable to double, char, result is boolean + checkExpr("vardouble > varint", "boolean"); // > applicable to double, int, result is boolean + checkExpr("vardouble > varlong", "boolean"); // > applicable to double, long, result is boolean + checkExpr("vardouble > varfloat", "boolean"); // > applicable to double, float, result is boolean + checkExpr("vardouble > vardouble", "boolean"); // > applicable to double, double, result is boolean + } + + @Test + public void testInvalidGreaterThanExpression() throws IOException { + checkErrorExpr("varboolean > varchar", "0xB0167"); // > not applicable to boolean, char + checkErrorExpr("varboolean > varbyte", "0xB0167"); // > not applicable to boolean, byte + checkErrorExpr("varboolean > varshort", "0xB0167"); // > not applicable to boolean, short + checkErrorExpr("varboolean > varint", "0xB0167"); // > not applicable to boolean, int + checkErrorExpr("varboolean > varlong", "0xB0167"); // > not applicable to boolean, long + checkErrorExpr("varboolean > varfloat", "0xB0167"); // > not applicable to boolean, float + checkErrorExpr("varboolean > vardouble", "0xB0167"); // > not applicable to boolean, double + checkErrorExpr("varchar > varboolean", "0xB0167"); // > not applicable to char, boolean + checkErrorExpr("varbyte > varboolean", "0xB0167"); // > not applicable to byte, boolean + checkErrorExpr("varshort > varboolean", "0xB0167"); // > not applicable to short, boolean + checkErrorExpr("varint > varboolean", "0xB0167"); // > not applicable to int, boolean + checkErrorExpr("varlong > varboolean", "0xB0167"); // > not applicable to long, boolean + checkErrorExpr("varfloat > varboolean", "0xB0167"); // > not applicable to float, boolean + checkErrorExpr("vardouble > varboolean", "0xB0167"); // > not applicable to double, boolean + checkErrorExpr("varchar = varchar > varchar", "0xA0179"); // expected char but provided boolean + checkErrorExpr("varbyte = varbyte > varbyte", "0xA0179"); // expected byte but provided boolean + checkErrorExpr("varshort = varshort > varshort", "0xA0179"); // expected short but provided boolean + checkErrorExpr("varint = varint > varint", "0xA0179"); // expected int but provided boolean + checkErrorExpr("varlong = varlong > varlong", "0xA0179"); // expected long but provided boolean + checkErrorExpr("varfloat = varfloat > varfloat", "0xA0179"); // expected float but provided boolean + checkErrorExpr("vardouble = vardouble > vardouble", "0xA0179"); // expected double but provided boolean + } + + @Test + public void deriveFromEqualsExpression() throws IOException { + checkExpr("varbyte == varbyte", "boolean"); // == applicable to byte, byte, result is boolean + checkExpr("varbyte == varshort", "boolean"); // == applicable to byte, short, result is boolean + checkExpr("varbyte == varchar", "boolean"); // == applicable to byte, char, result is boolean + checkExpr("varbyte == varint", "boolean"); // == applicable to byte, int, result is boolean + checkExpr("varbyte == varlong", "boolean"); // == applicable to byte, long, result is boolean + checkExpr("varbyte == varfloat", "boolean"); // == applicable to byte, float, result is boolean + checkExpr("varbyte == vardouble", "boolean"); // == applicable to byte, double, result is boolean + checkExpr("varshort == varbyte", "boolean"); // == applicable to short, byte, result is boolean + checkExpr("varshort == varshort", "boolean"); // == applicable to short, short, result is boolean + checkExpr("varshort == varchar", "boolean"); // == applicable to short, char, result is boolean + checkExpr("varshort == varint", "boolean"); // == applicable to short, int, result is boolean + checkExpr("varshort == varlong", "boolean"); // == applicable to short, long, result is boolean + checkExpr("varshort == varfloat", "boolean"); // == applicable to short, float, result is boolean + checkExpr("varshort == vardouble", "boolean"); // == applicable to short, double, result is boolean + checkExpr("varchar == varbyte", "boolean"); // == applicable to char, byte, result is boolean + checkExpr("varchar == varshort", "boolean"); // == applicable to char, short, result is boolean + checkExpr("varchar == varchar", "boolean"); // == applicable to char, char, result is boolean + checkExpr("varchar == varint", "boolean"); // == applicable to char, int, result is boolean + checkExpr("varchar == varlong", "boolean"); // == applicable to char, long, result is boolean + checkExpr("varchar == varfloat", "boolean"); // == applicable to char, float, result is boolean + checkExpr("varchar == vardouble", "boolean"); // == applicable to char, double, result is boolean + checkExpr("varint == varbyte", "boolean"); // == applicable to int, byte, result is boolean + checkExpr("varint == varshort", "boolean"); // == applicable to int, short, result is boolean + checkExpr("varint == varchar", "boolean"); // == applicable to int, char, result is boolean + checkExpr("varint == varint", "boolean"); // == applicable to int, int, result is boolean + checkExpr("varint == varlong", "boolean"); // == applicable to int, long, result is boolean + checkExpr("varint == varfloat", "boolean"); // == applicable to int, float, result is boolean + checkExpr("varint == vardouble", "boolean"); // == applicable to int, double, result is boolean + checkExpr("varlong == varbyte", "boolean"); // == applicable to long, byte, result is boolean + checkExpr("varlong == varshort", "boolean"); // == applicable to long, short, result is boolean + checkExpr("varlong == varchar", "boolean"); // == applicable to long, char, result is boolean + checkExpr("varlong == varint", "boolean"); // == applicable to long, int, result is boolean + checkExpr("varlong == varlong", "boolean"); // == applicable to long, long, result is boolean + checkExpr("varlong == varfloat", "boolean"); // == applicable to long, float, result is boolean + checkExpr("varlong == vardouble", "boolean"); // == applicable to long, double, result is boolean + checkExpr("varfloat == varbyte", "boolean"); // == applicable to float, byte, result is boolean + checkExpr("varfloat == varshort", "boolean"); // == applicable to float, short, result is boolean + checkExpr("varfloat == varchar", "boolean"); // == applicable to float, char, result is boolean + checkExpr("varfloat == varint", "boolean"); // == applicable to float, int, result is boolean + checkExpr("varfloat == varlong", "boolean"); // == applicable to float, long, result is boolean + checkExpr("varfloat == varfloat", "boolean"); // == applicable to float, float, result is boolean + checkExpr("varfloat == vardouble", "boolean"); // == applicable to float, double, result is boolean + checkExpr("vardouble == varbyte", "boolean"); // == applicable to double, byte, result is boolean + checkExpr("vardouble == varshort", "boolean"); // == applicable to double, short, result is boolean + checkExpr("vardouble == varchar", "boolean"); // == applicable to double, char, result is boolean + checkExpr("vardouble == varint", "boolean"); // == applicable to double, int, result is boolean + checkExpr("vardouble == varlong", "boolean"); // == applicable to double, long, result is boolean + checkExpr("vardouble == varfloat", "boolean"); // == applicable to double, float, result is boolean + checkExpr("vardouble == vardouble", "boolean"); // == applicable to double, double, result is boolean + checkExpr("student1 == student2", "boolean"); // example with two objects of the same class + checkExpr("person1 == student1", "boolean"); // example with two objects in sub-supertype relation + } + + @Test + public void testInvalidEqualsExpression() throws IOException { + checkErrorExpr("varboolean == varchar", "0xB0166"); // == not applicable to boolean, char + checkErrorExpr("varboolean == varbyte", "0xB0166"); // == not applicable to boolean, byte + checkErrorExpr("varboolean == varshort", "0xB0166"); // == not applicable to boolean, short + checkErrorExpr("varboolean == varint", "0xB0166"); // == not applicable to boolean, int + checkErrorExpr("varboolean == varlong", "0xB0166"); // == not applicable to boolean, long + checkErrorExpr("varboolean == varfloat", "0xB0166"); // == not applicable to boolean, float + checkErrorExpr("varboolean == vardouble", "0xB0166"); // == not applicable to boolean, double + checkErrorExpr("varchar == varboolean", "0xB0166"); // == not applicable to char, boolean + checkErrorExpr("varbyte == varboolean", "0xB0166"); // == not applicable to byte, boolean + checkErrorExpr("varshort == varboolean", "0xB0166"); // == not applicable to short, boolean + checkErrorExpr("varint == varboolean", "0xB0166"); // == not applicable to int, boolean + checkErrorExpr("varlong == varboolean", "0xB0166"); // == not applicable to long, boolean + checkErrorExpr("varfloat == varboolean", "0xB0166"); // == not applicable to float, boolean + checkErrorExpr("vardouble == varboolean", "0xB0166"); // == not applicable to double, boolean + checkErrorExpr("varchar = varchar == varchar", "0xA0179"); // expected char but provided boolean + checkErrorExpr("varbyte = varbyte == varbyte", "0xA0179"); // expected byte but provided boolean + checkErrorExpr("varshort = varshort == varshort", "0xA0179"); // expected short but provided boolean + checkErrorExpr("varint = varint == varint", "0xA0179"); // expected int but provided boolean + checkErrorExpr("varlong = varlong == varlong", "0xA0179"); // expected long but provided boolean + checkErrorExpr("varfloat = varfloat == varfloat", "0xA0179"); // expected float but provided boolean + checkErrorExpr("vardouble = vardouble == vardouble", "0xA0179"); // expected double but provided boolean + checkErrorExpr("person1==varboolean", "0xB0166"); + } + + @Test + public void deriveFromNotEqualsExpression() throws IOException { + checkExpr("varbyte != varbyte", "boolean"); // != applicable to byte, byte, result is boolean + checkExpr("varbyte != varshort", "boolean"); // != applicable to byte, short, result is boolean + checkExpr("varbyte != varchar", "boolean"); // != applicable to byte, char, result is boolean + checkExpr("varbyte != varint", "boolean"); // != applicable to byte, int, result is boolean + checkExpr("varbyte != varlong", "boolean"); // != applicable to byte, long, result is boolean + checkExpr("varbyte != varfloat", "boolean"); // != applicable to byte, float, result is boolean + checkExpr("varbyte != vardouble", "boolean"); // != applicable to byte, double, result is boolean + checkExpr("varshort != varbyte", "boolean"); // != applicable to short, byte, result is boolean + checkExpr("varshort != varshort", "boolean"); // != applicable to short, short, result is boolean + checkExpr("varshort != varchar", "boolean"); // != applicable to short, char, result is boolean + checkExpr("varshort != varint", "boolean"); // != applicable to short, int, result is boolean + checkExpr("varshort != varlong", "boolean"); // != applicable to short, long, result is boolean + checkExpr("varshort != varfloat", "boolean"); // != applicable to short, float, result is boolean + checkExpr("varshort != vardouble", "boolean"); // != applicable to short, double, result is boolean + checkExpr("varchar != varbyte", "boolean"); // != applicable to char, byte, result is boolean + checkExpr("varchar != varshort", "boolean"); // != applicable to char, short, result is boolean + checkExpr("varchar != varchar", "boolean"); // != applicable to char, char, result is boolean + checkExpr("varchar != varint", "boolean"); // != applicable to char, int, result is boolean + checkExpr("varchar != varlong", "boolean"); // != applicable to char, long, result is boolean + checkExpr("varchar != varfloat", "boolean"); // != applicable to char, float, result is boolean + checkExpr("varchar != vardouble", "boolean"); // != applicable to char, double, result is boolean + checkExpr("varint != varbyte", "boolean"); // != applicable to int, byte, result is boolean + checkExpr("varint != varshort", "boolean"); // != applicable to int, short, result is boolean + checkExpr("varint != varchar", "boolean"); // != applicable to int, char, result is boolean + checkExpr("varint != varint", "boolean"); // != applicable to int, int, result is boolean + checkExpr("varint != varlong", "boolean"); // != applicable to int, long, result is boolean + checkExpr("varint != varfloat", "boolean"); // != applicable to int, float, result is boolean + checkExpr("varint != vardouble", "boolean"); // != applicable to int, double, result is boolean + checkExpr("varlong != varbyte", "boolean"); // != applicable to long, byte, result is boolean + checkExpr("varlong != varshort", "boolean"); // != applicable to long, short, result is boolean + checkExpr("varlong != varchar", "boolean"); // != applicable to long, char, result is boolean + checkExpr("varlong != varint", "boolean"); // != applicable to long, int, result is boolean + checkExpr("varlong != varlong", "boolean"); // != applicable to long, long, result is boolean + checkExpr("varlong != varfloat", "boolean"); // != applicable to long, float, result is boolean + checkExpr("varlong != vardouble", "boolean"); // != applicable to long, double, result is boolean + checkExpr("varfloat != varbyte", "boolean"); // != applicable to float, byte, result is boolean + checkExpr("varfloat != varshort", "boolean"); // != applicable to float, short, result is boolean + checkExpr("varfloat != varchar", "boolean"); // != applicable to float, char, result is boolean + checkExpr("varfloat != varint", "boolean"); // != applicable to float, int, result is boolean + checkExpr("varfloat != varlong", "boolean"); // != applicable to float, long, result is boolean + checkExpr("varfloat != varfloat", "boolean"); // != applicable to float, float, result is boolean + checkExpr("varfloat != vardouble", "boolean"); // != applicable to float, double, result is boolean + checkExpr("vardouble != varbyte", "boolean"); // != applicable to double, byte, result is boolean + checkExpr("vardouble != varshort", "boolean"); // != applicable to double, short, result is boolean + checkExpr("vardouble != varchar", "boolean"); // != applicable to double, char, result is boolean + checkExpr("vardouble != varint", "boolean"); // != applicable to double, int, result is boolean + checkExpr("vardouble != varlong", "boolean"); // != applicable to double, long, result is boolean + checkExpr("vardouble != varfloat", "boolean"); // != applicable to double, float, result is boolean + checkExpr("vardouble != vardouble", "boolean"); // != applicable to double, double, result is boolean + checkExpr("person1!=person2", "boolean"); // example with two objects of the same class + checkExpr("student2!=person2", "boolean"); //example with two objects in sub-supertype relation + } + + @Test + public void testInvalidNotEqualsExpression() throws IOException { + checkErrorExpr("aBoolean != aChar", "0xFD118"); // != not applicable to boolean, char + checkErrorExpr("aBoolean != aByte", "0xFD118"); // != not applicable to boolean, byte + checkErrorExpr("aBoolean != aShort", "0xFD118"); // != not applicable to boolean, short + checkErrorExpr("aBoolean != anInt", "0xFD118"); // != not applicable to boolean, int + checkErrorExpr("aBoolean != aLong", "0xFD118"); // != not applicable to boolean, long + checkErrorExpr("aBoolean != aFloat", "0xFD118"); // != not applicable to boolean, float + checkErrorExpr("aBoolean != aDouble", "0xFD118"); // != not applicable to boolean, double + checkErrorExpr("aChar != aBoolean", "0xFD118"); // != not applicable to char, boolean + checkErrorExpr("aByte != aBoolean", "0xFD118"); // != not applicable to byte, boolean + checkErrorExpr("aShort != aBoolean", "0xFD118"); // != not applicable to short, boolean + checkErrorExpr("anInt != aBoolean", "0xFD118"); // != not applicable to int, boolean + checkErrorExpr("aLong != aBoolean", "0xFD118"); // != not applicable to long, boolean + checkErrorExpr("aFloat != aBoolean", "0xFD118"); // != not applicable to float, boolean + checkErrorExpr("aDouble != aBoolean", "0xFD118"); // != not applicable to double, boolean + checkErrorExpr("aChar = aChar != aChar", "0xFD118"); // expected char but provided boolean + checkErrorExpr("aByte = aByte != aByte", "0xFD118"); // expected byte but provided boolean + checkErrorExpr("aShort = aShort != aShort", "0xFD118"); // expected short but provided boolean + checkErrorExpr("anInt = anInt != anInt", "0xFD118"); // expected int but provided boolean + checkErrorExpr("aLong = aLong != aLong", "0xFD118"); // expected long but provided boolean + checkErrorExpr("aFloat = aFloat != aFloat", "0xFD118"); // expected float but provided boolean + checkErrorExpr("aDouble = aDouble != aDouble", "0xFD118"); // expected double but provided boolean + checkErrorExpr("person1!=varboolean", "0xB0166"); // person1 is a Person, foo is a boolean + } + + @Test + public void deriveFromBooleanAndOpExpression() throws IOException { + checkExpr("varboolean && varboolean", "boolean"); // && applicable to boolean, boolean, result is boolean + checkExpr("true&&true", "boolean"); //only possible with two booleans + checkExpr("(3<=4&&5>6)", "boolean"); + } + + @Test + public void testInvalidAndOpExpression() throws IOException { + checkErrorExpr("varboolean && varchar", "0xB0113"); // && not applicable to boolean, char + checkErrorExpr("varboolean && varbyte", "0xB0113"); // && not applicable to boolean, byte + checkErrorExpr("varboolean && varshort", "0xB0113"); // && not applicable to boolean, short + checkErrorExpr("varboolean && varint", "0xB0113"); // && not applicable to boolean, int + checkErrorExpr("varboolean && varlong", "0xB0113"); // && not applicable to boolean, long + checkErrorExpr("varboolean && varfloat", "0xB0113"); // && not applicable to boolean, float + checkErrorExpr("varboolean && vardouble", "0xB0113"); // && not applicable to boolean, double + checkErrorExpr("varchar && varboolean", "0xB0113"); // && not applicable to char, boolean + checkErrorExpr("varchar && varchar", "0xB0113"); // && not applicable to char, char + checkErrorExpr("varchar && varbyte", "0xB0113"); // && not applicable to char, byte + checkErrorExpr("varchar && varshort", "0xB0113"); // && not applicable to char, short + checkErrorExpr("varchar && varint", "0xB0113"); // && not applicable to char, int + checkErrorExpr("varchar && varlong", "0xB0113"); // && not applicable to char, long + checkErrorExpr("varchar && varfloat", "0xB0113"); // && not applicable to char, float + checkErrorExpr("varchar && vardouble", "0xB0113"); // && not applicable to char, double + checkErrorExpr("varbyte && varboolean", "0xB0113"); // && not applicable to byte, boolean + checkErrorExpr("varbyte && varchar", "0xB0113"); // && not applicable to byte, char + checkErrorExpr("varbyte && varbyte", "0xB0113"); // && not applicable to byte, byte + checkErrorExpr("varbyte && varshort", "0xB0113"); // && not applicable to byte, short + checkErrorExpr("varbyte && varint", "0xB0113"); // && not applicable to byte, int + checkErrorExpr("varbyte && varlong", "0xB0113"); // && not applicable to byte, long + checkErrorExpr("varbyte && varfloat", "0xB0113"); // && not applicable to byte, float + checkErrorExpr("varbyte && vardouble", "0xB0113"); // && not applicable to byte, double + checkErrorExpr("varshort && varboolean", "0xB0113"); // && not applicable to short, boolean + checkErrorExpr("varshort && varchar", "0xB0113"); // && not applicable to short, char + checkErrorExpr("varshort && varbyte", "0xB0113"); // && not applicable to short, byte + checkErrorExpr("varshort && varshort", "0xB0113"); // && not applicable to short, short + checkErrorExpr("varshort && varint", "0xB0113"); // && not applicable to short, int + checkErrorExpr("varshort && varlong", "0xB0113"); // && not applicable to short, long + checkErrorExpr("varshort && varfloat", "0xB0113"); // && not applicable to short, float + checkErrorExpr("varshort && vardouble", "0xB0113"); // && not applicable to short, double + checkErrorExpr("varint && varboolean", "0xB0113"); // && not applicable to short, boolean + checkErrorExpr("varint && varchar", "0xB0113"); // && not applicable to int, char + checkErrorExpr("varint && varbyte", "0xB0113"); // && not applicable to int, byte + checkErrorExpr("varint && varshort", "0xB0113"); // && not applicable to int, short + checkErrorExpr("varint && varint", "0xB0113"); // && not applicable to int, int + checkErrorExpr("varint && varlong", "0xB0113"); // && not applicable to int, long + checkErrorExpr("varint && varfloat", "0xB0113"); // && not applicable to int, float + checkErrorExpr("varint && vardouble", "0xB0113"); // && not applicable to int, double + checkErrorExpr("varlong && varboolean", "0xB0113"); // && not applicable to long, boolean + checkErrorExpr("varlong && varchar", "0xB0113"); // && not applicable to long, char + checkErrorExpr("varlong && varbyte", "0xB0113"); // && not applicable to long, byte + checkErrorExpr("varlong && varshort", "0xB0113"); // && not applicable to long, short + checkErrorExpr("varlong && varint", "0xB0113"); // && not applicable to long, int + checkErrorExpr("varlong && varlong", "0xB0113"); // && not applicable to long, long + checkErrorExpr("varlong && varfloat", "0xB0113"); // && not applicable to long, float + checkErrorExpr("varlong && vardouble", "0xB0113"); // && not applicable to long, double + checkErrorExpr("varfloat && varboolean", "0xB0113"); // && not applicable to float, boolean + checkErrorExpr("varfloat && varchar", "0xB0113"); // && not applicable to float, char + checkErrorExpr("varfloat && varbyte", "0xB0113"); // && not applicable to float, byte + checkErrorExpr("varfloat && varshort", "0xB0113"); // && not applicable to float, short + checkErrorExpr("varfloat && varint", "0xB0113"); // && not applicable to float, int + checkErrorExpr("varfloat && varlong", "0xB0113"); // && not applicable to float, long + checkErrorExpr("varfloat && varfloat", "0xB0113"); // && not applicable to float, float + checkErrorExpr("varfloat && vardouble", "0xB0113"); // && not applicable to float, double + checkErrorExpr("vardouble && varboolean", "0xB0113"); // && not applicable to double, boolean + checkErrorExpr("vardouble && varchar", "0xB0113"); // && not applicable to double, char + checkErrorExpr("vardouble && varbyte", "0xB0113"); // && not applicable to double, byte + checkErrorExpr("vardouble && varshort", "0xB0113"); // && not applicable to double, short + checkErrorExpr("vardouble && varint", "0xB0113"); // && not applicable to double, int + checkErrorExpr("vardouble && varlong", "0xB0113"); // && not applicable to double, long + checkErrorExpr("vardouble && varfloat", "0xB0113"); // && not applicable to double, float + checkErrorExpr("vardouble && vardouble", "0xB0113"); // && not applicable to double, double + checkErrorExpr("varchar = varboolean && varboolean", "0xA0179"); // expected char but provided boolean + checkErrorExpr("varbyte = varboolean && varboolean", "0xA0179"); // expected byte but provided boolean + checkErrorExpr("varshort = varboolean && varboolean", "0xA0179"); // expected short but provided boolean + checkErrorExpr("varint = varboolean && varboolean", "0xA0179"); // expected int but provided boolean + checkErrorExpr("varlong = varboolean && varboolean", "0xA0179"); // expected long but provided boolean + checkErrorExpr("varfloat = varboolean && varboolean", "0xA0179"); // expected float but provided boolean + checkErrorExpr("vardouble = varboolean && varboolean", "0xA0179"); // expected double but provided boolean + } + + @Test + public void deriveFromBooleanOrOpExpression() throws IOException { + checkExpr("varboolean || varboolean", "boolean"); // || applicable to boolean, boolean, result is boolean + checkExpr("true||false", "boolean"); //only possible with two booleans + checkExpr("(3<=4.5f||5.3>6)", "boolean"); + } + + @Test + public void testInvalidOrOpExpression() throws IOException { + checkErrorExpr("varboolean || varchar", "0xB0113"); // || not applicable to boolean, char + checkErrorExpr("varboolean || varbyte", "0xB0113"); // || not applicable to boolean, byte + checkErrorExpr("varboolean || varshort", "0xB0113"); // || not applicable to boolean, short + checkErrorExpr("varboolean || varint", "0xB0113"); // || not applicable to boolean, int + checkErrorExpr("varboolean || varlong", "0xB0113"); // || not applicable to boolean, long + checkErrorExpr("varboolean || varfloat", "0xB0113"); // || not applicable to boolean, float + checkErrorExpr("varboolean || vardouble", "0xB0113"); // || not applicable to boolean, double + checkErrorExpr("varchar || varboolean", "0xB0113"); // || not applicable to char, boolean + checkErrorExpr("varchar || varchar", "0xB0113"); // || not applicable to char, char + checkErrorExpr("varchar || varbyte", "0xB0113"); // || not applicable to char, byte + checkErrorExpr("varchar || varshort", "0xB0113"); // || not applicable to char, short + checkErrorExpr("varchar || varint", "0xB0113"); // || not applicable to char, int + checkErrorExpr("varchar || varlong", "0xB0113"); // || not applicable to char, long + checkErrorExpr("varchar || varfloat", "0xB0113"); // || not applicable to char, float + checkErrorExpr("varchar || vardouble", "0xB0113"); // || not applicable to char, double + checkErrorExpr("varbyte || varboolean", "0xB0113"); // || not applicable to byte, boolean + checkErrorExpr("varbyte || varchar", "0xB0113"); // || not applicable to byte, char + checkErrorExpr("varbyte || varbyte", "0xB0113"); // || not applicable to byte, byte + checkErrorExpr("varbyte || varshort", "0xB0113"); // || not applicable to byte, short + checkErrorExpr("varbyte || varint", "0xB0113"); // || not applicable to byte, int + checkErrorExpr("varbyte || varlong", "0xB0113"); // || not applicable to byte, long + checkErrorExpr("varbyte || varfloat", "0xB0113"); // || not applicable to byte, float + checkErrorExpr("varbyte || vardouble", "0xB0113"); // || not applicable to byte, double + checkErrorExpr("varshort || varboolean", "0xB0113"); // || not applicable to short, boolean + checkErrorExpr("varshort || varchar", "0xB0113"); // || not applicable to short, char + checkErrorExpr("varshort || varbyte", "0xB0113"); // || not applicable to short, byte + checkErrorExpr("varshort || varshort", "0xB0113"); // || not applicable to short, short + checkErrorExpr("varshort || varint", "0xB0113"); // || not applicable to short, int + checkErrorExpr("varshort || varlong", "0xB0113"); // || not applicable to short, long + checkErrorExpr("varshort || varfloat", "0xB0113"); // || not applicable to short, float + checkErrorExpr("varshort || vardouble", "0xB0113"); // || not applicable to short, double + checkErrorExpr("varint || varboolean", "0xB0113"); // || not applicable to short, boolean + checkErrorExpr("varint || varchar", "0xB0113"); // || not applicable to int, char + checkErrorExpr("varint || varbyte", "0xB0113"); // || not applicable to int, byte + checkErrorExpr("varint || varshort", "0xB0113"); // || not applicable to int, short + checkErrorExpr("varint || varint", "0xB0113"); // || not applicable to int, int + checkErrorExpr("varint || varlong", "0xB0113"); // || not applicable to int, long + checkErrorExpr("varint || varfloat", "0xB0113"); // || not applicable to int, float + checkErrorExpr("varint || vardouble", "0xB0113"); // || not applicable to int, double + checkErrorExpr("varlong || varboolean", "0xB0113"); // || not applicable to long, boolean + checkErrorExpr("varlong || varchar", "0xB0113"); // || not applicable to long, char + checkErrorExpr("varlong || varbyte", "0xB0113"); // || not applicable to long, byte + checkErrorExpr("varlong || varshort", "0xB0113"); // || not applicable to long, short + checkErrorExpr("varlong || varint", "0xB0113"); // || not applicable to long, int + checkErrorExpr("varlong || varlong", "0xB0113"); // || not applicable to long, long + checkErrorExpr("varlong || varfloat", "0xB0113"); // || not applicable to long, float + checkErrorExpr("varlong || vardouble", "0xB0113"); // || not applicable to long, double + checkErrorExpr("varfloat || varboolean", "0xB0113"); // || not applicable to float, boolean + checkErrorExpr("varfloat || varchar", "0xB0113"); // || not applicable to float, char + checkErrorExpr("varfloat || varbyte", "0xB0113"); // || not applicable to float, byte + checkErrorExpr("varfloat || varshort", "0xB0113"); // || not applicable to float, short + checkErrorExpr("varfloat || varint", "0xB0113"); // || not applicable to float, int + checkErrorExpr("varfloat || varlong", "0xB0113"); // || not applicable to float, long + checkErrorExpr("varfloat || varfloat", "0xB0113"); // || not applicable to float, float + checkErrorExpr("varfloat || vardouble", "0xB0113"); // || not applicable to float, double + checkErrorExpr("vardouble || varboolean", "0xB0113"); // || not applicable to double, boolean + checkErrorExpr("vardouble || varchar", "0xB0113"); // || not applicable to double, char + checkErrorExpr("vardouble || varbyte", "0xB0113"); // || not applicable to double, byte + checkErrorExpr("vardouble || varshort", "0xB0113"); // || not applicable to double, short + checkErrorExpr("vardouble || varint", "0xB0113"); // || not applicable to double, int + checkErrorExpr("vardouble || varlong", "0xB0113"); // || not applicable to double, long + checkErrorExpr("vardouble || varfloat", "0xB0113"); // || not applicable to double, float + checkErrorExpr("vardouble || vardouble", "0xB0113"); // || not applicable to double, double + checkErrorExpr("varchar = varboolean || varboolean", "0xA0179"); // expected char but provided boolean + checkErrorExpr("varbyte = varboolean || varboolean", "0xA0179"); // expected byte but provided boolean + checkErrorExpr("varshort = varboolean || varboolean", "0xA0179"); // expected short but provided boolean + checkErrorExpr("varint = varboolean || varboolean", "0xA0179"); // expected int but provided boolean + checkErrorExpr("varlong = varboolean || varboolean", "0xA0179"); // expected long but provided boolean + checkErrorExpr("varfloat = varboolean || varboolean", "0xA0179"); // expected float but provided boolean + checkErrorExpr("vardouble = varboolean || varboolean", "0xA0179"); // expected double but provided boolean + } + + @Test + public void deriveFromLogicalNotExpression() throws IOException { + checkExpr("!varboolean", "boolean"); // ~ applicable to boolean + checkExpr("!true", "boolean"); // only possible with boolean as inner expression + checkExpr("!(2.5>=0.3)", "boolean"); + checkExpr("!varboolean", "boolean"); // ~ applicable to boolean, result is boolean + } + + @Test + public void testInvalidLogicalNotExpression() throws IOException { + checkErrorExpr("!varchar", "0xB0164"); // ! not applicable to char + checkErrorExpr("!varbyte", "0xB0164"); // ! not applicable to byte + checkErrorExpr("!varshort", "0xB0164"); // ! not applicable to short + checkErrorExpr("!varint", "0xB0164"); // ! not applicable to int + checkErrorExpr("!varlong", "0xB0164"); // ! not applicable to long + checkErrorExpr("!varfloat", "0xB0164"); // ! not applicable to float + checkErrorExpr("!vardouble", "0xB0164"); // ! not applicable to double + checkErrorExpr("!varchar", "0xB0164"); // ! not applicable to char + checkErrorExpr("!varbyte", "0xB0164"); // ! not applicable to byte + checkErrorExpr("!varshort", "0xB0164"); // ! not applicable to short + checkErrorExpr("!varint", "0xB0164"); // ! not applicable to int + checkErrorExpr("!varlong", "0xB0164"); // ! not applicable to long + checkErrorExpr("!varfloat", "0xB0164"); // ! not applicable to float + checkErrorExpr("!vardouble", "0xB0164"); // ! not applicable to double + } + + @Test + public void deriveFromBracketExpression() throws IOException { + checkExpr("(3)", "int"); // test with only a literal in the inner expression + checkExpr("(3+4*(18-7.5))", "double"); // test with a more complex inner expression + checkExpr("(person1)", "Person"); // test without primitive types in inner expression + } + + @Test + public void testInvalidBracketExpression() throws IOException { + checkErrorExpr("(a)", "0xFD118"); // a cannot be resolved -> a has no type + } + + @Test + public void deriveFromConditionalExpression() throws IOException { + ISymTypeRelations typeRel = new SymTypeRelations(); + + //test with byte and short + ASTExpression astExpr = parseExpr("varboolean ? varbyte : varshort"); + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + SymTypeExpression type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_shortSymType, type)); + assertFalse(typeRel.isCompatible(_byteSymType, type)); + + //test with two ints as true and false expression + astExpr = parseExpr("3<4?9:10"); + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_intSymType, type)); + + // test with boolean and int + astExpr = parseExpr("3<4?true:7"); + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertFalse(typeRel.isCompatible(_booleanSymType, type)); + assertFalse(typeRel.isCompatible(_intSymType, type)); + + //test with float and long + astExpr = parseExpr("3>4?4.5f:10L"); + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_floatSymType, type)); + assertFalse(typeRel.isCompatible(_longSymType, type)); + + //test without primitive types as true and false expression + astExpr = parseExpr("3<9?person1:person2"); + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_personSymType, type)); + + //test with two objects in a sub-supertype relation + astExpr = parseExpr("3<9?student1:person2"); + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_personSymType, type)); + assertFalse(typeRel.isCompatible(_studentSymType, type)); + + astExpr = parseExpr("varboolean ? 0 : 1"); // ? applicable to boolean + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_intSymType, type)); + + astExpr = parseExpr("varboolean ? varboolean : varboolean"); // ? applicable to boolean, boolean, result is boolean + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_booleanSymType, type)); + + astExpr = parseExpr("varbyte = varboolean ? varbyte : varbyte"); // ? applicable to byte, byte, result is byte + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_byteSymType, type)); + + astExpr = parseExpr("varshort = varboolean ? varbyte : varshort"); // ? applicable to byte, short, result is short + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_shortSymType, type)); + + astExpr = parseExpr("varshort = varboolean ? varshort : varbyte"); // ? applicable to short, byte, result is short + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_shortSymType, type)); + + astExpr = parseExpr("varshort = varboolean ? varshort : varshort"); // ? applicable to short, short, result is short + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_shortSymType, type)); + + astExpr = parseExpr("varchar = varboolean ? varchar : varchar"); // ? applicable to char, char, result is char + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_charSymType, type)); + + astExpr = parseExpr("varint = varboolean ? varchar : varbyte"); // ? applicable to char, byte, result is int + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_intSymType, type)); + + astExpr = parseExpr("varint = varboolean ? varbyte : varchar"); // ? applicable to byte, char, result is int + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_intSymType, type)); + + astExpr = parseExpr("varint = varboolean ? varchar : varshort"); // ? applicable to char, short, result is int + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_intSymType, type)); + + astExpr = parseExpr("varint = varboolean ? varshort : varchar"); // ? applicable to short, char, result is int + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_intSymType, type)); + + astExpr = parseExpr("varint = varboolean ? varint : varbyte"); // ? applicable to int, byte, result is int + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_intSymType, type)); + + astExpr = parseExpr("varint = varboolean ? varint : varshort"); // ? applicable to int, short, result is int + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_intSymType, type)); + + astExpr = parseExpr("varint = varboolean ? varint : varchar"); // ? applicable to int, char, result is int + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_intSymType, type)); + + astExpr = parseExpr("varint = varboolean ? varbyte : varint"); // ? applicable to byte, int, result is int + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_intSymType, type)); + + astExpr = parseExpr("varint = varboolean ? varshort : varint"); // ? applicable to short, int, result is int + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_intSymType, type)); + + astExpr = parseExpr("varint = varboolean ? varchar : varint"); // ? applicable to char, int, result is int + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_intSymType, type)); + + astExpr = parseExpr("varint = varboolean ? varint : varint"); // ? applicable to int, int, result is int + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_longSymType, type)); + + astExpr = parseExpr("varlong = varboolean ? varlong : varbyte"); // ? applicable to long, byte, result is long + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_longSymType, type)); + + astExpr = parseExpr("varlong = varboolean ? varlong : varshort"); // ? applicable to long, short, result is long + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_longSymType, type)); + + astExpr = parseExpr("varlong = varboolean ? varlong : varchar"); // ? applicable to long, char, result is long + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_longSymType, type)); + + astExpr = parseExpr("varlong = varboolean ? varlong : varint"); // ? applicable to long, int, result is long + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_longSymType, type)); + + astExpr = parseExpr("varlong = varboolean ? varbyte : varlong"); // ? applicable to byte, long, result is long + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_longSymType, type)); + + astExpr = parseExpr("varlong = varboolean ? varshort : varlong"); // ? applicable to short, long, result is long + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_longSymType, type)); + + astExpr = parseExpr("varlong = varboolean ? varchar : varlong"); // ? applicable to char, long, result is long + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_longSymType, type)); + + astExpr = parseExpr("varlong = varboolean ? varint : varlong"); // ? applicable to int, long, result is long + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_longSymType, type)); + + astExpr = parseExpr("varlong = varboolean ? varlong : varlong"); // ? applicable to long, long, result is long + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_longSymType, type)); + + astExpr = parseExpr("varfloat = varboolean ? varfloat : varbyte"); // ? applicable to float, byte, result is float + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_floatSymType, type)); + + astExpr = parseExpr("varfloat = varboolean ? varfloat : varshort"); // ? applicable to float, short, result is float + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_floatSymType, type)); + + astExpr = parseExpr("varfloat = varboolean ? varfloat : varchar"); // ? applicable to float, char, result is float + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_floatSymType, type)); + + astExpr = parseExpr("varfloat = varboolean ? varfloat : varint"); // ? applicable to float, int, result is float + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_floatSymType, type)); + + astExpr = parseExpr("varfloat = varboolean ? varfloat : varlong"); // ? applicable to float, long, result is float + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_floatSymType, type)); + + astExpr = parseExpr("varfloat = varboolean ? varbyte : varfloat"); // ? applicable to byte, float, result is float + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_floatSymType, type)); + + astExpr = parseExpr("varfloat = varboolean ? varshort : varfloat"); // ? applicable to short, float, result is float + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_floatSymType, type)); + + astExpr = parseExpr("varfloat = varboolean ? varchar : varfloat"); // ? applicable to char, float, result is float + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_floatSymType, type)); + + astExpr = parseExpr("varfloat = varboolean ? varint : varfloat"); // ? applicable to int, float, result is float + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_floatSymType, type)); + + astExpr = parseExpr("varfloat = varboolean ? varlong : varfloat"); // ? applicable to long, float, result is float + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_floatSymType, type)); + + astExpr = parseExpr("varfloat = varboolean ? varfloat : varfloat"); // ? applicable to float, float, result is float + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_floatSymType, type)); + + astExpr = parseExpr("vardouble = varboolean ? vardouble : varbyte"); // ? applicable to double, byte, result is double + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_doubleSymType, type)); + + astExpr = parseExpr("vardouble = varboolean ? vardouble : varshort"); // ? applicable to double, short, result is double + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_doubleSymType, type)); + + astExpr = parseExpr("vardouble = varboolean ? vardouble : varchar"); // ? applicable to double, char, result is double + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_doubleSymType, type)); + + astExpr = parseExpr("vardouble = varboolean ? vardouble : varint"); // ? applicable to double, int, result is double + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_doubleSymType, type)); + + astExpr = parseExpr("vardouble = varboolean ? vardouble : varlong"); // ? applicable to double, long, result is double + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_doubleSymType, type)); + + astExpr = parseExpr("vardouble = varboolean ? vardouble : varfloat"); // ? applicable to double, long, result is double + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_doubleSymType, type)); + + astExpr = parseExpr("vardouble = varboolean ? varbyte : vardouble"); // ? applicable to byte, double, result is double + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_doubleSymType, type)); + + astExpr = parseExpr("vardouble = varboolean ? varshort : vardouble"); // ? applicable to short, double, result is double + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_doubleSymType, type)); + + astExpr = parseExpr("vardouble = varboolean ? varchar : vardouble"); // ? applicable to char, double, result is double + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_doubleSymType, type)); + + astExpr = parseExpr("vardouble = varboolean ? varint : vardouble"); // ? applicable to int, double, result is double + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_doubleSymType, type)); + + astExpr = parseExpr("vardouble = varboolean ? varlong : vardouble"); // ? applicable to long, double, result is double + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_doubleSymType, type)); + + astExpr = parseExpr("vardouble = varboolean ? varfloat : vardouble"); // ? applicable to float, double, result is double + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_doubleSymType, type)); + + astExpr = parseExpr("vardouble = varboolean ? vardouble : vardouble"); // ? applicable to double, double, result is double + generateScopes(astExpr); + assertNoFindings(); + calculateTypes(astExpr); + type = getType4Ast().getTypeOfExpression(astExpr); + assertTrue(typeRel.isCompatible(_doubleSymType, type)); + } + + @Test + public void testInvalidConditionalExpression() throws IOException { + checkErrorExpr("3?true:fvarlse", "0xFD118"); + checkErrorExpr("varbyte ? 0 : 1", "0xB0165"); // ? not applicable to byte + checkErrorExpr("varshort ? 0 : 1", "0xB0165"); // ? not applicable to short + checkErrorExpr("varchar ? 0 : 1", "0xB0165"); // ? not applicable to char + checkErrorExpr("varint ? 0 : 1", "0xB0165"); // ? not applicable to int + checkErrorExpr("varlong ? 0 : 1", "0xB0165"); // ? not applicable to long + checkErrorExpr("varfloat ? 0 : 1", "0xB0165"); // ? not applicable to float + checkErrorExpr("vardouble ? 0 : 1", "0xB0165"); // ? not applicable to double + checkErrorExpr("varboolean = varboolean ? varbyte : varboolean", "0xA0179"); // expected boolean but provided byte + checkErrorExpr("varboolean = varboolean ? varboolean : varbyte", "0xA0179"); // expected boolean but provided byte + checkErrorExpr("varboolean = varboolean ? varbyte : varbyte", "0xA0179"); // expected boolean but provided byte + } + + @Test + public void deriveFromBooleanNotExpression() throws IOException { + checkExpr("~varchar", "int"); // ~ applicable to char + checkExpr("~varbyte", "int"); // ~ applicable to byte + checkExpr("~varshort", "int"); // ~ applicable to short + checkExpr("~varint", "int"); // ~ applicable to int + checkExpr("~varlong", "long"); // ~ applicable to long + checkExpr("~varchar", "int"); // ~ applicable to char, result is int + checkExpr("~varbyte", "int"); // ~ applicable to byte, result is int + checkExpr("~varshort", "int"); // ~ applicable to short, result is int + checkExpr("~varint", "int"); // ~ applicable to int, result is int + checkExpr("~varlong", "long"); // ~ applicable to long, result is long + } + + @Test + public void testInvalidBooleanNotExpression() throws IOException { + checkErrorExpr("~varboolean", "0xB0175"); // ! not applicable to boolean + checkErrorExpr("~varfloat", "0xB0175"); // ! not applicable to boolean + checkErrorExpr("~vardouble", "0xB0175"); // ! not applicable to boolean + checkErrorExpr("varchar = ~varchar", "0xA0179"); // ~ applicable to char, but result is int + checkErrorExpr("varbyte = ~varbyte", "0xA0179"); // ~ applicable to byte, but result is int + checkErrorExpr("varshort = ~varshort", "0xA0179"); // ~ applicable to short, but result is int + } + + /** + * initialize symboltable including + * global scope, artifact scopes and scopes with symbols for testing + * (mostly used for FieldAccessExpressions) + */ + public void init_advanced() { + ICombineExpressionsWithLiteralsGlobalScope globalScope = + CombineExpressionsWithLiteralsMill.globalScope(); + + ICombineExpressionsWithLiteralsArtifactScope artifactScope2 = + CombineExpressionsWithLiteralsMill.artifactScope(); + artifactScope2.setEnclosingScope(globalScope); + artifactScope2.setImportsList(Lists.newArrayList()); + artifactScope2.setName("types"); + artifactScope2.setPackageName(""); + + ICombineExpressionsWithLiteralsArtifactScope artifactScope3 = + CombineExpressionsWithLiteralsMill.artifactScope(); + artifactScope3.setEnclosingScope(globalScope); + artifactScope3.setImportsList(Lists.newArrayList()); + artifactScope3.setName("types2"); + + ICombineExpressionsWithLiteralsArtifactScope artifactScope4 = + CombineExpressionsWithLiteralsMill.artifactScope(); + artifactScope4.setEnclosingScope(globalScope); + artifactScope4.setImportsList(Lists.newArrayList()); + artifactScope4.setName("types3"); + artifactScope4.setPackageName("types3"); + + ICombineExpressionsWithLiteralsArtifactScope artifactScope5 = + CombineExpressionsWithLiteralsMill.artifactScope(); + artifactScope5.setEnclosingScope(globalScope); + artifactScope5.setImportsList(Lists.newArrayList()); + artifactScope5.setName("functions1"); + artifactScope5.setPackageName("functions1"); + + ICombineExpressionsWithLiteralsArtifactScope artifactScope6 = + CombineExpressionsWithLiteralsMill.artifactScope(); + artifactScope6.setEnclosingScope(globalScope); + artifactScope6.setImportsList(Lists.newArrayList()); + artifactScope6.setName("functions2"); + artifactScope6.setPackageName("functions2"); + + //todo + // No enclosing Scope: Search ending here + + ICombineExpressionsWithLiteralsScope scope3 = + CombineExpressionsWithLiteralsMill.scope(); + scope3.setName("types2"); + artifactScope4.addSubScope(scope3); + scope3.setEnclosingScope(artifactScope4); + + TypeSymbol selfReflectiveStudent = BasicSymbolsMill.typeSymbolBuilder() + .setName("SelfReflectiveStudent") + .setSpannedScope(OOSymbolsMill.scope()) + .setSuperTypesList(List.of(_studentSymType)) + .setEnclosingScope(globalScope) + .build(); + inScope(selfReflectiveStudent.getSpannedScope(), method("self", + createTypeObject(selfReflectiveStudent)) + ); + inScope(selfReflectiveStudent.getSpannedScope(), + variable("selfVar", createTypeObject(selfReflectiveStudent)) + ); + inScope(globalScope, variable("selfReflectiveStudent", + createTypeObject("SelfReflectiveStudent", globalScope)) + ); + + inScope(artifactScope2, type("AClass")); + inScope(scope3, type("AClass")); + inScope(globalScope, type("AClass")); + + inScope(artifactScope2, type("BClass")); + inScope(scope3, type("BClass")); + + inScope(artifactScope2, type("CClass")); + inScope(scope3, type("CClass")); + + inScope(artifactScope2, selfReflectiveStudent); + + MethodSymbol ms2 = method("isInt", _booleanSymType); + inScope(globalScope, ms2); + inScope(globalScope, method("isInt", _booleanSymType, _intSymType)); + MethodSymbol ms0 = method("areInt", _booleanSymType, _intSymType); + ms0.setIsElliptic(true); + inScope(globalScope, ms0); + inScope(globalScope, method("getIsInt", ms2.getFunctionType())); + inScope(globalScope, method("getAreInt", ms0.getFunctionType())); + + OOTypeSymbol testType = + inScope(globalScope, oOtype("Test", + Collections.emptyList(), + Collections.emptyList(), + List.of( + method("store", _doubleSymType), + method("pay", _voidSymType, _intSymType) + ), + List.of(field("variable", _intSymType)) + )); + testType.getMethodList().forEach(m -> m.setIsStatic(true)); + testType.getFieldList().forEach(f -> f.setIsStatic(true)); + + OOTypeSymbol testType2 = + inScope(artifactScope2, oOtype("Test", + Collections.emptyList(), + Collections.emptyList(), + List.of( + method("store", _doubleSymType), + method("pay", _voidSymType, _intSymType) + ), + List.of(field("variable", _intSymType)) + )); + testType2.getMethodList().forEach(m -> m.setIsStatic(true)); + testType2.getFieldList().forEach(f -> f.setIsStatic(true)); + + OOTypeSymbol testType3 = + oOtype("Test", + Collections.emptyList(), + Collections.emptyList(), + List.of( + method("store", _doubleSymType), + method("pay", _voidSymType, _intSymType) + ), + List.of(field("variable", _intSymType)) + ); + testType3.getMethodList().forEach(m -> m.setIsStatic(true)); + testType3.getFieldList().forEach(f -> f.setIsStatic(true)); + + IOOSymbolsScope testScope = testType3.getSpannedScope(); + + FieldSymbol testVariable = field("testVariable", _shortSymType); + testVariable.setIsStatic(true); + OOTypeSymbol testInnerType = OOSymbolsMill.oOTypeSymbolBuilder() + .setName("TestInnerType") + .setSpannedScope(CombineExpressionsWithLiteralsMill.scope()) + .setEnclosingScope(testScope) + .build(); + testInnerType.addFieldSymbol(testVariable); + testInnerType.setIsStatic(true); + inScope(testScope, testInnerType); + inScope(testInnerType.getSpannedScope(), testVariable); + + testType3.setSpannedScope(testScope); + + inScope(artifactScope2, testType2); + inScope(scope3, testType3); + inScope(globalScope, testType); + + inScope(artifactScope5, function("getPi", _floatSymType)); + inScope(artifactScope6, function("getPi", _floatSymType)); + + // Creating types for legal access + // on "types.DeepNesting.firstLayer.onlyMember", + // where firstLayer and onlyMember are fields + OOTypeSymbol oneFieldMember = + inScope(globalScope, oOtype("OneFieldMember")); + FieldSymbol onlyMember = field("onlyMember", _intSymType); + inScope(oneFieldMember.getSpannedScope(), onlyMember); + + OOTypeSymbol deepNesting = oOtype("DeepNesting"); + inScope(artifactScope2, deepNesting); + FieldSymbol firstLayer = field("firstLayer", + SymTypeExpressionFactory.createTypeExpression(oneFieldMember)); + firstLayer.setIsStatic(true); + inScope(deepNesting.getSpannedScope(), firstLayer); + } + + @Test + public void deriveFromFieldAccessExpression() throws IOException { + //initialize symbol table + init_advanced(); + + //test for variable of a type with one package + checkExpr("types.Test.variable", "int"); + + //test for variable of type with more than one package + checkExpr("types3.types2.Test.variable", "int"); + + //test for variable in inner type + checkExpr("types3.types2.Test.TestInnerType.testVariable", "short"); + + // test for nested field access ("firstLayer" is a static member, "onlyMember" is an instance member) + checkExpr("types.DeepNesting.firstLayer.onlyMember", "int"); + } + + @Test + public void deriveFromFieldAccessExpressionTypeVarUpperBound() throws IOException { + IBasicSymbolsGlobalScope gs = BasicSymbolsMill.globalScope(); + // add Person.age + inScope(_personSymType.getTypeInfo().getSpannedScope(), + variable("age", _intSymType) + ); + // TPSub extends Person + // TPSub tPSub; + inScope(gs, variable("tPSub", + createTypeVariable(inScope(gs, + typeVariable("TPSub", List.of(_personSymType)) + )) + )); + // TSSub extends Student + // TSSub tSSub; + inScope(gs, variable("tSSub", + createTypeVariable(inScope(gs, + typeVariable("TSSub", List.of(_studentSymType)) + )) + )); + + checkExpr("tPSub.age", "int"); + checkExpr("tSSub.age", "int"); + } + + @Test + public void deriveFromFieldAccessExpressionUnion() throws IOException { + IBasicSymbolsGlobalScope gs = BasicSymbolsMill.globalScope(); + // add Person.age + inScope(_personSymType.getTypeInfo().getSpannedScope(), + variable("age", _intSymType) + ); + // (Student | Child) teachablePerson; + inScope(gs, variable("teachablePerson", + createUnion(_studentSymType, _childSymType) + )); + + checkExpr("teachablePerson.age", "int"); + } + + @Test + public void testInvalidFieldAccessExpressionUnion() throws IOException { + IBasicSymbolsGlobalScope gs = BasicSymbolsMill.globalScope(); + // add Person.age + inScope(_personSymType.getTypeInfo().getSpannedScope(), + variable("age", _intSymType) + ); + // (Person | int) maybePerson; + inScope(gs, variable("maybePerson", + createUnion(_personSymType, _intSymType) + )); + + checkErrorExpr("maybePerson.age", "0xF737F"); + } + + @Test + public void deriveFromFieldAccessExpressionIntersection() throws IOException { + IBasicSymbolsGlobalScope gs = BasicSymbolsMill.globalScope(); + // add Person.age + inScope(_personSymType.getTypeInfo().getSpannedScope(), + variable("age", _intSymType) + ); + // (Person & Car) talkingCar; + inScope(gs, variable("talkingCar", + createIntersection(_personSymType, _carSymType) + )); + checkExpr("talkingCar.age", "int"); + } + + @Test + public void syntheziseFromFieldAccessExpression() throws IOException { + init_advanced(); + + checkType("Test", "Test"); + + //test for type with only one package + checkType("types.Test", "Test"); + + //test for type with more than one package + checkType("types3.types2.Test", "types3.types2.Test"); + } + + @Test + public void deriveFromCallExpression() throws IOException { + //initialize symbol table + init_advanced(); + + //test for method with unqualified name without parameters + checkExpr("isInt()", "boolean"); + + //test for method with unqualified name with parameters + checkExpr("isInt(4)", "boolean"); + + //test for method with varargs with no optional value + checkExpr("areInt()", "boolean"); + + //test for method with varargs with one optional value + checkExpr("areInt(1)", "boolean"); + + //test for method with varargs with multiple optional values + checkExpr("areInt(1, 2, 3)", "boolean"); + + //test for method with qualified name without parameters + checkExpr("types.Test.store()", "double"); + + //test for method with qualified name with parameters + checkExpr("types.Test.pay(4)", "void"); + + //test for function with that exists in another scope with + //the same name but different qualified name + checkExpr("functions1.functions1.getPi()", "float"); + + // test method chaining + checkExpr("selfReflectiveStudent.self().self()", "SelfReflectiveStudent"); + + // test function chaining + checkExpr("getIsInt()()", "boolean"); + checkExpr("(()->()->1)()()", "int"); + + // test indirect function chaining + checkExpr("(getIsInt())()", "boolean"); + checkExpr("((()->()->1)())()", "int"); + + // test function chaining with varargs + checkExpr("getAreInt()()", "boolean"); + checkExpr("getAreInt()(1,2)", "boolean"); + + // test function chaining using fields + checkExpr("selfReflectiveStudent.self().self()", "SelfReflectiveStudent"); + } + + @Test + public void deriveFromCallExpressionTypeVarUpperBound() throws IOException { + IBasicSymbolsGlobalScope gs = BasicSymbolsMill.globalScope(); + // add Person::getAge + inScope(_personSymType.getTypeInfo().getSpannedScope(), + function("getAge", _intSymType) + ); + // TPSub extends Person + // TPSub tPSub; + inScope(gs, variable("tPSub", + createTypeVariable(inScope(gs, + typeVariable("TPSub", List.of(_personSymType)) + )) + )); + // TSSub extends Student + // TSSub tSSub; + inScope(gs, variable("tSSub", + createTypeVariable(inScope(gs, + typeVariable("TSSub", List.of(_studentSymType)) + )) + )); + + checkExpr("tPSub.getAge()", "int"); + checkExpr("tSSub.getAge()", "int"); + } + + @Test + public void deriveFromCallExpressionUnion() throws IOException { + IBasicSymbolsGlobalScope gs = BasicSymbolsMill.globalScope(); + // add Person::getAge + inScope(_personSymType.getTypeInfo().getSpannedScope(), + function("getAge", _intSymType) + ); + // (Student | Child) teachablePerson; + inScope(gs, variable("teachablePerson", + createUnion(_studentSymType, _childSymType) + )); + + checkExpr("teachablePerson.getAge()", "int"); + } + + @Test + public void testInvalidCallExpressionUnion() throws IOException { + IBasicSymbolsGlobalScope gs = BasicSymbolsMill.globalScope(); + // add Person::getAge + inScope(_personSymType.getTypeInfo().getSpannedScope(), + function("getAge", _intSymType) + ); + // (Person | int) maybePerson; + inScope(gs, variable("maybePerson", + createUnion(_personSymType, _intSymType) + )); + + checkErrorExpr("maybePerson.getAge()", "0xF737F"); + } + + @Test + public void deriveFromCallExpressionIntersection() throws IOException { + IBasicSymbolsGlobalScope gs = BasicSymbolsMill.globalScope(); + // add Person::getAge + inScope(_personSymType.getTypeInfo().getSpannedScope(), + function("getAge", _intSymType) + ); + // (Person & Car) talkingCar; + inScope(gs, variable("talkingCar", + createIntersection(_personSymType, _carSymType) + )); + checkExpr("talkingCar.getAge()", "int"); + } + + @Test + public void testInvalidCallExpression() throws IOException { + //method isNot() is not in scope -> method cannot be resolved -> method has no return type + init_advanced(); + checkErrorExpr("isNot()", "0xFD118"); + } + + @Test + public void testInvalidCallExpressionWithMissingNameAndNotComposedOfCallback() + throws IOException { + // Expression (2 + 3)() and all other Expressions in front of brackets are parsable + init_advanced(); + checkErrorExpr("(2 + 3)()", "0xFDABC"); + } + + @Test + public void testInvalidCallExpressionWithInvalidQualifiedName() throws IOException { + //method isInt() is not in the specified scope -> method cannot be resolved + init_advanced(); + checkErrorExpr("notAScope.isInt()", "0xF735F"); + } + + @Test + public void testInvalidCallExpressionWithFunctionChaining() throws IOException { + //function isNot() is part of the return type of getIsInt() -> function cannot be resolved + init_advanced(); + checkErrorExpr("getIsInt.isNot()", "0xFDB3A"); + } + + @Test + public void testInvalidCallExpressionWithInvalidArgument() throws IOException { + init_advanced(); + checkErrorExpr("isInt(\"foo\" / 2)", "0xB0163"); + } + + @Test + public void testRegularAssignmentWithTwoMissingFields() throws IOException { + checkErrorExpr("missingField = missingField2", "0xFD118"); + } + + @Test + public void testMissingMethodWithMissingArgs() throws IOException { + checkErrorExpr("missingMethod(missing1, missing2)", "0xFD118"); + } + + /** + * initialize the symbol table for a basic inheritance example + * we only have one scope and the symbols are all in this scope or in subscopes + */ + public void init_inheritance() { + //inheritance example + IOOSymbolsGlobalScope globalScope = OOSymbolsMill.globalScope(); + //super + OOTypeSymbol aList = inScope(globalScope, + oOtype("AList", + Collections.emptyList(), + Collections.emptyList(), + List.of(method("add", _voidSymType, + List.of(_boxedString))), + List.of(field("name", _boxedString)) + ) + ); + + //sub + OOTypeSymbol myList = inScope(globalScope, + oOtype("MyList", List.of(createTypeObject(aList))) + ); + inScope(globalScope, field("myList", createTypeObject(myList))); + + //subsub + OOTypeSymbol mySubList = inScope(globalScope, + oOtype("MySubList", List.of(createTypeObject(myList))) + ); + inScope(globalScope, field("mySubList", createTypeObject(myList))); + } + + /** + * test if the methods and fields of superclasses can be used by subclasses + */ + @Test + public void testInheritance() throws IOException { + //initialize symbol table + init_inheritance(); + + //methods + //test normal inheritance + checkExpr("myList.add(\"Hello\")", "void"); + + //test inheritance over two levels + checkExpr("mySubList.add(\"World\")", "void"); + + //fields + checkExpr("myList.name", "java.lang.String"); + + checkExpr("mySubList.name", "java.lang.String"); + } + + /** + * test the inheritance of a generic type with one type variable + */ + @Test + public void testListAndArrayListInheritance() throws IOException { + //one generic parameter, supertype List + IBasicSymbolsScope listScope = + _boxedListSymType.getTypeInfo().getSpannedScope(); + TypeVarSymbol listTVar = listScope.getLocalTypeVarSymbols().get(0); + listScope.add(function("add", _booleanSymType, + List.of(createTypeVariable(listTVar)) + )); + listScope.add(variable("next", createTypeVariable(listTVar))); + + //test methods and fields of the supertype + checkExpr("intList.add(2)", "boolean"); + + checkExpr("intList.next", "int"); + + //test inherited methods and fields of the subtype + checkExpr("intLinkedList.add(3)", "boolean"); + + checkExpr("intLinkedList.next", "int"); + } + + /** + * test the inheritance of generic types with two type variables + */ + @Test + public void testGenericInheritanceTwoTypeVariables() throws IOException { + // two generic parameters, supertype GenSup, + // create SymType GenSup + IOOSymbolsGlobalScope gs = OOSymbolsMill.globalScope(); + TypeVarSymbol tvS = typeVariable("S"); + TypeVarSymbol tvV = typeVariable("V"); + TypeSymbol genSup = inScope(gs, + oOtype("GenSup", + Collections.emptyList(), + List.of(tvS, tvV), + List.of(method("load", + createTypeVariable(tvS), + createTypeVariable(tvV)) + ), + List.of( + field("f1", createTypeVariable(tvS)), + field("f2", createTypeVariable(tvV)) + ) + ) + ); + SymTypeExpression genSupType = + createGenerics(genSup, _boxedString, _intSymType); + inScope(gs, field("genSupVar", genSupType)); + + // two generic parameters, subtype GenSub, + // create SymType GenSub + // same name of variables on purpose + TypeVarSymbol tvS2 = typeVariable("S"); + TypeVarSymbol tvV2 = typeVariable("V"); + OOTypeSymbol genSub = inScope(gs, + oOtype("GenSub", + List.of(createGenerics(genSup, + createTypeVariable(tvS2), createTypeVariable(tvV2))), + List.of(tvS2, tvV2), + // override f1 + Collections.emptyList(), + List.of(field("f1", createTypeVariable(tvS2))) + ) + ); + SymTypeExpression genSubType = + createGenerics(genSub, _boxedString, _intSymType); + inScope(gs, field("genSubVar", genSubType)); + + //two generic parameters, subsubtype GenSubSub, create GenSubSub + TypeVarSymbol tvS3 = typeVariable("S"); + TypeVarSymbol tvV3 = typeVariable("V"); + OOTypeSymbol genSubSub = inScope(gs, + oOtype("GenSubSub", + List.of(createGenerics(genSub, + createTypeVariable(tvS3), createTypeVariable(tvV3))), + List.of(tvV3, tvS3) + ) + ); + SymTypeExpression genSubSubType = + createGenerics(genSubSub, _boxedString, _intSymType); + inScope(gs, field("genSubSubVar", genSubSubType)); + + //supertype: test methods and fields + checkExpr("genSupVar.load(3)", "java.lang.String"); + + checkExpr("genSupVar.f1", "java.lang.String"); + + checkExpr("genSupVar.f2", "int"); + + //subtype: test inherited methods and fields + checkExpr("genSubVar.load(3)", "java.lang.String"); + + checkExpr("genSubVar.f1", "java.lang.String"); + + checkExpr("genSubVar.f2", "int"); + + //subsubtype: test inherited methods and fields + checkExpr("genSubSubVar.load(\"Hello\")", "int"); + + checkExpr("genSubSubVar.f1", "int"); + + checkExpr("genSubSubVar.f2", "java.lang.String"); + } + + /** + * test if methods and a field from a fixed subtype(generic type, but instead of type variable concrete type) + * are inherited correctly + */ + @Test + public void testSubVarSupFix() throws IOException { + //subtype with variable generic parameter, supertype with fixed generic parameter + //supertype with fixed generic parameter FixGen and SymType FixGen + IOOSymbolsGlobalScope gs = OOSymbolsMill.globalScope(); + + TypeVarSymbol tvA = typeVariable("A"); + OOTypeSymbol fixGen = inScope(gs, + oOtype("FixGen", + Collections.emptyList(), + List.of(tvA), + List.of(method("add", _booleanSymType, createTypeVariable(tvA))), + List.of(field("next", createTypeVariable(tvA))) + ) + ); + SymTypeExpression fixGenType = createGenerics(fixGen, _intSymType); + inScope(gs, field("fixGenVar", fixGenType)); + + // subtype with variable generic parameter VarGen + // which extends FixGen, + // SymType VarGen + TypeVarSymbol tvN = typeVariable("N"); + OOTypeSymbol varGen = inScope(gs, + oOtype("VarGen", + List.of(fixGenType), + List.of(tvN), + List.of(method("calculate", createTypeVariable(tvN))), + Collections.emptyList() + ) + ); + SymTypeExpression varGenType = createGenerics(varGen, _boxedString); + inScope(gs, field("varGen", varGenType)); + + //test own methods first + checkExpr("varGen.calculate()", "java.lang.String"); + + //test inherited methods and fields + checkExpr("varGen.add(4)", "boolean"); + + checkExpr("varGen.next", "int"); + } + + /** + * Test-Case: SubType has more generic parameters than its supertype + */ + @Test + public void testSubTypeWithMoreGenericParameters() throws IOException { + IOOSymbolsGlobalScope gs = OOSymbolsMill.globalScope(); + //one generic parameter, supertype List + TypeSymbol list = _unboxedListSymType.getTypeInfo(); + TypeVarSymbol tvT = list.getTypeParameterList().get(0); + inScope(list.getSpannedScope(), + function("add", _booleanSymType, createTypeVariable(tvT)) + ); + inScope(list.getSpannedScope(), variable("next", createTypeVariable(tvT))); + + //two generic parameters, subtype MoreGen + TypeVarSymbol tvT2 = typeVariable("T"); + TypeVarSymbol tvF = typeVariable("F"); + TypeSymbol moreGenType = inScope(gs, + type("MoreGen", + List.of(createGenerics(list, createTypeVariable(tvT2))), + List.of(tvT2, tvF), + List.of(function("insert", createTypeVariable(tvT2), createTypeVariable(tvF))), + Collections.emptyList() + ) + ); + VariableSymbol moreGen = variable("moreGen", + createGenerics(moreGenType, _intSymType, _longSymType) + ); + inScope(gs, moreGen); + + //test own method + checkExpr("moreGen.insert(12L)", "int"); + + //test inherited methods and fields + checkExpr("moreGen.add(12)", "boolean"); + + checkExpr("moreGen.next", "int"); + } + + /** + * Test-Case: SubType is a normal object type and extends a fixed generic type + */ + @Test + public void testSubTypeWithoutGenericParameter() throws IOException { + IOOSymbolsGlobalScope gs = OOSymbolsMill.globalScope(); + //one generic parameter, supertype List + TypeSymbol list = _unboxedListSymType.getTypeInfo(); + TypeVarSymbol tvT = list.getTypeParameterList().get(0); + inScope(list.getSpannedScope(), + function("add", _booleanSymType, createTypeVariable(tvT)) + ); + inScope(list.getSpannedScope(), variable("next", createTypeVariable(tvT))); + + //subtype without generic parameter NotGen extends List + OOTypeSymbol notGeneric = inScope(gs, + oOtype("NotGen", + List.of(createGenerics(list, _intSymType)) + ) + ); + inScope(gs, field("notGen", createTypeObject(notGeneric))); + + //test inherited methods and fields + checkExpr("notGen.add(14)", "boolean"); + + checkExpr("notGen.next", "int"); + } + + /** + * Test-Case: Multi-Inheritance 1, test if the methods and fields are inherited correctly + * every type in the example has exactly one type variable + */ + @Test + public void testMultiInheritance() throws IOException { + IOOSymbolsGlobalScope gs = OOSymbolsMill.globalScope(); + //supertype SupA + TypeVarSymbol tvSupA = typeVariable("T"); + OOTypeSymbol supAType = inScope(gs, + oOtype("SupA", + Collections.emptyList(), + List.of(tvSupA), + List.of(method("testA", createTypeVariable(tvSupA))), + List.of(field("currentA", createTypeVariable(tvSupA))) + ) + ); + + //supertype SupB + TypeVarSymbol tvSupB = typeVariable("T"); + OOTypeSymbol supBType = inScope(gs, + oOtype("SupB", + Collections.emptyList(), + List.of(tvSupB), + List.of(method("testB", createTypeVariable(tvSupB))), + List.of(field("currentB", createTypeVariable(tvSupB))) + ) + ); + + //subType SubA + TypeVarSymbol tvSubA = typeVariable("T"); + OOTypeSymbol subAType = inScope(gs, + oOtype("SubA", + List.of(createGenerics(supAType, createTypeVariable(tvSubA)), + createGenerics(supBType, createTypeVariable(tvSubA))), + List.of(tvSubA) + ) + ); + inScope(gs, field("sub", createGenerics(subAType, _charSymType))); + + checkExpr("sub.testA()", "char"); + + checkExpr("sub.currentA", "char"); + + checkExpr("sub.testB()", "char"); + + checkExpr("sub.currentB", "char"); + } + + /** + * Test-Case: Multi-Inheritance 1, test if the methods and fields are inherited correctly + * the supertypes have one type variable and the subtype has two type variables + */ + @Test + public void testMultiInheritanceSubTypeMoreGen() throws IOException { + IOOSymbolsGlobalScope gs = OOSymbolsMill.globalScope(); + //supertype SupA + TypeVarSymbol tvSupA = typeVariable("T"); + OOTypeSymbol supAType = inScope(gs, + oOtype("SupA", + Collections.emptyList(), + List.of(tvSupA), + List.of(method("testA", createTypeVariable(tvSupA))), + List.of(field("currentA", createTypeVariable(tvSupA))) + ) + ); + + //supertype SupB + TypeVarSymbol tvSupB = typeVariable("T"); + OOTypeSymbol supBType = inScope(gs, + oOtype("SupB", + Collections.emptyList(), + List.of(tvSupB), + List.of(method("testB", createTypeVariable(tvSupB))), + List.of(field("currentB", createTypeVariable(tvSupB))) + ) + ); + + //subType SubA + TypeVarSymbol tvSubAT = typeVariable("T"); + TypeVarSymbol tvSubAV = typeVariable("V"); + OOTypeSymbol subAType = inScope(gs, + oOtype("SubA", + List.of(createGenerics(supAType, createTypeVariable(tvSubAT)), + createGenerics(supBType, createTypeVariable(tvSubAV))), + List.of(tvSubAT, tvSubAV) + ) + ); + inScope(gs, field("sub", + createGenerics(subAType, _booleanSymType, _charSymType) + )); + + checkExpr("sub.testA()", "boolean"); + + checkExpr("sub.currentA", "boolean"); + + checkExpr("sub.testB()", "char"); + + checkExpr("sub.currentB", "char"); + } + + /** + * test if you can use methods, types and fields of the type or its supertypes in its method scopes + */ + @Test + public void testMethodScope() throws IOException { + IOOSymbolsGlobalScope gs = OOSymbolsMill.globalScope(); + //one generic parameter, supertype List + TypeSymbol list = _boxedListSymType.getTypeInfo(); + TypeVarSymbol tvT = list.getTypeParameterList().get(0); + inScope(list.getSpannedScope(), + function("add", _voidSymType, createTypeVariable(tvT)) + ); + inScope(list.getSpannedScope(), field("name", _unboxedString)); + + //sub + TypeSymbol linkedList = _linkedListSymType.getTypeInfo(); + TypeVarSymbol tvT2 = linkedList.getTypeParameterList().get(0); + inScope(linkedList.getSpannedScope(), field("next", createTypeVariable(tvT2))); + + //subsub + TypeVarSymbol tvV = typeVariable("V"); + FunctionSymbol myAdd = function("myAdd", _voidSymType); + VariableSymbol myAddParameter = variable("parameter", createTypeVariable(tvV)); + myAdd.getParameterList().add(myAddParameter); + inScope(myAdd.getSpannedScope(), myAddParameter); + TypeSymbol mySubListType = inScope(gs, + type("MySubList", + List.of(createGenerics(linkedList, createTypeVariable(tvV))), + List.of(tvV), + List.of(myAdd), + List.of(field("myName", _unboxedString)) + ) + ); + mySubListType.getFullName(); + + // all expressions are to be in the myAdd() scope, + // we do not have subscopes in these expressions + ExpressionsBasisTraverser scopeSetter = + ExpressionsBasisMill.inheritanceTraverser(); + scopeSetter.add4ExpressionsBasis( + new ExpressionsBasisVisitor2() { + @Override + public void visit(ASTExpression node) { + node.setEnclosingScope(myAdd.getSpannedScope()); + } + } + ); + + // we calculate subexpressions within the myAdd method + ASTExpression astexpr = parseExpr("myAdd(null)"); + generateScopes(astexpr); + astexpr.accept(scopeSetter); + calculateTypes(astexpr); + SymTypeExpression type = getType4Ast().getTypeOfExpression(astexpr); + assertNoFindings(); + assertEquals("void", type.printFullName()); + + astexpr = parseExpr("myName"); + generateScopes(astexpr); + astexpr.accept(scopeSetter); + calculateTypes(astexpr); + type = getType4Ast().getTypeOfExpression(astexpr); + assertNoFindings(); + assertEquals("String", type.printFullName()); + + astexpr = parseExpr("next"); + generateScopes(astexpr); + astexpr.accept(scopeSetter); + calculateTypes(astexpr); + type = getType4Ast().getTypeOfExpression(astexpr); + assertNoFindings(); + assertEquals("MySubList.V", type.printFullName()); + + astexpr = parseExpr("name"); + generateScopes(astexpr); + astexpr.accept(scopeSetter); + calculateTypes(astexpr); + type = getType4Ast().getTypeOfExpression(astexpr); + assertNoFindings(); + assertEquals("String", type.printFullName()); + + astexpr = parseExpr("parameter"); + generateScopes(astexpr); + astexpr.accept(scopeSetter); + calculateTypes(astexpr); + type = getType4Ast().getTypeOfExpression(astexpr); + assertNoFindings(); + assertEquals("MySubList.V", type.printFullName()); + + astexpr = parseExpr("add(parameter)"); + generateScopes(astexpr); + astexpr.accept(scopeSetter); + calculateTypes(astexpr); + type = getType4Ast().getTypeOfExpression(astexpr); + assertNoFindings(); + assertEquals("void", type.printFullName()); + } + + public void init_static_example() { + IOOSymbolsGlobalScope gs = OOSymbolsMill.globalScope(); + //types A and B + MethodSymbol atest = method("test", _voidSymType); + atest.setIsStatic(true); + FieldSymbol afield = field("field", _intSymType); + afield.setIsStatic(true); + OOTypeSymbol a = inScope(gs, + oOtype("A", + Collections.emptyList(), + Collections.emptyList(), + List.of(atest), + List.of(afield) + ) + ); + //A has static inner type D + FieldSymbol aDX = field("x", _intSymType); + aDX.setIsStatic(true); + OOTypeSymbol aD = inScope(a.getSpannedScope(), oOtype("D", + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyList(), + List.of(aDX) + )); + aD.setIsStatic(true); + + MethodSymbol btest = method("test", _voidSymType); + FieldSymbol bfield = field("field", _intSymType); + OOTypeSymbol b = inScope(gs, + oOtype("B", + Collections.emptyList(), + Collections.emptyList(), + List.of(btest), + List.of(bfield) + ) + ); + //B has non-static inner type D + FieldSymbol bDX = field("x", _intSymType); + OOTypeSymbol bD = inScope(b.getSpannedScope(), oOtype("D", + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyList(), + List.of(bDX) + )); + + //A has static method test, static field field, static type D + //B has normal method test, normal field field, normal type D + //type C extends A and has no method, field or type + inScope(gs, oOtype("C", List.of(createTypeObject(a)))); + } + + @Test + public void testStaticType() throws IOException { + init_static_example(); + + checkType("A.D", "A.D"); + checkType("B.D", "B.D"); + } + + @Test + public void testInvalidStaticType() throws IOException { + init_static_example(); + + checkErrorMCType("A.NotAType", "0xA0324"); + } + + @Test + public void testStaticField() throws IOException { + init_static_example(); + + checkExpr("A.field", "int"); + } + + @Test + public void testInvalidStaticField() throws IOException { + init_static_example(); + + checkErrorExpr("B.field", "0xF736F"); + } + + @Test + public void testStaticMethod() throws IOException { + init_static_example(); + + checkExpr("A.test()", "void"); + } + + @Test + public void testInvalidStaticMethod() throws IOException { + init_static_example(); + + checkErrorExpr("B.test()", "0xF736F"); + } + + @Test + public void testMissingTypeQualified() throws IOException { + checkErrorMCType("pac.kage.not.present.Type", "0xA0324"); + } + + @Test + public void testMissingFieldQualified() throws IOException { + init_static_example(); + + checkErrorExpr("B.notPresentField", "0xF736F"); + } + + @Test + public void testMissingFieldUnqualified() throws IOException { + checkErrorExpr("notPresentField", "0xFD118"); + } + + @Test + public void testMissingMethodQualified() throws IOException { + checkErrorExpr("pac.kage.not.present.Type.method()", "0xF735F"); + } + + @Test + public void testSubClassesKnowsStaticMethodsOfSuperClasses() throws IOException { + init_static_example(); + + checkExpr("C.test()", "void"); + } + + @Test + public void testSubClassesKnowsStaticFieldsOfSuperClasses() throws IOException { + init_static_example(); + + checkExpr("C.field", "int"); + } + + @Test + public void testSubClassesKnowsStaticTypesOfSuperClasses() throws IOException { + init_static_example(); + + checkType("C.D", "A.D"); + checkExpr("C.D.x", "int"); + } + + /** + * test if we can use functions and variables + * as e.g. imported by Class2MC + */ + @Test + public void testDoNotFilterBasicTypes() throws IOException { + IOOSymbolsGlobalScope gs = OOSymbolsMill.globalScope(); + TypeSymbol A = inScope(gs, + type("A", + Collections.emptyList(), + Collections.emptyList(), + List.of(function("func", _voidSymType)), + List.of(variable("var", _booleanSymType)) + ) + ); + inScope(gs, variable("a", createTypeObject(A))); + + // todo copied from typecheck 1 tests, but to be discussed + // functions are available as if they were static + checkExpr("A.func()", "void"); + //checkExpr("a.func()", "void"); + + // variables are available as if they were non-static + //checkErrorExpr("A.var", "0xA0241"); + //checkExpr("a.var", "boolean"); + } + + protected void init_method_test() { + //see MC Ticket #3298 for this example, use test instead of bar because bar is a keyword in CombineExpressions + //create types A, B and C, B extends A, C extends B + IOOSymbolsGlobalScope globalScope = OOSymbolsMill.globalScope(); + TypeSymbol a = inScope(globalScope, type("A")); + SymTypeExpression aSym = createTypeObject(a); + TypeSymbol b = inScope(globalScope, type("B", Lists.newArrayList(aSym))); + SymTypeExpression bSym = createTypeObject(b); + TypeSymbol c = inScope(globalScope, type("C", Lists.newArrayList(bSym))); + SymTypeExpression cSym = createTypeObject(c); + + inScope(globalScope, method("foo", aSym, aSym)); + inScope(globalScope, method("foo", bSym, bSym)); + inScope(globalScope, method("foo", cSym, cSym)); + + inScope(globalScope, method("foo", aSym, aSym, aSym)); + inScope(globalScope, method("foo", bSym, aSym, bSym)); + inScope(globalScope, method("foo", cSym, aSym, cSym)); + + inScope(globalScope, method("foo2", aSym, aSym, bSym, cSym)); + inScope(globalScope, method("foo2", aSym, cSym, cSym, aSym)); + + inScope(globalScope, method("foo2", bSym, aSym, bSym)); + inScope(globalScope, method("foo2", cSym, cSym, aSym)); + + inScope(globalScope, field("a", aSym)); + inScope(globalScope, field("b", bSym)); + inScope(globalScope, field("c", cSym)); + } + + @Test + public void testCorrectMethodChosen() throws IOException { + init_method_test(); + + /* + available methods: + A foo(A x) + B foo(B x) + C foo(C x) + A foo(A x, A y) + B foo(A x, B y) + C foo(A x, C y) + + A foo2(A x, B y, C z) + C foo2(C x, C y, A z) + B foo2(A x, B y) + C foo2(C x, A y) + */ + + checkExpr("foo(a)", "A"); + checkExpr("foo(b)", "B"); + checkExpr("foo(c)", "C"); + + checkExpr("foo(a, a)", "A"); + checkExpr("foo(a, b)", "B"); + checkExpr("foo(a, c)", "C"); + checkExpr("foo(b, a)", "A"); + checkExpr("foo(b, b)", "B"); + checkExpr("foo(b, c)", "C"); + checkExpr("foo(c, a)", "A"); + checkExpr("foo(c, b)", "B"); + checkExpr("foo(c, c)", "C"); + + checkErrorExpr("foo2(c, c, c)", "0xFDCBA"); + + checkErrorExpr("foo2(a, a)", "0xFDABE"); + checkErrorExpr("foo2(b, a)", "0xFDABE"); + checkErrorExpr("foo2(c, b)", "0xFDCBA"); + checkErrorExpr("foo2(c, c)", "0xFDCBA"); + + checkExpr("foo2(a, b)", "B"); + checkExpr("foo2(a, c)", "B"); + checkExpr("foo2(b, b)", "B"); + checkExpr("foo2(b, c)", "B"); + checkExpr("foo2(c, a)", "C"); + } + + @Test + public void testCorrectMethodChosenPrimitives() throws IOException { + IBasicSymbolsGlobalScope gs = BasicSymbolsMill.globalScope(); + // primitive id + inScope(gs, function("pid", _charSymType, _charSymType)); + inScope(gs, function("pid", _byteSymType, _byteSymType)); + inScope(gs, function("pid", _shortSymType, _shortSymType)); + inScope(gs, function("pid", _intSymType, _intSymType)); + inScope(gs, function("pid", _longSymType, _longSymType)); + inScope(gs, function("pid", _floatSymType, _floatSymType)); + inScope(gs, function("pid", _doubleSymType, _doubleSymType)); + inScope(gs, function("pid", _booleanSymType, _booleanSymType)); + + // based on String::valueOf + // byte and short get converted to int + inScope(gs, function("pid2", _charSymType, _charSymType)); + inScope(gs, function("pid2", _intSymType, _intSymType)); + inScope(gs, function("pid2", _longSymType, _longSymType)); + inScope(gs, function("pid2", _floatSymType, _floatSymType)); + inScope(gs, function("pid2", _doubleSymType, _doubleSymType)); + inScope(gs, function("pid2", _booleanSymType, _booleanSymType)); + + checkExpr("pid(varchar)", "char"); + checkExpr("pid(varbyte)", "byte"); + checkExpr("pid(varshort)", "short"); + checkExpr("pid(varint)", "int"); + checkExpr("pid(varlong)", "long"); + checkExpr("pid(varfloat)", "float"); + checkExpr("pid(vardouble)", "double"); + checkExpr("pid(varboolean)", "boolean"); + + checkExpr("pid2(varchar)", "char"); + checkExpr("pid2(varbyte)", "int"); + checkExpr("pid2(varshort)", "int"); + checkExpr("pid2(varint)", "int"); + checkExpr("pid2(varlong)", "long"); + checkExpr("pid2(varfloat)", "float"); + checkExpr("pid2(vardouble)", "double"); + checkExpr("pid2(varboolean)", "boolean"); + } + + @Test + public void testCorrectMethodChosenStringValueOf() throws IOException { + // explicitly tries to recreate String::valueOf + IBasicSymbolsGlobalScope gs = BasicSymbolsMill.globalScope(); + // Object being the superType of reference types, + SymTypeExpression objectSymType = + createTypeObject(inScope(gs, type("Object"))); + _CharacterSymType.getTypeInfo().setSuperTypesList(List.of(objectSymType)); + _ByteSymType.getTypeInfo().setSuperTypesList(List.of(objectSymType)); + _ShortSymType.getTypeInfo().setSuperTypesList(List.of(objectSymType)); + _IntegerSymType.getTypeInfo().setSuperTypesList(List.of(objectSymType)); + _LongSymType.getTypeInfo().setSuperTypesList(List.of(objectSymType)); + _FloatSymType.getTypeInfo().setSuperTypesList(List.of(objectSymType)); + _DoubleSymType.getTypeInfo().setSuperTypesList(List.of(objectSymType)); + _IntegerSymType.getTypeInfo().setSuperTypesList(List.of(objectSymType)); + + inScope(gs, function("valueOf", _boxedString, objectSymType)); + inScope(gs, function("valueOf", _boxedString, _charSymType)); + inScope(gs, function("valueOf", _boxedString, _intSymType)); + inScope(gs, function("valueOf", _boxedString, _longSymType)); + inScope(gs, function("valueOf", _boxedString, _floatSymType)); + inScope(gs, function("valueOf", _boxedString, _doubleSymType)); + inScope(gs, function("valueOf", _boxedString, _booleanSymType)); + // presumably not necessary, but for good measure: + inScope(gs, function("valueOf", _boxedString, + createTypeArray(_charSymType, 1))); + inScope(gs, function("valueOf", _boxedString, + createTypeArray(_charSymType, 1), _intSymType, _intSymType)); + + checkExpr("valueOf(varchar)", "java.lang.String"); + checkExpr("valueOf(varbyte)", "java.lang.String"); + checkExpr("valueOf(varshort)", "java.lang.String"); + checkExpr("valueOf(varint)", "java.lang.String"); + checkExpr("valueOf(varlong)", "java.lang.String"); + checkExpr("valueOf(varfloat)", "java.lang.String"); + checkExpr("valueOf(vardouble)", "java.lang.String"); + checkExpr("valueOf(varboolean)", "java.lang.String"); + + checkExpr("valueOf(varCharacter)", "java.lang.String"); + checkExpr("valueOf(varByte)", "java.lang.String"); + checkExpr("valueOf(varShort)", "java.lang.String"); + checkExpr("valueOf(varInteger)", "java.lang.String"); + checkExpr("valueOf(varLong)", "java.lang.String"); + checkExpr("valueOf(varFloat)", "java.lang.String"); + checkExpr("valueOf(varDouble)", "java.lang.String"); + checkExpr("valueOf(varBoolean)", "java.lang.String"); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types3/CommonLiteralsTypeVisitorTest.java b/monticore-grammar/src/test/java/de/monticore/types3/CommonLiteralsTypeVisitorTest.java new file mode 100644 index 0000000000..a14ca0939a --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types3/CommonLiteralsTypeVisitorTest.java @@ -0,0 +1,144 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types3; + +import de.monticore.expressions.combineexpressionswithliterals.CombineExpressionsWithLiteralsMill; +import de.monticore.literals.mccommonliterals.MCCommonLiteralsMill; +import de.monticore.literals.mcliteralsbasis._ast.ASTLiteral; +import de.monticore.types3.util.DefsTypesForTests; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +public class CommonLiteralsTypeVisitorTest extends AbstractTypeVisitorTest { + + @Before + public void setupForEach() { + setupValues(); + } + + @Test + public void deriveTFromLiteral1() throws IOException { + checkExpr("17", "int"); + } + + @Test + public void deriveTFromLiteral2() throws IOException { + checkExpr("true", "boolean"); + } + + @Test + public void deriveTFromLiteral3() throws IOException { + checkExpr("17.3", "double"); + } + + @Test + public void deriveTFromLiteral1Null() { + ASTLiteral lit = MCCommonLiteralsMill.nullLiteralBuilder().build(); + check(lit, "null"); + } + + @Test + public void deriveTFromLiteral1Boolean() { + ASTLiteral lit = MCCommonLiteralsMill.booleanLiteralBuilder() + .setSource(0) + .build(); + check(lit, "boolean"); + } + + @Test + public void deriveTFromLiteral1Char() { + ASTLiteral lit = MCCommonLiteralsMill.charLiteralBuilder() + .setSource("c") + .build(); + check(lit, "char"); + } + + @Test + public void deriveTFromLiteral1String() { + ASTLiteral lit = MCCommonLiteralsMill.stringLiteralBuilder() + .setSource("Y05H1") + .build(); + check(lit, "String"); + } + + @Test + public void deriveTFromLiteralStringUnBoxedAvailable() { + // only String is available + MCCommonLiteralsMill.globalScope().clear(); + DefsTypesForTests.set_unboxedObjects(); + ASTLiteral lit = MCCommonLiteralsMill.stringLiteralBuilder() + .setSource("G0M84") + .build(); + check(lit, "String"); + } + + @Test + public void deriveTFromLiteralStringBoxedAvailable() { + // only java.util.String is available + MCCommonLiteralsMill.globalScope().clear(); + DefsTypesForTests.set_boxedObjects(); + ASTLiteral lit = MCCommonLiteralsMill.stringLiteralBuilder() + .setSource("W4210") + .build(); + check(lit, "java.lang.String"); + } + + @Test + public void deriveTFromLiteralStringUnavailable() { + // only java.util.String is available + MCCommonLiteralsMill.globalScope().clear(); + ASTLiteral lit = MCCommonLiteralsMill.stringLiteralBuilder() + .setSource("50N1C") + .build(); + lit.setEnclosingScope(CombineExpressionsWithLiteralsMill.globalScope()); + lit.accept(getTypeMapTraverser()); + assertFalse(getType4Ast().hasTypeOfExpression(lit)); + assertHasErrorCode("0xD02A6"); + } + + @Test + public void deriveTFromLiteral1Int() { + ASTLiteral lit = MCCommonLiteralsMill.natLiteralBuilder() + .setDigits("17") + .build(); + check(lit, "int"); + } + + @Test + public void deriveTFromLiteral1BasicLong() { + ASTLiteral lit = MCCommonLiteralsMill.basicLongLiteralBuilder() + .setDigits("17") + .build(); + check(lit, "long"); + } + + @Test + public void deriveTFromLiteral1BasicFloat() { + ASTLiteral lit = MCCommonLiteralsMill.basicFloatLiteralBuilder() + .setPre("10") + .setPost("03") + .build(); + check(lit, "float"); + } + + @Test + public void deriveTFromLiteral1BasicDouble() { + ASTLiteral lit = MCCommonLiteralsMill.basicDoubleLiteralBuilder() + .setPre("710") + .setPost("93") + .build(); + check(lit, "double"); + } + + protected void check(ASTLiteral lit, String expected) { + lit.setEnclosingScope(CombineExpressionsWithLiteralsMill.globalScope()); + lit.accept(getTypeMapTraverser()); + assertEquals(expected, getType4Ast().getTypeOfExpression(lit).printFullName()); + assertNoFindings(); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types3/ExpressionBasisTypeVisitorTest.java b/monticore-grammar/src/test/java/de/monticore/types3/ExpressionBasisTypeVisitorTest.java new file mode 100644 index 0000000000..0a2b722cb8 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types3/ExpressionBasisTypeVisitorTest.java @@ -0,0 +1,61 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types3; + +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; + +public class ExpressionBasisTypeVisitorTest extends AbstractTypeVisitorTest { + + @Before + public void setupForEach() { + setupValues(); + } + + @Test + public void deriveTFromASTNameExpression() throws IOException { + checkExpr("varint", "int"); + } + + @Test + public void deriveTFromASTNameExpression2() throws IOException { + checkExpr("varboolean", "boolean"); + } + + @Test + public void deriveTFromASTNameExpression3() throws IOException { + checkExpr("person1", "Person"); + } + + @Test + public void deriveTFromASTNameExpression4() throws IOException { + checkExpr("student1", "Student"); + } + + @Test + public void deriveTFromASTNameExpression5() throws IOException { + checkExpr("csStudent1", "CsStudent"); + } + + @Test + public void deriveTFromASTNameExpression6() throws IOException { + checkExpr("intList", "java.util.List"); + } + + @Test + public void deriveTFromASTNameExpression7() throws IOException { + checkExpr("intLinkedList", "LinkedList"); + } + + @Test + public void deriveTFromLiteralInt() throws IOException { + checkExpr("42", "int"); + } + + @Test + public void deriveTFromLiteralString() throws IOException { + checkExpr("\"aStringi\"", "String"); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types3/LambdaExpressionsTypeVisitorTest.java b/monticore-grammar/src/test/java/de/monticore/types3/LambdaExpressionsTypeVisitorTest.java new file mode 100644 index 0000000000..0e1af328c7 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types3/LambdaExpressionsTypeVisitorTest.java @@ -0,0 +1,45 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types3; + +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; + +public class LambdaExpressionsTypeVisitorTest extends AbstractTypeVisitorTest { + + @Before + public void setup() { + setupValues(); + } + + @Test + public void deriveFromLambdaExpressionNoParameterTest() throws IOException { + // example with int + checkExpr("() -> 5", "() -> int"); + // example with lambda nesting + checkExpr("() -> () -> 5", "() -> () -> int"); + } + + @Test + public void deriveFromLambdaExpressionOneParameterTest() throws IOException { + // example with int, long + checkExpr("(int x) -> 5L", "(int) -> long"); + // example with input equaling output + checkExpr("(int x) -> x", "(int) -> int"); + // example with lambda nesting + checkExpr("(int x) -> (int y) -> x + y", "(int) -> (int) -> int"); + } + + @Test + public void deriveFromLambdaExpressionMultipleParameterTest() throws IOException { + // example with int, long, int + checkExpr("(int x, long y) -> 5", "(int, long) -> int"); + // example with lambda nesting + checkExpr("(int x, long y) -> () -> 5", "(int, long) -> () -> int"); + // example with int, long, expression + checkExpr("(int x, long y) -> x + y", "(int, long) -> long"); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types3/MCArrayTypesTypeVisitorTest.java b/monticore-grammar/src/test/java/de/monticore/types3/MCArrayTypesTypeVisitorTest.java new file mode 100644 index 0000000000..f1d1b488ac --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types3/MCArrayTypesTypeVisitorTest.java @@ -0,0 +1,26 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types3; + +import org.junit.Test; + +import java.io.IOException; + +public class MCArrayTypesTypeVisitorTest + extends AbstractTypeVisitorTest { + + @Test + public void symTypeFromAST_ArrayTest() throws IOException { + checkTypeRoundTrip("int[][]"); + } + + @Test + public void symTypeFromAST_ArrayTest2() throws IOException { + checkTypeRoundTrip("Person[]"); + } + + @Test + public void symTypeFromAST_ArrayTest3() throws IOException { + checkErrorMCType("notAType[]", "0xE9CDC"); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types3/MCBasicTypesTypeVisitorTest.java b/monticore-grammar/src/test/java/de/monticore/types3/MCBasicTypesTypeVisitorTest.java new file mode 100644 index 0000000000..a9f832ead0 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types3/MCBasicTypesTypeVisitorTest.java @@ -0,0 +1,93 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types3; + +import de.monticore.types.mcbasictypes._ast.ASTMCReturnType; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.se_rwth.commons.logging.Log; +import org.junit.Test; + +import java.io.IOException; +import java.util.Optional; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; + +public class MCBasicTypesTypeVisitorTest + extends AbstractTypeVisitorTest { + + @Test + public void symTypeFromAST_Test() throws IOException { + checkTypeRoundTrip("double"); + } + + @Test + public void symTypeFromAST_Test2() throws IOException { + checkTypeRoundTrip("int"); + } + + @Test + public void symTypeFromAST_Test3() throws IOException { + checkErrorMCType("notAType", "0xA0324"); + } + + @Test + public void symTypeFromAST_Test4() throws IOException { + checkTypeRoundTrip("Person"); + } + + @Test + public void symTypeFromAST_Test5() throws IOException { + // tests with resolving in a sub scope + checkType("java.util.Map", + "java.util.Map"); + } + + @Test + public void symTypeFromAST_VoidTest() throws IOException { + Optional typeOpt = parser.parse_StringMCType("void"); + if (parser.hasErrors()) { + // OK + } + else { + // if it can be parsed, we expect an error + assertTrue(typeOpt.isPresent()); + generateScopes(typeOpt.get()); + typeOpt.get().accept(typeMapTraverser); + assertTrue(!Log.getFindings().isEmpty()); + assertNotEquals( + "void", + getType4Ast().getPartialTypeOfTypeId(typeOpt.get()).printFullName() + ); + } + } + + @Test + public void symTypeFromAST_ReturnTest() throws IOException { + Optional typeOpt = + parser.parse_StringMCReturnType("void"); + assertTrue(typeOpt.isPresent()); + typeOpt.get().accept(typeMapTraverser); + assertEquals( + "void", + getType4Ast().getPartialTypeOfTypeId(typeOpt.get()).printFullName() + ); + assertNoFindings(); + } + + @Test + public void symTypeFromAST_ReturnTest2() throws IOException { + Optional typeOpt = + parser.parse_StringMCReturnType("Person"); + assertTrue(typeOpt.isPresent()); + assertTrue(typeOpt.get().isPresentMCType()); + generateScopes(typeOpt.get().getMCType()); + typeOpt.get().accept(typeMapTraverser); + assertEquals( + "Person", + getType4Ast().getPartialTypeOfTypeId(typeOpt.get()).printFullName() + ); + assertNoFindings(); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types3/MCCollectionTypesTypeVisitorTest.java b/monticore-grammar/src/test/java/de/monticore/types3/MCCollectionTypesTypeVisitorTest.java new file mode 100644 index 0000000000..7cd6b63e09 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types3/MCCollectionTypesTypeVisitorTest.java @@ -0,0 +1,186 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types3; + +import de.monticore.expressions.combineexpressionswithliterals.CombineExpressionsWithLiteralsMill; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.ICombineExpressionsWithLiteralsArtifactScope; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.ICombineExpressionsWithLiteralsGlobalScope; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbolSurrogate; +import de.monticore.symbols.oosymbols._symboltable.OOTypeSymbolSurrogate; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeOfGenerics; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.mccollectiontypes._ast.ASTMCListType; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; + +import static de.monticore.types3.util.DefsTypesForTests.inScope; +import static de.monticore.types3.util.DefsTypesForTests.type; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class MCCollectionTypesTypeVisitorTest + extends AbstractTypeVisitorTest { + + @Before + public void initFurtherTypes() { + ICombineExpressionsWithLiteralsGlobalScope gs = + CombineExpressionsWithLiteralsMill.globalScope(); + // add two further Person Types within different packages + ICombineExpressionsWithLiteralsArtifactScope azScope = + inScope(gs, CombineExpressionsWithLiteralsMill.artifactScope()); + azScope.setPackageName("a.z"); + inScope(azScope, type("Person")); + ICombineExpressionsWithLiteralsArtifactScope dexScope = + inScope(gs, CombineExpressionsWithLiteralsMill.artifactScope()); + dexScope.setPackageName("de.x"); + inScope(dexScope, type("Person")); + } + + @Test + public void symTypeFromAST_TestList1() throws IOException { + // Given + String s = "List"; + ASTMCListType asttype = parser.parse_StringMCListType(s).get(); + generateScopes(asttype); + + // When + asttype.accept(getTypeMapTraverser()); + SymTypeExpression result = getType4Ast().getPartialTypeOfTypeId(asttype); + + // Then + assertNoFindings(); + assertEquals(s, result.printFullName()); + assertTrue(result instanceof SymTypeOfGenerics); + assertFalse(result.getTypeInfo() instanceof TypeSymbolSurrogate); + assertFalse(result.getTypeInfo() instanceof OOTypeSymbolSurrogate); + assertFalse( + ((SymTypeOfGenerics) result).getArgument(0) + .getTypeInfo() instanceof TypeSymbolSurrogate + ); + assertFalse( + ((SymTypeOfGenerics) result).getArgument(0) + .getTypeInfo() instanceof OOTypeSymbolSurrogate + ); + + } + + @Test + public void symTypeFromAST_TestSet1() throws IOException { + // Given + String s = "Set"; + ASTMCType asttype = parser.parse_StringMCType(s).get(); + generateScopes(asttype); + + // When + asttype.accept(getTypeMapTraverser()); + SymTypeExpression result = getType4Ast().getPartialTypeOfTypeId(asttype); + + // Then + assertNoFindings(); + assertEquals(s, result.printFullName()); + assertTrue(result instanceof SymTypeOfGenerics); + assertFalse(result.getTypeInfo() instanceof TypeSymbolSurrogate); + assertFalse(result.getTypeInfo() instanceof OOTypeSymbolSurrogate); + assertFalse( + ((SymTypeOfGenerics) result).getArgument(0) + .getTypeInfo() instanceof TypeSymbolSurrogate + ); + assertFalse( + ((SymTypeOfGenerics) result).getArgument(0) + .getTypeInfo() instanceof OOTypeSymbolSurrogate + ); + } + + @Test + public void symTypeFromAST_TestMap1() throws IOException { + // Given + String s = "Map"; + ASTMCType asttype = parser.parse_StringMCType(s).get(); + generateScopes(asttype); + + // When + asttype.accept(getTypeMapTraverser()); + SymTypeExpression result = getType4Ast().getPartialTypeOfTypeId(asttype); + + // Then + assertNoFindings(); + assertEquals(s, result.printFullName()); + assertTrue(result instanceof SymTypeOfGenerics); + assertFalse(result.getTypeInfo() instanceof TypeSymbolSurrogate); + assertFalse(result.getTypeInfo() instanceof OOTypeSymbolSurrogate); + assertFalse( + ((SymTypeOfGenerics) result).getArgument(0) + .getTypeInfo() instanceof TypeSymbolSurrogate + ); + assertFalse( + ((SymTypeOfGenerics) result).getArgument(0) + .getTypeInfo() instanceof TypeSymbolSurrogate + ); + assertFalse( + ((SymTypeOfGenerics) result).getArgument(1) + .getTypeInfo() instanceof OOTypeSymbolSurrogate + ); + assertFalse( + ((SymTypeOfGenerics) result).getArgument(1) + .getTypeInfo() instanceof OOTypeSymbolSurrogate + ); + } + + @Test + public void symTypeFromAST_TestSet2() throws IOException { + // Given + String s = "Set"; + ASTMCType asttype = parser.parse_StringMCType(s).get(); + generateScopes(asttype); + + // When + asttype.accept(getTypeMapTraverser()); + SymTypeExpression result = getType4Ast().getPartialTypeOfTypeId(asttype); + + // Then + assertNoFindings(); + assertEquals(s, result.printFullName()); + assertTrue(result instanceof SymTypeOfGenerics); + assertFalse(result.getTypeInfo() instanceof TypeSymbolSurrogate); + assertFalse(result.getTypeInfo() instanceof OOTypeSymbolSurrogate); + assertFalse( + ((SymTypeOfGenerics) result).getArgument(0) + .getTypeInfo() instanceof TypeSymbolSurrogate + ); + assertFalse( + ((SymTypeOfGenerics) result).getArgument(0) + .getTypeInfo() instanceof OOTypeSymbolSurrogate + ); + } + + @Test + public void symTypeFromAST_TestOptional1() throws IOException { + // Given + String s = "Optional"; + ASTMCType asttype = parser.parse_StringMCType(s).get(); + generateScopes(asttype); + + // When + asttype.accept(getTypeMapTraverser()); + SymTypeExpression result = getType4Ast().getPartialTypeOfTypeId(asttype); + + // Then + assertNoFindings(); + assertEquals(s, result.printFullName()); + assertTrue(result instanceof SymTypeOfGenerics); + assertFalse(result.getTypeInfo() instanceof TypeSymbolSurrogate); + assertFalse(result.getTypeInfo() instanceof OOTypeSymbolSurrogate); + assertFalse( + ((SymTypeOfGenerics) result).getArgument(0) + .getTypeInfo() instanceof TypeSymbolSurrogate + ); + assertFalse( + ((SymTypeOfGenerics) result).getArgument(0) + .getTypeInfo() instanceof OOTypeSymbolSurrogate + ); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types3/MCFullGenericTypesTypeVisitorTest.java b/monticore-grammar/src/test/java/de/monticore/types3/MCFullGenericTypesTypeVisitorTest.java new file mode 100644 index 0000000000..9ed3ebfd77 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types3/MCFullGenericTypesTypeVisitorTest.java @@ -0,0 +1,16 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types3; + +import org.junit.Test; + +import java.io.IOException; + +public class MCFullGenericTypesTypeVisitorTest + extends AbstractTypeVisitorTest { + + @Test + public void symTypeFromAST_TestFullGeneric() throws IOException { + checkTypeRoundTrip("List"); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types3/MCFunctionTypesTypeVisitorTest.java b/monticore-grammar/src/test/java/de/monticore/types3/MCFunctionTypesTypeVisitorTest.java new file mode 100644 index 0000000000..969d092eec --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types3/MCFunctionTypesTypeVisitorTest.java @@ -0,0 +1,45 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types3; + +import org.junit.Test; + +import java.io.IOException; + +public class MCFunctionTypesTypeVisitorTest + extends AbstractTypeVisitorTest { + + @Test + public void symTypeFromAST_TestRunnable() throws IOException { + checkTypeRoundTrip("() -> void"); + } + + @Test + public void symTypeFromAST_TestSimpleFunction1() throws IOException { + checkTypeRoundTrip("(int) -> int"); + } + + @Test + public void symTypeFromAST_TestSimpleFunction2() throws IOException { + checkTypeRoundTrip("(long, int) -> int"); + } + + @Test + public void symTypeFromAST_TestEllipticFunction1() throws IOException { + checkTypeRoundTrip("(int...) -> int"); + } + + @Test + public void symTypeFromAST_TestEllipticFunction2() throws IOException { + checkTypeRoundTrip("(long, int...) -> void"); + } + + @Test + public void symTypeFromAST_TestHigherOrderFunction1() throws IOException { + checkTypeRoundTrip("((int) -> void) -> () -> int"); + } + + @Test + public void symTypeFromAST_TestHigherOrderEllipticFunction() throws IOException { + checkTypeRoundTrip("(int) -> (() -> (int, long...) -> int...) -> void"); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/types3/MCSimpleGenericTypesTypeVisitorTest.java b/monticore-grammar/src/test/java/de/monticore/types3/MCSimpleGenericTypesTypeVisitorTest.java new file mode 100644 index 0000000000..d8dead8c41 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types3/MCSimpleGenericTypesTypeVisitorTest.java @@ -0,0 +1,89 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types3; + +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.IBasicSymbolsGlobalScope; +import de.monticore.symbols.basicsymbols._symboltable.IBasicSymbolsScope; +import de.monticore.types3.util.DefsTypesForTests; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +public class MCSimpleGenericTypesTypeVisitorTest + extends AbstractTypeVisitorTest { + + @Before + public void initFurtherTypes() { + IBasicSymbolsGlobalScope gs = BasicSymbolsMill.globalScope(); + IBasicSymbolsScope javaScope = + DefsTypesForTests.inScope(gs, DefsTypesForTests.scope("java")); + IBasicSymbolsScope utilScope = + DefsTypesForTests.inScope(javaScope, DefsTypesForTests.scope("util")); + gs.add(DefsTypesForTests.type( + "Iterator", + Collections.emptyList(), + List.of(DefsTypesForTests.typeVariable("T"))) + ); + utilScope.add(DefsTypesForTests.type( + "Iterator", + Collections.emptyList(), + List.of(DefsTypesForTests.typeVariable("T")) + )); + gs.add(DefsTypesForTests.type( + "Collection", + Collections.emptyList(), + List.of(DefsTypesForTests.typeVariable("T"))) + ); + } + + @Test + public void symTypeFromAST_TestSimpleGeneric() throws IOException { + checkTypeRoundTrip("Iterator"); + } + + @Test + public void symTypeFromAST_TestSimpleGeneric2() throws IOException { + checkTypeRoundTrip("Iterator"); + } + + @Test + public void symTypeFromAST_TestSimpleGeneric3() throws IOException { + checkTypeRoundTrip("java.util.Iterator"); + } + + @Test + public void symTypeFromAST_TestSimpleGeneric4() throws IOException { + checkTypeRoundTrip("Collection"); + } + + @Test + public void symTypeFromAST_TestSimpleGeneric5() throws IOException { + checkTypeRoundTrip("java.util.Iterator"); + } + + @Test + public void symTypeFromAST_TestSimpleGeneric6() throws IOException { + checkTypeRoundTrip("Map,int>,int>"); + } + + @Test + public void symTypeFromAST_TestSimpleGeneric7() throws IOException { + checkTypeRoundTrip( + "java.util.Iterator<" + + "java.util.Iterator<" + + "java.util.Iterator<" + + "int" + + ">>>" + ); + } + + @Test + public void symTypeFromAST_TestSimpleGeneric8() throws IOException { + checkTypeRoundTrip( + "java.util.Iterator>>" + ); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/types3/ResolveWithinOOTypeTest.java b/monticore-grammar/src/test/java/de/monticore/types3/ResolveWithinOOTypeTest.java new file mode 100644 index 0000000000..fbd5f69d0a --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types3/ResolveWithinOOTypeTest.java @@ -0,0 +1,199 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types3; + +import de.monticore.expressions.combineexpressionswithliterals.CombineExpressionsWithLiteralsMill; +import de.monticore.expressions.expressionsbasis.ExpressionsBasisMill; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.expressionsbasis._visitor.ExpressionsBasisTraverser; +import de.monticore.expressions.expressionsbasis._visitor.ExpressionsBasisVisitor2; +import de.monticore.mcbasics._symboltable.IMCBasicsScope; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.FunctionSymbol; +import de.monticore.symbols.basicsymbols._symboltable.IBasicSymbolsGlobalScope; +import de.monticore.symbols.basicsymbols._symboltable.IBasicSymbolsScope; +import de.monticore.symbols.oosymbols._symboltable.MethodSymbol; +import de.monticore.symbols.oosymbols._symboltable.MethodSymbolTOP; +import de.monticore.symbols.oosymbols._symboltable.OOTypeSymbol; +import de.monticore.symboltable.modifiers.AccessModifier; +import de.monticore.symboltable.modifiers.BasicAccessModifier; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types.check.SymTypeOfFunction; +import de.monticore.types3.util.CombineExpressionsWithLiteralsTypeTraverserFactory; +import de.monticore.types3.util.OOWithinTypeBasicSymbolsResolver; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.List; +import java.util.stream.Collectors; + +import static de.monticore.types3.util.DefsTypesForTests._intSymType; +import static de.monticore.types3.util.DefsTypesForTests.inScope; +import static de.monticore.types3.util.DefsTypesForTests.method; +import static de.monticore.types3.util.DefsTypesForTests.oOtype; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +/** + * tests whether we can resolve correctly constructors within a type. + * It mostly tests {@link OOWithinTypeBasicSymbolsResolver} + */ +public class ResolveWithinOOTypeTest extends AbstractTypeVisitorTest { + + @Before + public void before() { + CombineExpressionsWithLiteralsMill.reset(); + CombineExpressionsWithLiteralsMill.init(); + // replace the typeMapTraverser with an OO-aware variant + typeMapTraverser = new CombineExpressionsWithLiteralsTypeTraverserFactory() + .createTraverserForOO(type4Ast); + } + + // class t { + // public int t() {} + // public t() {} + // public t(t) {} + // => test to resolve method here + // => test to resolve constructor + // } + @Test + public void test1() throws IOException { + IBasicSymbolsGlobalScope gs = BasicSymbolsMill.globalScope(); + + OOTypeSymbol oOType = oOtype("t"); + SymTypeExpression oOTypeSymType = + SymTypeExpressionFactory.createTypeObject(oOType); + inScope(gs, oOType); + + MethodSymbol method = method("t", _intSymType); + inScope(oOType.getSpannedScope(), method); + + MethodSymbol constructor = method("t", oOTypeSymType); + constructor.setIsConstructor(true); + inScope(oOType.getSpannedScope(), constructor); + + MethodSymbol constructor2 = method("t", oOTypeSymType, oOTypeSymType); + constructor2.setIsConstructor(true); + inScope(oOType.getSpannedScope(), constructor2); + + SymTypeExpression type = + calculateTypeWithinScope("t", oOType.getSpannedScope()); + assertEquals("() -> int", type.printFullName()); + assertSame(method, ((SymTypeOfFunction) type).getSymbol()); + + List constructors = calculateConstructorWithinScope( + oOType.getSpannedScope(), "t", BasicAccessModifier.PRIVATE + ); + assertEquals(2, constructors.size()); + assertTrue(constructors.contains(constructor)); + assertTrue(constructors.contains(constructor2)); + } + + // class t { + // private t() {} + // public t(t) {} + // public s() {} // some constructor with a different name + // // that should not be resolved + // => test to resolve constructor using different AccessModifiers + // } + @Test + public void test2() throws IOException { + IBasicSymbolsGlobalScope gs = BasicSymbolsMill.globalScope(); + + OOTypeSymbol oOType = oOtype("t"); + SymTypeExpression oOTypeSymType = + SymTypeExpressionFactory.createTypeObject(oOType); + inScope(gs, oOType); + + MethodSymbol constructor = method("t", oOTypeSymType); + constructor.setIsConstructor(true); + constructor.setIsPublic(false); + constructor.setIsPrivate(true); + inScope(oOType.getSpannedScope(), constructor); + + MethodSymbol constructor2 = method("t", oOTypeSymType, oOTypeSymType); + constructor2.setIsConstructor(true); + inScope(oOType.getSpannedScope(), constructor2); + + MethodSymbol constructor3 = method("s", oOTypeSymType); + constructor3.setIsConstructor(true); + inScope(oOType.getSpannedScope(), constructor3); + + List constructors = calculateConstructorWithinScope( + oOType.getSpannedScope(), "t", BasicAccessModifier.PRIVATE + ); + assertEquals(2, constructors.size()); + assertTrue(constructors.contains(constructor)); + assertTrue(constructors.contains(constructor2)); + + constructors = calculateConstructorWithinScope( + oOType.getSpannedScope(), "t", BasicAccessModifier.PUBLIC + ); + assertEquals(1, constructors.size()); + assertTrue(constructors.contains(constructor2)); + } + + // Helper + + /** + * calculates the type of the (simple) expression within the scope + * s.a. getExpressionScopeSetter + */ + SymTypeExpression calculateTypeWithinScope( + String exprStr, + IMCBasicsScope scope + ) throws IOException { + ASTExpression expr = parseExpr(exprStr); + generateScopes(expr); + expr.accept(getExpressionScopeSetter(scope)); + calculateTypes(expr); + SymTypeExpression type = getType4Ast().getTypeOfExpression(expr); + assertNoFindings(); + return type; + } + + /** + * resolves the construtors given the accessmodifier + */ + protected List calculateConstructorWithinScope( + IBasicSymbolsScope scope, + String name, + AccessModifier accessModifier + ) { + List functions = + new OOWithinTypeBasicSymbolsResolver().resolveConstructorLocally( + scope, name, accessModifier, c -> true + ); + assertNoFindings(); + assertTrue(functions.stream().allMatch(f -> f instanceof MethodSymbol)); + List constructors = functions.stream() + .map(f -> (MethodSymbol) f) + .collect(Collectors.toList()); + assertTrue(constructors.stream().allMatch(MethodSymbolTOP::isIsConstructor)); + return constructors; + } + + /** + * Sets every (sub-)expression to the given scope. + * This can be used to test the expression in specific contexts. + * This only works for expressions, in which no own scope is spanned, + * e.g., lambdas shall not be used with this + */ + protected ExpressionsBasisTraverser getExpressionScopeSetter( + IMCBasicsScope scope) { + ExpressionsBasisTraverser scopeSetter = + ExpressionsBasisMill.inheritanceTraverser(); + scopeSetter.add4ExpressionsBasis( + new ExpressionsBasisVisitor2() { + @Override + public void visit(ASTExpression node) { + node.setEnclosingScope(scope); + } + } + ); + return scopeSetter; + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types3/ResolveWithinTypeTest.java b/monticore-grammar/src/test/java/de/monticore/types3/ResolveWithinTypeTest.java new file mode 100644 index 0000000000..9e48b56396 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types3/ResolveWithinTypeTest.java @@ -0,0 +1,335 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types3; + +import de.monticore.expressions.combineexpressionswithliterals.CombineExpressionsWithLiteralsMill; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.ICombineExpressionsWithLiteralsGlobalScope; +import de.monticore.expressions.expressionsbasis.ExpressionsBasisMill; +import de.monticore.expressions.expressionsbasis._ast.ASTExpression; +import de.monticore.expressions.expressionsbasis._visitor.ExpressionsBasisTraverser; +import de.monticore.expressions.expressionsbasis._visitor.ExpressionsBasisVisitor2; +import de.monticore.mcbasics._symboltable.IMCBasicsScope; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.IBasicSymbolsGlobalScope; +import de.monticore.symbols.basicsymbols._symboltable.VariableSymbol; +import de.monticore.symbols.oosymbols._symboltable.FieldSymbol; +import de.monticore.symbols.oosymbols._symboltable.MethodSymbol; +import de.monticore.symbols.oosymbols._symboltable.OOTypeSymbol; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types.check.SymTypeOfIntersection; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.List; + +import static de.monticore.types3.util.DefsTypesForTests._booleanSymType; +import static de.monticore.types3.util.DefsTypesForTests.field; +import static de.monticore.types3.util.DefsTypesForTests.inScope; +import static de.monticore.types3.util.DefsTypesForTests.method; +import static de.monticore.types3.util.DefsTypesForTests.oOtype; +import static de.monticore.types3.util.DefsTypesForTests.typeVariable; +import static de.monticore.types3.util.DefsTypesForTests.variable; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +/** + * tests whether we can resolve correctly within a type. + * E.g., the inner type of a super class. + * It mostly tests {@link de.monticore.types3.util.WithinTypeBasicSymbolsResolver} + */ +public class ResolveWithinTypeTest extends AbstractTypeVisitorTest { + + @Before + public void before() { + CombineExpressionsWithLiteralsMill.reset(); + CombineExpressionsWithLiteralsMill.init(); + } + + // class t { + // t t; + // t t() { + // => test expression "t" in this method scope + // } + // => test expressions "t()" and "t" in this class scope + // } + @Test + public void test1() throws IOException { + IBasicSymbolsGlobalScope gs = BasicSymbolsMill.globalScope(); + + OOTypeSymbol oOType = oOtype("t"); + inScope(gs, oOType); + + FieldSymbol field = field("t", + SymTypeExpressionFactory.createTypeObject(oOType)); + inScope(oOType.getSpannedScope(), field); + + MethodSymbol method = method("t", + SymTypeExpressionFactory.createTypeObject(oOType)); + inScope(oOType.getSpannedScope(), method); + + SymTypeExpression type = + calculateTypeWithinScope("t", oOType.getSpannedScope()); + assertEquals("(() -> t & t)", type.printFullName()); + assertTrue(type.isIntersectionType()); + assertTrue(((SymTypeOfIntersection) type).getIntersectedTypeSet() + .stream() + .anyMatch(t -> t.hasTypeInfo() && t.getTypeInfo() == oOType)); + + type = calculateTypeWithinScope("t()", oOType.getSpannedScope()); + assertNoFindings(); + assertEquals("t", type.printFullName()); + assertSame(type.getTypeInfo(), oOType); + + type = calculateTypeWithinScope("t", method.getSpannedScope()); + assertEquals("(() -> t & t)", type.printFullName()); + assertTrue(type.isIntersectionType()); + assertTrue(((SymTypeOfIntersection) type).getIntersectedTypeSet() + .stream() + .anyMatch(t -> t.hasTypeInfo() && t.getTypeInfo() == oOType)); + } + + // class t { + // t t; + // t t() { + // t t; + // => test expression "t" in this method scope + // } + // => test expressions "t" and "t()" in this class scope + // } + @Test + public void test2() throws IOException { + IBasicSymbolsGlobalScope gs = BasicSymbolsMill.globalScope(); + + OOTypeSymbol oOType = oOtype("t"); + inScope(gs, oOType); + inScope(oOType.getSpannedScope(), typeVariable("t")); + + FieldSymbol field = field("t", + SymTypeExpressionFactory.createTypeObject(oOType)); + inScope(oOType.getSpannedScope(), field); + + MethodSymbol method = method("t", + SymTypeExpressionFactory.createTypeObject(oOType)); + inScope(oOType.getSpannedScope(), method); + inScope(method.getSpannedScope(), typeVariable("t")); + + VariableSymbol variable = variable("t", + SymTypeExpressionFactory.createTypeObject(oOType)); + inScope(method.getSpannedScope(), variable); + + SymTypeExpression type = + calculateTypeWithinScope("t", oOType.getSpannedScope()); + assertEquals("((t) -> t & t)", type.printFullName()); + assertTrue(type.isIntersectionType()); + assertTrue(((SymTypeOfIntersection) type).getIntersectedTypeSet() + .stream() + .anyMatch(t -> t.hasTypeInfo() && t.getTypeInfo() == oOType)); + + type = calculateTypeWithinScope("t", method.getSpannedScope()); + assertEquals("((t) -> t & t)", type.printFullName()); + assertTrue(type.isIntersectionType()); + assertTrue(((SymTypeOfIntersection) type).getIntersectedTypeSet() + .stream() + .anyMatch(t -> t.hasTypeInfo() && t.getTypeInfo() == oOType)); + } + + // class s { + // class t {} + // t t; + // t t() { + // t t; + // => test expression "t" in this class scope + // } + // } + @Test + public void test3() throws IOException { + IBasicSymbolsGlobalScope gs = BasicSymbolsMill.globalScope(); + + OOTypeSymbol oOType = oOtype("s"); + inScope(gs, oOType); + inScope(oOType.getSpannedScope(), typeVariable("t")); + + OOTypeSymbol oOType1 = oOtype("t"); + inScope(oOType.getSpannedScope(), oOType1); + + FieldSymbol field = field("t", + SymTypeExpressionFactory.createTypeObject(oOType1)); + inScope(oOType.getSpannedScope(), field); + + MethodSymbol method = method("t", + SymTypeExpressionFactory.createTypeObject(oOType)); + inScope(oOType.getSpannedScope(), method); + inScope(method.getSpannedScope(), typeVariable("t")); + + VariableSymbol variable = variable("t", + SymTypeExpressionFactory.createTypeObject(oOType)); + inScope(method.getSpannedScope(), variable); + + SymTypeExpression type = + calculateTypeWithinScope("t", oOType.getSpannedScope()); + assertEquals("((s) -> s & s.t)", type.printFullName()); + assertTrue(type.isIntersectionType()); + assertTrue(((SymTypeOfIntersection) type).getIntersectedTypeSet() + .stream() + .anyMatch(t -> t.hasTypeInfo() && t.getTypeInfo() == oOType1)); + + type = calculateTypeWithinScope("t", method.getSpannedScope()); + assertEquals("((s) -> s & s)", type.printFullName()); + assertTrue(type.isIntersectionType()); + assertTrue(((SymTypeOfIntersection) type).getIntersectedTypeSet() + .stream() + .anyMatch(t -> t.hasTypeInfo() && t.getTypeInfo() == oOType)); + } + + // class s { + // class t {} + // t t; + // t t() { + // => test expression "t" in this method scope + // } + // => test expressions "t()" and "t" in this class scope + // } + @Test + public void test4() throws IOException { + IBasicSymbolsGlobalScope gs = BasicSymbolsMill.globalScope(); + + OOTypeSymbol oOType = oOtype("s"); + inScope(gs, oOType); + + OOTypeSymbol oOType1 = oOtype("t"); + inScope(oOType.getSpannedScope(), oOType1); + + FieldSymbol field = field("t", + SymTypeExpressionFactory.createTypeObject(oOType1)); + inScope(oOType.getSpannedScope(), field); + + MethodSymbol method = method("t", + SymTypeExpressionFactory.createTypeObject(oOType1)); + inScope(oOType.getSpannedScope(), method); + + SymTypeExpression type = + calculateTypeWithinScope("t", oOType.getSpannedScope()); + assertEquals("(() -> s.t & s.t)", type.printFullName()); + assertTrue(type.isIntersectionType()); + assertTrue(((SymTypeOfIntersection) type).getIntersectedTypeSet() + .stream() + .anyMatch(t -> t.hasTypeInfo() && t.getTypeInfo() == oOType1)); + + type = calculateTypeWithinScope("t()", oOType.getSpannedScope()); + assertEquals("s.t", type.printFullName()); + assertSame(type.getTypeInfo(), oOType1); + + type = calculateTypeWithinScope("t", method.getSpannedScope()); + assertEquals("(() -> s.t & s.t)", type.printFullName()); + assertTrue(type.isIntersectionType()); + assertTrue(((SymTypeOfIntersection) type).getIntersectedTypeSet() + .stream() + .anyMatch(t -> t.hasTypeInfo() && t.getTypeInfo() == oOType1)); + } + + // class t {} + // class s extends t { + // t t; + // => test expression "t" in this class scope + // } + @Test + public void test5() throws IOException { + IBasicSymbolsGlobalScope gs = BasicSymbolsMill.globalScope(); + + OOTypeSymbol oOType = oOtype("t"); + inScope(gs, oOType); + inScope(oOType.getSpannedScope(), typeVariable("t")); + + OOTypeSymbol oOType1 = oOtype("s", List.of( + SymTypeExpressionFactory.createGenerics(oOType, _booleanSymType) + )); + inScope(gs, oOType1); + + FieldSymbol field = field("t", + SymTypeExpressionFactory.createTypeObject(oOType)); + inScope(oOType1.getSpannedScope(), field); + + SymTypeExpression type = + calculateTypeWithinScope("t", oOType1.getSpannedScope()); + assertEquals("t", type.printFullName()); + assertSame(oOType, type.getTypeInfo()); + } + + // class t {} + // class s {} + // class u extends s { + // t t; + // => test expression "t" in this class scope + // } + @Test + public void test6() throws IOException { + ICombineExpressionsWithLiteralsGlobalScope gs = + CombineExpressionsWithLiteralsMill.globalScope(); + + OOTypeSymbol oOType = oOtype("t"); + inScope(gs, oOType); + + OOTypeSymbol oOType1 = oOtype("s"); + inScope(gs, oOType1); + inScope(oOType1.getSpannedScope(), typeVariable("t")); + + OOTypeSymbol oOType2 = oOtype("u", List.of( + SymTypeExpressionFactory.createGenerics(oOType1, + SymTypeExpressionFactory.createTypeObject(oOType) + ) + )); + inScope(gs, oOType2); + + FieldSymbol fieldSymbol = field("t", + SymTypeExpressionFactory.createTypeObject(oOType)); + inScope(oOType2.getSpannedScope(), fieldSymbol); + + SymTypeExpression type = + calculateTypeWithinScope("t", oOType2.getSpannedScope()); + assertEquals("t", type.printFullName()); + assertSame(type.getTypeInfo(), oOType); + } + + // Helper + + /** + * calculates the type of the (simple) expression within the scope + * s.a. getExpressionScopeSetter + */ + SymTypeExpression calculateTypeWithinScope( + String exprStr, + IMCBasicsScope scope + ) throws IOException { + ASTExpression expr = parseExpr(exprStr); + generateScopes(expr); + expr.accept(getExpressionScopeSetter(scope)); + calculateTypes(expr); + SymTypeExpression type = getType4Ast().getTypeOfExpression(expr); + assertNoFindings(); + return type; + } + + /** + * Sets every (sub-)expression to the given scope. + * This can be used to test the expression in specific contexts. + * This only works for expressions, in which no own scope is spanned, + * e.g., lambdas shall not be used with this + */ + protected ExpressionsBasisTraverser getExpressionScopeSetter( + IMCBasicsScope scope) { + ExpressionsBasisTraverser scopeSetter = + ExpressionsBasisMill.inheritanceTraverser(); + scopeSetter.add4ExpressionsBasis( + new ExpressionsBasisVisitor2() { + @Override + public void visit(ASTExpression node) { + node.setEnclosingScope(scope); + } + } + ); + return scopeSetter; + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types3/SymTypeBoxingVisitorTest.java b/monticore-grammar/src/test/java/de/monticore/types3/SymTypeBoxingVisitorTest.java new file mode 100644 index 0000000000..d2ae4ce64c --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types3/SymTypeBoxingVisitorTest.java @@ -0,0 +1,111 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.types3; + +import de.monticore.expressions.combineexpressionswithliterals.CombineExpressionsWithLiteralsMill; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.ICombineExpressionsWithLiteralsGlobalScope; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.symbols.basicsymbols._symboltable.TypeVarSymbol; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types3.util.DefsTypesForTests; +import de.monticore.types3.util.SymTypeBoxingVisitor; +import org.junit.Before; +import org.junit.Test; + +import static de.monticore.types.check.SymTypeExpressionFactory.*; +import static de.monticore.types3.util.DefsTypesForTests.*; +import static org.junit.Assert.assertEquals; + +public class SymTypeBoxingVisitorTest extends AbstractTypeTest { + + SymTypeBoxingVisitor visitor = new SymTypeBoxingVisitor(); + + @Before + public void init() { + CombineExpressionsWithLiteralsMill.reset(); + CombineExpressionsWithLiteralsMill.init(); + DefsTypesForTests.setup(); + } + + @Test + public void boxPrimitives() { + check(_intSymType, "java.lang.Integer"); + check(_doubleSymType, "java.lang.Double"); + check(_floatSymType, "java.lang.Float"); + check(_shortSymType, "java.lang.Short"); + check(_longSymType, "java.lang.Long"); + check(_booleanSymType, "java.lang.Boolean"); + check(_byteSymType, "java.lang.Byte"); + check(_charSymType, "java.lang.Character"); + assertNoFindings(); + } + + @Test + public void doNotBoxToUnknownTypes() { + //remove Double + _DoubleSymType.getTypeInfo().getEnclosingScope() + .remove(_DoubleSymType.getTypeInfo()); + // as Double does not exists, double remains double + check(createPrimitive(BasicSymbolsMill.DOUBLE), "double"); + // same with Collections + _boxedMapSymType.getTypeInfo().getEnclosingScope() + .remove(_boxedMapSymType.getTypeInfo()); + check(createGenerics(_unboxedMapSymType.getTypeInfo(), _intSymType, _doubleSymType), + "Map"); + } + + @Test + public void boxObjects() { + check(createTypeObject(_unboxedString.getTypeInfo()), "java.lang.String"); + } + + @Test + public void boxCollections() { + check(createGenerics(_unboxedOptionalSymType.getTypeInfo(), _intSymType), + "java.util.Optional"); + check(createGenerics(_unboxedSetSymType.getTypeInfo(), _intSymType), + "java.util.Set"); + check(createGenerics(_unboxedListSymType.getTypeInfo(), _intSymType), + "java.util.List"); + check(createGenerics(_unboxedMapSymType.getTypeInfo(), _intSymType, _doubleSymType), + "java.util.Map"); + assertNoFindings(); + } + + @Test + public void boxComplexTypes() { + ICombineExpressionsWithLiteralsGlobalScope gs = + CombineExpressionsWithLiteralsMill.globalScope(); + TypeSymbol person = type("Person"); + TypeVarSymbol tVar = typeVariable("T"); + check( + createTypeArray( + createGenerics(_unboxedMapSymType.getTypeInfo(), + createUnion( + createTypeObject(person), + _IntegerSymType, + _doubleSymType + ), + createTypeArray( + createIntersection( + createTypeObject(person), + createTypeVariable(tVar), + createGenerics(_unboxedOptionalSymType.getTypeInfo(), + createTypeArray(_intSymType, 1) + ) + ), 2 + ) + ), 1 + ), + "java.util.Map<(Person | java.lang.Double | java.lang.Integer)," + + "(Person & T & java.util.Optional)[][]>[]" + ); + } + + public void check(SymTypeExpression unboxed, String expectedBoxedName) { + SymTypeExpression boxed = visitor.calculate(unboxed); + assertNoFindings(); + assertEquals(expectedBoxedName, boxed.printFullName()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types3/SymTypeCompatibilityTest.java b/monticore-grammar/src/test/java/de/monticore/types3/SymTypeCompatibilityTest.java new file mode 100644 index 0000000000..2745111dbd --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types3/SymTypeCompatibilityTest.java @@ -0,0 +1,678 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.types3; + +import de.monticore.expressions.combineexpressionswithliterals.CombineExpressionsWithLiteralsMill; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.ICombineExpressionsWithLiteralsGlobalScope; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.ICombineExpressionsWithLiteralsScope; +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.IBasicSymbolsGlobalScope; +import de.monticore.symbols.basicsymbols._symboltable.IBasicSymbolsScope; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.symbols.basicsymbols._symboltable.TypeVarSymbol; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeOfGenerics; +import de.monticore.types.check.SymTypeOfObject; +import de.monticore.types.check.SymTypeVariable; +import de.monticore.types3.util.DefsTypesForTests; +import de.monticore.types3.util.SymTypeRelations; +import org.junit.Before; +import org.junit.Test; + +import java.util.Collections; +import java.util.List; + +import static de.monticore.types.check.SymTypeExpressionFactory.createFunction; +import static de.monticore.types.check.SymTypeExpressionFactory.createGenerics; +import static de.monticore.types.check.SymTypeExpressionFactory.createIntersection; +import static de.monticore.types.check.SymTypeExpressionFactory.createTypeArray; +import static de.monticore.types.check.SymTypeExpressionFactory.createTypeObject; +import static de.monticore.types.check.SymTypeExpressionFactory.createTypeOfNull; +import static de.monticore.types.check.SymTypeExpressionFactory.createTypeVariable; +import static de.monticore.types.check.SymTypeExpressionFactory.createUnion; +import static de.monticore.types3.util.DefsTypesForTests.*; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class SymTypeCompatibilityTest extends AbstractTypeTest { + + protected ICombineExpressionsWithLiteralsScope scope; + + protected ISymTypeRelations tr; + + @Before + public void init() { + CombineExpressionsWithLiteralsMill.reset(); + CombineExpressionsWithLiteralsMill.init(); + DefsTypesForTests.setup(); + tr = new SymTypeRelations(); + } + + @Test + public void isCompatiblePrimitives() { + assertFalse(tr.isCompatible(_booleanSymType, _doubleSymType)); + assertFalse(tr.isCompatible(_booleanSymType, _floatSymType)); + assertFalse(tr.isCompatible(_booleanSymType, _longSymType)); + assertFalse(tr.isCompatible(_booleanSymType, _intSymType)); + assertFalse(tr.isCompatible(_booleanSymType, _charSymType)); + assertFalse(tr.isCompatible(_booleanSymType, _shortSymType)); + assertFalse(tr.isCompatible(_booleanSymType, _byteSymType)); + assertTrue(tr.isCompatible(_booleanSymType, _booleanSymType)); + + assertTrue(tr.isCompatible(_doubleSymType, _doubleSymType)); + assertTrue(tr.isCompatible(_doubleSymType, _floatSymType)); + assertTrue(tr.isCompatible(_doubleSymType, _longSymType)); + assertTrue(tr.isCompatible(_doubleSymType, _intSymType)); + assertTrue(tr.isCompatible(_doubleSymType, _charSymType)); + assertTrue(tr.isCompatible(_doubleSymType, _shortSymType)); + assertTrue(tr.isCompatible(_doubleSymType, _byteSymType)); + assertFalse(tr.isCompatible(_doubleSymType, _booleanSymType)); + + assertFalse(tr.isCompatible(_floatSymType, _doubleSymType)); + assertTrue(tr.isCompatible(_floatSymType, _floatSymType)); + assertTrue(tr.isCompatible(_floatSymType, _longSymType)); + assertTrue(tr.isCompatible(_floatSymType, _intSymType)); + assertTrue(tr.isCompatible(_floatSymType, _charSymType)); + assertTrue(tr.isCompatible(_floatSymType, _shortSymType)); + assertTrue(tr.isCompatible(_floatSymType, _byteSymType)); + assertFalse(tr.isCompatible(_floatSymType, _booleanSymType)); + + assertFalse(tr.isCompatible(_longSymType, _doubleSymType)); + assertFalse(tr.isCompatible(_longSymType, _floatSymType)); + assertTrue(tr.isCompatible(_longSymType, _longSymType)); + assertTrue(tr.isCompatible(_longSymType, _intSymType)); + assertTrue(tr.isCompatible(_longSymType, _charSymType)); + assertTrue(tr.isCompatible(_longSymType, _shortSymType)); + assertTrue(tr.isCompatible(_longSymType, _byteSymType)); + assertFalse(tr.isCompatible(_longSymType, _booleanSymType)); + + assertFalse(tr.isCompatible(_intSymType, _doubleSymType)); + assertFalse(tr.isCompatible(_intSymType, _floatSymType)); + assertFalse(tr.isCompatible(_intSymType, _longSymType)); + assertTrue(tr.isCompatible(_intSymType, _intSymType)); + assertTrue(tr.isCompatible(_intSymType, _charSymType)); + assertTrue(tr.isCompatible(_intSymType, _shortSymType)); + assertTrue(tr.isCompatible(_intSymType, _byteSymType)); + assertFalse(tr.isCompatible(_intSymType, _booleanSymType)); + + assertFalse(tr.isCompatible(_charSymType, _doubleSymType)); + assertFalse(tr.isCompatible(_charSymType, _floatSymType)); + assertFalse(tr.isCompatible(_charSymType, _longSymType)); + assertFalse(tr.isCompatible(_charSymType, _intSymType)); + assertTrue(tr.isCompatible(_charSymType, _charSymType)); + assertFalse(tr.isCompatible(_charSymType, _shortSymType)); + assertFalse(tr.isCompatible(_charSymType, _byteSymType)); + assertFalse(tr.isCompatible(_charSymType, _booleanSymType)); + + assertFalse(tr.isCompatible(_shortSymType, _doubleSymType)); + assertFalse(tr.isCompatible(_shortSymType, _floatSymType)); + assertFalse(tr.isCompatible(_shortSymType, _longSymType)); + assertFalse(tr.isCompatible(_shortSymType, _intSymType)); + assertFalse(tr.isCompatible(_shortSymType, _charSymType)); + assertTrue(tr.isCompatible(_shortSymType, _shortSymType)); + assertTrue(tr.isCompatible(_shortSymType, _byteSymType)); + assertFalse(tr.isCompatible(_shortSymType, _booleanSymType)); + + assertFalse(tr.isCompatible(_byteSymType, _doubleSymType)); + assertFalse(tr.isCompatible(_byteSymType, _floatSymType)); + assertFalse(tr.isCompatible(_byteSymType, _longSymType)); + assertFalse(tr.isCompatible(_byteSymType, _intSymType)); + assertFalse(tr.isCompatible(_byteSymType, _charSymType)); + assertFalse(tr.isCompatible(_byteSymType, _shortSymType)); + assertTrue(tr.isCompatible(_byteSymType, _byteSymType)); + assertFalse(tr.isCompatible(_byteSymType, _booleanSymType)); + + assertFalse(tr.isCompatible(_booleanSymType, _personSymType)); + assertTrue(tr.isCompatible(_booleanSymType, _BooleanSymType)); + assertNoFindings(); + } + + @Test + public void isSubTypePrimitives() { + assertFalse(tr.isSubTypeOf(_booleanSymType, _doubleSymType)); + assertFalse(tr.isSubTypeOf(_booleanSymType, _floatSymType)); + assertFalse(tr.isSubTypeOf(_booleanSymType, _longSymType)); + assertFalse(tr.isSubTypeOf(_booleanSymType, _intSymType)); + assertFalse(tr.isSubTypeOf(_booleanSymType, _charSymType)); + assertFalse(tr.isSubTypeOf(_booleanSymType, _shortSymType)); + assertFalse(tr.isSubTypeOf(_booleanSymType, _byteSymType)); + assertTrue(tr.isSubTypeOf(_booleanSymType, _booleanSymType)); + + assertTrue(tr.isSubTypeOf(_doubleSymType, _doubleSymType)); + assertFalse(tr.isSubTypeOf(_doubleSymType, _floatSymType)); + assertFalse(tr.isSubTypeOf(_doubleSymType, _longSymType)); + assertFalse(tr.isSubTypeOf(_doubleSymType, _intSymType)); + assertFalse(tr.isSubTypeOf(_doubleSymType, _charSymType)); + assertFalse(tr.isSubTypeOf(_doubleSymType, _shortSymType)); + assertFalse(tr.isSubTypeOf(_doubleSymType, _byteSymType)); + assertFalse(tr.isSubTypeOf(_doubleSymType, _booleanSymType)); + + assertTrue(tr.isSubTypeOf(_floatSymType, _doubleSymType)); + assertTrue(tr.isSubTypeOf(_floatSymType, _floatSymType)); + assertFalse(tr.isSubTypeOf(_floatSymType, _longSymType)); + assertFalse(tr.isSubTypeOf(_floatSymType, _intSymType)); + assertFalse(tr.isSubTypeOf(_floatSymType, _charSymType)); + assertFalse(tr.isSubTypeOf(_floatSymType, _shortSymType)); + assertFalse(tr.isSubTypeOf(_floatSymType, _byteSymType)); + assertFalse(tr.isSubTypeOf(_floatSymType, _booleanSymType)); + + assertTrue(tr.isSubTypeOf(_longSymType, _doubleSymType)); + assertTrue(tr.isSubTypeOf(_longSymType, _floatSymType)); + assertTrue(tr.isSubTypeOf(_longSymType, _longSymType)); + assertFalse(tr.isSubTypeOf(_longSymType, _intSymType)); + assertFalse(tr.isSubTypeOf(_longSymType, _charSymType)); + assertFalse(tr.isSubTypeOf(_longSymType, _shortSymType)); + assertFalse(tr.isSubTypeOf(_longSymType, _byteSymType)); + assertFalse(tr.isSubTypeOf(_longSymType, _booleanSymType)); + + assertTrue(tr.isSubTypeOf(_intSymType, _doubleSymType)); + assertTrue(tr.isSubTypeOf(_intSymType, _floatSymType)); + assertTrue(tr.isSubTypeOf(_intSymType, _longSymType)); + assertTrue(tr.isSubTypeOf(_intSymType, _intSymType)); + assertFalse(tr.isSubTypeOf(_intSymType, _charSymType)); + assertFalse(tr.isSubTypeOf(_intSymType, _shortSymType)); + assertFalse(tr.isSubTypeOf(_intSymType, _byteSymType)); + assertFalse(tr.isSubTypeOf(_intSymType, _booleanSymType)); + + assertTrue(tr.isSubTypeOf(_charSymType, _doubleSymType)); + assertTrue(tr.isSubTypeOf(_charSymType, _floatSymType)); + assertTrue(tr.isSubTypeOf(_charSymType, _longSymType)); + assertTrue(tr.isSubTypeOf(_charSymType, _intSymType)); + assertTrue(tr.isSubTypeOf(_charSymType, _charSymType)); + assertFalse(tr.isSubTypeOf(_charSymType, _shortSymType)); + assertFalse(tr.isSubTypeOf(_charSymType, _byteSymType)); + assertFalse(tr.isSubTypeOf(_charSymType, _booleanSymType)); + + assertTrue(tr.isSubTypeOf(_shortSymType, _doubleSymType)); + assertTrue(tr.isSubTypeOf(_shortSymType, _floatSymType)); + assertTrue(tr.isSubTypeOf(_shortSymType, _longSymType)); + assertTrue(tr.isSubTypeOf(_shortSymType, _intSymType)); + assertFalse(tr.isSubTypeOf(_shortSymType, _charSymType)); + assertTrue(tr.isSubTypeOf(_shortSymType, _shortSymType)); + assertFalse(tr.isSubTypeOf(_shortSymType, _byteSymType)); + assertFalse(tr.isSubTypeOf(_shortSymType, _booleanSymType)); + + assertTrue(tr.isSubTypeOf(_byteSymType, _doubleSymType)); + assertTrue(tr.isSubTypeOf(_byteSymType, _floatSymType)); + assertTrue(tr.isSubTypeOf(_byteSymType, _longSymType)); + assertTrue(tr.isSubTypeOf(_byteSymType, _intSymType)); + assertFalse(tr.isSubTypeOf(_byteSymType, _charSymType)); + assertTrue(tr.isSubTypeOf(_byteSymType, _shortSymType)); + assertTrue(tr.isSubTypeOf(_byteSymType, _byteSymType)); + assertFalse(tr.isSubTypeOf(_byteSymType, _booleanSymType)); + + assertFalse(tr.isSubTypeOf(_booleanSymType, _personSymType)); + assertFalse(tr.isSubTypeOf(_booleanSymType, _BooleanSymType)); + assertNoFindings(); + } + + @Test + public void isCompatibleObjects() { + assertTrue(tr.isCompatible(_personSymType, _personSymType)); + assertTrue(tr.isCompatible(_personSymType, _studentSymType)); + assertTrue(tr.isCompatible(_personSymType, _csStudentSymType)); + assertTrue(tr.isCompatible(_personSymType, _teacherSymType)); + + assertFalse(tr.isCompatible(_studentSymType, _personSymType)); + assertTrue(tr.isCompatible(_studentSymType, _studentSymType)); + assertTrue(tr.isCompatible(_studentSymType, _csStudentSymType)); + assertFalse(tr.isCompatible(_studentSymType, _teacherSymType)); + + assertFalse(tr.isCompatible(_csStudentSymType, _personSymType)); + assertFalse(tr.isCompatible(_csStudentSymType, _studentSymType)); + assertTrue(tr.isCompatible(_csStudentSymType, _csStudentSymType)); + assertFalse(tr.isCompatible(_csStudentSymType, _teacherSymType)); + + assertFalse(tr.isCompatible(_teacherSymType, _personSymType)); + assertFalse(tr.isCompatible(_teacherSymType, _studentSymType)); + assertFalse(tr.isCompatible(_teacherSymType, _csStudentSymType)); + assertTrue(tr.isCompatible(_teacherSymType, _teacherSymType)); + + // String + assertTrue(tr.isCompatible(_boxedString, _boxedString)); + assertTrue(tr.isCompatible(_boxedString, _unboxedString)); + assertTrue(tr.isCompatible(_unboxedString, _boxedString)); + assertTrue(tr.isCompatible(_unboxedString, _unboxedString)); + + // diverse types + assertFalse(tr.isCompatible(_personSymType, _intSymType)); + assertFalse(tr.isCompatible( + _personSymType, + createTypeArray(_personSymType, 1)) + ); + assertTrue(tr.isCompatible( + _personSymType, + createTypeArray(_personSymType, 0)) + ); + assertFalse(tr.isCompatible(_personSymType, _unboxedMapSymType)); + assertFalse(tr.isCompatible(_personSymType, _BooleanSymType)); + assertNoFindings(); + } + + @Test + public void isCompatibleGenerics() { + IBasicSymbolsGlobalScope gs = BasicSymbolsMill.globalScope(); + // List, List, List + SymTypeOfGenerics listOfPerson + = createGenerics(_unboxedListSymType.getTypeInfo(), _personSymType); + SymTypeOfGenerics listOfInt + = createGenerics(_unboxedListSymType.getTypeInfo(), _intSymType); + SymTypeOfGenerics listOfBoolean + = createGenerics(_unboxedListSymType.getTypeInfo(), _booleanSymType); + // SubPersonList extends List + TypeSymbol subPersonSym = + inScope(gs, type("SubPersonList", List.of(listOfPerson))); + SymTypeOfObject subPersonList = createTypeObject(subPersonSym); + // LinkedList + SymTypeOfGenerics linkedListOfPerson = + createGenerics(_linkedListSymType.getTypeInfo(), _personSymType); + + assertTrue(tr.isCompatible(listOfPerson, listOfPerson)); + assertTrue(tr.isCompatible(listOfPerson, subPersonList)); + assertTrue(tr.isCompatible(listOfPerson, linkedListOfPerson)); + + assertFalse(tr.isCompatible(listOfInt, _intSymType)); + assertFalse(tr.isCompatible(listOfInt, listOfBoolean)); + assertFalse(tr.isCompatible(listOfBoolean, listOfInt)); + assertFalse(tr.isCompatible(listOfBoolean, listOfPerson)); + assertFalse(tr.isCompatible(listOfPerson, listOfBoolean)); + assertFalse(tr.isCompatible(listOfBoolean, subPersonList)); + assertFalse(tr.isCompatible(subPersonList, listOfPerson)); + assertNoFindings(); + } + + @Test + public void isSubTypeGenericsDoNotIgnoreTypeArguments() { + //s. https://git.rwth-aachen.de/monticore/monticore/-/issues/2977 + // Test if we do not ignore TypeVariables in the description of the supertypes. + // e.g., given HashMap extends Map + // do not ignore the identity of the variables, e.g., do not allow + // Map x = new HashMap(); + SymTypeOfGenerics iSMap = createGenerics( + _boxedMapSymType.getTypeInfo(), _IntegerSymType, _boxedString); + SymTypeOfGenerics iSHashMap = createGenerics( + _hashMapSymType.getTypeInfo(), _IntegerSymType, _boxedString); + SymTypeOfGenerics sIHashMap = createGenerics( + _hashMapSymType.getTypeInfo(), _boxedString, _IntegerSymType); + + assertTrue(tr.isCompatible(iSMap, iSHashMap)); + assertFalse(tr.isCompatible(iSMap, sIHashMap)); + assertNoFindings(); + } + + @Test + public void isCompatibleUnions() { + assertTrue(tr.isCompatible( + createUnion(_personSymType, _carSymType), _personSymType + )); + assertTrue(tr.isCompatible( + createUnion(_personSymType, _carSymType), + createUnion(_personSymType, _carSymType) + )); + assertTrue(tr.isCompatible( + _personSymType, createUnion(_studentSymType, _teacherSymType) + )); + assertFalse(tr.isCompatible( + _personSymType, createUnion(_studentSymType, _carSymType) + )); + assertFalse(tr.isCompatible( + createUnion(_studentSymType, _teacherSymType), _personSymType + )); + assertNoFindings(); + } + + @Test + public void IsCompatibleIntersections() { + assertTrue(tr.isCompatible( + createIntersection(_personSymType, _carSymType), + createIntersection(_personSymType, _carSymType) + )); + assertTrue(tr.isCompatible( + _personSymType, createIntersection(_studentSymType, _teacherSymType) + )); + assertTrue(tr.isCompatible( + _personSymType, createIntersection(_personSymType, _carSymType) + )); + assertTrue(tr.isCompatible( + createIntersection(_teachableSymType, _personSymType), + createUnion(_childSymType, _studentSymType) + )); + assertFalse(tr.isCompatible( + createIntersection(_personSymType, _carSymType), _personSymType + )); + assertNoFindings(); + } + + @Test + public void isCompatibleFunctions() { + assertTrue(tr.isCompatible( + createFunction(_personSymType), createFunction(_personSymType) + )); + assertTrue(tr.isCompatible( + createFunction(_personSymType, _intSymType), + createFunction(_personSymType, _intSymType) + )); + assertTrue(tr.isCompatible( + createFunction(_personSymType, List.of(_intSymType), true), + createFunction(_personSymType, List.of(_intSymType), true) + )); + assertTrue(tr.isCompatible( + createFunction(_studentSymType, _intSymType), + createFunction(_personSymType, _intSymType) + )); + assertTrue(tr.isCompatible( + createFunction(_personSymType, _longSymType), + createFunction(_personSymType, _intSymType) + )); + assertTrue(tr.isCompatible( + createFunction(_personSymType, _longSymType), + createFunction(_personSymType, List.of(_intSymType), true) + )); + assertTrue(tr.isCompatible( + createFunction(_personSymType, _intSymType, _intSymType), + createFunction(_personSymType, List.of(_intSymType), true) + )); + assertTrue(tr.isCompatible( + createFunction(_personSymType), + createFunction(_personSymType, List.of(_intSymType), true) + )); + assertFalse(tr.isCompatible( + createFunction(_personSymType), createFunction(_carSymType) + )); + assertFalse(tr.isCompatible( + createFunction(_personSymType, _intSymType), + createFunction(_studentSymType, _intSymType) + )); + assertFalse(tr.isCompatible( + createFunction(_personSymType, _intSymType), + createFunction(_personSymType, _longSymType) + )); + assertFalse(tr.isCompatible( + createFunction(_personSymType, List.of(_intSymType), true), + createFunction(_personSymType, _intSymType) + )); + + assertNoFindings(); + } + + @Test + public void isSubTypeBottom() { + // bottom is subType of EVERY other type + assertTrue(tr.isSubTypeOf(_bottomType, _bottomType)); + assertTrue(tr.isSubTypeOf(_bottomType, _topType)); + assertTrue(tr.isSubTypeOf(_bottomType, _intSymType)); + assertTrue(tr.isSubTypeOf(_bottomType, _booleanSymType)); + assertTrue(tr.isSubTypeOf(_bottomType, _IntegerSymType)); + assertTrue(tr.isSubTypeOf(_bottomType, _personSymType)); + assertTrue(tr.isSubTypeOf(_bottomType, _nullSymType)); + assertTrue(tr.isSubTypeOf(_bottomType, _unboxedListSymType)); + assertTrue(tr.isSubTypeOf(_bottomType, _unboxedListSymType)); + assertTrue(tr.isSubTypeOf(_bottomType, _linkedListSymType)); + // bottom is never the superType except for bottom itself + assertFalse(tr.isSubTypeOf(_topType, _bottomType)); + assertFalse(tr.isSubTypeOf(_intSymType, _bottomType)); + assertFalse(tr.isSubTypeOf(_booleanSymType, _bottomType)); + assertFalse(tr.isSubTypeOf(_IntegerSymType, _bottomType)); + assertFalse(tr.isSubTypeOf(_personSymType, _bottomType)); + assertFalse(tr.isSubTypeOf(_nullSymType, _bottomType)); + assertFalse(tr.isSubTypeOf(_unboxedListSymType, _bottomType)); + assertFalse(tr.isSubTypeOf(_unboxedListSymType, _bottomType)); + assertFalse(tr.isSubTypeOf(_linkedListSymType, _bottomType)); + } + + @Test + public void isSubTypeTop() { + // top is superType of EVERY other type + assertTrue(tr.isSubTypeOf(_bottomType, _topType)); + assertTrue(tr.isSubTypeOf(_topType, _topType)); + assertTrue(tr.isSubTypeOf(_intSymType, _topType)); + assertTrue(tr.isSubTypeOf(_booleanSymType, _topType)); + assertTrue(tr.isSubTypeOf(_IntegerSymType, _topType)); + assertTrue(tr.isSubTypeOf(_personSymType, _topType)); + assertTrue(tr.isSubTypeOf(_nullSymType, _topType)); + assertTrue(tr.isSubTypeOf(_unboxedListSymType, _topType)); + assertTrue(tr.isSubTypeOf(_unboxedListSymType, _topType)); + assertTrue(tr.isSubTypeOf(_linkedListSymType, _topType)); + // top is never the subType except for top itself + assertFalse(tr.isSubTypeOf(_topType, _bottomType)); + assertFalse(tr.isSubTypeOf(_topType, _intSymType)); + assertFalse(tr.isSubTypeOf(_topType, _booleanSymType)); + assertFalse(tr.isSubTypeOf(_topType, _IntegerSymType)); + assertFalse(tr.isSubTypeOf(_topType, _personSymType)); + assertFalse(tr.isSubTypeOf(_topType, _nullSymType)); + assertFalse(tr.isSubTypeOf(_topType, _unboxedListSymType)); + assertFalse(tr.isSubTypeOf(_topType, _unboxedListSymType)); + assertFalse(tr.isSubTypeOf(_topType, _linkedListSymType)); + } + + @Test + public void isCompatibleTypeVariable() { + // Setup type variables + IBasicSymbolsScope someScope = BasicSymbolsMill.scope(); + // unbounded, e.g., E in List + SymTypeVariable unboundedTVar = createTypeVariable( + inScope(someScope, typeVariable("unbounded")) + ); + SymTypeVariable unbounded2TVar = createTypeVariable( + inScope(someScope, typeVariable("unbounded2")) + ); + // upper bound Student, e.g., E in List + SymTypeVariable subStudentTVar = + createTypeVariable(_bottomType, _studentSymType); + // upper bound Person + SymTypeVariable subPersonTVar = + createTypeVariable(_bottomType, _personSymType); + // lower bound Student, no direct Java representation available + SymTypeVariable superStudentTVar = + createTypeVariable(_studentSymType, _topType); + // lower bound Person + SymTypeVariable superPersonTVar = + createTypeVariable(_personSymType, _topType); + // lower and upper Bound + SymTypeVariable superSSubCsSTVar = + createTypeVariable(_csStudentSymType, _studentSymType); + + // unbounded type variable are like existential types: + // we don't know enough to do pretty much anything with it + assertTrue(tr.isCompatible(unboundedTVar, unboundedTVar)); + assertFalse(tr.isCompatible(unboundedTVar, unbounded2TVar)); + assertFalse(tr.isCompatible(unboundedTVar, _personSymType)); + assertFalse(tr.isCompatible(_personSymType, unboundedTVar)); + + // we can assign variables if we know their upper bound + assertTrue(tr.isCompatible(_personSymType, subStudentTVar)); + assertTrue(tr.isCompatible(_studentSymType, subStudentTVar)); + assertFalse(tr.isCompatible(_csStudentSymType, subStudentTVar)); + // we cannot really assign to variable if we only know their upper bounds + assertFalse(tr.isCompatible(subStudentTVar, _personSymType)); + assertFalse(tr.isCompatible(subStudentTVar, _studentSymType)); + assertFalse(tr.isCompatible(subStudentTVar, _csStudentSymType)); + + // we can assign to variables if we know their lower bound + assertFalse(tr.isCompatible(superStudentTVar, _personSymType)); + assertTrue(tr.isCompatible(superStudentTVar, _studentSymType)); + assertTrue(tr.isCompatible(superStudentTVar, _csStudentSymType)); + // we cannot really assign variables if we only know their lower bounds + assertFalse(tr.isCompatible(_personSymType, superStudentTVar)); + assertFalse(tr.isCompatible(_studentSymType, superStudentTVar)); + assertFalse(tr.isCompatible(_csStudentSymType, superStudentTVar)); + + // two single bounded variables + assertTrue(tr.isCompatible(superStudentTVar, subStudentTVar)); + assertTrue(tr.isCompatible(superPersonTVar, subStudentTVar)); + assertFalse(tr.isCompatible(superStudentTVar, subPersonTVar)); + assertFalse(tr.isCompatible(subPersonTVar, subStudentTVar)); + assertFalse(tr.isCompatible(subStudentTVar, subPersonTVar)); + + // in case of upper AND lower bound set, + // we can assign and assign to the type variable + // assign to: + assertFalse(tr.isCompatible(superSSubCsSTVar, _personSymType)); + assertFalse(tr.isCompatible(superSSubCsSTVar, _studentSymType)); + assertTrue(tr.isCompatible(superSSubCsSTVar, _csStudentSymType)); + assertTrue(tr.isCompatible(superSSubCsSTVar, _firstSemesterCsStudentSymType)); + // assign: + assertTrue(tr.isCompatible(_personSymType, superSSubCsSTVar)); + assertTrue(tr.isCompatible(_studentSymType, superSSubCsSTVar)); + assertFalse(tr.isCompatible(_csStudentSymType, superSSubCsSTVar)); + assertFalse(tr.isCompatible(_firstSemesterCsStudentSymType, superSSubCsSTVar)); + + assertNoFindings(); + } + + @Test + public void isCompatibleTypeVariableRecursive() { + // check that we can handle recursively defined generics, + // e.g., A> + assertTrue(tr.isCompatible(_simpleCrtSymType, _simpleCrtSymType)); + assertTrue(tr.isCompatible(_graphSymType, _graphSymType)); + assertTrue(tr.isCompatible(_graphNodeSymType, _graphNodeSymType)); + assertTrue(tr.isCompatible(_graphEdgeSymType, _graphEdgeSymType)); + } + + @Test + public void isCompatibleUpperBoundedGenerics() { + ICombineExpressionsWithLiteralsGlobalScope gs = + CombineExpressionsWithLiteralsMill.globalScope(); + // List + SymTypeOfGenerics pList = createGenerics( + _boxedListSymType.getTypeInfo(), _personSymType); + // List + SymTypeOfGenerics sList = createGenerics( + _boxedListSymType.getTypeInfo(), _studentSymType); + // List + SymTypeOfGenerics sSubList = createGenerics( + _boxedListSymType.getTypeInfo(), + createTypeVariable(_bottomType, _studentSymType) + ); + // List + SymTypeOfGenerics pSubList = createGenerics( + _boxedListSymType.getTypeInfo(), + createTypeVariable(_bottomType, _personSymType) + ); + // LinkedList + SymTypeOfGenerics sSubLinkedList = createGenerics( + _linkedListSymType.getTypeInfo(), + createTypeVariable(_bottomType, _studentSymType) + ); + // LinkedList + SymTypeOfGenerics pSubLinkedList = createGenerics( + _linkedListSymType.getTypeInfo(), + createTypeVariable(_bottomType, _personSymType) + ); + + assertFalse(tr.isCompatible(pList, sList)); + assertFalse(tr.isCompatible(sList, pList)); + assertFalse(tr.isCompatible(pList, sSubList)); + assertFalse(tr.isCompatible(sList, sSubList)); + + assertTrue(tr.isCompatible(pSubList, pList)); + assertTrue(tr.isCompatible(pSubList, sList)); + assertTrue(tr.isCompatible(pSubList, pSubList)); + assertTrue(tr.isCompatible(pSubList, sSubList)); + assertFalse(tr.isCompatible(sSubList, pList)); + assertTrue(tr.isCompatible(sSubList, sList)); + assertFalse(tr.isCompatible(sSubList, pSubList)); + assertTrue(tr.isCompatible(sSubList, sSubList)); + + assertTrue(tr.isCompatible(pSubList, pSubLinkedList)); + assertTrue(tr.isCompatible(pSubList, sSubLinkedList)); + assertFalse(tr.isCompatible(sSubList, pSubLinkedList)); + assertTrue(tr.isCompatible(sSubList, sSubLinkedList)); + + assertNoFindings(); + } + + @Test + public void isCompatibleLowerBoundedGenerics() { + ICombineExpressionsWithLiteralsGlobalScope gs = + CombineExpressionsWithLiteralsMill.globalScope(); + // List + SymTypeOfGenerics pList = createGenerics( + _boxedListSymType.getTypeInfo(), _personSymType); + // List + SymTypeOfGenerics sList = createGenerics( + _boxedListSymType.getTypeInfo(), _studentSymType); + // List + SymTypeOfGenerics sSuperList = createGenerics( + _boxedListSymType.getTypeInfo(), + createTypeVariable(_studentSymType, _topType) + ); + // List + SymTypeOfGenerics pSuperList = createGenerics( + _boxedListSymType.getTypeInfo(), + createTypeVariable(_personSymType, _topType) + ); + // LinkedList + SymTypeOfGenerics sSuperLinkedList = createGenerics( + _linkedListSymType.getTypeInfo(), + createTypeVariable(_studentSymType, _topType) + ); + // LinkedList + SymTypeOfGenerics pSuperLinkedList = createGenerics( + _linkedListSymType.getTypeInfo(), + createTypeVariable(_personSymType, _topType) + ); + + assertFalse(tr.isCompatible(pList, sList)); + assertFalse(tr.isCompatible(sList, pList)); + assertFalse(tr.isCompatible(pList, sSuperList)); + assertFalse(tr.isCompatible(sList, sSuperList)); + + assertTrue(tr.isCompatible(pSuperList, pList)); + assertFalse(tr.isCompatible(pSuperList, sList)); + assertTrue(tr.isCompatible(pSuperList, pSuperList)); + assertFalse(tr.isCompatible(pSuperList, sSuperList)); + assertTrue(tr.isCompatible(sSuperList, pList)); + assertTrue(tr.isCompatible(sSuperList, sList)); + assertTrue(tr.isCompatible(sSuperList, pSuperList)); + assertTrue(tr.isCompatible(sSuperList, sSuperList)); + + assertTrue(tr.isCompatible(pSuperList, pSuperLinkedList)); + assertFalse(tr.isCompatible(pSuperList, sSuperLinkedList)); + assertTrue(tr.isCompatible(sSuperList, pSuperLinkedList)); + assertTrue(tr.isCompatible(sSuperList, sSuperLinkedList)); + + assertNoFindings(); + } + + @Test + public void nullCompatibilityAndSubTyping() { + assertFalse(tr.isCompatible(_intSymType, createTypeOfNull())); + assertTrue(tr.isCompatible(_IntegerSymType, createTypeOfNull())); + assertFalse(tr.isSubTypeOf(createTypeOfNull(), _personSymType)); + assertFalse(tr.isSubTypeOf(_personSymType, createTypeOfNull())); + } + + /** + * tests wether the unboxed type + * is compatible to a superclass of the boxed type, e.g., + * Comparable ci = 2; + * Iterable ii = List; + */ + @Test + public void isCompatibleSuperTypeOfBoxed() { + // add superclasses, these would exist for Class2MC + // Integer implements Comparable + TypeSymbol integerSym = _IntegerSymType.getTypeInfo(); + TypeVarSymbol comparableSymVar = typeVariable("T"); + TypeSymbol comparableSym = + type("Comparable", Collections.emptyList(), List.of(comparableSymVar)); + SymTypeExpression comparableInteger = createGenerics(comparableSym, _IntegerSymType); + integerSym.addSuperTypes(comparableInteger); + // java.util.List implements Iterable + TypeSymbol listSym = _boxedListSymType.getTypeInfo(); + TypeVarSymbol iterableSymVar = typeVariable("T"); + TypeSymbol iterableSym = + type("Iterable", Collections.emptyList(), List.of(iterableSymVar)); + listSym.setSuperTypesList(List.of( + createGenerics(iterableSym, _boxedListSymType.getArgument(0)) + )); + + assertTrue(tr.isCompatible(comparableInteger, _IntegerSymType)); + assertTrue(tr.isCompatible( + createGenerics(iterableSym, _IntegerSymType), + createGenerics(_unboxedListSymType.getTypeInfo(), _intSymType) + )); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/types3/SymTypeLeastUpperBoundTest.java b/monticore-grammar/src/test/java/de/monticore/types3/SymTypeLeastUpperBoundTest.java new file mode 100644 index 0000000000..2ebe1bb866 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types3/SymTypeLeastUpperBoundTest.java @@ -0,0 +1,67 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.types3; + +import de.monticore.expressions.combineexpressionswithliterals.CombineExpressionsWithLiteralsMill; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.ICombineExpressionsWithLiteralsScope; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types3.util.DefsTypesForTests; +import de.monticore.types3.util.SymTypeRelations; +import org.junit.Before; +import org.junit.Test; + +import java.util.Optional; + +import static de.monticore.types.check.SymTypeExpressionFactory.createIntersection; +import static de.monticore.types.check.SymTypeExpressionFactory.createTypeArray; +import static de.monticore.types.check.SymTypeExpressionFactory.createUnion; +import static de.monticore.types3.util.DefsTypesForTests._carSymType; +import static de.monticore.types3.util.DefsTypesForTests._childSymType; +import static de.monticore.types3.util.DefsTypesForTests._csStudentSymType; +import static de.monticore.types3.util.DefsTypesForTests._personSymType; +import static de.monticore.types3.util.DefsTypesForTests._studentSymType; +import static org.junit.Assert.assertEquals; + +public class SymTypeLeastUpperBoundTest extends AbstractTypeTest { + + protected ICombineExpressionsWithLiteralsScope scope; + + protected ISymTypeRelations tr; + + @Before + public void setup() { + CombineExpressionsWithLiteralsMill.reset(); + CombineExpressionsWithLiteralsMill.init(); + DefsTypesForTests.setup(); + tr = new SymTypeRelations(); + } + + @Test + public void leastUpperBound() { + checkLub(_personSymType, "Person"); + checkLub(createUnion(_personSymType, _studentSymType), "Person"); + checkLub(createUnion(_childSymType, _studentSymType), "(Person & Teachable)"); + checkLub(createUnion(_childSymType, _csStudentSymType), "(Person & Teachable)"); + checkLub(createUnion(_childSymType, _carSymType), "Obscure"); + checkLub( + createIntersection( + _personSymType, + createUnion(_childSymType, _studentSymType) + ), + "(Person & Teachable)" + ); + checkLub(createUnion( + createTypeArray(_childSymType, 2), + createTypeArray(_csStudentSymType, 2) + ), + "(Person[][] & Teachable[][])" + ); + } + + protected void checkLub(SymTypeExpression type, String expectedPrint) { + Optional lubOpt = tr.leastUpperBound(type); + String printed = lubOpt.map(SymTypeExpression::printFullName).orElse(""); + assertNoFindings(); + assertEquals(expectedPrint, printed); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/types3/SymTypeNormalizeVisitorTest.java b/monticore-grammar/src/test/java/de/monticore/types3/SymTypeNormalizeVisitorTest.java new file mode 100644 index 0000000000..d3ccf3eda9 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types3/SymTypeNormalizeVisitorTest.java @@ -0,0 +1,190 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.types3; + +import de.monticore.expressions.combineexpressionswithliterals.CombineExpressionsWithLiteralsMill; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeOfIntersection; +import de.monticore.types.check.SymTypeOfUnion; +import de.monticore.types3.util.DefsTypesForTests; +import de.monticore.types3.util.SymTypeNormalizeVisitor; +import de.monticore.types3.util.SymTypeRelations; +import org.junit.Before; +import org.junit.Test; + +import static de.monticore.types.check.SymTypeExpressionFactory.createFunction; +import static de.monticore.types.check.SymTypeExpressionFactory.createIntersection; +import static de.monticore.types.check.SymTypeExpressionFactory.createObscureType; +import static de.monticore.types.check.SymTypeExpressionFactory.createTypeArray; +import static de.monticore.types.check.SymTypeExpressionFactory.createTypeObject; +import static de.monticore.types.check.SymTypeExpressionFactory.createUnion; +import static de.monticore.types3.util.DefsTypesForTests._IntegerSymType; +import static de.monticore.types3.util.DefsTypesForTests._carSymType; +import static de.monticore.types3.util.DefsTypesForTests._intSymType; +import static de.monticore.types3.util.DefsTypesForTests._personSymType; +import static de.monticore.types3.util.DefsTypesForTests._shortSymType; +import static de.monticore.types3.util.DefsTypesForTests._studentSymType; +import static org.junit.Assert.assertEquals; + +public class SymTypeNormalizeVisitorTest extends AbstractTypeTest { + + SymTypeNormalizeVisitor visitor; + + @Before + public void init() { + CombineExpressionsWithLiteralsMill.reset(); + CombineExpressionsWithLiteralsMill.init(); + DefsTypesForTests.setup(); + SymTypeRelations tr = new SymTypeRelations(); + visitor = new SymTypeNormalizeVisitor(tr); + assertNoFindings(); + } + + @Test + public void doNotChangeSimpleTypes() { + check(_IntegerSymType, "java.lang.Integer"); + check(_intSymType, "int"); + check(_personSymType, "Person"); + assertNoFindings(); + } + + @Test + public void normalizeSimpleUnions() { + // Tests normalization that is at max changing one level + check(createUnion(_personSymType, _carSymType), "(Car | Person)"); + check(createUnion(_personSymType), "Person"); + check(createUnion( + _personSymType, createTypeObject(_personSymType.getTypeInfo()) + ), "Person"); + check(createUnion(_personSymType, createObscureType()), "Person"); + check(createUnion(_personSymType, _studentSymType), "Person"); + check(createUnion( + createFunction(_personSymType, _shortSymType), + createFunction(_studentSymType, _intSymType) + ), + "(int) -> Student" + ); + assertNoFindings(); + } + + @Test + public void normalizeSimpleIntersections() { + // Tests normalization that is at max changing one level + check(createIntersection(_personSymType, _carSymType), "(Car & Person)"); + check(createIntersection(_personSymType), "Person"); + check(createIntersection( + _personSymType, createTypeObject(_personSymType.getTypeInfo()) + ), "Person"); + check(createIntersection(_personSymType, createObscureType()), "Obscure"); + check(createIntersection(_personSymType, _studentSymType), "Student"); + check(createIntersection( + createFunction(_personSymType, _shortSymType), + createFunction(_studentSymType, _intSymType) + ), + "(short) -> Person" + ); + assertNoFindings(); + } + + @Test + public void normalizeArrays() { + check(createTypeArray(createObscureType(), 2), "Obscure"); + check(createTypeArray(_personSymType, 2), "Person[][]"); + check(createTypeArray(_personSymType, 0), "Person"); + check( + createTypeArray(createTypeArray(_personSymType, 1), 2), + "Person[][][]" + ); + assertNoFindings(); + } + + @Test + public void moveArraysBelowSetOperations() { + check( + createTypeArray( + createUnion( + createTypeArray(_personSymType, 1), + createTypeArray(_carSymType, 1) + ), + 1 + ), + "(Car[][] | Person[][])" + ); + check( + createTypeArray( + createIntersection( + createTypeArray(_personSymType, 1), + createTypeArray(_carSymType, 1) + ), + 1 + ), + "(Car[][] & Person[][])" + ); + check( + createTypeArray( + createIntersection( + createTypeArray( + createUnion( + createTypeArray(_personSymType, 1), + _personSymType + ), + 1 + ), + createTypeArray(_personSymType, 1) + ), + 1 + ), + "Person[][]" + ); + assertNoFindings(); + } + + @Test + public void normalizeComplexTypes() { + SymTypeOfUnion union1 = createUnion(_personSymType, _carSymType); + SymTypeOfUnion union2 = createUnion(_personSymType, _intSymType); + SymTypeOfIntersection inter1 = createIntersection(_personSymType, _carSymType); + SymTypeOfIntersection inter2 = createIntersection(_personSymType, _intSymType); + + // unions + check(createUnion(union1, _personSymType), "(Car | Person)"); + check(createUnion(union1, _intSymType), "(Car | Person | int)"); + check(createUnion(union1, union2), "(Car | Person | int)"); + + // intersections + check(createIntersection(inter1, _personSymType), "(Car & Person)"); + check(createIntersection(inter1, inter2), "(Car & Person & int)"); + + // combine unions and intersections + check(createUnion(inter1, _personSymType), "Person"); + check(createUnion(inter1, _intSymType), "((Car & Person) | int)"); + check(createUnion(inter1, union2), "(Person | int)"); + check(createUnion(inter1, union1), "(Car | Person)"); + check(createUnion(inter1, inter2), "((Car & Person) | (Person & int))"); + + check(createIntersection(union1, _personSymType), "Person"); + check(createIntersection(union1, _intSymType), "((Car & int) | (Person & int))"); + check(createIntersection(union1, union2), "((Car & int) | Person)"); + check(createIntersection(union1, inter1), "(Car & Person)"); + check(createIntersection(union1, inter2), "(Person & int)"); + + // normalize within arrays + check( + createTypeArray( + createTypeArray( + createIntersection(union1, _personSymType), + 2 + ), + 1 + ), + "Person[][][]" + ); + assertNoFindings(); + } + + public void check(SymTypeExpression type, String expectedPrint) { + SymTypeExpression normalized = visitor.calculate(type); + assertNoFindings(); + assertEquals(expectedPrint, normalized.printFullName()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types3/SymTypeUnboxingVisitorTest.java b/monticore-grammar/src/test/java/de/monticore/types3/SymTypeUnboxingVisitorTest.java new file mode 100644 index 0000000000..c97a1a989d --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types3/SymTypeUnboxingVisitorTest.java @@ -0,0 +1,110 @@ +// (c) https://github.com/MontiCore/monticore +package de.monticore.types3; + +import de.monticore.expressions.combineexpressionswithliterals.CombineExpressionsWithLiteralsMill; +import de.monticore.expressions.combineexpressionswithliterals._symboltable.ICombineExpressionsWithLiteralsGlobalScope; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.symbols.basicsymbols._symboltable.TypeVarSymbol; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types3.util.DefsTypesForTests; +import de.monticore.types3.util.SymTypeUnboxingVisitor; +import org.junit.Before; +import org.junit.Test; + +import static de.monticore.types.check.SymTypeExpressionFactory.*; +import static de.monticore.types3.util.DefsTypesForTests.*; +import static org.junit.Assert.assertEquals; + +public class SymTypeUnboxingVisitorTest extends AbstractTypeTest { + + SymTypeUnboxingVisitor visitor = new SymTypeUnboxingVisitor(); + + @Before + public void init() { + CombineExpressionsWithLiteralsMill.reset(); + CombineExpressionsWithLiteralsMill.init(); + DefsTypesForTests.setup(); + } + + @Test + public void unboxPrimitives() { + check(_IntegerSymType, "int"); + check(_DoubleSymType, "double"); + check(_FloatSymType, "float"); + check(_ShortSymType, "short"); + check(_LongSymType, "long"); + check(_BooleanSymType, "boolean"); + check(_ByteSymType, "byte"); + check(_CharacterSymType, "char"); + assertNoFindings(); + } + + @Test + public void doNotUnboxToUnknownTypes() { + //remove double + _doubleSymType.getTypeInfo().getEnclosingScope() + .remove(_doubleSymType.getTypeInfo()); + // as double does not exists, java.lang.Double remains java.Lang.Double + check(_DoubleSymType, "java.lang.Double"); + // same with Collections + _unboxedMapSymType.getTypeInfo().getEnclosingScope() + .remove(_unboxedMapSymType.getTypeInfo()); + check(createGenerics(_boxedMapSymType.getTypeInfo(), _IntegerSymType, _DoubleSymType), + "java.util.Map"); + } + + @Test + public void unboxObjects() { + check(createTypeObject(_boxedString.getTypeInfo()), "String"); + } + + @Test + public void unboxCollections() { + check(createGenerics(_boxedOptionalSymType.getTypeInfo(), _IntegerSymType), + "Optional"); + check(createGenerics(_boxedSetSymType.getTypeInfo(), _IntegerSymType), + "Set"); + check(createGenerics(_boxedListSymType.getTypeInfo(), _IntegerSymType), + "List"); + check(createGenerics(_boxedMapSymType.getTypeInfo(), _IntegerSymType, _DoubleSymType), + "Map"); + assertNoFindings(); + } + + @Test + public void unboxComplexTypes() { + ICombineExpressionsWithLiteralsGlobalScope gs = + CombineExpressionsWithLiteralsMill.globalScope(); + TypeSymbol person = type("Person"); + TypeVarSymbol tVar = typeVariable("T"); + check( + createTypeArray( + createGenerics(_boxedMapSymType.getTypeInfo(), + createUnion( + createTypeObject(person), + _intSymType, + _DoubleSymType + ), + createTypeArray( + createFunction( + createTypeObject(person), + createTypeVariable(tVar), + createGenerics(_boxedOptionalSymType.getTypeInfo(), + createTypeArray(_IntegerSymType, 1) + ) + ), 2 + ) + ), 1 + ), + "Map<(Person | double | int)," + + "(T, Optional) -> Person[][]>[]" + ); + } + + public void check(SymTypeExpression boxed, String expectedUnboxedName) { + SymTypeExpression unboxed = visitor.calculate(boxed); + assertNoFindings(); + assertEquals(expectedUnboxedName, unboxed.printFullName()); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types3/util/CombineExpressionsWithLiteralsTypeTraverserFactory.java b/monticore-grammar/src/test/java/de/monticore/types3/util/CombineExpressionsWithLiteralsTypeTraverserFactory.java new file mode 100644 index 0000000000..df1a4a49fe --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types3/util/CombineExpressionsWithLiteralsTypeTraverserFactory.java @@ -0,0 +1,171 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types3.util; + +import de.monticore.expressions.assignmentexpressions.types3.AssignmentExpressionsTypeVisitor; +import de.monticore.expressions.bitexpressions.types3.BitExpressionsTypeVisitor; +import de.monticore.expressions.combineexpressionswithliterals.CombineExpressionsWithLiteralsMill; +import de.monticore.expressions.combineexpressionswithliterals._visitor.CombineExpressionsWithLiteralsTraverser; +import de.monticore.expressions.commonexpressions.types3.CommonExpressionsTypeVisitor; +import de.monticore.expressions.expressionsbasis.types3.ExpressionBasisTypeVisitor; +import de.monticore.expressions.lambdaexpressions.types3.LambdaExpressionsTypeVisitor; +import de.monticore.literals.mccommonliterals.types3.MCCommonLiteralsTypeVisitor; +import de.monticore.types.mcarraytypes.types3.MCArrayTypesTypeVisitor; +import de.monticore.types.mcbasictypes.types3.MCBasicTypesTypeVisitor; +import de.monticore.types.mccollectiontypes.types3.MCCollectionTypesTypeVisitor; +import de.monticore.types.mcfullgenerictypes.types3.MCFullGenericTypesTypeVisitor; +import de.monticore.types.mcfunctiontypes.types3.MCFunctionTypesTypeVisitor; +import de.monticore.types.mcsimplegenerictypes.types3.MCSimpleGenericTypesTypeVisitor; +import de.monticore.types3.Type4Ast; + +public class CombineExpressionsWithLiteralsTypeTraverserFactory { + + public CombineExpressionsWithLiteralsTraverser createTraverser( + Type4Ast type4Ast + ) { + CombineExpressionsWithLiteralsTraverser traverser = + CombineExpressionsWithLiteralsMill.inheritanceTraverser(); + VisitorList visitors = constructVisitors(); + setType4Ast(visitors, type4Ast); + populateTraverser(visitors, traverser); + return traverser; + } + + public CombineExpressionsWithLiteralsTraverser createTraverserForOO( + Type4Ast type4Ast + ) { + CombineExpressionsWithLiteralsTraverser traverser = + CombineExpressionsWithLiteralsMill.inheritanceTraverser(); + VisitorList visitors = constructVisitorsForOO(); + setType4Ast(visitors, type4Ast); + populateTraverser(visitors, traverser); + return traverser; + } + + protected void setType4Ast(VisitorList visitors, Type4Ast type4Ast) { + // Expressions + visitors.derAssignmentExpressions.setType4Ast(type4Ast); + visitors.derBitExpressions.setType4Ast(type4Ast); + visitors.derCommonExpressions.setType4Ast(type4Ast); + visitors.derExpressionBasis.setType4Ast(type4Ast); + visitors.derLambdaExpressions.setType4Ast(type4Ast); + visitors.derCombineExpressionsWithLiterals.setType4Ast(type4Ast); + visitors.derOfMCCommonLiterals.setType4Ast(type4Ast); + // MCTypes + visitors.synMCArrayTypes.setType4Ast(type4Ast); + visitors.synMCBasicTypes.setType4Ast(type4Ast); + visitors.synMCCollectionTypes.setType4Ast(type4Ast); + visitors.synMCFullGenericTypes.setType4Ast(type4Ast); + visitors.synMCFunctionTypes.setType4Ast(type4Ast); + visitors.synMCSimpleGenericTypes.setType4Ast(type4Ast); + } + + // Expressions + + protected VisitorList constructVisitors() { + VisitorList visitors = new VisitorList(); + // Expressions + visitors.derAssignmentExpressions = new AssignmentExpressionsTypeVisitor(); + visitors.derBitExpressions = new BitExpressionsTypeVisitor(); + visitors.derCommonExpressions = new CommonExpressionsTypeVisitor(); + visitors.derExpressionBasis = new ExpressionBasisTypeVisitor(); + visitors.derLambdaExpressions = new LambdaExpressionsTypeVisitor(); + visitors.derCombineExpressionsWithLiterals = + new CombineExpressionsWithLiteralsTypeVisitor(); + visitors.derOfMCCommonLiterals = new MCCommonLiteralsTypeVisitor(); + // MCTypes + visitors.synMCArrayTypes = new MCArrayTypesTypeVisitor(); + visitors.synMCBasicTypes = new MCBasicTypesTypeVisitor(); + visitors.synMCCollectionTypes = new MCCollectionTypesTypeVisitor(); + visitors.synMCFullGenericTypes = new MCFullGenericTypesTypeVisitor(); + visitors.synMCFunctionTypes = new MCFunctionTypesTypeVisitor(); + visitors.synMCSimpleGenericTypes = new MCSimpleGenericTypesTypeVisitor(); + return visitors; + } + + /** + * initializes additional logic for languages that have access to OO Symbols + * + * @return + */ + protected VisitorList constructVisitorsForOO() { + VisitorList visitors = constructVisitors(); + WithinTypeBasicSymbolsResolver withinTypeBasicSymbolsResolver = + new OOWithinTypeBasicSymbolsResolver(); + NameExpressionTypeCalculator nameExpressionTypeCalculator = + new OONameExpressionTypeCalculator(); + visitors.derCommonExpressions.setWithinTypeBasicSymbolsResolver( + withinTypeBasicSymbolsResolver + ); + visitors.derCommonExpressions.setNameExpressionTypeCalculator( + nameExpressionTypeCalculator + ); + visitors.derExpressionBasis.setNameExpressionTypeCalculator( + nameExpressionTypeCalculator + ); + visitors.synMCBasicTypes.setWithinTypeResolver( + withinTypeBasicSymbolsResolver + ); + visitors.synMCBasicTypes.setNameExpressionTypeCalculator( + nameExpressionTypeCalculator + ); + return visitors; + } + + protected void populateTraverser( + VisitorList visitors, + CombineExpressionsWithLiteralsTraverser traverser + ) { + // Expressions + traverser.add4AssignmentExpressions(visitors.derAssignmentExpressions); + traverser.add4BitExpressions(visitors.derBitExpressions); + traverser.add4CommonExpressions(visitors.derCommonExpressions); + traverser.setCommonExpressionsHandler(visitors.derCommonExpressions); + traverser.add4ExpressionsBasis(visitors.derExpressionBasis); + traverser.add4LambdaExpressions(visitors.derLambdaExpressions); + traverser.add4CombineExpressionsWithLiterals(visitors.derCombineExpressionsWithLiterals); + traverser.add4MCCommonLiterals(visitors.derOfMCCommonLiterals); + // MCTypes + traverser.add4MCArrayTypes(visitors.synMCArrayTypes); + traverser.add4MCBasicTypes(visitors.synMCBasicTypes); + traverser.add4MCCollectionTypes(visitors.synMCCollectionTypes); + traverser.add4MCFullGenericTypes(visitors.synMCFullGenericTypes); + traverser.add4MCFunctionTypes(visitors.synMCFunctionTypes); + traverser.add4MCSimpleGenericTypes(visitors.synMCSimpleGenericTypes); + } + + /** + * POD + */ + protected static class VisitorList { + + // Expressions + + public AssignmentExpressionsTypeVisitor derAssignmentExpressions; + + public BitExpressionsTypeVisitor derBitExpressions; + + public CommonExpressionsTypeVisitor derCommonExpressions; + + public ExpressionBasisTypeVisitor derExpressionBasis; + + public LambdaExpressionsTypeVisitor derLambdaExpressions; + + public CombineExpressionsWithLiteralsTypeVisitor derCombineExpressionsWithLiterals; + + public MCCommonLiteralsTypeVisitor derOfMCCommonLiterals; + + // MCTypes + + public MCArrayTypesTypeVisitor synMCArrayTypes; + + public MCBasicTypesTypeVisitor synMCBasicTypes; + + public MCCollectionTypesTypeVisitor synMCCollectionTypes; + + public MCFullGenericTypesTypeVisitor synMCFullGenericTypes; + + public MCFunctionTypesTypeVisitor synMCFunctionTypes; + + public MCSimpleGenericTypesTypeVisitor synMCSimpleGenericTypes; + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/types3/util/CombineExpressionsWithLiteralsTypeTraverserProvider.java b/monticore-grammar/src/test/java/de/monticore/types3/util/CombineExpressionsWithLiteralsTypeTraverserProvider.java new file mode 100644 index 0000000000..143e9bc21b --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types3/util/CombineExpressionsWithLiteralsTypeTraverserProvider.java @@ -0,0 +1,142 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types3.util; + +import de.monticore.expressions.assignmentexpressions.types3.AssignmentExpressionsTypeVisitor; +import de.monticore.expressions.bitexpressions.types3.BitExpressionsTypeVisitor; +import de.monticore.expressions.combineexpressionswithliterals._visitor.CombineExpressionsWithLiteralsTraverser; +import de.monticore.expressions.commonexpressions.types3.CommonExpressionsTypeVisitor; +import de.monticore.expressions.expressionsbasis.types3.ExpressionBasisTypeVisitor; +import de.monticore.expressions.lambdaexpressions.types3.LambdaExpressionsTypeVisitor; +import de.monticore.literals.mccommonliterals.types3.MCCommonLiteralsTypeVisitor; +import de.monticore.types.mcarraytypes.types3.MCArrayTypesTypeVisitor; +import de.monticore.types.mcbasictypes.types3.MCBasicTypesTypeVisitor; +import de.monticore.types.mccollectiontypes.types3.MCCollectionTypesTypeVisitor; +import de.monticore.types.mcfullgenerictypes.types3.MCFullGenericTypesTypeVisitor; +import de.monticore.types.mcfunctiontypes.types3.MCFunctionTypesTypeVisitor; +import de.monticore.types.mcsimplegenerictypes.types3.MCSimpleGenericTypesTypeVisitor; +import de.monticore.types3.Type4Ast; + +/** + * @deprecated use {@link CombineExpressionsWithLiteralsTypeTraverserFactory} + */ +@Deprecated(forRemoval = true) +public class CombineExpressionsWithLiteralsTypeTraverserProvider { + + // Expressions + + protected AssignmentExpressionsTypeVisitor derAssignmentExpressions; + + protected BitExpressionsTypeVisitor derBitExpressions; + + protected CommonExpressionsTypeVisitor derCommonExpressions; + + protected ExpressionBasisTypeVisitor derExpressionBasis; + + protected LambdaExpressionsTypeVisitor derLambdaExpressions; + + protected CombineExpressionsWithLiteralsTypeVisitor derCombineExpressionsWithLiterals; + + protected MCCommonLiteralsTypeVisitor derOfMCCommonLiterals; + + // MCTypes + + protected MCArrayTypesTypeVisitor synMCArrayTypes; + + protected MCBasicTypesTypeVisitor synMCBasicTypes; + + protected MCCollectionTypesTypeVisitor synMCCollectionTypes; + + protected MCFullGenericTypesTypeVisitor synMCFullGenericTypes; + + protected MCFunctionTypesTypeVisitor synMCFunctionTypes; + + protected MCSimpleGenericTypesTypeVisitor synMCSimpleGenericTypes; + + public CombineExpressionsWithLiteralsTypeTraverserProvider() { + init(); + } + + protected void init() { + // Expressions + derAssignmentExpressions = new AssignmentExpressionsTypeVisitor(); + derBitExpressions = new BitExpressionsTypeVisitor(); + derCommonExpressions = new CommonExpressionsTypeVisitor(); + derExpressionBasis = new ExpressionBasisTypeVisitor(); + derLambdaExpressions = new LambdaExpressionsTypeVisitor(); + derCombineExpressionsWithLiterals = new CombineExpressionsWithLiteralsTypeVisitor(); + derOfMCCommonLiterals = new MCCommonLiteralsTypeVisitor(); + // MCTypes + synMCArrayTypes = new MCArrayTypesTypeVisitor(); + synMCBasicTypes = new MCBasicTypesTypeVisitor(); + synMCCollectionTypes = new MCCollectionTypesTypeVisitor(); + synMCFullGenericTypes = new MCFullGenericTypesTypeVisitor(); + synMCFunctionTypes = new MCFunctionTypesTypeVisitor(); + synMCSimpleGenericTypes = new MCSimpleGenericTypesTypeVisitor(); + } + + /** + * initializes additional logic for languages that have access to OO Symbols + */ + public void initForOOSymbols() { + init(); + WithinTypeBasicSymbolsResolver withinTypeBasicSymbolsResolver = + new OOWithinTypeBasicSymbolsResolver(); + NameExpressionTypeCalculator nameExpressionTypeCalculator = + new OONameExpressionTypeCalculator(); + derCommonExpressions.setWithinTypeBasicSymbolsResolver( + withinTypeBasicSymbolsResolver + ); + derCommonExpressions.setNameExpressionTypeCalculator( + nameExpressionTypeCalculator + ); + derExpressionBasis.setNameExpressionTypeCalculator( + nameExpressionTypeCalculator + ); + synMCBasicTypes.setWithinTypeResolver( + withinTypeBasicSymbolsResolver + ); + synMCBasicTypes.setNameExpressionTypeCalculator( + nameExpressionTypeCalculator + ); + } + + public CombineExpressionsWithLiteralsTraverser init( + CombineExpressionsWithLiteralsTraverser traverser) { + // Expressions + traverser.add4AssignmentExpressions(derAssignmentExpressions); + traverser.add4BitExpressions(derBitExpressions); + traverser.add4CommonExpressions(derCommonExpressions); + traverser.setCommonExpressionsHandler(derCommonExpressions); + traverser.add4ExpressionsBasis(derExpressionBasis); + traverser.add4LambdaExpressions(derLambdaExpressions); + traverser.add4CombineExpressionsWithLiterals(derCombineExpressionsWithLiterals); + traverser.add4MCCommonLiterals(derOfMCCommonLiterals); + // MCTypes + traverser.add4MCArrayTypes(synMCArrayTypes); + traverser.add4MCBasicTypes(synMCBasicTypes); + traverser.add4MCCollectionTypes(synMCCollectionTypes); + traverser.add4MCFullGenericTypes(synMCFullGenericTypes); + traverser.add4MCFunctionTypes(synMCFunctionTypes); + traverser.add4MCSimpleGenericTypes(synMCSimpleGenericTypes); + + return traverser; + } + + public void setType4Ast(Type4Ast type4Ast) { + // Expressions + derAssignmentExpressions.setType4Ast(type4Ast); + derBitExpressions.setType4Ast(type4Ast); + derCommonExpressions.setType4Ast(type4Ast); + derExpressionBasis.setType4Ast(type4Ast); + derLambdaExpressions.setType4Ast(type4Ast); + derCombineExpressionsWithLiterals.setType4Ast(type4Ast); + derOfMCCommonLiterals.setType4Ast(type4Ast); + // MCTypes + synMCArrayTypes.setType4Ast(type4Ast); + synMCBasicTypes.setType4Ast(type4Ast); + synMCCollectionTypes.setType4Ast(type4Ast); + synMCFullGenericTypes.setType4Ast(type4Ast); + synMCFunctionTypes.setType4Ast(type4Ast); + synMCSimpleGenericTypes.setType4Ast(type4Ast); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/types3/util/CombineExpressionsWithLiteralsTypeVisitor.java b/monticore-grammar/src/test/java/de/monticore/types3/util/CombineExpressionsWithLiteralsTypeVisitor.java new file mode 100644 index 0000000000..8f353674b8 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types3/util/CombineExpressionsWithLiteralsTypeVisitor.java @@ -0,0 +1,33 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types3.util; + +import de.monticore.expressions.combineexpressionswithliterals._ast.ASTExtReturnType; +import de.monticore.expressions.combineexpressionswithliterals._ast.ASTExtType; +import de.monticore.expressions.combineexpressionswithliterals._visitor.CombineExpressionsWithLiteralsVisitor2; +import de.monticore.types3.AbstractTypeVisitor; + +/** + * @deprecated only used because of the ASTExtType not being a MCType, etc. + * to be changed + */ +@Deprecated +public class CombineExpressionsWithLiteralsTypeVisitor extends AbstractTypeVisitor + implements CombineExpressionsWithLiteralsVisitor2 { + + @Override + public void endVisit(ASTExtType type){ + getType4Ast().internal_setTypeOfTypeIdentifier2( + type, + getType4Ast().internal_getPartialTypeOfTypeId2(type.getMCType()) + ); + } + + @Override + public void endVisit(ASTExtReturnType returnType){ + getType4Ast().internal_setTypeOfTypeIdentifier2( + returnType, + getType4Ast().getPartialTypeOfTypeId(returnType.getMCReturnType()) + ); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types3/util/DefsTypesForTests.java b/monticore-grammar/src/test/java/de/monticore/types3/util/DefsTypesForTests.java new file mode 100644 index 0000000000..48f3933f82 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types3/util/DefsTypesForTests.java @@ -0,0 +1,755 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types3.util; + +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.FunctionSymbol; +import de.monticore.symbols.basicsymbols._symboltable.IBasicSymbolsGlobalScope; +import de.monticore.symbols.basicsymbols._symboltable.IBasicSymbolsScope; +import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol; +import de.monticore.symbols.basicsymbols._symboltable.TypeVarSymbol; +import de.monticore.symbols.basicsymbols._symboltable.VariableSymbol; +import de.monticore.symbols.oosymbols.OOSymbolsMill; +import de.monticore.symbols.oosymbols._symboltable.FieldSymbol; +import de.monticore.symbols.oosymbols._symboltable.IOOSymbolsScope; +import de.monticore.symbols.oosymbols._symboltable.MethodSymbol; +import de.monticore.symbols.oosymbols._symboltable.OOTypeSymbol; +import de.monticore.symboltable.modifiers.AccessModifier; +import de.monticore.types.check.SymTypeExpression; +import de.monticore.types.check.SymTypeExpressionFactory; +import de.monticore.types.check.SymTypeObscure; +import de.monticore.types.check.SymTypeOfGenerics; +import de.monticore.types.check.SymTypeOfNull; +import de.monticore.types.check.SymTypeOfObject; +import de.monticore.types.check.SymTypePrimitive; +import de.monticore.types.check.SymTypeVariable; +import de.monticore.types.check.SymTypeVoid; + +import java.util.ArrayList; +import java.util.List; + +import static de.monticore.types.check.SymTypeExpressionFactory.createBottomType; +import static de.monticore.types.check.SymTypeExpressionFactory.createGenerics; +import static de.monticore.types.check.SymTypeExpressionFactory.createPrimitive; +import static de.monticore.types.check.SymTypeExpressionFactory.createTopType; +import static de.monticore.types.check.SymTypeExpressionFactory.createTypeObject; +import static de.monticore.types.check.SymTypeExpressionFactory.createTypeVariable; + +/** + * offers one Symbol-Infrastructure + * including Scopes etc. that is used to provide relevant Symbols. + * This infrastructure can be used for testing + */ +public class DefsTypesForTests { + + /** + * Initialization of the structure (can be called again to reinitialize). + */ + public static void setup() { + set_specialSymTypes(); + set_thePrimitives(); + set_boxedPrimitives(); + set_unboxedObjects(); + set_boxedObjects(); + set_unboxedCollections(); + set_boxedCollections(); + set_genericsRecursive(); + set_objectTypes(); + set_generics(); + set_bottomTopTypes(); + } + + /*********************************************************************/ + + /* + * Helpers that efficiently create Scopes + */ + public static IBasicSymbolsScope scope(String name) { + return scope(name, true); + } + + public static IBasicSymbolsScope scope(String name, boolean shadowing) { + IBasicSymbolsScope scope = BasicSymbolsMill.scope(); + scope.setName(name); + scope.setShadowing(shadowing); + scope.setExportingSymbols(true); + return scope; + } + + /*********************************************************************/ + + /** + * add a Scope to a Scope (bidirectional) + */ + public static T inScope(IBasicSymbolsScope p, T s) { + p.addSubScope(s); + return s; + } + + /** + * add a Type to a Scope (bidirectional) + */ + public static TypeSymbol inScope(IBasicSymbolsScope p, TypeSymbol s) { + s.setEnclosingScope(p); + s.getSpannedScope().setEnclosingScope(p); + p.add(s); + p.addSubScope(s.getSpannedScope()); + return s; + } + + public static OOTypeSymbol inScope(IOOSymbolsScope p, OOTypeSymbol s) { + s.setEnclosingScope(p); + s.getSpannedScope().setEnclosingScope(p); + p.add(s); + p.addSubScope(s.getSpannedScope()); + return s; + } + + /** + * add a Variable to a Scope (bidirectional) + */ + public static VariableSymbol inScope(IBasicSymbolsScope p, VariableSymbol s) { + s.setEnclosingScope(p); + p.add(s); + return s; + } + + public static FieldSymbol inScope(IOOSymbolsScope p, FieldSymbol s) { + s.setEnclosingScope(p); + p.add(s); + return s; + } + + /** + * add a Function to a Scope (bidirectional) + */ + public static FunctionSymbol inScope(IBasicSymbolsScope p, FunctionSymbol s) { + s.setEnclosingScope(p); + s.getSpannedScope().setEnclosingScope(p); + p.add(s); + p.addSubScope(s.getSpannedScope()); + return s; + } + + public static MethodSymbol inScope(IOOSymbolsScope p, MethodSymbol s) { + s.setEnclosingScope(p); + s.getSpannedScope().setEnclosingScope(p); + p.add(s); + p.addSubScope(s.getSpannedScope()); + return s; + } + + /** + * add a TypeVariable to a Scope (bidirectional) + */ + public static TypeVarSymbol inScope(IBasicSymbolsScope p, TypeVarSymbol s) { + s.setEnclosingScope(p); + p.add(s); + return s; + } + + /*********************************************************************/ + + /* + * Helpers that efficiently create Symbols + * (which by the way can also later be extended) + */ + + // create TypeSymbols (some defaults apply) + public static TypeSymbol type(String name) { + IBasicSymbolsScope scope = BasicSymbolsMill.scope(); + scope.setShadowing(true); + return BasicSymbolsMill.typeSymbolBuilder() + .setSpannedScope(scope) + .setName(name) + .setAccessModifier(AccessModifier.ALL_INCLUSION) + .build(); + } + + public static TypeSymbol type(String name, + List superTypeList) { + TypeSymbol ts = type(name); + ts.setSuperTypesList(superTypeList); + return ts; + } + + public static TypeSymbol type(String name, + List superTypeList, + List typeVariableList) { + TypeSymbol ts = type(name, superTypeList); + typeVariableList.forEach(tv -> inScope(ts.getSpannedScope(), tv)); + return ts; + } + + public static TypeSymbol type(String name, + List superTypeList, + List typeVariableList, + List functionList, + List variableList) { + TypeSymbol ts = type(name, superTypeList, typeVariableList); + functionList.forEach(fs -> inScope(ts.getSpannedScope(), fs)); + variableList.forEach(vs -> inScope(ts.getSpannedScope(), vs)); + return ts; + } + + // create OOTypeSymbols (some defaults apply) + + public static OOTypeSymbol oOtype(String name) { + IOOSymbolsScope scope = OOSymbolsMill.scope(); + scope.setShadowing(true); + return OOSymbolsMill.oOTypeSymbolBuilder() + .setSpannedScope(scope) + .setName(name) + .setIsPublic(true) + .setIsStatic(true) + .build(); + } + + public static OOTypeSymbol oOtype(String name, + List superTypeList) { + OOTypeSymbol ts = oOtype(name); + ts.setSuperTypesList(superTypeList); + return ts; + } + + public static OOTypeSymbol oOtype(String name, + List superTypeList, + List typeVariableList) { + OOTypeSymbol ts = oOtype(name, superTypeList); + typeVariableList.forEach(tv -> inScope(ts.getSpannedScope(), tv)); + return ts; + } + + public static OOTypeSymbol oOtype(String name, + List superTypeList, + List typeVariableList, + List methodList, + List fieldList) { + OOTypeSymbol ts = oOtype(name, superTypeList, typeVariableList); + methodList.forEach(fs -> inScope(ts.getSpannedScope(), fs)); + fieldList.forEach(vs -> inScope(ts.getSpannedScope(), vs)); + return ts; + } + + // create TypeVarSymbols (some defaults apply) + + public static TypeVarSymbol typeVariable(String name) { + return typeVariable(name, new ArrayList<>()); + } + + public static TypeVarSymbol typeVariable(String name, + List superTypeList) { + return BasicSymbolsMill.typeVarSymbolBuilder() + .setName(name) + .setSuperTypesList(superTypeList) + .setSpannedScope(BasicSymbolsMill.scope()) + .build(); + } + + // create FunctionSymbols (some defaults apply) + + public static FunctionSymbol function(String name, SymTypeExpression returnType, + SymTypeExpression... argumentTypes) { + return function(name, returnType, List.of(argumentTypes)); + } + + public static FunctionSymbol function(String name, SymTypeExpression returnType, + List argumentTypes) { + return function(name, returnType, argumentTypes, false); + } + + public static FunctionSymbol function(String name, SymTypeExpression returnType, + List argumentTypes, boolean elliptic) { + IBasicSymbolsScope scope = BasicSymbolsMill.scope(); + scope.setOrdered(true); + scope.setShadowing(true); + for (int i = 0; i < argumentTypes.size(); i++) { + scope.add( + BasicSymbolsMill.variableSymbolBuilder() + .setType(argumentTypes.get(i)) + .setName("arg" + i) + .build() + ); + } + return BasicSymbolsMill.functionSymbolBuilder() + .setSpannedScope(scope) + .setName(name) + .setAccessModifier(AccessModifier.ALL_INCLUSION) + .setType(returnType) + .setIsElliptic(elliptic) + .build(); + } + + // create MethodSymbols (some defaults apply) + + public static MethodSymbol method(String name, SymTypeExpression returnType, + SymTypeExpression... argumentTypes) { + return method(name, returnType, List.of(argumentTypes)); + } + + public static MethodSymbol method(String name, SymTypeExpression returnType, + List argumentTypes) { + return method(name, returnType, argumentTypes, false); + } + + public static MethodSymbol method(String name, SymTypeExpression returnType, + List argumentTypes, boolean elliptic) { + IOOSymbolsScope scope = OOSymbolsMill.scope(); + scope.setOrdered(true); + scope.setShadowing(true); + for (int i = 0; i < argumentTypes.size(); i++) { + scope.add( + OOSymbolsMill.fieldSymbolBuilder() + .setType(argumentTypes.get(i)) + .setName("arg" + i) + .build() + ); + } + return OOSymbolsMill.methodSymbolBuilder() + .setSpannedScope(scope) + .setName(name) + .setType(returnType) + .setIsElliptic(elliptic) + .setIsPublic(true) + .setIsStatic(false) + .build(); + } + + // create VariableSymbols (some defaults apply) + + public static VariableSymbol variable(String name, SymTypeExpression type) { + return BasicSymbolsMill.variableSymbolBuilder() + .setName(name) + .setAccessModifier(AccessModifier.ALL_INCLUSION) + .setType(type) + .build(); + } + + // create FieldSymbols (some defaults apply) + + public static FieldSymbol field(String name, SymTypeExpression type) { + return OOSymbolsMill.fieldSymbolBuilder() + .setName(name) + .setType(type) + .setIsPublic(true) + .setIsStatic(false) + .build(); + } + + /*********************************************************************/ + + /* + * This is the predefined Symbol for 'special' types like void + * these are usually the ones with a fixed amount of values + */ + + public static SymTypeOfNull _nullSymType; + + public static SymTypeVoid _voidSymType; + + public static SymTypeObscure _obscureSymType; + + public static void set_specialSymTypes() { + _nullSymType = SymTypeExpressionFactory.createTypeOfNull(); + _voidSymType = SymTypeExpressionFactory.createTypeVoid(); + _obscureSymType = SymTypeExpressionFactory.createObscureType(); + } + + /*********************************************************************/ + + /* + * This is the predefined Symbol for all Primitives, such as "int" + * which has empty Variables and Functions + */ + + public static SymTypePrimitive _intSymType; + + public static SymTypePrimitive _charSymType; + + public static SymTypePrimitive _booleanSymType; + + public static SymTypePrimitive _doubleSymType; + + public static SymTypePrimitive _floatSymType; + + public static SymTypePrimitive _longSymType; + + public static SymTypePrimitive _byteSymType; + + public static SymTypePrimitive _shortSymType; + + public static void set_thePrimitives() { + IBasicSymbolsGlobalScope typeSymbolsScope = BasicSymbolsMill.globalScope(); + if (typeSymbolsScope.resolveType(BasicSymbolsMill.INT).isEmpty()) { + BasicSymbolsMill.initializePrimitives(); + } + _intSymType = createPrimitive( + typeSymbolsScope.resolveType(BasicSymbolsMill.INT).get()); + _charSymType = createPrimitive( + typeSymbolsScope.resolveType(BasicSymbolsMill.CHAR).get()); + _booleanSymType = createPrimitive( + typeSymbolsScope.resolveType(BasicSymbolsMill.BOOLEAN).get()); + _doubleSymType = createPrimitive( + typeSymbolsScope.resolveType(BasicSymbolsMill.DOUBLE).get()); + _floatSymType = createPrimitive( + typeSymbolsScope.resolveType(BasicSymbolsMill.FLOAT).get()); + _longSymType = createPrimitive( + typeSymbolsScope.resolveType(BasicSymbolsMill.LONG).get()); + _byteSymType = createPrimitive( + typeSymbolsScope.resolveType(BasicSymbolsMill.BYTE).get()); + _shortSymType = createPrimitive( + typeSymbolsScope.resolveType(BasicSymbolsMill.SHORT).get()); + } + + /*********************************************************************/ + + /* + * These are the predefined Symbol for boxed Primitives, such as "Integer" + */ + + public static SymTypeOfObject _IntegerSymType; + + public static SymTypeOfObject _CharacterSymType; + + public static SymTypeOfObject _BooleanSymType; + + public static SymTypeOfObject _DoubleSymType; + + public static SymTypeOfObject _FloatSymType; + + public static SymTypeOfObject _LongSymType; + + public static SymTypeOfObject _ByteSymType; + + public static SymTypeOfObject _ShortSymType; + + public static void set_boxedPrimitives() { + // add java.lang scope + IBasicSymbolsScope javaScope = scope("java"); + IBasicSymbolsScope langScope = inScope(javaScope, scope("lang")); + BasicSymbolsMill.globalScope().addSubScope(javaScope); + // create boxed primitives + _IntegerSymType = + createTypeObject(inScope(langScope, type("Integer"))); + _CharacterSymType = + createTypeObject(inScope(langScope, type("Character"))); + _BooleanSymType = + createTypeObject(inScope(langScope, type("Boolean"))); + _DoubleSymType = + createTypeObject(inScope(langScope, type("Double"))); + _FloatSymType = + createTypeObject(inScope(langScope, type("Float"))); + _LongSymType = + createTypeObject(inScope(langScope, type("Long"))); + _ByteSymType = + createTypeObject(inScope(langScope, type("Byte"))); + _ShortSymType = + createTypeObject(inScope(langScope, type("Short"))); + } + + /*********************************************************************/ + + /* + * These are the predefined Symbol for unboxed Objects like "String" + */ + + public static SymTypeOfObject _unboxedString; + + public static void set_unboxedObjects() { + IBasicSymbolsGlobalScope gs = BasicSymbolsMill.globalScope(); + _unboxedString = createTypeObject(inScope(gs, type("String"))); + } + + /*********************************************************************/ + + /* + * These are the predefined Symbol for boxed Objects like "String" + */ + + public static SymTypeOfObject _boxedString; + + public static void set_boxedObjects() { + IBasicSymbolsGlobalScope gs = BasicSymbolsMill.globalScope(); + IBasicSymbolsScope javaScope = inScope(gs, scope("java")); + IBasicSymbolsScope langScope = inScope(javaScope, scope("lang")); + _boxedString = createTypeObject(inScope(langScope, type("String"))); + } + + /*********************************************************************/ + + /* + * These are the predefined Symbol for unboxed Collections like "List" + * They have separate setup function as different languages (s. OCL) + * have their own version of some of them + */ + + public static SymTypeOfGenerics _unboxedOptionalSymType; + + public static SymTypeOfGenerics _unboxedSetSymType; + + public static SymTypeOfGenerics _unboxedListSymType; + + public static SymTypeOfGenerics _unboxedMapSymType; + + public static void set_unboxedCollections() { + set_unboxedOptionalSymType(); + set_unboxedSetSymType(); + set_unboxedListSymType(); + set_unboxedMapSymType(); + } + + public static void set_unboxedOptionalSymType() { + IBasicSymbolsGlobalScope gs = BasicSymbolsMill.globalScope(); + TypeVarSymbol optVar = typeVariable("T"); + _unboxedOptionalSymType = createGenerics( + inScope(gs, type("Optional", List.of(), List.of(optVar))), + createTypeVariable(optVar) + ); + } + + public static void set_unboxedSetSymType() { + IBasicSymbolsGlobalScope gs = BasicSymbolsMill.globalScope(); + TypeVarSymbol setVar = typeVariable("T"); + _unboxedSetSymType = createGenerics( + inScope(gs, type("Set", List.of(), List.of(setVar))), + createTypeVariable(setVar) + ); + } + + public static void set_unboxedListSymType() { + IBasicSymbolsGlobalScope gs = BasicSymbolsMill.globalScope(); + TypeVarSymbol listVar = typeVariable("T"); + _unboxedListSymType = createGenerics( + inScope(gs, type("List", List.of(), List.of(listVar))), + createTypeVariable(listVar) + ); + } + + public static void set_unboxedMapSymType() { + IBasicSymbolsGlobalScope gs = BasicSymbolsMill.globalScope(); + TypeVarSymbol mapVar1 = typeVariable("T"); + TypeVarSymbol mapVar2 = typeVariable("U"); + _unboxedMapSymType = createGenerics( + inScope(gs, type("Map", List.of(), List.of(mapVar1, mapVar2))), + createTypeVariable(mapVar1), createTypeVariable(mapVar2) + ); + } + + /*********************************************************************/ + + /* + * These are the predefined Symbol for unboxed Collections like "List" + */ + + public static SymTypeOfGenerics _boxedOptionalSymType; + + public static SymTypeOfGenerics _boxedSetSymType; + + public static SymTypeOfGenerics _boxedListSymType; + + public static SymTypeOfGenerics _boxedMapSymType; + + public static void set_boxedCollections() { + IBasicSymbolsGlobalScope gs = BasicSymbolsMill.globalScope(); + IBasicSymbolsScope javaScope = inScope(gs, scope("java")); + IBasicSymbolsScope utilScope = inScope(javaScope, scope("util")); + TypeVarSymbol optVar = typeVariable("OptT"); + _boxedOptionalSymType = createGenerics( + inScope(utilScope, type("Optional", List.of(), List.of(optVar))), + createTypeVariable(optVar) + ); + TypeVarSymbol setVar = typeVariable("SetT"); + _boxedSetSymType = createGenerics( + inScope(utilScope, type("Set", List.of(), List.of(setVar))), + createTypeVariable(setVar) + ); + TypeVarSymbol listVar = typeVariable("ListT"); + _boxedListSymType = createGenerics( + inScope(utilScope, type("List", List.of(), List.of(listVar))), + createTypeVariable(listVar) + ); + TypeVarSymbol mapVar1 = typeVariable("KeyT"); + TypeVarSymbol mapVar2 = typeVariable("ValueT"); + _boxedMapSymType = createGenerics( + inScope(utilScope, type("Map", List.of(), List.of(mapVar1, mapVar2))), + createTypeVariable(mapVar1), createTypeVariable(mapVar2) + ); + } + + /*********************************************************************/ + + /* + * These are some predefined Symbols for recursively defined generic types + */ + + /** + * s. simple curiously recurring template pattern + */ + public static SymTypeOfGenerics _simpleCrtSymType; + + // Graph example out of Wild FJ (2015) + //class Node , E extends Edge> + //class Edge , E extends Edge> + //class Graph, E extends Edge> + + public static SymTypeOfGenerics _graphNodeSymType; + + public static SymTypeOfGenerics _graphEdgeSymType; + + public static SymTypeOfGenerics _graphSymType; + + public static void set_genericsRecursive() { + IBasicSymbolsGlobalScope gs = BasicSymbolsMill.globalScope(); + // SimpleCrt> + TypeVarSymbol crtVar = typeVariable("CrtT"); + _simpleCrtSymType = createGenerics( + inScope(gs, type("SimpleCrt", List.of(), List.of(crtVar))), + createTypeVariable(crtVar) + ); + crtVar.addSuperTypes(_simpleCrtSymType); + + // Graph example out of Wild FJ (2015) + TypeSymbol nodeSymbol = inScope(gs, type("Node")); + TypeSymbol edgeSymbol = inScope(gs, type("Edge")); + TypeSymbol graphSymbol = inScope(gs, type("Graph")); + //class Node , E extends Edge> + SymTypeVariable nodeNodeVar = createTypeVariable(typeVariable("NodeN")); + SymTypeVariable nodeEdgeVar = createTypeVariable(typeVariable("NodeE")); + nodeSymbol.getSpannedScope().add(nodeNodeVar.getTypeVarSymbol()); + nodeSymbol.getSpannedScope().add(nodeEdgeVar.getTypeVarSymbol()); + nodeNodeVar.getTypeVarSymbol().addSuperTypes(createGenerics( + nodeSymbol, List.of(nodeNodeVar, nodeEdgeVar) + )); + nodeEdgeVar.getTypeVarSymbol().addSuperTypes(createGenerics( + edgeSymbol, List.of(nodeNodeVar, nodeEdgeVar) + )); + _graphNodeSymType = createGenerics(nodeSymbol, nodeNodeVar, nodeEdgeVar); + //class Edge , E extends Edge> + SymTypeVariable edgeNodeVar = createTypeVariable(typeVariable("EdgeN")); + SymTypeVariable edgeEdgeVar = createTypeVariable(typeVariable("EdgeE")); + edgeSymbol.getSpannedScope().add(edgeNodeVar.getTypeVarSymbol()); + edgeSymbol.getSpannedScope().add(edgeEdgeVar.getTypeVarSymbol()); + edgeNodeVar.getTypeVarSymbol().addSuperTypes(createGenerics( + nodeSymbol, List.of(edgeNodeVar, edgeEdgeVar) + )); + edgeEdgeVar.getTypeVarSymbol().addSuperTypes(createGenerics( + edgeSymbol, List.of(edgeNodeVar, edgeEdgeVar) + )); + _graphEdgeSymType = createGenerics(edgeSymbol, edgeNodeVar, edgeEdgeVar); + //class Graph, E extends Edge> + SymTypeVariable graphNodeVar = createTypeVariable(typeVariable("GraphN")); + SymTypeVariable graphEdgeVar = createTypeVariable(typeVariable("GraphE")); + graphSymbol.getSpannedScope().add(graphNodeVar.getTypeVarSymbol()); + graphSymbol.getSpannedScope().add(graphEdgeVar.getTypeVarSymbol()); + graphNodeVar.getTypeVarSymbol().addSuperTypes(createGenerics( + nodeSymbol, List.of(graphNodeVar, graphEdgeVar) + )); + graphEdgeVar.getTypeVarSymbol().addSuperTypes(createGenerics( + edgeSymbol, List.of(graphNodeVar, graphEdgeVar) + )); + _graphSymType = createGenerics(graphSymbol, graphNodeVar, graphEdgeVar); + } + + /*********************************************************************/ + + /* + * These are some predefined Symbols for Object Types + */ + + public static SymTypeOfObject _personSymType; + + public static SymTypeOfObject _teachableSymType; + + // student is a subtype of person, teachable + public static SymTypeOfObject _studentSymType; + + // computer science student is a subtype of student; + public static SymTypeOfObject _csStudentSymType; + + // first semester computer science student + // is a subType of computer science student + public static SymTypeOfObject _firstSemesterCsStudentSymType; + + // child is a subtype of person, teachable + public static SymTypeOfObject _childSymType; + + // teacher is a subtype of person + public static SymTypeOfObject _teacherSymType; + + public static SymTypeOfObject _carSymType; + + public static SymTypeOfObject _schoolSymType; + + public static void set_objectTypes() { + IBasicSymbolsGlobalScope gs = BasicSymbolsMill.globalScope(); + _personSymType = createTypeObject(inScope(gs, type("Person"))); + _teachableSymType = createTypeObject(inScope(gs, type("Teachable"))); + _studentSymType = createTypeObject(inScope(gs, + type("Student", List.of(_personSymType, _teachableSymType))) + ); + _csStudentSymType = createTypeObject(inScope(gs, + type("CsStudent", List.of(_studentSymType))) + ); + _firstSemesterCsStudentSymType = createTypeObject(inScope(gs, + type("FirstSemesterCsStudent", List.of(_csStudentSymType))) + ); + _childSymType = createTypeObject(inScope(gs, + type("Child", List.of(_personSymType, _teachableSymType))) + ); + _teacherSymType = createTypeObject(inScope(gs, + type("Teacher", List.of(_personSymType))) + ); + _carSymType = createTypeObject(inScope(gs, type("Car"))); + _schoolSymType = createTypeObject(inScope(gs, type("School"))); + } + + /*********************************************************************/ + + /* + * These are the predefined Symbol for further generics + * in most cases, the collections ought to be enough + */ + + public static SymTypeOfGenerics _linkedListSymType; + + public static SymTypeOfGenerics _hashMapSymType; + + public static void set_generics() { + IBasicSymbolsGlobalScope gs = BasicSymbolsMill.globalScope(); + TypeVarSymbol listVar = typeVariable("LinkedListT"); + _linkedListSymType = createGenerics( + inScope(gs, type("LinkedList", + List.of(createGenerics(_boxedListSymType.getTypeInfo(), + createTypeVariable(listVar))), + List.of(listVar) + )) + ); + TypeVarSymbol mapKVar = typeVariable("HashMapK"); + TypeVarSymbol mapVVar = typeVariable("HashMapV"); + _hashMapSymType = createGenerics( + inScope(gs, type("HashMap", + List.of(createGenerics( + _boxedMapSymType.getTypeInfo(), + createTypeVariable(mapKVar), + createTypeVariable(mapVVar)) + ), + List.of(mapKVar, mapVVar)) + ) + ); + } + + /*********************************************************************/ + + /* + * These are predefined symbols for bottom and top types + */ + + public static SymTypeExpression _bottomType; + + public static SymTypeExpression _topType; + + public static void set_bottomTopTypes() { + _bottomType = createBottomType(); + _topType = createTopType(); + } + +} diff --git a/monticore-grammar/src/test/java/de/monticore/types3/util/DefsVariablesForTests.java b/monticore-grammar/src/test/java/de/monticore/types3/util/DefsVariablesForTests.java new file mode 100644 index 0000000000..98c1411384 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/types3/util/DefsVariablesForTests.java @@ -0,0 +1,222 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.types3.util; + +import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.symbols.basicsymbols._symboltable.IBasicSymbolsScope; +import de.monticore.symbols.basicsymbols._symboltable.VariableSymbol; + +import static de.monticore.types.check.SymTypeExpressionFactory.createGenerics; +import static de.monticore.types.check.SymTypeExpressionFactory.createTypeObject; +import static de.monticore.types3.util.DefsTypesForTests.*; + +/** + * creates default variables for type check tests, + * structure of this class is analoge to {@link DefsTypesForTests}, + * each variable represents one type in {@link DefsTypesForTests} + * (as far as applicable, e.g., one cannot create a variable of the null-type). + * The name of the variable is "var[typeArguments][Boxed?][NameOfType]", + * e.g., vardouble, varDouble, varBoxedString, varintBoxedList; + * In case of boxed reference types (e.g., String, List), + * the "Boxed" is added before the name of tne type ("[...]BoxedList") + * In case of generics with type parameters, "int" is added after "var" + * and the type arguments are "int", e.g., "varintList, varintBoxedMap" + *

+ * This class is NOT intended to have variables for all tests, + * if specific variables are needed + * (e.g., specific generics or two variables of the same type), + * they ought to be added in the specific test. + */ +public class DefsVariablesForTests { + + /** + * initializes default variables in the scope + */ + public static void setup() { + setup(BasicSymbolsMill.globalScope()); + } + + /** + * initializes default variables in the scope + */ + public static void setup(IBasicSymbolsScope scope) { + set_thePrimitives(scope); + set_boxedPrimitives(scope); + set_unboxedObjects(scope); + set_boxedObjects(scope); + set_unboxedCollections(scope); + set_boxedCollections(scope); + set_objectTypes(scope); + set_generics(scope); + } + + public static VariableSymbol _booleanVarSym; + + public static VariableSymbol _byteVarSym; + + public static VariableSymbol _charVarSym; + + public static VariableSymbol _shortVarSym; + + public static VariableSymbol _intVarSym; + + public static VariableSymbol _longVarSym; + + public static VariableSymbol _floatVarSym; + + public static VariableSymbol _doubleVarSym; + + public static void set_thePrimitives(IBasicSymbolsScope scope) { + _booleanVarSym = inScope(scope, variable("varboolean", _booleanSymType)); + _byteVarSym = inScope(scope, variable("varbyte", _byteSymType)); + _charVarSym = inScope(scope, variable("varchar", _charSymType)); + _shortVarSym = inScope(scope, variable("varshort", _shortSymType)); + _intVarSym = inScope(scope, variable("varint", _intSymType)); + _longVarSym = inScope(scope, variable("varlong", _longSymType)); + _floatVarSym = inScope(scope, variable("varfloat", _floatSymType)); + _doubleVarSym = inScope(scope, variable("vardouble", _doubleSymType)); + } + + public static VariableSymbol _BooleanVarSym; + + public static VariableSymbol _ByteVarSym; + + public static VariableSymbol _CharacterVarSym; + + public static VariableSymbol _ShortVarSym; + + public static VariableSymbol _IntegerVarSym; + + public static VariableSymbol _LongVarSym; + + public static VariableSymbol _FloatVarSym; + + public static VariableSymbol _DoubleVarSym; + + public static void set_boxedPrimitives(IBasicSymbolsScope scope) { + _BooleanVarSym = inScope(scope, variable("varBoolean", _BooleanSymType)); + _ByteVarSym = inScope(scope, variable("varByte", _ByteSymType)); + _CharacterVarSym = inScope(scope, variable("varCharacter", _CharacterSymType)); + _ShortVarSym = inScope(scope, variable("varShort", _ShortSymType)); + _IntegerVarSym = inScope(scope, variable("varInteger", _IntegerSymType)); + _LongVarSym = inScope(scope, variable("varLong", _LongSymType)); + _FloatVarSym = inScope(scope, variable("varFloat", _FloatSymType)); + _DoubleVarSym = inScope(scope, variable("varDouble", _DoubleSymType)); + } + + public static VariableSymbol _unboxedStringVarSym; + + public static void set_unboxedObjects(IBasicSymbolsScope scope) { + _unboxedStringVarSym = inScope(scope, variable("varString", _unboxedString)); + } + + public static VariableSymbol _boxedStringVarSym; + + public static void set_boxedObjects(IBasicSymbolsScope scope) { + _boxedStringVarSym = inScope(scope, variable("varBoxedString", _boxedString)); + } + + /* + * These are the predefined Symbol for unboxed Collections like "List" + */ + + public static VariableSymbol _intUnboxedOptionalVarSym; + + public static VariableSymbol _intUnboxedSetVarSym; + + public static VariableSymbol _intUnboxedListVarSym; + + public static VariableSymbol _intUnboxedMapVarSym; + + public static void set_unboxedCollections(IBasicSymbolsScope scope) { + _intUnboxedOptionalVarSym = inScope(scope, variable("varintOptional", + createGenerics(_unboxedOptionalSymType.getTypeInfo(), _intSymType))); + _intUnboxedSetVarSym = inScope(scope, variable("varintSet", + createGenerics(_unboxedSetSymType.getTypeInfo(), _intSymType))); + _intUnboxedListVarSym = inScope(scope, variable("varintList", + createGenerics(_unboxedListSymType.getTypeInfo(), _intSymType))); + _intUnboxedMapVarSym = inScope(scope, variable("varintMap", + createGenerics(_unboxedMapSymType.getTypeInfo(), _intSymType, _intSymType))); + } + + /* + * These are the predefined Symbol for unboxed Collections like "List" + */ + + public static VariableSymbol _boxedOptionalVarSym; + + public static VariableSymbol _boxedSetVarSym; + + public static VariableSymbol _boxedListVarSym; + + public static VariableSymbol _boxedMapVarSym; + + public static void set_boxedCollections(IBasicSymbolsScope scope) { + _boxedOptionalVarSym = inScope(scope, variable("varintBoxedOptional", + createGenerics(_boxedOptionalSymType.getTypeInfo(), _intSymType))); + _boxedSetVarSym = inScope(scope, variable("varintBoxedSet", + createGenerics(_boxedSetSymType.getTypeInfo(), _intSymType))); + _boxedListVarSym = inScope(scope, variable("varintBoxedList", + createGenerics(_boxedListSymType.getTypeInfo(), _intSymType))); + _boxedMapVarSym = inScope(scope, variable("varintBoxedMap", + createGenerics(_boxedMapSymType.getTypeInfo(), _intSymType, _intSymType))); + } + + /* + * These are some predefined Symbols for Object Types + */ + + public static VariableSymbol _personVarSym; + + public static VariableSymbol _teachableVarSym; + + public static VariableSymbol _studentVarSym; + + public static VariableSymbol _csStudentVarSym; + + public static VariableSymbol _firstSemesterCsStudentVarSym; + + public static VariableSymbol _childVarSym; + + public static VariableSymbol _teacherVarSym; + + public static VariableSymbol _carVarSym; + + public static VariableSymbol _schoolVarSym; + + public static void set_objectTypes(IBasicSymbolsScope scope) { + _personVarSym = inScope(scope, variable("varPerson", + createTypeObject(_personSymType.getTypeInfo()))); + _teachableVarSym = inScope(scope, variable("varTeachable", + createTypeObject(_teachableSymType.getTypeInfo()))); + _studentVarSym = inScope(scope, variable("varStudent", + createTypeObject(_studentSymType.getTypeInfo()))); + _csStudentVarSym = inScope(scope, variable("varCsStudent", + createTypeObject(_csStudentSymType.getTypeInfo()))); + _firstSemesterCsStudentVarSym = inScope(scope, variable("varFirstSemesterCsStudent", + createTypeObject(_firstSemesterCsStudentSymType.getTypeInfo()))); + _childVarSym = inScope(scope, variable("varChild", + createTypeObject(_childSymType.getTypeInfo()))); + _teacherVarSym = inScope(scope, variable("varTeacher", + createTypeObject(_teacherSymType.getTypeInfo()))); + _carVarSym = inScope(scope, variable("varCar", + createTypeObject(_carSymType.getTypeInfo()))); + _schoolVarSym = inScope(scope, variable("varSchool", + createTypeObject(_schoolSymType.getTypeInfo()))); + } + + /* + * These are the predefined Symbols for further generics + * in most cases, the collections ought to be enough + */ + + public static VariableSymbol _intLinkedListVarSym; + + public static VariableSymbol _intHashMapVarSym; + + public static void set_generics(IBasicSymbolsScope scope) { + _intLinkedListVarSym = inScope(scope, variable("varintLinkedList", + createGenerics(_linkedListSymType.getTypeInfo(), _intSymType))); + _intHashMapVarSym = inScope(scope, variable("varintHashMap", + createGenerics(_hashMapSymType.getTypeInfo(), _intSymType, _intSymType))); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/umlmodifier/ModifierBuilderTest.java b/monticore-grammar/src/test/java/de/monticore/umlmodifier/ModifierBuilderTest.java new file mode 100644 index 0000000000..f2532f6160 --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/umlmodifier/ModifierBuilderTest.java @@ -0,0 +1,35 @@ +/*(c) https://github.com/MontiCore/monticore*/ + +package de.monticore.umlmodifier; + +import de.monticore.umlmodifier._ast.ASTModifierBuilder; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + +public class ModifierBuilderTest { + + @Before + public void before() { + LogStub.init(); + Log.enableFailQuick(false); + } + + @Test + public void checkAllOptions() { + assertTrue(new ASTModifierBuilder().PUBLIC().build().isPublic()); + assertTrue(new ASTModifierBuilder().PRIVATE().build().isPrivate()); + assertTrue(new ASTModifierBuilder().PROTECTED().build().isProtected()); + assertTrue(new ASTModifierBuilder().FINAL().build().isFinal()); + assertTrue(new ASTModifierBuilder().ABSTRACT().build().isAbstract()); + assertTrue(new ASTModifierBuilder().LOCAL().build().isLocal()); + assertTrue(new ASTModifierBuilder().DERIVED().build().isDerived()); + assertTrue(new ASTModifierBuilder().READONLY().build().isReadonly()); + assertTrue(new ASTModifierBuilder().STATIC().build().isStatic()); + + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-grammar/src/test/java/de/monticore/umlmodifier/ModifierPrettyPrintTest.java b/monticore-grammar/src/test/java/de/monticore/umlmodifier/ModifierPrettyPrintTest.java new file mode 100644 index 0000000000..6f5661141e --- /dev/null +++ b/monticore-grammar/src/test/java/de/monticore/umlmodifier/ModifierPrettyPrintTest.java @@ -0,0 +1,47 @@ +/*(c) https://github.com/MontiCore/monticore*/ + +package de.monticore.umlmodifier; + +import de.monticore.umlmodifier._ast.ASTModifier; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class ModifierPrettyPrintTest { + + @Before + public void before() { + LogStub.init(); + Log.enableFailQuick(false); + UMLModifierMill.reset(); + UMLModifierMill.init(); + Log.clearFindings(); + } + + @Test + public void testLongFormsIndividual() { + assertEquals("public", UMLModifierMill.prettyPrint(UMLModifierMill.modifierBuilder().PUBLIC().build(), false)); + assertEquals("private", UMLModifierMill.prettyPrint(UMLModifierMill.modifierBuilder().PRIVATE().build(), false)); + assertEquals("protected", UMLModifierMill.prettyPrint(UMLModifierMill.modifierBuilder().PROTECTED().build(), false)); + assertEquals("final", UMLModifierMill.prettyPrint(UMLModifierMill.modifierBuilder().FINAL().build(), false)); + assertEquals("abstract", UMLModifierMill.prettyPrint(UMLModifierMill.modifierBuilder().ABSTRACT().build(), false)); + assertEquals("local", UMLModifierMill.prettyPrint(UMLModifierMill.modifierBuilder().LOCAL().build(), false)); + assertEquals("derived", UMLModifierMill.prettyPrint(UMLModifierMill.modifierBuilder().DERIVED().build(), false)); + assertEquals("readonly", UMLModifierMill.prettyPrint(UMLModifierMill.modifierBuilder().READONLY().build(), false)); + assertEquals("static", UMLModifierMill.prettyPrint(UMLModifierMill.modifierBuilder().STATIC().build(), false)); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testLong() { + assertEquals("public static", UMLModifierMill.prettyPrint(UMLModifierMill.modifierBuilder().PUBLIC().STATIC().build(), false)); + assertEquals("abstract readonly", UMLModifierMill.prettyPrint(UMLModifierMill.modifierBuilder().ABSTRACT().READONLY().build(), false)); + assertEquals("protected static", UMLModifierMill.prettyPrint(UMLModifierMill.modifierBuilder().PROTECTED().STATIC().build(), false)); + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-grammar/src/test/java/prettyprint/MCHexNumbersFullPrettyPrinter.java b/monticore-grammar/src/test/java/prettyprint/MCHexNumbersFullPrettyPrinter.java new file mode 100644 index 0000000000..e3273d559f --- /dev/null +++ b/monticore-grammar/src/test/java/prettyprint/MCHexNumbersFullPrettyPrinter.java @@ -0,0 +1,39 @@ +/* (c) https://github.com/MontiCore/monticore */ +package prettyprint; + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.prettyprint.MCBasicsPrettyPrinter; +import mchexnumbers.MCHexNumbersMill; +import mchexnumbers._ast.ASTMCHexNumbersNode; +import mchexnumbers._visitor.MCHexNumbersTraverser; + +@Deprecated(forRemoval = true) +public class MCHexNumbersFullPrettyPrinter extends MCNumbersFullPrettyPrinter { + + private MCHexNumbersTraverser traverser; + + protected IndentPrinter printer; + + public MCHexNumbersFullPrettyPrinter(IndentPrinter printer){ + super(printer); + this.traverser = MCHexNumbersMill.traverser(); + + MCHexNumbersPrettyPrinter hexNumbers = new MCHexNumbersPrettyPrinter(printer); + traverser.add4MCHexNumbers(hexNumbers); + traverser.setMCHexNumbersHandler(hexNumbers); + + MCNumbersPrettyPrinter numbers = new MCNumbersPrettyPrinter(printer); + traverser.add4MCNumbers(numbers); + traverser.setMCNumbersHandler(numbers); + + traverser.add4MCBasics(new MCBasicsPrettyPrinter(printer)); + } + + public MCHexNumbersTraverser getTraverser() { + return traverser; + } + + public void setTraverser(MCHexNumbersTraverser traverser) { + this.traverser = traverser; + } +} diff --git a/monticore-grammar/src/test/java/prettyprint/MCHexNumbersPrettyPrinter.java b/monticore-grammar/src/test/java/prettyprint/MCHexNumbersPrettyPrinter.java new file mode 100644 index 0000000000..6ef114a3f6 --- /dev/null +++ b/monticore-grammar/src/test/java/prettyprint/MCHexNumbersPrettyPrinter.java @@ -0,0 +1,55 @@ +/* (c) https://github.com/MontiCore/monticore */ +package prettyprint; + +import de.monticore.prettyprint.IndentPrinter; +import mchexnumbers._ast.ASTHexInteger; +import mchexnumbers._ast.ASTHexadecimal; +import mchexnumbers._ast.ASTMCHexNumbersNode; +import mchexnumbers._visitor.MCHexNumbersHandler; +import mchexnumbers._visitor.MCHexNumbersTraverser; +import mchexnumbers._visitor.MCHexNumbersVisitor2; + +@Deprecated(forRemoval = true) +public class MCHexNumbersPrettyPrinter implements MCHexNumbersVisitor2, MCHexNumbersHandler { + + protected MCHexNumbersTraverser traverser; + + private IndentPrinter printer = null; + + public MCHexNumbersPrettyPrinter(IndentPrinter printer) { + this.printer = printer; + } + + @Override + public MCHexNumbersTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(MCHexNumbersTraverser traverser) { + this.traverser = traverser; + } + + @Override + public void handle(ASTHexadecimal node) { + getPrinter().print(node.getSource()); + } + + @Override + public void handle(ASTHexInteger node) { + if (node.isNegative()) { + getPrinter().print("-"); + } + getPrinter().print(node.getHexadecimalpart()); + } + + public IndentPrinter getPrinter() { + return this.printer; + } + + public String prettyprint(ASTMCHexNumbersNode node) { + getPrinter().clearBuffer(); + node.accept(getTraverser()); + return getPrinter().getContent(); + } +} diff --git a/monticore-grammar/src/test/java/prettyprint/MCNumbersFullPrettyPrinter.java b/monticore-grammar/src/test/java/prettyprint/MCNumbersFullPrettyPrinter.java new file mode 100644 index 0000000000..9dd5ec00ac --- /dev/null +++ b/monticore-grammar/src/test/java/prettyprint/MCNumbersFullPrettyPrinter.java @@ -0,0 +1,50 @@ +/*(c) https://github.com/MontiCore/monticore*/ +package prettyprint; + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.prettyprint.MCBasicsPrettyPrinter; +import mcnumbers.MCNumbersMill; +import mcnumbers._ast.ASTMCNumbersNode; +import mcnumbers._visitor.MCNumbersTraverser; + +@Deprecated(forRemoval = true) +public class MCNumbersFullPrettyPrinter { + + private MCNumbersTraverser traverser; + + protected IndentPrinter printer; + + public MCNumbersFullPrettyPrinter(IndentPrinter printer){ + this.printer = printer; + this.traverser = MCNumbersMill.traverser(); + + MCNumbersPrettyPrinter numbers = new MCNumbersPrettyPrinter(printer); + traverser.add4MCNumbers(numbers); + traverser.setMCNumbersHandler(numbers); + + traverser.add4MCBasics(new MCBasicsPrettyPrinter(printer)); + } + + public MCNumbersTraverser getTraverser() { + return traverser; + } + + public void setTraverser(MCNumbersTraverser traverser) { + this.traverser = traverser; + } + + public IndentPrinter getPrinter() { + return printer; + } + + public void setPrinter(IndentPrinter printer) { + this.printer = printer; + } + + public String prettyprint(ASTMCNumbersNode node){ + getPrinter().clearBuffer(); + node.accept(getTraverser()); + return printer.getContent(); + } + +} diff --git a/monticore-grammar/src/test/java/prettyprint/MCNumbersPrettyPrinter.java b/monticore-grammar/src/test/java/prettyprint/MCNumbersPrettyPrinter.java new file mode 100644 index 0000000000..65d3cded8d --- /dev/null +++ b/monticore-grammar/src/test/java/prettyprint/MCNumbersPrettyPrinter.java @@ -0,0 +1,56 @@ +/* (c) https://github.com/MontiCore/monticore */ +package prettyprint; + +import de.monticore.prettyprint.IndentPrinter; +import mcnumbers._ast.ASTDecimal; +import mcnumbers._ast.ASTInteger; +import mcnumbers._ast.ASTMCNumbersNode; +import mcnumbers._visitor.MCNumbersHandler; +import mcnumbers._visitor.MCNumbersTraverser; +import mcnumbers._visitor.MCNumbersVisitor2; + +@Deprecated(forRemoval = true) +public class MCNumbersPrettyPrinter implements MCNumbersVisitor2, MCNumbersHandler { + + protected MCNumbersTraverser traverser; + + private IndentPrinter printer = null; + + public MCNumbersPrettyPrinter(IndentPrinter printer) { + this.printer = printer; + } + + @Override + public MCNumbersTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(MCNumbersTraverser traverser) { + this.traverser = traverser; + } + + @Override + public void handle(ASTDecimal node) { + getPrinter().print(node.getSource()); + } + + @Override + public void handle(ASTInteger node) { + if (node.isNegative()) { + getPrinter().print("-"); + } + getPrinter().print(node.getDecimalpart()); + } + + public IndentPrinter getPrinter() { + return this.printer; + } + + public String prettyprint(ASTMCNumbersNode node) { + getPrinter().clearBuffer(); + node.accept(getTraverser()); + return getPrinter().getContent(); + } + +} diff --git a/monticore-grammar/src/test/java/prettyprint/StringLiteralsFullPrettyPrinter.java b/monticore-grammar/src/test/java/prettyprint/StringLiteralsFullPrettyPrinter.java new file mode 100644 index 0000000000..ddfbe9f4f9 --- /dev/null +++ b/monticore-grammar/src/test/java/prettyprint/StringLiteralsFullPrettyPrinter.java @@ -0,0 +1,49 @@ +/*(c) https://github.com/MontiCore/monticore*/ +package prettyprint; + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.prettyprint.MCBasicsPrettyPrinter; +import stringliterals.StringLiteralsMill; +import stringliterals._ast.ASTStringLiteralsNode; +import stringliterals._visitor.StringLiteralsTraverser; + +@Deprecated(forRemoval = true) +public class StringLiteralsFullPrettyPrinter { + + private StringLiteralsTraverser traverser; + + protected IndentPrinter printer; + + public StringLiteralsFullPrettyPrinter(IndentPrinter printer){ + this.printer = printer; + this.traverser = StringLiteralsMill.traverser(); + + StringLiteralsPrettyPrinter stringLiterals = new StringLiteralsPrettyPrinter(printer); + traverser.add4StringLiterals(stringLiterals); + traverser.setStringLiteralsHandler(stringLiterals); + + traverser.add4MCBasics(new MCBasicsPrettyPrinter(printer)); + } + + public StringLiteralsTraverser getTraverser() { + return traverser; + } + + public void setTraverser(StringLiteralsTraverser traverser) { + this.traverser = traverser; + } + + public IndentPrinter getPrinter() { + return printer; + } + + public void setPrinter(IndentPrinter printer) { + this.printer = printer; + } + + public String prettyprint(ASTStringLiteralsNode node){ + getPrinter().clearBuffer(); + node.accept(getTraverser()); + return printer.getContent(); + } +} diff --git a/monticore-grammar/src/test/java/prettyprint/StringLiteralsPrettyPrinter.java b/monticore-grammar/src/test/java/prettyprint/StringLiteralsPrettyPrinter.java new file mode 100644 index 0000000000..17cf2271a5 --- /dev/null +++ b/monticore-grammar/src/test/java/prettyprint/StringLiteralsPrettyPrinter.java @@ -0,0 +1,53 @@ +/* (c) https://github.com/MontiCore/monticore */ +package prettyprint; + +import de.monticore.prettyprint.IndentPrinter; +import stringliterals._ast.ASTCharLiteral; +import stringliterals._ast.ASTStringLiteral; +import stringliterals._ast.ASTStringLiteralsNode; +import stringliterals._visitor.StringLiteralsHandler; +import stringliterals._visitor.StringLiteralsTraverser; +import stringliterals._visitor.StringLiteralsVisitor2; + +@Deprecated(forRemoval = true) +public class StringLiteralsPrettyPrinter implements StringLiteralsVisitor2, StringLiteralsHandler { + + protected StringLiteralsTraverser traverser; + + private IndentPrinter printer = null; + + public StringLiteralsPrettyPrinter(IndentPrinter printer) { + this.printer = printer; + } + + @Override + public StringLiteralsTraverser getTraverser() { + return traverser; + } + + @Override + public void setTraverser(StringLiteralsTraverser traverser) { + this.traverser = traverser; + } + + @Override + public void handle(ASTCharLiteral node) { + getPrinter().print("'" + node.getSource() + "'"); + } + + @Override + public void handle(ASTStringLiteral node) { + getPrinter().print("\"" + node.getSource() + "\""); + } + + public IndentPrinter getPrinter() { + return this.printer; + } + + public String prettyprint(ASTStringLiteralsNode node) { + getPrinter().clearBuffer(); + node.accept(getTraverser()); + return getPrinter().getContent(); + } + +} diff --git a/monticore-grammar/src/test/java/stringliterals/_prettyprint/StringLiteralsPrettyPrinter.java b/monticore-grammar/src/test/java/stringliterals/_prettyprint/StringLiteralsPrettyPrinter.java new file mode 100644 index 0000000000..8735529f7f --- /dev/null +++ b/monticore-grammar/src/test/java/stringliterals/_prettyprint/StringLiteralsPrettyPrinter.java @@ -0,0 +1,25 @@ +/* (c) https://github.com/MontiCore/monticore */ +package stringliterals._prettyprint; + +import de.monticore.prettyprint.IndentPrinter; +import stringliterals._ast.ASTCharLiteral; +import stringliterals._ast.ASTStringLiteral; + +public class StringLiteralsPrettyPrinter extends StringLiteralsPrettyPrinterTOP{ + public StringLiteralsPrettyPrinter(IndentPrinter printer, boolean printComments) { + super(printer, printComments); + } + + // We have to TOP-override these printing methods, + // as the default generator only supports MCCommonLiterals Chars/Strings + + @Override + public void handle(ASTCharLiteral node) { + getPrinter().print("'" + node.getSource() + "'"); + } + + @Override + public void handle(ASTStringLiteral node) { + getPrinter().print("\"" + node.getSource() + "\""); + } +} diff --git a/monticore-grammar/src/test/resources/Automaton.mc4 b/monticore-grammar/src/test/resources/Automaton.mc4 new file mode 100644 index 0000000000..913ae470c6 --- /dev/null +++ b/monticore-grammar/src/test/resources/Automaton.mc4 @@ -0,0 +1,41 @@ +/* (c) https://github.com/MontiCore/monticore */ + + +grammar Automaton extends de.monticore.common.TestLexicals { + +/** A ASTAutomaton represents a finite automaton + @attribute Name Name of the automaton + @attribute States List of states + @attribute Transitions List of transitions +*/ +symbol scope Automaton = + "automaton" Name "{" (State | Transition)* "}" ; + +/** A ASTState represents a state of a finite automaton + @attribute Name Name of state + @attribute Initial True if state is initial state + @attribute Final True if state is a final state + @attribute States List of sub states + @attribute Transitions List of transitions +*/ +symbol State = + "state" Name + + (("<<" ["initial"] ">>" ) | ("<<" ["final"] ">>" ))* + + ( ("{" (State | Transition)* "}") | ";") ; + + +/** A ASTTransition represents a transition + @attribute From Name of the state from which the transitions starts + @attribute Input Activation signal for this transition + @attribute To Name of the state to which the transitions goes +*/ +Transition = + from:Name@State "-" Name ">" to:Name@State ";"; + + + Transition2 extends Transition = + "foo"; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/Annotations.mc4 b/monticore-grammar/src/test/resources/de/monticore/Annotations.mc4 new file mode 100644 index 0000000000..4f4af45c66 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/Annotations.mc4 @@ -0,0 +1,19 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore; + +grammar Annotations { + + @Deprecated + interface A; + + @Override + B; + + @Override + @Deprecated("message") + abstract C; + + D = @Deprecated A | B; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/CombiningGrammar.mc4 b/monticore-grammar/src/test/resources/de/monticore/CombiningGrammar.mc4 new file mode 100644 index 0000000000..912b8af613 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/CombiningGrammar.mc4 @@ -0,0 +1,6 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore; + +grammar CombiningGrammar extends Automaton, de.monticore.inherited.Supergrammar { + NewProd = Name "new" Automaton X; +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/KeyAndNext.mc4 b/monticore-grammar/src/test/resources/de/monticore/KeyAndNext.mc4 new file mode 100644 index 0000000000..86abbe99d1 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/KeyAndNext.mc4 @@ -0,0 +1,28 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore; + +grammar KeyAndNext { + + A = key("b") {next("c")}? b:Name; + + B = b:key("b") {next("c")}? b:Name; + + C = key("b") b:["c"]; + + D = key("b") b:Name; + + E = b:key("b") b:Name; + + F = key("b"); + + G = b:key("b"); + + H = {next("b")}? Name b:["c"]; + + I = {next("b")}? b:Name "foo" b:Name?; + + token Name = + ( 'a'..'z' | 'A'..'Z' | '_' | '$' ) + ( 'a'..'z' | 'A'..'Z' | '_' | '0'..'9' | '$' )*; + } diff --git a/monticore-grammar/src/test/resources/de/monticore/Keywords.mc4 b/monticore-grammar/src/test/resources/de/monticore/Keywords.mc4 new file mode 100644 index 0000000000..a6a3a3c909 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/Keywords.mc4 @@ -0,0 +1,15 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore; + +grammar Keywords { + + A = "A"; + + B = "B"; + + replacekeyword "A": "a"; + + replacekeyword "B": "B", "x", "y", "z"; + + } diff --git a/monticore-grammar/src/test/resources/de/monticore/LexicalsWithMode.mc4 b/monticore-grammar/src/test/resources/de/monticore/LexicalsWithMode.mc4 new file mode 100644 index 0000000000..adb04784d1 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/LexicalsWithMode.mc4 @@ -0,0 +1,16 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore; + +component grammar LexicalsWithMode { + + token Name = + ( 'a'..'z' | 'A'..'Z' | '_' | '$' )( 'a'..'z' | 'A'..'Z' | '_' | '0'..'9' | '$' )*; + + token Digit(BLA_MODE) = + ('0' ..'9')+; + + token Digit2(BLA2_MODE) = + ('0' ..'9')+; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/Modes.mc4 b/monticore-grammar/src/test/resources/de/monticore/Modes.mc4 new file mode 100644 index 0000000000..dea931b30f --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/Modes.mc4 @@ -0,0 +1,17 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore; + +grammar Modes extends de.monticore.LexicalsWithMode { + + token Open = "[" : -> pushmode (FOO_MODE); + + token Close = "]" : ->popmode; + + token SLASH(FOO_MODE) = "/"; + + @Override + token Digit2 = + ('0' ..'9')+; + + } diff --git a/monticore-grammar/src/test/resources/de/monticore/NonTerminalsWithSameName.mc4 b/monticore-grammar/src/test/resources/de/monticore/NonTerminalsWithSameName.mc4 new file mode 100644 index 0000000000..05698c74ea --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/NonTerminalsWithSameName.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore; + +grammar NonTerminalsWithSameName { + + Transition = (arg:Argument ( "," arg:Argument)*); + + Argument = "arg"; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/RuleWithSymbolReference.mc4 b/monticore-grammar/src/test/resources/de/monticore/RuleWithSymbolReference.mc4 new file mode 100644 index 0000000000..558d023704 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/RuleWithSymbolReference.mc4 @@ -0,0 +1,20 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore; + +component grammar RuleWithSymbolReference { + + Name; + + interface symbol S; + + interface T extends S; + + A implements S = "a"; + + B = an:Name@T; + + external symbol E; + abstract symbol R; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/Statechart.mc4 b/monticore-grammar/src/test/resources/de/monticore/Statechart.mc4 new file mode 100644 index 0000000000..a5bfb0c5c1 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/Statechart.mc4 @@ -0,0 +1,38 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore; + +component grammar Statechart extends de.monticore.common.TestLexicals { + + Statechart implements SCStructure= "statechart" Name "{" (State | transition:Transition | userCode:Code)* "}"; + + + EntryAction= "entry" ":" block:BlockStatement; + + ExitAction= "exit" ":" block:BlockStatement; + + + State implements SCStructure = "state" Name ("<<" (initial:["initial"] | final:["final"])* ">>")? + ( ("{ options{}" (entryAction:EntryAction)? (exitAction:ExitAction)? (state:State | transition:Transition)* "}") | ";") ; + + Transition = from:Name "->" to:Name + (":" (event:Name ( "(" ((Argument || ",")+) ")" )? )? + ("[" guard: Expression "]")? + ("/" action: BlockStatement)? ";" + | ";"); + + Argument= paramType:Name paramName:Name; + + Code= "code" body: Classbody ; + + interface SCStructure; + + abstract AbstractAnything; + + external BlockStatement; + + external Expression; + + external Classbody; + + } diff --git a/monticore-grammar/src/test/resources/de/monticore/SubStatechart.mc4 b/monticore-grammar/src/test/resources/de/monticore/SubStatechart.mc4 new file mode 100644 index 0000000000..a8dba679d4 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/SubStatechart.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore; + +grammar SubStatechart extends de.monticore.Statechart { + + First = "Hello"; + Second = "Bye"; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/TestGrammar.mc4 b/monticore-grammar/src/test/resources/de/monticore/TestGrammar.mc4 new file mode 100644 index 0000000000..1edd920a5e --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/TestGrammar.mc4 @@ -0,0 +1,552 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore; + +/** The grammar describes the MontiCore grammar in its own format +*/ +component grammar TestGrammar extends de.monticore.common.TestLiterals { + + /** Used for embedding actions */ + external Action; + + /** Used as Expression in syntactic predicates */ + external ExpressionPredicate; + + /** Used for embedding concepts */ + external MCConcept; + + /** A MontiCore grammar describes a context free grammar in an own format + @attribute name The name of this grammar + @attribute supergrammar Supergrammars of this grammar + @attribute GrammarOption Options for this grammar + @attribute LexProd Lexical productions + @attribute ClassProd Class productions + @attribute EnumProd Enumeration productions + @attribute ExternalProd External productions + @attribute InterfaceProd List of all interface productions of this grammar + @attribute AbstractProd List of all abstract productions of this grammar + @attribute ASTRule List of all ast rules of this grammar + @attribute AssociationBlock List of all associations blocks of this grammars + @attribute Concepts List of additional concepts + */ + MCGrammar = + ("package" Package:(Name& || ".")+ ";")? + (ImportStatements:MCImportStatement)* + + (["component"])? "grammar" Name + ("extends" supergrammar:(GrammarReference || ",")+ )? + "{" + ( + GrammarOption + | + LexProd + | + ClassProd + | + EnumProd + | + ExternalProd + | + InterfaceProd + | + AbstractProd + | + ASTRule + | + Concept + )* + "}" ; + + MCImportStatement = "import" ImportList:Name& ( "." ImportList:Name&)* ("." Star:["*"])? ";" ; + + // One OptionBlock only + astrule MCGrammar = + ASTGrammarOption max=1 ; + + + /** A GrammarReference references another grammar + @attribute name Qualified name of the other grammar + */ + GrammarReference = + (Name& || ".")+ ; + + // ------------------------------------------------------------------------------ + // Options + + /** Options for a grammar + @attribute followOption Expression that shall be realized by lexer rules + @attribute genericOption Expression that shall be realized by lexer rules + */ + GrammarOption= + "options" "{" + ( + FollowOption + | + AntlrOption + )* + "}" ; + + /** A FollowOption specifies an additional follow set for a production + @attribute prodName name of the production + @attribute alt the follow set + */ + FollowOption = + "follow" prodName:Name Alt ";"; + + + /** A antlr option is an option that is passed on Anltr + itself + @attribute name name of the option + @attribute value optional value + */ + AntlrOption = + Name ("=" value:Name |"=" value:String)? ; + + // ------------------------------------------------------------------------------ + // Productions + + /** Productions form the syntactical structure of the grammar + */ + interface Prod; + + astrule Prod = + Name ; + + + /** ParserProduction are handled by the parser + */ + interface ParserProd extends Prod; + + // ------------------------------------------------------------------------------ + // Productions + + /** A LexProd is formed by the keyword ident in the beginning followed by an option + slash indicating that this LexProd is protected. The following name, options and + symbol are handed to antlr directly + @attribute Protected If true, this identifier is protected and call only be called by + other identifiers + @attribute Name Name of this identifier, only uppercase letters should be used + @attribute LexOption Options for lexer rule + @attribute InitAction Init actions for lexer rule + @attribute Alts List of alts for this lexer rule + @attribute Variable Variable used for mapping + (if type != null) or standard type (if type == null) + @attribute Type Type of the lexer tokens + @attribute Block Block used if other than standard mapping shall be used + */ + LexProd implements Prod = + (["fragment"] | ["comment"] )* + ("token") Name + ( + ( + LexOption ("{" InitAction:Action "}")? // Statements + | + "{" InitAction:Action "}" + )? + "=" Alts:(LexAlt || "|")+ + + (":" ("{" EndAction:Action "}")? (Variable:Name& + ("->" Type:( Name& || ".")+ (":" "{" Block:Action "}" )? )? )? // Statements + )? + ) + ";"; + + + /** An Enumeration-Production + @attribute name Name of the production + @attribute constant List of all constants + */ + EnumProd implements Prod = + "enum" Name "=" Constant ("|" Constant)* ";"; + + + /** A ExternalProduction represents a hole in the grammar where another production + can be plugged in + @attribute name Name of the hole + @attribute genericType External type used for this hole rule + */ + ExternalProd implements Prod = + "external" Name SymbolDefinition? GenericType? ";"; + + + /** An ASTInterfaceRule represents an explicit definition for an interface production + and may overwrite the standard generated rule which is mentioned in the productions + @attribute external True if this interface is programmed manually + @attribute name Name of the production + @attribute Parameter List of production parameters + @attribute superInterfaceRule Super interfaces + @attribute aSTSuperInterface Super interfaces abstract syntax only + @attribute overrideStandard True if interfaces is relevant + @attribute interfaces Ordered list of productions that are used for parsing + */ + InterfaceProd implements ParserProd = + "interface" Name SymbolDefinition? + ( + "extends" superInterfaceRule:(RuleReference || "," )+ + | + "astextends" aSTSuperInterface:(GenericType || ",")+ + )* + ";"; + + + /** An ASTInterfaceRule represents an explicit definition for an interface rule and + overwrites the standard generated rule which is mentioned in the rules + @attribute name Name of the production + @attribute Parameter List of production parameters + @attribute superInterfaceRule Super interfaces + @attribute aSTSuperInterface Super interfaces abstract syntax only + @attribute superRule Super productions + @attribute aSTSuperClass Super productions abstract syntax only + @attribute overrideStandard True if interfaces is relevant + @attribute interfaces Ordered list of productions that are used for parsing + */ + AbstractProd implements Prod = + "abstract" Name SymbolDefinition? + ( + "extends" superRule:(RuleReference || ",")+ + | + "implements" superInterfaceRule:(RuleReference || ",")+ + | + "astextends" aSTSuperClass:(GenericType || ",")+ + | + "astimplements" aSTSuperInterface:(GenericType || ",")+ + )* ";"; + + + /** A rule represents a rule (production) in a context free grammar. + @attribute name Name of the rule + @attribute type Type this rule contributes to + @attribute superrule Rule reference to a super rule + @attribute superInterfaceRule Rule reference to a super interface rule + @attribute aSTSuperClass Rule reference to a super rule used for abstract syntax only + @attribute aSTSuperInterfacerule Rule reference to a super rule used for abstract + syntax only + @attribute superInterfaceRule Rule reference to a super interface rule + @attribute ruleParameters List of formal Parameters handed to this rule + @attribute action Action executed priot to the rule body + @attribute alts List of alternatives representing the body of the rule + */ + ClassProd implements ParserProd = + Name SymbolDefinition? + ( + "extends" superRule:(RuleReference || ",")+ + | + "implements" superInterfaceRule:(RuleReference || ",")+ + | + "astextends" aSTSuperClass:(GenericType || ",")+ + | + "astimplements" aSTSuperInterface:(GenericType || ",")+ + )* + + ("{" Action "}")? // Statements + ("=" alts:(Alt || "|")+ )? ";"; + + astrule ClassProd = + method public String toString() { return name; } ; + + + /** Cardinality + @attribute True if unbounded cardinality (min and max are meaningless) + @attribute min Minimal cardinality + @attribute max Maximal cardinality + */ + Card = + ( + Unbounded:["*"] + | + "min" "=" Min:Num_Int + ("max" "=" ( Max:Num_Int | Max:"*"))? + | + "max" "=" ( Max:Num_Int | Max:"*") + ); + + + + /** A RuleReference refers to another production + */ + RuleReference = + SemanticpredicateOrAction? Name ; + + astrule RuleReference = + method public String getTypeName() { + return getName(); + } + method public boolean isExternal() { + return false; + }; + + // ------------------------------------------------------------------------------ + // Production body + + + /** An alternative represents an alternative in a rule or block and contains + (Rule)Components + @attribute Components List of the rule components of this alternative + */ + Alt = + Components:RuleComponent* ; + + + /** RuleComponents are parts of the alternatives + */ + interface RuleComponent; + + /** A NonTerminalSeparator is a shortcut for the description of list structures + @attribute variableName Name for a variable binding (Note: Only one attribute of + VariableName and UsuageName may have a value) + @attribute usageName Name for attribute + @attribute name The name of the nonterminal + @attribute separator The terminal as list separator + @attribute plusKeywords True indicates that all keywords are allowed as well + @attribute iteration Iteration of nonterminal + */ + NonTerminalSeparator implements RuleComponent = + (variableName:Name& "=" | usageName:Name& ":")? + "(" Name (plusKeywords:["&"])? "||" separator:String ")" Iteration:["*"|"+"]; + + + + /** A block is something used in rules which is surrounded by () + @attribute Option options for this look like a non-greedy behavior + @attribute Alts List of alternatives + @attribute Iteration Iteration of this block + */ + Block implements RuleComponent = + "(" + ( + ( + Option ( "init" "{" InitAction:Action "}" )? // Statements + | + "init" "{" InitAction:Action "}" // Statements + ) + ":" + )? + Alts:(Alt || "|")+ ")" + (Iteration:["?"|"*"|"+"])?; + + + /** Option values handed to Antlr + @attribute optionValue Value handes to Antlr + */ + Option = + "options" "{" OptionValue+ "}"; + + + /** Key/Value pairs handed to Antlr + @attribute key Key + @attribute value Value + */ + OptionValue = + key:Name "=" value:Name ";" ; + + + /** Reference to another rule + @attribute variableName Name for a variable binding (Note: Only one attribute of + VariableName and UsuageName may have a value) + @attribute usageName Name for attribute + @attribute plusKeywords True indicates that all keywords are allowed as well + @attribute iteration Iteration of nonterminal + */ + NonTerminal implements RuleComponent = + (variableName:Name&? "=" | usageName:Name&? ":")? + Name ("@" referencedSymbol:Name)? + (plusKeywords:["&"] | iteration:["?"|"*"|"+"] )*; + + + interface ITerminal; + + astrule ITerminal = + Name; + + /** A Terminal is usually something like "," or "start" */ + Terminal implements RuleComponent, ITerminal = + (variableName:Name& "=" | usageName:Name& ":")? + name:String + (iteration:["?"|"*"|"+"])*; + + + /** Constants are constant string but lead to an attribute */ + Constant implements ITerminal = + (humanName:Name& ":")? name:String ; + + /** constants are sth like keywords which one needs to know that they where there, + e.g. public. In the ast-class they are reflected as int oder boolean isMethods */ + ConstantGroup implements RuleComponent = + (variableName:Name& "=" | usageName:Name& ":")? + "[" Constant ("|" Constant )* "]" + (iteration:["?"|"*"|"+"])? ; + + /** MCAnything is a special lexer production which represents + */ + MCAnything implements RuleComponent = + "MCA"; + + Anything implements RuleComponent= + "."; + + /** Handed on as antlr action or predicate, realised by external JavaDSLParser */ + SemanticpredicateOrAction implements RuleComponent= + "{" ExpressionPredicate "}" Predicate:["?"] + | + "{" Action "}" + ; + + + // ------------------------------------------------------------------------------ + // Concept + + + /** The grammar can be extended by using concepts + @attribute name Name of the concept + @attribute concept The concept itself + */ + Concept= + "concept" Name& Concept:MCConcept; + + + // -------------------------------------------------------------------------------- + // AST + + + /** A ASTClassgenWithinGrammar additional information for the class generation + process + @attribute + @attribute type Type this rule refers to + @attribute aSTSuperInterface Super interfaces abstract syntax only + @attribute aSTSuperClass Super productions abstract syntax only + @attribute methods Methods to be added to the class generation + @attribute attributeInAST Attributes to be added to the class generation + @attribute method Sons to be added to the generation + */ + ASTRule = + "ast" type:Name + ( + "astextends" aSTSuperClass:(GenericType || ",")+ + | + "astimplements" aSTSuperInterface:(GenericType || ",")+ + )* + ( + "=" ( Method | AttributeInAST )* + )? ";"; + + + /** A method to be added to a certain ASTClass + @attribute public True if method is public + @attribute private True if method is private + @attribute protected True if method is protected + @attribute final True if method is final + @attribute static True if method is static + @attribute return Qualified name of the return type of the method + @attribute name Name of the method + @attribute parameter List of the parameters + @attribute exceptions Lit of the thrown exceptions + @attribute body Body of the Method (mostly a JavaLazy-Object) + */ + Method= + "method" + (["public"] | ["private"] | ["protected"])? + (["final"])? + (["static"])? + returnType:GenericType Name + "(" (MethodParameter || ",")* ")" + ("throws" exceptions:(GenericType || ",")+)? + "{" body:Action "}" ; // Statements + + + /** Formal parameter of a method + @attribute type Qualified name of the type + @attribute name Name of the parameter + */ + MethodParameter = + type:GenericType Name; + + + /** New Sons (=another ASTClass or String) or changed type (changed on difference to + the automatic inference rules) in ASTClass + @attribute name Name of the son + @attribute genericType Name of the referring type + @attribute Iterate True if more than one son can be added (=list) + @attribute card Cardinality + + */ + AttributeInAST = + (Name ":")? + GenericType (Iterated:["*"]|Optional:["?"])? + Card?; + + + /** + A GenericType represents an generic java type + @attribute name List containing the name of the type + @attribute genericType List of all type parameters + @attribute dimension Array dimension of the type + */ + GenericType = + (Name& || ".")+ + ("<" (GenericType || ",")+ ">")? + {_aNode.setDimension(0);} ("[" "]" + {_aNode.setDimension(_aNode.getDimension()+1);} )*; + + astrule GenericType = + Dimension:int + method public String toString() { + return de.monticore._grammar.HelperGrammar.printGenericType(this); + } + method public String getTypeName() { + return de.monticore._grammar.HelperGrammar.printGenericType(this); + } + method public boolean isExternal() { + return true; + }; + + // ------------------------------------------------------------------------------ + // Lexer + + LexAlt = + (lexComponents:LexComponent)*; + + interface LexComponent; + + LexBlock implements LexComponent = + (negate:["~"])? "(" + ( + ( + option:LexOption ("init" "{" initAction:Action "}")? // Statements + | + "init" "{" initAction:Action "}" // Statements + ) + ":" + )? + lexAlts:(LexAlt || "|")+ ")" + (iteration:["?"|"*"|"+"])?; + + LexCharRange implements LexComponent = + (negate:["~"])? lowerChar:Char ".." upperChar:Char; + + LexChar implements LexComponent = + (negate:["~"])? Char ; + + LexString implements LexComponent = + string:String ; + + LexActionOrPredicate implements LexComponent = + "{" ExpressionPredicate "}" Predicate:["?"] + ; + + + LexNonTerminal implements LexComponent = + (variable:Name& "=")? Name ; + + LexSimpleIteration implements LexComponent = + (LexString | LexChar) iteration:["?"|"*"|"+"]; + + LexOption = + "options" "{" iD:Name "=" value:Name ";" "}"; + + // ------------------------------------------------------------------------------ + // Symbol Table + + SymbolDefinition = + "@!" symbolKind:Name?; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/TypeReferences.mc4 b/monticore-grammar/src/test/resources/de/monticore/TypeReferences.mc4 new file mode 100644 index 0000000000..23fb0d5a6f --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/TypeReferences.mc4 @@ -0,0 +1,17 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore; + +grammar TypeReferences { + + interface A; + interface B extends A; + + + P implements B = "p"; + + Q implements C = "q"; + + interface C extends B; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/WrongPackage.mc4 b/monticore-grammar/src/test/resources/de/monticore/WrongPackage.mc4 new file mode 100644 index 0000000000..ce01ffc155 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/WrongPackage.mc4 @@ -0,0 +1,11 @@ + +/* (c) https://github.com/MontiCore/monticore */ + +package de.ronticore; + +grammar WrongPackage { + + Test = "test"; + +} + diff --git a/monticore-grammar/src/test/resources/de/monticore/aggregation/models/FooModel.mc4 b/monticore-grammar/src/test/resources/de/monticore/aggregation/models/FooModel.mc4 new file mode 100644 index 0000000000..e69de29bb2 diff --git a/monticore-grammar/src/test/resources/de/monticore/aggregation/models/blahmodel.mc4 b/monticore-grammar/src/test/resources/de/monticore/aggregation/models/blahmodel.mc4 new file mode 100644 index 0000000000..e69de29bb2 diff --git a/monticore-grammar/src/test/resources/de/monticore/common/TestLexicals.mc4 b/monticore-grammar/src/test/resources/de/monticore/common/TestLexicals.mc4 new file mode 100644 index 0000000000..b1578732af --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/common/TestLexicals.mc4 @@ -0,0 +1,57 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.common; + +component grammar TestLexicals { + + token Name = + ( 'a'..'z' | 'A'..'Z' | '_' | '$' )( 'a'..'z' | 'A'..'Z' | '_' | '0'..'9' | '$' )*; + + fragment token NEWLINE = + ('\r' '\n' | + '\r' | + '\n' ): + ; + + token WS = + (' ' | + '\t' | + '\r' '\n' | + '\r' | + '\n' ) :->skip; + + token SL_COMMENT = + "//" (~('\n' | + '\r' ) + )* + :{ _channel = HIDDEN; + if (getCompiler() != null) { + de.monticore.ast.Comment _comment = new de.monticore.ast.Comment(getText()); + de.se_rwth.commons.SourcePosition startPos = new de.se_rwth.commons.SourcePosition(_tokenStartLine, _tokenStartCharPositionInLine); + _comment.set_SourcePositionStart(startPos); + _comment.set_SourcePositionEnd(getCompiler().computeEndPosition(startPos, getText())); + getCompiler().addComment(_comment); + } + } + ; + + token ML_COMMENT = + "/*" ( + NEWLINE | + ~('*' | + '\n' | + '\r' ) + )* + "*/" + :{ _channel = HIDDEN; + if (getCompiler() != null) { + de.monticore.ast.Comment _comment = new de.monticore.ast.Comment(getText()); + de.se_rwth.commons.SourcePosition startPos = new de.se_rwth.commons.SourcePosition(_tokenStartLine, _tokenStartCharPositionInLine); + _comment.set_SourcePositionStart(startPos); + _comment.set_SourcePositionEnd(getCompiler().computeEndPosition(startPos, getText())); + getCompiler().addComment(_comment); + } + } + ; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/common/TestLiterals.mc4 b/monticore-grammar/src/test/resources/de/monticore/common/TestLiterals.mc4 new file mode 100644 index 0000000000..9e8be8988e --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/common/TestLiterals.mc4 @@ -0,0 +1,416 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.common; + +/** + * This grammar defines Java compliant literals. The scope of this grammar is to + * ease the reuse of literals structures in Java-like sublanguages, e.g., by + * grammar inheritance or grammar embedment. + * The grammar contains literals from Java, e.g., Boolean, Char, String, .... + * +*/ + +grammar TestLiterals extends de.monticore.common.TestLexicals { + + /*========================================================================*/ + /*======================= AST DEFINITIONS ================================*/ + /*========================================================================*/ + astrule BooleanLiteral = + method public boolean getValue() { + return this.source == ASTConstantsTestLiterals.TRUE; + } + ; + + astrule CharLiteral = + method public char getValue() { + try { + return de.monticore.literals.LiteralsHelper.getInstance().decodeChar(getSource()); + } + catch (Exception e) { + return ' '; + } + } + ; + + astrule StringLiteral = + method public String getValue() { + try { + return de.monticore.literals.LiteralsHelper.getInstance().decodeString(getSource()); + } + catch (Exception e) { + return ""; + } + } + ; + + astrule IntLiteral = + method public int getValue() { + try { + return de.monticore.literals.LiteralsHelper.getInstance().decodeInt(getSource()); + } + catch (NumberFormatException e) { + return 0; + } + } + ; + + astrule SignedIntLiteral = + method public int getValue() { + if (negative) { + return -super.getValue(); + } + return super.getValue(); + } + ; + + astrule LongLiteral = + method public long getValue() { + try { + return de.monticore.literals.LiteralsHelper.getInstance().decodeLong(getSource()); + } + catch (NumberFormatException e) { + return 0; + } + } + ; + + astrule SignedLongLiteral = + method public long getValue() { + if (negative) { + return -super.getValue(); + } + return super.getValue(); + } + ; + + astrule FloatLiteral = + method public float getValue() { + try { + return de.monticore.literals.LiteralsHelper.getInstance().decodeFloat(getSource()); + } + catch (NumberFormatException e) { + return 0.0f; + } + } + ; + + astrule SignedFloatLiteral = + method public float getValue() { + if (negative) { + return -super.getValue(); + } + return super.getValue(); + } + ; + + astrule DoubleLiteral = + method public double getValue() { + try { + return de.monticore.literals.LiteralsHelper.getInstance().decodeDouble(getSource()); + } + catch (NumberFormatException e) { + return 0.0; + } + } + ; + + astrule SignedDoubleLiteral = + method public double getValue() { + if (negative) { + return -super.getValue(); + } + return super.getValue(); + } + ; + + /*========================================================================*/ + /*======================= INTERFACE DEFINITIONS ==========================*/ + /*========================================================================*/ + + /** ASTLiteral is the interface for all literals (NullLiteral, + BooleanLiteral, CharLiteral, StringLiteral and all NumericLiterals) + */ + interface Literal; + + + /** ASTSignedLiteral is the interface for all literals (NullLiteral, + BooleanLiteral, CharLiteral, StringLiteral and all NumericLiterals). + Compared to Literal it also includes negative NumericLiterals + */ + interface SignedLiteral; + + + /** The interface ASTNumericLiteral combines the numeric literal types for + Integer, Long, Float and Double + */ + interface NumericLiteral extends Literal; + + + /** The interface ASTNumericLiteral combines the numeric literal types for + Integer, Long, Float and Double. + Compared to NumericLiteral it also includes negative numbers. + */ + interface SignedNumericLiteral extends SignedLiteral; + + + /*========================================================================*/ + /*============================ PARSER RULES ==============================*/ + /*========================================================================*/ + + /** ASTNullLiteral represents 'null' + */ + NullLiteral implements Literal, SignedLiteral = + "null"; + + + /** ASTBooleanLiteral represents "true" or "false" + @attribute source String-representation (including '"'). + */ + BooleanLiteral implements Literal, SignedLiteral = + source:["true" | "false"]; + + + /** ASTCharLiteral represents any valid character parenthesized with "'". + @attribute source String-representation (including "'"). + */ + CharLiteral implements Literal, SignedLiteral = + source:Char; + + + /** ASTStringLiteral represents any valid character sequence parenthesized + with '"'. + @attribute source String-representation (including '"'). + */ + StringLiteral implements Literal, SignedLiteral = + source:String; + + + /** ASTIntLiteral represents a positive Integer number. + @attribute source String-representation (including '"'). + */ + IntLiteral implements NumericLiteral = + source:Num_Int; + + + /** ASTSignedIntLiteral represents a positive or negative Integer number. + @attribute source String-representation (including '"'). + */ + SignedIntLiteral implements SignedNumericLiteral extends IntLiteral = + (negative:["-"])? source:Num_Int; + + + /** ASTLongLiteral represents a positive Long number. + @attribute source String-representation (including '"'). + */ + LongLiteral implements NumericLiteral = + source:Num_Long; + + + /** ASTSignedLongLiteral represents a positive or negative Long number. + @attribute source String-representation (including '"'). + */ + SignedLongLiteral extends LongLiteral implements SignedNumericLiteral = + (negative:["-"])? source:Num_Long; + + + /** ASTFloatLiteral represents a positive Float number. + @attribute source String-representation (including '"'). + */ + FloatLiteral implements NumericLiteral = + source:Num_Float; + + + /** ASTSignedFloatLiteral represents a positive or negative Float number. + @attribute source String-representation (including '"'). + */ + SignedFloatLiteral extends FloatLiteral implements SignedNumericLiteral = + (negative:["-"])? source:Num_Float; + + + /** ASTDoubleLiteral represents a positive Double number. + @attribute source String-representation (including '"'). + */ + DoubleLiteral implements NumericLiteral = + source:Num_Double; + + + /** ASTSignedDoubleLiteral represents a positive or negative Double number. + @attribute source String-representation (including '"'). + */ + SignedDoubleLiteral extends DoubleLiteral implements SignedNumericLiteral = + (negative:["-"])? source:Num_Double; + + + /*========================================================================*/ + /*============================ LEXER RULES ===============================*/ + /*========================================================================*/ + + + /*========================================================================*/ + /* The following section is adapted from */ + /* https://github.com/antlr/grammars-v4/blob/master/java/Java.g4 */ + /*========================================================================*/ + + // §3.10.1 Integer Literals + + token Num_Int + = DecimalIntegerLiteral | HexIntegerLiteral | OctalIntegerLiteral | BinaryIntegerLiteral; + + token Num_Long + = (DecimalIntegerLiteral IntegerTypeSuffix) | (HexIntegerLiteral IntegerTypeSuffix) | (OctalIntegerLiteral IntegerTypeSuffix) | (BinaryIntegerLiteral IntegerTypeSuffix); + + fragment token DecimalIntegerLiteral + = DecimalNumeral; + + fragment token HexIntegerLiteral + = HexNumeral; + + fragment token OctalIntegerLiteral + = OctalNumeral; + + fragment token BinaryIntegerLiteral + = BinaryNumeral; + + fragment token IntegerTypeSuffix + = 'l' | 'L'; + + fragment token DecimalNumeral + = '0' | NonZeroDigit ((Digits)? | Underscores Digits); + + fragment token Digits + = Digit ((DigitOrUnderscore)* Digit)?; + + fragment token Digit + = '0' | NonZeroDigit; + + fragment token NonZeroDigit + = '1'..'9' ; + + fragment token DigitOrUnderscore + = Digit | '_'; + + fragment token Underscores + = '_'+; + + fragment token HexNumeral + = '0' ('x' | 'X') HexDigits; + + fragment token HexDigits + = HexDigit ((HexDigitOrUnderscore)* HexDigit)?; + + fragment token HexDigit + = '0'..'9' | 'a'..'f' | 'A'..'F' ; + + fragment token HexDigitOrUnderscore + = HexDigit | '_'; + + fragment token OctalNumeral + = '0' (Underscores)? OctalDigits; + + fragment token OctalDigits + = OctalDigit ((OctalDigitOrUnderscore)* OctalDigit)?; + + fragment token OctalDigit + = '0'..'7' ; + + fragment token OctalDigitOrUnderscore + = OctalDigit | '_'; + + fragment token BinaryNumeral + = '0' ('b' | 'B') BinaryDigits; + + fragment token BinaryDigits + = BinaryDigit ((BinaryDigitOrUnderscore)* BinaryDigit)?; + + fragment token BinaryDigit + = '0' | '1'; + + fragment token BinaryDigitOrUnderscore + = BinaryDigit | '_'; + + + // §3.10.2 Floating-Point Literals + + token Num_Float + = DecimalFloatingPointLiteral | HexadecimalFloatingPointLiteral; + + token Num_Double + = DecimalDoublePointLiteral | HexadecimalDoublePointLiteral; + + fragment token DecimalDoublePointLiteral + = Digits '.' (Digits)? (ExponentPart)? (DoubleTypeSuffix)? | '.' Digits (ExponentPart)? (DoubleTypeSuffix)? | Digits ExponentPart (DoubleTypeSuffix)? | Digits DoubleTypeSuffix; + + fragment token DecimalFloatingPointLiteral + = Digits '.' (Digits)? (ExponentPart)? (FloatTypeSuffix) | '.' Digits (ExponentPart)? (FloatTypeSuffix) | Digits ExponentPart (FloatTypeSuffix) | Digits FloatTypeSuffix; + + fragment token ExponentPart + = ExponentIndicator SignedInteger; + + fragment token ExponentIndicator + = 'e' | 'E'; + + fragment token SignedInteger + = (Sign)? Digits; + + fragment token Sign + = '+' | '-'; + + fragment token FloatTypeSuffix + = 'f' | 'F'; + + fragment token DoubleTypeSuffix + = 'd' | 'D'; + + fragment token HexadecimalDoublePointLiteral + = HexSignificand BinaryExponent (DoubleTypeSuffix)?; + + fragment token HexadecimalFloatingPointLiteral + = HexSignificand BinaryExponent (FloatTypeSuffix); + + fragment token HexSignificand + = HexNumeral '.'? | '0' ('x' | 'X') (HexDigits)? '.' HexDigits; + + fragment token BinaryExponent + = BinaryExponentIndicator SignedInteger; + + fragment token BinaryExponentIndicator + = 'p' | 'P'; + + + // §3.10.4 Character Literals + token Char + = '\'' SingleCharacter '\'' | '\'' EscapeSequence '\''; + + fragment token SingleCharacter + = ~ ('\''); + + + // §3.10.5 String Literals + token String + = '"' (StringCharacters)? '"' ; + // TODO: Sollen die Quotes rausgeworfen werden? + // Es gibt in MC3 2 Varianten. STRING (ohne Quote) und String (mit Quote) + // So würde man das rauswerfen: + // : {setText(getText().substring(1, getText().length() - 1));}; + + fragment token StringCharacters + = (StringCharacter)+; + + fragment token StringCharacter + = ~ ('"') | EscapeSequence; + + // §3.10.6 Escape Sequences for Character and String Literals + fragment token EscapeSequence + = '\\' ('b' | 't' | 'n' | 'f' | 'r' | '"' | '\'' | '\\') | OctalEscape | UnicodeEscape; + + fragment token OctalEscape + = '\\' OctalDigit | '\\' OctalDigit OctalDigit | '\\' ZeroToThree OctalDigit OctalDigit; + + fragment token UnicodeEscape + = '\\' 'u' HexDigit HexDigit HexDigit HexDigit; + + fragment token ZeroToThree + = '0'..'3' ; + +} + diff --git a/monticore-grammar/src/test/resources/de/monticore/common/TestTypes.mc4 b/monticore-grammar/src/test/resources/de/monticore/common/TestTypes.mc4 new file mode 100644 index 0000000000..61a0dca5d2 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/common/TestTypes.mc4 @@ -0,0 +1,253 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.common; + +/** + * This grammar defines Java compliant types. The scope of this grammar is to + * ease the reuse of type structures in Java-like sublanguages, e.g., by grammar + * inheritance or grammar embedment. + * The grammar contains types from Java, e.g., primitives, void, types with + * dimensions, reference types, generics, and type parameters. + * +*/ +grammar TestTypes extends de.monticore.common.TestLiterals { + + /*========================================================================*/ + /*======================= AST Definition =================================*/ + /*========================================================================*/ + + astrule QualifiedName = + method public String toString(){ + return de.se_rwth.commons.Names.getQualifiedName( + this.getPartList()); + }; + + astrule PrimitiveType = + method public String toString(){ + if (isBoolean()){ + return "boolean"; + } + if (isByte()){ + return "byte"; + } + if (isChar()){ + return "char"; + } + if (isShort()){ + return "short"; + } + if (isInt()){ + return "int"; + } + if (isFloat()){ + return "float"; + } + if (isLong()){ + return "long"; + } + if (isDouble()){ + return "double"; + } + return ""; + } + method public boolean isBoolean(){ + return this.getPrimitive()==ASTConstantsTestTypes.BOOLEAN; + } + method public boolean isByte(){ + return this.getPrimitive()==ASTConstantsTestTypes.BYTE; + } + method public boolean isChar(){ + return this.getPrimitive()==ASTConstantsTestTypes.CHAR; + } + method public boolean isShort(){ + return this.getPrimitive()==ASTConstantsTestTypes.SHORT; + } + method public boolean isInt(){ + return this.getPrimitive()==ASTConstantsTestTypes.INT; + } + method public boolean isFloat(){ + return this.getPrimitive()==ASTConstantsTestTypes.FLOAT; + } + method public boolean isLong(){ + return this.getPrimitive()==ASTConstantsTestTypes.LONG; + } + method public boolean isDouble(){ + return this.getPrimitive()==ASTConstantsTestTypes.DOUBLE; + }; + + astrule ReferenceType astextends ASTType; + + astrule ArrayType astimplements ASTReferenceType = + componentType:ASTType + dimensions:int; + + /*========================================================================*/ + /*======================= INTERFACE DEFINITIONS ==========================*/ + /*========================================================================*/ + + /** ASTType defines types like primitives, Set, List, Collection, or + class types. It might also be an array or generic type. + */ + interface Type extends TypeArgument, ReturnType; + + /** ASTReferenceType defines a reference type like arrays or complex types. + */ + interface ReferenceType; + + /** ASTTypeArgument represents a type argument (generics). + */ + interface TypeArgument; + + /** ASTReturnType represents return types. + */ + interface ReturnType; + + + /*========================================================================*/ + /*============================ PARSER RULES ==============================*/ + /*========================================================================*/ + + /** The ASTQualifiedName represents a possibly qualified name in the AST. + The different parts of a qualified name are separated by '.'; they are + stored in an ASTStringList. + @attribute parts A List of ASTStringList concludes all name parts + */ + QualifiedName = + part:(Name& || ".")+; + + /** The ASTAbstractArrayType represents array of Type + */ + abstract ArrayType implements Type; + + /** The ASTArrayType represents an array of any type. The rule + ComplexArrayType itself treats all arrays except the primitive + ones. Especially it treats generic types. + @attribute componentType The kind of type which is used for the array. + Could be every complex type. + @attribute dimensions Counts the number of '[]' + */ + ComplexArrayType extends ArrayType implements Type + = + // Things are getting ugly. We have to disambiguate between + // Class>[] + // and + // Class[]> + // So we have to make sure that we only take the array dimensions if we + // are at the right level. If we would not, we wouldn't know that ">>" + // actually closes two levels and not just one as ">" does. It would + // then count the array dimensions to the inner type resulting in the + // same tree for each of the cases above. + componentType:ComplexReferenceType + ( + ( + ( + "[" "]" + {_aNode.setDimensions(_aNode.getDimensions()+1);} + )+ + ) + ); + + /** The ASTArrayType represents an array of any type. The rule + PrimitiveArrayType itself treats arrays of primitive types, such as + 'int[]'. + @attribute componentType The kind of which is used for the array. + Could be every primitive type. + @attribute dimensions Counts the number of '[]' + */ + PrimitiveArrayType extends ArrayType implements Type = + componentType:PrimitiveType + ( + "[" "]" + {_aNode.setDimensions(_aNode.getDimensions()+1);} + )+; + + /** ASTVoidType represents the return type "void". + */ + VoidType implements ReturnType = + "void"; + + /** ASTPrimitiveType represents every primitive type supported by Java. + @attribute primitive BOOLEAN, BYTE, CHAR, SHORT, INT, FLOAT, LONG, + or DOUBLE + */ + PrimitiveType implements Type = + primitive: ["boolean" | "byte" | "short" | "int" | "long" | "char" |"float" | "double"]; + + /** ASTSimpleReferenceType represents types like class or interface types + which could have a qualified name like this: a.b.c. The + qualification stored in the name (a.b) could be package or a type name. + The qualified name could contain type arguments only at the end. + a.b.c.d would be one ASTSimpleReferenceType (a.b.c) and one + ASTQualifiedType (d). + @attribute name Name of the type + Note: Although the class name contains the + word 'simple', the name could be a qualified + one. So it is saved in an ASTStringList + @attribute typeArguments The types between '<...>' + */ + SimpleReferenceType implements ReferenceType, Type = + Name + ("." Name&)* + (TypeArguments)?; + + /** ASTComplexReferenceType represents types like class or interface types which + always have ASTSimpleReferenceType as + qualification. + For example: + a.b.c.d.e + @attribute name Name of the type + @attribute typeArguments The types between '<...>' + @attribute qualification Another ASTQualifiedType or + ASTSimpleReferenceType. + */ + ComplexReferenceType implements ReferenceType, Type = + (SimpleReferenceType || ".")+; + + /** ASTTypeArguments represents a list of generic arguments parenthesized + by '<...>'. It is also possible to nest type arguments in each other + like this >>. + @attribute typeArguments List of arguments + */ + TypeArguments = + ( + "<" (TypeArgument || ",")* ">" + ); + + /** ASTWildcardType represents a wildcard type in a type argument (generics) + It could also contain an upper- or lower bound. + @attribute upperBound Supertye of the type argument + @attribute lowerBound Subtype of the type argument + */ + WildcardType implements TypeArgument = + "?" ( + ("extends" upperBound:Type) | ("super" lowerBound:Type) + )?; + + /** ASTTypeParameters represents a list of generic parameter parenthesized + by '<...>' in type declarations (e.g., class-, interface-, method-, or + constructor declarations). + @attribute typeVariableDeclarations List of parameters + */ + TypeParameters = + ( + "<" (TypeVariableDeclaration || ",")+ ">" + )?; + + /** ASTTypeVariableDeclaration represents the generic variable declaration + in '<'...'>' (e.g., in front of method or constructor declarations or + behind the class or interface name). + E.g.: public void test(T t) + @attribute name Name of the type variable + @attribute upperBounds Optional list of required super classes + */ + TypeVariableDeclaration = + Name + ( + "extends" upperBounds:(ComplexReferenceType || "&")+ + )?; + + /** ImportStatement represents the import list for diagrams + E.g.: import A.b; + E.g.: import A.*; + */ + ImportStatement = "import" importList:(Name& || ".")+ ("." Star:["*"])? ";" ; +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/SamePackage.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/SamePackage.mc4 new file mode 100644 index 0000000000..c1531abefa --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/SamePackage.mc4 @@ -0,0 +1,7 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar; + +component grammar SamePackage { + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/TestFullyQualifiedGrammar.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/TestFullyQualifiedGrammar.mc4 new file mode 100644 index 0000000000..96b8e9a4ec --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/TestFullyQualifiedGrammar.mc4 @@ -0,0 +1,7 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar; + +grammar TestFullyQualifiedGrammar extends de.monticore.grammar.SamePackage, de.monticore.grammar.pack.DifferentPackage { + A = "A"; +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/TestFullyQualifiedGrammarSamePackage.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/TestFullyQualifiedGrammarSamePackage.mc4 new file mode 100644 index 0000000000..f0712c9b7f --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/TestFullyQualifiedGrammarSamePackage.mc4 @@ -0,0 +1,7 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar; + +grammar TestFullyQualifiedGrammarSamePackage extends SamePackage, de.monticore.grammar.pack.DifferentPackage { + A = "A"; +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/TestQualifiedImportGrammar.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/TestQualifiedImportGrammar.mc4 new file mode 100644 index 0000000000..a13c2f704a --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/TestQualifiedImportGrammar.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar; + +import de.monticore.grammar.SamePackage; +import de.monticore.grammar.pack.DifferentPackage; + +grammar TestQualifiedImportGrammar extends SamePackage, DifferentPackage { + A = "A"; +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/TestStarImportGrammar.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/TestStarImportGrammar.mc4 new file mode 100644 index 0000000000..ef5ae181e3 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/TestStarImportGrammar.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar; + +import de.monticore.grammar.*; +import de.monticore.grammar.pack.*; + +grammar TestStarImportGrammar extends SamePackage, DifferentPackage { + A = "A"; +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0112/A0112.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0112/A0112.mc4 new file mode 100644 index 0000000000..f215b4348d --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0112/A0112.mc4 @@ -0,0 +1,13 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A0112; + +grammar A0112 { + + Foo = "foo"; + + Bar = "bar"; + + Bar = "foo"; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0112/A0112a.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0112/A0112a.mc4 new file mode 100644 index 0000000000..4b01a269da --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0112/A0112a.mc4 @@ -0,0 +1,13 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A0112; + +grammar A0112a { + + Foo = "foo"; + + interface Bar = "bar"; + + Bar = "foo"; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0112/A0112b.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0112/A0112b.mc4 new file mode 100644 index 0000000000..f3b5f6c138 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0112/A0112b.mc4 @@ -0,0 +1,13 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A0112; + +grammar A0112b { + + Foo = "foo"; + + interface Bar = "bar"; + + interface Bar = "foo"; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0112/A0112c.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0112/A0112c.mc4 new file mode 100644 index 0000000000..4c0b365c73 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0112/A0112c.mc4 @@ -0,0 +1,13 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A0112; + +grammar A0112c { + + Foo = "foo"; + + enum Bar = "bar"; + + enum Bar = "foo"; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0113/A0113.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0113/A0113.mc4 new file mode 100644 index 0000000000..ca04241431 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0113/A0113.mc4 @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos.invalid.A0113; + +grammar A0113 { + + Sup extends Super = "a"; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0113/A0113a.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0113/A0113a.mc4 new file mode 100644 index 0000000000..5e54cb9e40 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0113/A0113a.mc4 @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos.invalid.A0113; + +grammar A0113a { + + Sup implements Super = "a"; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0117/A0117.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0117/A0117.mc4 new file mode 100644 index 0000000000..53dcf937b9 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0117/A0117.mc4 @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A0117; + +grammar A0117 { + + A; + + symbolrule B = + isBla: boolean; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0117/A0117a.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0117/A0117a.mc4 new file mode 100644 index 0000000000..f255ea069e --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0117/A0117a.mc4 @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A0117; + +grammar A0117a { + + A; + + symbolrule A = + isBla: boolean; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0118/A0118.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0118/A0118.mc4 new file mode 100644 index 0000000000..4b9fa9dfa6 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0118/A0118.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A0118; + +grammar A0118 { + + symbolrule A = + boolean; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0120/A0120.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0120/A0120.mc4 new file mode 100644 index 0000000000..7be4571b1d --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0120/A0120.mc4 @@ -0,0 +1,13 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A0120; + +grammar A0120 { + + A = "a"; + + interface Foo = bar:"bar" A "b"; + + + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0120/A0120b.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0120/A0120b.mc4 new file mode 100644 index 0000000000..bffdd36588 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0120/A0120b.mc4 @@ -0,0 +1,13 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A0120; + +grammar A0120b { + + A = "a"; + + interface Foo = bar:"bar" A key("b"); + + + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0125/A0125.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0125/A0125.mc4 new file mode 100644 index 0000000000..b59d4dd0eb --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0125/A0125.mc4 @@ -0,0 +1,13 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A0125; + +grammar A0125 { + + interface symbol I1; + + interface symbol I2; + + A implements I1, I2; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0125/A0125a.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0125/A0125a.mc4 new file mode 100644 index 0000000000..338ee9d946 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0125/A0125a.mc4 @@ -0,0 +1,15 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A0125; + +grammar A0125a { + + interface symbol I1; + + interface symbol I2; + + interface I3 extends I2; + + A implements I1, I3; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0135/A0135.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0135/A0135.mc4 new file mode 100644 index 0000000000..5452f5e5d1 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0135/A0135.mc4 @@ -0,0 +1,13 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A0135; + +grammar A0135 { + + interface scope I1; + + interface scope I2; + + A implements I1, I2; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0135/A0135a.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0135/A0135a.mc4 new file mode 100644 index 0000000000..9e5579b6a9 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0135/A0135a.mc4 @@ -0,0 +1,15 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A0135; + +grammar A0135a { + + interface scope I1; + + interface scope I2; + + interface I3 extends I2; + + A implements I1, I3; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0135/A0135b.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0135/A0135b.mc4 new file mode 100644 index 0000000000..fa3fce8308 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0135/A0135b.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A0135; + +grammar A0135b { + + scope C1; + + scope A extends C1; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0142/A0142.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0142/A0142.mc4 new file mode 100644 index 0000000000..96c91e8202 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0142/A0142.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A0142; + +grammar A0142 { + + Foo = key("foo"); + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0142/A0142a.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0142/A0142a.mc4 new file mode 100644 index 0000000000..a737b18316 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0142/A0142a.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A0142; + +grammar A0142a { + + Foo = "foo"; + + nokeyword "foo"; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0274/A0274Sub.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0274/A0274Sub.mc4 new file mode 100644 index 0000000000..eb7d0d1ddf --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0274/A0274Sub.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A0274; + +grammar A0274Sub extends de.monticore.grammar.cocos.invalid.A0274.A0274Super { + + symbol Foo; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0274/A0274Super.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0274/A0274Super.mc4 new file mode 100644 index 0000000000..d6547bf691 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0274/A0274Super.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A0274; + +grammar A0274Super { + + symbol Foo; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0275/A0275Sub.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0275/A0275Sub.mc4 new file mode 100644 index 0000000000..6cc1fe491b --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0275/A0275Sub.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A0275; + +grammar A0275Sub extends de.monticore.grammar.cocos.invalid.A0275.A0275Super { + + scope Foo; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0275/A0275Super.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0275/A0275Super.mc4 new file mode 100644 index 0000000000..f0e367ea5b --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0275/A0275Super.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A0275; + +grammar A0275Super { + + scope Foo; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0276/A0276.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0276/A0276.mc4 new file mode 100644 index 0000000000..55b18458dc --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0276/A0276.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A0276; + +grammar A0276 { + + external A; + + B = A; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0276/A0276a.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0276/A0276a.mc4 new file mode 100644 index 0000000000..eda844f790 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0276/A0276a.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A0276; + +component grammar A0276a { + + external A; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0276/A0276b.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0276/A0276b.mc4 new file mode 100644 index 0000000000..5bc17c66e1 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0276/A0276b.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A0276; + +grammar A0276b extends de.monticore.grammar.cocos.invalid.A0276.A0276a { + + B = A; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0277/A0277.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0277/A0277.mc4 new file mode 100644 index 0000000000..5687012716 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0277/A0277.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A0277; + +grammar A0277 { + + abstract A; + + B = A; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0277/A0277a.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0277/A0277a.mc4 new file mode 100644 index 0000000000..7e7a72c53d --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0277/A0277a.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos.invalid.A0277; + +component grammar A0277a{ + + + abstract A; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0277/A0277b.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0277/A0277b.mc4 new file mode 100644 index 0000000000..aac974b54d --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0277/A0277b.mc4 @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos.invalid.A0277; + +grammar A0277b extends de.monticore.grammar.cocos.invalid.A0277.A0277a{ + +B = A; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0277/A0277c.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0277/A0277c.mc4 new file mode 100644 index 0000000000..98895d0863 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0277/A0277c.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos.invalid.A0277; + +grammar A0277c extends de.monticore.grammar.cocos.invalid.A0277.A0277a{ + + abstract C extends A; + + X = A; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0278/A0278.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0278/A0278.mc4 new file mode 100644 index 0000000000..451e8f2780 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0278/A0278.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A0278; + +grammar A0278 { + + interface A; + + B = A; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0278/A0278a.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0278/A0278a.mc4 new file mode 100644 index 0000000000..cd82c779d0 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0278/A0278a.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos.invalid.A0278; + +component grammar A0278a{ + + + interface A; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0278/A0278b.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0278/A0278b.mc4 new file mode 100644 index 0000000000..6da4112d83 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0278/A0278b.mc4 @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos.invalid.A0278; + +grammar A0278b extends de.monticore.grammar.cocos.invalid.A0278.A0278a{ + +B = A; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0278/A0278c.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0278/A0278c.mc4 new file mode 100644 index 0000000000..42f31ccbd1 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0278/A0278c.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos.invalid.A0278; + +grammar A0278c extends de.monticore.grammar.cocos.invalid.A0278.A0278a { + + interface C extends A; + + X = A; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0278/A0278d.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0278/A0278d.mc4 new file mode 100644 index 0000000000..3bab26695f --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0278/A0278d.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A0278; + +grammar A0278d extends de.monticore.grammar.cocos.invalid.A0278.A0278a { + + interface D extends A; + + X implements A = "x"; + Y = D; +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0278/A0278e.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0278/A0278e.mc4 new file mode 100644 index 0000000000..9cd15ab40a --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0278/A0278e.mc4 @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A0278; + +grammar A0278e extends de.monticore.grammar.cocos.invalid.A0278.A0278a { + + interface D extends A; + interface Z; + + X implements A = Z; + Y = A; +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0279/A0279.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0279/A0279.mc4 new file mode 100644 index 0000000000..d58674a499 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0279/A0279.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A0279; + +grammar A0279 { + + symbol A = Name*; + + B = A; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0279/A0279a.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0279/A0279a.mc4 new file mode 100644 index 0000000000..5c35cbf13b --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0279/A0279a.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A0279; + +grammar A0279a { + + symbol A = Name Name; + + B = A; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0810/A0810a.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0810/A0810a.mc4 new file mode 100644 index 0000000000..de9cada2ad --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0810/A0810a.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos.invalid.A0810; + +grammar A0810a extends de.monticore.common.TestLexicals { + + interface symbol A = Name; + + scope B implements A = Name "Test"; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0810/A0810b.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0810/A0810b.mc4 new file mode 100644 index 0000000000..f93ae86138 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0810/A0810b.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos.invalid.A0810; + +grammar A0810b extends de.monticore.common.TestLexicals { + + symbol E = Name; + + scope F extends E = Name "Test3"; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0810/A0810c.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0810/A0810c.mc4 new file mode 100644 index 0000000000..7d60a0a7b8 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A0810/A0810c.mc4 @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos.invalid.A0810; + +grammar A0810c extends de.monticore.common.TestLexicals { + + interface symbol A = Name; + + I implements A = Name; + + scope J extends I = Name; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2007/A2007Sup1.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2007/A2007Sup1.mc4 new file mode 100644 index 0000000000..83b96b58a1 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2007/A2007Sup1.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A2007; + +grammar A2007Sup1 extends de.monticore.grammar.cocos.invalid.A2007.A2007Super { + + A extends M = Name*; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2007/A2007Sup2.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2007/A2007Sup2.mc4 new file mode 100644 index 0000000000..58a48aca81 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2007/A2007Sup2.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A2007; + +grammar A2007Sup2 extends de.monticore.grammar.cocos.invalid.A2007.A2007Super { + + M = Name?; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2007/A2007Sup3.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2007/A2007Sup3.mc4 new file mode 100644 index 0000000000..68c03ca58f --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2007/A2007Sup3.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A2007; + +grammar A2007Sup3 extends de.monticore.grammar.cocos.invalid.A2007.A2007Super { + + A extends M = Name | "name"; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2007/A2007Sup4.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2007/A2007Sup4.mc4 new file mode 100644 index 0000000000..2a55090f79 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2007/A2007Sup4.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A2007; + +grammar A2007Sup4 extends de.monticore.grammar.cocos.invalid.A2007.A2007Super { + + M = n:Name; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2007/A2007Sup5.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2007/A2007Sup5.mc4 new file mode 100644 index 0000000000..0ea4c1624e --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2007/A2007Sup5.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A2007; + +grammar A2007Sup5 extends de.monticore.grammar.cocos.invalid.A2007.A2007Super { + + N = "one" M?; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2007/A2007Sup6.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2007/A2007Sup6.mc4 new file mode 100644 index 0000000000..055738a905 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2007/A2007Sup6.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A2007; + +grammar A2007Sup6 extends de.monticore.grammar.cocos.invalid.A2007.A2007Super { + + A extends M = M? "one" M; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2007/A2007Sup7.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2007/A2007Sup7.mc4 new file mode 100644 index 0000000000..322e8d692d --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2007/A2007Sup7.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A2007; + +grammar A2007Sup7 extends de.monticore.grammar.cocos.invalid.A2007.A2007Super { + + M = "something"; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2007/A2007Super.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2007/A2007Super.mc4 new file mode 100644 index 0000000000..3ef62dd1c7 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2007/A2007Super.mc4 @@ -0,0 +1,17 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A2007; + +grammar A2007Super { + + Name = "name"; + + N = "one" M; + + M = Name; + + P = "some" M*; + + Q = "optional" M?; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2008/A2008a.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2008/A2008a.mc4 new file mode 100644 index 0000000000..05adfac000 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2008/A2008a.mc4 @@ -0,0 +1,10 @@ + +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A2008; + +grammar A2008a { + + A = "a"; + B = as:A* A*; +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2008/A2008b.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2008/A2008b.mc4 new file mode 100644 index 0000000000..459bf36edc --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2008/A2008b.mc4 @@ -0,0 +1,10 @@ + +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A2008; + +grammar A2008b { + + A = "a"; + B = A+ as:A+; +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2008/A2008c.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2008/A2008c.mc4 new file mode 100644 index 0000000000..3806a5345e --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2008/A2008c.mc4 @@ -0,0 +1,9 @@ + +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A2008; + +grammar A2008c extends de.monticore.common.TestTypes { + + B = Name* names:Name*; +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2025/A2025.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2025/A2025.mc4 new file mode 100644 index 0000000000..2dd90766bd --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2025/A2025.mc4 @@ -0,0 +1,33 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A2025; + +grammar A2025 { + + B; + + A; + + abstract A; + + enum A = "B"; + + interface A; + + external A; + + token A = "B"; + + A; + + abstract A; + + enum A = "B"; + + interface A; + + external A; + + token A = "B"; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2026/A2026.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2026/A2026.mc4 new file mode 100644 index 0000000000..2273462adf --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2026/A2026.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A2026; + +grammar A2026 { + + a; + + A; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2030/A2030a.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2030/A2030a.mc4 new file mode 100644 index 0000000000..4a607df09e --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2030/A2030a.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A2030; + +grammar A2030a { + + A extends B; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2030/A2030b.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2030/A2030b.mc4 new file mode 100644 index 0000000000..71ae010a32 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2030/A2030b.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A2030; + +grammar A2030b { + + A implements B; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2030/A2030c.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2030/A2030c.mc4 new file mode 100644 index 0000000000..a8f48e08f3 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2030/A2030c.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A2030; + +grammar A2030c { + + abstract A implements B; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2030/A2030d.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2030/A2030d.mc4 new file mode 100644 index 0000000000..f316142c8c --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2030/A2030d.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A2030; + +grammar A2030d { + + abstract A extends B; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2030/A2030e.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2030/A2030e.mc4 new file mode 100644 index 0000000000..189d7bea4f --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2030/A2030e.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A2030; + +grammar A2030e { + + interface A extends B; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2031/A2031.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2031/A2031.mc4 new file mode 100644 index 0000000000..623cfc50c9 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2031/A2031.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A2031; + +grammar A2031 { + + A = B; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2102/A2102a.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2102/A2102a.mc4 new file mode 100644 index 0000000000..6572abcc51 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2102/A2102a.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A2102; + +grammar A2102a { + A; + + B implements A; + +} + diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2102/A2102b.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2102/A2102b.mc4 new file mode 100644 index 0000000000..6c7a739708 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2102/A2102b.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A2102; + +grammar A2102b { + external A; + + B implements A; + +} + diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2102/A2102c.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2102/A2102c.mc4 new file mode 100644 index 0000000000..6be2bca06a --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2102/A2102c.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A2102; + +grammar A2102c { + abstract A; + + B implements A; + +} + diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2102/A2102d.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2102/A2102d.mc4 new file mode 100644 index 0000000000..1f27318270 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2102/A2102d.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A2102; + +grammar A2102d { + enum A = "a"; + + B implements A; + +} + diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2103/A2103a.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2103/A2103a.mc4 new file mode 100644 index 0000000000..0975e814b2 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2103/A2103a.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A2103; + +grammar A2103a { + interface A; + + B extends A; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2103/A2103b.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2103/A2103b.mc4 new file mode 100644 index 0000000000..9a61a56e4c --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2103/A2103b.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A2103; + +grammar A2103b { + external A; + + B extends A; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2106/A2106a.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2106/A2106a.mc4 new file mode 100644 index 0000000000..3627b3ebfb --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2106/A2106a.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A2106; + +grammar A2106a { + A; + + abstract B implements A; + +} + diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2106/A2106b.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2106/A2106b.mc4 new file mode 100644 index 0000000000..26a10e7cdd --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2106/A2106b.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A2106; + +grammar A2106b { + external A; + + abstract B implements A; + +} + diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2106/A2106c.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2106/A2106c.mc4 new file mode 100644 index 0000000000..181e376f23 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2106/A2106c.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A2106; + +grammar A2106c { + abstract A; + + abstract B implements A; + +} + diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2106/A2106d.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2106/A2106d.mc4 new file mode 100644 index 0000000000..a52f3e6c15 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2106/A2106d.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A2106; + +grammar A2106d { + enum A = "a"; + + abstract B implements A; + +} + diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2107/A2107a.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2107/A2107a.mc4 new file mode 100644 index 0000000000..4be89a19fc --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2107/A2107a.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A2107; + +grammar A2107a { + interface A; + + abstract B extends A; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2107/A2107b.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2107/A2107b.mc4 new file mode 100644 index 0000000000..2979d37800 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2107/A2107b.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A2107; + +grammar A2107b { + external A; + + abstract B extends A; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2116/A2116a.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2116/A2116a.mc4 new file mode 100644 index 0000000000..49e7621ddc --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2116/A2116a.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A2116; + +grammar A2116a { + abstract A; + + interface B extends A; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2116/A2116b.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2116/A2116b.mc4 new file mode 100644 index 0000000000..2066b9f554 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2116/A2116b.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A2116; + +grammar A2116b { + external A; + + interface B extends A; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2116/A2116c.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2116/A2116c.mc4 new file mode 100644 index 0000000000..9a783d1e4d --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2116/A2116c.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A2116; + +grammar A2116c { + A; + + interface B extends A; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2116/A2116d.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2116/A2116d.mc4 new file mode 100644 index 0000000000..9b768971b8 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A2116/A2116d.mc4 @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A2116; + +grammar A2116d { + A; + + interface C; + + interface B extends C, A; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4001/A4001.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4001/A4001.mc4 new file mode 100644 index 0000000000..19617c5681 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4001/A4001.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4001; + +grammar A4001 extends de.monticore.common.TestTypes { + + QualifiedName extends Name; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4002/A4002.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4002/A4002.mc4 new file mode 100644 index 0000000000..c34d889e36 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4002/A4002.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4002; + +grammar A4002 extends de.monticore.common.TestTypes { + + abstract ArrayType extends Name; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4003/A4003.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4003/A4003.mc4 new file mode 100644 index 0000000000..c9db24b492 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4003/A4003.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4003; + +grammar A4002 { + + A; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4004/A4004.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4004/A4004.mc4 new file mode 100644 index 0000000000..be4f6df36c --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4004/A4004.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4003; + +grammar A4004 { + + A; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4005/A4005.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4005/A4005.mc4 new file mode 100644 index 0000000000..ba4c337eb4 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4005/A4005.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4005; + +grammar A4005 { + A; + + B = ((C:A)*)?; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4006/A4006.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4006/A4006.mc4 new file mode 100644 index 0000000000..8f6411a193 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4006/A4006.mc4 @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4006; + +grammar A4006 { + A; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4007/A4007.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4007/A4007.mc4 new file mode 100644 index 0000000000..84a36729ba --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4007/A4007.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4007; + +grammar A4007 extends de.monticore.grammar.cocos.invalid.A4007.A4007 { + + interface ReturnType; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4007/A4007a.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4007/A4007a.mc4 new file mode 100644 index 0000000000..3e9d4450f7 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4007/A4007a.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4007; + +grammar A4007a extends de.monticore.grammar.cocos.invalid.A4007.A4007 { + + interface ReturnType; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4008/A4008.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4008/A4008.mc4 new file mode 100644 index 0000000000..4e6990db13 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4008/A4008.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4008; + +grammar A4008 { + + abstract ArrayType = "a"; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4008/A4008a.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4008/A4008a.mc4 new file mode 100644 index 0000000000..ca352f60b4 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4008/A4008a.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4008; + +grammar A4008a extends de.monticore.grammar.cocos.invalid.A4008.A4008 { + + interface ArrayType; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4008/A4008b.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4008/A4008b.mc4 new file mode 100644 index 0000000000..b3ef567a2a --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4008/A4008b.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4008; + +grammar A4008b extends de.monticore.grammar.cocos.invalid.A4008.A4008 { + + enum ArrayType = "a"; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4008/A4008c.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4008/A4008c.mc4 new file mode 100644 index 0000000000..75162c864f --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4008/A4008c.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4008; + +grammar A4008c extends de.monticore.grammar.cocos.invalid.A4008.A4008 { + + token ArrayType = "a"; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4008/A4008d.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4008/A4008d.mc4 new file mode 100644 index 0000000000..9b34cff4cf --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4008/A4008d.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4008; + +grammar A4008d extends de.monticore.grammar.cocos.invalid.A4008.A4008 { + + external ArrayType; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4009/A4009.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4009/A4009.mc4 new file mode 100644 index 0000000000..df87dcd967 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4009/A4009.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4009; + +grammar A4009 { + + QualifiedName = "q"; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4009/A4009a.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4009/A4009a.mc4 new file mode 100644 index 0000000000..6cf3b01a40 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4009/A4009a.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4009; + +grammar A4009a extends de.monticore.grammar.cocos.invalid.A4009.A4009 { + + interface QualifiedName; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4009/A4009b.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4009/A4009b.mc4 new file mode 100644 index 0000000000..f694c17afb --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4009/A4009b.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4009; + +grammar A4009b extends de.monticore.grammar.cocos.invalid.A4009.A4009 { + + enum QualifiedName = "a"; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4009/A4009c.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4009/A4009c.mc4 new file mode 100644 index 0000000000..aaa72a213c --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4009/A4009c.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4009; + +grammar A4009c extends de.monticore.grammar.cocos.invalid.A4009.A4009 { + + token QualifiedName = "a"; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4009/A4009d.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4009/A4009d.mc4 new file mode 100644 index 0000000000..0795ed5469 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4009/A4009d.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4009; + +grammar A4009d extends de.monticore.grammar.cocos.invalid.A4009.A4009 { + + external QualifiedName; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4009/A4009e.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4009/A4009e.mc4 new file mode 100644 index 0000000000..9837bd72c4 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4009/A4009e.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4009; + +grammar A4009e extends de.monticore.grammar.cocos.invalid.A4009.A4009 { + + abstract QualifiedName; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4010/A4010.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4010/A4010.mc4 new file mode 100644 index 0000000000..fcef23ac42 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4010/A4010.mc4 @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4010; + +grammar A4010 extends de.monticore.common.TestTypes { + abstract ArrayType; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4011/A4011a.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4011/A4011a.mc4 new file mode 100644 index 0000000000..2c8ea68b22 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4011/A4011a.mc4 @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4011; + +grammar A4011a { + A; + + B; + + C extends A, B; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4011/A4011b.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4011/A4011b.mc4 new file mode 100644 index 0000000000..459e8c88c1 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4011/A4011b.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4011; + +grammar A4011b { + + A astextends B, C; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4012/A4012a.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4012/A4012a.mc4 new file mode 100644 index 0000000000..9435e7694a --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4012/A4012a.mc4 @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4012; + +grammar A4012a { + A; + + B; + + abstract C extends A, B; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4012/A4012b.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4012/A4012b.mc4 new file mode 100644 index 0000000000..9d3768ef60 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4012/A4012b.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4012; + +grammar A4012b { + + abstract A astextends B, C; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4013/A4013.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4013/A4013.mc4 new file mode 100644 index 0000000000..c787e02eea --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4013/A4013.mc4 @@ -0,0 +1,14 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4013; + +grammar A4013 { + A extends B; + + astrule A astextends C; + + B; + + C; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4014/A4014.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4014/A4014.mc4 new file mode 100644 index 0000000000..c2384743f3 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4014/A4014.mc4 @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4014; + +grammar A4014 { + enum A = "a" | "a"; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4015/A4015.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4015/A4015.mc4 new file mode 100644 index 0000000000..308b29b7cf --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4015/A4015.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4015; + +grammar A4015 { + + token A = "a"?; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4016/A4016.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4016/A4016.mc4 new file mode 100644 index 0000000000..2f8c968fe2 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4016/A4016.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4016; + +grammar A4016 { + + token A = B; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4017/A4017.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4017/A4017.mc4 new file mode 100644 index 0000000000..fdaf932171 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4017/A4017.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4017; + +grammar A4017 { + + token A = B; + + B; +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4018/A4018.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4018/A4018.mc4 new file mode 100644 index 0000000000..2b369f7ddb --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4018/A4018.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4018; + +grammar A4018 { + + A = ["`"]; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4019/A4019.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4019/A4019.mc4 new file mode 100644 index 0000000000..6e0f988ec2 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4019/A4019.mc4 @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4019; + +grammar A4019 { + A= ["a" | "A"| "b"]; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4020/A4020.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4020/A4020.mc4 new file mode 100644 index 0000000000..8701825b0e --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4020/A4020.mc4 @@ -0,0 +1,14 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4020; + +grammar A4020 { + + A; + + astrule A; + + astrule A; + + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4021/A4021.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4021/A4021.mc4 new file mode 100644 index 0000000000..975fe85233 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4021/A4021.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4021; + +grammar A4021 { + + + astrule A; + + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4022/A4022.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4022/A4022.mc4 new file mode 100644 index 0000000000..265a24c734 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4022/A4022.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4022; + +grammar A4022 { + + A extends A; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4022/A4022b.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4022/A4022b.mc4 new file mode 100644 index 0000000000..83d4fe4ca3 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4022/A4022b.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4022; + +grammar A4022b { + + A extends B; + + B extends A; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4023/A4023.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4023/A4023.mc4 new file mode 100644 index 0000000000..d95bc7d2fd --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4023/A4023.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4023; + +grammar A4023 extends de.monticore.grammar.cocos.invalid.A4023.A4023 { + + A = "a"; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4023/A4023b.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4023/A4023b.mc4 new file mode 100644 index 0000000000..509bb08ac8 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4023/A4023b.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4023; + +grammar A4023b extends de.monticore.grammar.cocos.invalid.A4023c{ + + A = "a"; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4023/A4023c.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4023/A4023c.mc4 new file mode 100644 index 0000000000..073eaa6dcd --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4023/A4023c.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4023; + +grammar A4023c extends de.monticore.grammar.cocos.invalid.A4023b{ + + A = "a"; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4024/A4024.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4024/A4024.mc4 new file mode 100644 index 0000000000..56366f6385 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4024/A4024.mc4 @@ -0,0 +1,14 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4024; + +grammar A4024 { + A = a:C; + + B extends A = a:D; + + C; + + D; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4024/A4024_b.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4024/A4024_b.mc4 new file mode 100644 index 0000000000..18b697a57b --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4024/A4024_b.mc4 @@ -0,0 +1,14 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4024; + +grammar A4024_b { + + A; + + C; + + Super = C; + + Sub extends Super = c:A; +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4024/A4024_c.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4024/A4024_c.mc4 new file mode 100644 index 0000000000..7d7e983bca --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4024/A4024_c.mc4 @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4024; + +grammar A4024_c { + + A; + + Super = c:["state"]; + + Sub extends Super = c:A; +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4024/A4024_d.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4024/A4024_d.mc4 new file mode 100644 index 0000000000..61e6cbc694 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4024/A4024_d.mc4 @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4024; + +grammar A4024_d { + + A; + + Super = c:"state"; + + Sub extends Super = c:A; +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4024/A4024_e.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4024/A4024_e.mc4 new file mode 100644 index 0000000000..d20ece756e --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4024/A4024_e.mc4 @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4024; + +grammar A4024_e { + + A; + + Super = ["c"]; + + Sub extends Super = c:A; +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4024/A4024_f.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4024/A4024_f.mc4 new file mode 100644 index 0000000000..7edc23df4a --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4024/A4024_f.mc4 @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4024; + +grammar A4024_f { + + A; + + Super = [c:"state"]; + + Sub extends Super = c:A; +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4024/A4024_g.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4024/A4024_g.mc4 new file mode 100644 index 0000000000..1f42ee18b6 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4024/A4024_g.mc4 @@ -0,0 +1,14 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4024; + +grammar A4024_g { + + A; + + D; + + Super = c:D; + + Sub extends Super = c:A; +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4024/A4024_h.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4024/A4024_h.mc4 new file mode 100644 index 0000000000..b82fb1c69c --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4024/A4024_h.mc4 @@ -0,0 +1,15 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4024; + +grammar A4024_h { + + token Name = + ( 'a'..'z' | 'A'..'Z' | '_' | '$' )( 'a'..'z' | 'A'..'Z' | '_' | '0'..'9' | '$' )*; + + A; + + Super = c:Name; + + Sub extends Super = c:A; +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4024/A4024_i.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4024/A4024_i.mc4 new file mode 100644 index 0000000000..ee828aaa2b --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4024/A4024_i.mc4 @@ -0,0 +1,15 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4024; + +grammar A4024_i { + + A; + + D; + + Super = c:D (c:D | ",")*; + + Sub extends Super = c:A (c:A | ",")*; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025.mc4 new file mode 100644 index 0000000000..8b0c1bfbd0 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4025; + +grammar A4025 extends de.monticore.common.TestTypes { + QualifiedName = + part:StringLiteral; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025_sub_b.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025_sub_b.mc4 new file mode 100644 index 0000000000..6244567408 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025_sub_b.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4025; + +grammar A4025_sub_b extends de.monticore.grammar.cocos.invalid.A4025.A4025_super_b { + + B; + + A = c:B; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025_sub_c.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025_sub_c.mc4 new file mode 100644 index 0000000000..ab21d392c8 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025_sub_c.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4025; + +grammar A4025_sub_c extends de.monticore.grammar.cocos.invalid.A4025.A4025_super_c { + + B; + + A = c:B; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025_sub_d.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025_sub_d.mc4 new file mode 100644 index 0000000000..34fccd7b5b --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025_sub_d.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4025; + +grammar A4025_sub_d extends de.monticore.grammar.cocos.invalid.A4025.A4025_super_d { + + B; + + A = c:B; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025_sub_e.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025_sub_e.mc4 new file mode 100644 index 0000000000..d98b4c76b4 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025_sub_e.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4025; + +grammar A4025_sub_e extends de.monticore.grammar.cocos.invalid.A4025.A4025_super_e { + + B; + + A = c:B; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025_sub_f.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025_sub_f.mc4 new file mode 100644 index 0000000000..024c13efe8 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025_sub_f.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4025; + +grammar A4025_sub_f extends de.monticore.grammar.cocos.invalid.A4025.A4025_super_f { + + B; + + A = c:B; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025_sub_g.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025_sub_g.mc4 new file mode 100644 index 0000000000..38d1c464a3 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025_sub_g.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4025; + +grammar A4025_sub_g extends de.monticore.grammar.cocos.invalid.A4025.A4025_super_g { + + B; + + A = c:B; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025_super_b.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025_super_b.mc4 new file mode 100644 index 0000000000..6c8903af65 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025_super_b.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4025; + +grammar A4025_super_b { + + C; + + A = C; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025_super_c.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025_super_c.mc4 new file mode 100644 index 0000000000..1ea189e9e9 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025_super_c.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4025; + +grammar A4025_super_c { + + A = c:["b"]; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025_super_d.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025_super_d.mc4 new file mode 100644 index 0000000000..a16c9d8f0c --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025_super_d.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4025; + +grammar A4025_super_d { + + A = ["c"]; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025_super_e.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025_super_e.mc4 new file mode 100644 index 0000000000..454c4c2f58 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025_super_e.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4025; + +grammar A4025_super_e { + + A = [c:"b"]; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025_super_f.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025_super_f.mc4 new file mode 100644 index 0000000000..568b462bdb --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025_super_f.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4025; + +grammar A4025_super_f { + + D; + + A = c:D; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025_super_g.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025_super_g.mc4 new file mode 100644 index 0000000000..0add32a307 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4025/A4025_super_g.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4025; + +grammar A4025_super_g { + + A = c:"b"; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4026/A4026.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4026/A4026.mc4 new file mode 100644 index 0000000000..a01ac82487 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4026/A4026.mc4 @@ -0,0 +1,14 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4026; + +grammar A4026 { + token CARDINALITY = ('0'..'9')+ | '*' : + x -> int : { // Java code: + if (x.equals(" * ")) + return -1; + else + return Integer.parseInt(x.getText()); + }; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4026/A4026b.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4026/A4026b.mc4 new file mode 100644 index 0000000000..79f511e408 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4026/A4026b.mc4 @@ -0,0 +1,14 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4026; + +grammar A4026b extends de.monticore.grammar.cocos.invalid.A4026.A4026 { + token CARDINALITY = ('0'..'9')+ | '*' : + x -> double : { // Java code: + if (x.equals(" * ")) + return -1; + else + return Double.parseDouble(x.getText()); + }; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4027/A4027.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4027/A4027.mc4 new file mode 100644 index 0000000000..3ecf53b01f --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4027/A4027.mc4 @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4027; + +grammar A4027 extends de.monticore.grammar.cocos.valid.Enum { + + E; +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4028/A4028.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4028/A4028.mc4 new file mode 100644 index 0000000000..6d1b7aa9b6 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4028/A4028.mc4 @@ -0,0 +1,16 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4028; + +grammar A4028 { + + A; + + B; + + E = a:A; + + astrule E = a:B; + + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4029/A4029.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4029/A4029.mc4 new file mode 100644 index 0000000000..dd4d54efff --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4029/A4029.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4029; + +grammar A4029 { + A; + + B extends A astextends C; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4030/A4030.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4030/A4030.mc4 new file mode 100644 index 0000000000..418059be1b --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4030/A4030.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4030; + +grammar A4030 { + A; + + abstract B extends A astextends C; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4031/A4031a.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4031/A4031a.mc4 new file mode 100644 index 0000000000..dd8a871b96 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4031/A4031a.mc4 @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4031; + +grammar A4031a { + a = "a"; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4031/A4031b.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4031/A4031b.mc4 new file mode 100644 index 0000000000..e0dd08466e --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4031/A4031b.mc4 @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4031; + +grammar A4031b { + abstract a; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4031/A4031c.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4031/A4031c.mc4 new file mode 100644 index 0000000000..b5b77d183b --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4031/A4031c.mc4 @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4031; + +grammar A4031c { + interface a; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4031/A4031d.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4031/A4031d.mc4 new file mode 100644 index 0000000000..9f69ba03e3 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4031/A4031d.mc4 @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4031; + +grammar A4031d { + external a; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4031/A4031e.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4031/A4031e.mc4 new file mode 100644 index 0000000000..ebf429d797 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4031/A4031e.mc4 @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4031; + +grammar A4031e { + enum a = "a"; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4032/A4032.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4032/A4032.mc4 new file mode 100644 index 0000000000..a5f3263f12 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4032/A4032.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4032; + +grammar A4032 { + + enum A = "a"; + + astrule A; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4033/a4033.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4033/a4033.mc4 new file mode 100644 index 0000000000..409e57e1fb --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4033/a4033.mc4 @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4033; + +grammar a4033 { + a = "a"; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4035/A4035.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4035/A4035.mc4 new file mode 100644 index 0000000000..7c5c8636b5 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4035/A4035.mc4 @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4035; + +grammar A4035 { + + S = "s" Name ";" ; + + astrule S = b:boolean + b:String ; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4035/A4035a.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4035/A4035a.mc4 new file mode 100644 index 0000000000..3096856175 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4035/A4035a.mc4 @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4035; + +grammar A4035a { + + symbol S = "s" Name ";"; + + symbolrule S = b:boolean + b:String; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4035/A4035b.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4035/A4035b.mc4 new file mode 100644 index 0000000000..ae0946a5fc --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4035/A4035b.mc4 @@ -0,0 +1,16 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4035; + +grammar A4035b { + + symbol S = "s" Name ";" ; + + symbol T extends S = "t" Name ";" ; + + symbolrule S = b:boolean ; + + symbolrule T = b:String ; + + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4036/I.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4036/I.mc4 new file mode 100644 index 0000000000..41de5b4cc1 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4036/I.mc4 @@ -0,0 +1,7 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos.invalid.A4036; + +grammar I { + + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4037/A4037.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4037/A4037.mc4 new file mode 100644 index 0000000000..3da30f10fa --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4037/A4037.mc4 @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4037; + +grammar A4037 extends de.monticore.common.TestTypes{ + + symbol A = Name; + + // C = Name; + + B = g:Name@C; +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4038/A4038.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4038/A4038.mc4 new file mode 100644 index 0000000000..3b14a60daa --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4038/A4038.mc4 @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4038; + +grammar A4038 { + symbol scope Automaton = "automaton" Name "{" (State | Transition | Tag)* "}" ; + State = "Foo"; + Transition = "Baa"; + Tag = StartTag EndTag; + token StartTag = "<": -> pushMode(text); + token EndTag (text) = ">" : -> popMode; +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4039/A4039.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4039/A4039.mc4 new file mode 100644 index 0000000000..2019e8fb40 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4039/A4039.mc4 @@ -0,0 +1,13 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4039; + +grammar A4039 extends de.monticore.common.TestTypes { + + symbol A = Name State; + + symbol State = "state" Name; + + B = bstate:State@A; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4041/A4041.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4041/A4041.mc4 new file mode 100644 index 0000000000..494e6154ba --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4041/A4041.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4041; + +grammar A4041 { + + symbol symbol A = "Hello"; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4047/A4047a.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4047/A4047a.mc4 new file mode 100644 index 0000000000..576b6f1f26 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4047/A4047a.mc4 @@ -0,0 +1,14 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4047; + +grammar A4047a { + + interface A = c:C; + + B implements A = c:D; + + C; + + D; +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4047/A4047b.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4047/A4047b.mc4 new file mode 100644 index 0000000000..6a0b91e997 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4047/A4047b.mc4 @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4047; + +grammar A4047b { + + interface A = C; + + abstract B implements A; + + C; +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4047/A4047c.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4047/A4047c.mc4 new file mode 100644 index 0000000000..ffcdc759dc --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4047/A4047c.mc4 @@ -0,0 +1,18 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4047; + +grammar A4047c { + + interface A = E; + + interface B extends A; + + interface C extends B = F; + + D implements C = F; + + E; + + F; +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4047/A4047d.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4047/A4047d.mc4 new file mode 100644 index 0000000000..21a9f0640d --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4047/A4047d.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4047; + +grammar A4047d { + + interface A = foo:"Foo"; + + B implements A; +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4047/A4047e.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4047/A4047e.mc4 new file mode 100644 index 0000000000..6b6f817da6 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4047/A4047e.mc4 @@ -0,0 +1,13 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4047; + +grammar A4047e { + + D; + + interface A = D*; + + AImpl implements A = D; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4047/A4047f.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4047/A4047f.mc4 new file mode 100644 index 0000000000..a73d93b50d --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4047/A4047f.mc4 @@ -0,0 +1,13 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4047; + +grammar A4047f { + + D; + + interface B = D+; + + BImpl implements B = D; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4047/A4047g.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4047/A4047g.mc4 new file mode 100644 index 0000000000..4f432be5cc --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4047/A4047g.mc4 @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4047; + +grammar A4047g { + + D; + + interface A = D?; + + AImpl implements A = D; +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4047/A4047h.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4047/A4047h.mc4 new file mode 100644 index 0000000000..f334e98a49 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4047/A4047h.mc4 @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4047; + +grammar A4047h { + Foo = "abc"; + E = Foo*; +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4047/A4047i.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4047/A4047i.mc4 new file mode 100644 index 0000000000..e1834f2938 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4047/A4047i.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4047; + +grammar A4047i extends A4047h { + E implements I; + + interface I = Foo*; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4054/A4054.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4054/A4054.mc4 new file mode 100644 index 0000000000..583a408f90 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4054/A4054.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4054; + +grammar A4054 { + + A = ""; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4056/A4056.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4056/A4056.mc4 new file mode 100644 index 0000000000..f2b6291d4b --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4056/A4056.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4056; + +grammar A4056 { + + A = (A "*" A); + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4056/A4056a.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4056/A4056a.mc4 new file mode 100644 index 0000000000..ccc8cb1eed --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4056/A4056a.mc4 @@ -0,0 +1,13 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4056; + +grammar A4056a { + + interface Expression; + + PlusExpression implements Expression = (Expression) "+" Expression; + + LeafExpression implements Expression = "Bar"; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4058/A4058.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4058/A4058.mc4 new file mode 100644 index 0000000000..7129765c93 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4058/A4058.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4058; + +grammar A4058 { + + A = "123"; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4059/A4059a.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4059/A4059a.mc4 new file mode 100644 index 0000000000..dbd1bb6370 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4059/A4059a.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4059; + +grammar A4059a { + + A = token("b-") ; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4059/A4059b.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4059/A4059b.mc4 new file mode 100644 index 0000000000..a7cd49896c --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4059/A4059b.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4059; + +grammar A4059b { + + A = token("-") ; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4062/A4062a.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4062/A4062a.mc4 new file mode 100644 index 0000000000..8d7ed6f78d --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4062/A4062a.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4062; + +grammar A4062a { + + A = "b-"; + + splittoken "b-"; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4062/A4062b.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4062/A4062b.mc4 new file mode 100644 index 0000000000..4c4c786c72 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4062/A4062b.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4062; + +grammar A4062b { + + A = "-"; + + splittoken "-"; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4063/A4063.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4063/A4063.mc4 new file mode 100644 index 0000000000..9ede761902 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4063/A4063.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4063; + +grammar A4063 { + + A = key("--") ; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4064/A4064.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4064/A4064.mc4 new file mode 100644 index 0000000000..0182595917 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4064/A4064.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4064; + +grammar A4064 { + Foo = "--"; + + nokeyword "--"; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4068/A4068.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4068/A4068.mc4 new file mode 100644 index 0000000000..2fcb5eb9c9 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4068/A4068.mc4 @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4068; + +component grammar A4068 { + symbol scope Automaton = "automaton" Name "{" (State | Transition | Tag)* "}" ; + State = "Foo"; + Transition = "Baa"; + Tag = StartTag EndTag; + token StartTag = "<": -> pushMode(text); + token EndTag (text) = ">" : -> popMode; +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4069/A4069Sub.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4069/A4069Sub.mc4 new file mode 100644 index 0000000000..77746a4b48 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4069/A4069Sub.mc4 @@ -0,0 +1,7 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4069; + grammar A4069Sub extends de.monticore.grammar.cocos.invalid.A4069.A4069Super{ + token StartTag = "<"; + token EndTag = ">"; +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4069/A4069Super.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4069/A4069Super.mc4 new file mode 100644 index 0000000000..7f3c217ce2 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4069/A4069Super.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4069; + grammar A4069Super { + symbol scope Automaton = "automaton" Name "{" (State | Transition | Tag)* "}" ; + State = "Foo"; + Transition = "Baa"; + Tag = StartTag EndTag; + token StartTag = "<": -> pushMode(TEXT); + token EndTag (TEXT) = ">" : -> popMode; +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4090/A4090a.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4090/A4090a.mc4 new file mode 100644 index 0000000000..0ee5a15b76 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4090/A4090a.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4090; + +grammar A4090a { + + A = "a"; + B = (who:A | who:"Tom")*; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4090/A4090b.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4090/A4090b.mc4 new file mode 100644 index 0000000000..5c7227a0ab --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4090/A4090b.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4090; + +grammar A4090b extends de.monticore.common.TestTypes { + + B = (who:["Tom"] | who:"Tom")*; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4090/A4090c.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4090/A4090c.mc4 new file mode 100644 index 0000000000..714c480a76 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4090/A4090c.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4090; + +grammar A4090c extends de.monticore.common.TestTypes { + + A = "Tom"; + B = (who:["Tom"] | who:A)*; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4090/A4090d.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4090/A4090d.mc4 new file mode 100644 index 0000000000..22db23a2ce --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4090/A4090d.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4090; + +grammar A4090d { + + A = "a"; + B = who:A who:"Tom"; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4090/A4090e.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4090/A4090e.mc4 new file mode 100644 index 0000000000..76ecf6fcf5 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4090/A4090e.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4090; + +grammar A4090e extends de.monticore.common.TestLiterals{ + + B = (who:["T1" | "T2"] | who:"Tom")*; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4090/A4090f.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4090/A4090f.mc4 new file mode 100644 index 0000000000..49dd344092 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4090/A4090f.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4090; + +grammar A4090f extends de.monticore.common.TestTypes{ + + A = "a"; + B = (who:Name | who:A)*; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4090/A4090g.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4090/A4090g.mc4 new file mode 100644 index 0000000000..ac2458c372 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4090/A4090g.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4090; + +grammar A4090g extends de.monticore.common.TestLiterals{ + + A = "a"; + B = who:Num_Int who:A; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4090/A4090h.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4090/A4090h.mc4 new file mode 100644 index 0000000000..d0381f5465 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4090/A4090h.mc4 @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4090; + +grammar A4090h { + + A = "a"; + C = "c"; + D = "d"; + B = who:A who:C who:D; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4090/A4090i.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4090/A4090i.mc4 new file mode 100644 index 0000000000..5c5048a0b2 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4090/A4090i.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4090; + +grammar A4090i { + + A = "a"; + C = "c"; + B = who:A who:C who:A; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4090/A4090j.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4090/A4090j.mc4 new file mode 100644 index 0000000000..7b5b6ed289 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4090/A4090j.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4090; + +grammar A4090j { + + A = "a"; + C = "c"; + B = A a:C; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4090/A4090k.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4090/A4090k.mc4 new file mode 100644 index 0000000000..754ad503d8 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4090/A4090k.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4090; + +grammar A4090k { + + A = "a"; + B = A a:"Tom"; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4094/A4094.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4094/A4094.mc4 new file mode 100644 index 0000000000..e56cab8440 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4094/A4094.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4094; + +grammar A4094 extends de.monticore.grammar.cocos.invalid.A4094.A4094Super{ + + @Override + Foo = "foo"; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4094/A4094Super.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4094/A4094Super.mc4 new file mode 100644 index 0000000000..812c84205a --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4094/A4094Super.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4094; + +grammar A4094Super { + + Bla; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4096/A4096a.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4096/A4096a.mc4 new file mode 100644 index 0000000000..752612f7bd --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4096/A4096a.mc4 @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos.invalid.A4096; + +grammar A4096a { + + ConstantsA4096a = "foo"; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4096/A4096b.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4096/A4096b.mc4 new file mode 100644 index 0000000000..c177ef61b6 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4096/A4096b.mc4 @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos.invalid.A4096; + +grammar A4096b { + + A4096bNode = "bar"; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4096/A4096c.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4096/A4096c.mc4 new file mode 100644 index 0000000000..5b6b20df45 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4096/A4096c.mc4 @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos.invalid.A4096; + +grammar A4096c { + + EnclosingScope = "test"; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4096/A4096d.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4096/A4096d.mc4 new file mode 100644 index 0000000000..0a337597c4 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4096/A4096d.mc4 @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos.invalid.A4096; + +grammar A4096d { + + Traverser = "traverser"; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4096/A4096e.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4096/A4096e.mc4 new file mode 100644 index 0000000000..5226e7efd8 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4096/A4096e.mc4 @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos.invalid.A4096; + +grammar A4096e { + + Node = "node"; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4096/A4096f.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4096/A4096f.mc4 new file mode 100644 index 0000000000..45acb56d40 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4096/A4096f.mc4 @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos.invalid.A4096; + +grammar A4096f { + + Class = "class"; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4096/A4096g.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4096/A4096g.mc4 new file mode 100644 index 0000000000..7a16ace570 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4096/A4096g.mc4 @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos.invalid.A4096; + +grammar A4096g { + + Mode = "mode"; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4097/A4097a.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4097/A4097a.mc4 new file mode 100644 index 0000000000..d27c39c693 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4097/A4097a.mc4 @@ -0,0 +1,7 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos.invalid.A4097; + +grammar A4097a { + + A astextends java.util.Observer; +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4097/A4097b.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4097/A4097b.mc4 new file mode 100644 index 0000000000..e3af4a49f2 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4097/A4097b.mc4 @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos.invalid.A4097; + +grammar A4097b { + A; + + astrule A astextends java.util.Observer; +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4098/A4098.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4098/A4098.mc4 new file mode 100644 index 0000000000..57868b80ea --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4098/A4098.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4098; + +grammar A4098 extends de.monticore.grammar.cocos.invalid.A4098.A4098Super{ + + Foo = "foo"; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4098/A4098Super.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4098/A4098Super.mc4 new file mode 100644 index 0000000000..660feca3a7 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4098/A4098Super.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4098; + +grammar A4098Super { + + Foo; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4099/A4099Symbol.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4099/A4099Symbol.mc4 new file mode 100644 index 0000000000..e81d615f0c --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4099/A4099Symbol.mc4 @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos.invalid.A4099; + +grammar A4099Symbol { + + symbol A4099 = Name; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4100/A4100.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4100/A4100.mc4 new file mode 100644 index 0000000000..cb7ca9331e --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4100/A4100.mc4 @@ -0,0 +1,13 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4100; + +grammar A4100 extends de.monticore.common.TestTypes { + + symbol A = "a" Name; + + symbol B = "b" Name; + + C = "c" ref:Name@A ref:Name@B; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4101/A4101.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4101/A4101.mc4 new file mode 100644 index 0000000000..eaccaeaf30 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4101/A4101.mc4 @@ -0,0 +1,15 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4101; + +grammar A4101 { + + abstract A = "foo"; + + B extends A; + + interface C; + + D implements C; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4102/A4102a.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4102/A4102a.mc4 new file mode 100644 index 0000000000..d160bd0bec --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4102/A4102a.mc4 @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4102; + +grammar A4102a { + + A = "a"; + + astrule A = + b:List>; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4102/A4102b.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4102/A4102b.mc4 new file mode 100644 index 0000000000..f7ad27a06e --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4102/A4102b.mc4 @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4102; + +grammar A4102b { + + A = "a"; + + astrule A = + b:Optional+; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4102/A4102c.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4102/A4102c.mc4 new file mode 100644 index 0000000000..d62393224c --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4102/A4102c.mc4 @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4102; + +grammar A4102c { + + A = "a"; + + astrule A = + b:Optional?; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4102/A4102d.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4102/A4102d.mc4 new file mode 100644 index 0000000000..3192690d2a --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4102/A4102d.mc4 @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4102; + +grammar A4102d { + + A = "a"; + + astrule A = + b:Optional max=5; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4102/A4102e.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4102/A4102e.mc4 new file mode 100644 index 0000000000..180704bd54 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4102/A4102e.mc4 @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4102; + +grammar A4102e { + + A = "a"; + + astrule A = + b:Optional*; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4102/A4102f.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4102/A4102f.mc4 new file mode 100644 index 0000000000..d6f7d2ba34 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4102/A4102f.mc4 @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4102; + +grammar A4102f { + + A = "a"; + + astrule A = + b:Optional min=0; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4102/A4102g.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4102/A4102g.mc4 new file mode 100644 index 0000000000..50da857014 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4102/A4102g.mc4 @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4102; + +grammar A4102g extends de.monticore.common.TestTypes { + + symbol A = "a" Name; + + symbolrule A = + b:Set>; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4102/A4102h.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4102/A4102h.mc4 new file mode 100644 index 0000000000..08ce34d0da --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4102/A4102h.mc4 @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4102; + +grammar A4102h { + + A = "a"; + + scoperule = + b:Optional max=5; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4102/A4102i.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4102/A4102i.mc4 new file mode 100644 index 0000000000..4dc384724e --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4102/A4102i.mc4 @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4102; + +grammar A4102i { + + A = "a"; + + astrule A = + b:Optional max=*; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4118/A4118.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4118/A4118.mc4 new file mode 100644 index 0000000000..3185935be0 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4118/A4118.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos.invalid.A4118; + +grammar A4118{ + + external A; + + astrule A = Name; +} + \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4119/A4119.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4119/A4119.mc4 new file mode 100644 index 0000000000..88d211a259 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4119/A4119.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4119; + +grammar A4119 { + + @Override + @Override + A = ""; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4119/A4119a.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4119/A4119a.mc4 new file mode 100644 index 0000000000..d92c367d88 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4119/A4119a.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4119; + +grammar A4119a { + + @Deprecated + @Deprecated + A = ""; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4120/A4120.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4120/A4120.mc4 new file mode 100644 index 0000000000..0debcd281a --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4120/A4120.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos.invalid.A4120; + +grammar A4120 { + + A = "foo"; + + ABuilder = "bar"; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4121/A4121a.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4121/A4121a.mc4 new file mode 100644 index 0000000000..2faa30a3c2 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4121/A4121a.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos.invalid.A4121; + +grammar A4121a { + + symbol A = Name; + + symbol AMany = Name; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4121/A4121b.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4121/A4121b.mc4 new file mode 100644 index 0000000000..4c9c74d2bc --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4121/A4121b.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos.invalid.A4121; + +grammar A4121b { + + symbol A = Name; + + symbol AdaptedA = Name; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4122/A4122.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4122/A4122.mc4 new file mode 100644 index 0000000000..34514d03a6 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4122/A4122.mc4 @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos.invalid.A4122; + +grammar A4122 { + + symbol A = Name; + + ASymbol = "foo"; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4150/A4150.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4150/A4150.mc4 new file mode 100644 index 0000000000..0d1ba85865 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4150/A4150.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4150; + +grammar A4150 extends de.monticore.common.TestTypes, de.monticore.common.TestTypes { + + A = "foo"; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4151/A4151.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4151/A4151.mc4 new file mode 100644 index 0000000000..d13ce231f7 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4151/A4151.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4151; + +grammar A4151 extends de.monticore.common.TestTypes, de.monticore.common.TestTypes { + + A = "foo"; + symbolrule A = random:int; + symbolrule A = invalid:String; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4161/A4161.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4161/A4161.mc4 new file mode 100644 index 0000000000..223ae26ee6 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4161/A4161.mc4 @@ -0,0 +1,14 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4161; + +grammar A4161 { + + P = "a" un:"b" "c"* cg:["cg"]; + + replacekeyword "a" : "a", "aa"; + replacekeyword "c" : "c", "cc"; + + replacekeyword "b" : "b", "bb"; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4162/A4162.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4162/A4162.mc4 new file mode 100644 index 0000000000..b034f70d7a --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/invalid/A4162/A4162.mc4 @@ -0,0 +1,14 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.invalid.A4162; + +grammar A4162 { + + P = "a" un:"b" "c"* cg:["cg"]; + + replacekeyword "a" : "a", "aa"; + replacekeyword "c" : "c", "cc"; + + replacekeyword "cg" : "cg", "cgg"; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/ASTRules.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/ASTRules.mc4 new file mode 100644 index 0000000000..e0deffb3c4 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/ASTRules.mc4 @@ -0,0 +1,39 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.valid; + +grammar ASTRules { + + A; + + astrule A; + + interface B; + + astrule B; + + abstract C; + + astrule C; + + D implements B; + + E = d:D; + + astrule E = d:B; + + F astextends ASTD; + + G; + + astrule G astextends ASTD; + + interface H; + + astrule H astextends java.util.Observer; + + I; + + astrule I = d:A; // Must not trigger ASTRuleAndNTUseSameAttrNameForDiffNTs + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/Attributes.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/Attributes.mc4 new file mode 100644 index 0000000000..29464b8c2c --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/Attributes.mc4 @@ -0,0 +1,64 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.valid; + +grammar Attributes { + + A; + + abstract B; + + interface scope C; + + + D = a:A b:B c:C a:A b:B c:C; + + E extends B implements C = "e"; + + /** A ASTAutomaton represents a finite automaton + @attribute Name Name of the automaton + @attribute States List of states + @attribute Transitions List of transitions + */ + Automaton = + "automaton" Name "{" (State | Transition)* "}" ; + + /** A ASTState represents a state of a finite automaton + @attribute Name Name of state + @attribute Initial True if state is initial state + @attribute Final True if state is a final state + @attribute States List of sub states + @attribute Transitions List of transitions + */ + symbol State = + "state" Name + + (("<<" ["initial"] ">>" ) | ("<<" ["final"] ">>" ))* + + ( ("{" (State | Transition)* "}") | ";") ; + + symbolrule State = + isFinal: boolean; + + /** A ASTTransition represents a transition + @attribute From Name of the state from which the transitions starts + @attribute Input Activation signal for this transition + @attribute To Name of the state to which the transitions goes + */ + Transition = + from:Name "-" input:Name ">" to:Name ";" ; + + token Name = + ( 'a'..'z' | 'A'..'Z' | '_' | '$' )( 'a'..'z' | 'A'..'Z' | '_' | '0'..'9' | '$' )*; + + Keywords = "step" step:Name; + + Bla = token("::") Name; + + Bla2 = key("unused") Name; + + splittoken ":::"; + + nokeyword "automaton"; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/Component.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/Component.mc4 new file mode 100644 index 0000000000..80c7f6ada1 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/Component.mc4 @@ -0,0 +1,13 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.valid; + +component grammar Component { + + external A; + + abstract B; + + interface C; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/ConservativeExtensionSup.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/ConservativeExtensionSup.mc4 new file mode 100644 index 0000000000..0fbb672cca --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/ConservativeExtensionSup.mc4 @@ -0,0 +1,23 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.valid; + +grammar ConservativeExtensionSup extends de.monticore.grammar.cocos.valid.ConservativeExtensionSuper { + + A extends M = Name P? ; + + P = "-"? "some" M* ; + + C extends M = P* Name P? ; + + D extends M = Name n:Name; + + Q = M? "optional" ; + + F extends N = x:M? "one" M; + + // With Anno + @NonConservative + A extends M = Name*; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/ConservativeExtensionSuper.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/ConservativeExtensionSuper.mc4 new file mode 100644 index 0000000000..d9323b0273 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/ConservativeExtensionSuper.mc4 @@ -0,0 +1,14 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.valid; + +grammar ConservativeExtensionSuper extends de.monticore.common.TestTypes { + + M = Name; + + N = "one" M; + + P = "some" M*; + + Q = "optional" M?; +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/CorrectSymbolInheritance.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/CorrectSymbolInheritance.mc4 new file mode 100644 index 0000000000..48347698a6 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/CorrectSymbolInheritance.mc4 @@ -0,0 +1,21 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos.valid; + +grammar CorrectSymbolInheritance extends de.monticore.common.TestLexicals { + + interface symbol A = Name; + + symbol scope B implements A = "Test" Name; + + symbol C = Name; + + symbol scope D extends C = "Test2" Name; + + // Second case + interface symbol scope A2 = Name; + + B2 implements A2 = "B2" Name; + + scope C2 extends B2 = "C2" Name; + +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/Enum.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/Enum.mc4 new file mode 100644 index 0000000000..a6dbe70e8a --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/Enum.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.valid; + +grammar Enum { + + enum E = "a"; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/ExtendNTs.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/ExtendNTs.mc4 new file mode 100644 index 0000000000..c808792776 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/ExtendNTs.mc4 @@ -0,0 +1,33 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.valid; + +grammar ExtendNTs { + A; + + abstract B; + + abstract C extends A; + + abstract D extends B; + + E extends A; + + F extends B; + + interface G; + + interface H extends G; + + interface I extends G, H; + + J astextends K; + + K; + + abstract L astextends K; + + M astimplements I; + +} + diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/ImplementInterfaceNTs.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/ImplementInterfaceNTs.mc4 new file mode 100644 index 0000000000..53439ba10f --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/ImplementInterfaceNTs.mc4 @@ -0,0 +1,38 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.valid; + +grammar ImplementInterfaceNTs { + interface A = D; + + B implements A = D; + + abstract C implements A = D; + + D; + + interface Expression = operator:""; + + TestExpression implements Expression = operator:"+"; + + interface IList = D*; + + interface IList2 = D+; + + ListImpl implements IList, IList2 = D*; + + interface IOpt = D?; + + OptImpl implements IOpt = D?; + + interface ITerminal = bracketOpen:"{" bracketClosed:"}"; + + TerminalImpl implements ITerminal = bracketOpen:"{" bracketClosed:"}"; + + interface I = u:Digit; + P1 implements I = u:Char; + token Digit = '0'..'9'; + token Char = 'a'..'z'; + +} + diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/InterfaceWithoutImplementation.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/InterfaceWithoutImplementation.mc4 new file mode 100644 index 0000000000..bd132769cc --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/InterfaceWithoutImplementation.mc4 @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.valid; + +grammar InterfaceWithoutImplementation { + + interface A; + interface D extends A; + + X implements A = "x"; + Y = A; +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/InterfaceWithoutImplementation2.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/InterfaceWithoutImplementation2.mc4 new file mode 100644 index 0000000000..23bc87c398 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/InterfaceWithoutImplementation2.mc4 @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.valid; + +grammar InterfaceWithoutImplementation2 { + + interface A; + interface D extends A; + + X implements D = "x"; + Y = A; +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/InterfaceWithoutImplementation3.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/InterfaceWithoutImplementation3.mc4 new file mode 100644 index 0000000000..80f7d7ac2a --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/InterfaceWithoutImplementation3.mc4 @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.valid; + +grammar InterfaceWithoutImplementation3 { + + interface A; + interface D extends A; + + X implements D = "x"; + Y = D; +} \ No newline at end of file diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/Overriding.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/Overriding.mc4 new file mode 100644 index 0000000000..353046cf97 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/Overriding.mc4 @@ -0,0 +1,21 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.valid; + +grammar Overriding extends de.monticore.common.TestTypes { + + @Override + abstract ArrayType; + + @Override + QualifiedName; + + // Override mc.literals.Literals + @Override + interface Literal extends Expression; + + Expression; + + A extends Name; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/Overriding2.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/Overriding2.mc4 new file mode 100644 index 0000000000..564b16114b --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/Overriding2.mc4 @@ -0,0 +1,17 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.valid; + +grammar Overriding2 extends de.monticore.common.TestTypes { + + + QualifiedName; + + // Override mc.literals.Literals + interface Literal extends Expression; + + Expression; + + + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/ProdAndExtendedProdUseSameAttrNameForDiffNTs.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/ProdAndExtendedProdUseSameAttrNameForDiffNTs.mc4 new file mode 100644 index 0000000000..59a3f7a144 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/ProdAndExtendedProdUseSameAttrNameForDiffNTs.mc4 @@ -0,0 +1,36 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos.valid; + +grammar ProdAndExtendedProdUseSameAttrNameForDiffNTs{ + + A; + + B; + + Super1 = x:B; + + Sub1 extends Super1 = c:A; + + Super2 = c:A; + + Sub2 extends Super2 = c:A; + + Super3 = "c"; + + Sub3 extends Super3 = c:A; + + State = + "state" Name + ( ("{" (State)* "}") | ";") ; + + ActState extends State = + "state" Name + ("entry:" entry:Name)? + ( ("{" (State)* "}") | ";") ; + + Super4 = "Bla"; + + Sub4_1 extends Super4 = c:A; + + Sub4_2 extends Super4 = c:A; +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/ProdAndOverriddenProdUseSameAttrNameForDiffNTs_sub.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/ProdAndOverriddenProdUseSameAttrNameForDiffNTs_sub.mc4 new file mode 100644 index 0000000000..54dfb979ca --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/ProdAndOverriddenProdUseSameAttrNameForDiffNTs_sub.mc4 @@ -0,0 +1,22 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos.valid; + +grammar ProdAndOverriddenProdUseSameAttrNameForDiffNTs_sub extends + de.monticore.grammar.cocos.valid.ProdAndOverriddenProdUseSameAttrNameForDiffNTs_super{ + + B; + + Prod1 = c:B; + + Prod2 = c:B; + + Prod3 = c:B; + + State = + "state" Name + ("entry:" entry:Name)? + ( ("{" (State)* "}") | ";") ; + + Prod4 = u:Digit | u:Char; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/ProdAndOverriddenProdUseSameAttrNameForDiffNTs_super.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/ProdAndOverriddenProdUseSameAttrNameForDiffNTs_super.mc4 new file mode 100644 index 0000000000..45b1989e58 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/ProdAndOverriddenProdUseSameAttrNameForDiffNTs_super.mc4 @@ -0,0 +1,22 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos.valid; + +grammar ProdAndOverriddenProdUseSameAttrNameForDiffNTs_super{ + + A; + + Prod1 = x:A; + + Prod2 = c:B; + + Prod3 = "b"; + + State = + "state" Name + ( ("{" (State)* "}") | ";") ; + + Prod4 = u:Digit | u:Char; + token Digit = '0'..'9'; + token Char = 'a'..'z'; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/ProdExtendsNotExistingProd.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/ProdExtendsNotExistingProd.mc4 new file mode 100644 index 0000000000..41a05af00e --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/ProdExtendsNotExistingProd.mc4 @@ -0,0 +1,14 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.grammar.cocos.valid; + +grammar ProdExtendsNotExistingProd { + + interface ISub; + + Super = "A"; + + Sup extends Super = "a"; + + Sub implements ISub = "b"; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/ReferencedSymbol.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/ReferencedSymbol.mc4 new file mode 100644 index 0000000000..dcb2c35980 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/ReferencedSymbol.mc4 @@ -0,0 +1,16 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.valid; + +grammar ReferencedSymbol extends de.monticore.grammar.cocos.valid.SubGrammarWithSymbol, + de.monticore.grammar.cocos.valid.Attributes{ + + symbol A = Name; + + B = bname:Name@A; + + D = sub:Name@SubSymbol; + + S = state:Name@State; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/ReplaceKeyword.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/ReplaceKeyword.mc4 new file mode 100644 index 0000000000..168dcffea7 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/ReplaceKeyword.mc4 @@ -0,0 +1,13 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.valid; + +grammar ReplaceKeyword { + + P = "a" un:"b" "c"* cg:["cg"]; + + replacekeyword "a" : "a", "aa"; + replacekeyword "c" : "c", "cc"; + + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/ScopeRule.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/ScopeRule.mc4 new file mode 100644 index 0000000000..d077a74ddb --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/ScopeRule.mc4 @@ -0,0 +1,15 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.valid; + +grammar ScopeRule { + + symbol A = Name; + + scoperule = + name:String* + foo:int + gen:List + opt:String?; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/SubGrammarWithSymbol.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/SubGrammarWithSymbol.mc4 new file mode 100644 index 0000000000..1d2e24c544 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/SubGrammarWithSymbol.mc4 @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.valid; + +grammar SubGrammarWithSymbol extends de.monticore.common.TestTypes{ + + symbol SubSymbol = Name "sub"; + + SubNonTer = Name "NonTer"; + + scope SubScope = Name; +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/SymbolAndScopeOverwriting.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/SymbolAndScopeOverwriting.mc4 new file mode 100644 index 0000000000..fb12c6276b --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/SymbolAndScopeOverwriting.mc4 @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.valid; + +grammar SymbolAndScopeOverwriting extends de.monticore.grammar.cocos.valid.SubGrammarWithSymbol { + + SubSymbol = Name "sub"; + + symbol scope SubNonTer = Name "NonTer"; + + SubScope = Name; +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/SymbolRules.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/SymbolRules.mc4 new file mode 100644 index 0000000000..529ce53174 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/SymbolRules.mc4 @@ -0,0 +1,40 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.valid; + +grammar SymbolRules extends de.monticore.common.TestTypes { + + symbol A = Name; + + symbolrule A implements ISymbol; + + interface symbol B; + + symbolrule B = + name:String* + foo:int + gen:List + opt:String?; + + abstract symbol C = Name; + + symbolrule C = + bla:boolean + method public int getDepth() {} +; + + D implements B; + + symbol E = d:D; + + symbolrule E = d:B; + + symbol F = Name; + + symbolrule F extends ESymbol; + + interface symbol H; + + symbolrule H extends java.util.Observer; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/enum/Enum.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/enum/Enum.mc4 new file mode 100644 index 0000000000..763800893e --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/cocos/valid/enum/Enum.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.cocos.valid.enum; + +grammar Enum extends cocos.valid.Enum { + + A = "a"; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/grammar/pack/DifferentPackage.mc4 b/monticore-grammar/src/test/resources/de/monticore/grammar/pack/DifferentPackage.mc4 new file mode 100644 index 0000000000..8cb53b0da5 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/grammar/pack/DifferentPackage.mc4 @@ -0,0 +1,7 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.grammar.pack; + +component grammar DifferentPackage { + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/inherited/Subgrammar.mc4 b/monticore-grammar/src/test/resources/de/monticore/inherited/Subgrammar.mc4 new file mode 100644 index 0000000000..22b9f38ff0 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/inherited/Subgrammar.mc4 @@ -0,0 +1,24 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.inherited; + +grammar Subgrammar extends de.monticore.inherited.Supergrammar { + + A = X; + + B extends X = D; + + M = K; + + D = L; + + // Y = "KK"; + + L implements G = "JJ"; + + O = X*; + + @Override + N = X?; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/inherited/Subsubgrammar.mc4 b/monticore-grammar/src/test/resources/de/monticore/inherited/Subsubgrammar.mc4 new file mode 100644 index 0000000000..114ab99ea1 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/inherited/Subsubgrammar.mc4 @@ -0,0 +1,16 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.inherited; + +grammar Subsubgrammar extends de.monticore.inherited.Subgrammar { + + @Override + N = X? D; + +// P extends X = D; + + S = X*; + +// X = Y "HH" J D; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/inherited/Supergrammar.mc4 b/monticore-grammar/src/test/resources/de/monticore/inherited/Supergrammar.mc4 new file mode 100644 index 0000000000..665aade3a8 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/inherited/Supergrammar.mc4 @@ -0,0 +1,18 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.inherited; + +component grammar Supergrammar { + + X = Y "HH" J; + + Y = G J; + + J = K; + + interface G; + + K implements G = "OO"; + + N; +} diff --git a/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0802/A0802.java b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0802/A0802.java new file mode 100644 index 0000000000..43c624e6a4 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0802/A0802.java @@ -0,0 +1,2 @@ +/* (c) https://github.com/MontiCore/monticore */ +private abstract void a(){} diff --git a/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0803/A0803.java b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0803/A0803.java new file mode 100644 index 0000000000..336e9bbdaf --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0803/A0803.java @@ -0,0 +1,2 @@ +/* (c) https://github.com/MontiCore/monticore */ +private abstract void method(){} diff --git a/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0803/A0803a.java b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0803/A0803a.java new file mode 100644 index 0000000000..ab20bee271 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0803/A0803a.java @@ -0,0 +1,2 @@ +/* (c) https://github.com/MontiCore/monticore */ +public native void method(){} diff --git a/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0804/A0804.java b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0804/A0804.java new file mode 100644 index 0000000000..98241a1bb0 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0804/A0804.java @@ -0,0 +1,2 @@ +/* (c) https://github.com/MontiCore/monticore */ +public void method(); diff --git a/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0804/A0804a.java b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0804/A0804a.java new file mode 100644 index 0000000000..2806ef73cd --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0804/A0804a.java @@ -0,0 +1,2 @@ +/* (c) https://github.com/MontiCore/monticore */ +private void method (); diff --git a/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0808/A0808.java b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0808/A0808.java new file mode 100644 index 0000000000..3f1501e3cc --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0808/A0808.java @@ -0,0 +1,2 @@ +/* (c) https://github.com/MontiCore/monticore */ +public public constructor(){} diff --git a/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0809/A0809.java b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0809/A0809.java new file mode 100644 index 0000000000..0035fa0497 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0809/A0809.java @@ -0,0 +1,2 @@ +/* (c) https://github.com/MontiCore/monticore */ +public protected constructor(){} diff --git a/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0811/A0811.java b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0811/A0811.java new file mode 100644 index 0000000000..12d6413248 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0811/A0811.java @@ -0,0 +1,2 @@ +/* (c) https://github.com/MontiCore/monticore */ +public void meth1() throws A {} diff --git a/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0812/A0812.java b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0812/A0812.java new file mode 100644 index 0000000000..87a1e96fac --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0812/A0812.java @@ -0,0 +1,2 @@ +/* (c) https://github.com/MontiCore/monticore */ +public void meth1(int i, boolean i) {} diff --git a/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0812/A0812a.java b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0812/A0812a.java new file mode 100644 index 0000000000..9db1974c8c --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0812/A0812a.java @@ -0,0 +1,2 @@ +/* (c) https://github.com/MontiCore/monticore */ +public void meth1(int i, String... i) {} diff --git a/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0819/A0819.java b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0819/A0819.java new file mode 100644 index 0000000000..faf091a064 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0819/A0819.java @@ -0,0 +1,2 @@ +/* (c) https://github.com/MontiCore/monticore */ +native strictfp void method() {} diff --git a/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0820/A0820.java b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0820/A0820.java new file mode 100644 index 0000000000..acefa40a9d --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0820/A0820.java @@ -0,0 +1,2 @@ +/* (c) https://github.com/MontiCore/monticore */ +public final constructor(){} diff --git a/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0821/A0821.java b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0821/A0821.java new file mode 100644 index 0000000000..b3b6cd2410 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0821/A0821.java @@ -0,0 +1,2 @@ +/* (c) https://github.com/MontiCore/monticore */ +public const1(int i, boolean i) {} diff --git a/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0821/A0821a.java b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0821/A0821a.java new file mode 100644 index 0000000000..cdb5ef60c2 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/invalid/A0821/A0821a.java @@ -0,0 +1,2 @@ +/* (c) https://github.com/MontiCore/monticore */ +public const1(int i, String... i) {} diff --git a/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/valid/A0802.java b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/valid/A0802.java new file mode 100644 index 0000000000..b25c4d58e4 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/valid/A0802.java @@ -0,0 +1,2 @@ +/* (c) https://github.com/MontiCore/monticore */ +abstract public a() {} diff --git a/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/valid/A0803.java b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/valid/A0803.java new file mode 100644 index 0000000000..7b2f586567 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/valid/A0803.java @@ -0,0 +1,2 @@ +/* (c) https://github.com/MontiCore/monticore */ +public void method(){} diff --git a/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/valid/A0804.java b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/valid/A0804.java new file mode 100644 index 0000000000..342106fa8d --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/valid/A0804.java @@ -0,0 +1,2 @@ +/* (c) https://github.com/MontiCore/monticore */ +abstract void method(); diff --git a/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/valid/A0808.java b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/valid/A0808.java new file mode 100644 index 0000000000..a1651da9e3 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/valid/A0808.java @@ -0,0 +1,2 @@ +/* (c) https://github.com/MontiCore/monticore */ +static constructor(){} diff --git a/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/valid/A0809.java b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/valid/A0809.java new file mode 100644 index 0000000000..c1350692f9 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/valid/A0809.java @@ -0,0 +1,2 @@ +/* (c) https://github.com/MontiCore/monticore */ +private constructor(){} diff --git a/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/valid/A0819.java b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/valid/A0819.java new file mode 100644 index 0000000000..737a8bc684 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/valid/A0819.java @@ -0,0 +1,2 @@ +/* (c) https://github.com/MontiCore/monticore */ +public strictfp method(){} diff --git a/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/valid/A0820.java b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/valid/A0820.java new file mode 100644 index 0000000000..2a1ce4deeb --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/valid/A0820.java @@ -0,0 +1,2 @@ +/* (c) https://github.com/MontiCore/monticore */ +public constructor(){} diff --git a/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/valid/A0821.java b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/valid/A0821.java new file mode 100644 index 0000000000..c6718e776f --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/valid/A0821.java @@ -0,0 +1,2 @@ +/* (c) https://github.com/MontiCore/monticore */ +public const1(int i, boolean b){} diff --git a/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/valid/MethodDecl.java b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/valid/MethodDecl.java new file mode 100644 index 0000000000..dfed6e644c --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/javalight/cocos/valid/MethodDecl.java @@ -0,0 +1,2 @@ +/* (c) https://github.com/MontiCore/monticore */ +public void meth1(int i, boolean b) throws A {} diff --git a/monticore-grammar/src/test/resources/de/monticore/point.in.packagename/PackagePathTest.mc4 b/monticore-grammar/src/test/resources/de/monticore/point.in.packagename/PackagePathTest.mc4 new file mode 100644 index 0000000000..8b0d65c15b --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/point.in.packagename/PackagePathTest.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ +package point.in.packagename; + +// grammar used to checked if points are within a packeagename the package is wrong +grammar PackagePathTest extends de.monticore.common.TestLexicals { + + Test = Name; + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/script/AutomatonV1.mc4 b/monticore-grammar/src/test/resources/de/monticore/script/AutomatonV1.mc4 new file mode 100644 index 0000000000..9e29cdf7f6 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/script/AutomatonV1.mc4 @@ -0,0 +1,7 @@ +/* (c) https://github.com/MontiCore/monticore */ + +grammar AutomatonV1 extends Lexicals { + Automaton = Name ( State | Transition )* ; + State = Name ( "initial" | "final" )* ; + Transition = Name Name Name ; // = from input to +} diff --git a/monticore-grammar/src/test/resources/de/monticore/script/AutomatonV2.mc4 b/monticore-grammar/src/test/resources/de/monticore/script/AutomatonV2.mc4 new file mode 100644 index 0000000000..c839c83e5d --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/script/AutomatonV2.mc4 @@ -0,0 +1,7 @@ +/* (c) https://github.com/MontiCore/monticore */ + +grammar AutomatonV2 extends Lexicals { + Automaton = Name ( State | Transition )* ; + State = Name ( ["initial"] | ["final"] )* ; + Transition = from:Name input:Name to:Name ; +} diff --git a/monticore-grammar/src/test/resources/de/monticore/script/AutomatonV3.mc4 b/monticore-grammar/src/test/resources/de/monticore/script/AutomatonV3.mc4 new file mode 100644 index 0000000000..841a89f59c --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/script/AutomatonV3.mc4 @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +grammar Automaton extends Lexicals { + Automaton = "automaton" Name + "{" ( State | Transition )* "}" ; + + State = "state" Name + ("<<" ["initial"] ">>" | "<<" ["final"] ">>" )* ";" ; + + Transition = from:Name "-" input:Name ">" to:Name ";" ; +} diff --git a/monticore-grammar/src/test/resources/de/monticore/script/AutomatonWithInvs.mc4 b/monticore-grammar/src/test/resources/de/monticore/script/AutomatonWithInvs.mc4 new file mode 100644 index 0000000000..b55775fa78 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/script/AutomatonWithInvs.mc4 @@ -0,0 +1,6 @@ +/* (c) https://github.com/MontiCore/monticore */ + + +grammar Automaton extends InvAutomaton, Expression { + Invariant = Exp; +} diff --git a/monticore-grammar/src/test/resources/de/monticore/script/AutomatonWithInvsAndStartRule.mc4 b/monticore-grammar/src/test/resources/de/monticore/script/AutomatonWithInvsAndStartRule.mc4 new file mode 100644 index 0000000000..b58ba24691 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/script/AutomatonWithInvsAndStartRule.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + + +grammar Automaton extends InvAutomaton, Expression { + + start Automaton; + + Invariant = Exp; +} diff --git a/monticore-grammar/src/test/resources/de/monticore/script/AutomatonWithInvsComp.mc4 b/monticore-grammar/src/test/resources/de/monticore/script/AutomatonWithInvsComp.mc4 new file mode 100644 index 0000000000..f6d5bf7609 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/script/AutomatonWithInvsComp.mc4 @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +component grammar InvAutomaton { + external Invariant; + + State = "state" Name + Invariant + ( "<<" ["initial"] ">>" | "<<" ["final"] ">>" )* ";" ; +} diff --git a/monticore-grammar/src/test/resources/de/monticore/script/HierarchicalAutomaton.mc4 b/monticore-grammar/src/test/resources/de/monticore/script/HierarchicalAutomaton.mc4 new file mode 100644 index 0000000000..6a99b155cd --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/script/HierarchicalAutomaton.mc4 @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ + +grammar HierarchicalAutomaton extends Automaton { + State = "state" Name + ( "<<" ["initial"] ">>" | + "<<" ["final"] ">>" )* + ( ";" | "{" State* "}" ) ; +} diff --git a/monticore-grammar/src/test/resources/de/monticore/script/ScriptExample.mc4 b/monticore-grammar/src/test/resources/de/monticore/script/ScriptExample.mc4 new file mode 100644 index 0000000000..50f9d6ddbf --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/script/ScriptExample.mc4 @@ -0,0 +1,161 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.script; + +grammar ScriptExample { + + //-- extractfile gen/WhiteSpaces.x + token WS = + (' ' + | '\t' + | '\r' '\n' // DOS + | '\r' // Macintosh + | '\n' // Unix + ) :{_channel = HIDDEN;}; + //-- endfile gen/WhiteSpaces.x + + //-- extractfile gen/Token.x + token Name = ('a'..'z'|'A'..'Z')+ ; + + token SIMPLESTRING = '\"' ('a'..'z'|'A'..'Z')*'\"'; + //-- endfile gen/Token.x + + //-- extractfile gen/LexicalsFragment.x + token NUM_INT = + ('0'..'9')+ (EXPONENT)? (SUFFIX)? ; + + fragment token SUFFIX = + 'f'|'F'|'d'|'D' ; + + fragment token EXPONENT = + ('e'|'E') ('+'|'-')? ('0'..'9')+ ; + //-- endfile gen/LexicalsFragment.x + + //-- extractfile gen/LexicalsAction.x + token STRING = '"' + ( ESC + | ~('"' | '\\' | '\n' | '\r' ) + )* + '"' + :{setText(getText().substring(1, getText().length() - 1));}; + //-- endfile gen/LexicalsActions.x + + //-- extractfile gen/LexicalsAdaptedTypes1.x + A = b:Name c:Name; + + token NUMBER = + ('0'..'9')* '.' ('0'..'9')* 'f': float; + + A = b:NUMBER c:NUMBER*; + //-- endfile gen/LexicalsAdaptedTypes1.x + + + //-- extractfile gen/LexicalsAdaptedTypes2.x + token + CARDINALITY = ('0'..'9')+ | '*' : + x -> int : { // Java code: + if (x.equals("*")) + return -1; + else + return Integer.parseInt(x.getText()); + }; + + //-- endfile gen/LexicalsAdaptedTypes2.x + + //-- extractfile gen/Terminals.x + A = "Hello" + (who: "World" | who: "Tom") + "!"; + + B = ["initial"]?; + //-- endfile gen/Terminals.x + + //-- extractfile gen/Enumerations1.x + A = vis:[ PUBLIC:"+" | "public" | + PRIVATE:"-" | "private"]; + //-- endfile gen/Enumerations1.x + + + //-- extractfile gen/Enumerations2.x + enum VISIBILITY = + PUBLIC:"+" | "public" | + PRIVATE:"-" | "private" ; + A = vis:VISIBILITY; + //-- endfile gen/Enumerations2.x + + //-- extractfile gen/AutomaticNaming1.x + Automaton = + "automaton" name:Name "{" + ( state:State | transition:Transition )* + "}"; + //-- endfile gen/AutomaticNaming1.x + + //-- extractfile gen/AutomaticNaming2.x + Automaton = + "automaton" Name "{" + ( State | Transition )* + "}"; + //-- endfile gen/AutomaticNaming2.x + + //-- extractfile gen/Sequence.x + A; + + S = x:A y:A*; + S = x:A "::=" y:A ("," y:A)*; + //-- endfile gen/Sequence.x + + //-- extractfile gen/Interfaces.x + interface I; + A implements I = "...1"; + B implements I = "...2"; + C = I "..."; + //-- endfile gen/Interfaces.x + + //-- extractfile gen/InterfacesEquiv.x + I = A | B; + A = "...1"; + B = "...2"; + C = I "..."; + //-- endfile gen/InterfacesEquiv.x + + //-- extractfile gen/Subclassing.x + A = "...1"; + B extends A = "...2"; + C = A; + //-- endfile gen/Subclassing.x + + //-- extractfile gen/ASTRule.x + State; + astrule State = + reachableStates:State* + method public boolean isReachable(State s) { + return reachableStates.contains(sss); + }; + //-- endfile gen/ASTRule.x + + //-- extractfile gen/AbstractProd.x + abstract AutomatonElement; + + State extends AutomatonElement = "..." ; + + Transition extends AutomatonElement = "..." ; + //-- endfile gen/AbstractProd.x + + //-- extractfile gen/ExternalProd.x + external Invariant; + + State = "state" Name + Invariant + ( "<<" ["initial"] ">>" | "<<" ["final"] ">>" )* ";" ; + //-- endfile gen/ExternalProd.x + + //-- extractfile gen/OptionalNTs.x + A = B?; + B = "x"; + C = (D | E); + D = "y"; + E = "z"; + //-- endfile gen/OptionalNTs.x + + +} diff --git a/monticore-grammar/src/test/resources/de/monticore/types/check/Iterator.cesym b/monticore-grammar/src/test/resources/de/monticore/types/check/Iterator.cesym new file mode 100644 index 0000000000..b75747c5e8 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/types/check/Iterator.cesym @@ -0,0 +1,20 @@ +{ + "generated-using": "www.MontiCore.de technology", + "name": "Iterator", + "package": "java.util", + "symbols": [ + { + "kind": "de.monticore.symbols.oosymbols._symboltable.OOTypeSymbol", + "name": "Iterator", + "isClass": false, + "isInterface": false, + "isEnum": false, + "isAbstract": false, + "isPrivate": false, + "isProtected": false, + "isPublic": false, + "isStatic": false, + "isFinal": false + } + ] +} diff --git a/monticore-grammar/src/test/resources/de/monticore/types/check/Pair.cesym b/monticore-grammar/src/test/resources/de/monticore/types/check/Pair.cesym new file mode 100644 index 0000000000..198dffc510 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/types/check/Pair.cesym @@ -0,0 +1,20 @@ +{ + "generated-using": "www.MontiCore.de technology", + "name": "Pair", + "package": "de.util", + "symbols": [ + { + "kind": "de.monticore.symbols.oosymbols._symboltable.OOTypeSymbol", + "name": "Pair", + "isClass": false, + "isInterface": false, + "isEnum": false, + "isAbstract": false, + "isPrivate": false, + "isProtected": false, + "isPublic": false, + "isStatic": false, + "isFinal": false + } + ] +} diff --git a/monticore-grammar/src/test/resources/de/monticore/types/check/Pair2.cesym b/monticore-grammar/src/test/resources/de/monticore/types/check/Pair2.cesym new file mode 100644 index 0000000000..cb06a88393 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/types/check/Pair2.cesym @@ -0,0 +1,20 @@ +{ + "generated-using": "www.MontiCore.de technology", + "name": "Pair2", + "package": "de.util", + "symbols": [ + { + "kind": "de.monticore.symbols.oosymbols._symboltable.OOTypeSymbol", + "name": "Pair2", + "isClass": false, + "isInterface": false, + "isEnum": false, + "isAbstract": false, + "isPrivate": false, + "isProtected": false, + "isPublic": false, + "isStatic": false, + "isFinal": false + } + ] +} diff --git a/monticore-grammar/src/test/resources/de/monticore/types/check/PairA.cesym b/monticore-grammar/src/test/resources/de/monticore/types/check/PairA.cesym new file mode 100644 index 0000000000..52c99c01cd --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/types/check/PairA.cesym @@ -0,0 +1,20 @@ +{ + "generated-using": "www.MontiCore.de technology", + "name": "PairA", + "package": "de.mc", + "symbols": [ + { + "kind": "de.monticore.symbols.oosymbols._symboltable.OOTypeSymbol", + "name": "PairA", + "isClass": false, + "isInterface": false, + "isEnum": false, + "isAbstract": false, + "isPrivate": false, + "isProtected": false, + "isPublic": false, + "isStatic": false, + "isFinal": false + } + ] +} diff --git a/monticore-grammar/src/test/resources/de/monticore/types/check/PairB.cesym b/monticore-grammar/src/test/resources/de/monticore/types/check/PairB.cesym new file mode 100644 index 0000000000..11d4cb5526 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/types/check/PairB.cesym @@ -0,0 +1,20 @@ +{ + "generated-using": "www.MontiCore.de technology", + "name": "PairB", + "package": "de.mc", + "symbols": [ + { + "kind": "de.monticore.symbols.oosymbols._symboltable.OOTypeSymbol", + "name": "PairB", + "isClass": false, + "isInterface": false, + "isEnum": false, + "isAbstract": false, + "isPrivate": false, + "isProtected": false, + "isPublic": false, + "isStatic": false, + "isFinal": false + } + ] +} diff --git a/monticore-grammar/src/test/resources/de/monticore/types/check/PairC.cesym b/monticore-grammar/src/test/resources/de/monticore/types/check/PairC.cesym new file mode 100644 index 0000000000..9487f3a76a --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/types/check/PairC.cesym @@ -0,0 +1,20 @@ +{ + "generated-using": "www.MontiCore.de technology", + "name": "PairC", + "package": "de.mc", + "symbols": [ + { + "kind": "de.monticore.symbols.oosymbols._symboltable.OOTypeSymbol", + "name": "PairC", + "isClass": false, + "isInterface": false, + "isEnum": false, + "isAbstract": false, + "isPrivate": false, + "isProtected": false, + "isPublic": false, + "isStatic": false, + "isFinal": false + } + ] +} diff --git a/monticore-grammar/src/test/resources/de/monticore/types/check/PersonKey.cesym b/monticore-grammar/src/test/resources/de/monticore/types/check/PersonKey.cesym new file mode 100644 index 0000000000..b82553a13b --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/types/check/PersonKey.cesym @@ -0,0 +1,20 @@ +{ + "generated-using": "www.MontiCore.de technology", + "name": "PersonKey", + "package": "de.mc", + "symbols": [ + { + "kind": "de.monticore.symbols.oosymbols._symboltable.OOTypeSymbol", + "name": "PersonKey", + "isClass": false, + "isInterface": false, + "isEnum": false, + "isAbstract": false, + "isPrivate": false, + "isProtected": false, + "isPublic": false, + "isStatic": false, + "isFinal": false + } + ] +} diff --git a/monticore-grammar/src/test/resources/de/monticore/types/check/PersonValue.cesym b/monticore-grammar/src/test/resources/de/monticore/types/check/PersonValue.cesym new file mode 100644 index 0000000000..4b997db63e --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/types/check/PersonValue.cesym @@ -0,0 +1,20 @@ +{ + "generated-using": "www.MontiCore.de technology", + "name": "PersonValue", + "package": "de.mc", + "symbols": [ + { + "kind": "de.monticore.symbols.oosymbols._symboltable.OOTypeSymbol", + "name": "PersonValue", + "isClass": false, + "isInterface": false, + "isEnum": false, + "isAbstract": false, + "isPrivate": false, + "isProtected": false, + "isPublic": false, + "isStatic": false, + "isFinal": false + } + ] +} diff --git a/monticore-grammar/src/test/resources/de/monticore/types/check/Personaz.cesym b/monticore-grammar/src/test/resources/de/monticore/types/check/Personaz.cesym new file mode 100644 index 0000000000..e213f411c9 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/types/check/Personaz.cesym @@ -0,0 +1,20 @@ +{ + "generated-using": "www.MontiCore.de technology", + "name": "Person", + "package": "a.z", + "symbols": [ + { + "kind": "de.monticore.symbols.oosymbols._symboltable.OOTypeSymbol", + "name": "Person", + "isClass": false, + "isInterface": false, + "isEnum": false, + "isAbstract": false, + "isPrivate": false, + "isProtected": false, + "isPublic": false, + "isStatic": false, + "isFinal": false + } + ] +} diff --git a/monticore-grammar/src/test/resources/de/monticore/types/check/Persondemc.cesym b/monticore-grammar/src/test/resources/de/monticore/types/check/Persondemc.cesym new file mode 100644 index 0000000000..6f15554c81 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/types/check/Persondemc.cesym @@ -0,0 +1,20 @@ +{ + "generated-using": "www.MontiCore.de technology", + "name": "Person", + "package": "de.mc", + "symbols": [ + { + "kind": "de.monticore.symbols.oosymbols._symboltable.OOTypeSymbol", + "name": "Person", + "isClass": false, + "isInterface": false, + "isEnum": false, + "isAbstract": false, + "isPrivate": false, + "isProtected": false, + "isPublic": false, + "isStatic": false, + "isFinal": false + } + ] +} diff --git a/monticore-grammar/src/test/resources/de/monticore/types/check/Persondex.cesym b/monticore-grammar/src/test/resources/de/monticore/types/check/Persondex.cesym new file mode 100644 index 0000000000..4605cfd4e8 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/types/check/Persondex.cesym @@ -0,0 +1,20 @@ +{ + "generated-using": "www.MontiCore.de technology", + "name": "Person", + "package": "de.x", + "symbols": [ + { + "kind": "de.monticore.symbols.oosymbols._symboltable.OOTypeSymbol", + "name": "Person", + "isClass": false, + "isInterface": false, + "isEnum": false, + "isAbstract": false, + "isPrivate": false, + "isProtected": false, + "isPublic": false, + "isStatic": false, + "isFinal": false + } + ] +} diff --git a/monticore-grammar/src/test/resources/de/monticore/types/check/Personjl.cesym b/monticore-grammar/src/test/resources/de/monticore/types/check/Personjl.cesym new file mode 100644 index 0000000000..fbf3551d06 --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/types/check/Personjl.cesym @@ -0,0 +1,20 @@ +{ + "generated-using": "www.MontiCore.de technology", + "name": "Person", + "package": "java.lang", + "symbols": [ + { + "kind": "de.monticore.symbols.oosymbols._symboltable.OOTypeSymbol", + "name": "Person", + "isClass": false, + "isInterface": false, + "isEnum": false, + "isAbstract": false, + "isPrivate": false, + "isProtected": false, + "isPublic": false, + "isStatic": false, + "isFinal": false + } + ] +} diff --git a/monticore-grammar/src/test/resources/de/monticore/types/check/Personju.cesym b/monticore-grammar/src/test/resources/de/monticore/types/check/Personju.cesym new file mode 100644 index 0000000000..418c8239bf --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/types/check/Personju.cesym @@ -0,0 +1,20 @@ +{ + "generated-using": "www.MontiCore.de technology", + "name": "Person", + "package": "java.util", + "symbols": [ + { + "kind": "de.monticore.symbols.oosymbols._symboltable.OOTypeSymbol", + "name": "Person", + "isClass": false, + "isInterface": false, + "isEnum": false, + "isAbstract": false, + "isPrivate": false, + "isProtected": false, + "isPublic": false, + "isStatic": false, + "isFinal": false + } + ] +} diff --git a/monticore-grammar/src/test/resources/de/monticore/types/check/String.cesym b/monticore-grammar/src/test/resources/de/monticore/types/check/String.cesym new file mode 100644 index 0000000000..4154b2475b --- /dev/null +++ b/monticore-grammar/src/test/resources/de/monticore/types/check/String.cesym @@ -0,0 +1,20 @@ +{ + "generated-using": "www.MontiCore.de technology", + "name": "String", + "package": "java.lang", + "symbols": [ + { + "kind": "de.monticore.symbols.oosymbols._symboltable.OOTypeSymbol", + "name": "String", + "isClass": false, + "isInterface": false, + "isEnum": false, + "isAbstract": false, + "isPrivate": false, + "isProtected": false, + "isPublic": false, + "isStatic": false, + "isFinal": false + } + ] +} diff --git a/monticore-grammar/src/test/resources/logback.groovy b/monticore-grammar/src/test/resources/logback.groovy new file mode 100644 index 0000000000..3c95e7453d --- /dev/null +++ b/monticore-grammar/src/test/resources/logback.groovy @@ -0,0 +1,26 @@ +/* (c) https://github.com/MontiCore/monticore */ + +// this is a very user friendly console appender +// which only outputs level >= INFO +appender("CONSOLE", ConsoleAppender) { + filter(ch.qos.logback.classic.filter.ThresholdFilter) { + level = INFO + } + encoder(PatternLayoutEncoder) { + pattern = "%-7([%level]) %message%n" + } +} + +def bySecond = timestamp("yyyy-MM-dd-HHmmss") +def out = System.getProperty("MC_OUT") == null ? "target" : System.getProperty("MC_OUT") + +// this is a rather technically detailed file appender +appender("FILE", FileAppender) { + file = "${out}/monticore.test.${bySecond}.log" + encoder(PatternLayoutEncoder) { + pattern = "%date{yyyy-MM-dd HH:mm:ss} %-7([%level]) %logger{26} %message%n" + } +} + +// everything with level >= DEBUG is logged to the file (see above) +root(DEBUG, ["FILE", "CONSOLE"]) diff --git a/monticore-runtime/build.gradle b/monticore-runtime/build.gradle new file mode 100644 index 0000000000..b8f4278244 --- /dev/null +++ b/monticore-runtime/build.gradle @@ -0,0 +1,44 @@ +/* (c) https://github.com/MontiCore/monticore */ +plugins { + id "jacoco" +} + +configurations { + testsImplementation.extendsFrom(testImplementation) +} + +dependencies { + api 'de.se_rwth.commons:se-commons-logging:' + se_commons_version + api "org.antlr:antlr4-runtime:$antlr_version" + api "commons-cli:commons-cli:$commons_cli_version" + api "com.google.guava:guava:$guava_version" + + implementation 'de.se_rwth.commons:se-commons-utilities:' + se_commons_version + implementation 'org.reflections:reflections:0.9.9' + implementation "org.freemarker:freemarker:$freemarker_version" + implementation group: 'org.apache.commons', name: 'commons-text', version: '1.10.0' + testImplementation "junit:junit:$junit_version" +} + +sourceSets { + test { + resources { + srcDirs += "src/test/java" + includes = ["**/*.ftl"] + } + } + tests.java{ + srcDirs += "$projectDir/src/test/java" + includes = ["**/Assert.java"] + } +} + +description = 'MontiCore: Runtime' + +java { + registerFeature('tests') { + usingSourceSet(sourceSets.tests) + } +} + +jar.dependsOn testsJar diff --git a/monticore-runtime/src/main/java/de/monticore/AmbiguityException.java b/monticore-runtime/src/main/java/de/monticore/AmbiguityException.java new file mode 100644 index 0000000000..544b9d4ec0 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/AmbiguityException.java @@ -0,0 +1,34 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore; + +/** + * This exception is thrown whenever a model or resource is ambiguously specified. Common examples + * are two models in the modelpath sharing the same fully qualified name or two symbols in the + * symboltable sharing an identifier. + * + */ +public class AmbiguityException extends RuntimeException { + + private static final long serialVersionUID = 2754767948180345585L; + + protected String[] ambiguities = new String[] {}; + + public AmbiguityException() { + } + + public AmbiguityException(String message, String... ambiguities) { + super(message); + this.ambiguities = ambiguities; + } + + @Override + public String getMessage() { + StringBuilder builder = new StringBuilder("Ambiguities:\n"); + for (String ambiguity : ambiguities) { + builder.append(ambiguity + "\n"); + } + builder.append(super.getMessage()); + return builder.toString(); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/MontiCoreConstants.java b/monticore-runtime/src/main/java/de/monticore/MontiCoreConstants.java new file mode 100644 index 0000000000..35fae9c27d --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/MontiCoreConstants.java @@ -0,0 +1,22 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + +/** + * Common constants in MontiCore. + * + */ +public final class MontiCoreConstants { + + public static final Charset DEFAULT_MODELFILE_CHARSET = StandardCharsets.UTF_8; + + /** + * Private constructor permitting manual instantiation. + */ + private MontiCoreConstants() { + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/antlr4/MCConcreteParser.java b/monticore-runtime/src/main/java/de/monticore/antlr4/MCConcreteParser.java new file mode 100644 index 0000000000..cd0af32a7c --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/antlr4/MCConcreteParser.java @@ -0,0 +1,65 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.antlr4; + +import java.io.IOException; +import java.io.Reader; +import java.util.Optional; + +import de.monticore.ast.ASTNode; + +/** + * A MCConcreteParser is used for every single language. MCConcreteParser wrap + * around an antlr parser, for having a parse method for a specific rule and + * access in a type safe way + * + */ +public abstract class MCConcreteParser { + + protected boolean hasErrors = false; + + /** + * Creates a MCConcreteParser with a certain name + * + */ + public MCConcreteParser() { + } + + /** + * Implement this method to call top rule of parser. This method will be + * overridden in generated classes with covariant return type. + * + * @param fileName The name of the file to be parsed + * @return An Optional of the created AST + * @throws IOException Errors during file handling + */ + public abstract Optional parse(String fileName) throws IOException; + + /** + * Implement this method to call top rule of parser. This method will be + * overridden in generated classes with covariant return type. + * + * @param reader The reader containing the input to be parsed + * @return An Optional of the created AST + * @throws IOException Errors during reader handling + */ + public abstract Optional parse(Reader reader) throws IOException; + + /** + * Returns true, if errors occured while parsing + * + * @return + */ + public boolean hasErrors() { + return hasErrors; + } + + public void setError(boolean value) { + hasErrors = value; + } + + public Optional parse(Reader reader, String qualifiedModelName) + throws IOException { + return parse(reader); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/antlr4/MCErrorListener.java b/monticore-runtime/src/main/java/de/monticore/antlr4/MCErrorListener.java new file mode 100644 index 0000000000..a9b5d78e09 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/antlr4/MCErrorListener.java @@ -0,0 +1,47 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.antlr4; + +import com.google.common.collect.Lists; +import de.se_rwth.commons.SourcePosition; +import de.se_rwth.commons.StringTransformations; +import de.se_rwth.commons.logging.Log; +import org.antlr.v4.runtime.*; + +import java.util.List; + +public class MCErrorListener extends BaseErrorListener { + + protected MCParser parser = null; + + public MCErrorListener(MCParser parser) { + super(); + this.parser = parser; + } + + @Override + public void syntaxError(Recognizer recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) { + // Improve error message + if (recognizer instanceof Parser) { + if ((e instanceof org.antlr.v4.runtime.InputMismatchException) && (offendingSymbol instanceof CommonToken)) { + // add the found token type to the message + String s = parser.getVocabulary().getSymbolicName(((CommonToken) offendingSymbol).getType()); + if (s != null && !s.isEmpty()) { + msg += "(found: " + s + ")"; + } + } + // Determine rule stack without eof-rule + List stack = ((Parser) recognizer).getRuleInvocationStack(); + List rules = Lists.newArrayList(); + for (int i = stack.size() - 1; i >= 0; i--) { + if (!(i == stack.size() - 1 && stack.get(i).endsWith("_eof"))) { + rules.add(StringTransformations.capitalize(stack.get(i))); + } + } + Log.error(msg + " in rule stack: " + rules, new SourcePosition(line, charPositionInLine, parser.getFilename())); + } else { + Log.error(msg, new SourcePosition(line, charPositionInLine, parser.getFilename())); + } + parser.setErrors(true); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/antlr4/MCLexer.java b/monticore-runtime/src/main/java/de/monticore/antlr4/MCLexer.java new file mode 100644 index 0000000000..afa92f0194 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/antlr4/MCLexer.java @@ -0,0 +1,17 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.antlr4; + +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.Lexer; + + +public abstract class MCLexer extends Lexer { + + public MCLexer(CharStream input) { + super(input); + } + + public void newline() {} + +} diff --git a/monticore-runtime/src/main/java/de/monticore/antlr4/MCParser.java b/monticore-runtime/src/main/java/de/monticore/antlr4/MCParser.java new file mode 100644 index 0000000000..8951ff9f7a --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/antlr4/MCParser.java @@ -0,0 +1,284 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.antlr4; + +import de.monticore.ast.ASTNode; +import de.monticore.ast.ASTNodeBuilder; +import de.monticore.ast.Comment; +import de.se_rwth.commons.SourcePosition; +import de.se_rwth.commons.logging.Log; +import org.antlr.v4.runtime.Parser; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.TokenStream; + +import java.util.ArrayList; +import java.util.List; +import java.util.ListIterator; + +public abstract class MCParser extends Parser { + + protected List comments = new ArrayList(); + + protected ASTNodeBuilder activeBuilder; + + protected ASTNode activeastnode; + + public MCParser(TokenStream input) { + super(input); + removeErrorListeners(); + addErrorListener(new MCErrorListener(this)); + } + + public MCParser() { + super(null); + removeErrorListeners(); + addErrorListener(new MCErrorListener(this)); + } + + protected boolean hasErrors = false; + + protected String filename = ""; + + public String getFilename() { + return filename; + } + + public de.se_rwth.commons.SourcePosition computeEndPosition(Token token) { + if (token == null || token.getText() == null) { + return SourcePosition.getDefaultSourcePosition(); + } + return computeEndPosition(new SourcePosition(token.getLine(), token.getCharPositionInLine(), getFilename()), + token.getText()); + } + + public de.se_rwth.commons.SourcePosition computeStartPosition(Token token) { + if (token == null) { + return null; + } + int line = token.getLine(); + int column = token.getCharPositionInLine(); + return new de.se_rwth.commons.SourcePosition(line, column, getFilename()); + } + + public SourcePosition computeEndPosition(SourcePosition start, String text) { + int line = start.getLine(); + int column = start.getColumn(); + if (text == null) { + throw new IllegalArgumentException("0xA0708 text was null!"); + } + else if ("\n".equals(text)) { + column += text.length(); + } + else if (text.indexOf("\n") == -1) { + column += text.length(); + } + else { + String[] splitted = text.split("\n", -1); + line += splitted.length - 1; + // +1: if there is 1 character on the last line, sourcepos must + // be 2... + column = splitted[splitted.length - 1].length() + 1; + } + return new de.se_rwth.commons.SourcePosition(line, column, getFilename()); + } + + public boolean hasErrors() { + return hasErrors; + } + + public void setErrors(boolean val) { + hasErrors = val; + } + + public boolean checkMin(int actual, int reference) { + return actual >= reference; + } + + public boolean checkMax(int actual, int reference) { + return reference < 0 || actual <= reference; + } + + public void setFilename(String filename) { + this.filename = filename; + } + + public void addComment(Comment comment) { + comments.add(comment); + } + + protected void addToIteratedAttributeIfNotNull(List attribute, E value) { + if (value != null) { + attribute.add(value); + } + } + + public void setActiveASTNode(ASTNode n) { + + ListIterator listIterator = comments.listIterator(); + while (listIterator.hasNext()) { + Comment c = listIterator.next(); + if (this.activeastnode != null && this.activeastnode.get_SourcePositionEnd().getLine() == c + .get_SourcePositionStart().getLine()) { + this.activeastnode.get_PostCommentList().add(c); + listIterator.remove(); + } + else if (c.get_SourcePositionStart().compareTo(n.get_SourcePositionStart()) < 0) { + n.get_PreCommentList().add(c); + listIterator.remove(); + } + } + + this.activeastnode = n; + } + + public boolean noSpace() { + if (!checkToken(-1) || (!checkToken(-2))) { + return false; + } + + org.antlr.v4.runtime.Token t1 = _input.LT(-1); + org.antlr.v4.runtime.Token t2 = _input.LT(-2); + // token are on same line + // and columns differ exactly length of earlier token (t2) + return ((t1.getLine() == t2.getLine()) && + (t1.getCharPositionInLine() == t2.getCharPositionInLine() + t2.getText().length())); + } + + public boolean noSpace(Integer... is) { + for (Integer i: is) { + if (!checkToken(i) || (!checkToken(i-1))) { + return false; + } + org.antlr.v4.runtime.Token t1 = _input.LT(i); + org.antlr.v4.runtime.Token t2 = _input.LT(i - 1); + // token are on same line + // and columns differ exactly length of earlier token (t2) + if (((t1.getLine() != t2.getLine()) || + (t1.getCharPositionInLine() != t2.getCharPositionInLine() + t2.getText().length()))) { + return false; + } + } + return true; + } + + + /* + * Compare the string of token (counting from the current token) with the given strings + */ + public boolean cmpToken(int i, String... str) { + if (!checkToken(i)) { + return false; + } + org.antlr.v4.runtime.Token t1 = _input.LT(i); + for (String s: str) { + if (t1.getText().equals(s)) { + return true; + } + } + return false; + } + + /* + * Returns if the string of the token (counting from the current token) matches the given string + */ + public boolean cmpTokenRegEx(int i, String regEx) { + if (!checkToken(i)) { + return false; + } + org.antlr.v4.runtime.Token t1 = _input.LT(i); + return t1.getText().matches(regEx); + } + + /* + * Compare the string of the actual token with the given strings + */ + public boolean is(String... str) { + if (!checkToken(-1)) { + return false; + } + org.antlr.v4.runtime.Token t1 = _input.LT(-1); + for (int i = 0; i < str.length; i++) { + if (t1.getText().equals(str[i])) { + return true; + } + } + return false; + } + + /* + * Compare the string of the next token with the given strings + */ + public boolean next(String... str) { + if (!checkToken(1)) { + return false; + } + org.antlr.v4.runtime.Token t1 = _input.LT(1); + for (int i = 0; i < str.length; i++) { + if (t1.getText().equals(str[i])) { + return true; + } + } + return false; + } + + /* + * Returns the string of the token (counting from the current token). If the token does + * not exist, an empty string is returned + */ + /** + * @deprecated Use {@link #getToken(int)} instead. + */ + @Deprecated + public String token(int i) { + if (!checkToken(i)) { + return ""; + } + org.antlr.v4.runtime.Token t1 = _input.LT(i); + return t1.getText(); + } + + /* + * Returns the string of the token (counting from the current token). If the token does + * not exist, an empty string is returned + */ + public String getToken(int i) { + if (!checkToken(i)) { + return ""; + } + org.antlr.v4.runtime.Token t1 = _input.LT(i); + return t1.getText(); + } + + protected boolean checkToken(int i) { + if (_input.LT(i) == null) { + Log.warn("0xA1610 The token at position + " + i + " is not defined!"); + return false; + } + return true; + } + + public void setActiveBuilder(ASTNodeBuilder builder) { + + ListIterator listIterator = comments.listIterator(); + SourcePosition defaultPos = SourcePosition.getDefaultSourcePosition(); + if (builder.isPresent_SourcePositionStart()) { + defaultPos = builder.get_SourcePositionStart(); + } + while (listIterator.hasNext()) { + Comment c = listIterator.next(); + if (this.activeBuilder != null && this.activeBuilder.isPresent_SourcePositionEnd() && this.activeBuilder.get_SourcePositionEnd().getLine() == c + .get_SourcePositionStart().getLine()) { + this.activeBuilder.get_PostCommentList().add(c); + listIterator.remove(); + } + else if (c.get_SourcePositionStart().compareTo(defaultPos) < 0) { + builder.get_PreCommentList().add(c); + listIterator.remove(); + } + } + + this.activeBuilder = builder; + } + + +} diff --git a/monticore-runtime/src/main/java/de/monticore/ast/ASTCNode.java b/monticore-runtime/src/main/java/de/monticore/ast/ASTCNode.java new file mode 100644 index 0000000000..33a0eb9b59 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/ast/ASTCNode.java @@ -0,0 +1,433 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.ast; + +import java.util.Collection; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Optional; +import java.util.Spliterator; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; +import java.util.stream.Stream; + +import com.google.common.collect.Lists; + +import de.se_rwth.commons.SourcePosition; + +/** + * Foundation class of all AST-classes Shouldn't be used in an implementation, all AST-classes also + * share the interface ASTNode + * + */ +public abstract class ASTCNode implements ASTNode, Cloneable { + + protected Optional start = Optional.empty(); + + protected Optional end = Optional.empty(); + + protected List precomments = Lists.newArrayList(); + + protected List postcomments = Lists.newArrayList(); + + + public abstract ASTNode deepClone(); + + // ---------------------------------------------------------------------- + // Handle the Optional SourcePosition end + // ---------------------------------------------------------------------- + + public SourcePosition get_SourcePositionEnd() { + if (end.isPresent()) { + return end.get(); + } + return SourcePosition.getDefaultSourcePosition(); + } + + public void set_SourcePositionEnd(SourcePosition end) { + this.end = Optional.ofNullable(end); + } + + public boolean isPresent_SourcePositionEnd() { + return end.isPresent(); + } + + public void set_SourcePositionEndAbsent() { + end = Optional.empty(); + } + + // ---------------------------------------------------------------------- + // Handle the Optional SourcePosition start + // ---------------------------------------------------------------------- + + public SourcePosition get_SourcePositionStart() { + if (start.isPresent()) { + return start.get(); + } + return SourcePosition.getDefaultSourcePosition(); + } + + public void set_SourcePositionStart(SourcePosition start) { + this.start = Optional.ofNullable(start); + } + + public boolean isPresent_SourcePositionStart() { + return start.isPresent(); + } + + public void set_SourcePositionStartAbsent() { + start = Optional.empty(); + } + + // ---------------------------------------------------------------------- + // Handle Pre Comments + // ---------------------------------------------------------------------- + + @Override + public void clear_PreComments() { + this.precomments.clear(); + } + + @Override + public boolean add_PreComment(Comment precomment) { + return this.precomments.add(precomment); + } + + @Override + public boolean addAll_PreComments(Collection precomments) { + return this.precomments.addAll(precomments); + } + + @Override + public boolean contains_PreComment(Object element) { + return this.precomments.contains(element); + } + + @Override + public boolean containsAll_PreComments(Collection element) { + return this.precomments.containsAll(element); + } + + @Override + public boolean isEmpty_PreComments() { + return this.precomments.isEmpty(); + } + + @Override + public Iterator iterator_PreComments() { + return this.precomments.iterator(); + } + + @Override + public boolean remove_PreComment(Object element) { + return this.precomments.remove(element); + } + + @Override + public boolean removeAll_PreComments(Collection element) { + return this.precomments.removeAll(element); + } + + @Override + public boolean retainAll_PreComments(Collection element) { + return this.precomments.retainAll(element); + } + + @Override + public int size_PreComments() { + return this.precomments.size(); + } + + @Override + public Comment[] toArray_PreComments(Comment[] array) { + return this.precomments.toArray(array); + } + + @Override + public boolean removeIf_PreComment(Predicate filter) { + return this.precomments.removeIf(filter); + } + + @Override + public Spliterator spliterator_PreComments() { + return this.precomments.spliterator(); + } + + @Override + public Stream stream_PreComments() { + return this.precomments.stream(); + } + + @Override + public Stream parallelStream_PreComments() { + return this.precomments.parallelStream(); + } + + @Override + public void forEach_PreComments(Consumer action) { + this.precomments.forEach(action); + } + + @Override + public void add_PreComment(int index, Comment precomment) { + this.precomments.add(index, precomment); + } + + @Override + public boolean addAll_PreComments(int index, Collection precomments) { + return this.precomments.addAll(index, precomments); + } + + @Override + public Comment get_PreComment(int index) { + return this.precomments.get(index); + } + + @Override + public int indexOf_PreComment(Object element) { + return this.precomments.indexOf(element); + } + + @Override + public int lastIndexOf_PreComment(Object element) { + return this.precomments.lastIndexOf(element); + } + + @Override + public boolean equals_PreComments(Object element) { + return this.precomments.equals(element); + } + + @Override + public int hashCode_PreComments() { + return this.precomments.hashCode(); + } + + @Override + public ListIterator listIterator_PreComments() { + return this.precomments.listIterator(); + } + + @Override + public Comment remove_PreComment(int index) { + return this.precomments.remove(index); + } + + @Override + public List subList_PreComments(int start, int end) { + return this.precomments.subList(start, end); + } + + @Override + public void replaceAll_PreComments(UnaryOperator operator) { + this.precomments.replaceAll(operator); + } + + @Override + public void sort_PreComments(Comparator comparator) { + this.precomments.sort(comparator); + } + + @Override + public void set_PreCommentList(List preComments) { + this.precomments = preComments; + } + + @Override + public List get_PreCommentList() { + return this.precomments; + } + + @Override + public ListIterator listIterator_PreComments(int index) { + return this.precomments.listIterator(index); + } + + @Override + public Comment set_PreComment(int index, Comment precomment) { + return this.precomments.set(index, precomment); + } + + @Override + public Object[] toArray_PreComments() { + return this.precomments.toArray(); + } + + // ---------------------------------------------------------------------- + // Handle Post Comments + // ---------------------------------------------------------------------- + + @Override + public void clear_PostComments() { + this.postcomments.clear(); + } + + @Override + public boolean add_PostComment(Comment postcomment) { + return this.postcomments.add(postcomment); + } + + @Override + public boolean addAll_PostComments(Collection postcomments) { + return this.postcomments.addAll(postcomments); + } + + @Override + public boolean contains_PostComment(Object element) { + return this.postcomments.contains(element); + } + + @Override + public boolean containsAll_PostComments(Collection element) { + return this.postcomments.containsAll(element); + } + + @Override + public boolean isEmpty_PostComments() { + return this.postcomments.isEmpty(); + } + + @Override + public Iterator iterator_PostComments() { + return this.postcomments.iterator(); + } + + @Override + public boolean remove_PostComment(Object element) { + return this.postcomments.remove(element); + } + + @Override + public boolean removeAll_PostComments(Collection element) { + return this.postcomments.removeAll(element); + } + + @Override + public boolean retainAll_PostComments(Collection element) { + return this.postcomments.retainAll(element); + } + + @Override + public int size_PostComments() { + return this.postcomments.size(); + } + + @Override + public Comment[] toArray_PostComments(Comment[] array) { + return this.postcomments.toArray(array); + } + + @Override + public boolean removeIf_PostComment(Predicate filter) { + return this.postcomments.removeIf(filter); + } + + @Override + public Spliterator spliterator_PostComments() { + return this.postcomments.spliterator(); + } + + @Override + public Stream stream_PostComments() { + return this.postcomments.stream(); + } + + @Override + public Stream parallelStream_PostComments() { + return this.postcomments.parallelStream(); + } + + @Override + public void forEach_PostComments(Consumer action) { + this.postcomments.forEach(action); + } + + @Override + public void add_PostComment(int index, Comment postcomment) { + this.postcomments.add(index, postcomment); + } + + @Override + public boolean addAll_PostComments(int index, Collection postcomments) { + return this.postcomments.addAll(index, postcomments); + } + + @Override + public Comment get_PostComment(int index) { + return this.postcomments.get(index); + } + + @Override + public int indexOf_PostComment(Object element) { + return this.postcomments.indexOf(element); + } + + @Override + public int lastIndexOf_PostComment(Object element) { + return this.postcomments.lastIndexOf(element); + } + + @Override + public boolean equals_PostComments(Object element) { + return this.postcomments.equals(element); + } + + @Override + public int hashCode_PostComments() { + return this.postcomments.hashCode(); + } + + @Override + public ListIterator listIterator_PostComments() { + return this.postcomments.listIterator(); + } + + @Override + public Comment remove_PostComment(int index) { + return this.postcomments.remove(index); + } + + @Override + public List subList_PostComments(int start, int end) { + return this.postcomments.subList(start, end); + } + + @Override + public void replaceAll_PostComments(UnaryOperator operator) { + this.postcomments.replaceAll(operator); + } + + @Override + public void sort_PostComments(Comparator comparator) { + this.postcomments.sort(comparator); + } + + @Override + public void set_PostCommentList(List postComments) { + this.postcomments = postComments; + } + + @Override + public List get_PostCommentList() { + return this.postcomments; + } + + @Override + public ListIterator listIterator_PostComments(int index) { + return this.postcomments.listIterator(index); + } + + @Override + public Comment set_PostComment(int index, Comment precomment) { + return this.postcomments.set(index, precomment); + } + + @Override + public Object[] toArray_PostComments() { + return this.postcomments.toArray(); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/ast/ASTNode.java b/monticore-runtime/src/main/java/de/monticore/ast/ASTNode.java new file mode 100644 index 0000000000..861f5f80de --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/ast/ASTNode.java @@ -0,0 +1,680 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.ast; + +import de.monticore.symboltable.IScope; +import de.monticore.visitor.ITraverser; +import de.se_rwth.commons.SourcePosition; + +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; +import java.util.stream.Stream; + +/** + * Foundation interface for all AST-classes + */ +public interface ASTNode { + + IScope getEnclosingScope(); + + default public boolean equalAttributes(Object o) { + if (o == null) { + return false; + } + throw new CompareNotSupportedException( + "0xA4078 Method equalAttributes is not implemented properly in class: " + + o.getClass().getName()); + } + + default public boolean equalsWithComments(Object o) { + if (o == null) { + return false; + } + throw new CompareNotSupportedException( + "0xA4042 Method equalsWithComments is not implemented properly in class: " + + o.getClass().getName()); + } + + /** + * Compare this object to another Object. Do not take comments into account. This method returns + * the same value as deepEquals(Object o, boolean + * forceSameOrder) method when using the default value for forceSameOrder of each Node. + */ + default public boolean deepEquals(Object o) { + if (o == null) { + return false; + } + throw new CompareNotSupportedException( + "0xA4043 Method deepEquals is not implemented properly in class: " + + o.getClass().getName()); + } + + /** + * Compare this object to another Object. Take comments into account. + * + * @param o the object to compare this node to + * stereotype <<unordered>> in the grammar. + */ + default public boolean deepEqualsWithComments(Object o) { + throw new CompareNotSupportedException( + "0xA4044 Method deepEqualsWithComments is not implemented properly in class: " + + o.getClass().getName()); + } + + /** + * Compare this object to another Object. Do not take comments into account. + * + * @param o the object to compare this node to + * @param forceSameOrder consider the order in ancestor lists, even if these lists are of + * stereotype <<unordered>> in the grammar. + */ + default public boolean deepEquals(Object o, boolean forceSameOrder) { + if (o == null) { + return false; + } + throw new CompareNotSupportedException( + "0xA4045 Method deepEquals is not implemented properly in class: " + + o.getClass().getName()); + } + + /** + * Compare this object to another Object. Take comments into account. This method returns the same + * value as deepEqualsWithComment(Object o, boolean forceSameOrder) method when using the + * default value for forceSameOrder of each Node. + */ + default public boolean deepEqualsWithComments(Object o, boolean forceSameOrder) { + if (o == null) { + return false; + } + throw new CompareNotSupportedException( + "0xA4046 Method deepEqualsWithComments is not implemented properly in class: " + + o.getClass().getName()); + } + + /** + * Performs a deep clone of this ASTNode and all of its successors + * + * @return Clone of current ASTNode with a parent which is equal to null + */ + ASTNode deepClone(); + + /** + * Returns the end position of this ASTNode + * + * @return end position of this ASTNode + */ + SourcePosition get_SourcePositionEnd(); + + /** + * Sets the end position of this ASTNode + * + * @param end end position of this ASTNode + */ + void set_SourcePositionEnd(SourcePosition end); + + /** + * Sets the optional end position of this ASTNode absent + */ + void set_SourcePositionEndAbsent(); + + /** + * @return true if the optional source position end of this ASTNode is present + */ + boolean isPresent_SourcePositionEnd(); + + /** + * Returns the start source position of this ASTNode + * + * @return start position of this ASTNode + */ + SourcePosition get_SourcePositionStart(); + + /** + * Sets the start position of this ASTNode + * + * @param start start position of this ASTNode + */ + void set_SourcePositionStart(SourcePosition start); + + /** + * Sets the optional start position of this ASTNode absent + */ + void set_SourcePositionStartAbsent(); + + /** + * @return true if the optional source position start of this ASTNode is present + */ + boolean isPresent_SourcePositionStart(); + + /** + * Clears the list of preComments, that only an empty list stays + */ + void clear_PreComments(); + + /** + * Adds one new comment to the list of preComments + * + * @param precomment one comment + * @return boolean + */ + boolean add_PreComment(Comment precomment); + + /** + * Adds a list of comments to the already existing preComment list + * + * @param precomments a list of comments + * @return boolean + */ + boolean addAll_PreComments(Collection precomments); + + /** + * Checks if the list contains the given Object Returns true if the Object is contained in the + * list of comments and false if it is not + * + * @param element which should be contained in the comments + * @return boolean true if the list contains the Object + */ + boolean contains_PreComment(Object element); + + /** + * Checks if the list contains the given Collection of elements Returns true if the Collection is + * contained in the list of comments and false if it is not + * + * @param element collection which should be contained in the comment list + * @return boolean true if the list contains the Object + */ + boolean containsAll_PreComments(Collection element); + + /** + * Checks if the list is empty, has no elements Returns true if the list is empty and false if it + * has elements + * + * @return boolean true if the list is empty + */ + boolean isEmpty_PreComments(); + + /** + * Returns the Iterator for the preComment list + * + * @return Iterator to iterate over the preComment list + */ + Iterator iterator_PreComments(); + + /** + * Removes one given element from the list if this element is contained + * + * @param element which should be removed if it is present + * @return boolean + */ + boolean remove_PreComment(Object element); + + /** + * Removes a collection of elements from the list if the elements are contained + * + * @param element collection which should be removed if they are present + * @return boolean + */ + boolean removeAll_PreComments(Collection element); + + /** + * Retrains a collection of elements from the list if the elements are contained + * + * @param element collection which should be retrained if they are present + * @return boolean + */ + boolean retainAll_PreComments(Collection element); + + /** + * counts the number of preComments in the preComment list and returns that number as an Int + * + * @return int the number of comments is returned + */ + int size_PreComments(); + + /** + * Converts the list of preComments into an array of comments and returns that array + * + * @param array into which the comments should be added + * @return Comment[] array which contains all the comments + */ + Comment[] toArray_PreComments(Comment[] array); + + /** + * removes if the comment if the predicate is fulfilled + * + * @param filter which selects different comments + * @return boolean + */ + boolean removeIf_PreComment(Predicate filter); + + /** + * Returns the Spliterator of the preComment list + * + * @return Spliterator of the preComment list + */ + Spliterator spliterator_PreComments(); + + /** + * Returns the Steam of the preComment list + * + * @return Steam of the preComment list + */ + Stream stream_PreComments(); + + /** + * Returns the parallel Steam of the preComment list + * + * @return Steam parallel Stream of the preComment list + */ + Stream parallelStream_PreComments(); + + /** + * Consumer is given that performs an action but does not return a value + * + * @param action that does something but has no return value + */ + void forEach_PreComments(Consumer action); + + /** + * adds one comment to the preComment list at the position of the given index + * + * @param index of the existing list where it should be added + * @param precomment the comment that is added to the existing list + */ + void add_PreComment(int index, Comment precomment); + + /** + * adds a list of comments to the preComment list at the position of the given index + * + * @param index of the existing list where the new list should be added + * @param precomments list that is added to the existing list + */ + boolean addAll_PreComments(int index, Collection precomments); + + /** + * Returns one comment of the list from the position of the given index + * + * @param index in the existing list where the comment should be returned + * @return Comment at the given index is returned + */ + Comment get_PreComment(int index); + + /** + * Returns the index of the given element if it exists in the list + * + * @param element of which the index in the comment list should be returned + * @return int index where the Object is found + */ + int indexOf_PreComment(Object element); + + /** + * Returns the last index of the given element if it exists in the list + * + * @param element of which the last index in the comment list should be returned + * @return int index where the Object is found latest + */ + int lastIndexOf_PreComment(Object element); + + /** + * Returns true if the object equals the preComment list Returns false if the object and the + * preComment list are not equal + * + * @param element which should be equal to the preComment list + * @return boolean if the object an preComment list are equal or not + */ + boolean equals_PreComments(Object element); + + /** + * Returns the hashCode of the preComment as an int + * + * @return int the hashCode of the preComment + */ + int hashCode_PreComments(); + + /** + * Returns the ListIterator of the preComment list + * + * @return ListIterator which iterates over the list of preComments + */ + ListIterator listIterator_PreComments(); + + /** + * Returns the new preComment list without the removed element at the given index + * + * @param index where the element should be removed + * @return List where the comment at the index is removed + */ + Comment remove_PreComment(int index); + + /** + * Returns the sub list form the preComment list form the start to the end index which are given + * + * @param start index of the sublist + * @param end index of the sublist + * @return ListIterator which iterates over the list of preComments + */ + List subList_PreComments(int start, int end); + + /** + * replaces all preComments that fit to the given unaryOperator + * + * @param operator that defines which preComments should be replaced + */ + void replaceAll_PreComments(UnaryOperator operator); + + /** + * sorts the preComment list through a given way of comparing through the comparator + * + * @param comparator that defines in which way the preComments should be + * sorted + */ + void sort_PreComments(Comparator comparator); + + /** + * sets the complete list of preComments to the given preComment list + * + * @param preComments list that should be set + */ + void set_PreCommentList(List preComments); + + /** + * returns the complete preComments list + * + * @return List that is contained in the preComment list at the moment + */ + List get_PreCommentList(); + + /** + * returns a ListIterator of the type Comment for the preComment list + * + * @param index of the iterator + * @return ListIterator of a special index + */ + ListIterator listIterator_PreComments(int index); + + /** + * sets the comment at the given index and returns that comment + * + * @param index where the comment should be added to the list + * @param precomment that should be added at the index + * @return Comment at a special index + */ + Comment set_PreComment(int index, Comment precomment); + + /** + * converts the commentlist into an array of the type Object and returns thar array + * + * @return an array of the type Object + */ + Object[] toArray_PreComments(); + + + /** + * Clears the list of postComments, that only an empty list stays + */ + void clear_PostComments(); + + /** + * Adds one new comment to the list of postComments + * + * @param postcomment one comment + * @return boolean + */ + boolean add_PostComment(Comment postcomment); + + /** + * Adds a list of comments to the already existing postComment list + * + * @param postcomments a list of comments + * @return boolean + */ + boolean addAll_PostComments(Collection postcomments); + + /** + * Checks if the list contains the given Object Returns true if the Object is contained in the + * list of comments and false if it is not + * + * @param element which should be contained in the comments + * @return boolean true if the list contains the Object + */ + boolean contains_PostComment(Object element); + + /** + * Checks if the list contains the given Collection of elements Returns true if the Collection is + * contained in the list of comments and false if it is not + * + * @param element collection of elements which should be contained in the comment list + * @return boolean true if the list contains the Object + */ + boolean containsAll_PostComments(Collection element); + + /** + * Checks if the list is empty, has no elements Returns true if the list is empty and false if it + * has elements + * + * @return boolean true if the list is empty + */ + boolean isEmpty_PostComments(); + + /** + * Returns the Iterator for the postComment list + * + * @return Iterator to iterate over the postComment list + */ + Iterator iterator_PostComments(); + + /** + * removes one given element from the list if this element is contained + * + * @param element which should be removed if it is postsent + * @return boolean + */ + boolean remove_PostComment(Object element); + + /** + * removes a collection of elements from the list if the elements are contained + * + * @param element collection which should be removed if they are postsent + * @return boolean true if the elements were postsent and are now removed + */ + boolean removeAll_PostComments(Collection element); + + /** + * Retrains a collection of elements from the list if the elements are contained + * + * @param element collection which should be retrained if they are present + * @return boolean + */ + boolean retainAll_PostComments(Collection element); + + /** + * counts the number of postComments in the postComment list and returns that number as an Int + * + * @return int the number of comments is returned + */ + int size_PostComments(); + + /** + * Converts the list of postComments into an array of comments and returns that array + * + * @param array into which the comments should be added + * @return Comment[] array which contains all the comments + */ + Comment[] toArray_PostComments(Comment[] array); + + /** + * removes if the comment if the postdicate is fulfilled + * + * @param filter which selects different comments + * @return boolean + */ + boolean removeIf_PostComment(Predicate filter); + + /** + * Returns the Spliterator of the postComment list + * + * @return Spliterator of the postComment list + */ + Spliterator spliterator_PostComments(); + + /** + * Returns the Steam of the postComment list + * + * @return Steam of the postComment list + */ + Stream stream_PostComments(); + + /** + * Returns the parallel Steam of the postComment list + * + * @return Steam parallel Stream of the postComment list + */ + Stream parallelStream_PostComments(); + + /** + * Consumer is given that performs an action but does not return a value + * + * @param action that does something but has no return value + */ + void forEach_PostComments(Consumer action); + + /** + * adds one comment to the postComment list at the position of the given index + * + * @param index of the existing list where it should be added + * @param postcomment the comment that is added to the existing list + */ + void add_PostComment(int index, Comment postcomment); + + /** + * adds a list of comments to the postComment list at the position of the given index + * + * @param index of the existing list where the new list should be added + * @param postcomments the comment list that is added to the existing list + */ + boolean addAll_PostComments(int index, Collection postcomments); + + /** + * Returns one comment of the list from the position of the given index + * + * @param index of the existing list where the comment should be returned + * @return Comment at the given index is returned + */ + Comment get_PostComment(int index); + + /** + * Returns the index of the given element if this exists in the list + * + * @param element of which the index in the comment list should be returned + * @return int index where the Object is found + */ + int indexOf_PostComment(Object element); + + /** + * Returns the last index of the given element if this exists in the list + * + * @param element of which the last index in the comment list should be returned + * @return int index where the Object is found latest + */ + int lastIndexOf_PostComment(Object element); + + /** + * Returns true if the object equals the postComment list Returns false if the object and the + * postComment list are not equal + * + * @param element which should be equal to the postComment list + * @return boolean if the object an postComment list are equal or not + */ + boolean equals_PostComments(Object element); + + /** + * Returns the hashCode of the postComment as an int + * + * @return int the hashCode of the postComment + */ + int hashCode_PostComments(); + + /** + * Returns the ListIterator of the postComment list + * + * @return ListIterator which iterates over the list of postComments + */ + ListIterator listIterator_PostComments(); + + /** + * Returns the new postComment list without the removed element at the given index + * + * @param index where the element should be removed + * @return List where the comment at the index is removed + */ + Comment remove_PostComment(int index); + + /** + * Returns the sub list form the postComment list form the start to the end index which are given + * + * @param start index of the sublist + * @param end index of the sublist + * @return ListIterator which iterates over the list of postComments + */ + List subList_PostComments(int start, int end); + + /** + * replaces all postComments that fit to the given unaryOperator + * + * @param operator that defines which postComments should be replaced + */ + void replaceAll_PostComments(UnaryOperator operator); + + /** + * sorts the postComment list through a given way of comparing through the comparator + * + * @param comparator that defines in which way the postComments should be + * sorted + */ + void sort_PostComments(Comparator comparator); + + /** + * sets the complete list of postComments to the given postComment list + * + * @param postComments list that should be set + */ + void set_PostCommentList(List postComments); + + /** + * returns the complete postComments list + * + * @return List that is contained in the postComment list at the moment + */ + List get_PostCommentList(); + + /** + * returns a ListIterator of the type Comment for the postComment list + * + * @param index of the iterator + * @return ListIterator of a special index + */ + ListIterator listIterator_PostComments(int index); + + /** + * sets the comment at the given index and returns that comment + * + * @param index where the comment should be added to the list + * @param postcomment that should be added at the index + * @return Comment at a special index + */ + Comment set_PostComment(int index, Comment postcomment); + + /** + * converts the commentlist into an array of the type Object and returns thar array + * + * @return an array of the type Object + */ + Object[] toArray_PostComments(); + + default void accept (ITraverser visitor) { + visitor.handle(this); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/ast/ASTNodeBuilder.java b/monticore-runtime/src/main/java/de/monticore/ast/ASTNodeBuilder.java new file mode 100644 index 0000000000..8c65251f01 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/ast/ASTNodeBuilder.java @@ -0,0 +1,405 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.ast; + +import java.util.Collection; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Optional; +import java.util.Spliterator; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; +import java.util.stream.Stream; + +import com.google.common.collect.Lists; + +import de.se_rwth.commons.SourcePosition; +import de.se_rwth.commons.logging.Log; + +/** + * Foundation class for all ASTBuilder. + */ +public abstract class ASTNodeBuilder> { + + protected Optional sourcePositionStart = Optional.empty(); + + protected Optional sourcePositionEnd = Optional.empty(); + + protected List precomments = Lists.newArrayList(); + + protected List postcomments = Lists.newArrayList(); + + protected T realBuilder; + + protected ASTNodeBuilder() { + this.realBuilder = (T) this; + } + + // ---------------------------------------------------------------------- + // Handle the Optional SourcePosition end + // ---------------------------------------------------------------------- + + public T set_SourcePositionEnd(SourcePosition end) { + this.sourcePositionEnd = Optional.ofNullable(end); + return this.realBuilder; + } + + public SourcePosition get_SourcePositionEnd() { + if (sourcePositionEnd.isPresent()) { + return sourcePositionEnd.get(); + } + Log.error("0xB9266 get_SourcePositionEnd can't return a value. It is empty."); + // Normally this statement is not reachable + throw new IllegalStateException(); + } + + public boolean isPresent_SourcePositionEnd() { + return sourcePositionEnd.isPresent(); + } + + public T set_SourcePositionEndAbsent() { + sourcePositionEnd = Optional.empty(); + return this.realBuilder; + } + + // ---------------------------------------------------------------------- + // Handle the Optional SourcePosition start + // ---------------------------------------------------------------------- + + public T set_SourcePositionStart(SourcePosition start) { + this.sourcePositionStart = Optional.ofNullable(start); + return this.realBuilder; + } + + public SourcePosition get_SourcePositionStart() { + if (sourcePositionStart.isPresent()) { + return sourcePositionStart.get(); + } + Log.error("0xB9267 get_SourcePositionStart can't return a value. It is empty."); + // Normally this statement is not reachable + throw new IllegalStateException(); + } + + public boolean isPresent_SourcePositionStart() { + return sourcePositionStart.isPresent(); + } + + public T set_SourcePositionStartAbsent() { + sourcePositionStart = Optional.empty(); + return this.realBuilder; + } + + // ---------------------------------------------------------------------- + // Handle Pre Comments + // ---------------------------------------------------------------------- + + public T clear_PreComments() { + this.precomments.clear(); + return this.realBuilder; + } + + public T add_PreComment(Comment precomment) { + this.precomments.add(precomment); + return this.realBuilder; + } + + public T addAll_PreComments(Collection precomments) { + this.precomments.addAll(precomments); + return this.realBuilder; + } + + public boolean contains_PreComment(Object element) { + return this.precomments.contains(element); + } + + public boolean containsAll_PreComments(Collection element) { + return this.precomments.containsAll(element); + } + + public boolean isEmpty_PreComments() { + return this.precomments.isEmpty(); + } + + public Iterator iterator_PreComments() { + return this.precomments.iterator(); + } + + public T remove_PreComment(Object element) { + this.precomments.remove(element); + return this.realBuilder; + } + + public T removeAll_PreComments(Collection element) { + this.precomments.removeAll(element); + return this.realBuilder; + } + + public T retainAll_PreComments(Collection element) { + this.precomments.retainAll(element); + return this.realBuilder; + } + + public int size_PreComments() { + return this.precomments.size(); + } + + public Comment[] toArray_PreComments(Comment[] array) { + return this.precomments.toArray(array); + } + + public T removeIf_PreComment(Predicate filter) { + this.precomments.removeIf(filter); + return this.realBuilder; + } + + public Spliterator spliterator_PreComments() { + return this.precomments.spliterator(); + } + + public Stream stream_PreComments() { + return this.precomments.stream(); + } + + public Stream parallelStream_PreComments() { + return this.precomments.parallelStream(); + } + + public T forEach_PreComments(Consumer action) { + this.precomments.forEach(action); + return this.realBuilder; + } + + public T add_PreComment(int index, Comment precomment) { + this.precomments.add(index, precomment); + return this.realBuilder; + } + + public T addAll_PreComments(int index, Collection precomments) { + this.precomments.addAll(index, precomments); + return this.realBuilder; + } + + public Comment get_PreComment(int index) { + return this.precomments.get(index); + } + + public int indexOf_PreComment(Object element) { + return this.precomments.indexOf(element); + } + + public int lastIndexOf_PreComment(Object element) { + return this.precomments.lastIndexOf(element); + } + + public boolean equals_PreComments(Object element) { + return this.precomments.equals(element); + } + + public int hashCode_PreComments() { + return this.precomments.hashCode(); + } + + public ListIterator listIterator_PreComments() { + return this.precomments.listIterator(); + } + + public T remove_PreComment(int index) { + this.precomments.remove(index); + return this.realBuilder; + } + + public List subList_PreComments(int start, int end) { + return this.precomments.subList(start, end); + } + + public T replaceAll_PreComments(UnaryOperator operator) { + this.precomments.replaceAll(operator); + return this.realBuilder; + } + + public T sort_PreComments(Comparator comparator) { + this.precomments.sort(comparator); + return this.realBuilder; + } + + public T set_PreCommentList(List preComments) { + this.precomments = preComments; + return this.realBuilder; + } + + public List get_PreCommentList() { + return this.precomments; + } + + public ListIterator listIterator_PreComments(int index) { + return this.precomments.listIterator(index); + } + + public T set_PreComment(int index, Comment precomment) { + this.precomments.set(index, precomment); + return this.realBuilder; + } + + public Object[] toArray_PreComments() { + return this.precomments.toArray(); + } + + // ---------------------------------------------------------------------- + // Handle Post Comments + // ---------------------------------------------------------------------- + + public T clear_PostComments() { + this.postcomments.clear(); + return this.realBuilder; + } + + public T add_PostComment(Comment postcomment) { + this.postcomments.add(postcomment); + return this.realBuilder; + } + + public T addAll_PostComments(Collection postcomments) { + this.postcomments.addAll(postcomments); + return this.realBuilder; + } + + public boolean contains_PostComment(Object element) { + return this.postcomments.contains(element); + } + + public boolean containsAll_PostComments(Collection element) { + return this.postcomments.containsAll(element); + } + + public boolean isEmpty_PostComments() { + return this.postcomments.isEmpty(); + } + + public Iterator iterator_PostComments() { + return this.postcomments.iterator(); + } + + public T remove_PostComment(Object element) { + this.postcomments.remove(element); + return this.realBuilder; + } + + public T removeAll_PostComments(Collection element) { + this.postcomments.removeAll(element); + return this.realBuilder; + } + + public T retainAll_PostComments(Collection element) { + this.postcomments.retainAll(element); + return this.realBuilder; + } + + public int size_PostComments() { + return this.postcomments.size(); + } + + public Comment[] toArray_PostComments(Comment[] array) { + return this.postcomments.toArray(array); + } + + public T removeIf_PostComment(Predicate filter) { + this.postcomments.removeIf(filter); + return this.realBuilder; + } + + public Spliterator spliterator_PostComments() { + return this.postcomments.spliterator(); + } + + public Stream stream_PostComments() { + return this.postcomments.stream(); + } + + public Stream parallelStream_PostComments() { + return this.postcomments.parallelStream(); + } + + public T forEach_PostComments(Consumer action) { + this.postcomments.forEach(action); + return this.realBuilder; + } + + public T add_PostComment(int index, Comment postcomment) { + this.postcomments.add(index, postcomment); + return this.realBuilder; + } + + public T addAll_PostComments(int index, Collection postcomments) { + this.postcomments.addAll(index, postcomments); + return this.realBuilder; + } + + public Comment get_PostComment(int index) { + return this.postcomments.get(index); + } + + public int indexOf_PostComment(Object element) { + return this.postcomments.indexOf(element); + } + + public int lastIndexOf_PostComment(Object element) { + return this.postcomments.lastIndexOf(element); + } + + public boolean equals_PostComments(Object element) { + return this.postcomments.equals(element); + } + + public int hashCode_PostComments() { + return this.postcomments.hashCode(); + } + + public ListIterator listIterator_PostComments() { + return this.postcomments.listIterator(); + } + + public T remove_PostComment(int index) { + this.postcomments.remove(index); + return this.realBuilder; + } + + public List subList_PostComments(int start, int end) { + return this.postcomments.subList(start, end); + } + + public T replaceAll_PostComments(UnaryOperator operator) { + this.postcomments.replaceAll(operator); + return this.realBuilder; + } + + public T sort_PostComments(Comparator comparator) { + this.postcomments.sort(comparator); + return this.realBuilder; + } + + public T set_PostCommentList(List postComments) { + this.postcomments = postComments; + return this.realBuilder; + } + + public List get_PostCommentList() { + return this.postcomments; + } + + public ListIterator listIterator_PostComments(int index) { + return this.postcomments.listIterator(index); + } + + public T set_PostComment(int index, Comment precomment) { + this.postcomments.set(index, precomment); + return this.realBuilder; + } + + public Object[] toArray_PostComments() { + return this.postcomments.toArray(); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/ast/Comment.java b/monticore-runtime/src/main/java/de/monticore/ast/Comment.java new file mode 100644 index 0000000000..4c3b997c31 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/ast/Comment.java @@ -0,0 +1,76 @@ +/* (c) https://github.com/MontiCore/monticore */ + +/* + * WARNING: This file has been generated, don't modify !! + */ +package de.monticore.ast; + +import de.se_rwth.commons.SourcePosition; + + +/** + * Class represents a comment (contains the comment and the start- and end-position) + * + */ +public class Comment { + + protected SourcePosition start = SourcePosition.getDefaultSourcePosition(); + + protected SourcePosition end = SourcePosition.getDefaultSourcePosition(); + + public SourcePosition get_SourcePositionEnd() { + return end; + } + + public void set_SourcePositionEnd(SourcePosition end) { + this.end = end; + } + + public SourcePosition get_SourcePositionStart() { + return start; + } + + public void set_SourcePositionStart(SourcePosition start) { + this.start = start; + } + + public String toString() { + return text; + } + + protected String text; + + public Comment() { + text = ""; + } + + public Comment(String text) { + setText(text); + } + + public String getText() { + return this.text; + } + + public void setText(String text) { + this.text = text; + } + + public boolean equals(Object o) { + if (o instanceof Comment) { + return this.text.equals(((Comment) o).text); + } + return false; + } + + @Override + public int hashCode() { + if (start == null) { + return super.hashCode(); + } + else { + return start.getLine() * 1024 + start.getColumn(); + } + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/ast/CommentBuilder.java b/monticore-runtime/src/main/java/de/monticore/ast/CommentBuilder.java new file mode 100644 index 0000000000..29d29a6a6c --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/ast/CommentBuilder.java @@ -0,0 +1,61 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.ast; + +import de.se_rwth.commons.SourcePosition; +import de.se_rwth.commons.logging.Log; + +/** + * Builder for RTE class {@link Comment} + */ +public class CommentBuilder { + + protected String text = null; + + protected SourcePosition start = SourcePosition.getDefaultSourcePosition(); + + protected SourcePosition end = SourcePosition.getDefaultSourcePosition(); + + public Comment build() { + if (isValid()) { + Comment res = new Comment(text); + res.set_SourcePositionStart(start); + res.set_SourcePositionEnd(end); + return res; + } + else { + Log.error("0xA4322 text of type String must not be null"); + throw new IllegalStateException(); + } + } + + public boolean isValid() { + return this.text != null; + } + + public SourcePosition get_SourcePositionEnd() { + return end; + } + + public CommentBuilder set_SourcePositionEnd(SourcePosition end) { + this.end = end; + return this; + } + + public SourcePosition get_SourcePositionStart() { + return start; + } + + public CommentBuilder set_SourcePositionStart(SourcePosition start) { + this.start = start; + return this; + } + + public String getText() { + return this.text; + } + + public CommentBuilder setText(String text) { + this.text = text; + return this; + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/ast/CompareNotSupportedException.java b/monticore-runtime/src/main/java/de/monticore/ast/CompareNotSupportedException.java new file mode 100644 index 0000000000..c69367b300 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/ast/CompareNotSupportedException.java @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.ast; + +public class CompareNotSupportedException extends RuntimeException { + private static final long serialVersionUID = 1360314798474951220L; + + public CompareNotSupportedException(String message) { + super(message); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/GeneratorEngine.java b/monticore-runtime/src/main/java/de/monticore/generating/GeneratorEngine.java new file mode 100644 index 0000000000..ec1dda47e6 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/GeneratorEngine.java @@ -0,0 +1,192 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Strings.isNullOrEmpty; + +import java.io.IOException; +import java.io.Writer; +import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Optional; + +import de.monticore.ast.ASTCNode; +import de.monticore.ast.ASTNode; +import de.monticore.generating.templateengine.TemplateController; +import de.monticore.generating.templateengine.reporting.Reporting; +import de.monticore.io.paths.MCPath; +import de.monticore.symboltable.IScope; +import de.se_rwth.commons.Names; +import de.se_rwth.commons.logging.Log; + +/** + * Represents the whole generator engine component. + * Clients usually need only this class when generating. + * + */ +public class GeneratorEngine { + + public static final String DEFAULT_FILE_EXTENSION = ".java"; + + public static final String TOP_NAME_EXTENSION = "TOP"; + + /** + * Contains all configuration data + */ + protected GeneratorSetup setup; + + public GeneratorEngine(GeneratorSetup gs) { + Log.errorIfNull(gs); + this.setup = gs; + } + + /** + * Processes the template templateName with the node and the given + * templateArguments and writes the content into the filePath. Note: + * Unless not absolute, the filePath is relative to the configured output directory + * specified in the {@link de.monticore.generating.GeneratorSetup}. + * + * @param templateName the template to be processes + * @param filePath the file path in which the content is to be written + * @param node the ast node + * @param templateArguments additional template arguments (if needed). + */ + public void generate(String templateName, Path filePath, + ASTNode node, Object... templateArguments) { + Log.errorIfNull(node); + checkArgument(!isNullOrEmpty(templateName)); + Log.errorIfNull(filePath); + + TemplateController tc = setup.getNewTemplateController(templateName); + tc.writeArgs(templateName, filePath, node, Arrays.asList(templateArguments)); + } + + /** + * Processes the template templateName with the node and the given + * templateArguments and writes the content into the filePath. Note: + * Unless not absolute, the filePath is relative to the configured output directory + * specified in the {@link de.monticore.generating.GeneratorSetup}. + * + * @param templateName the template to be processes + * @param filePath the file path in which the content is to be written + * @param templateArguments additional template arguments (if needed). + */ + public void generateNoA(String templateName, Path filePath, Object... templateArguments) { + checkArgument(!isNullOrEmpty(templateName)); + Log.errorIfNull(filePath); + + TemplateController tc = setup.getNewTemplateController(templateName); + ASTCNode dummyAST = new ASTCNode(){ + + @Override public IScope getEnclosingScope() { + return null; + } + + @Override public ASTNode deepClone() { + return this; + } + }; + + tc.writeArgs(templateName, filePath, dummyAST, Arrays.asList(templateArguments)); + } + + /** + * Processes the template templateName with the node and the given + * templateArguments and writes the content into the filePath. Note: + * Unless not absolute, the filePath is relative to the configured output directory + * specified in the {@link de.monticore.generating.GeneratorSetup}. + * + * @param templateName the template to be processes + * @param writer the writer in which the content is to be written + * @param node the ast node + * @param templateArguments additional template arguments (if needed). + */ + public void generate(String templateName, Writer writer, + ASTNode node, Object... templateArguments) { + Log.errorIfNull(node); + checkArgument(!isNullOrEmpty(templateName)); + Log.errorIfNull(writer); + + TemplateController tc = setup.getNewTemplateController(templateName); + StringBuilder sb = tc.includeArgs(templateName, node, Arrays.asList(templateArguments)); + try { + writer.write(sb.toString()); + } + catch (IOException e) { + Log.error("0xA4110 Template " + templateName + " cannot write the content"); + } + } + + /** + * Processes the template templateName with the node and the given + * templateArguments and writes the content into the filePath. Note: + * Unless not absolute, the filePath is relative to the configured output directory + * specified in the {@link de.monticore.generating.GeneratorSetup}. + * + * @param templateName the template to be processes + * @param writer the writer in which the content is to be written + * @param templateArguments additional template arguments (if needed). + */ + public void generateNoA(String templateName, Writer writer, Object... templateArguments) { + checkArgument(!isNullOrEmpty(templateName)); + Log.errorIfNull(writer); + + TemplateController tc = setup.getNewTemplateController(templateName); + StringBuilder sb = tc.includeArgs(templateName, Arrays.asList(templateArguments)); + try { + writer.write(sb.toString()); + } + catch (IOException e) { + Log.error("0xA4089 Template " + templateName + " cannot write the content"); + } + } + + + /** + * Processes the template templateName with the given templateArguments + * and returns the content as StringBuilder. + * + * @param templateName the template to be processes + * @param node the ast node + * @param templateArguments additional template arguments (if needed). + */ + public StringBuilder generate(String templateName, ASTNode node, Object... templateArguments) { + checkArgument(!isNullOrEmpty(templateName)); + TemplateController tc = setup.getNewTemplateController(templateName); + return tc.includeArgs(templateName, node, Arrays.asList(templateArguments)); + } + + /** + * Processes the template templateName with the given templateArguments + * and returns the content as StringBuilder. + * + * @param templateName the template to be processes + * @param templateArguments additional template arguments (if needed). + */ + public StringBuilder generateNoA(String templateName, Object... templateArguments) { + checkArgument(!isNullOrEmpty(templateName)); + TemplateController tc = setup.getNewTemplateController(templateName); + return tc.includeArgs(templateName, Arrays.asList(templateArguments)); + } + + /** + * Check whewther an handwritten version of that class already exits and shall be integrated + * (e.g. to apply TOP mechanism) + */ + public static boolean existsHandwrittenClass(MCPath targetPath, String qualifiedName) { + String hwFile = Names.getPathFromPackage(qualifiedName)+ DEFAULT_FILE_EXTENSION; + Optional hwFilePath = targetPath.find(hwFile); + boolean result = hwFilePath.isPresent(); + if (result) { + Optional hwPath = MCPath.toPath(hwFilePath.get()); + hwPath.ifPresent(path -> Reporting.reportUseHandwrittenCodeFile(path, + Paths.get(hwFile))); + } + Reporting.reportHWCExistenceCheck(targetPath, Paths.get(hwFile), hwFilePath); + return result; + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/GeneratorSetup.java b/monticore-runtime/src/main/java/de/monticore/generating/GeneratorSetup.java new file mode 100644 index 0000000000..7d3d1300b9 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/GeneratorSetup.java @@ -0,0 +1,336 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating; + +import com.google.common.collect.Lists; +import de.monticore.generating.templateengine.GlobalExtensionManagement; +import de.monticore.generating.templateengine.TemplateController; +import de.monticore.generating.templateengine.freemarker.FreeMarkerTemplateEngine; +import de.monticore.generating.templateengine.freemarker.MontiCoreFileTemplateLoader; +import de.monticore.generating.templateengine.freemarker.MontiCoreTemplateExceptionHandler; +import de.monticore.generating.templateengine.freemarker.MontiCoreTemplateLoader; +import de.monticore.generating.templateengine.freemarker.alias.Alias; +import de.monticore.io.FileReaderWriter; +import de.monticore.io.paths.MCPath; +import de.se_rwth.commons.logging.Log; +import freemarker.cache.MultiTemplateLoader; +import freemarker.cache.TemplateLoader; +import freemarker.core.Macro; +import freemarker.template.Configuration; +import freemarker.template.DefaultObjectWrapper; +import freemarker.template.TemplateMethodModelEx; +import freemarker.template.Version; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * Setup for generator (see {@link GeneratorEngine}). + */ +public class GeneratorSetup { + + /** + * Where to store all files (e.g. "gen" or "out") + */ + protected File outputDirectory = new File("out"); + + /** + * Used for handling variables and hook points; + * Default is only created with first get-access. + */ + protected GlobalExtensionManagement glex = null; + + /** + * The path for the handwritten code + * Default is only created with first get-access. + */ + protected MCPath handcodedPath; + + /** + * Additional path as the source of templates + */ + protected List additionalTemplatePaths = new ArrayList<>(); + + + /** + * Defines if tracing infos are added to the result as comments + */ + protected boolean tracing = true; + + /** + * The characters for the start of a comment. + * Usually these are the comments of the target language. + */ + protected String commentStart = "/*"; + + /** + * The characters for the end of a comment. + * Usually these are the comments of the target language. + */ + protected String commentEnd = "*/"; + + /** + * The model name + * (if the arftifacts are generated from one model, this could + * be an identifier of this model) + * By default the model name is absent -- + * and then the according tracing info is not printed at all. + */ + protected Optional modelName = Optional.empty(); + + /** + * The real engine provided by FreeMarker + */ + protected FreeMarkerTemplateEngine freeMarkerTemplateEngine; + + /** + * Desired default file extension, e.g. "java" + */ + protected String defaultFileExtension = "java"; + + /** + * Additional Suffix for a generated Class, if the + * class itself already exists. + */ + public static final String GENERATED_CLASS_SUFFIX = "TOP"; + + public static final Version FREEMARKER_VERSION = Configuration.VERSION_2_3_28; + /** + * A list of all freemarker functions that serve as aliases for Java methods, + * e.g. 'include' as alias for 'tc.include' + */ + protected List aliases = Lists.newArrayList(); + + public static final String ALIASES_TEMPLATE = "de.monticore.generating.templateengine.freemarker.Aliases"; + + public Configuration getConfig() { + Configuration config = new Configuration(FREEMARKER_VERSION); + + DefaultObjectWrapper o = new DefaultObjectWrapper(FREEMARKER_VERSION); + o.setTreatDefaultMethodsAsBeanMembers(true); + config.setObjectWrapper(o); + // don't look for templateName.de.ftl when templateName.ftl requested + config.setLocalizedLookup(false); + + MontiCoreTemplateLoader mcClassLoader = new MontiCoreTemplateLoader(getClassLoader()); + + if (additionalTemplatePaths.isEmpty()) { + config.setTemplateLoader(mcClassLoader); + } + else { + List loaders = new ArrayList<>(); + + for (File file : additionalTemplatePaths) { + // add file loaders. IO checks by FileTemplateLoader constructor + try { + loaders.add(new MontiCoreFileTemplateLoader(file)); + } + catch (IOException e) { + Log.warn("0xA1020 Unable to load templates from non-existent path " + file.getPath(), e); + } + } + + // this loader is added last such that all "built-in" templates located on the additionaltemplatepath are + // taking precedence over this one + loaders.add(mcClassLoader); + + config.setTemplateLoader(new MultiTemplateLoader(loaders.toArray(new TemplateLoader[loaders + .size()]))); + } + + config.setTemplateExceptionHandler(new MontiCoreTemplateExceptionHandler( + MontiCoreTemplateExceptionHandler.THROW_ERROR)); + + return config; + } + + + /*******************************************************/ + /** + * Sets the default file extension used for the generated files, e.g. java or + * .java (with leading dot). + * + * @param o the file extension, e.g. java or .java (with leading + * dot) + */ + public void setDefaultFileExtension(String o) { + if (o.startsWith(".")) { + this.defaultFileExtension = o.substring(1); + } + else { + this.defaultFileExtension = o; + } + } + + public String getDefaultFileExtension() { + return defaultFileExtension; + } + + /*******************************************************/ + public void setFileHandler(FileReaderWriter o) { + FileReaderWriter.init(o); + } + + /*******************************************************/ + public void setFreeMarkerTemplateEngine(FreeMarkerTemplateEngine o) { + this.freeMarkerTemplateEngine = o; + } + + public FreeMarkerTemplateEngine getFreeMarkerTemplateEngine() { + if (this.freeMarkerTemplateEngine == null) { + this.freeMarkerTemplateEngine = new FreeMarkerTemplateEngine(getConfig()); + } + return freeMarkerTemplateEngine; + } + + /*******************************************************/ + /*******************************************************/ + + /** + * Constructor + */ + public GeneratorSetup() { + } + + /*******************************************************/ + + public void setOutputDirectory(File outputDirectory) { + this.outputDirectory = outputDirectory; + } + + public File getOutputDirectory() { + return outputDirectory; + } + + + protected ClassLoader getClassLoader() { + return getClass().getClassLoader(); + } + + public void setGlex(GlobalExtensionManagement glex) { + this.glex = glex; + } + + public GlobalExtensionManagement getGlex() { + if (this.glex == null) { + this.glex = new GlobalExtensionManagement(); //default + } + return this.glex; + } + + public void setAdditionalTemplatePaths(List additionalTemplatePaths) { + this.additionalTemplatePaths = new ArrayList<>(additionalTemplatePaths); + } + + public List getAdditionalTemplatePaths() { + return additionalTemplatePaths; + } + + /** + * @return targetPath + */ + public MCPath getHandcodedPath() { + if (this.handcodedPath == null) { + this.handcodedPath = new MCPath(); //default + } + return this.handcodedPath; + } + + /** + * @param hwcPath the handcoded path to set + */ + public void setHandcodedPath(MCPath hwcPath) { + this.handcodedPath = hwcPath; + } + + /** + * @param tracing defines if tracing infos are added to the result as comments. + */ + public void setTracing(boolean tracing) { + this.tracing = tracing; + } + + /** + * @return true, if tracing infos are added to the result as comments. + */ + public boolean isTracing() { + return tracing; + } + + /** + * @return the characters for the start of a comment. Usually same as the target language. + */ + public String getCommentStart() { + return commentStart; + } + + /** + * @param commentStart the characters for the start of a comment. Usually same as the target + * language. + */ + public void setCommentStart(String commentStart) { + this.commentStart = commentStart; + } + + /** + * @return the characters for the end of a comment. Usually same as the target language. + */ + public String getCommentEnd() { + return commentEnd; + } + + /** + * @param commentEnd the characters for the end of a comment. Usually same as the target language. + */ + public void setCommentEnd(String commentEnd) { + this.commentEnd = commentEnd; + } + + /** + * @return modelName + */ + public Optional getModelName() { + return this.modelName; + } + + /** + * @param modelName the modelName to set + */ + public void setModelName(String modelName) { + this.modelName = Optional.ofNullable(modelName); + } + + + /** + * @return the aliases + */ + public List getAliases() { + return this.aliases; + } + + + /** + * @param aliases the aliases to set + */ + public void setAliases(List aliases) { + this.aliases = aliases; + } + + public void addAlias(Alias alias) { + this.aliases.add(alias); + } + + /** + * This is the Method that creates TemplateControllers + * (it is used afresh for each template that is called) + * HotSPOT: If a different Template Controller shall be used + * then override this method in a subclass + */ + public TemplateController getNewTemplateController(String templateName) { + return new TemplateController(this, templateName); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/CodeHookPoint.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/CodeHookPoint.java new file mode 100644 index 0000000000..0cc0c16b7b --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/CodeHookPoint.java @@ -0,0 +1,15 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine; + +/** + * Represents a code hook point. + * + */ +public abstract class CodeHookPoint extends HookPoint { + + public CodeHookPoint(){ + super(); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/GlobalExtensionManagement.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/GlobalExtensionManagement.java new file mode 100644 index 0000000000..f72007ee75 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/GlobalExtensionManagement.java @@ -0,0 +1,659 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine; + +import com.google.common.base.Strings; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import de.monticore.ast.ASTNode; +import de.monticore.generating.templateengine.freemarker.SimpleHashFactory; +import de.monticore.generating.templateengine.reporting.Reporting; +import de.se_rwth.commons.logging.Log; +import freemarker.ext.beans.BeansWrapper; +import freemarker.template.SimpleHash; +import freemarker.template.TemplateModelException; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * Class for managing hook points, features and (global) variables in templates. + * + */ +public class GlobalExtensionManagement { + + + protected SimpleHash globalData = SimpleHashFactory.getInstance().createSimpleHash(); + + // This list contains hook point decorations that are added **before** + // a template is called (may be a list) + protected final Multimap before = ArrayListMultimap.create(); + + // This list of hookpoints replaces a template (i.e. the hookpoints are + // executed and their results printed to output in the order they arrive) + // but the original template is not executed anymore. + protected final Multimap replace = ArrayListMultimap.create(); + + // This list contains hook point decorations that are added **after** + // a template is called (may be a list) + protected final Multimap after = ArrayListMultimap.create(); + + // While Variable "replace" replaces all templates when executed, this + // specificReplacement is only applied when template name and ASTnode fit + // (thus the replacement is individual for each ASTnode) + protected final Map> specificReplacement = Maps.newHashMap(); + + /** + * Map of all hook points + * + * for explicitely define hook points (which are internally managed + * to be disjoint from the template hook points) + * This also means explicitely define hook points cannot be decorated + * with "before" or "after" and they also do not contain a list, but only + * a single realization. + * + * This could be harmonized with the replace hook points + * (by simple integration with replacements, before and after structure) + */ + protected final Map hookPoints = Maps.newHashMap(); + + public GlobalExtensionManagement() { + } + + /** + * Set a list of global data. The parameter should not be null. + * + * @param data list of global data + */ + public void setGlobalData(SimpleHash data) { + Log.errorIfNull(data); + this.globalData = data; + } + + /** + * Retrieve a list of all global data + */ + SimpleHash getGlobalData(){ + return this.globalData; + } + + /** + * Checks whether a value with the given name is defined and is not null + * + * @param name of the value to check + * @return true if a variable exists and its value is not null + */ + public boolean hasGlobalVar(String name) { + try { + return globalData.get(name) != null; + } + catch (TemplateModelException e) { + Log.error("0xA7123 Internal Error on global value for \"" + name + "\""); + return false; + } + } + + /** + * Sets a new value which can be accessed with the given name in the + * templates. If the name is already in use an error is reported the previous + * value is overridden. + * + * @param name of the value to set + * @param value the actual content + */ + public void setGlobalValue(String name, Object value) { + Log.errorIfNull(name); + + Reporting.reportSetValue(name, value); + globalData.put(name, value); + } + + /** + * Defines a new value which can be accessed with the given name in the + * templates. If the name is already in use an error is reported. + * + * @param name of the value to set + * @param value the actual content + */ + public void defineGlobalVar(String name, Object value) { + if (hasGlobalVar(name)) { + Log.error("0xA0122 Global Value '" + name + "' has already been set.\n Old value: " + + getGlobalVar(name) + "\n New value: " + value); + } + setGlobalValue(name, value); + } + + /** + * Changes the value of an existing global variable. If the name is + * not in use an error is reported. + * + * @param name of the value to set + * @param value the actual content + */ + public void changeGlobalVar(String name, Object value) { + if (!hasGlobalVar(name)) { + Log.error("0xA0124 Global Value '" + name + "' has not been defined."); + } else { + setGlobalValue(name, value); + } + } + + /** + * Adds a new value to the given name. It converts a single value into a list + * if necessary. + * + * @param name of the value to set + * @param value the actual content + */ + @SuppressWarnings({ "unchecked", "deprecation" }) + public void addToGlobalVar(String name, Object value) { + if (hasGlobalVar(name)){ + Object currentValue = null; + try { + currentValue = BeansWrapper.getDefaultInstance().unwrap(globalData.get(name)); + } + catch (TemplateModelException e) { + Log.error("0xA8123 Internal Error on global value for \"" + name + "\""); + } + + Collection newValue = new ArrayList<>(); + // the global variable has already a value assigned + if (currentValue != null) { + if (currentValue instanceof Collection) { + newValue.addAll((Collection) currentValue); + } + else { + newValue.add(currentValue); + } + if (value instanceof Collection) { + newValue.addAll((Collection) value); + Reporting.reportAddValue(name, value, newValue.size()); + } + else { + newValue.add(value); + Reporting.reportAddValue(name, value, 1); + } + setGlobalValue(name, newValue); + } + // the variable has been defined but it has no value assigned + else { + if (value instanceof Collection) { + newValue.addAll((Collection) value); + }else { + newValue.add(value); + } + setGlobalValue(name, newValue); + } + } else { + Log.error("0xA8124 Global value with name \"" + name + "\" does not exist!"); + } + } + + /** + * Returns the value of the given variable. + * + * @param name of the variable + * @return the value + */ + @SuppressWarnings("deprecation") + public Object getGlobalVar(String name) { + try { + return BeansWrapper.getDefaultInstance().unwrap(globalData.get(name)); + } + catch (TemplateModelException e) { + Log.error("0xA0121 Internal Error on global value for \"" + name + "\""); + } + return null; + } + + /** + * Returns the value of the given variable. + * + * @param name of the variable + * @param defaultObject replaces if the variable is not present + * @return the value or the default + */ + @SuppressWarnings("deprecation") + public Object getGlobalVar(String name, Object defaultObject) { + if (hasGlobalVar(name)) { + try { + return BeansWrapper.getDefaultInstance().unwrap(globalData.get(name)); + } + catch (TemplateModelException e) { + Log.error("0xA0123 Internal Error on global value for \"" + name + "\""); + } + } + return defaultObject; + } + + + /** + * check whether the variable name (parameter) is defined: if not issue an + * error and continue + * + * @param name variable name + */ + public void requiredGlobalVar(String name) { + if (getGlobalVar(name) == null) { + Log.error("0xA0126 Missing required value \"" + name + "\""); + } + } + + /** + * check whether the list of variable names (parameter) is defined: if not + * issue an error and continue + * + * @param names list of variable names + */ + public void requiredGlobalVars(String... names) { + for (int i = 0; i < names.length; i++){ + requiredGlobalVar(names[i]); + } + } + + + // ---------------------------------------------------------------------- + // Section on Hook Points + // ---------------------------------------------------------------------- + + + /** + * @param hookName name of the hook point + * @param content String to be used as hook point + */ + public void bindStringHookPoint(String hookName, String content) { + bindHookPoint(hookName, new StringHookPoint(content)); + } + + /** + * @param hookName name of the hook point + * @param content Template-content to be used as hook point + */ + public void bindTemplateStringHookPoint(String hookName, String content) { + try { + bindHookPoint(hookName, new TemplateStringHookPoint(content)); + } + catch (IOException e) { + Log.error("0xA7124 Cannot bind hookpoint " + hookName); + } + } + + /** + * @param hookName name of the hook point + * @param tpl Template to be used as hook point + */ + public void bindTemplateHookPoint(String hookName, String tpl) { + bindHookPoint(hookName, new TemplateHookPoint(tpl)); + } + + /** + * @param hookName name of the hook point + * @param hp + */ + public void bindHookPoint(String hookName, HookPoint hp) { + Reporting.reportSetHookPoint(hookName, hp); + warnIfHookPointExists(hookName); + hookPoints.put(hookName, hp); + } + + /** + * @param hookName name of the hook point + * @return the (processed) value of the hook point + */ + public String defineHookPoint(TemplateController controller, String hookName, ASTNode ast) { + + String result = controller.getGeneratorSetup().isTracing()?"/* Hookpoint: " + hookName + " */":""; + HookPoint hp = hookPoints.get(hookName); + Reporting.reportCallHookPointStart(hookName, hp, ast); + + if (hookPoints.containsKey(hookName)) { + result = hp.processValue(controller, ast); + } + + Reporting.reportCallHookPointEnd(hookName); + + return Strings.nullToEmpty(result); + } + + /** + * @param hookName name of the hook point + * @return the (processed) value of the hook point + */ + public String defineHookPointWithDefault(TemplateController controller, String hookName, ASTNode ast, String defStr) { + if (existsHookPoint(hookName)) { + return defineHookPoint(controller, hookName, ast); + } + return defStr; + } + + /** + * @param hookName name of the hook point + * @return the (processed) value of the hook point + */ + public String defineHookPoint(TemplateController controller, String hookName, ASTNode ast, Object... args) { + + String result = null; + HookPoint hp = hookPoints.get(hookName); + Reporting.reportCallHookPointStart(hookName, hp, ast); + + if (hookPoints.containsKey(hookName)) { + result = hp.processValue(controller, ast, Arrays.asList(args)); + } + + Reporting.reportCallHookPointEnd(hookName); + + return Strings.nullToEmpty(result); + } + + /** + * @param hookName name of the hook point + * @return the (processed) value of the hook point + */ + public String defineHookPointWithDefault(TemplateController controller, String hookName, ASTNode ast, String defStr, Object... args) { + if (existsHookPoint(hookName)) { + return defineHookPoint(controller, hookName, ast, args); + } + return defStr; + } + + /** + * @param hookName name of the hook point + * @return the (processed) value of the hook point + */ + public String defineHookPoint(TemplateController controller, String hookName, Object... args) { + + String result = null; + HookPoint hp = hookPoints.get(hookName); + Reporting.reportCallHookPointStart(hookName, hp, controller.getAST()); + + if (hookPoints.containsKey(hookName)) { + result = hp.processValue(controller, Arrays.asList(args)); + } + + Reporting.reportCallHookPointEnd(hookName); + + return Strings.nullToEmpty(result); + } + + /** + * @param hookName name of the hook point + * @return the (processed) value of the hook point + */ + public String defineHookPointWithDefault(TemplateController controller, String hookName, String defStr, Object... args) { + if (existsHookPoint(hookName)) { + return defineHookPoint(controller, hookName, args); + } + return defStr; + } + + /** + * @param hookName name of the hook point + * @return the (processed) value of the hook point + */ + public String defineHookPoint(TemplateController controller, String hookName) { + return defineHookPoint(controller, hookName, controller.getAST()); + } + + /** + * @param hookName name of the hook point + * @return the (processed) value of the hook point + */ + public String defineHookPointWithDefault(TemplateController controller, String hookName, String defStr) { + if (existsHookPoint(hookName)) { + return defineHookPoint(controller, hookName, controller.getAST()); + } + return defStr; + } + + /** + * @param hookName name of the hook point + * @return the (processed) value of the hook point + */ + public boolean existsHookPoint(String hookName) { + return hookPoints.containsKey(hookName); + } + + /** + * Returns a set of templates that have been defined to replace the template + * templateName. If no template forwardings have been defined, + * then templateName is returned. + * + * @param templateName The name of the template + * @return A list of templates that have been defined to replace the + * 'templateNames' templates + */ + protected List getTemplateForwardings(String templateName, ASTNode ast) { + List replacements = Lists.newArrayList(); + Collection beforeHooks = this.before.get(templateName); + Collection afterHooks = this.after.get(templateName); + + if (beforeHooks != null) { + replacements.addAll(beforeHooks); + Reporting.reportCallBeforeHookPoint(templateName, beforeHooks, ast); + } + + List hps = getSpecificReplacement(templateName, ast); + if(!hps.isEmpty()){ + Reporting.reportCallSpecificReplacementHookPoint(templateName, hps, ast); + } + else { + hps = getTemplateForwardingsX(templateName, ast); + } + replacements.addAll(hps); + + if (afterHooks != null) { + replacements.addAll(afterHooks); + Reporting.reportCallAfterHookPoint(templateName, afterHooks, ast); + } + return replacements; + } + + protected List getSpecificReplacement(String templateName, ASTNode ast) { + Map replacedTemplates = this.specificReplacement.get(templateName); + if (replacedTemplates != null && replacedTemplates.containsKey(ast)) { + return Lists.newArrayList(replacedTemplates.get(ast)); + } + return Lists.newArrayList(); + } + + /** + * Returns a set of templates that have been defined to replace the + * 'templateName' template. If no template forwardings have been defined, then + * the 'templateName' template is returned. + * + * @param templateName The name of the template + * @return A list of templats that have been defined to replace the + * 'templateName' template + */ + protected List getTemplateForwardingsX(String templateName, ASTNode ast) { + List forwardings = Lists.newArrayList(); + + if (containsTemplateForwarding(templateName)) { + if(this.replace.containsKey(templateName)){ + forwardings.addAll(this.replace.get(templateName)); + Reporting.reportCallReplacementHookPoint(templateName, forwardings, ast); + } else{ + forwardings.addAll(Lists.newArrayList(new TemplateHookPoint(templateName))); + } + } + else { + forwardings.add(new TemplateHookPoint(templateName)); + Reporting.reportExecuteStandardTemplate(templateName, ast); + } + return forwardings; + } + + protected boolean containsTemplateForwarding(String templateName) { + return this.before.containsKey(templateName) + | this.replace.containsKey(templateName) + | this.after.containsKey(templateName); + } + + /** + * Future inclusion of 'oldTemplate' will be replaced by 'newTemplate'. NOTE: + * This replacement has only an effect if 'oldTemplate' is included directly. + * + * @param oldTemplate qualified name of template to be replaced + */ + public void replaceTemplate(String oldTemplate, HookPoint hp) { + replaceTemplate(oldTemplate, Lists.newArrayList(hp)); + } + + /** + * Future inclusion of 'oldTemplate' will be replaced by list of + * 'newTemplates'. NOTE: This replacement has only an effect if 'oldTemplate' + * is included directly. + * + * @param oldTemplate qualified name of template to be replaced + */ + public void replaceTemplate(String oldTemplate, List newHps) { + Reporting.reportTemplateReplacement(oldTemplate, newHps); + + if (!newHps.isEmpty()) { + // remove all previous replacements + this.replace.removeAll(oldTemplate); + this.replace.putAll(oldTemplate, newHps); + } + else { + this.replace.removeAll(oldTemplate); + } + } + + public void replaceTemplate(String oldTemplate, ASTNode node, HookPoint newHp) { + Reporting.reportASTSpecificTemplateReplacement(oldTemplate, node, newHp); + + Map replacedTemplates = this.specificReplacement.get(oldTemplate); + if (replacedTemplates != null) { + replacedTemplates.put(node, newHp); + } + else { + Map specificTemplate = Maps.newHashMap(); + specificTemplate.put(node, newHp); + this.specificReplacement.put(oldTemplate, specificTemplate); + } + } + + /** + * Everytime 'template' is included directly (e.g. by + * {@link TemplateController#include(String, ASTNode)}), 'beforeTemplate' will + * be included before it. + * + * @param template qualified name of the template + */ + public void setBeforeTemplate(String template, HookPoint beforeHp) { + setBeforeTemplate(template, Lists.newArrayList(beforeHp)); + } + + /** + * Everytime 'template' is included directly (e.g. by + * {@link TemplateController#include(String, ASTNode)}), the templates in + * 'beforeTemplate' will be included before it. + * + * @param template qualified name of the template + */ + public void setBeforeTemplate(String template, List beforeHps) { + Reporting.reportSetBeforeTemplate(template, beforeHps); + + // remove all previous replacements + this.before.removeAll(template); + if (!beforeHps.isEmpty()) { + this.before.putAll(template, beforeHps); + } + } + + /** + * Everytime 'template' is included directly (e.g. by + * {@link TemplateController#include(String, ASTNode)}), + * the 'HookPoint' 'afterHp' will + * be included after it. + * + * Care: This overrides any effect that previous calls of 'addAfterTemplate' + * and 'setAfterTemplate' on the same template have. + * + * @param template qualified name of the template + */ + public void setAfterTemplate(String template, HookPoint afterHp) { + setAfterTemplate(template, Lists.newArrayList(afterHp)); + } + + /** + * Everytime 'template' is included directly (e.g. by + * {@link TemplateController#include(String, ASTNode)}), the 'HookPoints' in + * 'afterHps' will be included after it. + * + * Care: This overrides any effect that previous calls of 'addAfterTemplate' + * and 'setAfterTemplate' on the same template have. + * + * @param template qualified name of the template + */ + public void setAfterTemplate(String template, List afterHps) { + Reporting.reportSetAfterTemplate(template, afterHps); + + // remove all previous replacements + this.after.removeAll(template); + if (!afterHps.isEmpty()) { + this.after.putAll(template, afterHps); + } + } + + /** + * Everytime 'template' is included directly (e.g. by + * {@link TemplateController#include(String, ASTNode)}), the template in + * 'afterHp' will be included after it. + * + * Multiple additions are possible. 'setAfterTemplate' overrides all additions. + * + * @param template qualified name of the template + */ + public void addAfterTemplate(String template, HookPoint afterHp) { + Reporting.reportAddAfterTemplate(template, Lists.newArrayList(afterHp)); + + this.after.put(template, afterHp); + } + + protected void warnIfHookPointExists(String hookName) { + if (hookPoints.containsKey(hookName)) { + Log.warn("0xA1036 Hook point '" + hookName + "' is already defined. It will be overwritten."); + } + } + + /** + * Returns a new template hook point. + * It executes a template and injects the result at the hook point. + * @see TemplateHookPoint#TemplateHookPoint(String) + * @param template qualified name of the template + * @return the TemplateHookPoint + */ + public TemplateHookPoint templateHP(String template) { + return new TemplateHookPoint(template.contains(".") ? template : template + ".ftl"); + } + + /** + * Returns a new string hook point. + * It injects the uninterpreted string value at the hook point. + * @see StringHookPoint#StringHookPoint(String) + * @param value the uninterpreted string value + * @return the StringHookPoint + */ + public StringHookPoint stringHP(String value) { + return new StringHookPoint(value); + } + + /** + * Returns a new template string hook point. + * It executes the statement as template content and injects the result at the hook point. + * @see TemplateStringHookPoint#TemplateStringHookPoint(String) + * @param statement the inlined template text + * @return the TemplateStringHookPoint + */ + public TemplateStringHookPoint templateStringHP(String statement) throws IOException { + return new TemplateStringHookPoint(statement); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/HookPoint.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/HookPoint.java new file mode 100644 index 0000000000..6d8e0ec1a8 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/HookPoint.java @@ -0,0 +1,28 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine; + +import java.util.List; + +import de.monticore.ast.ASTNode; + +/** + * Represents a hook point in templates + * + */ +public abstract class HookPoint { + + public abstract + String processValue(TemplateController controller, + ASTNode ast); + + public abstract + String processValue(TemplateController controller, + List args); + + public abstract + String processValue(TemplateController controller, + ASTNode node, + List args); + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/ObjectFactory.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/ObjectFactory.java new file mode 100644 index 0000000000..db319ea299 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/ObjectFactory.java @@ -0,0 +1,107 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine; + +import de.se_rwth.commons.logging.Log; + +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.List; + +/** + * Factory to instantiate any object from Templates + * using op.instantiate(classname) + * + */ +public class ObjectFactory { + + protected static ObjectFactory factory = null; + protected ClassLoader classLoader; + + protected ObjectFactory() { + classLoader = getClass().getClassLoader(); + } + + public static Object createObject(String qualifiedName) { + return createObject(qualifiedName, new ArrayList<>(), new ArrayList<>()); + } + + public static Object createObject(String qualifiedName, List params) { + return createObject(qualifiedName, getTypes(params), params); + } + + public static Object createObject(String qualifiedName, List> paramTypes, List params) { + if (factory == null) { + factory = new ObjectFactory(); + } + return factory.doCreateObject(qualifiedName, paramTypes, params); + } + + protected Object doCreateObject(String qualifiedName, List> paramTypes, List params) { + Class referencedClass = null; + + // try to call constructor directly + try { + referencedClass = classLoader.loadClass(qualifiedName); + return referencedClass + .getConstructor(paramTypes.toArray(new Class[] {})) + .newInstance(params.toArray(new Object[] {})); + } + catch (ClassNotFoundException e) { + Log.error("0xA0118 Template-execution: Could not find Class " + qualifiedName); + } + catch (Exception e) { + // types of direct constructor-call are not identical + // try to fix it by searching for a matching constructor (using supertypes of the given params) + if (referencedClass != null) { + for (Constructor constr : referencedClass.getConstructors()) { + if (constr.getParameterTypes().length == paramTypes.size()) { + for (int i = 0; i < paramTypes.size(); i++) { + if (!constr.getParameterTypes()[i].isAssignableFrom(paramTypes.get(i))) { + continue; + } + try { + return constr.newInstance(params.toArray(new Object[] {})); + } + catch (Exception e2) { + Log.error("0xA0119 Template-execution: Could not instanciate Class " + + qualifiedName + " with parameters " + paramTypes); + } + } + } + } + } + } + return null; + } + + protected static List> getTypes(List params) { + if (factory == null) { + factory = new ObjectFactory(); + } + return factory.doGetTypes(params); + } + + protected List> doGetTypes(List params) { + List> paramTypes = new ArrayList<>(); + for (Object obj : params) { + paramTypes.add(obj.getClass()); + } + return paramTypes; + } + + static void setClassLoader(ClassLoader loader) { + if (loader == null) { + return; + } + if (factory == null) { + factory = new ObjectFactory(); + } + factory.doSetClassLoader(loader); + } + + protected void doSetClassLoader(ClassLoader loader) { + this.classLoader = loader; + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/StringHookPoint.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/StringHookPoint.java new file mode 100644 index 0000000000..4ce53e95ae --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/StringHookPoint.java @@ -0,0 +1,48 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine; + +import de.monticore.ast.ASTNode; + +import java.util.List; + +/** + * Represents a String hook point. + * + * The string will be copied as result of the hook without further change. + * + */ +public class StringHookPoint extends HookPoint { + + protected final String value; + + public StringHookPoint(String value) { + super(); + this.value = value; + } + + @Override + public String processValue(TemplateController controller, ASTNode ast) { + return value; + } + + @Override + public String processValue(TemplateController controller, List args) { + return value; + } + + /** + * @see de.monticore.generating.templateengine.HookPoint#processValue(de.monticore.generating.templateengine.TemplateController, de.monticore.ast.ASTNode, java.util.List) + */ + @Override + public String processValue(TemplateController controller, ASTNode node, List args) { + return value; + } + + public String getValue() { + return value; + } + + + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/TemplateController.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/TemplateController.java new file mode 100644 index 0000000000..42bf9049b2 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/TemplateController.java @@ -0,0 +1,696 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine; + +import com.google.common.base.Strings; +import com.google.common.collect.Lists; +import de.monticore.ast.ASTNode; +import de.monticore.generating.GeneratorSetup; +import de.monticore.generating.templateengine.freemarker.SimpleHashFactory; +import de.monticore.generating.templateengine.freemarker.alias.*; +import de.monticore.generating.templateengine.reporting.Reporting; +import de.monticore.io.FileReaderWriter; +import de.se_rwth.commons.Names; +import de.se_rwth.commons.logging.Log; +import freemarker.ext.beans.BeansWrapper; +import freemarker.template.SimpleHash; +import freemarker.template.Template; +import freemarker.template.TemplateModel; +import freemarker.template.TemplateModelException; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Strings.isNullOrEmpty; +import static com.google.common.collect.Lists.newArrayList; + +/** + * Provides methods for manipulating the content of templates, mainly for + * calling and including of templates. + */ +public class TemplateController { + + /** + * Variable name for the current node (used in the templates) + **/ + public static final String AST = "ast"; + + /** + * Variable name for the current TemplateController (used in the templates) + **/ + public static final String TC = "tc"; + + /** + * Variable name for the GLEX object (used in the templates) + **/ + public static final String GLEX = "glex"; + + /** + * General config variables that hold for all template executions + */ + protected final GeneratorSetup config; + + /** + * list that contains all the Template names that should be blocked from generating comments for + **/ + protected List templateBlackList; + + /** + * Name of the current template (usually fully qualified) + */ + protected String templatename; + + /** + * According to FreeMarker, templates don't have a "signature" + * We can mimic such a signature through method calls: + * The signature(...) method defines a list of variables. + * And the include call allows a list of arguments that are bound to + * these variables, when the template is executed. + */ + protected List arguments = newArrayList(); + + protected SimpleHash data = SimpleHashFactory.getInstance().createSimpleHash(); + + public TemplateController(GeneratorSetup setup, String templatename) { + this.config = setup; + this.templatename = templatename; + this.templateBlackList = new ArrayList<>(); + } + + public TemplateController(GeneratorSetup setup, String templatename, List templateBlackList) { + this.config = setup; + this.templatename = templatename; + this.templateBlackList = templateBlackList; + } + + /** + * getters and setters for the relevant attributes + */ + public GeneratorSetup getGeneratorSetup() { + return config; + } + + public String getTemplatename() { + return templatename; + } + + protected void setTemplatename(String templatename) { + this.templatename = templatename; + } + + public List getTemplateBlackList() { + return templateBlackList; + } + + public void setTemplateBlackList(List templateBlackList) { + this.templateBlackList = templateBlackList; + } + + /** + * Execute each of the templates on each ASTNode of the list. Concatenate the + * results together in one big String and include that into the currently + * processed output. We iterate on the templates and ASTNodes. In case order + * is important: The iteration goes like this: + * + * for ( templates ) { + * for ( ASTNodes ) {...} + * } + * + * Inside the inner loop, it is checked whether Hookpoints are to be called. + * Template filename may be qualified (using "."). When it + * is not qualified, the filename is taken from the current package (same as + * the calling template). + * + * @param templatenames list of filenames, qualified or not + * @param astlist where we execute the template on in an iteration + * @return produced output + */ + public StringBuilder include(List templatenames, List astlist) { + StringBuilder ret = new StringBuilder(); + for (String template : templatenames) { + for (ASTNode ast : astlist) { + List templateForwardings = + config.getGlex().getTemplateForwardings(template, ast); + for (HookPoint templateHp : templateForwardings) { + ret.append(templateHp.processValue(this, ast)); + } + } + } + + return ret; + } + + public boolean isTemplateNoteGenerated(Template template) { + return !templateBlackList.contains(template.getName()); + } + + /** + * Execute a template without resolving the forwardings. This method is + * package default and should only be used by the template hook point + * + * @param templateName the name of the template + * @param ast the ast node + * @return produced output + */ + StringBuilder includeWithoutForwarding(String templateName, ASTNode ast) { + StringBuilder ret = new StringBuilder(); + ret.append(processTemplate(templateName, ast, new ArrayList<>())); + return ret; + } + + /** + * Defines the signature of a template.
+ *
+ * Note that, due to technical constraints, at first, the current template is + * included and the arguments are passed. Second, the signature is defined. + * + * @param parameterNames the list of the parameter names (=signature) + */ + public void signature(List parameterNames) { + Log.errorIfNull(parameterNames); + + checkArgument(parameterNames.size() == arguments.size(), + "0xA5298 Template '" + templatename + "': Signature size (#" + parameterNames.size() + + ") and number of arguments (#" + arguments.size() + ") mismatch."); + + // bind values (arguments) to names (parameters/signature) + // and inject into template + for (int i = 0; i < parameterNames.size(); i++) { + data.put(parameterNames.get(i), arguments.get(i)); + } + + } + + /** + * Delegates to {@link #signature(List)}. + */ + public void signature(String... parameterName) { + signature(Lists.newArrayList(parameterName)); + } + + List getArguments() { + return arguments; + } + + + /** + * Includes a template without an explicit ast. (ast is current ast node) + * + * @param templateName + * @return + */ + public StringBuilder include(String templateName) { + return include(newArrayList(templateName), newArrayList(getAST())); + } + + /** + * Includes a template without an explicit ast. (ast is current ast node) + * Execute each of the templates on the ASTNode. Concatenate the results + * together in one big String and include that into the currently processed + * output. We iterate on the templates. Template filename may be qualified + * (using "."). When it is not qualified, the filename is taken from the + * current package (same as the calling template). + * + * @param templateNames list of filenames, qualified or not + * @return produced output + */ + public StringBuilder include(List templateNames) { + return include(templateNames, newArrayList(getAST())); + } + + /** + * Execute each of the templates on the ASTNode. Concatenate the results + * together in one big String and include that into the currently processed + * output. We iterate on the templates. Template filename may be qualified + * (using "."). When it is not qualified, the filename is taken from the + * current package (same as the calling template). + * + * @param templateNames list of filenames, qualified or not + * @param ast ast-node the template is operating on + * @return produced output + */ + public StringBuilder include(List templateNames, ASTNode ast) { + return include(templateNames, newArrayList(ast)); + } + + /** + * Execute the single template on each ASTNode of the list. Concatenate the + * results together in one big String and include that into the currently + * processed output. We iterate on the ASTNodes. Template filename may be + * qualified (using "."). When it is not qualified, the filename is taken from + * the current package (same as the calling template). + * + * @param templateName filename, qualified or not + * @param astlist where we execute the template on in an iteration + * @return produced output + */ + public StringBuilder include(String templateName, List astlist) { + return include(newArrayList(templateName), astlist); + } + + /** + * Include the template into the currently processed output. The template is + * executed on ASTNode ast. Remark: even though the name suggests to run + * several templates, this is the version that executes on a single template + * given as string. We only handle one template on one node. Template filename + * may be qualified (using "."). When it is not qualified, the filename is + * taken from the current package (same as the calling template). + * + * @param templateName name of the template to be executed, qualified or not + * @param ast ast-node the template is operating on + * @return output for the file (may be part of a file only) + */ + public StringBuilder include(String templateName, ASTNode ast) { + return include(newArrayList(templateName), newArrayList(ast)); + } + + /** + * Include the template into the currently processed output. Remark: even + * though the name suggests to run several templates, this is the version that + * executes on a single template given as string. We only handle one template + * on one node. Template filename may be qualified (using "."). When it is not + * qualified, the filename is taken from the current package (same as the + * calling template). + * + * @param templateName name of the template to be executed, qualified or not + * @param templateArguments additional data that is passed to the called + * template + * @return output for the file (may be part of a file only) + */ + public StringBuilder includeArgs(String templateName, ASTNode node, List templateArguments) { + StringBuilder ret = new StringBuilder(); + List templateForwardings = config.getGlex().getTemplateForwardings(templateName, node); + for (HookPoint tn : templateForwardings) { + ret.append(tn.processValue(this, node, templateArguments)); + } + + return ret; + } + + /** + * Include the template into the currently processed output. Remark: even + * though the name suggests to run several templates, this is the version that + * executes on a single template given as string. We only handle one template + * on one node. Template filename may be qualified (using "."). When it is not + * qualified, the filename is taken from the current package (same as the + * calling template). + * + * @param templateName name of the template to be executed, qualified or not + * @param templateArguments additional data that is passed to the called + * template + * @return output for the file (may be part of a file only) + */ + public StringBuilder includeArgs(String templateName, List templateArguments) { + StringBuilder ret = new StringBuilder(); + List templateForwardings = config.getGlex().getTemplateForwardings(templateName, getAST()); + for (HookPoint tn : templateForwardings) { + ret.append(tn.processValue(this, templateArguments)); + } + + return ret; + } + + /** + * Delegates to {@link #includeArgs(String, List)} + */ + public StringBuilder includeArgs(String templateName, String... templateArgument) { + return includeArgs(templateName, Lists.newArrayList(templateArgument)); + } + + /** + * This is an unusual method. It does nothing and should not be used. It will + * be called from freemarker automatically, when one of the arguments (either + * the filename or the ast) is null. In this case an error is issued. + * + * @param empty1 + * @param empty2 + */ + public void include(Object empty1, Object empty2) { + // Extension points in templates (holes) can be unused. In this case + // includeTemplates + // is called with null which is no error and can therefore be ignored. + + String msg; + if (empty1 == null && empty2 == null) { + msg = "0xA2936 Template name and ast node are null in " + templatename; + } else if (empty1 == null) { + msg = "0xA2937 Template name is null in " + templatename + " using " + empty2.getClass(); + } else { + msg = "0xA2938 Ast node is null in " + templatename + " calling template " + empty1; + } + Log.error(msg + " ## You made an illegal call of includeTemplate. As the error says at least " + + "one argument was null. That shouldn't happen. ##"); + } + + /** + * Execute the template and put the result into a file. The template is + * executed on ASTNode ast. File qualifiedFileName + default file extension is + * opened, written and closed again! + * + * @param templateName full qualified filename + * @param ast where we execute the template on + * @return none (= empty string within Freemarker) + */ + public void write(String templateName, String qualifiedFileName, ASTNode ast) { + writeArgs(templateName, qualifiedFileName, config.getDefaultFileExtension(), ast, + new ArrayList<>()); + } + + public void write(String templateName, String qualifiedFileName, String fileExtension, ASTNode ast) { + writeArgs(templateName, qualifiedFileName, fileExtension, ast, new ArrayList<>()); + } + + public void write(final String templateName, final Path filePath, final ASTNode ast) { + writeArgs(templateName, filePath, ast, new ArrayList<>()); + } + + /** + * Execute the template and put the result into a file. The template is + * executed on ASTNode ast. And the file qualifiedFileName + fileExtension is + * opened, written and closed again! If fileExtension == null, then use "". + * fileExtension may start with ".", otherwise one is added. So ".java" and + * "java" have the same effect. Template filename may be qualified (using + * "."). When it is not qualified, the filename is taken from the current + * package (same as the calling template). + * + * @param templateName full qualified filename + * @param ast where we execute the template on + * @return none (= empty string within Freemarker) + */ + public void writeArgs(final String templateName, final String qualifiedFileName, + final String fileExtension, final ASTNode ast, final List templateArguments) { + String fileExtensionWithDot = Strings.nullToEmpty(fileExtension); + if (fileExtensionWithDot.startsWith(".")) { + fileExtensionWithDot = fileExtensionWithDot.substring(1); + } + + final String filePathStr = Names.getPathFromPackage(qualifiedFileName) + "." + fileExtensionWithDot; + final Path filePath = Paths.get(filePathStr); + + writeArgs(templateName, filePath, ast, templateArguments); + } + + /** + * Processes the template templateName with the ast + * and the given templateArguments and writes the content into + * the filePath. Note: Unless not absolute, the + * filePath is relative to the configured target directory (i.e., + * + * @param templateName the template to be processes + * @param filePath the file path in which the content is to be written + * @param ast the ast + * @param templateArguments additional template arguments (if needed). + */ + public void writeArgs(final String templateName, final Path filePath, final ASTNode ast, + final List templateArguments) { + final String qualifiedTemplateName = completeQualifiedName(templateName); + + StringBuilder content = new StringBuilder(); + // Replacement added: no also writeArgs replaces its Templates + List templateForwardings = + config.getGlex().getTemplateForwardings(templateName, ast); + for (HookPoint tn : templateForwardings) { + content.append(tn.processValue(this, ast, templateArguments)); + } + + if (content.length() == 0) { + Log.warn("0xA4057 Template " + qualifiedTemplateName + " produced no content for."); + } + + // add trace to source-model: + if (config.isTracing() && config.getModelName().isPresent()) { + content.insert(0, config.getCommentStart() + " generated from model " + config.getModelName().get() + " " + + config.getCommentEnd() + "\n"); + } + + Path completeFilePath; + if (filePath.isAbsolute()) { + completeFilePath = filePath; + } else { + completeFilePath = Paths.get(config.getOutputDirectory().getAbsolutePath(), filePath.toString()); + } + + Reporting.reportFileCreation(qualifiedTemplateName, filePath, ast); + + FileReaderWriter.storeInFile(completeFilePath, content.toString()); + + Log.debug(completeFilePath + " written successfully!", this.getClass().getName()); + + Reporting.reportFileFinalization(qualifiedTemplateName, filePath, ast); + } + + /** + * Include a template with additional data: We only handle one template on one + * node. This method allows to parameterize templates. Template filename may + * be qualified (using "."). When it is not qualified, the filename is taken + * from the current package. The resulting output may be stored or included in + * another generation process. + * + * @param templateName name of the template to be executed, qualified or not + * @param astNode ast-node the template is operating on + * @param passedArguments additional data that is passed to the included + * template + * @return output for the file (may be part of a file only) + */ + protected String processTemplate(String templateName, ASTNode astNode, + List passedArguments) { + String qualifiedTemplateName = completeQualifiedName(templateName); + + ASTNode ast = astNode; + + // If no ast is passed, get current ast, if exists + if (ast == null) { + ast = getAST(); + } + + Reporting.reportTemplateStart(qualifiedTemplateName, ast); + + StringBuilder ret = runInEngine(passedArguments, qualifiedTemplateName, ast); + + Reporting.reportTemplateEnd(qualifiedTemplateName, ast); + + return ret.toString(); + } + + StringBuilder runInEngine(List passedArguments, String templateName, ASTNode ast) { + initAliases(); + + // Load template + // TODO: + // It's pretty inefficient each time to create a new instance of the + // FreeMarker configuration by FreeMarkerTemplateEngine.loadTemplate(...) + // method + // Template t = FreeMarkerTemplateEngine.loadTemplate(templatename, + // classLoader, MontiCoreTemplateExceptionHandler.THROW_ERROR, + // externalTemplatePath); + Template template = config.getFreeMarkerTemplateEngine().loadTemplate(templateName); + + // add static functions to template + for (Alias alias : config.getAliases()) { + template.getConfiguration().setSharedVariable(alias.getName(), alias); + } + + return runInEngine(passedArguments, template, ast); + } + + /** + * Init template aliases if not already done. This happens once (for all main + * templates) + */ + @SuppressWarnings("rawtypes") + protected void initAliases() { + if (config.getAliases().isEmpty()) { + // Template util + config.addAlias(new IncludeAlias()); + config.addAlias(new Include2Alias()); + config.addAlias(new IncludeArgsAlias()); + config.addAlias(new SignatureAlias()); + + // Logging + config.addAlias(new TraceAlias()); + config.addAlias(new DebugAlias()); + config.addAlias(new InfoAlias()); + config.addAlias(new WarnAlias()); + config.addAlias(new ErrorAlias()); + + // Global vars + config.addAlias(new DefineGlobalVarAlias()); + config.addAlias(new ChangeGlobalVarAlias()); + config.addAlias(new AddToGlobalVarAlias()); + config.addAlias(new GetGlobalVarAlias()); + config.addAlias(new RequiredGlobalVarAlias()); + config.addAlias(new RequiredGlobalVarsAlias()); + + // Hookpoints + config.addAlias(new BindHookPointAlias()); + config.addAlias(new DefineHookPointAlias()); + config.addAlias(new DefineHookPointWithDefaultAlias()); + config.addAlias(new DefineHookPointWithDefault3Alias()); + config.addAlias(new ExistsHookPointAlias()); + } + } + + StringBuilder runInEngine(List passedArguments, Template template, ASTNode ast) { + StringBuilder ret = new StringBuilder(); + + if (template != null) { + // Initialize standard-data for template + + // get ast + if (ast == null) { + ast = getAST(); + } + + TemplateController tc = config.getNewTemplateController(template.getName()); + + SimpleHash d = config.getGlex().getGlobalData(); + Optional oldAst = Optional.empty(); + if (d.containsKey(AST)) { + try { + oldAst = Optional.ofNullable(d.get(AST)); + } catch (TemplateModelException e) { + + String usage = this.templatename != null ? " (" + this.templatename + ")" : ""; + Log.error("0xA0128 Globally defined data could not be passed to the called template " + + usage + ". ## This is an internal" + + "error that should not happen. Try to remove all global data. ##"); + } + } + d.put(AST, ast); + d.put(TC, tc); + d.put(GLEX, config.getGlex()); + + tc.data = d; + tc.arguments = newArrayList(passedArguments); + + // Run template with data to create output + config.getFreeMarkerTemplateEngine().run(ret, d, template); + if (oldAst.isPresent()) { + d.put(AST, oldAst.get()); + } + } else { + // no template + String usage = this.templatename != null ? " (used in " + this.templatename + ")" : ""; + Log.error("0xA0127 Missing template " + + usage + + " ## You have tried to use a template that " + + "doesn't exist. It may be in another package? The name printed is the qualified version, " + + "but you may have used the unqualified name. ##"); + } + + // add trace to template: + if (ret.length() != 0 && config.isTracing() && isTemplateNoteGenerated(template)) { + ret.insert(0, config.getCommentStart() + " generated by template " + template.getName() + + config.getCommentEnd() + "\n"); + } + + return ret; + } + + /** + * checks if the name seems to be already qualified: if not, adds the current + * package (of the template it operates on) + */ + protected String completeQualifiedName(String name) { + Log.errorIfNull(!isNullOrEmpty(name)); + + if (name.contains(".")) { + return name; + } + + return Names.getQualifier(templatename) + "." + name; + } + + ASTNode getAST() { + ASTNode ast = null; + + Object o = getValueFromData(AST); + if ((o != null) && (o instanceof ASTNode)) { + ast = (ASTNode) o; + } + + return ast; + } + + protected Object getValueFromData(String name) { + try { + return BeansWrapper.getDefaultInstance().unwrap(data.get(name)); + } catch (TemplateModelException e) { + Log.error("0xA0139 Could not find value for \"" + name + "\" in template \"" + templatename + + "\"", e); + } + return null; + } + + protected void setValueToData(String name, Object value) { + data.put(name, value); + } + + /** + * Can be used to instantiate any Java-class with a default constructor (no + * args). The passed className is either in the same package as the calling + * template, or it needs to be qualified (dot-separated). + * + * @param className name of the class to be instantiated + * @return an object of the given className + */ + public Object instantiate(String className) { + Reporting.reportInstantiate(className, new ArrayList<>()); + return ObjectFactory.createObject(completeQualifiedName(className)); + } + + /** + * Can be used to instantiate any Java-class with constructor of a signature + * fitting to the params. The passed className is either in the same package + * as the calling template, or it needs to be qualified (dot-separated). + * + * @param className name of the class to be instantiated + * @param params parameters provided for the constructor-call + * @return an object of the given className + */ + public Object instantiate(String className, List params) { + Reporting.reportInstantiate(className, params); + return ObjectFactory.createObject(completeQualifiedName(className), params); + } + + /** + * @see de.se_rwth.commons.logging.Log#trace(String, String) + */ + public void trace(String msg, String logName) { + Log.trace(msg, logName); + } + + /** + * @see de.se_rwth.commons.logging.Log#debug(String, String) + */ + public void debug(String msg, String logName) { + Log.debug(msg, logName); + } + + /** + * @see de.se_rwth.commons.logging.Log#info(String, String) + */ + public void info(String msg, String logName) { + Log.info(msg, logName); + } + + /** + * @see de.se_rwth.commons.logging.Log#warn(String) + */ + public void warn(String msg) { + Log.warn(msg); + } + + /** + * @see de.se_rwth.commons.logging.Log#error(String) + */ + public void error(String msg) { + Log.error(msg); + } + + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/TemplateHookPoint.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/TemplateHookPoint.java new file mode 100644 index 0000000000..b83a2a88e6 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/TemplateHookPoint.java @@ -0,0 +1,83 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine; + +import java.util.ArrayList; +import java.util.List; + +import com.google.common.base.Strings; +import com.google.common.collect.Lists; + +import de.monticore.ast.ASTNode; + +/** + * Represents a template hook. It executes a template and injects the result at the hook point + */ +public class TemplateHookPoint extends HookPoint { + + protected final String templateName; + + protected List templateArguments = Lists.newArrayList(); + + public TemplateHookPoint(String templateName) { + super(); + this.templateName = templateName; + } + + public TemplateHookPoint(String templateName, Object... templateArguments) { + super(); + this.templateName = templateName; + this.templateArguments = Lists.newArrayList(templateArguments); + } + + /** + * @return templateName + */ + public String getTemplateName() { + return this.templateName; + } + + @Override + public String processValue(TemplateController controller, ASTNode ast) { + return controller.processTemplate(templateName, ast, this.templateArguments).toString(); + } + + // Here we handle the case that arguments come from: + // a) The Hookpoint itself and + // b) the Template call + // The Template-call arguments are includes first and the + // Hookpoint arguments are added second + @Override + public String processValue(TemplateController controller, List args) { + if (!this.templateArguments.isEmpty()) { + ArrayList l = Lists.newArrayList(args); + l.addAll(this.templateArguments); + return controller.processTemplate(templateName, controller.getAST(), l).toString(); + } + return controller.processTemplate(templateName, controller.getAST(), args).toString(); + } + + @Override + public String toString() { + return Strings.isNullOrEmpty(templateName) ? super.toString() : templateName; + } + + /** + * @see de.monticore.generating.templateengine.HookPoint#processValue(de.monticore.generating.templateengine.TemplateController, + * de.monticore.ast.ASTNode, java.util.List) + */ + @Override + public String processValue(TemplateController controller, ASTNode node, List args) { + // Here we handle the case that arguments come from: + // a) The Hookpoint itself and + // b) the Template call + // The Template-call arguments are includes first and the + // Hookpoint arguments are added second + if (!this.templateArguments.isEmpty()) { + ArrayList l = Lists.newArrayList(args); + l.addAll(this.templateArguments); + return controller.processTemplate(templateName, node, l).toString(); + } + return controller.processTemplate(templateName, node, args).toString(); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/TemplateStringHookPoint.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/TemplateStringHookPoint.java new file mode 100644 index 0000000000..221d0104c0 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/TemplateStringHookPoint.java @@ -0,0 +1,39 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine; + +import com.google.common.collect.Lists; +import de.monticore.ast.ASTNode; +import de.monticore.generating.GeneratorSetup; +import freemarker.template.Configuration; +import freemarker.template.Template; + +import java.io.IOException; +import java.io.StringReader; +import java.util.List; + +public class TemplateStringHookPoint extends HookPoint { + protected Template template; + + public TemplateStringHookPoint(String statement) throws IOException { + super(); + template = new Template("template", new StringReader(statement), + new Configuration(GeneratorSetup.FREEMARKER_VERSION)); + } + + @Override + public String processValue(TemplateController controller, ASTNode ast) { + return controller.runInEngine(Lists.newArrayList(), template, ast).toString(); + } + + @Override + public String processValue(TemplateController controller, List args) { + return controller.runInEngine(args, template, null).toString(); + } + + @Override + public String processValue(TemplateController controller, ASTNode node, List args) { + return controller.runInEngine(args, template, node).toString(); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/FreeMarkerTemplateEngine.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/FreeMarkerTemplateEngine.java new file mode 100644 index 0000000000..1851c950d3 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/FreeMarkerTemplateEngine.java @@ -0,0 +1,115 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.freemarker; + +import static com.google.common.base.Strings.isNullOrEmpty; + +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.lang.reflect.InvocationTargetException; + +import de.se_rwth.commons.logging.Log; +import freemarker.log.Logger; +import freemarker.template.Configuration; +import freemarker.template.Template; +import freemarker.template.TemplateException; + +/** + * Helps to process FreeMarker templates with MontiCore. + * + * + */ +public class FreeMarkerTemplateEngine { + + public static final String FM_FILE_EXTENSION = ".ftl"; + protected final Configuration configuration; + + public FreeMarkerTemplateEngine(Configuration configuration) { + this.configuration = Log + .errorIfNull( + configuration, + "0xA4048 Configuration must not be null in FreeMarkerTemplateEngine constructor."); + } + + /** + * Loads the template named qualifiedTemplateName from the class path. + * + * @param qualifiedTemplateName full qualified template name EXCLUDING the + * file extension + * @return the FreeMarker template or null, if the template is not available + */ + public Template loadTemplate(String qualifiedTemplateName) { + isNullOrEmpty(qualifiedTemplateName); + + // use empty logger to suppress default free marker log behaviour + System.setProperty(Logger.SYSTEM_PROPERTY_NAME_LOGGER_LIBRARY, Logger.LIBRARY_NAME_NONE); + + Template result; + try { + result = configuration.getTemplate(qualifiedTemplateName); + } + catch (IOException e) { + throw new MontiCoreFreeMarkerException("0xA0560 Unable to load template: " + e.getMessage()); + } + return result; + } + + /** + * Runs the Template engine on the given template and data and writes the + * result into the StringBuilder buffer + * + * @param buffer contains the result + * @param data data for the template + * @param template the template file + * @throws IOException + */ + public void run(StringBuilder buffer, Object data, Template template) { + Log.errorIfNull(template, "0xA0562 The given template must not be null"); + String seperator = System.getProperty("line.seperator"); + + Writer w = new StringWriter(); + try { + template.process(data, w); + w.flush(); + } + catch (TemplateException e) { + StringBuilder causedExceptionInfo = new StringBuilder(); + if (e.getCause() instanceof MontiCoreFreeMarkerException) { + throw (MontiCoreFreeMarkerException)e.getCause(); + } + + Throwable targetException; + if (e.getCause() instanceof InvocationTargetException) { + targetException = ((InvocationTargetException)e.getCause()).getTargetException(); + if (targetException != null) { + causedExceptionInfo.append("\n").append(targetException); + } + } + throw new MontiCoreFreeMarkerException("0xA0561 Unable to execute template " + template.getName() + " : " + + e.getLocalizedMessage() + + seperator + "Exception-type: " + e.getCause() + causedExceptionInfo.toString() + + seperator + "Caused by " + seperator + e.getFTLInstructionStack(), + e.getCause()); + } + catch (IOException e) { + throw new MontiCoreFreeMarkerException("0xA0563 Could not read template " + template.getName() + " : " + e.getLocalizedMessage() + + seperator + "Exception-type: " + e.getCause()) ; + } + buffer.append(w.toString()); + } + + /** + * Loads the template first using the loadTemplate() method then runs the + * template engine using the run() method. + * + * @param qualifiedTemplateName full qualified template name EXCLUDING the + * file extension + * @param buffer contains the result + * @param data data for the template + */ + public void loadAndRun(String qualifiedTemplateName, StringBuilder buffer, Object data) { + Template t = loadTemplate(qualifiedTemplateName); + run(buffer, data, t); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/MontiCoreFileTemplateLoader.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/MontiCoreFileTemplateLoader.java new file mode 100644 index 0000000000..7505398b5c --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/MontiCoreFileTemplateLoader.java @@ -0,0 +1,57 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.freemarker; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.Optional; + +import com.google.common.collect.Lists; + +import de.monticore.generating.templateengine.reporting.Reporting; +import de.se_rwth.commons.logging.Log; +import freemarker.cache.FileTemplateLoader; + +public class MontiCoreFileTemplateLoader extends FileTemplateLoader { + + /** + * A template loader that uses files in a specified directory as the source of templates. + * + */ + public MontiCoreFileTemplateLoader(File baseDir) throws IOException { + super(baseDir); + } + + @Override + public java.lang.Object findTemplateSource(String templateName) throws java.io.IOException { + Log.debug("Looking for template " + templateName, MontiCoreFileTemplateLoader.class.getName()); + + String completeName = templateName.replace('.', '/').concat( + FreeMarkerTemplateEngine.FM_FILE_EXTENSION); + Object template = super.findTemplateSource(completeName); + if (template == null) { + if (templateName.endsWith(FreeMarkerTemplateEngine.FM_FILE_EXTENSION)) { + template = super.findTemplateSource(templateName); + } + else { + template = super.findTemplateSource(templateName + .concat(FreeMarkerTemplateEngine.FM_FILE_EXTENSION)); + } + } + if (template != null) { + Reporting.reportOpenInputFile(Optional.of(getBaseDirectory().toPath()), Paths.get(completeName)); + Reporting.reportUserSpecificTemplate(getBaseDirectory().toPath(), Paths.get(completeName)); + } + else { + List pathes = Lists.newArrayList(); + pathes.add(Paths.get(getBaseDirectory().getAbsolutePath())); + Reporting.reportFileExistenceChecking(pathes, + Paths.get(completeName)); + } + return template; + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/MontiCoreFreeMarkerException.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/MontiCoreFreeMarkerException.java new file mode 100644 index 0000000000..74571aabc5 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/MontiCoreFreeMarkerException.java @@ -0,0 +1,54 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.freemarker; + +public class MontiCoreFreeMarkerException extends RuntimeException { + + private static final long serialVersionUID = -1596687416377465351L; + + protected String messageForProtocol; + + /** + * Constructor for FreeMarkerException + */ + public MontiCoreFreeMarkerException() { + super(); + } + + /** + * Constructor for FreeMarkerException + * + * @param message + */ + public MontiCoreFreeMarkerException(String message) { + super(message); + } + + /** + * Constructor for FreeMarkerException + * + * @param message + */ + public MontiCoreFreeMarkerException(String message, String logMessage) { + super(message); + this.messageForProtocol = logMessage; + } + + /** + * Constructor for FreeMarkerException + * + * @param message + * @param tthrowable + */ + public MontiCoreFreeMarkerException(String message, Throwable tthrowable) { + super(message, tthrowable); + } + + /** + * @return logMessage + */ + public String getMessageForProtocol() { + return messageForProtocol; + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/MontiCoreTemplateExceptionHandler.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/MontiCoreTemplateExceptionHandler.java new file mode 100644 index 0000000000..999439422f --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/MontiCoreTemplateExceptionHandler.java @@ -0,0 +1,117 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.freemarker; + +import de.se_rwth.commons.logging.Log; +import freemarker.core.Environment; +import freemarker.template.TemplateException; +import freemarker.template.TemplateExceptionHandler; + +import java.io.IOException; +import java.io.Writer; + +/** + * Handles exceptions occurring during the template processing. + * + * + */ +public class MontiCoreTemplateExceptionHandler implements TemplateExceptionHandler { + + /** + * Add an error, add a comment to the generated code and continue processing + * the template skipping the erroneous expression. + */ + public static final int LOG_AND_CONTINUE = 0; + + /** + * Add an error, add a comment to the generated code and abort processing the + * template . + */ + public static final int LOG_AND_ABORT = 1; + + /** + * Add an error and continue processing the template without adding a comment + * to the generated code. + */ + public static final int CONTINUE = 2; + + /** + * Add an error and abort without adding a comment to the generated code. + */ + public static final int ABORT = 3; + + /** + * Throw the error without logging it. + */ + public static final int THROW_ERROR = 4; + + protected static final String COMMENT_START = "/* TODO: "; + + protected static final String COMMENT_END = " */"; + + /** The behavior from the handler. */ + protected final int behavior; + + /** + * Creates a new {@link MontiCoreTemplateExceptionHandler} with the default + * exception handling LOG_AND_CONTINUE. + */ + public MontiCoreTemplateExceptionHandler() { + behavior = LOG_AND_CONTINUE; + } + + /** + * Creates a new {@link MontiCoreTemplateExceptionHandler} with the given + * exception handling behavior. + * + * @param behavior use one of + * MontiCoreTemplateExceptionHandler.LOG_AND_CONTINUE, + * MontiCoreTemplateExceptionHandler.LOG_AND_ABORT, + * MontiCoreTemplateExceptionHandler.CONTINUE or + * MontiCoreTemplateExceptionHandler.ABORT + */ + public MontiCoreTemplateExceptionHandler(int behavior) { + if (behavior >= 0 && behavior <= 4) { + this.behavior = behavior; + } + else { + this.behavior = LOG_AND_CONTINUE; + } + } + + @Override + public void handleTemplateException(TemplateException te, Environment env, Writer writer) throws TemplateException { + switch (behavior) { + case LOG_AND_ABORT: + Log.error("0xA0354 " + te.getMessage()); + try { + writer.append(COMMENT_START + te.getMessage() + COMMENT_END); + } + catch (IOException e) { + Log.info("IOException during appending a message", "MontiCoreTemplateExceptionHandler"); + } + throw te; + case CONTINUE: + Log.error("0xA0355 " + te.getMessage()); + break; + case ABORT: + Log.error("0xA0356 " + te.getMessage()); + throw te; + case THROW_ERROR: + throw te; + case LOG_AND_CONTINUE: + + default: // includes LOG_AND_CONTINUE + Log.error(te.getMessage()); + try { + writer.append(COMMENT_START + "0xA0357 " + te.getMessage() + COMMENT_END); + } + catch (IOException e) { + Log.info("IOException during appending a message", "MontiCoreTemplateExceptionHandler"); + } + break; + } + + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/MontiCoreTemplateLoader.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/MontiCoreTemplateLoader.java new file mode 100644 index 0000000000..06d0a141c6 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/MontiCoreTemplateLoader.java @@ -0,0 +1,71 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.freemarker; + +import java.net.URL; +import java.util.Optional; + +import de.monticore.io.FileReaderWriter; +import de.se_rwth.commons.logging.Log; +import freemarker.cache.URLTemplateLoader; + +/** + * Is used to load templates with a given {@link ClassLoader}. + * + */ +public class MontiCoreTemplateLoader extends URLTemplateLoader { + + /** the used class loader */ + protected final ClassLoader classLoader; + + /** + * Creates a new {@link MontiCoreTemplateLoader} that uses the given class loader to load + * FreeMarker templates from the class path + * + * @param classLoader + */ + public MontiCoreTemplateLoader(ClassLoader classLoader) { + Log.errorIfNull(classLoader, + "0xA4049 ClassLoader must not be null in MontiCoreTemplateLoader constructor."); + this.classLoader = classLoader; + } + + /** + * Resolves the location of a template on the local machine. + * + * @see freemarker.cache.URLTemplateLoader#getURL(java.lang.String) + * @param templateName The qualified name of the Template. Example: "cd2data.core.Attribute" or + * "cd2data/core/Attribute" or "cd2data.core.Attribute.ftl" + * @return URL of the template on the local machine. Example: + * "jar:file:/C:/.../dex/gtr/target/dex-gtr-0.9.4-SNAPSHOT.jar!/cd2data/core/Attribute.ftl" (this + * is from the toString method of the URL object) + */ + @Override + protected URL getURL(String templateName) { + Log.debug("Requested template " + templateName, MontiCoreTemplateLoader.class.getName()); + + // Since the input is almost always dot separated, this method just goes ahead and converts it + // without checking, only in the rare case that this procedure is unsuccessful are + // alternatives considered + Optional result = FileReaderWriter.getResource(classLoader, + templateName.replace('.', '/').concat(FreeMarkerTemplateEngine.FM_FILE_EXTENSION)); + if (result.isPresent()) { + return result.get(); + } + // if the search was still unsuccessful the method tries once more and checks if the problem + // was that the original input already had the .ftl suffix (in that case the previous + // procedure would've added this suffix again). + // Note: This part of the procedure was placed here because it is almost never chosen. + if (templateName.endsWith(FreeMarkerTemplateEngine.FM_FILE_EXTENSION)) { + String newName = templateName.substring(0, + templateName.length() - FreeMarkerTemplateEngine.FM_FILE_EXTENSION.length()); + result = FileReaderWriter.getResource(classLoader, + newName.replace('.', '/').concat(FreeMarkerTemplateEngine.FM_FILE_EXTENSION)); + } + else { + result = FileReaderWriter.getResource(classLoader, templateName); + } + return result.orElse(null); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/SimpleHashFactory.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/SimpleHashFactory.java new file mode 100644 index 0000000000..1be1c5381e --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/SimpleHashFactory.java @@ -0,0 +1,55 @@ +/* (c) https://github.com/MontiCore/monticore */ + +/** + * + */ +package de.monticore.generating.templateengine.freemarker; + +import de.monticore.generating.GeneratorSetup; +import freemarker.log.Logger; +import freemarker.template.DefaultObjectWrapper; +import freemarker.template.ObjectWrapper; +import freemarker.template.SimpleHash; + +import java.util.Map; + +/** + * Use this factory to instantiate SimpleHash objects. + * + */ +// STATE SMELL PN +public class SimpleHashFactory { + + private static SimpleHashFactory theInstance; + + private SimpleHashFactory() { + // use empty logger to suppress default free marker log behaviour + System.setProperty(Logger.SYSTEM_PROPERTY_NAME_LOGGER_LIBRARY, Logger.LIBRARY_NAME_NONE); + } + + public static SimpleHashFactory getInstance() { + if (theInstance == null) { + synchronized (SimpleHashFactory.class) { + theInstance = new SimpleHashFactory(); + } + } + return theInstance; + } + + public SimpleHash createSimpleHash() { + return new SimpleHash(new DefaultObjectWrapper(GeneratorSetup.FREEMARKER_VERSION)); + } + + public SimpleHash createSimpleHash(Map map) { + return new SimpleHash(map, new DefaultObjectWrapper(GeneratorSetup.FREEMARKER_VERSION)); + } + + public SimpleHash createSimpleHash(ObjectWrapper wrapper) { + return new SimpleHash(wrapper); + } + + public SimpleHash createSimpleHash(Map map, ObjectWrapper wrapper) { + return new SimpleHash(map, wrapper); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/AddToGlobalVarAlias.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/AddToGlobalVarAlias.java new file mode 100644 index 0000000000..ae39ee737a --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/AddToGlobalVarAlias.java @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.generating.templateengine.freemarker.alias; + +public class AddToGlobalVarAlias extends SimpleGlexAlias { + public AddToGlobalVarAlias() { + super("addToGlobalVar", "addToGlobalVar", 2); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/Alias.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/Alias.java new file mode 100644 index 0000000000..080cee9310 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/Alias.java @@ -0,0 +1,69 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.generating.templateengine.freemarker.alias; + +import freemarker.core.Environment; +import freemarker.ext.beans.BeansWrapper; +import freemarker.ext.beans.CollectionModel; +import freemarker.template.ObjectWrapperAndUnwrapper; +import freemarker.template.TemplateMethodModelEx; +import freemarker.template.TemplateModel; +import freemarker.template.TemplateModelException; + +import java.util.ArrayList; +import java.util.List; + +public abstract class Alias implements TemplateMethodModelEx { + protected final String name; + protected final String methodName; + + protected Alias(String name, String method) { + this.name = name; + this.methodName = method; + } + + public String getName(){ + return name; + } + + public String getMethodName() { + return methodName; + } + + public abstract TemplateMethodModelEx getMethod() throws TemplateModelException; + + @Override + public Object exec(List arguments) throws TemplateModelException { + return getMethod().exec(arguments); + } + + protected List convertVarargsToList(List arguments, int startIndex) throws TemplateModelException { + // Conversion of ... syntax + List l = new ArrayList(); + if(!arguments.isEmpty()) { + ObjectWrapperAndUnwrapper wrapper = (ObjectWrapperAndUnwrapper) Environment.getCurrentEnvironment().getObjectWrapper(); + for (Object o : arguments.subList(startIndex, arguments.size())) { + l.add(wrapper.unwrap((TemplateModel) o)); + } + } + return l; + } + + protected CollectionModel convertVarargsToCollectionModel(List arguments, int startIndex) throws TemplateModelException{ + return new CollectionModel( + convertVarargsToList(arguments, startIndex), + (BeansWrapper) Environment.getCurrentEnvironment().getObjectWrapper() + ); + } + + protected void exactArguments(List arguments, int count) throws TemplateModelException { + if(arguments.size() != count){ + throw new TemplateModelException("Expected " + count + " arguments but got " + arguments.size()); + } + } + + protected void atLeastArguments(List arguments, int count) throws TemplateModelException { + if(arguments.size() < count){ + throw new TemplateModelException("Expected at least " + count + " arguments but got " + arguments.size()); + } + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/BindHookPointAlias.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/BindHookPointAlias.java new file mode 100644 index 0000000000..b3a6f3d0a5 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/BindHookPointAlias.java @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.generating.templateengine.freemarker.alias; + +public class BindHookPointAlias extends SimpleGlexAlias { + public BindHookPointAlias() { + super("bindHookPoint", "bindHookPoint", 2); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/ChangeGlobalVarAlias.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/ChangeGlobalVarAlias.java new file mode 100644 index 0000000000..6ff192588f --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/ChangeGlobalVarAlias.java @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.generating.templateengine.freemarker.alias; + +public class ChangeGlobalVarAlias extends SimpleGlexAlias { + public ChangeGlobalVarAlias() { + super("changeGlobalVar", "changeGlobalVar", 2); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/DebugAlias.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/DebugAlias.java new file mode 100644 index 0000000000..eb2a7b6ac4 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/DebugAlias.java @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.generating.templateengine.freemarker.alias; + +public class DebugAlias extends SimpleTcAlias{ + public DebugAlias() { + super("debug", "debug", 2); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/DefineGlobalVarAlias.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/DefineGlobalVarAlias.java new file mode 100644 index 0000000000..6ce7e5d881 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/DefineGlobalVarAlias.java @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.generating.templateengine.freemarker.alias; + +public class DefineGlobalVarAlias extends SimpleGlexAlias { + public DefineGlobalVarAlias() { + super("defineGlobalVar", "defineGlobalVar", 2); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/DefineHookPointAlias.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/DefineHookPointAlias.java new file mode 100644 index 0000000000..5405206033 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/DefineHookPointAlias.java @@ -0,0 +1,27 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.generating.templateengine.freemarker.alias; + +import freemarker.core.Environment; +import freemarker.template.TemplateModelException; + +import java.util.ArrayList; +import java.util.List; + +public class DefineHookPointAlias extends GlexAlias { + public DefineHookPointAlias() { + super("defineHookPoint", "defineHookPoint"); + } + + @Override + public Object exec(List arguments) throws TemplateModelException { + // 1 or 2 arguments: name, ast? + atLeastArguments(arguments, 1); + if(arguments.size() > 1){ + exactArguments(arguments, 2); + } + // First argument of defineHookPoint is always tc + ArrayList args = new ArrayList(arguments); + args.add(0, Environment.getCurrentEnvironment().getVariable("tc")); + return super.exec(args); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/DefineHookPointWithDefault3Alias.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/DefineHookPointWithDefault3Alias.java new file mode 100644 index 0000000000..fe7a0787ab --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/DefineHookPointWithDefault3Alias.java @@ -0,0 +1,23 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.generating.templateengine.freemarker.alias; + +import freemarker.core.Environment; +import freemarker.template.TemplateModelException; + +import java.util.ArrayList; +import java.util.List; + +public class DefineHookPointWithDefault3Alias extends GlexAlias { + public DefineHookPointWithDefault3Alias() { + super("defineHookPointWithDefault3", "defineHookPointWithDefault"); + } + + @Override + public Object exec(List arguments) throws TemplateModelException { + exactArguments(arguments, 3); + + ArrayList args = new ArrayList(arguments); + args.add(0, Environment.getCurrentEnvironment().getVariable("tc")); + return super.exec(args); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/DefineHookPointWithDefaultAlias.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/DefineHookPointWithDefaultAlias.java new file mode 100644 index 0000000000..c21f279f7e --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/DefineHookPointWithDefaultAlias.java @@ -0,0 +1,23 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.generating.templateengine.freemarker.alias; + +import freemarker.core.Environment; +import freemarker.template.TemplateModelException; + +import java.util.ArrayList; +import java.util.List; + +public class DefineHookPointWithDefaultAlias extends GlexAlias { + public DefineHookPointWithDefaultAlias() { + super("defineHookPointWithDefault", "defineHookPointWithDefault"); + } + + @Override + public Object exec(List arguments) throws TemplateModelException { + exactArguments(arguments, 2); + + ArrayList args = new ArrayList(arguments); + args.add(0, Environment.getCurrentEnvironment().getVariable("tc")); + return super.exec(args); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/ErrorAlias.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/ErrorAlias.java new file mode 100644 index 0000000000..bb94fb855a --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/ErrorAlias.java @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.generating.templateengine.freemarker.alias; + +public class ErrorAlias extends SimpleTcAlias{ + public ErrorAlias() { + super("error", "error", 1); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/ExistsHookPointAlias.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/ExistsHookPointAlias.java new file mode 100644 index 0000000000..1054783b64 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/ExistsHookPointAlias.java @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.generating.templateengine.freemarker.alias; + +public class ExistsHookPointAlias extends SimpleGlexAlias { + public ExistsHookPointAlias() { + super("existsHookPoint", "existsHookPoint", 1); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/GetGlobalVarAlias.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/GetGlobalVarAlias.java new file mode 100644 index 0000000000..6d57fb55d6 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/GetGlobalVarAlias.java @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.generating.templateengine.freemarker.alias; + +public class GetGlobalVarAlias extends SimpleGlexAlias { + public GetGlobalVarAlias() { + super("getGlobalVar", "getGlobalVar", 1); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/GlexAlias.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/GlexAlias.java new file mode 100644 index 0000000000..ac97bcf8a8 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/GlexAlias.java @@ -0,0 +1,23 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.generating.templateengine.freemarker.alias; + +import freemarker.core.Environment; +import freemarker.ext.beans.BeanModel; +import freemarker.template.TemplateMethodModelEx; +import freemarker.template.TemplateModelException; + +public class GlexAlias extends Alias{ + protected GlexAlias(String name, String method) { + super(name, method); + } + + @Override + public TemplateMethodModelEx getMethod() throws TemplateModelException { + BeanModel tc = (BeanModel) Environment.getCurrentEnvironment().getVariable("glex"); + TemplateMethodModelEx res = (TemplateMethodModelEx) ((BeanModel) tc.getAPI()).get(getMethodName()); + if(res == null){ + throw new TemplateModelException("Can not find method " + getMethodName() + " of GlobalExtensionManagement"); + } + return res; + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/Include2Alias.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/Include2Alias.java new file mode 100644 index 0000000000..5decbb4bc6 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/Include2Alias.java @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.generating.templateengine.freemarker.alias; + +public class Include2Alias extends SimpleTcAlias{ + public Include2Alias() { + super("include2", "include", 2); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/IncludeAlias.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/IncludeAlias.java new file mode 100644 index 0000000000..34e82a5e17 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/IncludeAlias.java @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.generating.templateengine.freemarker.alias; + +public class IncludeAlias extends SimpleTcAlias { + public IncludeAlias() { + super("include", "include", 1); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/IncludeArgsAlias.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/IncludeArgsAlias.java new file mode 100644 index 0000000000..954f94de5a --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/IncludeArgsAlias.java @@ -0,0 +1,23 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.generating.templateengine.freemarker.alias; + +import freemarker.template.TemplateModelException; + +import java.util.Arrays; +import java.util.List; + +public class IncludeArgsAlias extends TcAlias { + public IncludeArgsAlias() { + super("includeArgs", "includeArgs"); + } + + @Override + public Object exec(List arguments) throws TemplateModelException { + atLeastArguments(arguments, 1); + + return super.exec(Arrays.asList( + arguments.get(0), + convertVarargsToCollectionModel(arguments, 1) + )); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/InfoAlias.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/InfoAlias.java new file mode 100644 index 0000000000..9c3d57e3a6 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/InfoAlias.java @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.generating.templateengine.freemarker.alias; + +public class InfoAlias extends SimpleTcAlias{ + public InfoAlias() { + super("info", "info", 2); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/RequiredGlobalVarAlias.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/RequiredGlobalVarAlias.java new file mode 100644 index 0000000000..4673394f17 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/RequiredGlobalVarAlias.java @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.generating.templateengine.freemarker.alias; + +public class RequiredGlobalVarAlias extends SimpleGlexAlias { + public RequiredGlobalVarAlias() { + super("requiredGlobalVar", "requiredGlobalVar", 1); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/RequiredGlobalVarsAlias.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/RequiredGlobalVarsAlias.java new file mode 100644 index 0000000000..ac630ca6c5 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/RequiredGlobalVarsAlias.java @@ -0,0 +1,18 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.generating.templateengine.freemarker.alias; + +import freemarker.template.TemplateModelException; + +import java.util.Collections; +import java.util.List; + +public class RequiredGlobalVarsAlias extends GlexAlias { + public RequiredGlobalVarsAlias() { + super("requiredGlobalVars", "requiredGlobalVars"); + } + + @Override + public Object exec(List arguments) throws TemplateModelException { + return super.exec(Collections.singletonList(convertVarargsToCollectionModel(arguments, 0))); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/SignatureAlias.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/SignatureAlias.java new file mode 100644 index 0000000000..b47339010c --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/SignatureAlias.java @@ -0,0 +1,18 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.generating.templateengine.freemarker.alias; + +import freemarker.template.TemplateModelException; + +import java.util.Collections; +import java.util.List; + +public class SignatureAlias extends TcAlias{ + public SignatureAlias() { + super("signature", "signature"); + } + + @Override + public Object exec(List arguments) throws TemplateModelException { + return super.exec(Collections.singletonList(convertVarargsToCollectionModel(arguments, 0))); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/SimpleGlexAlias.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/SimpleGlexAlias.java new file mode 100644 index 0000000000..1238bd621e --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/SimpleGlexAlias.java @@ -0,0 +1,21 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.generating.templateengine.freemarker.alias; + +import freemarker.template.TemplateModelException; + +import java.util.List; + +public class SimpleGlexAlias extends GlexAlias{ + protected final int params; + + public SimpleGlexAlias(String name, String method, int params) { + super(name, method); + this.params = params; + } + + @Override + public Object exec(List arguments) throws TemplateModelException { + exactArguments(arguments, params); + return super.exec(arguments); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/SimpleTcAlias.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/SimpleTcAlias.java new file mode 100644 index 0000000000..a1b78d4c6e --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/SimpleTcAlias.java @@ -0,0 +1,21 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.generating.templateengine.freemarker.alias; + +import freemarker.template.TemplateModelException; + +import java.util.List; + +public class SimpleTcAlias extends TcAlias{ + protected final int params; + + public SimpleTcAlias(String name, String method, int params) { + super(name, method); + this.params = params; + } + + @Override + public Object exec(List arguments) throws TemplateModelException { + exactArguments(arguments, params); + return super.exec(arguments); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/TcAlias.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/TcAlias.java new file mode 100644 index 0000000000..ee5edb79eb --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/TcAlias.java @@ -0,0 +1,23 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.generating.templateengine.freemarker.alias; + +import freemarker.core.Environment; +import freemarker.ext.beans.BeanModel; +import freemarker.template.TemplateMethodModelEx; +import freemarker.template.TemplateModelException; + +public class TcAlias extends Alias{ + public TcAlias(String name, String method) { + super(name, method); + } + + @Override + public TemplateMethodModelEx getMethod() throws TemplateModelException { + BeanModel tc = (BeanModel) Environment.getCurrentEnvironment().getVariable("tc"); + TemplateMethodModelEx res = (TemplateMethodModelEx) ((BeanModel) tc.getAPI()).get(getMethodName()); + if(res == null){ + throw new TemplateModelException("Can not find method " + getMethodName() + " of TemplateController"); + } + return res; + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/TraceAlias.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/TraceAlias.java new file mode 100644 index 0000000000..aea28f60d2 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/TraceAlias.java @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.generating.templateengine.freemarker.alias; + +public class TraceAlias extends SimpleTcAlias{ + public TraceAlias() { + super("trace", "trace", 2); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/WarnAlias.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/WarnAlias.java new file mode 100644 index 0000000000..2785c30501 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/freemarker/alias/WarnAlias.java @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.generating.templateengine.freemarker.alias; + +public class WarnAlias extends SimpleTcAlias{ + public WarnAlias() { + super("warn", "warn", 1); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/Reporting.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/Reporting.java new file mode 100644 index 0000000000..f23b77fc33 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/Reporting.java @@ -0,0 +1,915 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting; + +import java.net.URL; +import java.nio.file.Path; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import de.monticore.ast.ASTNode; +import de.monticore.generating.templateengine.HookPoint; +import de.monticore.generating.templateengine.TemplateController; +import de.monticore.generating.templateengine.reporting.commons.ReportManager; +import de.monticore.generating.templateengine.reporting.commons.ReportManager.ReportManagerFactory; +import de.monticore.io.paths.MCPath; +import de.monticore.symboltable.IScope; +import de.se_rwth.commons.SourcePosition; +import de.se_rwth.commons.logging.Log; + +/** + * Facade for all reporting activities. Invoking a report method causes all + * AReporter implementing this method to execute it. + * + */ +public class Reporting extends Log { + + /** + * Map of model names to actual report managers. + */ + protected Map reportManagers = new HashMap<>(); + + /** + * Where output will be written to. + */ + protected String outputDirectory; + + /** + * Where reports will be written to. + */ + protected String reportDirectory; + + + /** + * For creating report managers on-demand for newly processed models. + */ + protected ReportManagerFactory factory; + + /** + * Constructor for de.monticore.generating.templateengine.reporting.Reporting + * + * @param outputDirectory for storing the reports + * @param factory for creating specific report manager configurations + */ + private Reporting(String outputDirectory, String reportDirectory, ReportManagerFactory factory) { + this.outputDirectory = outputDirectory; + this.reportDirectory = reportDirectory; + this.factory = factory; + } + + protected Map getReportManagers() { + return this.reportManagers; + } + + protected String getOutputDirectory() { + return this.outputDirectory; + } + + protected String getReportDirectory() { + return this.reportDirectory; + } + + protected ReportManagerFactory getFactory() { + return this.factory; + } + + private ReportManager getReportManager(String modelName) { + if (!this.getReportManagers().containsKey(modelName)) { + ReportManager repoMan = this.getFactory().provide(modelName); + this.getReportManagers().put(modelName, repoMan); + } + return this.getReportManagers().get(modelName); + } + + // ######################### + // some log overriding magic + + /** + * @see de.se_rwth.commons.logging.Log#warn(java.lang.String) + */ + @Override + public void doWarn(String msg) { + reportWarning(msg); + super.doWarn(msg); + } + + @Override + public void doWarn(String msg, SourcePosition position) { + this.doWarn(msg); + } + + @Override + public void doWarn(String msg, SourcePosition start, SourcePosition end) { + this.doWarn(msg); + } + + + /** + * @see de.se_rwth.commons.logging.Log#warn(java.lang.String, + * java.lang.Throwable) + */ + @Override + public void doWarn(String msg, Throwable t) { + reportWarning(msg); + super.doWarn(msg, t); + } + + /** + * @see de.se_rwth.commons.logging.Log#error(java.lang.String) + */ + @Override + public void doError(String msg) { + this.doError(msg, Optional.empty()); + } + + @Override + public void doError(String msg, SourcePosition position) { + this.doError(msg); + } + + @Override + public void doError(String msg, SourcePosition start, SourcePosition end) { + this.doError(msg); + } + + /** + * @see de.se_rwth.commons.logging.Log#error(java.lang.String, + * java.lang.Throwable) + */ + @Override + public void doError(String msg, Throwable t) { + this.doError(msg, Optional.ofNullable(t)); + } + + protected void doError(String msg, Optional t) { + // we need to know whether we wanted to fail immediately + boolean wantsToFailQuick = Log.isFailQuickEnabled(); + if (wantsToFailQuick) { + // if so, then temporary deactivate fail quick to allow proper logging + Log.enableFailQuick(false); + } + // report the error + reportError(msg); + // and log the error + if (t.isPresent()) { + super.doError(msg, t.get()); + } + else { + super.doError(msg); + } + + // now if we wanted to fail quick, we need to flush the reports without + // causing a crash, i.e., we need to catch exceptions + if (wantsToFailQuick) { + try { + flush(null); + } + catch (Exception e) { + // this is rather generic but it'll probably do for now + super.doError("0xA4055 Error during error reporting. Enable debug for more details."); + super.doDebug("Error during error reporting", e, ReportManager.class.getName()); + } + } + + // eventually, if we wanted to fail quick we do it now + if (wantsToFailQuick) { + Log.enableFailQuick(true); + } + } + + // end of the log overriding magic + // ######################### + + /* the singleton */ + private static Reporting singleton; + + /* whether reporting is enabled at the moment */ + protected static boolean enabled = false; + + /* the currently active model for which reporting takes place */ + protected static String currentModel; + + /** + * @return the single reporting instance + */ + private static Reporting get() { + return singleton; + } + + public static void init(String outputDirectory, String reportDirectory, ReportManagerFactory factory) { + if (outputDirectory == null || outputDirectory.isEmpty()) { + Log.error("0xA4050 Output directory must not be null or empty."); + } + if (reportDirectory == null || reportDirectory.isEmpty()) { + Log.error("0xA4052 ReportDirectory directory must not be null or empty."); + } + if (factory == null) { + Log.error("0xA4051 Report manager factory must not be null."); + } + singleton = new Reporting(outputDirectory, reportDirectory, factory); + Log.setLog(singleton); + } + + /** + * @return whether reporting was properly initialized + * @see Reporting#init(String, String, ReportManagerFactory) + */ + public static boolean isInitialized() { + return get() != null; + } + + /** + * @return whether reporting is currently enabled + */ + public static boolean isEnabled() { + if (isInitialized()) { + return enabled; + } + // if it's not initialized it's also not enabled + return false; + } + + /** + * Enable reporting for the given model name. + * + * @param modelName for which to enable reporting + * @return whether reporting is enabled or not (reporting will not be enabled + * if reporting was not previously initialized) + */ + public static boolean on(String modelName) { + if (modelName == null || modelName.isEmpty()) { + Log.error("0xA4109 Must specify valid model name for reporting."); + } + if (!isInitialized()) { + Log.warn("0xA4053 You must initialize reporting before enabling it."); + return false; + } + + currentModel = modelName; + enabled = true; + return enabled; + } + + /** + * Disable reporting entirely. + * + * @return the currently active model name for which reporting was active + */ + public static String off() { + if (isInitialized()) { + enabled = false; + if (currentModel != null) { + return currentModel; + } + } + return ""; + } + + /** + * @return the currently active/responsible report manager instance + */ + private static ReportManager getReportManager() { + // must only be used internally and with preceeding checks fo initialization + return get().getReportManager(currentModel); + } + + public static void reportTransformationStart(String transformationName) { + if (isEnabled()) { + getReportManager().reportTransformationStart(transformationName); + } + } + + public static void reportTransformationObjectMatch(String transformationName, ASTNode ast) { + if (isEnabled()) { + getReportManager().reportTransformationObjectMatch(transformationName, ast); + } + } + + public static void reportTransformationOldValue(String transformationName, ASTNode ast){ + if (isEnabled()) { + getReportManager().reportTransformationOldValue(transformationName, ast); + } + } + + public static void reportTransformationNewValue(String transformationName, ASTNode ast){ + if (isEnabled()) { + getReportManager().reportTransformationNewValue(transformationName, ast); + } + } + public static void reportTransformationOldValue(String transformationName, String value){ + if (isEnabled()) { + getReportManager().reportTransformationOldValue(transformationName, value); + } + } + + public static void reportTransformationNewValue(String transformationName, String value){ + if (isEnabled()) { + getReportManager().reportTransformationNewValue(transformationName, value); + } + } + + public static void reportTransformationOldValue(String transformationName, boolean value){ + if (isEnabled()) { + getReportManager().reportTransformationOldValue(transformationName, value); + } + } + + public static void reportTransformationNewValue(String transformationName, boolean value){ + if (isEnabled()) { + getReportManager().reportTransformationNewValue(transformationName, value); + } + } + + public static void reportTransformationObjectChange(String transformationName, ASTNode ast, String attributeName) { + if (isEnabled()) { + getReportManager().reportTransformationObjectChange(transformationName, ast, attributeName); + } + } + + public static void reportTransformationObjectCreation(String transformationName, ASTNode ast) { + if (isEnabled()) { + getReportManager().reportTransformationObjectCreation(transformationName, ast); + } + } + + public static void reportTransformationObjectDeletion(String transformationName, ASTNode ast) { + if (isEnabled()) { + getReportManager().reportTransformationObjectDeletion(transformationName, ast); + } + } + + public static void reportModelStart(ASTNode ast, String modelName, String fileName) { + if (isEnabled()) { + getReportManager().reportModelStart(ast, modelName, fileName); + } + } + + + + /** + * Reports the execution of templates + * + * @param templateName + * @param ast + */ + /* handwritten templates, and templates within Template Hookpoints and AST + * specific Template Hookpoints */ + public static void reportTemplateStart(String templateName, ASTNode ast) { + if (isEnabled()) { + getReportManager().reportTemplateStart(templateName, ast); + } + } + + /** + * Reports the execution of a standard template that is wrapped into a + * template hook point via the + * {@link de.monticore.generating.templateengine.GlobalExtensionManagement#getTemplateForwardings(String , ASTNode) + * getTemplateForwardings} method. The template is wrapped into the template + * hook point only if there is no other template forwarding. + * + * @param templateName + * @param ast + */ + public static void reportExecuteStandardTemplate(String templateName, ASTNode ast) { + if (isEnabled()) { + getReportManager().reportExecuteStandardTemplate(templateName, ast); + } + } + + /** + * Reports a template based file creation via the + * {@link de.monticore.generating.templateengine.TemplateController#writeArgs(String, String, String, ASTNode, List) + * writeArgs} method of the + * {@link de.monticore.generating.templateengine.TemplateController + * TemplateController}. + * + * @param templateName + * @param path + * @param ast + */ + public static void reportFileCreation(String templateName, Path path, ASTNode ast) { + if (isEnabled()) { + getReportManager().reportFileCreation(templateName, path, ast); + } + } + + /** + * Reports a file creation + * + * @param parentPath + * @param file + */ + public static void reportFileCreation(Path parentPath, Path file) { + if (isEnabled()) { + getReportManager().reportFileCreation(parentPath, file); + } + } + + /** + * Reports a file creation + * + * @param fileName + */ + public static void reportFileCreation(String fileName) { + if (isEnabled()) { + getReportManager().reportFileCreation(fileName); + } + } + + /** + * Reports the end of a file creation (file finalization). + * + * @param templateName + * @param path + * @param ast + */ + public static void reportFileFinalization(String templateName, Path path, ASTNode ast) { + if (isEnabled()) { + getReportManager().reportFileFinalization(templateName, path, ast); + } + } + + /** + * Reports a template based file creation. + * + * @param templateName + * @param qualifiedFilename + * @param fileExtension + * @param ast + */ + public static void reportFileCreation(String templateName, String qualifiedFilename, + String fileExtension, ASTNode ast) { + if (isEnabled()) { + getReportManager().reportFileCreation(templateName, qualifiedFilename, fileExtension, ast); + } + } + + /** + * Reports the end of a file creation (file finalization). + * + * @param templateName + * @param qualifiedFilename + * @param fileExtension + * @param ast + */ + public static void reportFileFinalization(String templateName, String qualifiedFilename, + String fileExtension, ASTNode ast) { + if (isEnabled()) { + getReportManager() + .reportFileFinalization(templateName, qualifiedFilename, fileExtension, ast); + } + } + + /** + * Reports a checking of file existence + * + * @param parentPath + * @param file + */ + public static void reportFileExistenceChecking(List parentPath, Path file) { + if (isEnabled()) { + getReportManager() + .reportFileExistenceChecking(parentPath, file); + } + } + + /** + * Reports the end of a template execution. + * + * @param templateName + * @param ast + */ + public static void reportTemplateEnd(String templateName, ASTNode ast) { + if (isEnabled()) { + getReportManager().reportTemplateEnd(templateName, ast); + } + } + + public static void reportModelEnd(String modelName, String fileName) { + if (isEnabled()) { + getReportManager().reportModelEnd(modelName, fileName); + } + } + + public static void reportModelLoad(String qualifiedName) { + if (isEnabled()) { + getReportManager().reportModelLoad(qualifiedName); + } + } + + public static void reportSetValue(String name, Object value) { + if (isEnabled()) { + getReportManager().reportSetValue(name, value); + } + } + + public static void reportInstantiate(String className, List params) { + if (isEnabled()) { + getReportManager().reportInstantiate(className, params); + } + } + + public static void reportMethodCall(String className, String methodName, List params) { + if (isEnabled()) { + getReportManager().reportMethodCall(className, methodName, params); + } + } + + /** + * Reports the template inclusion via the + * logTemplateCallOrInclude} method called by the + * {@link de.monticore.generating.templateengine.TemplateController#processTemplate(String, ASTNode, List) + * processTemplate} method of the + * {@link de.monticore.generating.templateengine.TemplateController + * TemplateController} after calculating all forwardings. + * + * @param templateName + * @param ast + */ + public static void reportTemplateInclude(String templateName, ASTNode ast) { + if (isEnabled()) { + getReportManager().reportTemplateInclude(templateName, ast); + } + } + + /** + * Reports the template write via the + + * logTemplateCallOrInclude} method called by the + * {@link de.monticore.generating.templateengine.TemplateController#processTemplate(String, ASTNode, List) + * processTemplate} method of the + * {@link de.monticore.generating.templateengine.TemplateController + * TemplateController}. TemplateWrite does not calculate forwardings, it + * processes the template instantly. + * + * @param templateName + * @param ast + */ + public static void reportTemplateWrite(String templateName, ASTNode ast) { + if (isEnabled()) { + getReportManager().reportTemplateWrite(templateName, ast); + } + } + + /** + * Reports the registration of a hook point via the + * {@link de.monticore.generating.templateengine.GlobalExtensionManagement#defineHookPoint(TemplateController, String)} (String, HookPoint) + * setHookPoint} method of the + * {@link de.monticore.generating.templateengine.GlobalExtensionManagement + * GlobalExtensionManagement}. + * + * @param hookName + * @param hp + */ + public static void reportSetHookPoint(String hookName, HookPoint hp) { + if (isEnabled()) { + getReportManager().reportSetHookPoint(hookName, hp); + } + } + + /** + * Reports the execution of a hook point via the + * callHookPoint} method. This does not include the execution of hook points + * registered by the setBefore, setAfter or replaceTemplate Methods, nor the + * execution of AST specific hook points. + * + * @param hookName + * @param hp + * @param ast + */ + public static void reportCallHookPointStart(String hookName, HookPoint hp, ASTNode ast) { + if (isEnabled()) { + getReportManager().reportCallHookPointStart(hookName, hp, ast); + } + } + + /** + * Reports the end of the execution of a hook point via the + * {@link de.monticore.generating.templateengine.GlobalExtensionManagement#bindHookPoint(String, HookPoint)} (TemplateController, String, ASTNode) + * callHookPoint} method. + * + * @param hookName + */ + public static void reportCallHookPointEnd(String hookName) { + if (isEnabled()) { + getReportManager().reportCallHookPointEnd(hookName); + } + } + + /** + * Reports the execution of hook points via the + * {@link de.monticore.generating.templateengine.TemplateController#include(List, List) + * include} or + * {@link de.monticore.generating.templateengine.TemplateController#includeArgs(String, List) + * includeArgs} method of the + * {@link de.monticore.generating.templateengine.TemplateController + * TemplateController}. This includes the execution of all hook points + * registered by setAfter. This method is called in the + * {@link de.monticore.generating.templateengine.GlobalExtensionManagement#getTemplateForwardings(String , ASTNode) + * getTemplateForwardings} method triggered by the + * {@link de.monticore.generating.templateengine.TemplateController#processTemplate(String, ASTNode, List) + * processTemplate} method. + * + * @param oldTemplate + * @param afterHps + * @param ast + */ + public static void reportCallAfterHookPoint(String oldTemplate, Collection afterHps, + ASTNode ast) { + if (isEnabled()) { + getReportManager().reportCallAfterHookPoint(oldTemplate, afterHps, ast); + } + } + + /** + * Reports the execution of hook points via the + * {@link de.monticore.generating.templateengine.TemplateController#include(List, List) + * include} or + * {@link de.monticore.generating.templateengine.TemplateController#includeArgs(String, List) + * includeArgs} method of the + * {@link de.monticore.generating.templateengine.TemplateController + * TemplateController}. This includes the execution of all hook points + * registered by setBefore. This method is called in the + * {@link de.monticore.generating.templateengine.GlobalExtensionManagement#getTemplateForwardings(String , ASTNode) + * getTemplateForwardings} method triggered by the + * {@link de.monticore.generating.templateengine.TemplateController#processTemplate(String, ASTNode, List) + * processTemplate} method. + * + * @param oldTemplate + * @param beforeHps + * @param ast + */ + public static void reportCallBeforeHookPoint(String oldTemplate, Collection beforeHps, + ASTNode ast) { + if (isEnabled()) { + getReportManager().reportCallBeforeHookPoint(oldTemplate, beforeHps, ast); + } + } + + /** + * Reports the execution of hook points via the + * {@link de.monticore.generating.templateengine.TemplateController#include(List, List) + * include} or + * {@link de.monticore.generating.templateengine.TemplateController#includeArgs(String, List) + * includeArgs} method of the + * {@link de.monticore.generating.templateengine.TemplateController + * TemplateController}. This includes the execution of all hook points + * registered by setReplace. These hook points replace a template. This method + * is called in the + * {@link de.monticore.generating.templateengine.GlobalExtensionManagement#getTemplateForwardingsX(String , ASTNode) + * getTemplateForwardingsX} method triggered by the + * {@link de.monticore.generating.templateengine.TemplateController#processTemplate(String, ASTNode, List) + * processTemplate} method. + * + * @param oldTemplate + * @param hps + * @param ast + */ + public static void reportCallReplacementHookPoint(String oldTemplate, List hps, + ASTNode ast) { + if (isEnabled()) { + getReportManager().reportCallReplacementHookPoint(oldTemplate, hps, ast); + } + } + + /** + * Reports the execution of hook points via the + * {@link de.monticore.generating.templateengine.TemplateController#include(List, List) + * include} or + * {@link de.monticore.generating.templateengine.TemplateController#includeArgs(String, List) + * includeArgs} method of the + * {@link de.monticore.generating.templateengine.TemplateController + * TemplateController}. This includes the execution of all hook points + * registered by setASTSpecificReplacement. This method is called in the + * {@link de.monticore.generating.templateengine.GlobalExtensionManagement#getTemplateForwardings(String , ASTNode) + * getTemplateForwardings} method triggered by the + * {@link de.monticore.generating.templateengine.TemplateController#processTemplate(String, ASTNode, List) + * processTemplate} method. + * + * @param oldTemplate + * @param hps + * @param ast + */ + public static void reportCallSpecificReplacementHookPoint(String oldTemplate, + List hps, ASTNode ast) { + if (isEnabled()) { + getReportManager().reportCallSpecificReplacementHookPoint(oldTemplate, hps, ast); + } + } + + /** + * Reports the replacement of a template by an AST hook point via the + * {@link de.monticore.generating.templateengine.GlobalExtensionManagement#replaceTemplate(String , ASTNode , HookPoint ) + * replaceTemplate} method of the + * {@link de.monticore.generating.templateengine.GlobalExtensionManagement + * GlobalExtensionManagement}. This does not include any other assignment or + * replacement. + * + * @param oldTemplate + * @param node + * @param newHp + */ + public static void reportASTSpecificTemplateReplacement(String oldTemplate, ASTNode node, + HookPoint newHp) { + if (isEnabled()) { + getReportManager().reportASTSpecificTemplateReplacement(oldTemplate, node, newHp); + } + } + + /** + * Reports the replacement of a template by hook points via the + * {@link de.monticore.generating.templateengine.GlobalExtensionManagement#replaceTemplate(String , List ) + * replaceTemplate} method of the + * {@link de.monticore.generating.templateengine.GlobalExtensionManagement + * GlobalExtensionManagement}. This does not include any other assignment or + * replacement. + * + * @param oldTemplate + * @param newHps + */ + public static void reportTemplateReplacement(String oldTemplate, List newHps) { + if (isEnabled()) { + getReportManager().reportTemplateReplacement(oldTemplate, newHps); + } + } + + /** + * Reports the assignment of hook points to a template via + * {@link de.monticore.generating.templateengine.GlobalExtensionManagement#setBeforeTemplate(String , List )} + * . This does not include any other assignment or replacement. + * + * @param template + * @param beforeHps + */ + public static void reportSetBeforeTemplate(String template, List beforeHps) { + if (isEnabled()) { + getReportManager().reportSetBeforeTemplate(template, beforeHps); + } + } + + /** + * Reports the addition of hook points to a template via + * {@link de.monticore.generating.templateengine.GlobalExtensionManagement#addAfterTemplate(String , List )} + * . This does not include any other assignment or replacement. + * + * @param template + * @param afterHps + */ + public static void reportSetAfterTemplate(String template, List afterHps) { + if (isEnabled()) { + getReportManager().reportSetAfterTemplate(template, afterHps); + } + } + + public static void reportUseHandwrittenCodeFile(Path parentDir, Path fileName) { + if (isEnabled()) { + getReportManager().reportUseHandwrittenCodeFile(parentDir, fileName); + } + } + + /** + * Reports the assignment of hook points to a template via + * {@link de.monticore.generating.templateengine.GlobalExtensionManagement#setAfterTemplate(String , List )} + * . This does not include any other assignment or replacement. + * + * @param template + * @param afterHps + */ + public static void reportAddAfterTemplate(String template, List afterHps) { + if (isEnabled()) { + getReportManager().reportAddAfterTemplate(template, afterHps); + } + } + + + /** + * Reports the check for existence of an artifact + * + * @param mcp + * @param fileName + * @param resolvedPath contains the result if artifact exists + */ + public static void reportHWCExistenceCheck(MCPath mcp, Path fileName, Optional resolvedPath) { + if (isEnabled()) { + getReportManager().reportHWCExistenceCheck(mcp, fileName, resolvedPath); + } + } + + public static void reportUserSpecificTemplate(Path parentDir, Path fileName) { + if (isEnabled()) { + getReportManager().reportUserSpecificTemplate(parentDir, fileName); + } + } + + public static void reportAddValue(String name, Object value, int size) { + if (isEnabled()) { + getReportManager().reportAddValue(name, value, size); + } + } + + /** + * Invoking this method causes a report of value to DetailedReporter. + * + * @param value that will be reported in DetailedReporter + */ + public static void reportToDetailed(String value) { + if (isEnabled()) { + getReportManager().reportDetailed(value); + } + } + + /** + * This method is called when an input file is opened which is obtained via + * model resolution. Such files typically are dependency models (e.g., super + * grammars, super CDs, ...). + * + * @param parentPath + * @param file + */ + public static void reportOpenInputFile(Optional parentPath, Path file) { + if (isEnabled()) { + getReportManager().reportOpenInputFile(parentPath, file); + } + } + + /** + * This method is called when an input file is opened which is obtained via + * model resolution. Such files typically are dependency models (e.g., super + * grammars, super CDs, ...). + * + * @param fileName + */ + public static void reportOpenInputFile(String fileName) { + if (isEnabled()) { + getReportManager().reportOpenInputFile(fileName); + } + } + + /** + * This method is called when an input file is parsed; i.e., this report hook + * point is designed for the main input artifacts only. E.g., files that are + * loaded on demand during further processing should not report using this + * method but {@link #reportOpenInputFile(Optional, Path)} instead. + * + * @param inputFilePath + * @param modelName + */ + public static void reportParseInputFile(Path inputFilePath, String modelName) { + if (isEnabled()) { + getReportManager().reportParseInputFile(inputFilePath, modelName); + } + } + + /** + * This method is called to report the content of the symbol table. The method + * should only be called once during the execution of the generator. + * + * @param scope + */ + public static void reportSymbolTableScope(IScope scope) { + if (isEnabled()) { + getReportManager().reportSymbolTableScope(scope); + } + } + + /** + * Invoking this method causes all AReporter to close their files and all + * OneTimeReporter to write their content into files. + * + * @param ast the root node of the reported ast, may be null on error + */ + public static void flush(ASTNode ast) { + if (isEnabled()) { + getReportManager().flush(ast); + } + } + + public static void reportWarning(String message) { + if (isEnabled()) { + getReportManager().reportWarning(message); + } + } + + public static void reportError(String msg) { + if (isEnabled()) { + getReportManager().reportError(msg); + } + } + + public static String getOutputDir() { + if (isInitialized()) { + return get().getOutputDirectory(); + } + return ""; + } + + public static String getReportDir() { + if (isInitialized()) { + return get().getReportDirectory(); + } + return ""; + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/artifacts/ArtifactReporter.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/artifacts/ArtifactReporter.java new file mode 100644 index 0000000000..19d629660a --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/artifacts/ArtifactReporter.java @@ -0,0 +1,219 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.artifacts; + +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +import de.monticore.ast.ASTNode; +import de.monticore.generating.templateengine.reporting.artifacts.formatter.AFormatter; +import de.monticore.generating.templateengine.reporting.artifacts.model.Element; +import de.monticore.generating.templateengine.reporting.artifacts.model.ElementFactory; +import de.monticore.generating.templateengine.reporting.artifacts.model.ElementType; +import de.monticore.generating.templateengine.reporting.artifacts.model.RootPkg; +import de.monticore.generating.templateengine.reporting.commons.AReporter; + +/** + * Creates a dependency Graph based on a generator run. Dependency Graph can be printed via various + * printers. + * + */ +public class ArtifactReporter extends AReporter { + + ElementFactory factory = new ElementFactory(); + + Stack elementStack = new Stack(); + + /** + * Base of the generated dependency graph + */ + protected RootPkg rootPkg = new RootPkg(); + + // Nam of the dotgraph file + protected AFormatter formatter; + + // Filters to use + protected List filters = new ArrayList(); + + final static String SIMPLE_FILE_NAME = "Artifacts"; + + public ArtifactReporter(String path, String qualifiedFileName, String fileextension, AFormatter formatter, ElementType... filters) { + super(path, qualifiedFileName, fileextension); + this.formatter = formatter; + this.addFilters(filters); + } + + /** + * Allow elements of type filter to be displayed. First invocation disables all other types + */ + public void addFilter(ElementType filter) { + this.filters.add(filter); + } + + /** + * Add multiple filters at once + * + * @see addFilter + */ + public void addFilters(ElementType... filters) { + for (ElementType filter : filters) { + this.addFilter(filter); + } + } + + /* + * (non-Javadoc) + * @see mc.codegen.logging.GenLoggerDefaultClient#logModelStart(java.lang.String, + * java.lang.String) + */ + @Override + public void reportModelStart(ASTNode ast, String modelName, String fileName) { + if (this.filters.isEmpty() || this.filters.contains(ElementType.MODEL)) { + String extension = ReportingNameHelper.getSimpleName(fileName); + Element e = rootPkg.resolve(ReportingNameHelper.getPath(modelName), + ReportingNameHelper.getSimpleName(modelName), extension); + if (e == null) { + e = factory.createModel(rootPkg, modelName, extension); + } + count(e); + elementStack.push(e); + } + } + + /* + * (non-Javadoc) + * @see mc.codegen.logging.GenLoggerDefaultClient#logTemplateStart(java.lang.String , + * de.monticore.ast.ASTNode) + */ + @Override + public void reportTemplateEnd(String templatename, ASTNode ast) { + if (this.filters.isEmpty() || this.filters.contains(ElementType.TEMPLATE)) { + elementStack.pop(); + } + } + + /* + * (non-Javadoc) + * @see mc.codegen.logging.GenLoggerDefaultClient#logTemplateStart(java.lang.String , + * de.monticore.ast.ASTNode) + */ + @Override + public void reportTemplateStart(String templatename, ASTNode ast) { + if (this.filters.isEmpty() || this.filters.contains(ElementType.TEMPLATE)) { + Element e = handleTemplate(templatename); + count(e); + elementStack.push(e); + } + } + + /* + * (non-Javadoc) + * @see mc.codegen.logging.GenLoggerDefaultClient#logFileCreation(java.lang.String, + * java.lang.String, java.lang.String, de.monticore.ast.ASTNode) + */ + @Override + public void reportFileCreation(String templatename, String qualifiedfilename, String fileextension, + ASTNode ast) { + if (this.filters.isEmpty() || this.filters.contains(ElementType.FILE)) { + /* this method is called when the parent template of the template with name templatename is on + * top of the stack. Therefore the template with name templatename has to be handled first + */ + Element template = handleTemplate(templatename); + elementStack.push(template); + Element e = rootPkg.resolve(ReportingNameHelper.getPath(qualifiedfilename), + ReportingNameHelper.getSimpleName(qualifiedfilename), fileextension); + if (e == null) { + e = factory.createFile(rootPkg, qualifiedfilename, fileextension); + } + createElementLink(e); + count(e); + elementStack.pop(); + } + } + + /** + * @param templatename + * @return + */ + protected Element handleTemplate(String templatename) { + String extension = "ftl"; + Element e = rootPkg.resolve(ReportingNameHelper.getPath(templatename), + ReportingNameHelper.getSimpleName(templatename), extension); + if (e == null) { + e = factory.createTemplate(rootPkg, templatename, extension); + } + createElementLink(e); + return e; + } + + /** + * @see mc.codegen.logging.GenLoggerDefaultClient#logInstantiateStart(java.lang.String, + * java.util.List) + */ + @Override + public void reportInstantiate(String className, List params) { + if (this.filters.isEmpty() || this.filters.contains(ElementType.HELPER)) { + String extension = "java"; + Element e = rootPkg.resolve(ReportingNameHelper.getPath(className), + ReportingNameHelper.getSimpleName(className), extension); + if (e == null) { + e = factory.createHelper(rootPkg, className, extension); + } + createElementLink(e); + count(e); + } + } + + /* + * (non-Javadoc) + * @see mc.codegen.logging.GenLoggerDefaultClient#finish() + */ + @Override + public void flush(ASTNode node) { + writeContent(); + resetVariables(); + super.flush(node); + } + + protected void resetVariables() { + rootPkg = new RootPkg(); + elementStack.clear(); + } + + protected void writeContent() { + List lines = formatter.getContent(rootPkg); + for (String l : lines) { + writeLine(l); + } + } + + /** + * Creates a link from the top element of the stack to the given element. This only works if the + * stack is not empty. + * + * @param element + */ + protected void createElementLink(Element element) { + if (!elementStack.isEmpty()) { + elementStack.peek().addlink(element); + } + } + + public void count(Element element) { + element.incCalls(); + if (!elementStack.isEmpty()) { + Element source = elementStack.peek(); + source.incLinkCalls(element); + } + } + + /** + * @see mc.codegen.reporting.commons.AReporter#writeHeader() + */ + @Override + protected void writeHeader() { + // Write empty header + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/artifacts/ReportingNameHelper.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/artifacts/ReportingNameHelper.java new file mode 100644 index 0000000000..cd2bdb1b35 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/artifacts/ReportingNameHelper.java @@ -0,0 +1,117 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.artifacts; + +import java.nio.file.FileSystems; +import java.nio.file.Path; + +public class ReportingNameHelper { + + /** + * Constructor for de.monticore.generating.templateengine.reporting.artifacts.ReportingNameHelper. + */ + private ReportingNameHelper() { + } + + public static Path getPath(String outputDir, String qualifiedFilename, String fileextension) { + String[] pathParts = qualifiedFilename.split("\\."); + pathParts[pathParts.length-1] = pathParts[pathParts.length-1] + "." + fileextension; + return FileSystems.getDefault().getPath(outputDir, pathParts); + } + + /** + * @param qn QualifiedName (without fileExtesion) or FullQualifiedName (with fileExtesion) + * @return + */ + public static String getPath(String qn) { + String path = ""; + if (qn.contains(".")) { + path = qn.substring(0, qn.lastIndexOf(".")); + } + return path; + } + + /** + * @param qn QualifiedName (without fileExtension) + * @return + */ + public static String getSimpleName(String qn) { + String simpleName = qn; + if (qn.contains(".")) { + simpleName = qn.substring(qn.lastIndexOf(".") + 1, qn.length()); + } + return simpleName; + } + + /** + * @param simpleName + * @param extension fileExtension + * @return + */ + public static String getFullName(String simpleName, String extension) { + return extension.isEmpty() ? simpleName : simpleName + "." + extension; + } + + /** + * @param qn any qualifiedName + * @return first part of the qualifiedName + */ + public static String getFirstPathPart(String qn) { + if (!qn.contains(".")) { + return qn; + } + + return qn.substring(0, qn.indexOf(".")); + } + + /** + * @param qn any qualifiedName + * @return qn without the first part + */ + public static String removeFirstPathPart(String qn) { + if (!qn.contains(".")) { + return ""; + } + + return qn.substring(qn.indexOf(".") + 1); + } + + /** Returns a dot separated name of the file represented by the given path without its fileextension + * Example: outputdir is /a/b and path represents /a/b/c/d/e.txt + * returns "c.d.e" + * + * @param outputDir + * @param path + * @return + */ + public static String getQualifiedName(String outputDir, Path path) { + StringBuilder qualifiedName = new StringBuilder(path.getName(0).toString()); + + for (int i = 1; i < path.getNameCount() - 1; i++) { + qualifiedName.append("."); + qualifiedName.append(path.getName(i)); + } + if (path.getFileName() != null) { + String[] seperatedFileName = path.getFileName().toString().split("\\."); + qualifiedName.append("." + seperatedFileName[0]); + } + return qualifiedName.toString(); + } + + /** Returns the fileextension of the file represented by the given path + * Example: outputdir is /a/b and path represents /a/b/c/d/e.txt.tmp + * returns txt.tmp + * + * @param path + * @return + */ + public static String getFileextension(Path path) { + String fileextension = null; + String fullFileName = (path.getFileName()==null)?"":path.getFileName().toString(); + if (fullFileName.contains(".")) { + fileextension = fullFileName.substring(fullFileName.indexOf(".") + 1); + } + return fileextension; + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/artifacts/formatter/AFormatter.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/artifacts/formatter/AFormatter.java new file mode 100644 index 0000000000..ba35f0f6ff --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/artifacts/formatter/AFormatter.java @@ -0,0 +1,27 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.artifacts.formatter; + +import java.util.List; + +import de.monticore.generating.templateengine.reporting.artifacts.model.RootPkg; + +public abstract class AFormatter { + + protected String indentString = ""; + + protected void indent() { + indentString += " "; + } + + protected void unindent() { + indentString = indentString.substring(0, indentString.length() - 2); + } + + protected void addLine(List lines, String line) { + lines.add(indentString + line); + } + + public abstract List getContent(RootPkg rootPkg); + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/artifacts/formatter/GMLFormatter.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/artifacts/formatter/GMLFormatter.java new file mode 100644 index 0000000000..7a5e5a96f4 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/artifacts/formatter/GMLFormatter.java @@ -0,0 +1,261 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.artifacts.formatter; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.google.common.collect.Lists; + +import de.monticore.generating.templateengine.reporting.artifacts.model.APkg; +import de.monticore.generating.templateengine.reporting.artifacts.model.Element; +import de.monticore.generating.templateengine.reporting.artifacts.model.ElementType; +import de.monticore.generating.templateengine.reporting.artifacts.model.Pkg; +import de.monticore.generating.templateengine.reporting.artifacts.model.RootPkg; + +public class GMLFormatter extends AFormatter { + + protected Map elementIds = new HashMap(); + + protected Map packageIds = new HashMap(); + + protected int nodeIdCounter = 0; + + protected int edgeIdCounter = 0; + + protected long maxEdgeCalls = 0; + + protected double edgeSizeRange = 0; + + /** + * @see AFormatter.printer.APrinter#print(visualization.model.RootPkg) + */ + @Override + public List getContent(RootPkg rootPkg) { + List lines = Lists.newArrayList(); + calculateMaxEdgeCalls(rootPkg); + edgeSizeRange = maxEdgeCalls / 7; + + addLine(lines, "graph ["); + indent(); + addLine(lines, "directed 1"); + lines.addAll(getAllPkgContent(rootPkg)); + lines.addAll(getAllLinkContent(rootPkg)); + unindent(); + addLine(lines, "]"); + return lines; + } + + protected void calculateMaxEdgeCalls(APkg pkg) { + for (Element e : pkg.getElements()) { + for (Element link : e.getLinks()) { + Long linkCalls = e.getNumberOfLinkCalls(link); + if (linkCalls > maxEdgeCalls) { + maxEdgeCalls = linkCalls; + } + } + } + + for (Pkg p : pkg.getSubPkgs()) { + calculateMaxEdgeCalls(p); + } + } + + /** + * @param pkg + */ + protected List getAllLinkContent(APkg pkg) { + List lines = Lists.newArrayList(); + + for (Element e : pkg.getElements()) { + lines.addAll(getLinkContent(e)); + } + + for (Pkg p : pkg.getSubPkgs()) { + lines.addAll(getAllLinkContent(p)); + } + + return lines; + } + + protected List getAllPkgContent(APkg pkg) { + List lines = Lists.newArrayList(); + + if (pkg.hasElements()) { + lines.addAll(getPkgContent(pkg)); + } + + for (Pkg subPkg : pkg.getSubPkgs()) { + lines.addAll(getAllPkgContent(subPkg)); + } + + for (Element e : pkg.getElements()) { + lines.addAll(getElementContent(e)); + } + + return lines; + } + + protected List getPkgContent(APkg pkg) { + List lines = Lists.newArrayList(); + + if (!pkg.containsNonFileElement()) { + return lines; + } + addLine(lines, "node ["); + indent(); + addLine(lines, "id " + getGroupIDByPackage(pkg)); + addLine(lines, "label " + "\"" + pkg.getQualifiedName() + "\""); + addLine(lines, "graphics ["); + indent(); + addLine(lines, "type \"roundrectangle\""); + addLine(lines, "fill \"#F5F5F5\""); + addLine(lines, "outline \"#000000\""); + addLine(lines, "outlineStyle \"dashed\""); + addLine(lines, "topBorderInset 0.0"); + addLine(lines, "bottomBorderInset 0.0"); + addLine(lines, "leftBorderInset 0.0"); + addLine(lines, "rightBorderInset 0.0"); + unindent(); + addLine(lines, "]"); + addLine(lines, "LabelGraphics ["); + indent(); + addLine(lines, "fill \"#EBEBEB\""); + addLine(lines, "fontSize 15"); + addLine(lines, "fontName \"Dialog\""); + addLine(lines, "alignment \"right\""); + addLine(lines, "autoSizePolicy \"node_width\""); + addLine(lines, "anchor \"t\""); + addLine(lines, "borderDistance 0.0"); + unindent(); + addLine(lines, "]"); + addLine(lines, "isGroup 1"); + APkg ancestor = pkg.resolveAncestorWithElements(); + if (ancestor != null) { + addLine(lines, "gid " + getGroupIDByPackage(ancestor)); + } + unindent(); + addLine(lines, "]"); + + return lines; + } + + /** + * Print the dot graph representation of this element + * + * @param o Open file to write to + * @param space Space for indentation + */ + public List getElementContent(Element element) { + List lines = Lists.newArrayList(); + + if (element.getType() == ElementType.FILE) { + return lines; + } + + addLine(lines, "node ["); + indent(); + addLine(lines, "id " + getID(element)); + addLine(lines, "label " + "\"" + element.getFullName() + " (" + element.getNumberOfCalls() + + ")\""); + addLine(lines, "graphics ["); + indent(); + if (element.hasLinkToFile()) { + addLine(lines, "fill \"#00FF00\""); + } + addLine(lines, "type " + "\"" + getShape(element.getType()) + "\""); + unindent(); + addLine(lines, "]"); + addLine(lines, "gid " + getGroupIDByPackage(element.getPkg())); + unindent(); + addLine(lines, "]"); + + return lines; + } + + /** + * + * @param name + * @return + */ + protected String getShape(ElementType type) { + switch (type) { + case HELPER: + return "diamond"; + case MODEL: + return "hexagon"; + case TEMPLATE: + return "ellipse"; + default: + return "octagon"; + } + } + + /** + * @param pkg + * @return + */ + protected Integer getGroupIDByPackage(APkg pkg) { + if (packageIds.containsKey(pkg)) { + return packageIds.get(pkg); + } + + nodeIdCounter++; + packageIds.put(pkg, nodeIdCounter); + return nodeIdCounter; + } + + /** + * @param element + * @return + */ + protected Integer getID(Element element) { + if (elementIds.containsKey(element)) { + return elementIds.get(element); + } + + nodeIdCounter++; + elementIds.put(element, nodeIdCounter); + return nodeIdCounter; + } + + /** + * write all links to supplied open file + */ + public List getLinkContent(Element element) { + List lines = Lists.newArrayList(); + + Integer elementId = elementIds.get(element); + for (Element link : element.getLinks()) { + if (link.getType() == ElementType.FILE) { + continue; + } + long calls = element.getNumberOfLinkCalls(link); + edgeIdCounter++; + addLine(lines, "edge ["); + indent(); + addLine(lines, "id " + edgeIdCounter); + addLine(lines, "source " + elementId); + addLine(lines, "target " + elementIds.get(link)); + addLine(lines, "label " + "\"(" + calls + ")\""); + addLine(lines, "graphics ["); + indent(); + addLine(lines, "targetArrow \"standard\""); + addLine(lines, "width " + getEdgeWith(calls)); + unindent(); + addLine(lines, "]"); + unindent(); + addLine(lines, "]"); + } + return lines; + } + + /** + * @param calls + * @return + */ + protected int getEdgeWith(long calls) { + return Math.min((int) (calls/edgeSizeRange) + 1, 7); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/artifacts/formatter/GVFormatter.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/artifacts/formatter/GVFormatter.java new file mode 100644 index 0000000000..29e70ddb89 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/artifacts/formatter/GVFormatter.java @@ -0,0 +1,121 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.artifacts.formatter; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.google.common.collect.Lists; + +import de.monticore.generating.templateengine.reporting.artifacts.model.APkg; +import de.monticore.generating.templateengine.reporting.artifacts.model.Element; +import de.monticore.generating.templateengine.reporting.artifacts.model.ElementType; +import de.monticore.generating.templateengine.reporting.artifacts.model.Pkg; +import de.monticore.generating.templateengine.reporting.artifacts.model.RootPkg; + +public class GVFormatter extends AFormatter { + + protected Map shapes = new HashMap(); + + public GVFormatter() { + this.shapes.put(ElementType.HELPER, "cds"); + this.shapes.put(ElementType.MODEL, "box3d"); + this.shapes.put(ElementType.TEMPLATE, "ellipse"); + this.shapes.put(ElementType.FILE, "note"); + } + + protected List getAllLinkContent(APkg pkg) { + List lines = Lists.newArrayList(); + + for (Element e : pkg.getElements()) { + lines.addAll(getLinkContent(e)); + } + + for (Pkg p : pkg.getSubPkgs()) { + lines.addAll(getAllLinkContent(p)); + } + + return lines; + } + + protected List getContent(Element element) { + List lines = Lists.newArrayList(); + addLine(lines, "node[shape=" + getShape(element.getType()) + "];"); + addLine(lines, getUniqueName(element) + " [label=\"" + element.getFullQualifiedName() + " (" + + element.getNumberOfCalls() + ")\"];"); + return lines; + } + + protected List getLinkContent(Element element) { + List lines = Lists.newArrayList(); + + for (Element link : element.getLinks()) { + addLine(lines, getUniqueName(element) + " -> " + getUniqueName(link) + ";"); + } + return lines; + } + + protected String getUniqueName(Element e) { + return e.getType().getName() + "_" + + e.getQualifiedName().replaceAll("[.]", "_").replaceAll("-", "_"); + } + + /** + * Print package, subpackages and elements to open output file + */ + public List getElementContent(APkg pkg) { + List lines = Lists.newArrayList(); + + if (pkg.hasElements()) { + addLine(lines, "subgraph cluster_" + pkg.getQualifiedName().replace(".", "_") + " {"); + indent(); + addLine(lines, "label = \"" + pkg.getQualifiedName() + "\";"); + addLine(lines, "labeljust = l;"); + } + + // Recurse + for (Pkg subPkg : pkg.getSubPkgs()) { + lines.addAll(getElementContent(subPkg)); + } + + for (Element e : pkg.getElements()) { + lines.addAll(getContent(e)); + } + + if (pkg.hasElements()) { + unindent(); + addLine(lines, "}"); + } + return lines; + } + + + /** + * Get the dotgraph shape of a type + */ + public String getShape(ElementType type) { + if (this.shapes.containsKey(type)) { + return this.shapes.get(type); + } + else { + return "hexagon"; + } + } + + /** + * @see mc.codegen.reporting.visualization.printer.AFormatter#getContent(mc.codegen.reporting.visualization.model.RootPkg) + */ + @Override + public List getContent(RootPkg rootPkg) { + List lines = Lists.newArrayList(); + lines.add("digraph {"); + indent(); + lines.addAll(getElementContent(rootPkg)); + lines.addAll(getAllLinkContent(rootPkg)); + unindent(); + lines.add("}"); + return lines; + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/artifacts/model/APkg.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/artifacts/model/APkg.java new file mode 100644 index 0000000000..5bfb12f03b --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/artifacts/model/APkg.java @@ -0,0 +1,127 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.artifacts.model; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import de.monticore.generating.templateengine.reporting.artifacts.ReportingNameHelper; + +public abstract class APkg { + + /** + * Maps name of subpackage to subpackage + */ + protected Map subPkgs = new HashMap(); + + /** + * Maps fullName of Element ({@link Element#getFullName()}) to Element + */ + protected Map elements = new HashMap(); + + protected boolean containsNonFileElement = false; + + /** + * Add the provided element to this package. It can only exist one element per fullName ({@link Element#getFullName()}) + * @param e + */ + public void addElement(Element e) { + elements.put(e.getFullName(), e); + if (e.getType() != ElementType.FILE) { + containsNonFileElement = true; + } + } + + public Collection getElements() { + return this.elements.values(); + } + + /** + * @return The QualifiedName (Name without FileExtension) + */ + public abstract String getQualifiedName(); + + public void addSubPkg(Pkg pkg) { + subPkgs.put(pkg.getName(), pkg); + } + + /** + * @return + */ + public List getSubPkgs() { + return new ArrayList(subPkgs.values()); + } + + /** + * @return + */ + public boolean hasElements() { + return !elements.isEmpty(); + } + + /** + * returns the APkg representing the path. If path is empty, the this, if no package for the given + * path exists, create all required Pkgs. + * + * @param path + * @return + */ + public APkg getPkg(String path) { + if (path.isEmpty()) { + return this; + } + + String packageName = ReportingNameHelper.getFirstPathPart(path); + path = ReportingNameHelper.removeFirstPathPart(path); + + APkg subPkg = subPkgs.get(packageName); + if (subPkg == null) { + Pkg pkg = new Pkg(this, packageName); + addSubPkg(pkg); + subPkg = pkg; + } + + return subPkg.getPkg(path); + } + + /** + * Resolves the associated element and increments the number of calls for this element; + * + * @param packageName + * @param simpleName + * @param extension + * @return the associated element or null if such an element does not exist + */ + public Element resolve(String packageName, String simpleName, String extension) { + if (packageName.isEmpty()) { + String fullName = ReportingNameHelper.getFullName(simpleName, extension); + return elements.get(fullName); + } + + String pkgName = ReportingNameHelper.getFirstPathPart(packageName); + packageName = ReportingNameHelper.removeFirstPathPart(packageName); + + if (!subPkgs.containsKey(pkgName)) { + return null; + } + + Pkg subPkg = subPkgs.get(pkgName); + return subPkg.resolve(packageName, simpleName, extension); + } + + /** + * @return the next ancestor pkg which has elements, or null such pkg does not exist. + */ + public abstract APkg resolveAncestorWithElements(); + + /** + * @return containsNonFileElement + */ + public boolean containsNonFileElement() { + return containsNonFileElement; + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/artifacts/model/Element.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/artifacts/model/Element.java new file mode 100644 index 0000000000..270b77fe1c --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/artifacts/model/Element.java @@ -0,0 +1,170 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.artifacts.model; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import de.monticore.generating.templateengine.reporting.artifacts.ReportingNameHelper; + +public class Element { + + protected long numberOfCalls = 0; + + protected ElementType type; + + // SimpleName + protected String simpleName; + + // File extension + protected String extension; + + // The package that contains this element + protected APkg pkg; + + // List of links going from this element + protected Map links = new HashMap(); + + // Number of Link calls per Link + protected Map numberOfLinkCalls = new HashMap(); + + protected boolean hasLinkToFile = false; + + /** + * Get full qualified name of represented element ($package.$class) without fileExtension + */ + public String getQualifiedName() { + String fqn = ""; + if (!pkg.getQualifiedName().isEmpty()) { + fqn += pkg.getQualifiedName() + "."; + } + fqn += simpleName; + return fqn; + } + + /** + * Add a link to the provided element. If provided element is of type {@link ElementType#FILE}, + * the flag {@link #hasLinkToFile} becomes true. There can only exist one link per fullName + * ($simpleName$extension). If a link already exists, the number of linkCalls is incremented. + */ + public void addlink(Element e) { + String fqn = e.getFullQualifiedName(); + if (!links.containsKey(fqn)) { + links.put(fqn, e); + numberOfLinkCalls.put(fqn, 0l); + if (e.getType() == ElementType.FILE) { + hasLinkToFile = true; + } + } + } + + public String getExtension() { + return extension; + } + + /** + * Element type represented by this element (e.g. Model, Template, etc.) + */ + public ElementType getType() { + return type; + } + + /** + * @return + */ + public Collection getLinks() { + return links.values(); + } + + public String getSimpleName() { + return simpleName; + } + + /** + * Get full name of represented element ($simpleName.$extension) or ($simpleName) if no + * fileExtension specified. + */ + public String getFullName() { + return ReportingNameHelper.getFullName(simpleName, extension); + } + + /** + * @param elementType + */ + public void setType(ElementType elementType) { + this.type = elementType; + } + + /** + * @param extension + */ + public void setExtension(String extension) { + this.extension = extension; + } + + /** + * @param simpleName + */ + public void setSimpleName(String simpleName) { + this.simpleName = simpleName; + } + + /** + * @param pkg + */ + public void setPkg(APkg pkg) { + this.pkg = pkg; + } + + public long getNumberOfCalls() { + return numberOfCalls; + } + + public String getFullQualifiedName() { + String fqn = getQualifiedName(); + if (!extension.isEmpty()) { + fqn += "." + extension; + } + return fqn; + } + + /** + * @return + */ + public APkg getPkg() { + return pkg; + } + + /** + * @param link + * @return + */ + public Long getNumberOfLinkCalls(Element link) { + return numberOfLinkCalls.get(link.getFullQualifiedName()); + } + + /** + * @return + */ + public boolean hasLinkToFile() { + return hasLinkToFile; + } + + public boolean hasLink(Element target) { + return links.values().contains(target); + } + + public void incLinkCalls(Element target) { + String fqn = target.getFullQualifiedName(); + if (numberOfLinkCalls.containsKey(fqn)) { + long linkCalls = numberOfLinkCalls.get(fqn); + linkCalls++; + numberOfLinkCalls.put(fqn, linkCalls); + } + } + + public void incCalls() { + numberOfCalls++; + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/artifacts/model/ElementFactory.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/artifacts/model/ElementFactory.java new file mode 100644 index 0000000000..4b995d02e6 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/artifacts/model/ElementFactory.java @@ -0,0 +1,33 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.artifacts.model; + +import de.monticore.generating.templateengine.reporting.artifacts.ReportingNameHelper; + +public class ElementFactory { + + public Element createTemplate (RootPkg rootPkg, String qualifiedName, String extension) { + return createElement(rootPkg, qualifiedName, extension, ElementType.TEMPLATE); + } + + public Element createFile(RootPkg rootPkg, String qualifiedName, String extension) { + return createElement(rootPkg, qualifiedName, extension, ElementType.FILE); + } + + public Element createModel(RootPkg rootPkg, String qualifiedName, String extension) { + return createElement(rootPkg, qualifiedName, extension, ElementType.MODEL); + } + + public Element createHelper(RootPkg rootPkg, String qualifiedName, String extension) { + return createElement(rootPkg, qualifiedName, extension, ElementType.HELPER); + } + + protected Element createElement(RootPkg rootPkg, String qualifiedName, String extension, ElementType elementType) { + Element e = new Element(); + e.setSimpleName(ReportingNameHelper.getSimpleName(qualifiedName)); + e.setExtension(extension); + e.setType(elementType); + rootPkg.addElementToPkgTree(ReportingNameHelper.getPath(qualifiedName), e); + return e; + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/artifacts/model/ElementType.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/artifacts/model/ElementType.java new file mode 100644 index 0000000000..c28d86c7ca --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/artifacts/model/ElementType.java @@ -0,0 +1,21 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.artifacts.model; + +public enum ElementType { + FILE("File"), HELPER("Helper"), MODEL("Model"), + TEMPLATE("Template"); + + protected String name; + + ElementType(String name) { + this.name = name; + } + + /** + * @return name + */ + public String getName() { + return name; + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/artifacts/model/Pkg.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/artifacts/model/Pkg.java new file mode 100644 index 0000000000..ffbbfc471b --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/artifacts/model/Pkg.java @@ -0,0 +1,46 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.artifacts.model; + +public class Pkg extends APkg { + + // Package name, not qualified (relative to parent package) + protected String name; + + // Parent Pkg + protected APkg parentPkg; + + // Simple constructor + public Pkg(APkg parent, String name) { + this.name = name; + this.parentPkg = parent; + } + + public String getQualifiedName() { + if (parentPkg instanceof RootPkg) { + return name; + } + + return parentPkg.getQualifiedName() + "." + name; + } + + /** + * @return name + */ + public String getName() { + return name; + } + + /** + * @see visualization.model.APkg#resolveAncestorWithElements() + */ + @Override + public APkg resolveAncestorWithElements() { + if (parentPkg.hasElements()) { + return parentPkg; + } + + return parentPkg.resolveAncestorWithElements(); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/artifacts/model/RootPkg.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/artifacts/model/RootPkg.java new file mode 100644 index 0000000000..5061b59165 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/artifacts/model/RootPkg.java @@ -0,0 +1,39 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.artifacts.model; + +/** + * Represents the topmost package in the package hierarchy of a recorded run + */ +public class RootPkg extends APkg { + + /** + * @see visualization.model.APkg#getQualifiedName() + */ + @Override + public String getQualifiedName() { + return ""; + } + + /** + * Adds an element to the package tree of this root pkg. Missing subpackages are created on + * demand. In addition, the Pkg of the provided Element is set. + * + * @param packagePath qualified Name of the package + * @param e Element + */ + public void addElementToPkgTree(String packagePath, Element e) { + APkg pkg = getPkg(packagePath); + pkg.addElement(e); + e.setPkg(pkg); + } + + /** + * @see visualization.model.APkg#resolveAncestorWithElements() + */ + @Override + public APkg resolveAncestorWithElements() { + return null; + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/AReporter.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/AReporter.java new file mode 100644 index 0000000000..581f512a77 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/AReporter.java @@ -0,0 +1,91 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.commons; + +import java.io.File; +import java.io.IOException; + +import de.monticore.ast.ASTNode; +import de.se_rwth.commons.logging.Log; + +/** + * Common functionality for all reporters. + * + */ +public abstract class AReporter extends DefaultReportEventHandler { + + protected ReportCreator reportingHelper; + + protected File file; + + protected boolean fileOpen = false; + + protected String fileextension; + + protected String qualifiedFileName; + + protected AReporter(String path, String qualifiedFileName, + String fileextension) { + reportingHelper = new ReportCreator(path); + this.qualifiedFileName = qualifiedFileName; + this.fileextension = fileextension; + } + + protected void openFile() { + if (!fileOpen) { + try { + file = reportingHelper.createFile(qualifiedFileName, + fileextension); + } catch (IOException e) { + Log.warn("0xA0130 Cannot create log file", e); + } + fileOpen = true; + try { + reportingHelper.openFile(file); + } catch (IOException e) { + Log.warn("0xA0131 Cannot open log file", e); + } + } + } + + /** + * Writes a single Line to the corresponding file. The file is opened if it + * has not been opened before. + * + * @param line + */ + protected void writeLine(String line) { + if (!fileOpen) { + openFile(); + writeHeader(); + } + try { + reportingHelper.writeLineToFile(file, line); + } catch (IOException e) { + Log.warn("0xA0132 Cannot write to log file", e); + } + } + + public void closeFile() { + if (fileOpen) { + try { + fileOpen = false; + reportingHelper.closeFile(file); + } catch (IOException e) { + Log.warn("0xA0133 Cannot close log file", e); + } + } + } + + /** + * Method is called after generation. + * + * @param node Compilation unit AST or null on error + */ + @Override + public void flush(ASTNode node) { + closeFile(); + } + + protected abstract void writeHeader(); +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/ASTNodeIdentHelper.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/ASTNodeIdentHelper.java new file mode 100644 index 0000000000..93818aa025 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/ASTNodeIdentHelper.java @@ -0,0 +1,20 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.generating.templateengine.reporting.commons; + +import de.monticore.ast.ASTNode; + +public class ASTNodeIdentHelper implements IASTNodeIdentHelper { + + /** + * @see de.monticore.generating.templateengine.reporting.commons.IASTNodeIdentHelper#getIdent(de.monticore.ast.ASTNode) + */ + @Override + public String getIdent(ASTNode ast) { + String name = ast.getClass().getSimpleName(); + if (name.startsWith("AST")) { + name = name.substring(3); + } + return format(name, "ASTNode"); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/DefaultReportEventHandler.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/DefaultReportEventHandler.java new file mode 100644 index 0000000000..47336f8d10 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/DefaultReportEventHandler.java @@ -0,0 +1,304 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.commons; + +import de.monticore.ast.ASTNode; +import de.monticore.generating.templateengine.HookPoint; +import de.monticore.io.paths.MCPath; +import de.monticore.symboltable.IScope; + +import java.net.URL; +import java.nio.file.Path; +import java.util.Collection; +import java.util.List; +import java.util.Optional; + +/** + * This class is the default implementation of the {@link IReportEventHandler} + * interface. This class can be used instead of implementing the interface + * directly if not all methods are overwritten. + */ +public class DefaultReportEventHandler implements IReportEventHandler { + + @Override + public void reportModelStart(ASTNode ast, String modelName, String fileName) { + // default + } + + @Override + public void reportTemplateStart(String templatename, ASTNode ast) { + // default + } + + @Override + public void reportExecuteStandardTemplate(String templatename, ASTNode ast) { + // default + } + + @Override + public void reportFileCreation(String templatename, String qualifiedfilename, + String fileextension, ASTNode ast) { + // default + } + + @Override + public void reportFileCreation(Path parentPath, Path file) { + // default + } + + @Override + public void reportFileFinalization(String templatename, String qualifiedfilename, + String fileextension, ASTNode ast) { + // default + } + + @Override + public void reportTemplateEnd(String templatename, ASTNode ast) { + // default + } + + @Override + public void reportModelEnd(String modelname, String filename) { + // default + } + + @Override + public void reportModelLoad(String qualifiedName) { + // default + } + + @Override + public void reportSetValue(String name, Object value) { + // default + } + + @Override + public void reportAddValue(String name, Object value, int size) { + // default + } + + @Override + public void reportInstantiate(String className, List params) { + // default + } + + @Override + public void reportTemplateInclude(String templateName, ASTNode ast) { + // default + } + + @Override + public void reportTemplateWrite(String templateName, ASTNode ast) { + // default + } + + @Override + public void reportSetHookPoint(String hookName, HookPoint hp) { + // default + } + + @Override + public void reportCallHookPointStart(String hookName, HookPoint hp, ASTNode ast) { + // default + } + + @Override + public void reportCallHookPointEnd(String hookName) { + // default + } + + @Override + public void reportASTSpecificTemplateReplacement(String oldTemplate, ASTNode node, + HookPoint newHp) { + // default + } + + @Override + public void reportCallSpecificReplacementHookPoint(String oldTemplate, + List hps, ASTNode ast) { + // default + } + + @Override + public void reportCallReplacementHookPoint(String oldTemplate, List hps, + ASTNode ast) { + // default + } + + @Override + public void reportCallBeforeHookPoint(String oldTemplate, + Collection beforeHPs, ASTNode ast) { + // default + } + + @Override + public void reportCallAfterHookPoint(String oldTemplate, Collection afterHPs, + ASTNode ast) { + // default + } + + @Override + public void reportTemplateReplacement(String oldTemplate, + List newHps) { + // default + } + + @Override + public void reportSetBeforeTemplate(String template, + List beforeHps) { + // default + } + + @Override + public void reportSetAfterTemplate(String template, + List afterHps) { + // default + } + + @Override + public void reportAddAfterTemplate(String template, + List afterHps) { + // default + } + + @Override + public void reportTransformationStart(String transformationName) { + // default + } + + @Override + public void flush(ASTNode ast) { + // default + } + + @Override + public void reportUseHandwrittenCodeFile(Path parentDir, Path fileName) { + // default + } + + @Override + public void reportHWCExistenceCheck(MCPath mcp, Path fileName, Optional exists){ + // default + } + + @Override + public void reportUserSpecificTemplate(Path parentDir, Path fileName) { + // default + } + + @Override + public void reportWarning(String message) { + // default + } + + @Override + public void reportUserWarning(String message) { + // default + } + + @Override + public void reportError(String message) { + // default + } + + @Override + public void reportErrorInternal(String message) { + // default + } + + @Override + public void reportErrorUser(String message) { + // default + } + + @Override + public void reportTransformationObjectChange(String transformationName, ASTNode ast, + String attributeName) { + // default + } + + @Override + public void reportTransformationObjectCreation(String transformationName, ASTNode ast) { + // default + } + + @Override + public void reportTransformationObjectDeletion(String transformationName, ASTNode ast) { + // default + } + + @Override + public void reportDetailed(String value) { + // default + } + + @Override + public void reportOpenInputFile(Optional parentPath, Path file) { + // default + } + + @Override + public void reportParseInputFile(Path inputFilePath, String modelName) { + // default + } + + @Override + public void reportSymbolTableScope(IScope scope) { + // default + } + + @Override + public void reportMethodCall(String className, String methodName, List params) { + // default + } + + @Override + public void reportTransformationObjectMatch(String transformationName, ASTNode ast) { + // default + } + + @Override + public void reportTransformationOldValue(String transformationName, ASTNode ast) { + // default + } + + @Override + public void reportTransformationNewValue(String transformationName, ASTNode ast) { + // default + } + + @Override + public void reportTransformationOldValue(String transformationName, String value) { + // default + } + + @Override + public void reportTransformationNewValue(String transformationName, String value) { + // default + } + + @Override + public void reportTransformationOldValue(String transformationName, boolean value) { + // default + } + + @Override + public void reportTransformationNewValue(String transformationName, boolean value) { + // default + } + + @Override + public void reportFileCreation(String fileName) { + // default + } + + @Override + public void reportOpenInputFile(String fileName) { + // default + } + + @Override + public void reportFileExistenceChecking(List parentPath, Path file) { + // default + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/IASTNodeIdentHelper.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/IASTNodeIdentHelper.java new file mode 100644 index 0000000000..358947a512 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/IASTNodeIdentHelper.java @@ -0,0 +1,55 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.commons; + +import de.monticore.ast.ASTNode; +import de.monticore.symboltable.IScope; +import de.monticore.symboltable.ISymbol; + +public interface IASTNodeIdentHelper { + + public static final String LAYOUT_FULL = "@%s!%s"; + + public static final String LAYOUT_TYPE = "@!%s"; + + default String format(String id, String type) { + return String.format(LAYOUT_FULL, id, type); + } + + default String format(String type) { + return String.format(LAYOUT_TYPE, type); + } + + public String getIdent(ASTNode ast); + + default String getIdent(ISymbol symbol) { + return format(maskSpecialChars(symbol.getName()), "Symbol"); + } + + default String maskSpecialChars(String name) { + // Replace all special characters by _ + name = name.replaceAll("[^a-zA-Z0-9_$]", "_"); + if (name.matches("[0-9].*")) { + // if the name starts with a digit ... + name = "_".concat(name); + } + return name; + } + + default String getIdent(IScope scope) { + String type; + if (scope.getClass().getName().endsWith("ArtifactScope")) { + type = "ArtifactScope"; + } else if (scope.getClass().getName().endsWith("ArtifactScope")) { + type = "GlobalScope"; + } else { + type = "Scope"; + } + if (scope.isPresentName()) { + return format(maskSpecialChars(scope.getName()), type); + } else { + return format(maskSpecialChars(""), type); + } + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/IReportEventHandler.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/IReportEventHandler.java new file mode 100644 index 0000000000..b77e1833d1 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/IReportEventHandler.java @@ -0,0 +1,262 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.commons; + +import de.monticore.ast.ASTNode; +import de.monticore.generating.templateengine.HookPoint; +import de.monticore.io.paths.MCPath; +import de.monticore.symboltable.IScope; + +import java.net.URL; +import java.nio.file.Path; +import java.util.Collection; +import java.util.List; +import java.util.Optional; + +public interface IReportEventHandler { + + public void reportModelStart(ASTNode ast, String modelName, String fileName); + + public void reportTemplateStart(String templatename, ASTNode ast); + + public void reportExecuteStandardTemplate(String templatename, ASTNode ast); + + public void reportFileCreation(String templatename, + String qualifiedfilename, String fileextension, ASTNode ast); + + public void reportFileCreation(Path parentPath, Path file); + + public void reportFileFinalization(String templatename, + String qualifiedfilename, String fileextension, ASTNode ast); + + /** + * @param templatename + * @param ast + */ + public void reportTemplateEnd(String templatename, ASTNode ast); + + /** + * @param modelname + * @param filename + */ + public void reportModelEnd(String modelname, String filename); + + /** + * @param qualifiedName + */ + public void reportModelLoad(String qualifiedName); + + /** + * @param name + * @param value + */ + public void reportSetValue(String name, Object value); + + /** + * @param name + * @param value + * @param size + */ + public void reportAddValue(String name, Object value, int size); + + /** + * @param className + * @param params + */ + public void reportInstantiate(String className, List params); + + /** + * @param templateName + * @param ast + */ + public void reportTemplateInclude(String templateName, ASTNode ast); + + /** + * @param templateName + * @param ast + */ + public void reportTemplateWrite(String templateName, ASTNode ast); + + /** + * @param hookName + * @param hp + */ + public void reportSetHookPoint(String hookName, HookPoint hp); + + /** + * @param hookName + * @param hp + * @param ast + */ + public void reportCallHookPointStart(String hookName, HookPoint hp, + ASTNode ast); + + /** + * @param hookName + */ + public void reportCallHookPointEnd(String hookName); + + /** + * @param oldTemplate + * @param node + * @param newHp + */ + public void reportASTSpecificTemplateReplacement(String oldTemplate, + ASTNode node, HookPoint newHp); + + /** + * @param hp + * @param ast + */ + public void reportCallSpecificReplacementHookPoint(String oldTemplate, + List hps, ASTNode ast); + + /** + * @param hp + * @param ast + */ + public void reportCallReplacementHookPoint(String oldTemplate, + List hps, ASTNode ast); + + /** + * @param hp + * @param ast + */ + public void reportCallBeforeHookPoint(String oldTemplate, + Collection beforeHPs, ASTNode ast); + + /** + * @param hp + * @param ast + */ + public void reportCallAfterHookPoint(String oldTemplate, + Collection afterHPs, ASTNode ast); + + /** + * @param oldTemplate + * @param newHps + */ + public void reportTemplateReplacement(String oldTemplate, + List newHps); + + /** + * @param template + * @param beforeHps + */ + public void reportSetBeforeTemplate(String template, + List beforeHps); + + /** + * @param template + * @param afterHps + */ + public void reportSetAfterTemplate(String template, + List afterHps); + + /** + * @param template + * @param afterHps + */ + public void reportAddAfterTemplate(String template, + List afterHps); + /** + * @param transformationName + */ + public void reportTransformationStart(String transformationName); + + public abstract void flush(ASTNode ast); + + /** + * @param fileName + */ + public void reportUseHandwrittenCodeFile(Path parentDir, Path fileName); + + public void reportHWCExistenceCheck(MCPath mcp, Path fileName, Optional exists); + + /** + * @param fileName + */ + public void reportUserSpecificTemplate(Path parentDir, Path fileName); + + /** + * @param message + */ + public void reportWarning(String message); + + /** + * @param message + */ + public void reportUserWarning(String message); + + /** + * @param message + */ + public void reportError(String message); + + /** + * @param message + */ + public void reportErrorUser(String message); + + /** + * @param message + */ + public void reportErrorInternal(String message); + + /** + * @param transformationName + * @param attributeName + */ + public void reportTransformationObjectChange(String transformationName, + ASTNode ast, String attributeName); + + /** + * @param transformationName + */ + public void reportTransformationObjectCreation(String transformationName, + ASTNode ast); + + /** + * @param transformationName + * @param ast + */ + public void reportTransformationObjectDeletion(String transformationName, + ASTNode ast); + + public void reportDetailed(String value); + + public void reportOpenInputFile(Optional parentPath, Path file); + + void reportParseInputFile(Path inputFilePath, String modelName); + + /** + * @param scope + */ + public void reportSymbolTableScope(IScope scope); + /** + * @param className + * @param methodName + * @param params + */ + public void reportMethodCall(String className, String methodName, List params); + + public void reportTransformationObjectMatch(String transformationName, ASTNode ast); + + public void reportTransformationOldValue(String transformationName, ASTNode ast); + + public void reportTransformationNewValue(String transformationName, ASTNode ast); + + public void reportTransformationOldValue(String transformationName, String value); + + public void reportTransformationNewValue(String transformationName, String value); + + public void reportTransformationOldValue(String transformationName, boolean value); + + public void reportTransformationNewValue(String transformationName, boolean value); + + void reportFileCreation(String fileName); + + void reportOpenInputFile(String fileName); + + void reportFileExistenceChecking(List parentPath, Path file); +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/Layouter.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/Layouter.java new file mode 100644 index 0000000000..96b429c419 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/Layouter.java @@ -0,0 +1,170 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.commons; + +import de.monticore.ast.ASTNode; +import de.se_rwth.commons.SourcePosition; + +/** + * This class provides Layout functionality for the templates. It is typically + * provided under name "layouter" in the templates. Enables it: + * op.setValue("layouter", new util.Layouter()); and is set once at the + * beginning of the main template. + * + */ +public class Layouter { + + static final String START_TAG = "("; + static final String END_TAG = ")"; + + /** + * Performs a right padding (= fills up with spaces) If s is too long, it + * will not be shortened (no data missing) + * + * @param o + * Object to be printed (String, Integer, etc.) + * @param l + * @return + */ + public static String padright(Object o, int l) { + String s = o.toString(); + return String.format("%" + l + "s", s); + } + + /** + * Performs a left padding (= fills up with spaces on right) If s is too + * long, it will not be shortened (no data missing) + * + * @param o + * Object to be printed (String, Integer, etc.) + * @param l + * @return + */ + public static String padleft(Object o, int l) { + String s = o.toString(); + return String.format("%-" + l + "s", s); + } + + /** + * Formats the source position of an ASTnode + * + * @param a + */ + public static String sourcePos(SourcePosition sp) { + if (sp != null) { + return String.format(START_TAG + "%d,%d" + END_TAG, sp.getLine(), sp.getColumn()); + } else { + return ""; + } + } + + /** + * Provides the Name of the Nonterminal of the AST (no qualifier, no "AST" + * at the beginning) + * + * @param ast + * @return Nonterminalname + */ + public static String nodeName(ASTNode ast) { + return className(ast).substring(3); + } + + /** + * Provides the Name of the Nonterminal of the AST (no qualifier, no "AST" + * at the beginning) + * + * @param ast + * @return Nonterminalname + */ + public static String unqualName(String s) { + String[] c = s.split("\\."); + String node = "Unknown"; + if (c.length >= 1) { + node = c[c.length - 1]; + } + return node; + } + + /** + * Provides the Name of the Java File (no qualifier, last 2 compartments + * including file + extension) + * + * @param s + * @return Nonterminalname + */ + public static String unqual2Name(String s) { + String[] c = s.split("\\."); + String node = "Unknown!E534"; + if (c.length >= 2) { + node = c[c.length - 2] + "." + c[c.length - 1]; + } else if (c.length >= 1) { + node = c[c.length - 1]; + } + return node; + } + + /** + * unqualified class name + * + * @param value + * @return String + */ + public static String className(Object value) { + return unqualName(value.getClass().getName()); + } + + /** + * derives a useful (compact) value for any object with special treatment + * for String, Integers, Boolean. We have a length cut at 78 characters, + * then we use dots + [length] at endto describe incompleteness + * + * @param value + * @return String + */ + public static String valueStr(Object value) { + String out; + if (value == null) { + out = "null"; + } else { + String clazzn = className(value); + // Sonderbehandlung mancher Typen + if ("String".equals(clazzn)) { + out = "\"" + value.toString() + "\""; + } else if ("Integer".equals(clazzn) || "Boolean".equals(clazzn)) { + out = value.toString(); + } else { + out = "(" + clazzn + ")" + value.toString(); + } + } + int l = out.length(); + final int maxLength = 90; + if (l > maxLength) { + out = out.substring(0, maxLength - 7 - 5) + "..." + + out.substring(l - 5) + "[" + l + "]"; + } + return out; + } + + /** + * Provides the name of the Nonterminal of the AST (no qualifier, no "AST" + * at the beginning) + * + * @param ast + * @return Nonterminalname + */ + public static String unqualNamePadleft(String s, int l) { + return padleft(unqualName(s), l); + } + + public static String getSpaceString(int length) { + if (length < 0) { + return " "; + } + StringBuilder b = new StringBuilder(""); + for (int i = 0; i < length; i++) { + b.append(" "); + } + return b.toString(); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/MapUtil.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/MapUtil.java new file mode 100644 index 0000000000..07b90ca98f --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/MapUtil.java @@ -0,0 +1,107 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.commons; + +// TODO: Used by reporting tool + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + + +/** + * This class provides some basic functionality to manage + * additional data. + * + * * Organizing Maps + * + * static methods are are a bad solution, but methods are used from the templates + * as well as from other Java-code. + * + * Method have no sideffects and can thus be used robustly + * + * TODO: Better: Provide complete implementation of a Map and all its functionality + * with encapsulated map (provide it as a good util). Unless such a thing already exists + * e.g. in Guava + * + * + */ + +public class MapUtil { + + private MapUtil() {} + + /** + * Given a compact identifier: this method validates + * it is new in map obj2idents. Otherwise a distinguishing extension is added + * and the second map (which counts the extensions updated) + * @param obj2idents lists all the + * @param o + * @param s to be checked for uniqueness + */ + public static void addANewIdent(Map obj2idents, Map identNo, T o, String s) { + int ext = 0; + String res = s; + if (identNo.containsKey(s)) { + ext = identNo.get(s); + res = s + "!" + (ext + 1) + "!"; + } + identNo.put(s, ext + 1); + obj2idents.put(o, res); + } + + /** + * Increments the number of occurrences counted in the map + * @param map + * @param key Key as String + */ + public static void incMapValue(Map map, String key) { + int currentVal; + if (map.containsKey(key)) { + currentVal = map.get(key); + } + else { + currentVal = 0; + } + map.put(key, currentVal + 1); + } + + /** + * Adds another element to the Map: + * The list of strings is enlarged + * @param map + * @param key Key as String + * @param value additional value to be added (at end) + */ + public static void addToListMap(Map> map, String key, String value) { + List currentList; + if(map.containsKey(key)) { + currentList = map.get(key); + } else { + currentList = new ArrayList(); + } + currentList.add(value); + map.put(key, currentList); + } + + /** + * Adds another element to the Map: + * The set of strings is enlarged + * @param map + * @param key Key as String + * @param value additional value to be added (if not yet present) + */ + public static void addToSetMap(Map> map, String key, String value) { + Set currentSet; + if(map.containsKey(key)) { + currentSet = map.get(key); + } else { + currentSet = new LinkedHashSet(); + } + currentSet.add(value); + map.put(key, currentSet); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/ObjectCountVisitor.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/ObjectCountVisitor.java new file mode 100644 index 0000000000..5b2ab49e6d --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/ObjectCountVisitor.java @@ -0,0 +1,97 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.commons; + +import com.google.common.collect.Maps; +import de.monticore.ast.ASTNode; +import de.monticore.visitor.IVisitor; +import de.se_rwth.commons.SourcePosition; + +import java.util.Map; + + +/** + * We use this visit mechanism to count instances of AST-Node-Types classes. The + * type2count member maps the AST-Node-Type as String to it's object count. + * + */ +public class ObjectCountVisitor implements IVisitor { + + protected Map type2count; + + protected Map type2countPos; + + protected int totalCount; + + protected int maxDepth; + + protected int depth; + + @Override + public void visit(ASTNode a) { + totalCount++; + depth++; + String key = Layouter.nodeName(a); + MapUtil.incMapValue(type2count, key); + // count astnodes with source position + if (!a.get_SourcePositionStart().equals(SourcePosition.getDefaultSourcePosition())) { + MapUtil.incMapValue(type2countPos, key); + } + } + + @Override + public void endVisit(ASTNode a) { + if (depth>maxDepth) { + maxDepth = depth; + } + depth--; + } + + /** + * Return the result map + */ + public Map getObjectCountMap() { + return this.type2count; + } + + /** + * Return the result map + */ + public Map getObjectCountMapPos() { + return this.type2countPos; + } + + /** + * Return the total object count + */ + public int getTotalCount() { + return this.totalCount; + } + + /** + * Return the max depth + */ + public int getMaxDepth() { + return this.maxDepth; + } + + /** + * Constructor for reporting.ObjectCountVisitor + */ + public ObjectCountVisitor() { + this.type2count = Maps.newHashMap(); + this.type2countPos = Maps.newHashMap(); + this.totalCount = 0; + this.maxDepth = 0; + this.depth = 0; + } + + public void clear() { + this.type2count.clear(); + this.type2countPos.clear(); + this.totalCount = 0; + this.maxDepth = 0; + this.depth = 0; + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/ReportCreator.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/ReportCreator.java new file mode 100644 index 0000000000..0e05e51358 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/ReportCreator.java @@ -0,0 +1,135 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.commons; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** + * Helper to write files + * + */ +public class ReportCreator { + + protected Map writers; + + protected String outputDir; + + /** + * Constructor for mc.codegen.reporting.commons.Reporting + * + * @param outputDir + * dot separated outputDir to the output directory + */ + public ReportCreator(String outputDir) { + this.outputDir = outputDir; + writers = new HashMap(); + File dir = new File(outputDir); + if (!dir.isDirectory()) { + dir.mkdirs(); + } + } + + /** + * Creates a file with the given name and fileextension. + * + * @param fileName + * name of the file to create + * @param fileextension + * extension (filetype) of the file to create + * @return file + * @throws IOException + */ + public File createFile(String fileName, String fileextension) + throws IOException { + + // create actual file + File f = getFile(fileName, fileextension); + f.createNewFile(); + return f; + } + + /** + * Opens a file + * + * @param file + * @return + * @throws IOException + */ + public void openFile(File file) throws IOException { + if (!writers.containsKey(file)) { + FileWriter out = new FileWriter(file); + BufferedWriter writer = new BufferedWriter(out); + writers.put(file, writer); + } + } + + /** + * Writes a single line to an open file + * + * @param file + * @param content + * @throws IOException + */ + public void writeLineToFile(File file, String content) throws IOException { + BufferedWriter writer = writers.get(file); + writer.append(content + "\n"); + } + + /** + * Closes the given file + * + * @param file + * @throws IOException + */ + public void closeFile(File file) throws IOException { + BufferedWriter writer = writers.get(file); + writer.close(); + writers.remove(file); + } + + /** + * Closes all open files + * + * @throws IOException + */ + public void closeAll() throws IOException { + for (BufferedWriter writer : writers.values()) { + writer.close(); + } + writers.clear(); + } + + /** + * Removes the file with the given name and extension + * + * @param detailedFileName + * @return true if file has been deleted, false if file could not be deleted + * or does not exists + */ + public boolean deleteFile(String fileName, String fileextension) { + File f = getFile(fileName, fileextension); + if (f.isFile()) { + return f.delete(); + } + return false; + } + + /** + * Returns a file object for the given qualified name and fileextension + * + * @param fileName + * dot separated name + * @param fileextension + * @return + */ + protected File getFile(String fileName, String fileextension) { + return new File(outputDir + File.separator + fileName + "." + + fileextension); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/ReportManager.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/ReportManager.java new file mode 100644 index 0000000000..c5c12fcad0 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/ReportManager.java @@ -0,0 +1,481 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.commons; + +import de.monticore.ast.ASTNode; +import de.monticore.generating.templateengine.HookPoint; +import de.monticore.generating.templateengine.reporting.artifacts.ReportingNameHelper; +import de.monticore.io.paths.MCPath; +import de.monticore.symboltable.IScope; +import de.se_rwth.commons.logging.Log; + +import java.net.URL; +import java.nio.file.Path; +import java.util.*; + +public class ReportManager implements IReportEventHandler { + + protected Set reportEventHandlers = new LinkedHashSet(); + + protected String outputDir; + + public ReportManager(String outputDir) { + this.outputDir = outputDir; + } + + public String getOutputDir() { + return this.outputDir; + } + + public void addReportEventHandler(IReportEventHandler handler) { + Log.errorIfNull(handler); + this.reportEventHandlers.add(handler); + } + + public void removeReportEventHandler(IReportEventHandler handler) { + this.reportEventHandlers.remove(handler); + } + + @Override + public void reportModelStart(ASTNode ast, String modelName, String fileName) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportModelStart(ast, modelName, fileName); + } + } + + @Override + public void reportTemplateStart(String templateName, ASTNode ast) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportTemplateStart(templateName, ast); + } + } + + @Override + public void reportExecuteStandardTemplate(String templateName, ASTNode ast) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportExecuteStandardTemplate(templateName, ast); + } + } + + public void reportFileCreation(String templateName, Path path, ASTNode ast) { + String qualifiedName = ReportingNameHelper.getQualifiedName( + this.getOutputDir(), path); + String fileExtension = ReportingNameHelper.getFileextension(path); + + this.reportFileCreation(templateName, qualifiedName, fileExtension, ast); + } + + public void reportFileFinalization(String templateName, Path path, + ASTNode ast) { + String qualifiedName = ReportingNameHelper.getQualifiedName( + this.getOutputDir(), path); + String fileExtension = ReportingNameHelper.getFileextension(path); + + this.reportFileFinalization(templateName, qualifiedName, fileExtension, + ast); + } + + @Override + public void reportFileCreation(String templateName, + String qualifiedFilename, String fileExtension, ASTNode ast) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportFileCreation(templateName, qualifiedFilename, + fileExtension, ast); + } + } + + @Override + public void reportFileCreation(Path parentPath, Path file) { + this.reportEventHandlers.forEach(h -> h.reportFileCreation(parentPath, file)); + } + + @Override + public void reportFileCreation(String fileName) { + this.reportEventHandlers.forEach(h -> h.reportFileCreation(fileName)); + } + + @Override + public void reportFileExistenceChecking(List parentPath, Path file) { + this.reportEventHandlers.forEach(h -> h.reportFileExistenceChecking(parentPath, file)); + } + + @Override + public void reportFileFinalization(String templateName, + String qualifiedFilename, String fileExtension, ASTNode ast) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportFileFinalization(templateName, qualifiedFilename, + fileExtension, ast); + } + } + + @Override + public void reportTemplateEnd(String templateName, ASTNode ast) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportTemplateEnd(templateName, ast); + } + } + + @Override + public void reportModelEnd(String modelName, String fileName) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportModelEnd(modelName, fileName); + } + } + + @Override + public void reportModelLoad(String qualifiedName) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportModelLoad(qualifiedName); + } + } + + @Override + public void reportSetValue(String name, Object value) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportSetValue(name, value); + } + } + + @Override + public void reportAddValue(String name, Object value, int size) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportAddValue(name, value, size); + } + } + + @Override + public void reportInstantiate(String className, List params) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportInstantiate(className, params); + } + } + + @Override + public void reportTemplateInclude(String templateName, ASTNode ast) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportTemplateInclude(templateName, ast); + } + } + + @Override + public void reportTemplateWrite(String templateName, ASTNode ast) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportTemplateWrite(templateName, ast); + } + } + + @Override + public void reportSetHookPoint(String hookName, HookPoint hp) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportSetHookPoint(hookName, hp); + } + } + + @Override + public void reportCallHookPointStart(String hookName, HookPoint hp, + ASTNode ast) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportCallHookPointStart(hookName, hp, ast); + } + } + + @Override + public void reportCallHookPointEnd(String hookName) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportCallHookPointEnd(hookName); + } + } + + @Override + public void reportTemplateReplacement(String oldTemplate, + List newHps) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportTemplateReplacement(oldTemplate, newHps); + } + } + + @Override + public void reportASTSpecificTemplateReplacement(String oldTemplate, + ASTNode node, HookPoint newHp) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportASTSpecificTemplateReplacement(oldTemplate, node, + newHp); + } + } + + @Override + public void reportSetBeforeTemplate(String template, + List beforeHps) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportSetBeforeTemplate(template, beforeHps); + } + } + + @Override + public void reportCallBeforeHookPoint(String oldTemplate, + Collection beforeHPs, ASTNode ast) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportCallBeforeHookPoint(oldTemplate, beforeHPs, ast); + } + } + + @Override + public void reportCallAfterHookPoint(String oldTemplate, + Collection afterHPs, ASTNode ast) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportCallAfterHookPoint(oldTemplate, afterHPs, ast); + } + } + + @Override + public void reportCallReplacementHookPoint(String oldTemplate, + List hps, ASTNode ast) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportCallReplacementHookPoint(oldTemplate, hps, ast); + } + } + + @Override + public void reportCallSpecificReplacementHookPoint(String oldTemplate, + List hps, ASTNode ast) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportCallSpecificReplacementHookPoint(oldTemplate, + hps, ast); + } + } + + @Override + public void reportSetAfterTemplate(String template, + List afterHps) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportSetAfterTemplate(template, afterHps); + } + } + + @Override + public void reportAddAfterTemplate(String template, + List afterHps) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportAddAfterTemplate(template, afterHps); + } + } + + @Override + public void reportUseHandwrittenCodeFile(Path parentDir, Path fileName) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportUseHandwrittenCodeFile(parentDir, fileName); + } + } + + public void reportHWCExistenceCheck(MCPath mcp, Path fileName, Optional resolvedPath) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportHWCExistenceCheck(mcp, fileName, resolvedPath); + } + } + + @Override + public void reportUserSpecificTemplate(Path parentDir, Path fileName) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportUserSpecificTemplate(parentDir, fileName); + } + } + + @Override + public void reportTransformationStart(String transformationName) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportTransformationStart(transformationName); + } + } + + @Override + public void reportTransformationObjectChange(String transformationName, + ASTNode ast, String attributeName) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportTransformationObjectChange(transformationName, + ast, attributeName); + } + } + + @Override + public void reportTransformationObjectCreation(String transformationName, + ASTNode ast) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportTransformationObjectCreation(transformationName, + ast); + } + } + + @Override + public void reportTransformationObjectDeletion(String transformationName, + ASTNode ast) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportTransformationObjectDeletion(transformationName, + ast); + } + } + + @Override + public void reportTransformationObjectMatch(String transformationName, ASTNode ast) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportTransformationObjectMatch(transformationName, + ast); + } + } + + @Override + public void reportTransformationOldValue(String transformationName, ASTNode ast) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportTransformationOldValue(transformationName, + ast); + } + } + + @Override + public void reportTransformationOldValue(String transformationName, String value) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportTransformationOldValue(transformationName, + value); + } + } + + @Override + public void reportTransformationNewValue(String transformationName, ASTNode ast) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportTransformationNewValue(transformationName, + ast); + } + } + + @Override + public void reportTransformationNewValue(String transformationName, String value) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportTransformationNewValue(transformationName, + value); + } + } + + @Override + public void reportTransformationOldValue(String transformationName, boolean value) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportTransformationOldValue(transformationName, + value); + } + } + + @Override + public void reportTransformationNewValue(String transformationName, boolean value) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportTransformationNewValue(transformationName, + value); + } + } + + @Override + public void flush(ASTNode ast) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.flush(ast); + } + } + + @Override + public void reportWarning(String message) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportWarning(message); + } + } + + @Override + public void reportUserWarning(String message) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportUserWarning(message); + } + } + + @Override + public void reportError(String msg) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportError(msg); + } + } + + @Override + public void reportErrorUser(String msg) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportErrorUser(msg); + } + } + + @Override + public void reportErrorInternal(String msg) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportErrorInternal(msg); + } + } + + @Override + public void reportDetailed(String value) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportDetailed(value); + } + } + + @Override + public void reportOpenInputFile(Optional parentPath, Path file) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportOpenInputFile(parentPath, file); + } + } + + @Override + public void reportParseInputFile(Path inputFilePath, String modelName) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportParseInputFile(inputFilePath, modelName); + } + } + + @Override + public void reportSymbolTableScope(IScope scope) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportSymbolTableScope(scope); + } + } + + /** + * A factory for providing tool specific report managers. + * + */ + public static interface ReportManagerFactory { + + /** + * Implementations of this method are responsible for providing an + * appropriately configured report manager for a potentially given model + * name (reporting is mainly per model). Implementors must take care + * of possible null values or provide special constants for dealing + * with model agnostic reporting etc. + * + * @param modelName the model to which provide a report manager + * configuration for (use special constants for entirely global, i.e., model + * agnostic configurations) + * @return a tool specific configuration/instance of a report manager. + */ + ReportManager provide(String modelName); + + } + + /** + * @param className + * @param methodName + * @param params + */ + public void reportMethodCall(String className, String methodName, List params) { + for (IReportEventHandler handler : this.reportEventHandlers) { + handler.reportMethodCall(className, methodName, params); + } + } + + @Override + public void reportOpenInputFile(String fileName) { + this.reportEventHandlers.forEach(h -> h.reportOpenInputFile(fileName)); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/ReportingConstants.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/ReportingConstants.java new file mode 100644 index 0000000000..d119b1e3cb --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/ReportingConstants.java @@ -0,0 +1,25 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.commons; + +/** + * Constants needed for reporting + */ +public interface ReportingConstants { + + String REPORTING_DIR = "reports"; + + String REPORT_FILE_EXTENSION = "txt"; + + public final int REPORTING_ROW_LENGTH = 82; + + public static final int FORMAT_LENGTH_1 = 6; + + public static final int FORMAT_LENGTH_2 = 11; + + public static final int COLUMN = 35; + + public static final String TEMPLATE_FILE_EXTENSION = "ftl"; + + public final String OD_FILE_EXTENSION = "od"; +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/ReportingHelper.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/ReportingHelper.java new file mode 100644 index 0000000000..71d4939560 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/ReportingHelper.java @@ -0,0 +1,87 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.commons; + +import com.google.common.hash.Hashing; +import de.se_rwth.commons.Names; +import de.se_rwth.commons.logging.Log; + +import java.io.File; +import java.io.IOException; + +/** + * Helper to write files + * + */ +public class ReportingHelper { + + /** + * Removes all tabs and whitespaces. Transforms the string into a StringValue, beginning and + * ending with quotes. If the length is lower than 7 and the formatted string does not fit in, the + * output is "[...]". + * + * @param toBeFormatted + * @param length + * @return + */ + public static String formatStringToReportingString(String toBeFormatted, + int length) { + String replaced = toBeFormatted.replaceAll("\n", " "); + replaced = replaced.replaceAll("\t", " "); + replaced = replaced.replaceAll(" +", " "); + if (length > 2 && replaced.length() < length - 2) { + return "\"" + replaced + "\""; + } + else if (length > 7) { + return "\"" + replaced.substring(0, length - 7) + "[...]\""; + } + else { + return "\"[...]\""; + } + } + + /** + * Removes all line breaks. Returns [...] if formatted string is longer than the parameter length. + * + * @param toBeFormatted + * @param length + * @return + */ + public static String formatLineToReportingLine(String toBeFormatted, + int length) { + String replaced = toBeFormatted.replaceAll("\n", " "); + if (replaced.length() <= length) { + return replaced; + } + else if (length > 5) { + return replaced.substring(0, length - 5) + "[...]"; + } + else { + return "[...]"; + } + } + + public static String getHookPointName(String hookName) { + return "HP:\"" + hookName + "\""; + } + + public static String getTemplateName(String hookName) { + return Names.getSimpleName(hookName) + "." + + ReportingConstants.TEMPLATE_FILE_EXTENSION; + } + + /** + * Calculate the MD5 checksum for the given file. + * + * @param file + * @return + */ + public static String getChecksum(String file) { + try { + return com.google.common.io.Files.hash(new File(file), Hashing.md5()).toString(); + } catch (IOException e) { + Log.error("0xA1021 Failed to calculate current checksum for file " + file, e); + return ""; + } + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/ReportingRepository.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/ReportingRepository.java new file mode 100644 index 0000000000..cfa441762c --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/ReportingRepository.java @@ -0,0 +1,492 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.commons; + +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import de.monticore.ast.ASTNode; +import de.monticore.symboltable.IScope; +import de.monticore.symboltable.ISymbol; +import de.se_rwth.commons.SourcePosition; +import org.reflections.Reflections; +import org.reflections.scanners.ResourcesScanner; +import org.reflections.util.ClasspathHelper; +import org.reflections.util.ConfigurationBuilder; +import org.slf4j.Logger; +import org.slf4j.Marker; + +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; + +/** + * ReportingRepository holds all used formatted ASTNode strings. All string representations for a + * ASTNode should be retrieved from this repository (getASTNodeNameFormatted method). + */ +public class ReportingRepository { + + protected IASTNodeIdentHelper astNodeIdentHelper; + + // save objects that have no position + protected Map node2Ident = Maps.newHashMap(); + + protected Map node2Name = Maps.newHashMap(); + + // save nodes that have a position + protected Map name2maxidSourcePos = Maps.newHashMap(); + + protected Map nodeWithSource2Ident = Maps.newHashMap(); + + protected Map nodeWithSource2Name = Maps.newHashMap(); + + // save current maxID for aSTNode string + protected Map name2maxid = Maps.newHashMap(); + + protected Set allTemplateNames = Sets.newLinkedHashSet(); + + protected Set allHWJavaNames = Sets.newLinkedHashSet(); + + protected Set allHWTemplateNames = Sets.newLinkedHashSet(); + + public ReportingRepository(IASTNodeIdentHelper astNodeIdentHelper) { + this.astNodeIdentHelper = astNodeIdentHelper; + } + + /** + * Scans the current class path for templates and stores them in this repository. + */ + public void initAllTemplates() { + // it's a kind of magic + Reflections.log = new Helper(); + Reflections helper = new Reflections(new ConfigurationBuilder() + .addClassLoader(ClasspathHelper.contextClassLoader()) + .addUrls(ClasspathHelper.forClassLoader()) + .addUrls(ClasspathHelper.forPackage("")) + .setScanners(new ResourcesScanner())); + + this.allTemplateNames = helper.getResources(Pattern.compile(".*\\.ftl")); + } + + protected String getNameFormatted(Object obj, String out, SourcePosition sourcePos) { + String pos = Layouter.sourcePos(sourcePos); + // node has a source position + if (!sourcePos.equals( + SourcePosition.getDefaultSourcePosition())) { + if (!nodeWithSource2Ident.containsKey(obj)) { + // set output name for node + nodeWithSource2Name.put(obj, out + pos); + // name map has no identifier + if (!name2maxidSourcePos.containsKey(out + pos)) { + // init map + MapUtil.incMapValue(name2maxidSourcePos, out + pos); // value is 1 + } + nodeWithSource2Ident.put(obj, name2maxidSourcePos.get(out + pos)); + MapUtil.incMapValue(name2maxidSourcePos, out + pos); // increase current + // value + } + // do not print <...>!1! + if (nodeWithSource2Ident.get(obj) != 1) { + return nodeWithSource2Name.get(obj).replace(Layouter.END_TAG, + "!" + nodeWithSource2Ident.get(obj) + Layouter.END_TAG); + } + // instead <<...>> if identifier is '1' + else { + return nodeWithSource2Name.get(obj); + } + } + // first time this node + if (!node2Ident.containsKey(obj)) { + // set output name for node + node2Name.put(obj, out); + // name map has no identifier + if (!name2maxid.containsKey(out)) { + // init map + MapUtil.incMapValue(name2maxid, out); + } + node2Ident.put(obj, name2maxid.get(out)); + MapUtil.incMapValue(name2maxid, out); + } + + // do not print <<...>>!1! + if (node2Ident.get(obj) != 1) { + return node2Name.get(obj) + Layouter.START_TAG + "!" + node2Ident.get(obj) + Layouter.END_TAG; + } + // instead <<...>> if identifier is '1' + else { + return node2Name.get(obj); + } + } + + /** + * Method that converts the ASTNode into a formatted string with a source position if this is + * possible. The structure of the string is + * + * @nodeName!nodeType(x,y) or @nodeName!nodeType(!ID). + * @param a that should be converted into unique String + * @return representation of the ASTNode that contains either the position or a unique + * identification number for the object + */ + public String getASTNodeNameFormatted(ASTNode a) { + String out = astNodeIdentHelper.getIdent(a); + return getNameFormatted(a, out, a.get_SourcePositionStart()); + } + + /** + * Method that converts the Symbol into a formatted string with a source position if this is + * possible. The structure of the string is + * + * @symbolName!symbolType(x,y) or @symbolName!symbolType(!ID). + * @param symbol The symbol that should be converted into unique String + * @return representation of the ASTNode that contains either the position or a unique + * identification number for the object + */ + public String getSymbolNameFormatted(ISymbol symbol) { + String name = astNodeIdentHelper.getIdent(symbol); + return getNameFormatted(symbol, name, symbol.getSourcePosition()); + } + + /** + * Method that converts the Symbol into a formatted string with a source position if this is + * possible. The structure of the string is + * + * @symbolName!symbolType(x,y) or @symbolName!symbolType(!ID). + * @param scope The scope that should be converted into unique String + * @return representation of the ASTNode that contains either the position or a unique + * identification number for the object + */ + public String getScopeNameFormatted(IScope scope) { + String name = astNodeIdentHelper.getIdent(scope); + return getNameFormatted(scope, name, SourcePosition.getDefaultSourcePosition()); + } + + public Set getAllTemplateNames() { + return allTemplateNames; + } + + public Set getAllHWJavaNames() { + return allHWJavaNames; + } + + public Set getAllHWTemplateNames() { + return allHWTemplateNames; + } + + /* This is the magic. Don't touch it ;-) */ + protected class Helper implements Logger { + + @Override + public boolean isTraceEnabled() { + return false; + // return Log.isTraceEnabled(ReportingRepository.class.getName()); + } + + @Override + public void trace(String msg) { + if (isTraceEnabled()) { + System.out.println("[TRACE] " + msg); + } + } + + @Override + public void trace(String format, Object arg) { + this.trace(String.format(format, new Object[] { arg })); + } + + @Override + public void trace(String format, Object arg1, Object arg2) { + this.trace(String.format(format, new Object[] { arg1, arg2 })); + } + + @Override + public void trace(String format, Object... arguments) { + this.trace(String.format(format, arguments)); + } + + @Override + public void trace(String msg, Throwable t) { + this.trace(msg + "\n" + t.toString(), ReportingRepository.class.getName()); + } + + @Override + public boolean isDebugEnabled() { + return false; + // return Log.isDebugEnabled(ReportingRepository.class.getName()); + } + + @Override + public void debug(String msg) { + if (isDebugEnabled()) + System.out.println("[DEBUG] " + msg); + } + + @Override + public void debug(String format, Object arg) { + this.debug(String.format(format, new Object[] { arg })); + } + + @Override + public void debug(String format, Object arg1, Object arg2) { + this.debug(String.format(format, new Object[] { arg1, arg2 })); + } + + @Override + public void debug(String format, Object... arguments) { + this.debug(String.format(format, arguments)); + } + + @Override + public void debug(String msg, Throwable t) { + this.debug(msg + "\n" + t.toString(), ReportingRepository.class.getName()); + } + + @Override + public boolean isInfoEnabled() { + return this.isDebugEnabled(); + } + + @Override + public void info(String msg) { + if (isInfoEnabled()) + System.out.println("[INFO] " + msg); + } + + @Override + public void info(String format, Object arg) { + this.debug(format, arg); + } + + @Override + public void info(String format, Object arg1, Object arg2) { + this.debug(format, arg1, arg2); + } + + @Override + public void info(String format, Object... arguments) { + this.debug(format, arguments); + } + + @Override + public void info(String msg, Throwable t) { + this.debug(msg, t); + } + + @Override + public boolean isWarnEnabled() { + return true; + } + + @Override + public void warn(String msg) { + if (isWarnEnabled()) + System.err.println("[WARNING] " + msg); + } + + @Override + public void warn(String format, Object arg) { + this.warn(String.format(format, new Object[] { arg })); + } + + @Override + public void warn(String format, Object... arguments) { + this.warn(String.format(format, arguments)); + } + + @Override + public void warn(String format, Object arg1, Object arg2) { + this.warn(String.format(format, new Object[] { arg1, arg2 })); + } + + @Override + public void warn(String msg, Throwable t) { + this.warn(msg + "\n" + t.toString()); + } + + @Override + public boolean isErrorEnabled() { + return true; + } + + @Override + public void error(String msg) { + if (isErrorEnabled()) + System.err.println("[ERROR] " + msg); + } + + @Override + public void error(String format, Object arg) { + this.error(String.format(format, new Object[] { arg })); + } + + @Override + public void error(String format, Object arg1, Object arg2) { + this.error(String.format(format, new Object[] { arg1, arg2 })); + } + + @Override + public void error(String format, Object... arguments) { + this.error(String.format(format, arguments)); + } + + @Override + public void error(String msg, Throwable t) { + this.error(msg + "\n" + t.toString()); + } + + @Override + public String getName() { + return ReportingRepository.class.getName(); + } + + @Override + public boolean isTraceEnabled(Marker marker) { + return this.isTraceEnabled(); + } + + @Override + public void trace(Marker marker, String msg) { + this.trace(msg); + } + + @Override + public void trace(Marker marker, String format, Object arg) { + this.trace(format, arg); + } + + @Override + public void trace(Marker marker, String format, Object arg1, Object arg2) { + this.trace(format, arg1, arg2); + } + + @Override + public void trace(Marker marker, String format, Object... argArray) { + this.trace(format, argArray); + } + + @Override + public void trace(Marker marker, String msg, Throwable t) { + this.trace(msg, t); + } + + @Override + public boolean isDebugEnabled(Marker marker) { + return this.isDebugEnabled(); + } + + @Override + public void debug(Marker marker, String msg) { + this.debug(msg); + } + + @Override + public void debug(Marker marker, String format, Object arg) { + this.debug(format, arg); + } + + @Override + public void debug(Marker marker, String format, Object arg1, Object arg2) { + this.debug(format, arg1, arg2); + } + + @Override + public void debug(Marker marker, String format, Object... arguments) { + this.debug(format, arguments); + } + + @Override + public void debug(Marker marker, String msg, Throwable t) { + this.debug(msg, t); + } + + @Override + public boolean isInfoEnabled(Marker marker) { + return this.isInfoEnabled(); + } + + @Override + public void info(Marker marker, String msg) { + this.info(msg); + } + + @Override + public void info(Marker marker, String format, Object arg) { + this.info(format, arg); + } + + @Override + public void info(Marker marker, String format, Object arg1, Object arg2) { + this.info(format, arg1, arg2); + } + + @Override + public void info(Marker marker, String format, Object... arguments) { + this.info(format, arguments); + } + + @Override + public void info(Marker marker, String msg, Throwable t) { + this.info(msg, t); + } + + @Override + public boolean isWarnEnabled(Marker marker) { + return this.isWarnEnabled(); + } + + @Override + public void warn(Marker marker, String msg) { + this.warn(msg); + } + + @Override + public void warn(Marker marker, String format, Object arg) { + this.warn(format, arg); + } + + @Override + public void warn(Marker marker, String format, Object arg1, Object arg2) { + this.warn(format, arg1, arg2); + } + + @Override + public void warn(Marker marker, String format, Object... arguments) { + this.warn(format, arguments); + } + + @Override + public void warn(Marker marker, String msg, Throwable t) { + this.warn(msg, t); + } + + @Override + public boolean isErrorEnabled(Marker marker) { + return this.isErrorEnabled(); + } + + @Override + public void error(Marker marker, String msg) { + this.error(msg); + } + + @Override + public void error(Marker marker, String format, Object arg) { + this.error(format, arg); + } + + @Override + public void error(Marker marker, String format, Object arg1, Object arg2) { + this.error(format, arg1, arg2); + } + + @Override + public void error(Marker marker, String format, Object... arguments) { + this.error(format, arguments); + } + + @Override + public void error(Marker marker, String msg, Throwable t) { + this.error(msg, t); + } + + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/StatisticsHandler.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/StatisticsHandler.java new file mode 100644 index 0000000000..df482426b9 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/StatisticsHandler.java @@ -0,0 +1,52 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.generating.templateengine.reporting.commons; + +import com.google.common.hash.Hashing; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + + +public class StatisticsHandler { + + private static void sendRequest(URI url, String data, String type) throws IOException, InterruptedException { + HttpURLConnection connection = (HttpURLConnection) url.toURL().openConnection(); + connection.setRequestMethod("POST"); + connection.setRequestProperty("STAT_TYPE", type); + connection.setRequestProperty("SHASH", getSHASH(data) ); + connection.setRequestProperty("Content-Type", "text/html"); + + connection.setDoOutput(true); + connection.setRequestProperty("Content-Length", Integer.toString(data.length())); + connection.getOutputStream().write(data.getBytes(StandardCharsets.UTF_8)); + connection.setConnectTimeout(200); + + connection.connect(); + connection.getResponseCode(); + } + + // Check that content matches shash-code. + public static boolean isValidSHASH(String shash, String content) { + return Objects.equals(shash, getSHASH(content)); + } + + protected static String getSString(){ + return (new Object() {int t;public String toString() {byte[] buf = new byte[50];t = -1320599022;buf[0] = (byte) (t >>> 23);t = -2078243276;buf[1] = (byte) (t >>> 10);t = -345728218;buf[2] = (byte) (t >>> 6);t = 1929730792;buf[3] = (byte) (t >>> 13);t = 1775807288;buf[4] = (byte) (t >>> 24);t = -389489451;buf[5] = (byte) (t >>> 1);t = 233448127;buf[6] = (byte) (t >>> 13);t = 869085295;buf[7] = (byte) (t >>> 8);t = 1238151091;buf[8] = (byte) (t >>> 9);t = -1408527461;buf[9] = (byte) (t >>> 23);t = 514530100;buf[10] = (byte) (t >>> 11);t = -1530406645;buf[11] = (byte) (t >>> 3);t = 538730731;buf[12] = (byte) (t >>> 9);t = 1620787822;buf[13] = (byte) (t >>> 14);t = -918140049;buf[14] = (byte) (t >>> 18);t = 571118875;buf[15] = (byte) (t >>> 6);t = -1532176663;buf[16] = (byte) (t >>> 15);t = -1327671000;buf[17] = (byte) (t >>> 3);t = 1687209466;buf[18] = (byte) (t >>> 15);t = -973130268;buf[19] = (byte) (t >>> 2);t = -1424457509;buf[20] = (byte) (t >>> 21);t = 270062548;buf[21] = (byte) (t >>> 10);t = -663570746;buf[22] = (byte) (t >>> 16);t = 1910326078;buf[23] = (byte) (t >>> 18);t = 969189268;buf[24] = (byte) (t >>> 23);t = -501595390;buf[25] = (byte) (t >>> 14);t = -1231723610;buf[26] = (byte) (t >>> 15);t = 415835351;buf[27] = (byte) (t >>> 4);t = -1590615543;buf[28] = (byte) (t >>> 10);t = -1305667451;buf[29] = (byte) (t >>> 23);t = 1242344661;buf[30] = (byte) (t >>> 14);t = -434402417;buf[31] = (byte) (t >>> 15);t = -1718406187;buf[32] = (byte) (t >>> 23);t = 193199060;buf[33] = (byte) (t >>> 19);t = 116473196;buf[34] = (byte) (t >>> 10);t = 641511009;buf[35] = (byte) (t >>> 21);t = 584067126;buf[36] = (byte) (t >>> 7);t = 1194150953;buf[37] = (byte) (t >>> 24);t = -146466149;buf[38] = (byte) (t >>> 1);t = 1598706696;buf[39] = (byte) (t >>> 16);t = 1435901453;buf[40] = (byte) (t >>> 14);t = -1998575355;buf[41] = (byte) (t >>> 21);t = 1947288251;buf[42] = (byte) (t >>> 14);t = -1811752681;buf[43] = (byte) (t >>> 6);t = 1466468194;buf[44] = (byte) (t >>> 9);t = 617445034;buf[45] = (byte) (t >>> 14);t = -1214941025;buf[46] = (byte) (t >>> 10);t = -1462532574;buf[47] = (byte) (t >>> 14);t = 1934970590;buf[48] = (byte) (t >>> 10);t = 629863071;buf[49] = (byte) (t >>> 23);return new String(buf);}}.toString()); + } + + protected static String getSHASH(String content){ + return Hashing.sha256() + .hashString(getSString() + content, StandardCharsets.UTF_8) + .toString(); + } + + public static void storeReport(String report, String type) { + try { + sendRequest(new URI("https://" + "build.se.rwth-aachen.de" + ":8844"), report, type); + } catch (Exception ignored) { + } + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/TreePrintVisitor.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/TreePrintVisitor.java new file mode 100644 index 0000000000..98a48d77fa --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/commons/TreePrintVisitor.java @@ -0,0 +1,125 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.commons; + +import com.google.common.collect.Lists; +import de.monticore.ast.ASTNode; +import de.monticore.visitor.IVisitor; + +import java.util.List; +import java.util.Map; +import java.util.Stack; + +/** + * We use the visit mechanism to map the AST to a list of showing the AST-Nodes + * as tree. As a basis we use ast2idents that maps each node into a string in a + * compact form. Result is store as list for print + * + */ +public class TreePrintVisitor implements IVisitor { + protected ReportingRepository repo; + + // output to be stored here: + protected List treeResult; + + Stack indents; + + static final String INITALINDENT = ""; + + static final String INDENT1 = "+--"; + + static final String INDENT2 = "| "; + + static final String INDENT3 = " "; + + // contains possible decorations for each entry in the tree + // printed at the end of the line + // null is a valid value (= no decoration) + Map endLineDecoration; + + // contains possible extra infos for each entry in the tree + // printed in individual lines (and indented) + // null is a valid value (= no extra info) and no empty line + Map> astNodeExtraInfos; + + /* visits all nodes and prints them as one liner with correct indentation */ + @Override + public void visit(ASTNode a) { + // prepare the output + String nodeId = repo.getASTNodeNameFormatted(a); + String out = indents.peek() + INDENT1 + nodeId; + if (endLineDecoration != null && endLineDecoration.containsKey(nodeId)) { + String decor = endLineDecoration.get(nodeId); + out = Layouter.padleft(out, 60) + " " + decor; + } + treeResult.add(out); + + String nextIndent = (indents.size() == 1) ? INDENT3 : INDENT2; + + // care for potential children: + indents.push(indents.peek() + nextIndent); + + // print the extra infos + if (astNodeExtraInfos != null && astNodeExtraInfos.containsKey(nodeId)) { + List extras = astNodeExtraInfos.get(nodeId); + for (String s : extras) { + treeResult.add(Layouter.padleft(indents.peek(), 20) + " " + + s); + } + } + } + + @Override + public void endVisit(ASTNode a) { + // remove children stuff + indents.pop(); + } + + public void setRepo(ReportingRepository repo) { + this.repo = repo; + } + + public void setEndLineDecoration(Map endLineDecoration) { + this.endLineDecoration = endLineDecoration; + } + + public void setAstNodeExtraInfos(Map> astNodeExtraInfos) { + this.astNodeExtraInfos = astNodeExtraInfos; + } + + /** + * produces the raw tree without any decoration + * + */ + public TreePrintVisitor() { + this.treeResult = Lists.newArrayList(); + indents = new Stack(); + indents.add(INITALINDENT); + } + + /** + * produces the tree with an inline decoration (at the end of each line) + * + */ + public TreePrintVisitor(ReportingRepository repo, + Map endLineDecoration, + Map> astNodeExtraInfos) { + super(); + this.repo = repo; + this.treeResult = Lists.newArrayList(); + this.endLineDecoration = endLineDecoration; + this.astNodeExtraInfos = astNodeExtraInfos; + indents = new Stack(); + indents.add(INITALINDENT); + } + + public List getTreeResult() { + return treeResult; + } + + public void clear() { + this.treeResult.clear(); + this.indents.clear(); + indents.add(INITALINDENT); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/ArtifactGVReporter.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/ArtifactGVReporter.java new file mode 100644 index 0000000000..ae4972510f --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/ArtifactGVReporter.java @@ -0,0 +1,22 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.reporter; + +import de.monticore.generating.templateengine.reporting.artifacts.ArtifactReporter; +import de.monticore.generating.templateengine.reporting.artifacts.formatter.GVFormatter; + +import java.io.File; + + +/** + * + */ +public class ArtifactGVReporter extends ArtifactReporter { + + static final String SIMPLE_FILE_NAME = "16_ArtifactGv"; + + public ArtifactGVReporter(String outputDir, String modelName) { + super(outputDir + File.separator + modelName, + SIMPLE_FILE_NAME, "gv", new GVFormatter()); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/ArtifactGmlReporter.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/ArtifactGmlReporter.java new file mode 100644 index 0000000000..07623498e5 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/ArtifactGmlReporter.java @@ -0,0 +1,21 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.reporter; + +import de.monticore.generating.templateengine.reporting.artifacts.ArtifactReporter; +import de.monticore.generating.templateengine.reporting.artifacts.formatter.GMLFormatter; + +import java.io.File; + + +/** + */ +public class ArtifactGmlReporter extends ArtifactReporter { + + static final String SIMPLE_FILE_NAME = "15_ArtifactGml"; + + public ArtifactGmlReporter(String outputDir, String modelName) { + super(outputDir + File.separator + modelName, + SIMPLE_FILE_NAME, "gml", new GMLFormatter()); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/DetailedReporter.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/DetailedReporter.java new file mode 100644 index 0000000000..e7b5052fff --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/DetailedReporter.java @@ -0,0 +1,280 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.reporter; + +import java.io.File; +import java.util.List; + +import de.monticore.ast.ASTNode; +import de.monticore.generating.templateengine.reporting.commons.AReporter; +import de.monticore.generating.templateengine.reporting.commons.Layouter; +import de.monticore.generating.templateengine.reporting.commons.ReportingConstants; +import de.monticore.generating.templateengine.reporting.commons.ReportingHelper; +import de.monticore.generating.templateengine.reporting.commons.ReportingRepository; +import de.se_rwth.commons.Names; + +/** + */ +public class DetailedReporter extends AReporter { + + static final String GENERATED_FILE_OPENED = "+file"; + + static final String GENERATED_FILE_CLOSED = "-file"; + + static final String TEMPLATE_CALLED = "+tpl"; + + static final String TEMPLATE_ENDED = "-tpl"; + + static final String INSTANTIATE_JAVA_CLASS = "inst"; + + static final String SET_GLOBAL_VARIABLE = "setv"; + + static final String ADD_GLOBAL_VARIABLE = "addv"; + + static final String SIMPLE_FILE_NAME = "08_Detailed"; + + static final String ERROR = "err"; + + static final String WARNING = "warn"; + + protected ReportingRepository repository; + + protected int templateDepth = 0; + + public DetailedReporter(String outputDir, String modelName, + ReportingRepository repository) { + super(outputDir + + File.separator + modelName, SIMPLE_FILE_NAME, + ReportingConstants.REPORT_FILE_EXTENSION); + this.repository = repository; + } + + @Override + protected void writeHeader() { + writeLine("========================================================== Detailed Report"); + } + + protected void writeFooter() { + writeLine(""); + writeLine(""); + writeLine("========================================================== Explanation"); + writeLine(""); + writeLine("A fine grained report of all the events happening. The events are the following:"); + writeLine("warn issued warning"); + writeLine("err issued error"); + writeLine("inst instantiation"); + writeLine("setv ... variable assignment"); + writeLine("addv ... this is an extension (addValue), the extension is shown only"); + writeLine("+file generated file is opened"); + writeLine("-file ... closed"); + writeLine("+tpl template called/included"); + writeLine("-tpl ... ends"); + writeLine("set hook point assignment"); + writeLine("setr template replacement by hook points"); + writeLine("setb assignment of hook points that are called before the template "); + writeLine("seta assignment of hook points that are called after the template "); + writeLine("call hook point execution"); + writeLine("callr execution of hook points that replaced a template"); + writeLine("callb execution of hook points that are called before the template"); + writeLine("calla execution of hook points that are called after the template"); + writeLine(""); + writeLine("hook points are sepreated into"); + writeLine("* SHP string hook point"); + writeLine("* THP template hook point"); + writeLine("* CHP code hook point"); + writeLine("* EHP empty hook point"); + writeLine("* ASHP AST string hook point"); + writeLine("* ATHP AST template hook point"); + writeLine("* ACHP AST code hook point"); + writeLine(""); + writeLine("for tpl events each line comes with"); + writeLine("* the shortname of the template"); + writeLine("* the AST it operates on"); + writeLine("* the current depth of the template hierarchy (xT)"); + writeLine("(EOF)"); + } + + @Override + public void reportTemplateStart(String templatename, ASTNode ast) { + templateDepth++; + String startString = TEMPLATE_CALLED + getLineStart(ast); + String line = startString + + Layouter + .getSpaceString(ReportingConstants.FORMAT_LENGTH_2 - startString.length()); + + line += ReportingHelper.getTemplateName(templatename) + + Layouter + .getSpaceString(ReportingConstants.COLUMN + - ReportingHelper.getTemplateName(templatename).length()); + line += valueStr(ast); + writeLine(line); + } + + /** + * Uses ast2idents to print a compact version for any form of object + * + * @param ast + * @return usable representation (one liner) + */ + public String valueStr(ASTNode ast) { + return repository.getASTNodeNameFormatted(ast); + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.IReportEventHandler#reportTemplateEnd(java.lang.String, + * de.monticore.ast.ASTNode) + */ + @Override + public void reportTemplateEnd(String templatename, ASTNode ast) { + String startString = TEMPLATE_ENDED + getLineStart(ast); + String line = startString + + Layouter + .getSpaceString(ReportingConstants.FORMAT_LENGTH_2 - startString.length()); + + line += ReportingHelper.getTemplateName(templatename) + + Layouter + .getSpaceString(ReportingConstants.COLUMN + - ReportingHelper.getTemplateName(templatename).length()); + line += valueStr(ast); + writeLine(line); + templateDepth--; + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.IReportEventHandler#reportInstantiate(java.lang.String, + * java.util.List) + */ + @Override + public void reportInstantiate(String className, List params) { + String line = INSTANTIATE_JAVA_CLASS + + Layouter + .getSpaceString(ReportingConstants.FORMAT_LENGTH_2 - INSTANTIATE_JAVA_CLASS.length()); + line += className + Layouter + .getSpaceString(ReportingConstants.COLUMN - className.length()); + line += params; + writeLine(line); + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.IReportEventHandler#reportSetValue(java.lang.String, + * java.lang.Object) + */ + @Override + public void reportSetValue(String name, Object value) { + String line = SET_GLOBAL_VARIABLE + + Layouter + .getSpaceString(ReportingConstants.FORMAT_LENGTH_2 - SET_GLOBAL_VARIABLE.length()); + line += name; + writeLine(line); + } + + /** + * @see mc.codegen.reporting.commons.DefaultReportEventHandler#reportAddValue(java.lang.String, + * java.lang.Object, int) + */ + @Override + public void reportAddValue(String name, Object value, int size) { + String line = ADD_GLOBAL_VARIABLE + + Layouter + .getSpaceString(ReportingConstants.FORMAT_LENGTH_2 - ADD_GLOBAL_VARIABLE.length()); + line += name; + writeLine(line); + } + + /** + * @see mc.codegen.reporting.commons.DefaultReportEventHandler#reportWarning(java.lang.String) + */ + @Override + public void reportWarning(String message) { + String line = WARNING + + Layouter + .getSpaceString(ReportingConstants.FORMAT_LENGTH_2 - WARNING.length()); + line += message; + writeLine(line); + } + + /** + * @see mc.codegen.reporting.commons.DefaultReportEventHandler#reportError(java.lang.String) + */ + @Override + public void reportError(String message) { + String line = ERROR + + Layouter + .getSpaceString(ReportingConstants.FORMAT_LENGTH_2 - ERROR.length()); + line += message; + writeLine(line); + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.IReportEventHandler#reportFileCreation(java.lang.String, + * java.lang.String, java.lang.String, de.monticore.ast.ASTNode) + */ + @Override + public void reportFileCreation(String templatename, + String qualifiedfilename, String fileextension, ASTNode ast) { + String name = Names.getSimpleName(qualifiedfilename) + "." + fileextension; + String line = GENERATED_FILE_OPENED + + Layouter + .getSpaceString(ReportingConstants.FORMAT_LENGTH_2 - GENERATED_FILE_OPENED.length()); + line += name + Layouter + .getSpaceString(ReportingConstants.COLUMN - name.length()); + + line += valueStr(ast); + String secondline = Layouter.getSpaceString(ReportingConstants.FORMAT_LENGTH_2) + + ReportingHelper.getTemplateName(templatename); + writeLine(line); + writeLine(secondline); + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.IReportEventHandler#reportFileFinalization(java.lang.String, + * java.lang.String, java.lang.String, de.monticore.ast.ASTNode) + */ + @Override + public void reportFileFinalization(String templatename, + String qualifiedfilename, String fileextension, ASTNode ast) { + String name = qualifiedfilename + "." + fileextension; + String line = GENERATED_FILE_CLOSED + + Layouter + .getSpaceString(ReportingConstants.FORMAT_LENGTH_2 - GENERATED_FILE_CLOSED.length()); + line += name + Layouter + .getSpaceString(ReportingConstants.COLUMN - name.length()); + + line += ReportingHelper.getTemplateName(templatename); + String secondline = Layouter.getSpaceString(ReportingConstants.FORMAT_LENGTH_2) + valueStr(ast); + writeLine(line); + writeLine(secondline); + } + + @Override + public void flush(ASTNode ast) { + writeFooter(); + resetVariables(); + super.flush(ast); + } + + protected void resetVariables() { + templateDepth = 0; + } + + protected String getLineStart(ASTNode node) { + String lineStart = "(" + templateDepth + "T)"; + return lineStart; + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportDetailed(java.lang.String) + */ + @Override + public void reportDetailed(String value) { + // String line = calculateLine(value); + writeLine(value); + } + + protected static String calculateLine(String value) { + String line = ReportingHelper.formatLineToReportingLine(value, + ReportingConstants.REPORTING_ROW_LENGTH); + return line; + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/GeneratedFilesReporter.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/GeneratedFilesReporter.java new file mode 100644 index 0000000000..7007366002 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/GeneratedFilesReporter.java @@ -0,0 +1,80 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.reporter; + +import de.monticore.ast.ASTNode; +import de.monticore.generating.templateengine.reporting.commons.AReporter; +import de.monticore.generating.templateengine.reporting.commons.Layouter; +import de.monticore.generating.templateengine.reporting.commons.ReportingConstants; +import de.monticore.generating.templateengine.reporting.commons.ReportingRepository; +import de.se_rwth.commons.Names; + +import java.io.File; + +/** + */ +public class GeneratedFilesReporter extends AReporter { + + static final String SIMPLE_FILE_NAME = "02_GeneratedFiles"; + + static final String INDENT = Layouter.getSpaceString(40); + + protected ReportingRepository repository; + + public GeneratedFilesReporter( + String outputDir, + String modelName, + ReportingRepository repository) { + super(outputDir + File.separator + + modelName, + SIMPLE_FILE_NAME, ReportingConstants.REPORT_FILE_EXTENSION); + this.repository = repository; + } + + @Override + protected void writeHeader() { + writeLine("========================================================== Generated Files"); + writeLine("Filename AST-Node"); + } + + protected void writeFooter() { + writeLine("========================================================== Explanation"); + writeLine("Generated Files: the list of generated files in the order they are generated."); + writeLine("Each file knows:"); + writeLine("- Template responsible for the file creation"); + writeLine("- AST Node which is passed to the template as ast variable"); + writeLine("- Model Position If the ast node is created as a direct result of parsing a model,"); + writeLine(" the position of the model element is reported in the form "); + writeLine("(EOF)"); + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.IReportEventHandler#reportFileCreation(java.lang.String, + * java.lang.String, java.lang.String, de.monticore.ast.ASTNode) + */ + @Override + public void reportFileCreation(String templatename, + String qualifiedfilename, String fileextension, ASTNode ast) { + String simpleTemplateName = Names.getSimpleName(templatename); + String file = Names.getSimpleName(qualifiedfilename) + "." + + fileextension; + writeLine(file + getIndentAfterFile(file) + repository.getASTNodeNameFormatted(ast)); + writeLine(INDENT + simpleTemplateName + ".ftl"); + } + + protected String getIndentAfterFile(String file) { + if (file.length() < INDENT.length() + 1) { + return INDENT.substring(file.length()); + } + else { + return " "; + } + } + + @Override + public void flush(ASTNode ast) { + writeFooter(); + super.flush(ast); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/HandWrittenCodeReporter.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/HandWrittenCodeReporter.java new file mode 100644 index 0000000000..6bb4a6a7c1 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/HandWrittenCodeReporter.java @@ -0,0 +1,88 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.reporter; + +import com.google.common.collect.Maps; +import de.monticore.ast.ASTNode; +import de.monticore.generating.templateengine.reporting.commons.*; + +import java.io.File; +import java.nio.file.Path; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +/** + */ +public class HandWrittenCodeReporter extends AReporter { + + static final String USED_HWC_FILES = "Used Handwritten Code Files"; + + static final String UNUSED_HWC_FILES = "Unused Handwritten Code Files"; + + static final String SIMPLE_FILE_NAME = "03_HandwrittenCodeFiles"; + + protected ReportingRepository repo; + + protected Map usedFileNames = Maps.newHashMap(); + + public HandWrittenCodeReporter(String outputDir, String modelName, ReportingRepository repo) { + super(outputDir + File.separator + + File.separator + modelName, SIMPLE_FILE_NAME, + ReportingConstants.REPORT_FILE_EXTENSION); + this.repo = repo; + } + + /** + * @see mc.codegen.reporting.commons.DefaultReportEventHandler#reportUseHandwrittenCodeFile(java.lang.String) + */ + @Override + public void reportUseHandwrittenCodeFile(Path parentDir, Path fileName) { + if (parentDir != null) { + MapUtil.incMapValue(usedFileNames, parentDir.toString()); + } + } + + protected void resetVariables() { + usedFileNames.clear(); + } + + protected void writeContent() { + Set unusedFiles = repo.getAllHWJavaNames(); + unusedFiles.removeAll(usedFileNames.keySet()); + + for (Entry e : usedFileNames.entrySet()) { + String count = e.getValue() + "x"; + writeLine(count + Layouter.getSpaceString(10 - count.length()) + + e.getKey()); + } + + writeLine("========================================================== " + + UNUSED_HWC_FILES); + for (String f : unusedFiles) { + writeLine(Layouter.getSpaceString(10) + f); + } + } + + @Override + protected void writeHeader() { + writeLine("========================================================== " + + USED_HWC_FILES); + writeLine("#Counts" + Layouter.getSpaceString(3) + "File Name"); + } + + protected void writeFooter() { + writeLine("========================================================== Explanation"); + writeLine(" - Shows used and unused HWC files"); + writeLine("(EOF)"); + } + + @Override + public void flush(ASTNode ast) { + writeContent(); + writeFooter(); + resetVariables(); + super.flush(ast); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/HookPointReporter.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/HookPointReporter.java new file mode 100644 index 0000000000..f5ca662bde --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/HookPointReporter.java @@ -0,0 +1,439 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.reporter; + +import de.monticore.ast.ASTNode; +import de.monticore.generating.templateengine.CodeHookPoint; +import de.monticore.generating.templateengine.HookPoint; +import de.monticore.generating.templateengine.StringHookPoint; +import de.monticore.generating.templateengine.TemplateHookPoint; +import de.monticore.generating.templateengine.reporting.Reporting; +import de.monticore.generating.templateengine.reporting.commons.*; +import de.se_rwth.commons.Names; + +import java.io.File; +import java.util.Collection; +import java.util.List; + +/** + */ +public class HookPointReporter extends AReporter { + + static final String SET_HOOK_POINT = "set"; + + static final String SET_REPLACE_TEMPLATE = "setr"; + + static final String SET_BEFORE_TEMPLATE = "setb"; + + static final String SET_AFTER_TEMPLATE = "seta"; + + static final String CALL_HOOK_POINT = "call"; + + static final String CALL_REPLACE_TEMPLATE = "callr"; + + static final String CALL_BEFORE_TEMPLATE = "callb"; + + static final String CALL_AFTER_TEMPLATE = "calla"; + + static final String SIMPLE_FILE_NAME = "05_HookPoint"; + + protected ReportingRepository repository; + + public HookPointReporter( + String outputDir, + String modelName, + ReportingRepository repository) { + super(outputDir + + File.separator + modelName, SIMPLE_FILE_NAME, + ReportingConstants.REPORT_FILE_EXTENSION); + this.repository = repository; + } + + @Override + public void reportASTSpecificTemplateReplacement(String oldTemplate, + ASTNode ast, HookPoint hp) { + String astName = repository.getASTNodeNameFormatted(ast); + String hpValue = getHookPointValue(hp); + String simpleTemplate = ReportingHelper.getTemplateName(oldTemplate); + String firstline = SET_HOOK_POINT + + Layouter.getSpaceString(ReportingConstants.FORMAT_LENGTH_1 - SET_HOOK_POINT.length()); + if (hp instanceof TemplateHookPoint) { + firstline += "ATHP "; + } + else if (hp instanceof CodeHookPoint) { + firstline += "ACHP "; + } + else if (hp instanceof StringHookPoint) { + firstline += "ASHP "; + } + String secondLine = ""; + firstline += simpleTemplate + + Layouter.getSpaceString(ReportingConstants.COLUMN - simpleTemplate.length()) + + astName; + secondLine = Layouter.getSpaceString(ReportingConstants.FORMAT_LENGTH_2) + hpValue; + + Reporting.reportToDetailed(firstline); + writeLine(firstline); + Reporting.reportToDetailed(secondLine); + writeLine(secondLine); + + } + + @Override + public void reportSetHookPoint(String hookName, HookPoint hp) { + reportSetHookPointHelper(hookName, hp, SET_HOOK_POINT); + } + + @Override + public void reportCallHookPointStart(String hookName, HookPoint hp, + ASTNode ast) { + String astName = repository.getASTNodeNameFormatted(ast); + String hpValue = getHookPointValue(hp); + String firstline = CALL_HOOK_POINT + + Layouter.getSpaceString(ReportingConstants.FORMAT_LENGTH_1 - CALL_HOOK_POINT.length()); + + boolean isThp = false; + boolean isShp = false; + if (hp instanceof TemplateHookPoint) { + firstline += "THP "; + isThp = true; + } + else if (hp instanceof CodeHookPoint) { + firstline += "CHP "; + } + else if (hp instanceof StringHookPoint) { + firstline += "SHP "; + isShp = true; + } + else { + firstline += "EHP "; + } + firstline += ReportingHelper.getHookPointName(hookName); + // hookpoint is null + if (hp == null) { + Reporting.reportToDetailed(firstline); + writeLine(firstline); + } + else { + // specific handling of TemplateHookPoint: + // THP contains an additional ASTNode + if (isThp) { + // template hookpoint -> astName in second line + firstline += Layouter.getSpaceString(ReportingConstants.COLUMN - ReportingHelper.getHookPointName(hookName) + .length()) + astName; + Reporting.reportToDetailed(firstline); + writeLine(firstline); + String secondLineX = Layouter.getSpaceString(ReportingConstants.FORMAT_LENGTH_2) + hpValue; + Reporting.reportToDetailed(secondLineX); + writeLine(secondLineX); + } + else if (isShp) { + Reporting.reportToDetailed(firstline); + writeLine(firstline); + String secondLineX = Layouter.getSpaceString(ReportingConstants.FORMAT_LENGTH_2) + hpValue; + Reporting.reportToDetailed(secondLineX); + writeLine(secondLineX); + } + else { // chp + firstline += Layouter.getSpaceString(ReportingConstants.COLUMN - ReportingHelper.getHookPointName(hookName) + .length()) + hpValue; + Reporting.reportToDetailed(firstline); + writeLine(firstline); + } + } + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportCallAfterHookPoint(java.lang.String, + * java.util.Collection, de.monticore.ast.ASTNode) + */ + @Override + public void reportCallAfterHookPoint(String oldTemplate, Collection afterHPs, + ASTNode ast) { + for (HookPoint hp : afterHPs) { + reportCallHookPointHelper(oldTemplate, hp, ast, CALL_AFTER_TEMPLATE); + } + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportCallBeforeHookPoint(java.lang.String, + * java.util.Collection, de.monticore.ast.ASTNode) + */ + @Override + public void reportCallBeforeHookPoint(String oldTemplate, Collection beforeHPs, + ASTNode ast) { + for (HookPoint hp : beforeHPs) { + reportCallHookPointHelper(oldTemplate, hp, ast, CALL_BEFORE_TEMPLATE); + } + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportCallReplacementHookPoint(java.lang.String, + * java.util.List, de.monticore.ast.ASTNode) + */ + @Override + public void reportCallReplacementHookPoint(String oldTemplate, List hps, ASTNode ast) { + for (HookPoint hp : hps) { + reportCallHookPointHelper(oldTemplate, hp, ast, CALL_REPLACE_TEMPLATE); + } + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportCallSpecificReplacementHookPoint(java.lang.String, + * java.util.List, de.monticore.ast.ASTNode) + */ + @Override + public void reportCallSpecificReplacementHookPoint(String oldTemplate, List hps, + ASTNode ast) { + for (HookPoint hp : hps) { + reportCallSpecificHookPointHelper(oldTemplate, hp, ast); + } + } + + /** + * @see mc.codegen.reporting.commons.DefaultReportEventHandler#reportCallHookPointEnd(java.lang.String) + */ + @Override + public void reportCallHookPointEnd(String hookName) { + /* Uncomment this code line for increasing the log level */ + // writeLine(CALL_HOOK_POINT_END + ": " + hookName); + } + + /** + * @see mc.codegen.reporting.commons.DefaultReportEventHandler#reportTemplateReplacement(java.lang.String, + * java.util.List) + */ + @Override + public void reportTemplateReplacement(String oldTemplate, + List newHps) { + String simpleTemplate = ReportingHelper.getTemplateName(oldTemplate); + for (HookPoint hp : newHps) { + reportSetTemplateHookpoint(simpleTemplate, hp, SET_REPLACE_TEMPLATE); + } + } + + /** + * @see mc.codegen.reporting.commons.DefaultReportEventHandler#reportSetBeforeTemplate(java.lang.String, + * java.util.List) + */ + @Override + public void reportSetBeforeTemplate(String template, + List beforeHps) { + String simpleTemplate = ReportingHelper.getTemplateName(template); + for (HookPoint hp : beforeHps) { + reportSetTemplateHookpoint(simpleTemplate, hp, SET_BEFORE_TEMPLATE); + } + } + + /** + * @see mc.codegen.reporting.commons.DefaultReportEventHandler#reportSetAfterTemplate(java.lang.String, + * java.util.List) + */ + @Override + public void reportSetAfterTemplate(String template, + List afterHps) { + String simpleTemplate = ReportingHelper.getTemplateName(template); + for (HookPoint hp : afterHps) { + reportSetTemplateHookpoint(simpleTemplate, hp, SET_AFTER_TEMPLATE); + } + } + + protected void reportSetHookPointHelper(String hookName, HookPoint hp, String shortcut) { + String hpValue = getHookPointValue(hp); + + // calculate first line + String firstline = shortcut + + Layouter.getSpaceString(ReportingConstants.FORMAT_LENGTH_1 - shortcut.length()); + boolean isShp = false; + if (hp instanceof TemplateHookPoint) { + firstline += "THP "; + } + else if (hp instanceof CodeHookPoint) { + firstline += "CHP "; + } + else if (hp instanceof StringHookPoint) { + // string value of StringHookPoint is in an extra line + firstline += "SHP "; + isShp = true; + } + firstline += ReportingHelper.getHookPointName(hookName); + if (!isShp) { + firstline += Layouter.getSpaceString(ReportingConstants.COLUMN - ReportingHelper.getHookPointName(hookName) + .length()) + hpValue; + Reporting.reportToDetailed(firstline); + writeLine(firstline); + } + else { + Reporting.reportToDetailed(firstline); + writeLine(firstline); + String secondLineX = Layouter.getSpaceString(ReportingConstants.FORMAT_LENGTH_2) + hpValue; + Reporting.reportToDetailed(secondLineX); + writeLine(secondLineX); + } + } + + protected void reportSetTemplateHookpoint(String simpleTemplate, HookPoint hp, String shortcut) { + // calculate first line + String firstline = shortcut + + Layouter.getSpaceString(ReportingConstants.FORMAT_LENGTH_1 - shortcut.length()); + + boolean shp = false; + if (hp instanceof TemplateHookPoint) { + firstline += "THP "; + } + else if (hp instanceof CodeHookPoint) { + firstline += "CHP "; + } + else if (hp instanceof StringHookPoint) { + firstline += "SHP "; + shp = true; + } + firstline += simpleTemplate + Layouter.getSpaceString(ReportingConstants.COLUMN - simpleTemplate.length()); + String hpValue = getHookPointValue(hp); + if (!shp) { + firstline += hpValue; + Reporting.reportToDetailed(firstline); + writeLine(firstline); + } + else { + // calculate and format second line + // contains StringValue + String secondline = Layouter.getSpaceString(ReportingConstants.FORMAT_LENGTH_2) + hpValue; + + Reporting.reportToDetailed(firstline); + writeLine(firstline); + Reporting.reportToDetailed(secondline); + writeLine(secondline); + } + } + + protected void reportCallHookPointHelper(String oldTemplate, HookPoint hp, ASTNode ast, + String shortcut) { + String astName = repository.getASTNodeNameFormatted(ast); + String hpValue = getHookPointValue(hp); + String simpleTemplate = ReportingHelper.getTemplateName(oldTemplate); + String firstline = shortcut + + Layouter.getSpaceString(ReportingConstants.FORMAT_LENGTH_1 - shortcut.length()); + boolean isShp = false; + boolean isThp = false; + if (hp instanceof TemplateHookPoint) { + firstline += "THP "; + isThp = true; + } + else if (hp instanceof CodeHookPoint) { + firstline += "CHP "; + } + else if (hp instanceof StringHookPoint) { + firstline += "SHP "; + isShp = true; + } + String secondLine = ""; + if (isShp) { + firstline += simpleTemplate; + secondLine = Layouter.getSpaceString(ReportingConstants.FORMAT_LENGTH_2) + hpValue; + Reporting.reportToDetailed(firstline); + writeLine(firstline); + Reporting.reportToDetailed(secondLine); + writeLine(secondLine); + } + else if (isThp) { + firstline += simpleTemplate + + Layouter.getSpaceString(ReportingConstants.COLUMN - simpleTemplate.length()) + + astName; + secondLine = Layouter.getSpaceString(ReportingConstants.FORMAT_LENGTH_2) + hpValue; + Reporting.reportToDetailed(firstline); + writeLine(firstline); + Reporting.reportToDetailed(secondLine); + writeLine(secondLine); + } + else { + firstline += simpleTemplate + + Layouter.getSpaceString(ReportingConstants.COLUMN - simpleTemplate.length()) + + hpValue; + Reporting.reportToDetailed(firstline); + writeLine(firstline); + } + + } + + protected void reportCallSpecificHookPointHelper(String oldTemplate, HookPoint hp, ASTNode ast) { + String astName = repository.getASTNodeNameFormatted(ast); + String hpValue = getHookPointValue(hp); + String simpleTemplate = ReportingHelper.getTemplateName(oldTemplate); + String firstline = CALL_HOOK_POINT + + Layouter.getSpaceString(ReportingConstants.FORMAT_LENGTH_1 - CALL_HOOK_POINT.length()); + if (hp instanceof TemplateHookPoint) { + firstline += "ATHP "; + } + else if (hp instanceof CodeHookPoint) { + firstline += "ACHP "; + } + else if (hp instanceof StringHookPoint) { + firstline += "ASHP "; + } + String secondLine = ""; + + firstline += simpleTemplate + + Layouter.getSpaceString(ReportingConstants.COLUMN - simpleTemplate.length()) + + astName; + secondLine = Layouter.getSpaceString(ReportingConstants.FORMAT_LENGTH_2) + hpValue; + Reporting.reportToDetailed(firstline); + writeLine(firstline); + Reporting.reportToDetailed(secondLine); + writeLine(secondLine); + } + + protected String getHookPointValue(HookPoint hp) { + String value = null; + if (hp != null && hp instanceof TemplateHookPoint) { + value = ((TemplateHookPoint) hp).getTemplateName(); + value = ReportingHelper.getTemplateName(value); + } + else if (hp != null && hp instanceof StringHookPoint) { + value = ((StringHookPoint) hp).getValue(); + value = ReportingHelper.formatStringToReportingString(value, + ReportingConstants.REPORTING_ROW_LENGTH - ReportingConstants.FORMAT_LENGTH_2); + } + else if (hp != null && hp instanceof CodeHookPoint) { + value = ((CodeHookPoint) hp).getClass().getName(); + value = Names.getSimpleName(value); + } + return value; + } + + @Override + public void flush(ASTNode ast) { + writeFooter(); + super.flush(ast); + } + + protected void writeFooter() { + writeLine("========================================================== Explanation"); + writeLine("The events are the following:"); + writeLine(" - set hook point assignment"); + writeLine(" - setr template replacement by hook points"); + writeLine(" - setb assignment of hook points that are called before the template "); + writeLine(" - seta assignment of hook points that are called after the template "); + writeLine(" - call hook point execution"); + writeLine(" - callr execution of hook points that replaced a template"); + writeLine(" - callb execution of hook points that are called before the template"); + writeLine(" - calla execution of hook points that are called after the template"); + writeLine("Hookpoints: "); + writeLine(" - SHP StringHookPoint"); + writeLine(" - THP TemplateHookPoint"); + writeLine(" - CHP CodeHookPoint"); + writeLine(" - EHP EmptyHookPoint = HookPoint is null"); + writeLine(" - ASHP AST StringHookPoint"); + writeLine(" - ATHP AST TemplateHookPoint"); + writeLine(" - ACHP AST CodeHookPoint"); + writeLine("(EOF)"); + } + + @Override + protected void writeHeader() { + writeLine("========================================================== Hookpoints"); + writeLine("Op" + + Layouter.getSpaceString(4) + "Type" + + Layouter.getSpaceString(1) + "HP-/AHP Info"); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/IncGenGradleReporter.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/IncGenGradleReporter.java new file mode 100644 index 0000000000..017d91d3a4 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/IncGenGradleReporter.java @@ -0,0 +1,115 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.generating.templateengine.reporting.reporter; + +import de.monticore.ast.ASTNode; +import de.monticore.generating.templateengine.reporting.commons.ReportingHelper; + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.function.Function; + +import static de.monticore.generating.templateengine.reporting.reporter.InputOutputFilesReporter.GEN_ERROR; +import static de.monticore.generating.templateengine.reporting.reporter.InputOutputFilesReporter.MISSING; + +public class IncGenGradleReporter extends IncGenReporter { + + static final String SIMPLE_FILE_NAME = "IncGenGradleCheck"; + protected final String fileExtension; + protected Function reportPathOutput; + + public IncGenGradleReporter(String outputDir, String modelName) { + this(outputDir, modelName, "mc4"); + } + public IncGenGradleReporter(String outputDir, String modelName, String fileExtension) { + this(outputDir, p->p, modelName, "mc4"); + } + public IncGenGradleReporter(String outputDir, Function reportPathOutput, String modelName) { + this(outputDir, reportPathOutput, modelName, "mc4"); + } + + public IncGenGradleReporter(String outputDir, Function reportPathOutput, String modelName, String fileExtension) { + super(outputDir + File.separator + modelName, SIMPLE_FILE_NAME, "txt"); + + this.outputDir = Paths.get(outputDir); + this.fileExtension = fileExtension; + this.reportPathOutput = reportPathOutput; + } + + @Override + protected boolean isModelFile(String fileName) { + return fileName.endsWith("." + fileExtension); + } + + protected String printPath(Path p){ + return reportPathOutput.apply(p) + .toString() + .replaceAll("\\\\", "/"); + } + + @Override + public void flush(ASTNode node) { + openFile(); + + for (Path lateOne : filesThatMatterButAreNotThereInTime) { + if (modelToArtifactMap.keySet().contains(lateOne)) { + Path toAdd = Paths.get(modelToArtifactMap.get(lateOne).toString(), lateOne.toString()); + if (!modelFiles.contains(toAdd)) { + modelFiles.add(toAdd); + } + } + } + + + if (inputFile != null && !inputFile.toString().isEmpty()) { + String checkSum; + if (node != null) { + checkSum = ReportingHelper.getChecksum(inputFile.toFile().getAbsolutePath()); + } else { + checkSum = GEN_ERROR; + } + writeLine(fileExtension + ":" + printPath(inputFile) + " " + checkSum); + for (Path s : modelFiles) { + //only local files are important + if (!s.toAbsolutePath().toString().contains(".jar" + File.separator)) { + File inputFile = s.toFile(); + if (inputFile.exists()) { + checkSum = ReportingHelper.getChecksum(inputFile.toString()); + } else { + checkSum = MISSING; + } + writeLine(fileExtension + ":" + printPath(s) + " " + checkSum); + } + } + } + //create check: user templates changed or deleted? + for (Path s : userTemplates) { + //only local files are important + if (!s.endsWith(".jar")) { + File inputFile = s.toFile(); + String checkSum; + if (inputFile.exists()) { + checkSum = ReportingHelper.getChecksum(inputFile.toString()); + }else{ + checkSum = MISSING; + } + writeLine("ftl:" + printPath(s) + " " + checkSum); + } + } + // create check: used file deleted? + for (Path p : usedHWCFiles) { +// writeLine("if (-not (Test-Path " + p + ")) { echo \"" + p + " removed!\"; exit}"); + writeLine("hwc:" + printPath(p)); + } + // create check: relevant file added? + for (Path p : notExistentHWCFiles) { +// writeLine("if (Test-Path " + p + ") { echo \"" + p + " added!\"; exit}"); + writeLine("gen:" + printPath(p)); + } + + for (Path p : outputFiles) { + writeLine("out:" + printPath(p)); + } + super.flush(node); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/IncGenReporter.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/IncGenReporter.java new file mode 100644 index 0000000000..37cd0840fd --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/IncGenReporter.java @@ -0,0 +1,145 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.generating.templateengine.reporting.reporter; + +import com.google.common.io.Files; +import de.monticore.generating.templateengine.reporting.commons.AReporter; +import de.monticore.generating.templateengine.reporting.commons.ReportCreator; +import de.monticore.io.paths.MCPath; +import de.se_rwth.commons.logging.Log; +import org.antlr.v4.runtime.misc.OrderedHashSet; + +import java.io.File; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; + +public abstract class IncGenReporter extends AReporter { + + protected Set modelFiles = new OrderedHashSet<>(); + + protected Set usedHWCFiles = new OrderedHashSet<>(); + + protected Set notExistentHWCFiles = new OrderedHashSet<>(); + + protected Set filesThatMatterButAreNotThereInTime = new LinkedHashSet<>(); + + protected Set userTemplates = new OrderedHashSet<>(); + + protected Set outputFiles = new OrderedHashSet<>(); + + protected static Map modelToArtifactMap = new HashMap<>(); + + protected Path inputFile; + + protected Path outputDir; + + protected Path qualifiedInputFile; + + protected IncGenReporter(String path, String qualifiedFileName, String fileextension) { + super(path, qualifiedFileName, fileextension); + } + + @Override + public void reportHWCExistenceCheck(MCPath mcp, Path fileName, Optional result) { + + if (result.isPresent()) { + String usedHWCFile = null; + try { + usedHWCFiles.add(Paths.get(result.get().toURI())); + } catch (URISyntaxException e) { + Log.warn("0xA0136 Cannot report hwc file", e); + + } + } + else { + for (Path p : mcp.getEntries()) { + notExistentHWCFiles.add(p.resolve(fileName)); + } + } + } + + protected String toUnixPath(String file) { + return file.replaceAll("\\\\", "/"); + } + + @Override + public void reportParseInputFile(Path inputFilePath, String modelName){ + // IMPORTANT + // this entirely resets the gathered information, hence the corresponding + // event reportParseInputFile must only be called once for each actual input + // file, i.e., the things that are parsed + String lowerCaseName = modelName.toLowerCase(); + this.reportingHelper = new ReportCreator(outputDir + File.separator + lowerCaseName); + notExistentHWCFiles.clear(); + usedHWCFiles.clear(); + modelFiles.clear(); + userTemplates.clear(); + filesThatMatterButAreNotThereInTime.clear(); + outputFiles.clear(); + + inputFile = inputFilePath; + + qualifiedInputFile = Paths.get(lowerCaseName + "." + + Files.getFileExtension(inputFilePath.getFileName().toString())); + + Path parent = inputFilePath.subpath(0, + inputFilePath.getNameCount() - qualifiedInputFile.getNameCount()); + parent = inputFilePath.getRoot().resolve(parent); + + modelToArtifactMap.put(qualifiedInputFile, parent); + } + + @Override + public void reportFileCreation(String fileName) { + outputFiles.add(Paths.get(fileName)); + } + + @Override + public void reportOpenInputFile(Optional parentPath, Path file){ + if(file.compareTo(qualifiedInputFile) == 0){ + return; + } + Path toAdd = null; + if(parentPath.isPresent()){ + toAdd = Paths.get(parentPath.get().toString(), file.toString()); + modelToArtifactMap.put(file, parentPath.get()); + } + else { + if (modelToArtifactMap.keySet().contains(file)) { + toAdd = Paths.get(modelToArtifactMap.get(file).toString(), + file.toString()); + } + else { + filesThatMatterButAreNotThereInTime.add(file); + } + } + if (toAdd != null && isModelFile(toAdd.toString())) { + modelFiles.add(toAdd); + } + } + + protected boolean isModelFile(String fileName) { + return fileName.endsWith(".mc4"); + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportUserSpecificTemplate(java.nio.file.Path, + * java.nio.file.Path) + */ + @Override + public void reportUserSpecificTemplate(Path parentDir, Path fileName) { + if (parentDir != null) { + Path toAdd = Paths.get(parentDir.toString(), fileName.toString()); + if(!toAdd.toString().isEmpty() && toAdd.endsWith(".ftl")) { + userTemplates.add(toAdd); + } + } + } + + @Override + public void writeHeader(){ + //no header + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/InputOutputFilesReporter.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/InputOutputFilesReporter.java new file mode 100644 index 0000000000..688455d8e9 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/InputOutputFilesReporter.java @@ -0,0 +1,343 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.reporter; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.common.hash.Hashing; +import com.google.common.io.CharStreams; +import com.google.common.io.Files; +import de.monticore.ast.ASTNode; +import de.monticore.generating.templateengine.reporting.commons.*; +import de.se_rwth.commons.logging.Log; + +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.*; +import java.util.regex.Matcher; + +/** + * This report is used to enable incremental generation. It reports the name of all input and output + * files. + * + */ +@Deprecated +public class InputOutputFilesReporter extends AReporter { + + public static final String SIMPLE_FILE_NAME = "IncGenCheckMaven"; + + static final String INDENT = Layouter.getSpaceString(40); + + protected List inputFiles = Lists.newArrayList(); + + protected LinkedHashSet hwcFiles = Sets.newLinkedHashSet(); + + protected List outputFiles = Lists.newArrayList(); + + protected List userTemplates = Lists.newArrayList(); + + /** + * Constructor for de.monticore.generating.templateengine.reporting.reporter. + * InputOutputFilesReporter + * + * @param outputDir + */ + public InputOutputFilesReporter(String outputDir) { + super(outputDir + File.separator, + SIMPLE_FILE_NAME, ReportingConstants.REPORT_FILE_EXTENSION); + this.outputDirectory = outputDir; + } + + final String outputDirectory; + + public static final String INPUT_FILE_HEADING = "========================================================== Input files"; + + protected void writeInputFileHeading() { + writeLine(INPUT_FILE_HEADING); + } + + public static final String USER_TEMPLATE_HEADING = "====================================================== User templates"; + + protected void writeUserTemplateHeading() { + writeLine(USER_TEMPLATE_HEADING); + } + + public static final String HWC_FILE_HEADING = "====================================================== Handwritten files"; + + protected void writeHWCFileHeading() { + writeLine(HWC_FILE_HEADING); + } + + public static final String OUTPUT_FILE_HEADING = "========================================================== Output files"; + + protected void writeOutputFileHeading() { + writeLine(OUTPUT_FILE_HEADING); + } + + public static final String FOOTER_HEADING = "========================================================== Explanation"; + + protected void writeFooter() { + writeLine(FOOTER_HEADING); + writeLine("This report is used to enable incremental generation"); + writeLine("Input files: the list of input files ordered by their paths."); + writeLine("Types of input files are:"); + writeLine("- Model files"); + writeLine("- Handwritten sourcecode files"); + writeLine("- User specific script files"); + writeLine("- User specific template files"); + writeLine("Output files: the list of generated output files ordered by their paths."); + writeLine("(EOF)"); + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.IReportEventHandler#reportFileCreation(java.lang.String, + * java.lang.String, java.lang.String, de.monticore.ast.ASTNode) + */ + @Override + public void reportFileCreation(String templatename, + String qualifiedfilename, String fileextension, ASTNode ast) { + String filePath = qualifiedfilename.replaceAll("\\.", Matcher.quoteReplacement(File.separator)); + outputFiles.add(filePath + "." + fileextension); + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.IReportEventHandler#reportFileCreation(java.lang.String, + * java.lang.String, java.lang.String, de.monticore.ast.ASTNode) + */ + @Override + public void reportFileCreation(Path parentPath, Path file) { + if (file.compareTo(qualifiedInputFile) == 0) { + return; + } + String filePath = parentPath != null + ? parentPath.toString() + PARENT_FILE_SEPARATOR + file.toString() + : file.toString(); + outputFiles.add(filePath); + } + + public static final String PARENT_FILE_SEPARATOR = " sub "; + + public static final String INPUT_STATE_SEPARATOR = " state "; + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportOpenInputFile(java.nio.file.Path) + */ + @Override + public void reportOpenInputFile(Optional parentPath, Path file) { + if (file.compareTo(qualifiedInputFile) == 0) { + return; + } + String toAdd = ""; + if (parentPath.isPresent()) { + toAdd = parentPath.get().toString() + PARENT_FILE_SEPARATOR + file.toString(); + modelToArtifactMap.put(file, parentPath.get()); + } + else { + if (modelToArtifactMap.keySet().contains(file)) { + toAdd = modelToArtifactMap.get(file).toString() + PARENT_FILE_SEPARATOR + + file.toString(); + } + else { + filesThatMatterButAreNotThereInTime.add(file); + } + } + if (!toAdd.isEmpty() && !inputFiles.contains(toAdd)) { + inputFiles.add(toAdd); + } + } + + protected Set filesThatMatterButAreNotThereInTime = new LinkedHashSet<>(); + + // TODO: think about when to clean this up + protected static Map modelToArtifactMap = new HashMap<>(); + + // TODO: see todo above :-) + public static void resetModelToArtifactMap() { + modelToArtifactMap = new HashMap<>(); + } + + protected String inputFile; + + protected Path qualifiedInputFile; + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportParseInputFile(java.nio.file.Path, + * java.lang.String) + */ + @Override + public void reportParseInputFile(Path inputFilePath, String modelName) { + // IMPORTANT + // this entirely resets the gathered information, hence the corresponding + // event reportParseInputFile must only be called once for each actual input + // file, i.e., the things that are parsed + String lowerCaseName = modelName.replaceAll("\\.", "/").toLowerCase(); + this.reportingHelper = new ReportCreator(outputDirectory + File.separator + lowerCaseName); + inputFiles.clear(); + hwcFiles.clear(); + outputFiles.clear(); + userTemplates.clear(); + filesThatMatterButAreNotThereInTime.clear(); + inputFile = inputFilePath.toString(); + + qualifiedInputFile = Paths.get(lowerCaseName + "." + + Files.getFileExtension(inputFilePath.getFileName().toString())); + + Path parent = inputFilePath.subpath(0, + inputFilePath.getNameCount() - qualifiedInputFile.getNameCount()); + parent = inputFilePath.getRoot().resolve(parent); + + modelToArtifactMap.put(qualifiedInputFile, parent); + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportUseHandwrittenCodeFile(java.nio.file.Path, + * java.nio.file.Path) + */ + @Override + public void reportUseHandwrittenCodeFile(Path parentDir, Path fileName) { + Optional parent = Optional.ofNullable(parentDir); + String hwcLine = ""; + if (parent.isPresent()) { + Path parentPath = parent.get().subpath(0, + parent.get().getNameCount() - fileName.getNameCount()); + parentPath = parent.get().getRoot().resolve(parentPath); + hwcLine = parentPath.toString(); + } + hwcLine = hwcLine.concat(PARENT_FILE_SEPARATOR).concat(fileName.toString()); + hwcFiles.add(hwcLine); + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportUserSpecificTemplate(java.nio.file.Path, + * java.nio.file.Path) + */ + @Override + public void reportUserSpecificTemplate(Path parentDir, Path fileName) { + if (parentDir != null) { + userTemplates.add(parentDir.toString() + PARENT_FILE_SEPARATOR + fileName.toString()); + } + } + + public static final String MISSING = "not found"; + + public static final String GEN_ERROR = "error during generation"; + + protected void writeContent(ASTNode ast) { + + // the magic TODO AHo: document + for (Path lateOne : filesThatMatterButAreNotThereInTime) { + if (modelToArtifactMap.keySet().contains(lateOne)) { + String toAdd = modelToArtifactMap.get(lateOne).toString() + PARENT_FILE_SEPARATOR + + lateOne.toString(); + if (!inputFiles.contains(toAdd)) { + inputFiles.add(toAdd); + } + } + } + + Collections.sort(inputFiles); + Collections.sort(outputFiles); + + if (inputFile != null && !inputFile.isEmpty()) { + String checkSum; + if (ast != null) { + checkSum = ReportingHelper.getChecksum(inputFile); + writeLine(inputFile + INPUT_STATE_SEPARATOR + checkSum); + } + else { + writeLine(inputFile + INPUT_STATE_SEPARATOR + GEN_ERROR); + } + + for (String s : inputFiles) { + if (s.contains(PARENT_FILE_SEPARATOR)) { + String[] elements = s.split(PARENT_FILE_SEPARATOR); + if (elements[0].endsWith(".jar")) { + String inputFile = elements[0].concat("!" + File.separator).concat(elements[1]); + try { + String url = "jar:file:" + inputFile; + url = url.replaceAll("\\" + File.separator, "/"); + URL input = new URL(url); + String inputModel = CharStreams.toString(new InputStreamReader(input.openStream())); + + MessageDigest md = MessageDigest.getInstance("MD5"); + md.update(inputModel.getBytes()); + String digest = Hashing.md5().hashString(inputModel, StandardCharsets.UTF_8) + .toString(); + + writeLine(s + INPUT_STATE_SEPARATOR + digest); + } + catch (IOException | NoSuchAlgorithmException e) { + Log.warn("0xA0134 Cannot write to log file", e); + } + + } + else { + File inputFile = new File(elements[0].concat(File.separator).concat(elements[1])); + if (inputFile.exists()) { + checkSum = ReportingHelper.getChecksum(inputFile.toString()); + writeLine(s + INPUT_STATE_SEPARATOR + checkSum); + } + else { + writeLine(s + INPUT_STATE_SEPARATOR + MISSING); + } + } + } + else { + checkSum = ReportingHelper.getChecksum(s); + writeLine(s + INPUT_STATE_SEPARATOR + checkSum); + } + } + + writeUserTemplateHeading(); + for (String s : userTemplates) { + if (s.contains(PARENT_FILE_SEPARATOR)) { + String[] elements = s.split(PARENT_FILE_SEPARATOR); + File inputFile = new File(elements[0].concat(File.separator).concat(elements[1])); + if (inputFile.exists()) { + checkSum = ReportingHelper.getChecksum(inputFile.toString()); + writeLine(s + INPUT_STATE_SEPARATOR + checkSum); + } + else { + writeLine(s + INPUT_STATE_SEPARATOR + MISSING); + } + } + else { + writeLine(s + INPUT_STATE_SEPARATOR + MISSING); + } + } + + writeHWCFileHeading(); + for (String hwc : hwcFiles) { + writeLine(hwc); + } + + writeOutputFileHeading(); + for (String s : outputFiles) { + writeLine(s); + } + } + } + + @Override + public void flush(ASTNode ast) { + writeContent(ast); + writeFooter(); + super.flush(ast); + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.AReporter#writeHeader() + */ + @Override + protected void writeHeader() { + writeInputFileHeading(); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/InstantiationsReporter.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/InstantiationsReporter.java new file mode 100644 index 0000000000..518ea0da56 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/InstantiationsReporter.java @@ -0,0 +1,76 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.reporter; + +import com.google.common.collect.Maps; +import de.monticore.ast.ASTNode; +import de.monticore.generating.templateengine.reporting.commons.AReporter; +import de.monticore.generating.templateengine.reporting.commons.Layouter; +import de.monticore.generating.templateengine.reporting.commons.ReportingConstants; +import de.se_rwth.commons.Names; + +import java.io.File; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +/** + */ +public class InstantiationsReporter extends AReporter { + + static final String SIMPLE_FILE_NAME = "06_Instantiations"; + + protected Map instantiateCount = Maps.newTreeMap(); + + public InstantiationsReporter(String outputDir, String modelName) { + super(outputDir + + File.separator + modelName, SIMPLE_FILE_NAME, + ReportingConstants.REPORT_FILE_EXTENSION); + } + + @Override + protected void writeHeader() { + writeLine("========================================================== Instantiations"); + writeLine("#Instantiations JavaType"); + } + + protected void writeContent() { + for (Entry entry : instantiateCount.entrySet()) { + String s = entry.getValue() + "x"; + writeLine(s + Layouter.getSpaceString(17 - s.length()) + entry.getKey()); + } + } + + protected void writeFooter() { + writeLine("========================================================== Explanation"); + writeLine("This is the list of instantiated java type (triggered by the TC)."); + writeLine("- #Instantiations: how often an object of the corresponding type has"); + writeLine(" been instantiated."); + writeLine("(EOF)"); + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.IReportEventHandler#reportInstantiate(java.lang.String, + * java.util.List) + */ + @Override + public void reportInstantiate(String className, List params) { + className = Names.getSimpleName(className); + if (instantiateCount.containsKey(className)) { + Integer actualCount = instantiateCount.get(className); + instantiateCount.put(className, actualCount + 1); + } + else { + instantiateCount.put(className, 1); + } + } + + @Override + public void flush(ASTNode ast) { + writeContent(); + writeFooter(); + instantiateCount.clear(); + super.flush(ast); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/InvolvedFilesReporter.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/InvolvedFilesReporter.java new file mode 100644 index 0000000000..44cdc7e400 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/InvolvedFilesReporter.java @@ -0,0 +1,182 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.reporter; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.common.io.Files; +import de.monticore.ast.ASTNode; +import de.monticore.generating.templateengine.reporting.commons.AReporter; +import de.monticore.generating.templateengine.reporting.commons.Layouter; +import de.monticore.generating.templateengine.reporting.commons.ReportCreator; +import de.monticore.generating.templateengine.reporting.commons.ReportingConstants; + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; + +/** + * It reports the name of all input and output files as well as the name of + * files which existence was checked. + * + */ +public class InvolvedFilesReporter extends AReporter { + + public static final String SIMPLE_FILE_NAME = "18_InvolvedFiles"; + + public static final String INDENT = Layouter.getSpaceString(40); + + public static final String PARENT_FILE_SEPARATOR = "!/"; + + protected List inputFiles = Lists.newArrayList(); + + protected List outputFiles = Lists.newArrayList(); + + protected Set checkedFiles = Sets.newHashSet(); + + protected Map modelToArtifactMap = new HashMap<>(); + + protected String outputDirectory = ""; + + protected Optional inputFile = Optional.empty(); + + protected Optional qualifiedInputFile = Optional.empty(); + + /** + * Constructor for de.monticore.generating.templateengine.reporting.reporter. + * FilesReporter + * + * @param outputDir + */ + public InvolvedFilesReporter(String outputDir) { + super(outputDir + File.separator, + SIMPLE_FILE_NAME, ReportingConstants.REPORT_FILE_EXTENSION); + this.outputDirectory = outputDir; + } + + public static final String INPUT_FILE_HEADING = "================================= Parsed Input file =================="; + + protected void writeInputFileHeading() { + writeLine(INPUT_FILE_HEADING); + } + + public static final String INPUT_FILES_HEADING = "\n================================= Input files ========================"; + + protected void writeInputFilesHeading() { + writeLine(INPUT_FILES_HEADING); + } + + public static final String OUTPUT_FILE_HEADING = "\n================================= Output files ======================="; + + protected void writeOutputFileHeading() { + writeLine(OUTPUT_FILE_HEADING); + } + + public static final String HWC_FILE_HEADING = "\n================================= Handwritten files ============"; + + protected void writeHWCFileHeading() { + writeLine(HWC_FILE_HEADING); + } + + public static final String EOF = "\n================================= EOF ================================="; + + protected void writeFooter() { + writeLine(EOF); + } + + @Override + public void reportFileCreation(String fileName) { + if (!outputFiles.contains(fileName)) { + outputFiles.add(fileName); + } + } + + @Override + public void reportUseHandwrittenCodeFile(Path parentDir, Path fileName) { + checkedFiles.add(parentDir.toString()); + } + + @Override + public void reportOpenInputFile(Optional parentPath, Path file) { + if (qualifiedInputFile.isPresent() && qualifiedInputFile.get().compareTo(file) == 0) { + return; + } + String toAdd = ""; + if (parentPath.isPresent()) { + toAdd = parentPath.get().toString() + PARENT_FILE_SEPARATOR + file.toString(); + modelToArtifactMap.put(file, parentPath.get()); + } + else if (modelToArtifactMap.keySet().contains(file)) { + toAdd = modelToArtifactMap.get(file).toString() + PARENT_FILE_SEPARATOR + + file.toString(); + } + toAdd = format(toAdd); + if (!toAdd.isEmpty() && !inputFiles.contains(toAdd)) { + inputFiles.add(toAdd); + } + } + + @Override + public void reportOpenInputFile(String fileName) { + fileName = format(fileName); + if (!inputFiles.contains(fileName)) { + inputFiles.add(fileName); + } + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportParseInputFile(java.nio.file.Path, + * java.lang.String) + */ + @Override + public void reportParseInputFile(Path inputFilePath, String modelName) { + this.reportingHelper = new ReportCreator(outputDirectory + File.separator + modelName.toLowerCase()); + inputFiles.clear(); + outputFiles.clear(); + checkedFiles.clear(); + modelToArtifactMap.clear(); + inputFile = Optional.of(inputFilePath.toString()); + + qualifiedInputFile = Optional.of(Paths.get(modelName.replaceAll("\\.", "/").toLowerCase() + "." + + Files.getFileExtension(inputFilePath.getFileName().toString()))); + + Path parent = inputFilePath.subpath(0, + inputFilePath.getNameCount() - qualifiedInputFile.get().getNameCount()); + parent = inputFilePath.getRoot().resolve(parent); + modelToArtifactMap.put(qualifiedInputFile.get(), parent); + } + + @Override + public void flush(ASTNode ast) { + writeContent(); + writeFooter(); + super.flush(ast); + } + + @Override + protected void writeHeader() { + writeInputFileHeading(); + if (inputFile.isPresent()) { + writeLine(inputFile.get()); + } + } + + protected String format(String fileName) { + fileName = fileName.replace('\\', '/'); + return fileName.startsWith("file:/") ? fileName.substring(6) : fileName; + } + + protected void writeContent() { + writeInputFilesHeading(); + inputFiles.forEach(f -> writeLine(f)); + + Collections.sort(outputFiles); + writeOutputFileHeading(); + outputFiles.forEach(f -> writeLine(f)); + + writeHWCFileHeading(); + checkedFiles.forEach(f -> writeLine(f)); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/NodeTreeDecoratedReporter.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/NodeTreeDecoratedReporter.java new file mode 100644 index 0000000000..7986d8d79c --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/NodeTreeDecoratedReporter.java @@ -0,0 +1,275 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.reporter; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import de.monticore.ast.ASTNode; +import de.monticore.generating.templateengine.CodeHookPoint; +import de.monticore.generating.templateengine.HookPoint; +import de.monticore.generating.templateengine.StringHookPoint; +import de.monticore.generating.templateengine.TemplateHookPoint; +import de.monticore.generating.templateengine.reporting.commons.*; +import de.monticore.visitor.ITraverser; +import de.se_rwth.commons.Names; + +import java.io.File; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +/** + */ +public class NodeTreeDecoratedReporter extends AReporter { + + static final String INSTANTIATE_JAVA_CLASS = "inst"; + + static final String GENERATES_FILE = "generates"; + + static final String USED_TEMPLATE = "template"; + + static final String TEMPLATE_HOOKPOINT = "THP"; + + static final String SPECIFIC_TEMPLATE_HOOKPOINT = "ATHP"; + + static final String SPECIFIC_STRING_HOOKPOINT = "ASHP"; + + static final String SPECIFIC_CODE_HOOKPOINT = "ACHP"; + + static final String SIMPLE_FILE_NAME = "11_NodeTreeDecorated"; + + protected ReportingRepository repository; + + protected Map nodeVisits; + + protected Map> astNodeExtraInfos; + + protected List serializedTreeResult; + + protected ITraverser traverser; + + protected TreePrintVisitor tpv; + + public NodeTreeDecoratedReporter(String outputDir, String modelName, + ReportingRepository repository, ITraverser traverser) { + super(outputDir + + File.separator + modelName, SIMPLE_FILE_NAME, + ReportingConstants.REPORT_FILE_EXTENSION); + this.repository = repository; + nodeVisits = Maps.newHashMap(); + astNodeExtraInfos = Maps.newHashMap(); + serializedTreeResult = Lists.newArrayList(); + this.traverser = traverser; + this.tpv = new TreePrintVisitor(); + tpv.setRepo(repository); + traverser.add4IVisitor(tpv); + } + + @Override + protected void writeHeader() { + writeLine("========================================================== Node Tree + Extra Infos"); + } + + protected void writeContent(ASTNode ast) { + if (ast == null) { + return; + } + + deriveTreeStructureAST(ast); + for (String s : serializedTreeResult) { + writeLine(s); + } + } + + protected void writeFooter() { + writeLine("========================================================== Explanation"); + writeLine("Node Tree: this is the extended form: one with extra infos"); + writeLine("as sublines. The tree itself lists all AST nodes using their identifiers."); + writeLine("Vertical line: list all the direct children of a node."); + writeLine("Each node knows about:"); + writeLine("- #Visits through templates (in the raw version) looks like: (2x)"); + writeLine("- files generated originating from this node"); + writeLine("- templates called on this node"); + writeLine("Remark: a \"null\" as value in the tree means that the tree has been altered"); + writeLine("after parsing. --> this is not yet reflected in the protocol"); + writeLine("(EOF)"); + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportCallHookPointStart(java.lang.String, + * de.monticore.generating.templateengine.HookPoint, de.monticore.ast.ASTNode) + */ + @Override + public void reportCallHookPointStart(String hookName, HookPoint hp, ASTNode ast) { + callHP(hp, ast); + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportTemplateStart(java.lang.String, + * de.monticore.ast.ASTNode) + */ + @Override + public void reportTemplateStart(String templatename, ASTNode ast) { + String aident = compactStr(ast); + MapUtil.incMapValue(nodeVisits, aident); + MapUtil.addToListMap(astNodeExtraInfos, aident, USED_TEMPLATE + " " + + ReportingHelper.getTemplateName(templatename)); + } + + @Override + public void reportFileCreation(String templatename, + String qualifiedfilename, String fileextension, ASTNode ast) { + String aident = compactStr(ast); + if (qualifiedfilename != null) { + qualifiedfilename = Names.getSimpleName(qualifiedfilename); + } + MapUtil.incMapValue(nodeVisits, aident); + MapUtil.addToListMap(astNodeExtraInfos, aident, GENERATES_FILE + " \"" + + qualifiedfilename + "." + fileextension + "\""); + } + + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportCallAfterHookPoint(java.lang.String, + * java.util.Collection, de.monticore.ast.ASTNode) + */ + @Override + public void reportCallAfterHookPoint(String oldTemplate, Collection afterHPs, + ASTNode ast) { + callHPS(oldTemplate, afterHPs, ast); + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportCallBeforeHookPoint(java.lang.String, + * java.util.Collection, de.monticore.ast.ASTNode) + */ + @Override + public void reportCallBeforeHookPoint(String oldTemplate, Collection beforeHPs, + ASTNode ast) { + callHPS(oldTemplate, beforeHPs, ast); + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportCallReplacementHookPoint(java.lang.String, + * java.util.List, de.monticore.ast.ASTNode) + */ + @Override + public void reportCallReplacementHookPoint(String oldTemplate, List hps, ASTNode ast) { + callHPS(oldTemplate, hps, ast); + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportCallSpecificReplacementHookPoint(java.lang.String, + * java.util.List, de.monticore.ast.ASTNode) + */ + @Override + public void reportCallSpecificReplacementHookPoint(String oldTemplate, List hps, + ASTNode ast) { + callSpecificHPS(oldTemplate, hps, ast); + } + + /** + * Uses ast2idents to print a compact version of the ASTNode + * + * @param ast + * @return + */ + public String compactStr(ASTNode ast) { + return repository.getASTNodeNameFormatted(ast); + } + + /** + * derive the tree structure of the AST using the ast idents + some decoration + * coming from + * + * @param ast + * @return + */ + protected void deriveTreeStructureAST(ASTNode ast) { + + // this is a decoration of the tree at the lineend + Map endLineDecoration = Maps.newHashMap(); + + for (Entry entry : nodeVisits.entrySet()) { + endLineDecoration.put(entry.getKey(), "(" + entry.getValue() + "x)"); + } + + tpv.clear(); + tpv.setRepo(repository); + tpv.setEndLineDecoration(endLineDecoration); + tpv.setAstNodeExtraInfos(astNodeExtraInfos); + ast.accept(traverser); + + serializedTreeResult = tpv.getTreeResult(); + + } + + protected void resetVariables() { + nodeVisits.clear(); + astNodeExtraInfos.clear(); + serializedTreeResult.clear(); + } + + protected void callSpecificHPS(String oldTemplate, List hps, ASTNode ast) { + for (HookPoint hp : hps) { + callSpecificHP(hp, ast); + } + + } + + protected void callSpecificHP(HookPoint hp, ASTNode ast) { + if (hp != null) { + if (hp instanceof TemplateHookPoint) { + String aident = compactStr(ast); + MapUtil.incMapValue(nodeVisits, aident); + MapUtil.addToListMap(astNodeExtraInfos, aident, SPECIFIC_TEMPLATE_HOOKPOINT + " " + + getHookPointValue(hp)); + } + } + } + + protected void callHPS(String oldTemplate, Collection hps, ASTNode ast) { + for (HookPoint hp : hps) { + callHP(hp, ast); + } + } + + protected void callHP(HookPoint hp, ASTNode ast) { + if (hp != null) { + if (hp instanceof TemplateHookPoint) { + String aident = compactStr(ast); + MapUtil.incMapValue(nodeVisits, aident); + MapUtil.addToListMap(astNodeExtraInfos, aident, TEMPLATE_HOOKPOINT + " " + + getHookPointValue(hp)); + } + } + + } + + protected String getHookPointValue(HookPoint hp) { + String value = null; + if (hp != null && hp instanceof TemplateHookPoint) { + value = ((TemplateHookPoint) hp).getTemplateName(); + value = ReportingHelper.getTemplateName(value); + } + else if (hp != null && hp instanceof StringHookPoint) { + value = ((StringHookPoint) hp).getValue(); + value = ReportingHelper.formatStringToReportingString(value, 50); + } + else if (hp != null && hp instanceof CodeHookPoint) { + value = ((CodeHookPoint) hp).getClass().getName(); + value = Names.getSimpleName(value); + } + return value; + } + + @Override + public void flush(ASTNode ast) { + writeContent(ast); + writeFooter(); + resetVariables(); + super.flush(ast); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/NodeTreeReporter.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/NodeTreeReporter.java new file mode 100644 index 0000000000..fa9734d369 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/NodeTreeReporter.java @@ -0,0 +1,214 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.reporter; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import de.monticore.ast.ASTNode; +import de.monticore.generating.templateengine.HookPoint; +import de.monticore.generating.templateengine.TemplateHookPoint; +import de.monticore.generating.templateengine.reporting.commons.*; +import de.monticore.visitor.ITraverser; + +import java.io.File; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +/** + */ +public class NodeTreeReporter extends AReporter { + + static final String SIMPLE_FILE_NAME = "10_NodeTree"; + + protected ReportingRepository repository; + + protected Map nodeVisits; + + protected List serializedTreeResult; + + protected ITraverser traverser; + + protected TreePrintVisitor tpv; + + public NodeTreeReporter(String outputDir, + String modelName, ReportingRepository repository, ITraverser traverser) { + super(outputDir + File.separator + + modelName, + SIMPLE_FILE_NAME, ReportingConstants.REPORT_FILE_EXTENSION); + this.repository = repository; + nodeVisits = Maps.newHashMap(); + serializedTreeResult = Lists.newArrayList(); + this.traverser = traverser; + tpv = new TreePrintVisitor(); + tpv.setRepo(repository); + traverser.add4IVisitor(tpv); + + } + + @Override + protected void writeHeader() { + writeLine("========================================================== Node Tree + Extra Infos"); + } + + protected void writeContent(ASTNode ast) { + if (ast == null) { + return; + } + deriveTreeStructureAST(ast); + for (String s : serializedTreeResult) { + writeLine(s); + } + } + + protected void writeFooter() { + writeLine("========================================================== Explanation"); + writeLine("Node Tree: this is the extended form: one with extra infos"); + writeLine("as sublines. The tree itself lists all AST nodes using their identifiers."); + writeLine("Vertical line: list all the direct children of a node."); + writeLine("Each node knows about:"); + writeLine("- #Visits through templates (in the raw version) looks like: (2x)"); + writeLine("- files generated originating from this node"); + writeLine("- templates called on this node"); + writeLine("Remark: a \"null\" as value in the tree means that the tree has been altered"); + writeLine("after parsing. --> this is not yet reflected in the protocol"); + writeLine("(EOF)"); + } + + @Override + public void reportTemplateStart(String templatename, ASTNode ast) { + String aident = compactStr(ast); + MapUtil.incMapValue(nodeVisits, aident); + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportCallAfterHookPoint(java.lang.String, + * java.util.Collection, de.monticore.ast.ASTNode) + */ + @Override + public void reportCallAfterHookPoint(String oldTemplate, Collection afterHPs, + ASTNode ast) { + for (HookPoint hp : afterHPs) { + if (hp != null && hp instanceof TemplateHookPoint) { + String aident = compactStr(ast); + MapUtil.incMapValue(nodeVisits, aident); + } + } + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportCallBeforeHookPoint(java.lang.String, + * java.util.Collection, de.monticore.ast.ASTNode) + */ + @Override + public void reportCallBeforeHookPoint(String oldTemplate, Collection beforeHPs, + ASTNode ast) { + for (HookPoint hp : beforeHPs) { + if (hp != null && hp instanceof TemplateHookPoint) { + String aident = compactStr(ast); + MapUtil.incMapValue(nodeVisits, aident); + } + } + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportCallReplacementHookPoint(java.lang.String, + * java.util.List, de.monticore.ast.ASTNode) + */ + @Override + public void reportCallReplacementHookPoint(String oldTemplate, List hps, ASTNode ast) { + for (HookPoint hp : hps) { + if (hp != null && hp instanceof TemplateHookPoint) { + String aident = compactStr(ast); + MapUtil.incMapValue(nodeVisits, aident); + } + } + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportCallSpecificReplacementHookPoint(java.lang.String, + * java.util.List, de.monticore.ast.ASTNode) + */ + @Override + public void reportCallSpecificReplacementHookPoint(String oldTemplate, List hps, + ASTNode ast) { + for (HookPoint hp : hps) { + if (hp != null && hp instanceof TemplateHookPoint) { + String aident = compactStr(ast); + MapUtil.incMapValue(nodeVisits, aident); + } + } + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportCallHookPointStart(java.lang.String, + * de.monticore.generating.templateengine.HookPoint, de.monticore.ast.ASTNode) + */ + @Override + public void reportCallHookPointStart(String hookName, HookPoint hp, ASTNode ast) { + if (hp != null && hp instanceof TemplateHookPoint) { + String aident = compactStr(ast); + MapUtil.incMapValue(nodeVisits, aident); + } + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportFileCreation(java.lang.String, + * java.lang.String, java.lang.String, de.monticore.ast.ASTNode) + */ + @Override + public void reportFileCreation(String templatename, String qualifiedfilename, + String fileextension, ASTNode ast) { + String aident = compactStr(ast); + MapUtil.incMapValue(nodeVisits, aident); + } + + /** + * Uses ast2idents to print a compact version of the ASTNode + * + * @param ast + * @return + */ + public String compactStr(ASTNode ast) { + return repository.getASTNodeNameFormatted(ast); + } + + /** + * derive the tree structure of the AST using the ast idents + some decoration + * coming from + * + * @param ast + * @return + */ + protected void deriveTreeStructureAST(ASTNode ast) { + + // this is a decoration of the tree at the lineend + Map endLineDecoration = Maps.newHashMap(); + + for (Entry entry : nodeVisits.entrySet()) { + endLineDecoration.put(entry.getKey(), "(" + entry.getValue() + "x)"); + } + + tpv.clear(); + tpv.setRepo(repository); + tpv.setEndLineDecoration(endLineDecoration); + ast.accept(traverser); + serializedTreeResult = tpv.getTreeResult(); + + } + + protected void resetVariables() { + nodeVisits.clear(); + serializedTreeResult.clear(); + } + + @Override + public void flush(ASTNode ast) { + writeContent(ast); + writeFooter(); + resetVariables(); + super.flush(ast); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/NodeTypesReporter.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/NodeTypesReporter.java new file mode 100644 index 0000000000..e1255bf273 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/NodeTypesReporter.java @@ -0,0 +1,169 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.reporter; + +import com.google.common.collect.Maps; +import de.monticore.ast.ASTNode; +import de.monticore.generating.templateengine.reporting.commons.*; +import de.monticore.visitor.ITraverser; +import de.se_rwth.commons.SourcePosition; + +import java.io.File; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +/** + */ +public class NodeTypesReporter extends AReporter { + + static final String SIMPLE_FILE_NAME = "12_TypesOfNodes"; + + protected Map nodeTypeCount = Maps.newTreeMap(); + + protected Map nodeTypeCountPos = Maps.newTreeMap(); + + protected ITraverser traverser; + + protected ObjectCountVisitor ocv; + + public NodeTypesReporter(String outputDir, String modelName, ITraverser traverser) { + super(outputDir + + File.separator + modelName, SIMPLE_FILE_NAME, + ReportingConstants.REPORT_FILE_EXTENSION); + this.traverser = traverser; + this.ocv = new ObjectCountVisitor(); + traverser.add4IVisitor(ocv); + } + + @Override + protected void writeHeader() { + writeLine("======================================== Types of Nodes (all)"); + writeLine("#Objects #Visits Nonterminal-Name"); + } + + public void writeContent(ASTNode ast) { + if (ast == null) { + return; + } + + ocv.clear(); + ast.accept(traverser); + Map type2count = ocv.getObjectCountMap(); + + writeMaps(nodeTypeCount, type2count); + + writeLine("======================================== Types of Nodes (With Source Pos)"); + writeLine("#Objects #Visits Nonterminal-Name"); + Map type2countPos = ocv.getObjectCountMapPos(); + + writeMaps(nodeTypeCountPos, type2countPos); + + writeLine("======================================== Types of Nodes (No Source Pos)"); + writeLine("#Objects #Visits Nonterminal-Name"); + + Map nodeTypeCountNoPos = getMapDiff(nodeTypeCount, nodeTypeCountPos); + Map type2countNoPos = getMapDiff(type2count, type2countPos); + + writeMaps(nodeTypeCountNoPos, type2countNoPos); + } + + /** + * @param nodeTypeCount2: Map contains all ASTNodes + * @param nodetypeCountPos2: Map contains ASTNodes with a source position + * @return Map containing ASTNodes without source position + */ + protected Map getMapDiff(Map nodeTypeCount2, + Map nodetypeCountPos2) { + Map dif = Maps.newTreeMap(); + // merging keys of objects and visits + Set allKeys = new TreeSet(); + allKeys.addAll(nodetypeCountPos2.keySet()); + allKeys.addAll(nodeTypeCount2.keySet()); + for (String key : allKeys) { + int val1, val2; + if (nodeTypeCount2.containsKey(key)) { + val1 = nodeTypeCount2.get(key); + } + else { + val1 = 0; + } + if (nodetypeCountPos2.containsKey(key)) { + val2 = nodetypeCountPos2.get(key); + } + else { + val2 = 0; + } + dif.put(key, val1 - val2); + } + return dif; + } + + /** + * helper method: print these 2 maps the same way + * + * @param nodeTypeCount2 + * @param type2count + */ + protected void writeMaps(Map nodeTypeCount2, Map type2count) { + // merging keys of objects and visits + Set allKeys = new TreeSet(); + allKeys.addAll(type2count.keySet()); + allKeys.addAll(nodeTypeCount2.keySet()); + + for (String key : allKeys) { + String objectCount, s; + if (!type2count.containsKey(key)) { + objectCount = "0x"; + } + else { + objectCount = type2count.get(key) + "x"; + } + if (!nodeTypeCount2.containsKey(key)) { + s = "0x"; + } + else { + s = nodeTypeCount2.get(key) + "x"; + } + // evading 0x objects 0x visits line + if (!("0x".equals(s) && "0x".equals(objectCount))) { + writeLine(objectCount + + Layouter.getSpaceString(10 - objectCount.length()) + s + + Layouter.getSpaceString(9 - s.length()) + key); + } + } + + } + + protected void writeFooter() { + writeLine("========================================================== Explanation"); + writeLine("Types of Nodes: Shows a List of all AST-Node-Types that occur in the final AST."); + writeLine("Types of Nodes (with Source Position): Shows the subset of all AST Node Types"); + writeLine("which occurred in the initial AST after the parsing step."); + writeLine("Types of Nodes (without Source Position): Shows the subset of all AST Node Types"); + writeLine("which have been added to the initial AST after the parsing step (e.g. by model"); + writeLine("transformation)."); + writeLine("For each AST type entry in the lists the following information is reported: "); + writeLine(" - #Objects: number of its instances of the corresponding AST type"); + writeLine(" - #Visits: how often nodes of that type have been visited by a"); + writeLine(" call/includeTemplate"); + writeLine("(EOF)"); + } + + @Override + public void reportTemplateStart(String templatename, ASTNode ast) { + String key = Layouter.nodeName(ast); + MapUtil.incMapValue(nodeTypeCount, key); + if (!ast.get_SourcePositionStart().equals(SourcePosition.getDefaultSourcePosition())) { + MapUtil.incMapValue(nodeTypeCountPos, key); + } + } + + @Override + public void flush(ASTNode ast) { + writeContent(ast); + writeFooter(); + nodeTypeCount.clear(); + super.flush(ast); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/StatisticsReporter.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/StatisticsReporter.java new file mode 100644 index 0000000000..e8ee09d538 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/StatisticsReporter.java @@ -0,0 +1,96 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.generating.templateengine.reporting.reporter; + +import de.monticore.ast.ASTNode; +import de.monticore.generating.templateengine.reporting.commons.ReportingRepository; +import de.monticore.generating.templateengine.reporting.commons.StatisticsHandler; +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.symboltable.serialization.json.JsonElement; +import de.monticore.symboltable.serialization.json.JsonNumber; +import de.monticore.symboltable.serialization.json.JsonObject; +import de.monticore.symboltable.serialization.json.UserJsonString; +import de.monticore.visitor.ITraverser; +import de.se_rwth.commons.logging.Log; + +import java.time.Duration; +import java.time.Instant; +import java.util.Properties; +import java.util.UUID; + +/** + * Reporter, which sends its result to a remote server. + * This is used to track calls of the MontiCore jar + */ +public class StatisticsReporter extends SummaryReporter { + static final String SIMPLE_FILE_NAME = "20_Statistics"; + protected String STAT_TYPE; + protected Instant startTime; + + protected JsonObject report = new JsonObject(); // Main JSON, which is sent to server + protected JsonObject summary = new JsonObject(); + + + protected StatisticsReporter(String outputDir, String modelName, ReportingRepository repository, ITraverser traverser) { + super(outputDir, modelName, repository, traverser); + this.qualifiedFileName = SIMPLE_FILE_NAME; + startTime = Instant.now(); + + report.putMember("uuid", new UserJsonString(UUID.randomUUID().toString())); + putVersion(); + } + + public StatisticsReporter(JsonElement parameter, String stat_type, String outputDir, String modelName, ReportingRepository repository, ITraverser traverser) { + this(outputDir, modelName, repository, traverser); + STAT_TYPE = stat_type; + report.putMember("parameter", parameter); + StatisticsHandler.storeReport(report.print(new IndentPrinter()), "PRE_" + STAT_TYPE); + } + + @Override + protected void writeSummaryLine(String string, int number) { + summary.putMember(string, new JsonNumber("" + number)); + } + + @Override + protected void writeHeader() { + // Do nothing, there should be no header + // Otherwise output is not valid JSON + } + + @Override + public void reportError(String msg){ + report.putMember("error", new UserJsonString(msg)); + super.reportError(msg); + } + + @Override + public void flush(ASTNode ast) { + writeContent(ast); // fills summary JSON-Object + report.putMember("summary", summary); + + report.putMember("duration", new JsonNumber(String.valueOf(Duration.between(startTime, Instant.now()).toMillis()))); + + + // Create JSON-String and write it to file + String report = this.report.print(new IndentPrinter()); + writeLine(report); + + // Store JSON-String in statistics + StatisticsHandler.storeReport(report, STAT_TYPE); + + // Cleanup + resetVariables(); + closeFile(); + } + + private void putVersion() { + try { + Properties properties = new Properties(); + properties.load(this.getClass().getResourceAsStream("/buildInfo.properties")); + report.putMember("version", new UserJsonString(properties.getProperty("version"))); + + } catch (Exception e) { + Log.debug("0xA9005 Could not find local properties file", this.getClass().getName()); + } + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/SuccessfulReporter.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/SuccessfulReporter.java new file mode 100644 index 0000000000..7c00da7909 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/SuccessfulReporter.java @@ -0,0 +1,45 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.reporter; + +import de.monticore.ast.ASTNode; +import de.monticore.generating.templateengine.reporting.commons.AReporter; +import de.monticore.generating.templateengine.reporting.commons.ReportingConstants; + +import java.io.File; + +public class SuccessfulReporter extends AReporter { + + static final String SIMPLE_FILE_NAME = "19_Successful"; + + public SuccessfulReporter(String outputDir, String modelName) { + super(outputDir + + File.separator + modelName, SIMPLE_FILE_NAME, + ReportingConstants.REPORT_FILE_EXTENSION); + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportModelStart(de.monticore.ast.ASTNode, java.lang.String, java.lang.String) + */ + @Override + public void reportModelStart(ASTNode ast, String modelName, String fileName) { + reportingHelper.deleteFile(SIMPLE_FILE_NAME, ReportingConstants.REPORT_FILE_EXTENSION); + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportModelEnd(java.lang.String, java.lang.String) + */ + @Override + public void reportModelEnd(String modelname, String filename) { + writeLine("#Successful generation"); + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.AReporter#writeHeader() + */ + @Override + protected void writeHeader() { + } + + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/SummaryReporter.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/SummaryReporter.java new file mode 100644 index 0000000000..acdb6a3396 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/SummaryReporter.java @@ -0,0 +1,588 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.reporter; + +import com.google.common.collect.Sets; +import de.monticore.ast.ASTNode; +import de.monticore.generating.templateengine.CodeHookPoint; +import de.monticore.generating.templateengine.HookPoint; +import de.monticore.generating.templateengine.StringHookPoint; +import de.monticore.generating.templateengine.TemplateHookPoint; +import de.monticore.generating.templateengine.reporting.commons.*; +import de.monticore.visitor.ITraverser; + +import java.io.File; +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +/** + */ +public class SummaryReporter extends AReporter { + + static final String NUM_TEMPLATE_INCLUDE = "template includes"; + + static final String NUM_TEMPLATE_WRITE = "template writes"; + + static final String NUM_GENERATED_FILES = "generated files"; + + static final String NUM_INSTANTIATIONS = "instantiations"; + + static final String NUM_VARIABLES = "variables"; + + static final String NUM_VARIABLE_ASSIGNMENTS = "variable assignments"; + + static final String NUM_WARNINGS = "warnings"; + + static final String NUM_USER_WARNINGS = "user warnings"; + + static final String NUM_ERRORS = "errors"; + + static final String NUM_USER_ERRORS = "user errors"; + + static final String NUM_INTERNAL_ERRORS = "internal errors"; + + static final String MAX_TEMPLATE_DEPTH = "max template depth"; + + static final String MAX_AST_DEPTH = "max AST depth"; + + static final String NUM_USED_TEMPLATES = "used standard templates"; + + static final String NUM_UNUSED_TEMPLATES = "unused standard templates"; + + static final String NUM_USED_HWTEMPLATES = "used handwritten templates"; + + static final String NUM_UNUSED_HWTEMPLATES = "unused handwritten templates"; + + static final String NUM_ASTNODE_VISITS = "AST node visits"; + + static final String NUM_ASTNODE_TYPES = "AST node types"; + + static final String NUM_ASTNODE_INSTANCES = "AST node instances"; + + static final String NUM_AST_SPECIFIC_REPLACEMENTS = "AST hook point sets"; + + static final String NUM_AST_SPECIFIC_CALLS = "AST hook point calls"; + + static final String NUM_SET_CODE_HOOKPOINTS = "code hook point sets"; + + static final String NUM_CALL_CODE_HOOKPOINTS = "code hook point calls"; + + static final String NUM_SET_TEMPLATE_HOOKPOINTS = "template hook point sets"; + + static final String NUM_CALL_TEMPLATE_HOOKPOINTS = "template hook point calls"; + + static final String NUM_SET_STRING_HOOKPOINTS = "string hook point sets"; + + static final String NUM_CALL_STRING_HOOKPOINTS = "string hook point calls"; + + static final String NUM_CALLS_EMPTY_HOOKPOINTS = "empty hook point calls"; + + static final String NUM_CALLED_EMPTY_HOOKPOINTS = "empty hook point called"; + + static final String SIMPLE_FILE_NAME = "01_Summary"; + + protected int numTemplateIncludes; + + protected int numTemplateWrites; + + protected int numGeneratedFiles; + + protected int numInstantiations; + + protected int numVariableAssignments; + + protected int numWarnings; + + protected int numUserWarnings; + + protected int numErrors; + + protected int numUserErrors; + + protected int numInternalErrors; + + protected int templateDepth; + + protected int maxTemplateDepth; + + protected int numASTNodeVisits; + + protected int numASTSpecificReplacements; + + protected int numASTSpecificCalls; + + protected int numSetCodeHookpoints; + + protected int numCallCodeHookpoints; + + protected int numSetTemplateHookpoints; + + protected int numCallTemplateHookpoints; + + protected int numSetStringHookpoints; + + protected int numCallStringHookpoints; + + protected int numCallsUnsetHookpoints; + + protected Set variableNames = new LinkedHashSet(); + + protected Set usedTemplates = Sets.newLinkedHashSet(); + + protected Set usedHWTemplates = Sets.newLinkedHashSet(); + + protected Set calledUnsetHookpoints = Sets.newLinkedHashSet(); + + protected ReportingRepository repository; + + protected ITraverser traverser; + + protected ObjectCountVisitor ocv; + + public SummaryReporter( + String outputDir, + String modelName, + ReportingRepository repository, + ITraverser traverser) { + super(outputDir + + File.separator + modelName, SIMPLE_FILE_NAME, + ReportingConstants.REPORT_FILE_EXTENSION); + resetVariables(); + this.repository = repository; + this.traverser = traverser; + this.ocv = new ObjectCountVisitor(); + traverser.add4IVisitor(ocv); + + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportWarning(java.lang.String) + */ + @Override + public void reportWarning(String message) { + numWarnings++; + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportUserWarning(java.lang.String) + */ + @Override + public void reportUserWarning(String message) { + numUserWarnings++; + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportError(java.lang.String) + */ + @Override + public void reportError(String message) { + numErrors++; + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportErrorUser(java.lang.String) + */ + @Override + public void reportErrorUser(String message) { + numUserErrors++; + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportErrorInternal(java.lang.String) + */ + @Override + public void reportErrorInternal(String message) { + numInternalErrors++; + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportFileCreation(java.lang.String, + * java.lang.String, java.lang.String, de.monticore.ast.ASTNode) + */ + @Override + public void reportFileCreation(String templatename, + String qualifiedfilename, String fileextension, ASTNode ast) { + numGeneratedFiles++; + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportSetValue(java.lang.String, + * java.lang.Object) + */ + @Override + public void reportSetValue(String name, Object value) { + variableNames.add(name); + numVariableAssignments++; + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportInstantiate(java.lang.String, + * java.util.List) + */ + @Override + public void reportInstantiate(String className, List params) { + numInstantiations++; + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportTemplateInclude(java.lang.String, + * de.monticore.ast.ASTNode) + */ + @Override + public void reportTemplateInclude(String templateName, ASTNode ast) { + numTemplateIncludes++; + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportTemplateWrite(java.lang.String, + * de.monticore.ast.ASTNode) + */ + @Override + public void reportTemplateWrite(String templateName, ASTNode ast) { + numTemplateWrites++; + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportTemplateStart(java.lang.String, + * de.monticore.ast.ASTNode) + */ + @Override + public void reportTemplateStart(String templatename, ASTNode ast) { + numASTNodeVisits++; + if (repository.getAllHWTemplateNames().contains(templatename)) { + usedHWTemplates.add(templatename); + } + else { + usedTemplates.add(templatename); + } + templateDepth++; + if (templateDepth > maxTemplateDepth) { + maxTemplateDepth = templateDepth; + } + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportTemplateEnd(java.lang.String, + * de.monticore.ast.ASTNode) + */ + @Override + public void reportTemplateEnd(String templatename, ASTNode ast) { + templateDepth--; + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportCallAfterHookPoint(java.lang.String, + * java.util.Collection, de.monticore.ast.ASTNode) + */ + @Override + public void reportCallAfterHookPoint(String oldTemplate, Collection afterHPs, + ASTNode ast) { + for (HookPoint hp : afterHPs) { + if (hp != null && hp instanceof CodeHookPoint) { + numCallCodeHookpoints++; + } + else if (hp != null && hp instanceof TemplateHookPoint) { + numCallTemplateHookpoints++; + } + else if (hp != null && hp instanceof StringHookPoint) { + numCallStringHookpoints++; + } + } + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportCallBeforeHookPoint(java.lang.String, + * java.util.Collection, de.monticore.ast.ASTNode) + */ + @Override + public void reportCallBeforeHookPoint(String oldTemplate, Collection beforeHPs, + ASTNode ast) { + for (HookPoint hp : beforeHPs) { + if (hp != null && hp instanceof CodeHookPoint) { + numCallCodeHookpoints++; + } + else if (hp != null && hp instanceof TemplateHookPoint) { + numCallTemplateHookpoints++; + } + else if (hp != null && hp instanceof StringHookPoint) { + numCallStringHookpoints++; + } + } + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportCallReplacementHookPoint(java.lang.String, + * java.util.List, de.monticore.ast.ASTNode) + */ + @Override + public void reportCallReplacementHookPoint(String oldTemplate, List hps, ASTNode ast) { + for (HookPoint hp : hps) { + if (hp != null && hp instanceof CodeHookPoint) { + numCallCodeHookpoints++; + } + else if (hp != null && hp instanceof TemplateHookPoint) { + numCallTemplateHookpoints++; + } + else if (hp != null && hp instanceof StringHookPoint) { + numCallStringHookpoints++; + } + } + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportCallSpecificReplacementHookPoint(java.lang.String, + * java.util.List, de.monticore.ast.ASTNode) + */ + @Override + public void reportCallSpecificReplacementHookPoint(String oldTemplate, List hps, + ASTNode ast) { + for (HookPoint hp : hps) { + numASTSpecificCalls++; + if (!(hp instanceof TemplateHookPoint)) { + numASTNodeVisits++; + } + } + } + + protected void writeContent(ASTNode ast) { + if (ast == null) { + return; + } + + ocv.clear(); + ast.accept(traverser); + int numASTNodeInstances = ocv.getTotalCount(); + int numASTNodeTypes = ocv.getObjectCountMap().size(); + int maxASTDepth = ocv.getMaxDepth(); + int numCalledUnsetHookpoints = calledUnsetHookpoints.size(); + int numUsedTemplates = usedTemplates.size(); + int numUsedHWTemplates = usedHWTemplates.size(); + int numUnusedTemplates = repository.getAllTemplateNames().size() - + numUsedTemplates; + int numUnusedHWTemplates = repository.getAllHWTemplateNames().size() - + numUsedHWTemplates; + int numVariables = variableNames.size(); + writeSummaryLine(NUM_ERRORS, numErrors); + writeSummaryLine(NUM_USER_ERRORS, numUserErrors); + writeSummaryLine(NUM_INTERNAL_ERRORS, numInternalErrors); + writeSummaryLine(NUM_WARNINGS, numWarnings); + writeSummaryLine(NUM_USER_WARNINGS, numUserWarnings); + writeSummaryLine(NUM_GENERATED_FILES, numGeneratedFiles); + writeSummaryLine(NUM_INSTANTIATIONS, numInstantiations); + writeSummaryLine(NUM_TEMPLATE_INCLUDE, numTemplateIncludes); + writeSummaryLine(NUM_TEMPLATE_WRITE, numTemplateWrites); + writeSummaryLine(NUM_USED_TEMPLATES, numUsedTemplates); + writeSummaryLine(NUM_UNUSED_TEMPLATES, numUnusedTemplates); + writeSummaryLine(NUM_USED_HWTEMPLATES, numUsedHWTemplates); + writeSummaryLine(NUM_UNUSED_HWTEMPLATES, numUnusedHWTemplates); + writeSummaryLine(MAX_TEMPLATE_DEPTH, maxTemplateDepth); + writeSummaryLine(MAX_AST_DEPTH, maxASTDepth); + writeSummaryLine(NUM_ASTNODE_INSTANCES, numASTNodeInstances); + writeSummaryLine(NUM_ASTNODE_TYPES, numASTNodeTypes); + writeSummaryLine(NUM_ASTNODE_VISITS, numASTNodeVisits); + writeSummaryLine(NUM_VARIABLES, numVariables); + writeSummaryLine(NUM_VARIABLE_ASSIGNMENTS, numVariableAssignments); + writeSummaryLine(NUM_AST_SPECIFIC_REPLACEMENTS, numASTSpecificReplacements); + writeSummaryLine(NUM_AST_SPECIFIC_CALLS, numASTSpecificCalls); + writeSummaryLine(NUM_SET_CODE_HOOKPOINTS, numSetCodeHookpoints); + writeSummaryLine(NUM_CALL_CODE_HOOKPOINTS, numCallCodeHookpoints); + writeSummaryLine(NUM_SET_TEMPLATE_HOOKPOINTS, numSetTemplateHookpoints); + writeSummaryLine(NUM_CALL_TEMPLATE_HOOKPOINTS, numCallTemplateHookpoints); + writeSummaryLine(NUM_SET_STRING_HOOKPOINTS, numSetStringHookpoints); + writeSummaryLine(NUM_CALL_STRING_HOOKPOINTS, numCallStringHookpoints); + writeSummaryLine(NUM_CALLED_EMPTY_HOOKPOINTS, numCalledUnsetHookpoints); + writeSummaryLine(NUM_CALLS_EMPTY_HOOKPOINTS, numCallsUnsetHookpoints); + } + + protected void writeSummaryLine(String string, int number) { + writeLine(string + ":" + Layouter.getSpaceString(40 - string.length()) + + number); + } + + protected void resetVariables() { + numTemplateIncludes = 0; + numTemplateWrites = 0; + numGeneratedFiles = 0; + numInstantiations = 0; + variableNames.clear(); + numVariableAssignments = 0; + numASTNodeVisits = 0; + numErrors = 0; + numUserErrors = 0; + numInternalErrors = 0; + numWarnings = 0; + numUserWarnings = 0; + templateDepth = 0; + maxTemplateDepth = 0; + numCallCodeHookpoints = 0; + numSetCodeHookpoints = 0; + numCallTemplateHookpoints = 0; + numSetTemplateHookpoints = 0; + numCallStringHookpoints = 0; + numSetStringHookpoints = 0; + numCallsUnsetHookpoints = 0; + numASTSpecificReplacements = 0; + usedTemplates.clear(); + } + + @Override + public void flush(ASTNode ast) { + writeContent(ast); + resetVariables(); + writeFooter(); + super.flush(ast); + } + + protected void writeFooter() { + writeLine("========================================================== Explanation"); + writeLine("Summary of all reports:"); + writeLine(" -" + NUM_ERRORS + ": " + "Number of errors during the process"); + writeLine(" -" + NUM_USER_ERRORS + ": " + "Number of user errors during the process"); + writeLine(" -" + NUM_INTERNAL_ERRORS + ": " + "Number of internal errors during the process"); + writeLine(" -" + NUM_WARNINGS + ": " + "Number of warnings during the process"); + writeLine(" -" + NUM_USER_WARNINGS + ": " + "Number of user warnings during the process"); + writeLine(" -" + NUM_GENERATED_FILES + ": " + "Number of generated files"); + writeLine(" -" + NUM_INSTANTIATIONS + ": " + "Number of instantiated objects"); + writeLine(" -" + NUM_TEMPLATE_INCLUDE + ": " + "Number of templates being included"); + writeLine(" -" + NUM_TEMPLATE_WRITE + ": " + "Number of templates being used"); + writeLine(" -" + NUM_USED_TEMPLATES + ": " + "Number of templates being used"); + writeLine(" -" + NUM_UNUSED_TEMPLATES + ": " + "Number of templates being unused"); + writeLine(" -" + NUM_USED_HWTEMPLATES + ": " + "Number of handwritten templates being used"); + writeLine(" -" + NUM_UNUSED_HWTEMPLATES + ": " + "Number of handwritten templates being unused"); + writeLine(" -" + MAX_TEMPLATE_DEPTH + ": " + "Maximal depth of the template call hierarchy"); + writeLine(" -" + MAX_AST_DEPTH + ": " + "Maximal depth of the AST"); + writeLine(" -" + NUM_ASTNODE_INSTANCES + ": " + "Number of instantiated AST nodes"); + writeLine(" -" + NUM_ASTNODE_TYPES + ": " + "Number of used AST node types"); + writeLine(" -" + NUM_ASTNODE_VISITS + ": " + "Number of AST node visits"); + writeLine(" -" + NUM_VARIABLES + ": " + "Variable names that got a value during the process"); + writeLine(" -" + NUM_VARIABLE_ASSIGNMENTS + ": " + "Number of values assigned to variables"); + writeLine(" -" + NUM_AST_SPECIFIC_REPLACEMENTS + ": " + "Number of AST specific replacements"); + writeLine(" -" + NUM_AST_SPECIFIC_CALLS + ": " + "Number of AST hook point calls"); + writeLine(" -" + NUM_SET_CODE_HOOKPOINTS + ": " + + "Number of code hook points set including"); + writeLine(" template replacements, set before and set after template statements"); + writeLine(" -" + NUM_CALL_CODE_HOOKPOINTS + ": " + "Number of code hook point calls"); + writeLine(" -" + NUM_SET_TEMPLATE_HOOKPOINTS + ": " + + "Number of template hook points set including"); + writeLine(" template replacements, set before and set after template statements"); + writeLine(" -" + NUM_CALL_TEMPLATE_HOOKPOINTS + ": " + "Number of template hook point calls"); + writeLine(" -" + NUM_SET_STRING_HOOKPOINTS + ": " + + "Number of string hook points set including"); + writeLine(" template replacements, set before and set after template statements"); + writeLine(" -" + NUM_CALL_STRING_HOOKPOINTS + ": " + "Number of string hook point calls"); + writeLine(" -" + NUM_CALLED_EMPTY_HOOKPOINTS + ": " + "Number of empty hook point called"); + writeLine(" -" + NUM_CALLS_EMPTY_HOOKPOINTS + ": " + "Number of empty hook point calls"); + + writeLine("(EOF)"); + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportSetHookPoint(java.lang.String, + * de.monticore.generating.templateengine.HookPoint) + */ + @Override + public void reportSetHookPoint(String hookName, HookPoint hp) { + if (hp != null && hp instanceof CodeHookPoint) { + numSetCodeHookpoints++; + } + else if (hp != null && hp instanceof TemplateHookPoint) { + numSetTemplateHookpoints++; + } + else if (hp != null && hp instanceof StringHookPoint) { + numSetStringHookpoints++; + } + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportCallHookPointStart(java.lang.String, + * de.monticore.generating.templateengine.HookPoint, de.monticore.ast.ASTNode) + */ + @Override + public void reportCallHookPointStart(String hookName, HookPoint hp, ASTNode ast) { + if (hp != null && hp instanceof CodeHookPoint) { + numCallCodeHookpoints++; + } + else if (hp != null && hp instanceof TemplateHookPoint) { + numCallTemplateHookpoints++; + } + else if (hp != null && hp instanceof StringHookPoint) { + numCallStringHookpoints++; + } + calledUnsetHookpoints.add(ReportingHelper.getHookPointName(hookName)); + numCallsUnsetHookpoints++; + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportASTSpecificTemplateReplacement(java.lang.String, + * de.monticore.ast.ASTNode, de.monticore.generating.templateengine.HookPoint) + */ + @Override + public void reportASTSpecificTemplateReplacement(String oldTemplate, ASTNode node, HookPoint newHp) { + numASTSpecificReplacements++; + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportSetBeforeTemplate(java.lang.String, + * java.util.List) + */ + @Override + public void reportSetBeforeTemplate(String template, List beforeHps) { + for (HookPoint hp : beforeHps) { + if (hp != null && hp instanceof CodeHookPoint) { + numSetCodeHookpoints++; + } + else if (hp != null && hp instanceof TemplateHookPoint) { + numSetTemplateHookpoints++; + } + else if (hp != null && hp instanceof StringHookPoint) { + numSetStringHookpoints++; + } + } + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportSetAfterTemplate(java.lang.String, + * java.util.List) + */ + @Override + public void reportSetAfterTemplate(String template, List afterHps) { + for (HookPoint hp : afterHps) { + if (hp != null && hp instanceof CodeHookPoint) { + numSetCodeHookpoints++; + } + else if (hp != null && hp instanceof TemplateHookPoint) { + numSetTemplateHookpoints++; + } + else if (hp != null && hp instanceof StringHookPoint) { + numSetStringHookpoints++; + } + } + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.IReportEventHandler#reportTemplateReplacement(java.lang.String, + * java.util.List) + */ + @Override + public void reportTemplateReplacement(String oldTemplate, List newHps) { + for (HookPoint hp : newHps) { + if (hp != null && hp instanceof CodeHookPoint) { + numSetCodeHookpoints++; + } + else if (hp != null && hp instanceof TemplateHookPoint) { + numSetTemplateHookpoints++; + } + else if (hp != null && hp instanceof StringHookPoint) { + numSetStringHookpoints++; + } + } + } + + @Override + protected void writeHeader() { + writeLine("========================================================== Summary"); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/TemplateTreeReporter.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/TemplateTreeReporter.java new file mode 100644 index 0000000000..1dd3937493 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/TemplateTreeReporter.java @@ -0,0 +1,249 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.reporter; + +import java.io.File; +import java.util.Collection; +import java.util.List; + +import de.monticore.ast.ASTNode; +import de.monticore.generating.templateengine.CodeHookPoint; +import de.monticore.generating.templateengine.HookPoint; +import de.monticore.generating.templateengine.StringHookPoint; +import de.monticore.generating.templateengine.TemplateHookPoint; +import de.monticore.generating.templateengine.reporting.commons.AReporter; +import de.monticore.generating.templateengine.reporting.commons.ReportingConstants; +import de.monticore.generating.templateengine.reporting.commons.ReportingHelper; +import de.se_rwth.commons.Names; + +/** + */ +public class TemplateTreeReporter extends AReporter { + + static final String TEMPLATE_CALL_START = "+--"; + + static final String STRING_HOOKPOINT = "+--SHP"; + + static final String CODE_HOOKPOINT = "+--CHP"; + + static final String TEMPLATE_HOOKPOINT = "+--THP"; + + static final String SPECIFIC_TEMPLATE_HOOKPOINT = "+--ATHP"; + + static final String SPECIFIC_STRING_HOOKPOINT = "+--ASHP"; + + static final String SPECIFIC_CODE_HOOKPOINT = "+--ACHP"; + + static final String INSTANTIATE_JAVA_CLASS = "+--inst"; + + static final String INDENTATION = "| "; + + static final String SIMPLE_FILE_NAME = "09_TemplateTree"; + + protected int currentIndentLevel = 0; + + public TemplateTreeReporter(String outputDir, String modelName) { + super(outputDir + + File.separator + modelName, SIMPLE_FILE_NAME, + ReportingConstants.REPORT_FILE_EXTENSION); + } + + @Override + protected void writeHeader() { + writeLine("========================================================== Protocol"); + } + + protected void writeFooter() { + writeLine("========================================================== Explanation"); + writeLine("Tree structure for template calls."); + writeLine("Short forms:"); + writeLine(" +-- template call"); + writeLine(" +--SHP StringHookPoint call"); + writeLine(" +--CHP CodeHookPoint call"); + writeLine(" +--ASHP AST StringHookPoint call"); + writeLine(" +--ATHP AST TemplateHookPoint call"); + writeLine(" +--ACHP AST CodeHookPoint call"); + writeLine(" +inst instantiation of java class"); + writeLine("(EOF)"); + } + + protected String getIndent() { + String ret = ""; + for (int i = 0; i < currentIndentLevel; i++) { + ret += INDENTATION; + } + return ret; + } + + @Override + public void reportTemplateStart(String templatename, ASTNode ast) { + String line = getIndent(); + line += TEMPLATE_CALL_START; + line += ReportingHelper.getTemplateName(templatename); + writeLine(line); + currentIndentLevel++; + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.IReportEventHandler#reportTemplateEnd(java.lang.String, + * de.monticore.ast.ASTNode) + */ + @Override + public void reportTemplateEnd(String templatename, ASTNode ast) { + currentIndentLevel--; + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.IReportEventHandler#reportInstantiate(java.lang.String, + * java.util.List) + */ + @Override + public void reportInstantiate(String className, List params) { + String line = getIndent(); + line += INSTANTIATE_JAVA_CLASS; + line += ": " + Names.getSimpleName(className); + writeLine(line); + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.IReportEventHandler#reportSetValue(java.lang.String, + * java.lang.Object) + */ + @Override + public void reportSetValue(String name, Object value) { + writeLine("OP set value (key, value): "); + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportCallAfterHookPoint(java.lang.String, + * java.util.Collection, de.monticore.ast.ASTNode) + */ + @Override + public void reportCallAfterHookPoint(String oldTemplate, Collection afterHPs, + ASTNode ast) { + callHPS(oldTemplate, afterHPs); + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportCallBeforeHookPoint(java.lang.String, + * java.util.Collection, de.monticore.ast.ASTNode) + */ + @Override + public void reportCallBeforeHookPoint(String oldTemplate, Collection beforeHPs, + ASTNode ast) { + callHPS(oldTemplate, beforeHPs); + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportCallHookPointStart(java.lang.String, + * de.monticore.generating.templateengine.HookPoint, de.monticore.ast.ASTNode) + */ + @Override + public void reportCallHookPointStart(String hookName, HookPoint hp, ASTNode ast) { + callHP(hp); + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportCallReplacementHookPoint(java.lang.String, + * java.util.List, de.monticore.ast.ASTNode) + */ + @Override + public void reportCallReplacementHookPoint(String oldTemplate, List hps, ASTNode ast) { + callHPS(oldTemplate, hps); + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportCallSpecificReplacementHookPoint(java.lang.String, + * java.util.List, de.monticore.ast.ASTNode) + */ + @Override + public void reportCallSpecificReplacementHookPoint(String oldTemplate, List hps, + ASTNode ast) { + callSpecificHPS(oldTemplate, hps); + } + + protected void callSpecificHPS(String oldTemplate, List hps) { + for (HookPoint hp : hps) { + callSpecificHP(hp); + } + + } + + protected void callSpecificHP(HookPoint hp) { + if (hp != null) { + String line = getIndent(); + if (hp instanceof StringHookPoint) { + line += SPECIFIC_STRING_HOOKPOINT; + line += ": " + getHookPointValue(hp); + writeLine(line); + } + else if (hp instanceof TemplateHookPoint) { + line += SPECIFIC_TEMPLATE_HOOKPOINT; + line += ": " + getHookPointValue(hp); + writeLine(line); + } + else if (hp instanceof CodeHookPoint) { + line += SPECIFIC_CODE_HOOKPOINT; + line += ": " + getHookPointValue(hp); + writeLine(line); + } + } + } + + protected void callHPS(String oldTemplate, Collection hps) { + for (HookPoint hp : hps) { + callHP(hp); + } + } + + protected void callHP(HookPoint hp) { + if (hp != null) { + String line = getIndent(); + if (hp instanceof StringHookPoint) { + line += STRING_HOOKPOINT; + line += ": " + getHookPointValue(hp); + writeLine(line); + } + else if (hp instanceof TemplateHookPoint) { + line += TEMPLATE_HOOKPOINT; + line += ": " + getHookPointValue(hp); + writeLine(line); + } + else if (hp instanceof CodeHookPoint) { + line += CODE_HOOKPOINT; + line += ": " + getHookPointValue(hp); + writeLine(line); + } + } + } + + protected void resetVariables() { + currentIndentLevel = 0; + } + + protected String getHookPointValue(HookPoint hp) { + String value = null; + if (hp != null && hp instanceof TemplateHookPoint) { + value = ((TemplateHookPoint) hp).getTemplateName(); + value = ReportingHelper.getTemplateName(value); + } + else if (hp != null && hp instanceof StringHookPoint) { + value = ((StringHookPoint) hp).getValue(); + value = ReportingHelper.formatStringToReportingString(value, + ReportingConstants.REPORTING_ROW_LENGTH - ReportingConstants.FORMAT_LENGTH_2); + } + else if (hp != null && hp instanceof CodeHookPoint) { + value = ((CodeHookPoint) hp).getClass().getName(); + value = Names.getSimpleName(value); + } + return value; + } + + @Override + public void flush(ASTNode node) { + writeFooter(); + resetVariables(); + super.flush(node); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/TemplatesReporter.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/TemplatesReporter.java new file mode 100644 index 0000000000..7aa679a092 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/TemplatesReporter.java @@ -0,0 +1,149 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.reporter; + +import de.monticore.ast.ASTNode; +import de.monticore.generating.templateengine.reporting.commons.*; + +import java.io.File; +import java.util.*; +import java.util.Map.Entry; + +/** + */ +public class TemplatesReporter extends AReporter { + + static final String SIMPLE_FILE_NAME = "04_Templates"; + + static final String INDENT = Layouter.getSpaceString(10); + + protected SortedMap templateCount = new TreeMap(); + + protected SortedMap hwTemplateCount = new TreeMap(); + + protected Set realTemplateNames = new LinkedHashSet<>(); + + protected Set realHWTemplateNames = new LinkedHashSet<>(); + + protected ReportingRepository repository; + + public TemplatesReporter(String outputDir, String modelName, ReportingRepository repository) { + super(outputDir + + File.separator + modelName, SIMPLE_FILE_NAME, + ReportingConstants.REPORT_FILE_EXTENSION); + this.repository = repository; + } + + @Override + protected void writeHeader() { + // Write empty header + } + + protected void writeContent() { + writeUsedTemplates(); + writeUnusedTemplates(); + // TODO: see #1738 properly deal with handwritten template inclusion + writeUsedUSTemplates(); + writeUnusedUSTemplates(); + } + + protected void writeUsedTemplates() { + writeLine("========================================================== Used Templates"); + writeLine("#Calls: Template Name"); + for (Entry entry : templateCount.entrySet()) { + String countString = entry.getValue() + "x"; + writeLine(countString + getIndentAfterCount(countString) + entry.getKey()); + } + } + + protected void writeUnusedTemplates() { + writeLine("========================================================== Unused Templates"); + SortedSet unusedTemplateNames = new TreeSet(repository.getAllTemplateNames()); + unusedTemplateNames.removeAll(realTemplateNames); + for (String t : unusedTemplateNames) { + writeLine(t); + } + } + + protected void writeUsedUSTemplates() { + writeLine("========================================================== Used USTemplates"); + writeLine("#Calls: Template Name"); + for (Entry entry : hwTemplateCount.entrySet()) { + String countString = entry.getValue() + "x"; + writeLine(countString + getIndentAfterCount(countString) + entry.getKey()); + } + } + + protected void writeUnusedUSTemplates() { + writeLine("========================================================== Unused USTemplates"); + SortedSet unusedHWTemplateNames = new TreeSet( + repository.getAllHWTemplateNames()); + unusedHWTemplateNames.removeAll(realHWTemplateNames); + for (String t : unusedHWTemplateNames) { + writeLine(t); + } + } + + protected void writeFooter() { + writeLine("========================================================== Explanation"); + writeLine("Used Templates: the list of standard template being used."); + writeLine("Used USTemplates: the list of user specifc templates being used."); + writeLine("- #Executions: how often the template was called/included"); + writeLine("Unused Templates: the list of templates which have not been executed"); + writeLine("Unused USTemplates: the list of user specifc templates which"); + writeLine(" have not been executed."); + writeLine("All lists are sorted"); + writeLine("(EOF)"); + } + + protected String getIndentAfterCount(String countString) { + String indentString = Layouter.getSpaceString(2); + if (countString.length() < INDENT.length() + 1) { + indentString = INDENT.substring(countString.length()); + } + + return indentString; + } + + @Override + public void reportTemplateStart(String templatename, ASTNode ast) { + Set hwTemplates = repository.getAllHWTemplateNames(); + // if template is handwritten + if (hwTemplates.contains(templatename.replaceAll("\\.", "/").concat(".") + .concat(ReportingConstants.TEMPLATE_FILE_EXTENSION))) { + realHWTemplateNames.add(templatename.replaceAll("\\.", "/").concat(".") + .concat(ReportingConstants.TEMPLATE_FILE_EXTENSION)); + templatename = ReportingHelper.getTemplateName(templatename); + + if (hwTemplateCount.containsKey(templatename)) { + Integer actualCount = hwTemplateCount.get(templatename); + hwTemplateCount.put(templatename, actualCount + 1); + } + else { + hwTemplateCount.put(templatename, 1); + } + } + else { + realTemplateNames.add(templatename.replaceAll("\\.", "/").concat(".") + .concat(ReportingConstants.TEMPLATE_FILE_EXTENSION)); + templatename = ReportingHelper.getTemplateName(templatename); + + if (templateCount.containsKey(templatename)) { + Integer actualCount = templateCount.get(templatename); + templateCount.put(templatename, actualCount + 1); + } + else { + templateCount.put(templatename, 1); + } + } + } + + @Override + public void flush(ASTNode ast) { + writeContent(); + writeFooter(); + templateCount.clear(); + super.flush(ast); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/TransformationReporter.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/TransformationReporter.java new file mode 100644 index 0000000000..ad9f4c81ab --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/TransformationReporter.java @@ -0,0 +1,129 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.reporter; + +import java.io.File; + +import de.monticore.ast.ASTNode; +import de.monticore.generating.templateengine.reporting.commons.AReporter; +import de.monticore.generating.templateengine.reporting.commons.ReportingConstants; +import de.monticore.generating.templateengine.reporting.commons.ReportingRepository; + +/** + */ +public class TransformationReporter extends AReporter { + + /** + * @see mc.codegen.reporting.commons.DefaultReportEventHandler#reportTransformationStart(java.lang.String) + */ + @Override + public void reportTransformationStart(String transformationName) { + writeLine("Start Trafo: " + transformationName); + } + + static final String SIMPLE_FILE_NAME = "14_Transformations"; + + static final String INDENT = " "; + + protected ReportingRepository repository; + + public TransformationReporter( + String outputDir, + String modelName, + ReportingRepository repo) { + super(outputDir + File.separator + + modelName, SIMPLE_FILE_NAME, ReportingConstants.REPORT_FILE_EXTENSION); + this.repository = repo; + } + + @Override + public void reportTransformationObjectCreation(String transformationName, ASTNode ast) { + String formattedName = repository.getASTNodeNameFormatted(ast); + writeLine(transformationName + getIndentAfterFile(transformationName) + formattedName + + getIndentAfterFile(formattedName) + "added"); + } + + @Override + public void reportTransformationObjectChange(String transformationName, ASTNode ast, + String attributeName) { + String formattedName = repository.getASTNodeNameFormatted(ast); + writeLine(transformationName + getIndentAfterFile(transformationName) + formattedName + + getIndentAfterFile(formattedName) +attributeName + " changed"); + } + + @Override + public void reportTransformationObjectDeletion(String transformationName, ASTNode ast) { + String formattedName = repository.getASTNodeNameFormatted(ast); + writeLine(transformationName + getIndentAfterFile(transformationName) + formattedName + + getIndentAfterFile(formattedName) + "deleted"); + } + + @Override + public void reportTransformationObjectMatch(String transformationName, ASTNode ast) { + String formattedName = repository.getASTNodeNameFormatted(ast); + writeLine(transformationName + getIndentAfterFile(transformationName) + formattedName + + getIndentAfterFile(formattedName) + "matched"); + } + + @Override + public void reportTransformationOldValue(String transformationName, ASTNode ast) { + String formattedName = repository.getASTNodeNameFormatted(ast); + writeLine(" " + getIndentAfterFile("") + getIndentAfterFile("") + "old value: " + formattedName ); + } + + @Override + public void reportTransformationNewValue(String transformationName, ASTNode ast) { + String formattedName = repository.getASTNodeNameFormatted(ast); + writeLine(" " + getIndentAfterFile("") + getIndentAfterFile("") + "new value: " + formattedName ); + } + + @Override + public void reportTransformationOldValue(String transformationName, String value) { + writeLine(" " + getIndentAfterFile("") + getIndentAfterFile("") + "old value: " + value ); + } + + @Override + public void reportTransformationNewValue(String transformationName, String value) { + writeLine(" " + getIndentAfterFile("") + getIndentAfterFile("") + "new value: " + value ); + } + + @Override + public void reportTransformationOldValue(String transformationName, boolean value) { + writeLine(" " + getIndentAfterFile("") + getIndentAfterFile("") + "old value: " + value ); + } + + @Override + public void reportTransformationNewValue(String transformationName, boolean value) { + writeLine(" " + getIndentAfterFile("") + getIndentAfterFile("") + "new value: " + value ); + } + + @Override + public void flush(ASTNode ast) { + writeFooter(); + super.flush(ast); + } + + protected void writeFooter() { + writeLine("========================================================== Explanation"); + writeLine("Applied Transformations: the list of transformations in the order they are applied."); + writeLine("(EOF)"); + } + + protected String getIndentAfterFile(String file) { + if (file.length() < INDENT.length() + 1) { + return INDENT.substring(file.length()); + } + else { + return " "; + } + } + + @Override + protected void writeHeader() { + writeLine("========================================================== Applied Transformations"); + writeLine("Transformation Name AST-Node Transformation Type"); + } + + + +} diff --git a/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/VariablesReporter.java b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/VariablesReporter.java new file mode 100644 index 0000000000..5e10d84664 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/generating/templateengine/reporting/reporter/VariablesReporter.java @@ -0,0 +1,110 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.reporter; + +import java.io.File; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import de.monticore.ast.ASTNode; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +import de.monticore.generating.templateengine.reporting.commons.AReporter; +import de.monticore.generating.templateengine.reporting.commons.Layouter; +import de.monticore.generating.templateengine.reporting.commons.MapUtil; +import de.monticore.generating.templateengine.reporting.commons.ReportingConstants; + +/** + */ +public class VariablesReporter extends AReporter { + + static final String SIMPLE_FILE_NAME = "07_Variables"; + + protected Map var2asmt; + + protected Map var2adds; + + protected List templateCount; + + public VariablesReporter(String outputDir, String modelName) { + super(outputDir + File.separator + + modelName, + SIMPLE_FILE_NAME, ReportingConstants.REPORT_FILE_EXTENSION); + templateCount = Lists.newArrayList(); + var2adds = Maps.newHashMap(); + var2asmt = Maps.newHashMap(); + } + + @Override + protected void writeHeader() { + writeLine("========================================================== Variables assigned"); + writeLine("#Asmt #Adds Variable name"); + } + + protected void writeContent() { + Set allKeys = Sets.newLinkedHashSet(); + allKeys.addAll(var2adds.keySet()); + allKeys.addAll(var2asmt.keySet()); + for (String key : allKeys) { + String adds = getNumber(var2adds, key); + String asmts = getNumber(var2asmt, key); + + writeLine(asmts + Layouter.getSpaceString(7 - asmts.length()) + adds + + Layouter.getSpaceString(7 - adds.length()) + key); + } + } + + protected String getNumber(Map map, String key) { + if (map.get(key) != null) { + return map.get(key) + "x"; + } + else { + return "0x"; + } + } + + protected void writeFooter() { + writeLine("========================================================== Explanation"); + writeLine("Variables assigned: list all variable names that got a value during the"); + writeLine("process."); + writeLine("Each entry knows:"); + writeLine(" - #Asmt how often has a value been assigned (setValue)"); + writeLine(" - #Adds how often has the value been extended (addValue)"); + writeLine("(EOF)"); + } + + @Override + public void reportSetValue(String name, Object value) { + if (name != null) { + // templateCount.add("NAME: " + name + Layouter.getSpaceString(10 - + // name.length()) + // + "VALUE: " + value); + MapUtil.incMapValue(var2asmt, name); + } + } + + @Override + public void reportAddValue(String name, Object value, int size) { + if (name != null) { + MapUtil.incMapValue(var2adds, name); + } + } + + protected void resetVariables() { + templateCount.clear(); + var2adds.clear(); + var2asmt.clear(); + } + + @Override + public void flush(ASTNode ast) { + writeContent(); + writeFooter(); + resetVariables(); + super.flush(ast); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/io/FileReaderWriter.java b/monticore-runtime/src/main/java/de/monticore/io/FileReaderWriter.java new file mode 100644 index 0000000000..c3c5761843 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/io/FileReaderWriter.java @@ -0,0 +1,279 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.io; + +import com.google.common.base.Charsets; +import com.google.common.collect.Lists; +import de.monticore.AmbiguityException; +import de.monticore.generating.templateengine.reporting.Reporting; +import de.se_rwth.commons.logging.Log; +import org.apache.commons.io.FileUtils; + +import java.io.*; +import java.net.*; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; +import java.util.jar.JarFile; +import java.util.stream.Collectors; + +/** + * This class handles all I/O commands in Monticore. + *

+ * Important conventions: File locations are always encoded as Path or URL objects. When creating + * Path objects please make use of the {@link Path#resolve} method whenever applicable. This + * convention extends beyond the FileHandler. Do not exchange information between classes + * using String literals or File objects. Doing so within classes is permitted, though discouraged. + *
+ * Failure to adhere to such a convention has caused innumerable trivial bugs in past + * implementations. + *

+ * This class uses UTF_8 encoding per default and is realized as singleton. + */ +public class FileReaderWriter { + + protected static FileReaderWriter INSTANCE = null; + + protected Charset charset; + + /** + * Sets the encoding for all subsequent operations until another encoding is assigned. + * + * @param charset The encoding to be used + */ + public void setCharset(Charset charset) { + this.charset = charset; + } + + /** + * Uses the default encoding UTF_8; + */ + protected FileReaderWriter() { + this.charset = StandardCharsets.UTF_8; + } + + /** + * @param charset The initial encoding to be used until another encoding is assigned. + * @see #setCharset + */ + protected FileReaderWriter(Charset charset) { + this.charset = charset; + } + + public static void init() { + INSTANCE = new FileReaderWriter(); + } + + public static void init(Charset charset) { + INSTANCE = new FileReaderWriter(charset); + } + + public static void init(FileReaderWriter instance) { + INSTANCE = instance; + } + + protected static FileReaderWriter getFileReaderWriter(){ + if(null == INSTANCE){ + init(); + } + return INSTANCE; + } + + /** + * Writes a String to a file using the specified encoding. + * + * @param targetPath The location of the file to be written to. + * @param content The String that's supposed to be written into the file + * @see #setCharset(Charset) + */ + public static void storeInFile(Path targetPath, String content) { + getFileReaderWriter()._storeInFile(targetPath, content); + } + + protected void _storeInFile(Path targetPath, String content) { + try { + Reporting.reportFileCreation(targetPath.toString()); + FileUtils.write(targetPath.toFile(), content, this.charset); + } + catch (IOException e) { + Log.error("0xA1023 IOException occured.", e); + Log.debug("IOException occured while trying to write to the File " + targetPath + ".", e, + this.getClass().getName()); + } + } + + /** + * Reads the String content from a file using the specified encoding. + * + * @param sourcePath The absolute location (fully specifies the filename) of the file to be read + * @return The String content of the file + * @see #setCharset(Charset) + */ + public static String readFromFile(Path sourcePath) { + return getFileReaderWriter()._readFromFile(sourcePath); + } + + protected String _readFromFile(Path sourcePath) { + String content = null; + try { + Reporting.reportOpenInputFile(sourcePath.toString()); + content = FileUtils.readFileToString(sourcePath.toFile(), this.charset); + } + catch (IOException e) { + Log.error("0xA1027 IOException occured.", e); + Log.debug("IOException while trying to read the content of " + sourcePath + + ".", e, this.getClass().getName()); + } + Log.errorIfNull(content); + return content; + } + + /** + * Reads the String content from a file using the specified encoding. URLs can also address files + * within jars. + * + * @param sourcePath The absolute location (fully specifies the filename) of the file to be read + * @return The String content of the file + * @see #setCharset(Charset) + */ + public static String readFromFile(URL sourcePath) { + return getFileReaderWriter()._readFromFile(sourcePath); + } + + protected String _readFromFile(URL sourcePath) { + String content = null; + try { + Reporting.reportOpenInputFile(sourcePath.toString()); + URLConnection conn = sourcePath.openConnection(); + if(conn instanceof JarURLConnection){ + openedJarFiles.add(((JarURLConnection) conn).getJarFile()); + } + Reader reader = new InputStreamReader(conn.getInputStream(), charset.name()); + content = _readFromFile(reader); + reader.close(); + } + catch (IOException e) { + Log.error("0xA0577 IOException occured.", e); + Log.debug("IOException while trying to read the content of " + sourcePath + + ".", e, this.getClass().getName()); + } + Log.errorIfNull(content); + return content; + } + + public static String readFromFile(Reader reader) { + return getFileReaderWriter()._readFromFile(reader); + } + + protected String _readFromFile(Reader reader) { + BufferedReader buffer = new BufferedReader(reader); + String content = buffer.lines().collect(Collectors.joining()); + Log.errorIfNull(content); + return content; + } + + /** + * Tries to load a resource with the passed name in the file system using the passed classLoader. + * If multiple resources are found, throws an ambiguity exception. If no resource is found, + * returns {@link Optional#empty()} + * + * @param classLoader The classloader employed to load the resource + * @param name The name of the resource to load + * @return The optional URL of the resource, if present, or {@link Optional#empty()} else + */ + public static Optional getResource(ClassLoader classLoader, String name) { + return getFileReaderWriter()._getResource(classLoader, name); + } + + protected Optional _getResource(ClassLoader classLoader, String name) { + try { + ArrayList results = Collections.list(classLoader.getResources(name)); + if (results.size() > 1) { + throw new AmbiguityException("0xA4092 Multiple models were found with name '" + + name + "':" + results.toString()); + } + else if (results.size() < 1) { + Reporting.reportFileExistenceChecking(Lists.newArrayList(), Paths.get(name)); + } + else { + Reporting.reportOpenInputFile(results.get(0).getFile()); + return Optional.ofNullable(results.get(0)); + } + } + catch (IOException e) { + Log.error("0xA1024 IOException occured.", e); + Log.debug("IOException while trying to find the URL of " + name, e, + this.getClass().getName()); + } + return Optional.empty(); + } + + public static boolean existsFile(Path sourcePath) { + return getFileReaderWriter()._existsFile(sourcePath); + } + + protected boolean _existsFile(Path sourcePath) { + Reporting.reportFileExistenceChecking(Lists.newArrayList(), sourcePath); + return sourcePath.toFile().exists(); + } + + /** + * Saves all {@link JarFile}s opened by {@link FileReaderWriter#getReader(URL)} if the protocol "jar:" is used. + * These files must be closed at the end of programm via {@link FileReaderWriter#closeOpenedJarFiles()}. + */ + protected static Set openedJarFiles = new HashSet<>(); + + /** + * Obtains the reader for a passed model coordinate. The resulting reader + * can be used as argument for a parse method of a language's parser. + * @param location + * @return + */ + public static Reader getReader(URL location) { + try { + if (!"jar".equals(location.getProtocol())) { + Path p = Paths.get(location.toURI()); + Reporting.reportOpenInputFile(Optional.of(p.getParent()), + p.getParent().relativize(p)); + if (location.getFile().charAt(2) == ':') { + String filename = URLDecoder.decode(location.getFile(), "UTF-8"); + return new FileReader(filename.substring(1)); + } + return new FileReader(location.getFile()); + } + String[] parts = location.toURI().toString().split("!"); + Path p = Paths.get(parts[1].substring(1)); + Reporting.reportOpenInputFile(Optional.of(Paths.get(parts[0].substring(10))), + p); + + // Save opened jar files for later cleanup + URLConnection conn = location.openConnection(); + if(conn instanceof JarURLConnection){ + openedJarFiles.add(((JarURLConnection) conn).getJarFile()); + } + return new InputStreamReader(conn.getInputStream(), Charsets.UTF_8.name()); + } + catch (IOException | URISyntaxException e) { + Log.error("0xA6104 Exception occurred while reading the file at '" + location + "':", e); + } + return null; + } + + /** + * Closes all jar files opened by {@link FileReaderWriter#getReader(URL)}. + * Must be called at the end of the program to ensure all resources are freed. + */ + public static void closeOpenedJarFiles(){ + for (JarFile openedJarFile : openedJarFiles) { + try { + openedJarFile.close(); + } catch (IOException e) { + // JarFile contains no state to check if it is already closed. + // Therefore, a try-catch block is needed + } + } + openedJarFiles.clear(); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/io/MontiCoreClassLoader.java b/monticore-runtime/src/main/java/de/monticore/io/MontiCoreClassLoader.java new file mode 100644 index 0000000000..c162e7b46b --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/io/MontiCoreClassLoader.java @@ -0,0 +1,30 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.io; + +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Enumeration; + +public class MontiCoreClassLoader extends URLClassLoader { + + private final ClassLoader parent; + + public MontiCoreClassLoader(URL[] urls) { + super(urls); + this.parent = null; + } + + public MontiCoreClassLoader(URL[] urls, ClassLoader parent){ + super(urls, parent); + this.parent = parent; + } + + @Override + public Enumeration getResources(String name) throws IOException { + if(parent != null){ + return super.getResources(name); + } + return super.findResources(name); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/io/paths/GlobExpressionEvaluator.java b/monticore-runtime/src/main/java/de/monticore/io/paths/GlobExpressionEvaluator.java new file mode 100644 index 0000000000..43c6365087 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/io/paths/GlobExpressionEvaluator.java @@ -0,0 +1,76 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.io.paths; + +import de.se_rwth.commons.Joiners; +import de.se_rwth.commons.StringMatchers; +import de.se_rwth.commons.logging.Log; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.file.*; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.HashSet; +import java.util.Set; +import java.util.regex.Pattern; + +/** + * This class evaluates a globbing expression on the current file system. + */ +public class GlobExpressionEvaluator extends SimpleFileVisitor { + + protected Pattern pattern; + + protected Set result; + + protected FileSystem fileSystem; + + protected boolean isLocatedInJar; + + public GlobExpressionEvaluator(String pathRegex, FileSystem fs, boolean isLocatedInJar) { + this.isLocatedInJar = isLocatedInJar; + if (isLocatedInJar) { + // this line removes the file system path from the pathRegex + pathRegex = pathRegex.replaceAll(Pattern.quote(fs.toString().replaceAll("\\\\", "/")), ""); + } + pattern = Pattern.compile(pathRegex); + this.fileSystem = fs; + + result = new HashSet<>(); + } + + @Override + public FileVisitResult visitFile(Path path, BasicFileAttributes attribs) { + path = path.toAbsolutePath(); + if (pattern.matcher(path.toString()).matches()) { + URI uri = path.toUri(); + // this takes care of white spaces in files, especially jars, if they are double encoded + if (uri.toString().contains("%2520")) { + uri = URI.create(uri.toString().replaceAll("%2520", "%20")); + } + result.add(uri); + } + return FileVisitResult.CONTINUE; + } + + protected Set getResult() { + return result; + } + + public Set evaluate(File start) { + try { + Path startingPath = start.toPath(); + if (isLocatedInJar) { + startingPath = fileSystem.getPath("/"); + } + Files.walkFileTree(startingPath, this); + } + catch (IOException e) { + Log.error("0x1C193 Error while traversing the file system!", e); + e.printStackTrace(); + return null; + } + return getResult(); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/io/paths/MCPath.java b/monticore-runtime/src/main/java/de/monticore/io/paths/MCPath.java new file mode 100644 index 0000000000..54548e7e9a --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/io/paths/MCPath.java @@ -0,0 +1,258 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.io.paths; + +import de.monticore.AmbiguityException; +import de.monticore.io.FileReaderWriter; +import de.monticore.io.MontiCoreClassLoader; +import de.se_rwth.commons.Names; +import de.se_rwth.commons.logging.Log; +import org.apache.commons.io.filefilter.RegexFileFilter; + +import java.io.*; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; +import java.util.stream.Collectors; + +/** + * A ModelPath encapsulates the domain of accessible models inside the running + * language tool. + */ +public final class MCPath { + + protected final Map classloaderMap = new LinkedHashMap<>(); + + public MCPath() { } + + public MCPath(Collection entries) { + entries.stream() + .map(MCPath::toURL) + .filter(Optional::isPresent) + .map(Optional::get) + // parent class loader MUST BE null here! + // otherwise we would start to resolve from the system class path (or + // worse) unknowingly + .forEach(url -> classloaderMap.put(new MontiCoreClassLoader(new URL[] { url }, null), url)); + } + + public MCPath(Path... entries) { + this(Arrays.asList(entries)); + } + + public MCPath(String... entries) { + this(Arrays.stream(entries).map(Paths::get).collect(Collectors.toList())); + } + + public void addEntry(Path entry) { + Optional url = toURL(entry); + if(url.isPresent() && !classloaderMap.containsValue(url.get())){ + classloaderMap.put(new MontiCoreClassLoader(new URL[] { url.get() }, null), url.get()); + } + } + + public void removeEntry(Path entry) { + Optional urlClassLoader = toURL(entry) + .flatMap(url -> classloaderMap.entrySet().stream() + .filter(e -> e.getValue().equals(url)) + .findFirst()) + .map(Map.Entry::getKey); + urlClassLoader.ifPresent(classloaderMap::remove); + } + + public Collection getEntries() { + return classloaderMap.values().stream() + .map(MCPath::toPath) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toList()); + } + + public boolean isEmpty(){ + return classloaderMap.isEmpty(); + } + + /** + * Method for calculating a list of files located in an entry of the passed model path, + * with the passed qualified model name, and the passed regular expression over the file extension. + *

+ * Example: for a model path comprising two entries "src/test/resources" and "target", the + * qualified model name "foo.bar.Car", and the file extension regular expression "*sym", the + * result of this method could be a list with three files: + * "src/test/resources/foo/bar/Car.fdsym" + * "src/test/resources/foo/bar/Car.cdsym" + * "target/foo/bar/Car.fdsym" + * + * @param fileExtRegEx + * @return + */ + public Optional find(String qualifiedName, String fileExtRegEx) { + // calculate the folderPath (e.g., "foo/bar") and fileNameRegEx (e.g., "Car.*sym") + String folderPath = Names.getPathFromQualifiedName(qualifiedName); + String fileNameRegEx = Names.getSimpleName(qualifiedName) + "\\." + fileExtRegEx; + + // initialize a file filter filtering for the regular expression + FileFilter filter = new RegexFileFilter(fileNameRegEx); + + List resolvedURLs = new ArrayList<>(); + // iterate MCPath entries and check whether folder path exists within these + for (Path p : getEntries()) { + if(p.toString().endsWith(".jar")){ + String path = "/" + folderPath.replaceAll("\\\\", "/") + "/" + fileNameRegEx; + GlobExpressionEvaluator evaluator = new GlobExpressionEvaluator(path, getJarFS(p.toFile()), true); + resolvedURLs.addAll(evaluator.evaluate(p.toFile()).stream().map(uri -> { + try { + return uri.toURL(); + } catch (MalformedURLException e) { + e.printStackTrace(); + return null; + } + }).collect(Collectors.toList())); + } + File folder = p.resolve(folderPath).toFile(); //e.g., "src/test/resources/foo/bar" + if (folder.exists() && folder.isDirectory()) { + // perform the actual file filter on the folder and collect result + Arrays.stream(folder.listFiles(filter)) + .map(f -> toURL(folder.toPath().resolve(f.getName()))) + .filter(Optional::isPresent) + .forEach(f -> resolvedURLs.add(f.get())); + } + } + + if (1 == resolvedURLs.size()) { + return Optional.of(resolvedURLs.get(0)); + } + else if (1 < resolvedURLs.size()) { + reportAmbiguity(resolvedURLs, fileNameRegEx); + } + return Optional.empty(); + } + + /** + * Searches for a path in all entries. The result is either the fully qualified + * path or an empty Optional. An error is logged if multiple, ambiguous results + * are found. + * + * @param path a relative path, e.g, a/b/C.foo, to search in all MCPath entries + * @return the URL representing the unique fully qualified name of the result, + * if it exists + * @throws AmbiguityException if the search locates multiple potentially + * matching models + */ + public Optional find(String path) { + String fixedPath = path.replaceAll("\\" + File.separator, "/"); + + List resolvedURLs = classloaderMap.keySet().stream() + .map(classloader -> FileReaderWriter.getResource(classloader, fixedPath)) + .filter(Optional::isPresent) + .map(Optional::get) + .distinct() + .collect(Collectors.toList()); + + if (1 == resolvedURLs.size()) { + return Optional.of(resolvedURLs.get(0)); + } + else if (1 < resolvedURLs.size()) { + reportAmbiguity(resolvedURLs, fixedPath); + } + return Optional.empty(); + } + + @Override + public String toString() { + String result = "["; + result = result + this.classloaderMap.values().stream() + .map(URL::toString) + .collect(Collectors.joining(", ")); + return result + "]"; + } + + public static Optional toPath(URL url) { + try { + return Optional.of(Paths.get(url.toURI())); + } + catch (URISyntaxException e) { + Log.error("0xA1025 The entry " + url + " in the MCPath was invalid.", e); + return Optional.empty(); + } + } + + public static Optional toURL(Path p) { + try { + return Optional.of(p.toUri().toURL()); + } + catch (MalformedURLException e) { + Log.error("0xA1022 The entry " + p + " in the MCPath was invalid.", e); + return Optional.empty(); + } + } + + protected static void reportAmbiguity(List resolvedURLs, String path) { + StringBuilder ambiguityArray = new StringBuilder("{"); + String sep = ""; + for (URL url : resolvedURLs) { + ambiguityArray.append(sep); + sep = ",\n"; + ambiguityArray.append(url.toString()); + } + ambiguityArray.append("}"); + Log.error( + "0xA1294 The following entries for the file `" + path + "` are ambiguous:" + + "\n" + ambiguityArray.toString()); + } + + public void close(){ + classloaderMap.keySet().stream().forEach(c -> { + try { + c.close(); + } + catch (IOException e) { + Log.error("0xA1035 An exception occurred while trying to close a class loader!", e); + } + }); + } + + // A List of all file systems opened for jars. + private static Map openedJarFileSystems = new HashMap<>(); + + public static FileSystem getJarFS(File jar) { + if(openedJarFileSystems.containsKey(jar)){ + FileSystem fs = openedJarFileSystems.get(jar); + if(fs.isOpen()){ + return fs; + } + } + + try { + FileSystem fileSystem = FileSystems.newFileSystem(jar.toPath(), MCPath.class.getClassLoader()); + openedJarFileSystems.put(jar, fileSystem); + return fileSystem; + } + catch (IOException e) { + e.printStackTrace(); + } + return FileSystems.getDefault(); + } + + /** + * Closes all still opened file systems created with {@link MCPath#getJarFS(File)}. + */ + public static void closeAllJarFileSystems(){ + for (FileSystem fs : openedJarFileSystems.values()) { + if (fs.isOpen()) { + try { + fs.close(); + } catch (IOException e){ + e.printStackTrace(); + } + } + } + openedJarFileSystems.clear(); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/parser/Placeholder.java b/monticore-runtime/src/main/java/de/monticore/parser/Placeholder.java new file mode 100644 index 0000000000..58bbc6f881 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/parser/Placeholder.java @@ -0,0 +1,13 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.parser; + +/** + * This class only exists such that + * import de.monticore.parser.* is a valid import. + * Delete this class as soon as the first real class in this package + * exists. + */ +final class Placeholder { + private Placeholder(){} +} diff --git a/monticore-runtime/src/main/java/de/monticore/prettyprint/AstPrettyPrinter.java b/monticore-runtime/src/main/java/de/monticore/prettyprint/AstPrettyPrinter.java new file mode 100644 index 0000000000..ed906f98c0 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/prettyprint/AstPrettyPrinter.java @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.prettyprint; + +import de.monticore.ast.ASTNode; + +public interface AstPrettyPrinter { + + String prettyPrint(T node); + +} diff --git a/monticore-runtime/src/main/java/de/monticore/prettyprint/CommentPrettyPrinter.java b/monticore-runtime/src/main/java/de/monticore/prettyprint/CommentPrettyPrinter.java new file mode 100644 index 0000000000..5ec6a64438 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/prettyprint/CommentPrettyPrinter.java @@ -0,0 +1,56 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.prettyprint; + +import de.monticore.ast.ASTNode; +import de.monticore.ast.Comment; + +public class CommentPrettyPrinter { + + + /** + * Print all comments before an ASTNode + * + * @param a ASTNode that precomments shall be printed + * @param p IndentPrinter that should be used for printing + */ + public static void printPreComments(ASTNode a, IndentPrinter p) { + + if (a.get_PreCommentList() != null && a.get_PreCommentList().size() > 0) { + + // Additional line break for comments if necessary (that means if + // already text exists in current line) + if (!p.isStartOfLine()) { + p.println(); + } + + // print all comments + for (Comment c : a.get_PreCommentList()) { + p.println(c.getText()); + } + } + } + + /** + * Print all comments after an ASTNode + * + * @param a ASTNode that postcomments shall be printed + * @param p IndentPrinter that should be used for printing + */ + public static void printPostComments(ASTNode a, IndentPrinter p) { + + if (a.get_PostCommentList() != null && a.get_PostCommentList().size() > 0) { + + // Additional line break for comments if necessary (that means if + // already text exists in current line) + if (!p.isStartOfLine()) { + p.println(); + } + + // print all comments + for (Comment c : a.get_PostCommentList()) { + p.println(c.getText()); + } + } + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/prettyprint/IndentPrinter.java b/monticore-runtime/src/main/java/de/monticore/prettyprint/IndentPrinter.java new file mode 100644 index 0000000000..a2103d6c84 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/prettyprint/IndentPrinter.java @@ -0,0 +1,398 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.prettyprint; + +/** + * This class can be used as a printer to files or StringBuilders. It adds + * indentation by using the special methods indent(), unindent(). The method + * calls indent() and reindent() affect the current line, even if the first part + * was already printed. Note: Call flushBuffer() at the end in order to flush + * the internal buffer to retrieve the correct output. + * STATE Ok; Useful class for indent strings + */ +public class IndentPrinter { + + // Current level opf indentation + protected int indent = 0; + + // Currrent identation as String + protected String spacer = ""; + + // length of one indent: + // default length is 2 + protected String sp = " "; + + // line length + protected int maxlinelength = -1; + + // optional break (makes additional line breal only after + // optionalBreak()-invocation + protected boolean optionalBreak = false; + + protected int optionalBreakPosition = -1; + + // StringBuilder for for content that can still be moved by indent/unindent + protected StringBuilder linebuffer = new StringBuilder(); + + // StringBuilder for already correctly indented content + protected StringBuilder writtenbuffer; + + /** + * Uses a new interal buffer for writing + */ + public IndentPrinter() { + this(new StringBuilder()); + } + + /** + * Uses this StringBuilder for appending + * + * @param writtenbuffer Buffer to use + */ + public IndentPrinter(StringBuilder writtenbuffer) { + this.writtenbuffer = writtenbuffer; + } + + /** + * Uses startcontent as start of buffer an sets the indention + * + * @param startContent first line of content + * @param indention first indention (e.g. 0 for classes, 1 for methods and + * attributes) + */ + public IndentPrinter(String startContent, int indention) { + this(); + indent(indention); + addLine(startContent); + } + + /** + * Returns the content of the internal buffer Note: This method isn't side + * effect free: It flushes the internal buffer. After calling this method, new + * text added by print/println is automaticly starting in a new line + * + * @return Content of buffer as String + */ + public String getContent() { + + flushBuffer(); + return writtenbuffer.toString(); + } + + /** + * Set length of intendation: number of spaces per level + * + * @param l number of spaces per level (default is 2) + */ + public void setIndentLength(int l) { + sp = ""; + for (int i = 0; i < l; i++) { + sp += " "; + } + + spacer = ""; + for (int i = 0; i < indent; i++) { + spacer += sp; + } + } + + /** + * Returns length of intendation: number of spaces per level + * + * @return number of spaces per level + */ + public int getIndentLength() { + return sp.length(); + } + + /** + * Set the current indentation of the printer + * @return + */ + public void setIndentation(int indent) { + this.indent = indent; + } + + /** + * Returns the current indentation of the printer + * @return + */ + public int getIndentation() { + return indent; + } + + /** + * This method actually does the print. It deals with "\r""\n","\r","\n" in + * the string. This method is not meant for external use. + * + * @param s String to be + */ + protected void doPrint(String s) { + + // get position of "\n" + int pos = s.indexOf("\n"); + while (pos >= 0) { + String substring = s.substring(0, pos); + + // Start new line if string exceeds maxlinelength + if (maxlinelength != -1) { + if (pos + linebuffer.length() > maxlinelength) { + + handleOptionalBreak(); + } + } + // Print up to newline, then a newline and new spacer + linebuffer.append(substring); + + flushBuffer(); + writtenbuffer.append("\n"); + + s = s.substring(pos + 1); + pos = s.indexOf("\n"); + } + + // Start new line if string exceeds maxlinelength + if (maxlinelength != -1) { + if (s.length() + linebuffer.length() > maxlinelength) { + handleOptionalBreak(); + } + } + + linebuffer.append(s); + } + + protected void handleOptionalBreak() { + if (optionalBreak) { + + if (optionalBreakPosition > 0) { + String sub2 = linebuffer.substring(optionalBreakPosition); + linebuffer = linebuffer.delete(optionalBreakPosition, linebuffer.length()); + + flushBuffer(); + writtenbuffer.append("\n"); + + linebuffer.append(sub2); + } + + } + else { + flushBuffer(); + writtenbuffer.append("\n"); + } + } + + /** + * Flushes the internal buffer. After calling this method, new text added by + * print/println is automaticly starting in a new line + */ + public void flushBuffer() { + + optionalBreakPosition = 0; + + // HK: Live with trailing spaces, as comments keep on changing otherwise + // prune trailing spaces + // int i = linebuffer.length() - 1; + // while (i >= 0 && linebuffer.charAt(i) == ' ') + // i--; + // linebuffer.setLength(i + 1); + + // indent nonempty buffers + if (linebuffer.length() != 0) { + writtenbuffer.append(spacer); + writtenbuffer.append(linebuffer); + } + linebuffer.setLength(0); + } + + /** + * For positive values of i: indent i levels For negative values of i: + * unindent -i levels This method call affects the current line, even if the + * first part was already printed. + * + * @param i Number of levels to indent/unindent + */ + public void indent(int i) { + if (i > 0) { + indent += i; + for (int j = 0; j < i; j++) + spacer += sp; + } + else if (i < 0) { + while (i < 0 && indent > 0) { + this.indent--; + spacer = spacer.substring(sp.length()); + i++; + } + } + } + + /** + * Indent one level This method call affects the current line, even if the + * first part was already printed. + */ + public void indent() { + this.indent++; + spacer += sp; + } + + /** + * Unindent one level This method call affects the current line, even if the + * first part was already printed. + */ + public void unindent() { + if (this.indent > 0) { + this.indent--; + spacer = spacer.substring(sp.length()); + } + } + + /** + * Prints the result of the toString() method of Object o or the string "null" + * if o has the null value + * + * @param o Object to be printed + */ + public void print(Object o) { + doPrint((o == null) ? "null" : o.toString()); + } + + public void printWithoutProcessing(Object o) { + + linebuffer.append(o.toString()); + } + + /** + * Prints the result of the toString() method of Object o or the string "null" + * if o has the null value followed by a newline + * + * @param o Object to be printed + */ + public void println(Object o) { + print(o); + println(); + } + + /** + * Prints a newline + */ + public void println() { + doPrint("\n"); + } + + /** + * Prints n newlines + * + * @param n Number of newlines + */ + public void println(int n) { + for (int i = 0; i < n; i++) { + doPrint("\n"); + } + + } + + /** + * Returns the WrittenBuffer without flushing the buffer. This methdo call is + * side effect free, but might not contain the whole buffer. To retrieve the + * complete buffer call flushBuffer() before getWrtttenBuffer() + * + * @return Buffer with already written lines + */ + public StringBuilder getWrittenbuffer() { + return writtenbuffer; + } + + /** + * Returns true if the current position is the beginning of a new line + * + * @return true if current line is empty + */ + public boolean isStartOfLine() { + return (linebuffer.length() == 0); + } + + /** + * adds a line and sets the indention automatically as following: if more "}" + * than "{" in newContent, the next line will be unindented by the difference, + * otherwise the current line will be indented by the difference. If the + * difference is 0, no indention will be changed + * + * @param newContent content to add + */ + public void addLine(Object newContent) { + String nc = newContent.toString().trim(); + int counter = 0; + for (int i = 0; i < nc.length(); i++) { + if (nc.charAt(i) == '{') { + counter++; + } + else if (nc.charAt(i) == '}') { + counter--; + } + } + if (counter < 0) { + indent(counter); + } + print(newContent); + println(); + if (counter > 0) { + indent(counter); + } + } + + /** + * Returns the maximum used line length + * + * @return + */ + public int getMaxlinelength() { + return maxlinelength; + } + + /** + * Sets the maximum used line length + * + * @param maxlinelength + */ + public void setMaxlinelength(int maxlinelength) { + this.maxlinelength = maxlinelength; + } + + /** + * optional break (makes additional line breal only after + * optionalBreak()-invocation + * + * @return + */ + public boolean isOptionalBreak() { + return optionalBreak; + } + + /** + * optional break (makes additional line breal only after + * optionalBreak()-invocation + * + * @param optionBreak + */ + public void setOptionalBreak(boolean optionBreak) { + this.optionalBreak = optionBreak; + } + + public void optionalBreak() { + this.optionalBreakPosition = linebuffer.length(); + } + + public void clearBuffer() { + flushBuffer(); + writtenbuffer.setLength(0); + } + + /** + * Removes all trailing {@linkplain Character#isWhitespace(int) white spaces} on the current line + */ + public void stripTrailing() { + while (linebuffer.length() > 0 && Character.isWhitespace(linebuffer.charAt(linebuffer.length() - 1))) { + linebuffer.deleteCharAt(linebuffer.length() - 1); + } + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/IArtifactScope.java b/monticore-runtime/src/main/java/de/monticore/symboltable/IArtifactScope.java new file mode 100644 index 0000000000..f7a7022d35 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/IArtifactScope.java @@ -0,0 +1,85 @@ +/* (c) https://github.com/MontiCore/monticore */ + + +package de.monticore.symboltable; + +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import static de.se_rwth.commons.Names.getQualifier; +import static de.se_rwth.commons.Names.getSimpleName; +import static de.se_rwth.commons.logging.Log.trace; + +/** + * Common interface for all artifact scopes. + */ +public interface IArtifactScope { + + /** + * This method returns the package name of the current artifact scope. + * If the package is empty or a language does not support packages, + * the method implementation returns an empty String. + * @return + */ + String getPackageName(); + + /** + * This method can be used to set the package name of the current + * artifact scope. + * @param packageName + */ + void setPackageName(String packageName); + + /** + * This method returns the full name of the current artifact scope. + * The full name of an artifact scope is the name of the artifact, + * preceded by the package, if it is not empty. + * @return + */ + String getFullName(); + + /** + * Calculates possible qualified names for the simpleName. For this, + * it considers the (possible) packageName and the imports + * (i.e., import statements). + * + * @param name the simple name of the symbol + * @param packageName the possible package name + * @param imports the import statements + * @return a set of possible qualified names for the simpleName + * @deprecated This method will be removed soon. Instead, symbol table creators should + * qualify names pointing to symbols of foreign models with the respective + * import statements in the model. + */ + @Deprecated + default Set calculateQualifiedNames(String name, String packageName, + List imports) { + final Set potentialSymbolNames = new LinkedHashSet<>(); + + // the simple name (in default package) + potentialSymbolNames.add(name); + + // if name is already qualified, no further (potential) names exist. + if (getQualifier(name).isEmpty()) { + // maybe the model belongs to the same package + if (!packageName.isEmpty()) { + potentialSymbolNames.add(packageName + "." + name); + } + + for (ImportStatement importStatement : imports) { + if (importStatement.isStar()) { + potentialSymbolNames.add(importStatement.getStatement() + "." + name); + } + else if (getSimpleName(importStatement.getStatement()).equals(name)) { + potentialSymbolNames.add(importStatement.getStatement()); + } + } + } + trace("Potential qualified names for \"" + name + "\": " + potentialSymbolNames.toString(), + "IArtifactScope"); + + return potentialSymbolNames; + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/IGlobalScope.java b/monticore-runtime/src/main/java/de/monticore/symboltable/IGlobalScope.java new file mode 100644 index 0000000000..c4d0aa0c75 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/IGlobalScope.java @@ -0,0 +1,67 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symboltable; + +import de.monticore.io.paths.MCPath; +import de.monticore.symboltable.serialization.IDeSer; +import de.monticore.symboltable.serialization.ISymbolDeSer; + +import java.util.Map; + +/** + * Common interface for all global scopes. + */ +public interface IGlobalScope { + + /** + * Getter- and setter methods for the symbol path of this global scope. + * The model path contains paths in which files with stored symbol tables + * are located. + */ + MCPath getSymbolPath(); + + void setSymbolPath(MCPath symbolPath); + + /** + * Getter- and setter methods for the regular expression of file extensions + * for the (symbol) files that the global scope considers for symbol resolution + */ + String getFileExt(); + + void setFileExt(String fileExt); + + /** + * Methods for managing a list with files that this global scope has already loaded + * or attempted to load. This is used to avoid loading the same stored artifact scope + * more than once. + */ + void addLoadedFile(String name); + + void clearLoadedFiles(); + + boolean isFileLoaded(String name); + + /** + * This method initialized global scope attributes such as, e.g., the map of DeSers. + */ + void init(); + + /** + * This method resets all state-based attributes of the global scope including + * the lists with resolvers, the model path entries, the list of loaded files, etc. + * This is useful, e.g., for unit testing the symbol table. + */ + void clear(); + + Map getSymbolDeSers(); + + void setSymbolDeSers(Map symbolDeSers); + + void putSymbolDeSer(String key, ISymbolDeSer value); + + ISymbolDeSer getSymbolDeSer(String key); + + IDeSer getDeSer(); + + void setDeSer(IDeSer deSer); + +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/IScope.java b/monticore-runtime/src/main/java/de/monticore/symboltable/IScope.java new file mode 100644 index 0000000000..7c50084b8b --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/IScope.java @@ -0,0 +1,184 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symboltable; + +import com.google.common.collect.FluentIterable; +import com.google.common.collect.LinkedListMultimap; +import com.google.common.collect.Lists; +import de.monticore.ast.ASTNode; +import de.monticore.symboltable.modifiers.AccessModifier; +import de.monticore.symboltable.modifiers.IncludesAccessModifierSymbolPredicate; +import de.monticore.symboltable.resolving.ResolvedSeveralEntriesForSymbolException; +import de.monticore.visitor.ITraverser; +import de.se_rwth.commons.Splitters; + +import java.util.*; + +import static com.google.common.collect.FluentIterable.from; +import static de.se_rwth.commons.Joiners.DOT; +import static java.util.stream.Collectors.toSet; + +public interface IScope { + + IScope getEnclosingScope(); + + /** + * @return number of symbols directly defined/contained in this scope (not in enclosing scope). + */ + int getSymbolsSize(); + + /** + * @return true, if this scope shadows symbols of the enclosing scope. By default, named scopes + * (see #getName()) are shadowing scopes. + */ + boolean isShadowing(); + + /** + * @return true, if this scope is spanned by a symbol. For example, a Java method spans a + * method scope. + */ + boolean isPresentSpanningSymbol(); + + /** + * States whether this scope exports symbols that can be used from outside the scope. + * For example, a Java class exports symbols. In contrast, a Java if-block does notisym + * export any symbols, hence, its locally defined variables cannot be referenced + * from outside. By default, a scope with a name exports its symbols (although + * this does not apply for Java methods). + * + * @return true, if this scope exports symbols that can be used from outside the scope. + */ + boolean isExportingSymbols(); + + boolean isOrdered(); + + void setExportingSymbols(boolean b); + + void setShadowing(boolean b); + + void setOrdered(boolean b); + + /** + * @param node the corresponding ast node + */ + void setAstNode(ASTNode node); + + void setAstNodeAbsent(); + + ASTNode getAstNode(); + + boolean isPresentAstNode(); + + void setSpanningSymbol(IScopeSpanningSymbol symbol); + + void setSpanningSymbolAbsent(); + + + IScopeSpanningSymbol getSpanningSymbol(); + + void setName(String name); + + void setNameAbsent(); + + String getName(); + + boolean isPresentName(); + + default List getRemainingNameForResolveDown(String symbolName) { + return Lists.newArrayList(isPresentName() && !getName().isEmpty() && symbolName.startsWith(getName()) ? symbolName.substring(getName().length() + 1) : symbolName); + } + + default FluentIterable getNameParts(String symbolName) { + return from(Splitters.DOT.split(symbolName)); + } + + default boolean checkIfContinueWithEnclosingScope(boolean foundSymbols) { + // If this scope shadows its enclosing scope and already some symbols are found, + // there is no need to continue searching. + return !(foundSymbols && isShadowing()); + } + + default boolean checkIfContinueAsSubScope(String symbolName) { + if (this.isExportingSymbols()) { + final List nameParts = getNameParts(symbolName).toList(); + + if (nameParts.size() > 1) { + final String firstNamePart = nameParts.get(0); + // A scope that exports symbols usually has a name. + if (this.isPresentName()) { + return symbolName.startsWith(getName()); + } + else { + return firstNamePart.equals(""); + } + } + } + + return false; + } + + default Optional getResolvedOrThrowException(final Collection resolved) { + Set resolvedSet = new HashSet<>(resolved); + + if (resolvedSet.size() == 1) { + return Optional.of(resolvedSet.iterator().next()); + } else if (resolvedSet.size() > 1) { + throw new ResolvedSeveralEntriesForSymbolException("0xA4095 Found " + resolvedSet.size() + + " symbols: " + resolvedSet.iterator().next().getFullName(), + resolvedSet); + } + + return Optional.empty(); + } + + default List filterSymbolsByAccessModifier(AccessModifier modifier, Collection resolvedUnfiltered) { + return new ArrayList<>(resolvedUnfiltered.stream().filter(new IncludesAccessModifierSymbolPredicate(modifier)).collect(toSet())); + } + + default LinkedListMultimap getUnknownSymbols() { + return LinkedListMultimap.create(); + } + + default List getLocalUnknownSymbols() { + return getUnknownSymbols().values(); + } + + default void add(SymbolWithScopeOfUnknownKind symbol) { + throw new UnsupportedOperationException("This operation is not implemented."); + } + + default void remove(SymbolWithScopeOfUnknownKind symbol) { + throw new UnsupportedOperationException("This operation is not implemented."); + } + + default void accept(ITraverser visitor) { + visitor.handle(this); + } + + /** + * whether this is a subscope of the other scope (transitive) + */ + default boolean isSubScopeOf(IScope superScope) { + if (this == superScope) { + return true; + } + else if (this.getEnclosingScope() != null) { + return this.getEnclosingScope().isSubScopeOf(superScope); + } + else { + return false; + } + } + + /** + * whether this is a proper subscope of the other scope (transitive) + */ + default boolean isProperSubScopeOf(IScope superScope) { + if (this.getEnclosingScope() == null) { + return false; + } + else { + return this.getEnclosingScope().isSubScopeOf(superScope); + } + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/IScopeSpanningSymbol.java b/monticore-runtime/src/main/java/de/monticore/symboltable/IScopeSpanningSymbol.java new file mode 100644 index 0000000000..e5e682b74d --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/IScopeSpanningSymbol.java @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.symboltable; + +public interface IScopeSpanningSymbol extends ISymbol { + + /** + * @return the scope spanned by this symbol. + */ + IScope getSpannedScope(); + +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/ISymbol.java b/monticore-runtime/src/main/java/de/monticore/symboltable/ISymbol.java new file mode 100644 index 0000000000..4172a57510 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/ISymbol.java @@ -0,0 +1,88 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symboltable; + +import com.google.common.collect.ImmutableList; +import de.monticore.ast.ASTNode; +import de.monticore.symboltable.modifiers.AccessModifier; +import de.monticore.symboltable.modifiers.BasicAccessModifier; +import de.monticore.visitor.ITraverser; +import de.se_rwth.commons.SourcePosition; + +import java.util.*; + +import static de.monticore.symboltable.modifiers.AccessModifier.ALL_INCLUSION; + +public interface ISymbol { + + /** + * @return the symbol name + */ + String getName(); + + /** + * @return the package of this symbol. The package name of all symbols within an artifact is + * usually the same. For example, the package name of a state chart p.q.SC and its + * containing state s are the same, i.e., p.q. + * @see #getFullName() + */ + String getPackageName(); + + /** + * @return the package of this symbol. All symbols within an artifact usually have the same + * package name. For example, the state chart p.q.SC and its containing states all + * have the package p.q. + * @see #getPackageName() + */ + String getFullName(); + + /** + * @return Returns the enclosing scope of this symbol. Symbol classes implementing the + * {@link ISymbol} interface override this method and refine the return type to the scASTope + * classes of the language. + */ + IScope getEnclosingScope(); + + /** + * @return the access modifier, such as public or protected in Java. By default, the + * {@link AccessModifier#ALL_INCLUSION} is returned, which indicates that the symbol does not have + * any access modifier. Note that this is not the same as the (implicit) access modifier + * {@link BasicAccessModifier#PACKAGE_LOCAL} of Java. + */ + default AccessModifier getAccessModifier() { + return ALL_INCLUSION; + } + + /** + * Sets the access modifier, such as public or protected in Java. + * + * @param accessModifier the access modifier + */ + void setAccessModifier(AccessModifier accessModifier); + + boolean isPresentAstNode(); + + ASTNode getAstNode(); + + /** + * @return the position of this symbol in the source model. By default, it is the source position + * of the ast node. + */ + default SourcePosition getSourcePosition() { + if (isPresentAstNode()) { + return getAstNode().get_SourcePositionStart(); + } else { + return SourcePosition.getDefaultSourcePosition(); + } + } + + static List sortSymbolsByPosition(final Collection unorderedSymbols) { + final List sortedSymbols = new ArrayList<>(unorderedSymbols); + Collections.sort(sortedSymbols, Comparator.comparing(ISymbol::getSourcePosition)); + return ImmutableList.copyOf(sortedSymbols); + } + + default void accept (ITraverser visitor) { + visitor.handle(this); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/ISymbolPredicate.java b/monticore-runtime/src/main/java/de/monticore/symboltable/ISymbolPredicate.java new file mode 100644 index 0000000000..d404a8de5a --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/ISymbolPredicate.java @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.symboltable; + +import java.util.function.Predicate; + +public interface ISymbolPredicate extends Predicate { + + @Override + boolean test(ISymbol symbol); + +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/ImportStatement.java b/monticore-runtime/src/main/java/de/monticore/symboltable/ImportStatement.java new file mode 100644 index 0000000000..fd888e5c41 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/ImportStatement.java @@ -0,0 +1,35 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.symboltable; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Strings.isNullOrEmpty; + +public class ImportStatement { + + protected final String statement; + protected final boolean isStar; + + public ImportStatement(String statement, boolean isStar) { + checkArgument(!isNullOrEmpty(statement), "An import statement must not be null or empty"); + checkArgument(!statement.endsWith("."), "An import statement must not end with a dot."); + + this.statement = statement; + this.isStar = isStar; + } + + /** + * @return statement + */ + public String getStatement() { + return this.statement; + } + + /** + * @return isStar + */ + public boolean isStar() { + return this.isStar; + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/SymbolWithScopeOfUnknownKind.java b/monticore-runtime/src/main/java/de/monticore/symboltable/SymbolWithScopeOfUnknownKind.java new file mode 100644 index 0000000000..feb9a4c9d9 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/SymbolWithScopeOfUnknownKind.java @@ -0,0 +1,180 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symboltable; + +import java.util.*; + +import de.monticore.symboltable.modifiers.AccessModifier; +import de.monticore.visitor.ITraverser; +import de.se_rwth.commons.Names; +import de.se_rwth.commons.logging.Log; +import de.monticore.ast.ASTNode; + +/** Represents a symbol of a kind unknown to a language. */ +public class SymbolWithScopeOfUnknownKind implements IScopeSpanningSymbol { + + protected String name; + + protected IScope enclosingScope; + + protected AccessModifier accessModifier = AccessModifier.ALL_INCLUSION; + + protected String fullName; + + protected String packageName; + + protected IScope spannedScope; + + public SymbolWithScopeOfUnknownKind(String name) { + this.name = name; + } + + @Override + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public IScope getEnclosingScope() { + return this.enclosingScope; + } + + public void setEnclosingScope(IScope enclosingScope) { + this.enclosingScope = enclosingScope; + } + + /** This method always fails since an unknown symbol may not have a related ASTNode. */ + @Override + public ASTNode getAstNode() { + Log.error("0xA7400 get for AstNode can't return a value. Attribute is empty."); + throw new IllegalStateException(); // Normally this statement is not reachable + } + + /** This method always returns {@code false} since an unknown symbol may not have a related ASTNode. */ + @Override + public boolean isPresentAstNode() { + return false; + } + + @Override + public AccessModifier getAccessModifier() { + return this.accessModifier; + } + + @Override + public void setAccessModifier(AccessModifier accessModifier) { + this.accessModifier = accessModifier; + } + + public void setFullName(String fullName) { + this.fullName = fullName; + } + + @Override + public String getFullName() { + if (this.fullName == null) { + this.fullName = determineFullName(); + } + return this.fullName; + } + + public void setPackageName(String packageName) { + this.packageName = packageName; + } + + @Override + public String getPackageName() { + if (this.packageName == null) { + this.packageName = determinePackageName(); + } + return this.packageName; + } + + @Override + public void accept(ITraverser visitor) { + visitor.handle(this); + } + + protected String determinePackageName() { + IScope optCurrentScope = this.enclosingScope; + + while (optCurrentScope != null) { + final IScope currentScope = optCurrentScope; + + if (currentScope.isPresentSpanningSymbol()) { + // If one of the enclosing scope(s) is spanned by a symbol, take its + // package name. This check is important, since the package name of the + // enclosing symbol might be set manually. + return currentScope.getSpanningSymbol().getPackageName(); + } else if (currentScope instanceof IArtifactScope) { + return ((IArtifactScope) currentScope).getPackageName(); + } + + optCurrentScope = currentScope.getEnclosingScope(); + } + return ""; + } + + protected String determineFullName() { + if (this.enclosingScope == null) { + // There should not be a symbol that is not defined in any scope. This case should only + // occur while the symbol is built (by the symbol table creator). So, here the full name + // should not be cached yet. + return this.name; + } + + final Deque nameParts = new ArrayDeque<>(); + nameParts.addFirst(this.name); + + IScope optCurrentScope = this.enclosingScope; + + while (optCurrentScope != null) { + final IScope currentScope = optCurrentScope; + if (currentScope.isPresentSpanningSymbol()) { + // If one of the enclosing scope(s) is spanned by a symbol, the full name + // of that symbol is the missing prefix, and hence, the calculation + // ends here. This check is important, since the full name of the enclosing + // symbol might be set manually. + nameParts.addFirst(currentScope.getSpanningSymbol().getFullName()); + break; + } + + if (!(currentScope instanceof IGlobalScope)) { + if (currentScope instanceof IArtifactScope) { + // We have reached the artifact scope. Get the package name from the + // symbol itself, since it might be set manually. + if (!getPackageName().isEmpty()) { + nameParts.addFirst(getPackageName()); + } + } else { + if (currentScope.isPresentName()) { + nameParts.addFirst(currentScope.getName()); + } + // ...else stop? If one of the enclosing scopes is unnamed, + // the full name is same as the simple name. + } + optCurrentScope = currentScope.getEnclosingScope(); + } else { + break; + } + } + + return Names.constructQualifiedName(nameParts); + } + + public IScope getSpannedScope() { + if (this.spannedScope == null) throw new IllegalStateException(); + return this.spannedScope; + } + + public void setSpannedScope(IScope scope) { + if (scope != null) { + this.spannedScope = scope; + this.spannedScope.setSpanningSymbol(this); + } + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/SymbolWithScopeOfUnknownKindBuilder.java b/monticore-runtime/src/main/java/de/monticore/symboltable/SymbolWithScopeOfUnknownKindBuilder.java new file mode 100644 index 0000000000..b51f19b327 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/SymbolWithScopeOfUnknownKindBuilder.java @@ -0,0 +1,117 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symboltable; + +import de.monticore.symboltable.modifiers.AccessModifier; +import de.se_rwth.commons.logging.Log; +import de.monticore.ast.ASTNode; + +/** A builder for {@link SymbolWithScopeOfUnknownKind}. */ +public class SymbolWithScopeOfUnknownKindBuilder { + + protected SymbolWithScopeOfUnknownKindBuilder realBuilder; + + protected String name; + protected String fullName; + protected String packageName; + + protected AccessModifier accessModifier; + + protected IScope enclosingScope; + + protected IScope spannedScope; + + public SymbolWithScopeOfUnknownKindBuilder() { + this.realBuilder = this; + } + + public SymbolWithScopeOfUnknownKind build() { + SymbolWithScopeOfUnknownKind symbol = new SymbolWithScopeOfUnknownKind(name); + symbol.setName(this.name); + symbol.setFullName(this.fullName); + symbol.setPackageName(this.packageName); + symbol.setAccessModifier(this.accessModifier); + symbol.setEnclosingScope(this.enclosingScope); + symbol.setSpannedScope(this.spannedScope); + symbol.setName(this.name); + symbol.setFullName(this.fullName); + symbol.setPackageName(this.packageName); + symbol.setAccessModifier(this.accessModifier); + symbol.setEnclosingScope(this.enclosingScope); + return symbol; + } + + public boolean isValid() { + return name != null + && fullName != null + && packageName != null + && accessModifier != null + && enclosingScope != null + && spannedScope != null; + } + + public String getName() { + return this.name; + } + + public String getFullName() { + return this.fullName; + } + + public String getPackageName() { + return this.packageName; + } + + /** This method always fails since an unknown symbol may not have a related ASTNode. */ + public ASTNode getAstNode() { + Log.error("0xA7402 get for AstNode can't return a value. Attribute is empty."); + throw new IllegalStateException(); // Normally this statement is not reachable + } + + /** This method always returns {@code false} since an unknown symbol may not have a related ASTNode. */ + public boolean isPresentAstNode() { + return false; + } + + public AccessModifier getAccessModifier() { + return this.accessModifier; + } + + public IScope getEnclosingScope() { + return this.enclosingScope; + } + + public IScope getSpannedScope() { + return this.spannedScope; + } + + public SymbolWithScopeOfUnknownKindBuilder setName(String name) { + this.name = name; + return this.realBuilder; + } + + public SymbolWithScopeOfUnknownKindBuilder setFullName(String fullName) { + this.fullName = fullName; + return this.realBuilder; + } + + public SymbolWithScopeOfUnknownKindBuilder setPackageName(String packageName) { + this.packageName = packageName; + return this.realBuilder; + } + + public SymbolWithScopeOfUnknownKindBuilder setAccessModifier(AccessModifier accessModifier) { + this.accessModifier = accessModifier; + return this.realBuilder; + } + + public SymbolWithScopeOfUnknownKindBuilder setEnclosingScope(IScope enclosingScope) { + this.enclosingScope = enclosingScope; + return this.realBuilder; + } + + public SymbolWithScopeOfUnknownKindBuilder setSpannedScope(IScope spannedScope) { + this.spannedScope = spannedScope; + return this.realBuilder; + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/SymbolWithScopeOfUnknownKindDeSer.java b/monticore-runtime/src/main/java/de/monticore/symboltable/SymbolWithScopeOfUnknownKindDeSer.java new file mode 100644 index 0000000000..8073e7a66e --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/SymbolWithScopeOfUnknownKindDeSer.java @@ -0,0 +1,66 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symboltable; + +import java.util.function.Supplier; + +import de.monticore.symboltable.serialization.IDeSer; +import de.monticore.symboltable.serialization.ISymbolDeSer; +import de.monticore.symboltable.serialization.JsonDeSers; +import de.monticore.symboltable.serialization.json.JsonObject; +import de.se_rwth.commons.logging.Log; + +/** + * A deserializer for a {@link SymbolWithScopeOfUnknownKind}. + * + *

This DeSer only supports deserialization. Calling {@link #serialize(SymbolWithScopeOfUnknownKind, Object)} will + * fail.

+ */ +public class SymbolWithScopeOfUnknownKindDeSer implements ISymbolDeSer { + + private final IDeSer parent; + private final Supplier scopeFactory; + + /** + * Creates a new {@code SymbolWithScopeOfUnknownKindDeSer}. + * + * @param parent the language DeSer to delegate to when parsing spanned scopes + * @param scopeFactory the factory used to create language-specific spanned scopes + */ + public SymbolWithScopeOfUnknownKindDeSer(IDeSer parent, Supplier scopeFactory) { + this.parent = parent; + this.scopeFactory = scopeFactory; + } + + @Override + public String getSerializedKind() { + return "de.monticore.symboltable.SymbolWithScopeOfUnknownKind"; + } + + @Override + public String serialize(SymbolWithScopeOfUnknownKind toSerialize, Object s2j) { + Log.error("0xA7401 cannot serialize symbol with scope of unknown kind"); + throw new IllegalStateException(); // Normally this statement is not reachable + } + + @Override + public SymbolWithScopeOfUnknownKind deserialize(JsonObject symbolJson) { + SymbolWithScopeOfUnknownKindBuilder builder = new SymbolWithScopeOfUnknownKindBuilder(); + builder.setName(symbolJson.getStringMember(JsonDeSers.NAME)); + + if (symbolJson.hasObjectMember(JsonDeSers.SPANNED_SCOPE)) { + /* + * It is possible (and in fact part of the motivation behind this feature) that unknown spanned scopes contain + * known symbols. To properly handle this case, deserialization is delegated to a "parent" deserializer. (Usually, + * this will be the language-specific DeSer.) + */ + IScope spannedScope = this.parent.deserializeScope(symbolJson.getObjectMember(JsonDeSers.SPANNED_SCOPE)); + builder.setSpannedScope(spannedScope); + } else { + IScope spannedScope = this.scopeFactory.get(); + builder.setSpannedScope(spannedScope); + } + + return builder.build(); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/index.html b/monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/index.html new file mode 100644 index 0000000000..8edc7334ba --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/index.html @@ -0,0 +1,911 @@ + + + + + + + + + + + + + + + + + + + + + Symboltable - MontiCore + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + + +
+
+
+ + + + +
+
+ + + + + + +

Documentation of the Symbol Table Infrastructure

+

Conceptual Model of Symbol Tables

+
    +
  • What is a symbol?
  • +
  • What is a symbol kind?
  • +
  • What is a scope?
  • +
  • What are properties of scopes?
  • +
  • What is symbol resolution?
  • +
+

Define the Symbol Table of a Language via its Grammar

+
    +
  • Indicate that a nonterminal defines a symbol
  • +
  • Indicate that a nonterminal spans a scope
  • +
  • Indicate that a nonterminal uses the name of a symbol
  • +
+

Runtime Environment for Symbol Table Infrastructure

+

This section explains classes and interfaces that are part of the MontiCore runtime environment.

+

Symbol Table Infrastructure Interfaces

+

Most of the interfaces of the MontiCore runtime enviroenment are super types of generated classes +or interfaces that are explained here.

+

IScope Interface

+

This interface is the super type of the generated scope interfaces and thus, it is also transitive +of global scope interfaces and artifact scope interfaces. IScope contains signatures for methods
+realizing the scope's connection to its environment (i.e., AST classes, sub scopes, enclosing scopes). +Generated, language-specific scope interfaces refine the types of these methods.

+

IArtifactScope Interface

+

The IArtifactScope interface is an interface that all generated language-specific artifact scope +interfaces extend. It provides an abstract method for obtaining an artifact scope's package as String. +All further methods have either language-specific arguments or return types and are, thus, introduced +in the language-specific artifact scope interfaces.

+

IGlobalScope Interface

+

The IGlobalScope interface is an interface that all generated language-specific global scope +interfaces extend. It provides an abstract method for obtaining the global scope's Modelpath. +All further methods have either language-specific arguments or return types and are, thus, introduced +in the language-specific global scope interfaces.

+

ISymbol Interface

+

The ISymbol interface is an interface that all generated language-specific symbol classes +implement. It provides the signatures for methods to obtaining the symbol's name, its package, +its fully-qualified name, and its enclosing scope, and its AST node. +Further, the interface contains the signatures of methods for getting and setting the access +modifier of the symbol and default implementations for getting the source position of the symbol. +It also includes a static method for sorting a collection of symbols by their source position, which +is handy for realizing the semantics of ordered scopes. +All further methods have either language-specific arguments or return types and are, thus, introduced +in the specific symbol classes.

+

ISymbolPredicate Interface

+

An ISymbolPredicate is a predicate of a symbol and is used for filtering the results of symbol +resolution. This is explained in more detail in [HR17]. +The MontiCore runtime contains the class IncludesAccessModifierSymbolPredicate, which is an +implementation of a symbol predicate for filtering symbols based on their access modifier.

+

IScopeSpanningSymbol Interface

+

The IScopeSpanningSymbol interface extends the interface ISymbol and adds a method signature +for obtaining the scope that this symbol spans. Symbols that span a scope (which is the case, e.g., +if the respective nonterminal in the grammar is annotated with both the keywords symbol and scope) +implement this interface instead of the ISymbol interface.

+

Modifiers

+

The modifiers contained in the MontiCore runtime implement the interface AccessModifier, which again +is a Modifier. Out of the box, MontiCore supports the two access modifier +implementations BasicAccessModifier and NoAccessModifier. Further, more sophisticated access modifiers +have to be engineered individually, dedicated to their use for a specific modeling language.

+

JSON Infrastructure for Symbol Table Serialization

+

The MontiCore runtime contains classes that are required for serializing and deserializing +symbol tables to Json-encoded Strings. The following explains these in short:

+

JsonPrinter Class

+

The class JsonPrinter wraps the concrete syntax of Json. It is an API for building Json-encoded +String via a series of method calls.

+

JsonParser Class

+

The class JsonParser parses a Json-encoded String into an instance of the Json +abstract syntax model. The central method of this class is the static method +JsonElement parse(String s).

+

Json Parsing Infrastructure

+

Besides the JsonParser class, the MontiCore runtime contains more classes required +for translating JSON-encoded Strings into instances of the Json abstract syntax model. +The class JsonLexer lexes an input String into a sequence of JsonToken instances. +JsonToken instances realizes tokens that have a certain kind in form of a value from the +enumeration JsonTokenKind. The NumberLexer is able to lex all kinds of valid numbers encoded in +Json.

+

Json Model

+

The MontiCore runtime contains a model of the abstract syntax of JSON +that is used by the JsonParser and the JsonPrinter for serialization of symbol tables. +Individual classes exist for the different abstract syntax types of JSON.

+

JsonDeSers Class

+

The class JsonDeSers contains constants and static methods that support the generated language-specific +symbol and scope DeSer classes.

+

Generated Symbol Table Infrastructure

+

MontiCore generates large parts of the symbol table infrastructure that is strongly typed for each +MontiCore language. The following gives a short and technical introduction of each of these +generated classes, interfaces, and enums. The concepts behind each of these infrastructure part if +explained in the MontiCore Reference Manual [HR17].

+ +

Infrastructure Generated per Language

+

This section explains all parts of the symbol table infrastructure that MontiCore generates once per +language. +For scopes, artifact scopes, and global scopes, MontiCore separated classes and interfaces. The +interfaces follow the (multiple) inheritance of the grammars and realized most behavior in form +of default method implementations. The classes implement the interface and manage access to attributes.

+

Scope Interface

+

For each language, MontiCore generates a scope interface. The scope interface prescribes all public +methods of the scope class and realized some methods as default implementations. The hierarchy of +MontiCore grammars is also reflected in the hierarchy of scope interfaces. To realize the multiple +inheritance of MontiCore in Java, the scope interface is separated from the scope class. +If a language inherits from one or more grammars, the scope interface of the language extends all +scope interfaces of the inherited languages. Otherwise, if a language does not inherit from any +language, the scope interface extends the IScope interface from the MontiCore runtime.

+

Scope Class

+

The scope class is generated for each MontiCore language. It implements the scope interface of the +language and realizes scope attributes as well as method implementations that realize direct access +to scope attributes.

+

Scope Builder Class

+

MontiCore generated builder classes for each scope class. The instances of the builders are available through the language's mill. With the builder, the attributes of the scope class can be initialized and a new instance of the scope can be created.

+

We highly recommend instantiating scope classes only through the builder obtained via the mill. All other forms of instantiations will prohibit reconfiguration through sublanguages.

+

ArtifactScope Interface

+

The artifact scope interface is generated once for each MontiCore language. It extends the scope +interface of the language and the artifact scope interface of the MontiCore runtime. +Artifact scopes are instantiated once for each processed artifact and, conceptually, slightly differ +from scopes established within a model. To this end, their realization overrides some methods +of the scope interface with a special behavior and adds new methods.

+

ArtifactScope Class

+

MontiCore generates a single artifact scope class for each language that extends the scope class of +the language and implements the artifact scope interface of the language.

+

ArtifactScope Builder Class

+

MontiCore generated builder classes for each artifact scope class. The instances of the builders are available through the language's mill. With the builder, the attributes of the artifact scope class can be initialized and a new instance of the artifact scope can be created.

+

We highly recommend instantiating artifact scope classes only through the builder obtained via the mill. All other forms of instantiations will prohibit reconfiguration through sublanguages.

+

GlobalScope Interface

+

Similar to artifact scope interfaces, global scope interfaces extends the scope interface of the language. +Additionally, they implement the global scope interfaces of their parent languages or the +IGlobalScope of the MontiCore runtime if the languages do not inherit from another language.

+

GlobalScope Class

+

The global scope class is generated for each MontiCore language and realizes the concrete global +scope of a language. It extends the scope class and implements the global scope iterface of the +language.

+

GlobalScope Builder Class

+

MontiCore generated builder classes for each global scope class. The instances of the builders are available through the language's mill. With the builder, the attributes of the global scope class can be initialized and a new instance of the global scope can be created.

+

We highly recommend instantiating global scope classes only through the builder obtained via the mill. All other forms of instantiations will prohibit reconfiguration through sublanguages.

+

SymbolTableCreator Interface

+

TODO: SymbolTableCreator Interface is about to be changed

+

SymbolTableCreator Class

+

TODO: SymbolTableCreator Class is about to be changed

+

Common Symbol Interface

+

The common symbol interface of a language extends the MontiCore runtime class ISymbol and provides +methods for the connection to the enclosing scope and the visitor of the language. As these are specifically +typed for each language, the common symbol interface is generated. All symbol classes of a language +implement the common symbol interface.

+

SymbolTablePrinter

+

The symbol table printer traverses the scope tree of an artifact using a visitor and serializes it +in form of a JSON-encoded String. Traversal typically begins with an artifact scope. In each scope, +the local symbols are visited and serialized. If a symbol spans a scope, the spanned scope is visited +while visiting the symbol. It, therefore, realizes traversal of the scope tree along the +enclosingScope <-> localSymbols and the symbol <-> spannedScope associations. Symbol table printers +are used by ScopeDeSers and SymbolDeSers. For language composition, the symbol table printers of +individual languages are combined with a delegator visitor in the DeSer classes.

+

ScopeDeSer

+

The scope deser class provides methods realizing the loading and storing of scope objects of a language. +Besides this, it realizes the deserialization of scopes. The deserialization of symbols within this +scope is delegated to the respective symbol DeSers and serialization of symbols and scopes is +delegated to the symbol table printer. The reason for this separation is that employing a visitor is +suitable for serialization, but not for deserialization. The latter would visit elements of the +abstract syntax of JSON, such as a Json object, and would require a large number of case distinctions +within handling different objects that can be serialized as a Json object. Combining the visitor-based +serialization and the deserialization into a single class would be inefficient in terms of compilation +time.

+ +

Generated per Symbol

+

This section explains parts of the symbol table infrastructure that MontiCore generates once for +each symbol of a language.

+

Symbol Class

+

For each symbol of the language, MontiCore generates a symbol class that implements the common +symbol interface of the language. The symbol class realizes symbols of a certain kind. For example, +the class StateSymbol realizes the kind StateSymbol and objects of this class are concrete symbols. +A symbol kind can inherit from at most one other symbol kind. This is reflected in the symbol classes +by extending the class of the super kind.

+

Symbol Builder Class

+

MontiCore generated builder classes for each symbol. The instances of the builders are available through +the language's mill. With the builder, the attributes of the symbol class can be initialized and a new instance of the symbol can be created.

+

We highly recommend instantiating symbol classes only through the builder obtained via the mill. All other forms of instantiations will prohibit reconfiguration through sublanguages, e.g., in case the symbol production is overridden in the grammar.

+

Symbol DeSer

+

The symbol DeSer classes are generated for each symbol and realize serialization and deserialization +of symbols of a certain kind. The serialization is visitor-based and, thus, delegated to the symbol +table printer. Symbol DeSers are used by scope DeSers to realize the deserialization of symbols and +as such, are reused for all languages that inherit from the current language. As serialization and +deserialization of individual symbols is rarely triggered manually, no load and store methods exist +in symbol DeSer classes.

+

Symbol Surrogate Class

+

Symbol surrogate classes extend the generated symbol classes and realize lazy loading of symbls of this kind. Surrogates have a delegate of the symbol class that is empty during initialization of the surrogate., where only the enclosing scope and the name are set. They further define a method for resolving the symbol +with the on demand. +Symbol surrogates must only be if both of the following two conditions are met: +1. If on type level, a symbol has an attribute of another symbol, the attribute may be initialized with the surrogate as the symbol's subtype. +2. If on instance level, the symbol definition of the +Surrogates must never be used to simplify instantiation of local symbols, i.e., of symbols that are contained in a single model for which the symbol table currently is build. In this case, it is always possible to split symbol table creation into multiple phases: In the first phase, all symbol definitions +instantiate symbol class objects, for which the symbol attributes are not instantiated yet. In a later phase, the symbol attributes are filled with values.

+

Symbol Surrogate Builder

+

MontiCore generated builder classes for each symbol surrogate. The instances of the builders are available through the language's mill. With the builder, the attributes of the symbol surrogate class can be initialized and a new instance of the symbol surrogate can be created.

+

Resolvers

+

MontiCore generates a resolver interface for each symbol kind of a language. Resolvers +have a method for resolving adapted symbol kinds. Language engineers can develop concrete resolving delegates +that implement a resolver interface. Such classes can be added to the global scope of a language +to integrate resolving for adapted symbols into the resolution process. +For example, an automata language defines the generated resolver interface IStateSymbolResolver. +This interface can be used by language engineers to implement a CDClass2StateResolver class +implementing the interface that resolves, for example, for symbols of a CD class whenever +resolving for state symbols is invoked. The result of this is typically an adapter symbol, which +adapts the foreign symbol (e.g., CDClassSymbol) to the expected symbol (e.g., StateSymbol).

+ +

Further Information

+ + + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/modifiers/AccessModifier.java b/monticore-runtime/src/main/java/de/monticore/symboltable/modifiers/AccessModifier.java new file mode 100644 index 0000000000..a2eb9606ce --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/modifiers/AccessModifier.java @@ -0,0 +1,45 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.symboltable.modifiers; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public interface AccessModifier extends Modifier { + + /** + * Access modifier that includes all other access modifiers. It can be used + * to resolve symbols having any access modifier. + */ + AccessModifier ALL_INCLUSION = new AllInclusionAccessModifier(); + + boolean includes(AccessModifier modifier); + + Map getDimensionToModifierMap(); + + default CompoundAccessModifier shallowCopy() { + return new CompoundAccessModifier( + new ArrayList<>(getDimensionToModifierMap().values()) + ); + } + + final class AllInclusionAccessModifier implements AccessModifier { + + protected static final String DIMENSION = "All"; + + @Override + public boolean includes(AccessModifier modifier) { + return true; + } + + private AllInclusionAccessModifier() { + } + + @Override + public Map getDimensionToModifierMap() { + return Map.of(DIMENSION, this); + } + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/modifiers/BasicAccessModifier.java b/monticore-runtime/src/main/java/de/monticore/symboltable/modifiers/BasicAccessModifier.java new file mode 100644 index 0000000000..f297ab656e --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/modifiers/BasicAccessModifier.java @@ -0,0 +1,104 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.symboltable.modifiers; + +import java.util.Map; + +public enum BasicAccessModifier implements AccessModifier { + + PUBLIC { + @Override + public boolean includes(AccessModifier modifier) { + AccessModifier visibility = modifier.getDimensionToModifierMap().get(DIMENSION); + if(visibility != null){ + return visibility.equals(PUBLIC); + } + return true; + } + + @Override + public Map getDimensionToModifierMap() { + return Map.of(DIMENSION, this); + } + + @Override + public String toString() { + return "public"; + } + + + }, + + PROTECTED { + @Override + public boolean includes(AccessModifier modifier) { + AccessModifier visibility = modifier.getDimensionToModifierMap().get(DIMENSION); + if(visibility != null){ + return (visibility.equals(PUBLIC) + || visibility.equals(PROTECTED)); + } + return true; + } + + @Override + public String toString() { + return "protected"; + } + + @Override + public Map getDimensionToModifierMap() { + return Map.of(DIMENSION, this); + } + }, + + PACKAGE_LOCAL { + @Override + public boolean includes(AccessModifier modifier) { + AccessModifier visibility = modifier.getDimensionToModifierMap().get(DIMENSION); + if(visibility != null){ + return (visibility.equals(PUBLIC) + || visibility.equals(PROTECTED) + || visibility.equals(PACKAGE_LOCAL)); + } + return true; + } + + @Override + public String toString() { + return "package_local"; + } + + @Override + public Map getDimensionToModifierMap() { + return Map.of(DIMENSION, this); + } + }, + + PRIVATE { + @Override + public boolean includes(AccessModifier modifier) { + AccessModifier visibility = modifier.getDimensionToModifierMap().get(DIMENSION); + if(visibility != null){ + return (visibility.equals(PUBLIC) + || visibility.equals(PROTECTED) + || visibility.equals(PACKAGE_LOCAL) + || visibility.equals(PRIVATE)); + } + return true; + } + + @Override + public String toString() { + return "private"; + } + + @Override + public Map getDimensionToModifierMap() { + return Map.of(DIMENSION, this); + } + + }, + ; + + public static final String DIMENSION = "Visibility"; +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/modifiers/CompoundAccessModifier.java b/monticore-runtime/src/main/java/de/monticore/symboltable/modifiers/CompoundAccessModifier.java new file mode 100644 index 0000000000..53d9bec99d --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/modifiers/CompoundAccessModifier.java @@ -0,0 +1,52 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symboltable.modifiers; + +import de.se_rwth.commons.logging.Log; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class CompoundAccessModifier implements AccessModifier { + + protected Map dimensionToModifier; + + public CompoundAccessModifier(List modifiers) { + this.dimensionToModifier = new HashMap<>(); + for(AccessModifier modifier: modifiers){ + Map modifierMap = modifier.getDimensionToModifierMap(); + for(Map.Entry entry: modifierMap.entrySet()){ + if(dimensionToModifier.get(entry.getKey()) != null){ + Log.warn("0xA0143 Multiple entries for dimension " + entry.getKey()); + } + dimensionToModifier.put(entry.getKey(), entry.getValue()); + } + } + } + + public CompoundAccessModifier(AccessModifier... modifiers){ + this(Arrays.stream(modifiers).collect(Collectors.toList())); + } + + @Override + public boolean includes(AccessModifier modifier) { + for(Map.Entry entry: modifier.getDimensionToModifierMap().entrySet()){ + String key = entry.getKey(); + AccessModifier value = entry.getValue(); + if(this.dimensionToModifier.containsKey(key)){ + if(!this.dimensionToModifier.get(key).includes(value)){ + return false; + } + } + } + return true; + } + + + @Override + public Map getDimensionToModifierMap() { + return dimensionToModifier; + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/modifiers/IncludesAccessModifierSymbolPredicate.java b/monticore-runtime/src/main/java/de/monticore/symboltable/modifiers/IncludesAccessModifierSymbolPredicate.java new file mode 100644 index 0000000000..cb8cde07ba --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/modifiers/IncludesAccessModifierSymbolPredicate.java @@ -0,0 +1,26 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.symboltable.modifiers; + +import com.google.common.base.Predicate; +import de.monticore.symboltable.ISymbol; +import de.monticore.symboltable.ISymbolPredicate; + + +public class IncludesAccessModifierSymbolPredicate implements ISymbolPredicate { + + protected final AccessModifier modifier; + + public IncludesAccessModifierSymbolPredicate(AccessModifier modifier) { + this.modifier = modifier; + } + + /** + * @see Predicate#apply(Object) + */ + @Override + public boolean test(ISymbol symbol) { + return modifier.includes(symbol.getAccessModifier()); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/modifiers/Modifier.java b/monticore-runtime/src/main/java/de/monticore/symboltable/modifiers/Modifier.java new file mode 100644 index 0000000000..d64dd385d7 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/modifiers/Modifier.java @@ -0,0 +1,7 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.symboltable.modifiers; + +public interface Modifier { + +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/modifiers/NoAccessModifier.java b/monticore-runtime/src/main/java/de/monticore/symboltable/modifiers/NoAccessModifier.java new file mode 100644 index 0000000000..a45ddbd608 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/modifiers/NoAccessModifier.java @@ -0,0 +1,28 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.symboltable.modifiers; + +import java.util.Map; + +public final class NoAccessModifier implements AccessModifier { + + public static final NoAccessModifier INSTANCE = new NoAccessModifier(); + + private NoAccessModifier() { + + } + + /** + * @see AccessModifier#includes(AccessModifier) + */ + public boolean includes(AccessModifier modifier) { + return true; + } + + @Override + public Map getDimensionToModifierMap() { + return Map.of(DIMENSION, this); + } + + protected final static String DIMENSION = "All"; +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/modifiers/ReadableAccessModifier.java b/monticore-runtime/src/main/java/de/monticore/symboltable/modifiers/ReadableAccessModifier.java new file mode 100644 index 0000000000..8fbb64ba19 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/modifiers/ReadableAccessModifier.java @@ -0,0 +1,42 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symboltable.modifiers; + +import java.util.Map; + +public enum ReadableAccessModifier implements AccessModifier { + + READABLE { + @Override + public boolean includes(AccessModifier modifier) { + AccessModifier readable = modifier.getDimensionToModifierMap().get(DIMENSION); + if(readable != null){ + return readable.equals(READABLE); + } + return true; + } + + @Override + public Map getDimensionToModifierMap() { + return Map.of(DIMENSION, this); + } + }, + + NON_READABLE { + @Override + public boolean includes(AccessModifier modifier) { + AccessModifier readable = modifier.getDimensionToModifierMap().get(DIMENSION); + if(readable != null){ + return readable.equals(NON_READABLE); + } + return true; + } + + @Override + public Map getDimensionToModifierMap() { + return Map.of(DIMENSION, this); + } + } + ; + + public static final String DIMENSION = "Readable"; +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/modifiers/StaticAccessModifier.java b/monticore-runtime/src/main/java/de/monticore/symboltable/modifiers/StaticAccessModifier.java new file mode 100644 index 0000000000..d01c533a63 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/modifiers/StaticAccessModifier.java @@ -0,0 +1,53 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symboltable.modifiers; + +import java.util.Map; + +public enum StaticAccessModifier implements AccessModifier { + + STATIC { + @Override + public boolean includes(AccessModifier modifier) { + AccessModifier staticModifier = modifier.getDimensionToModifierMap().get(DIMENSION); + if(staticModifier != null){ + return staticModifier.equals(STATIC); + } + return true; + } + + @Override + public Map getDimensionToModifierMap() { + return Map.of(DIMENSION, this); + } + + @Override + public String toString() { + return "static"; + } + + }, + + NON_STATIC { + @Override + public boolean includes(AccessModifier modifier) { + AccessModifier staticModifier = modifier.getDimensionToModifierMap().get(DIMENSION); + if(staticModifier != null){ + return staticModifier.equals(NON_STATIC); + } + return true; + } + + @Override + public Map getDimensionToModifierMap() { + return Map.of(DIMENSION, this); + } + + @Override + public String toString() { + return ""; + } + } + ; + + public static final String DIMENSION = "Static"; +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/modifiers/WritableAccessModifier.java b/monticore-runtime/src/main/java/de/monticore/symboltable/modifiers/WritableAccessModifier.java new file mode 100644 index 0000000000..c37d36b9ed --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/modifiers/WritableAccessModifier.java @@ -0,0 +1,43 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symboltable.modifiers; + +import java.util.Map; + +public enum WritableAccessModifier implements AccessModifier { + + + WRITABLE { + @Override + public boolean includes(AccessModifier modifier) { + AccessModifier writeable = modifier.getDimensionToModifierMap().get(DIMENSION); + if(writeable != null){ + return writeable.equals(WRITABLE); + } + return true; + } + + @Override + public Map getDimensionToModifierMap() { + return Map.of(DIMENSION, this); + } + }, + + NON_WRITABLE { + @Override + public boolean includes(AccessModifier modifier) { + AccessModifier writeable = modifier.getDimensionToModifierMap().get(DIMENSION); + if(writeable != null){ + return writeable.equals(NON_WRITABLE); + } + return true; + } + + @Override + public Map getDimensionToModifierMap() { + return Map.of(DIMENSION, this); + } + } + ; + + public static final String DIMENSION = "Writable"; +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/resolving/ISymbolAdapter.java b/monticore-runtime/src/main/java/de/monticore/symboltable/resolving/ISymbolAdapter.java new file mode 100644 index 0000000000..e3835278f7 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/resolving/ISymbolAdapter.java @@ -0,0 +1,15 @@ +/* (c) https://github.com/MontiCore/monticore */ + + +package de.monticore.symboltable.resolving; + +import de.monticore.symboltable.ISymbol; + +public interface ISymbolAdapter { + + T getAdaptee(); + + default String getName() { + return getAdaptee().getName(); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/resolving/ResolvedSeveralEntriesForSymbolException.java b/monticore-runtime/src/main/java/de/monticore/symboltable/resolving/ResolvedSeveralEntriesForSymbolException.java new file mode 100644 index 0000000000..5447a10ccf --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/resolving/ResolvedSeveralEntriesForSymbolException.java @@ -0,0 +1,32 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.symboltable.resolving; + +import de.monticore.symboltable.ISymbol; + +import java.util.ArrayList; +import java.util.Collection; + +import static com.google.common.collect.ImmutableList.copyOf; + +public class ResolvedSeveralEntriesForSymbolException extends RuntimeException { + + private static final long serialVersionUID = 931330102959575779L; + + protected Collection symbols = new ArrayList<>(); + + public ResolvedSeveralEntriesForSymbolException(String message, Collection symbols) { + super(message); + this.symbols = new ArrayList<>(symbols); + } + + public ResolvedSeveralEntriesForSymbolException(Collection symbols) { + this("", symbols); + } + + @SuppressWarnings("unchecked") + public Collection getSymbols() { + return (Collection) copyOf(symbols); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/IDeSer.java b/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/IDeSer.java new file mode 100644 index 0000000000..572863f5fb --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/IDeSer.java @@ -0,0 +1,94 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symboltable.serialization; + +import de.monticore.symboltable.IArtifactScope; +import de.monticore.symboltable.IScope; +import de.monticore.symboltable.serialization.json.JsonObject; + +/** + * Interface that all scope DeSers implement to provide uniform serialize and + * deserialize methods. This is required to handle DeSers in the global scopes. + * A DeSer class realizes the serialization strategy for a specific type T. + * + * @param The type to serialize, i.e., a language-specific artifact scope interface + * @param The language-specific Symbols2Json Class for traversing the symbol table + */ +public interface IDeSer { + + /** + * serialize a passed artifact scope object to a String that is returned. + * + * @param toSerialize + * @param symbol2json + * @return + */ + String serialize(A toSerialize, J symbol2json); + /** + * serialize a passed scope object to a String that is returned. + * + * @param toSerialize + * @param symbol2json + * @return + */ + String serialize(S toSerialize, J symbol2json); + + /** + * Hook point for realizing additional serializations of a passed + * artifact scope object. + * + * @param toSerialize + * @param symbol2json + * @return + */ + default void serializeAddons(A toSerialize, J symbol2json) {} + + /** + * Hook point for realizing additional serializations of a passed + * scope object. + * + * @param toSerialize + * @param symbol2json + * @return + */ + default void serializeAddons(S toSerialize, J symbol2json) {} + + /** + * deserialize a passed artifact scope object. + * + * @param scopeJson + * @return + */ + A deserializeArtifactScope(JsonObject scopeJson); + /** + * deserialize a passed scope object. + * + * @param scopeJson + * @return + */ + S deserializeScope(JsonObject scopeJson); + + /** + * Hook point for realizing additional deserializations of a passed + * artifact scope object. + * + * @param artifactScope + * @param scopeJson + * @return + */ + default void deserializeAddons(A artifactScope, JsonObject scopeJson) { + } + + /** + * Hook point for realizing additional deserializations of a passed + * scope object. + * + * @param scope + * @param scopeJson + * @return + */ + default void deserializeAddons(S scope, JsonObject scopeJson) { + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/ISymbolDeSer.java b/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/ISymbolDeSer.java new file mode 100644 index 0000000000..0ae163caa6 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/ISymbolDeSer.java @@ -0,0 +1,43 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symboltable.serialization; + +import de.monticore.symboltable.ISymbol; +import de.monticore.symboltable.serialization.json.JsonObject; + +/** + * Interface that all symbol DeSers implement to provide uniform serialize and + * deserialize methods. This is required to handle DeSers in the global scopes. + * A DeSer class realizes the serialization strategy for a specific type T. + * @param The kind of the symbol to serialize + * @param The language-specific Symbols2Json Class for traversing the Symbol table + */ +public interface ISymbolDeSer { + + /** + * serialize a passed object to a String that is returned. + * @param toSerialize + * @param symbol2json + * @return + */ + String serialize (S toSerialize, J symbol2json); + + /** + * Deserialize a passed String to an instance of the type T + * @param serialized + * @return + */ + default S deserialize (String serialized){ + JsonObject symbol = JsonParser.parseJsonObject(serialized); + return deserialize(symbol); + } + + /** + * Deserialize a passed Json Object to an instance of the type T + * @param serialized + * @return + */ + S deserialize (JsonObject serialized); + + String getSerializedKind(); + +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/JsonDeSers.java b/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/JsonDeSers.java new file mode 100644 index 0000000000..fb41accd28 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/JsonDeSers.java @@ -0,0 +1,115 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symboltable.serialization; + +import de.monticore.symboltable.serialization.json.JsonElement; +import de.monticore.symboltable.serialization.json.JsonObject; +import de.se_rwth.commons.logging.Log; + +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * Collection of (static) methods and constants that support using DeSers in combination with Json. + */ +public class JsonDeSers { + + public static final String PACKAGE = "package"; + + public static final String IS_SHADOWING_SCOPE = "isShadowingScope"; + + public static final String NAME = "name"; + + public static final String KIND = "kind"; + + public static final String SYMBOLS = "symbols"; + + public static final String SPANNED_SCOPE = "spannedScope"; + + /** + * This method deserializes a stored package. If no package is stored, the default + * empty package ("") is returned. + * + * @param scopeJson + * @return + */ + public static String getPackage(JsonObject scopeJson) { + return scopeJson.getStringMemberOpt(PACKAGE).orElse(""); + } + + /** + * This method returns a list of JsonObjects from a passed serialized scope. + * @param scopeJson + * @return + */ + public static List getSymbols(JsonObject scopeJson) { + List symbols = new ArrayList<>(); + if (scopeJson.hasArrayMember(SYMBOLS)) { + for (JsonElement e : scopeJson.getArrayMember(SYMBOLS)) { + if(e.isJsonObject()){ + symbols.add(e.getAsJsonObject()); + } + else { + Log.error("0xA1233 Serialized symbol is not a JSON object: '" + e + "'."); + } + } + } + return symbols; + } + + public static String getKind(JsonObject symbol) { + Optional kind = getKindOpt(symbol); + + if (!kind.isPresent()) { + Log.error("0xA1235 Serialized object does not have a kind attribute: '" + symbol + "'."); + return "error"; + } + + return kind.get(); + } + + public static Optional getKindOpt(JsonObject symbol) { + return symbol.getStringMemberOpt(KIND); + } + + /** + * This method checks, if a passed JsonElement is a Json object of a certain passed kind. + * It is useful to check, whether a DeSer that can deserialize the passed deSerSymbolKind is + * capable of deserializing the passed serializedElement. + * + * @param deSerKind + * @param serializedElement + * @return + */ + public static void checkCorrectDeSerForKind(String deSerKind, JsonElement serializedElement) { + if (!serializedElement.isJsonObject()) { + Log.error("0xA7223 DeSer for kind '" + deSerKind + "' can only deserialize Json objects! '" + + serializedElement + "' is not a Json object."); + return; //return here to avoid consecutive errors in this method + } + JsonObject o = serializedElement.getAsJsonObject(); + if (!o.hasMember(KIND)) { + Log.error("0xA7224 Serialized symbol table classes must have a member describing their " + + "kind. The Json object '" + serializedElement + + "' does not have a member describing the kind."); + return; //return here to avoid consecutive errors in this method + } + if (!deSerKind.equals(o.getStringMember(KIND))) { + Log.error("0xA7225 DeSer for kind '" + deSerKind + "' cannot deserialize Json objects" + + " of kind '" + o.getStringMember(KIND) + "'"); + } + } + + public static String getSymbolFilePath(String symbolPath, String modelName, + String modelFileExtension) { + String simpleFileName = modelName + "." + modelFileExtension + "sym"; + return Paths.get(symbolPath, simpleFileName).toString(); + } + + public static String getSymbolFilePath(String symbolPath, String packagePath, String modelName, + String modelFileExtension) { + String simpleFileName = modelName + "." + modelFileExtension + "sym"; + return Paths.get(symbolPath, packagePath, simpleFileName).toString(); + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/JsonLexer.java b/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/JsonLexer.java new file mode 100644 index 0000000000..2db7c8205d --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/JsonLexer.java @@ -0,0 +1,251 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.symboltable.serialization; + +import de.se_rwth.commons.logging.Log; + +import static de.monticore.symboltable.serialization.JsonToken.*; +import static de.monticore.symboltable.serialization.JsonTokenKind.NUMBER; +import static de.monticore.symboltable.serialization.JsonTokenKind.STRING; + +/** + * This class is responsible to tokenize a String that encoded as JSON. The length of the input, + * thus, is limited by the maximum length of a String. + */ +public class JsonLexer { + + protected String json; + + //current position within the input string + protected int pos = 0; + + //currently peeked token, or null is not token has been peeked + protected JsonToken peeked; + + protected NumberLexer numbers; + + /** + * The lexer tokenizes the passed input String. + * + * @param input + */ + public JsonLexer(String input) { + json = input; + peeked = null; + numbers = new NumberLexer(); + } + + /** + * returns truee, iff the end of the String lexed so far is not reached. + * + * @return + */ + public boolean hasNext() { + return peek() != null; + } + + /** + * Returns the part of the input that has not been lexed yet (e.g., for prin + * + * @return + */ + public String getRemainder() { + return json; + } + + /** + * reads the next token of the input, without consuming it. In Json, each kind of token can be + * identified by its first character. + * + * @return + */ + public JsonToken peek() { + if (null != peeked) { + return peeked; + } + if (pos >= json.length()) { + return null; + } + char first = json.charAt(pos); + if (first == '[') { + pos++; + peeked = BEGIN_ARRAY; + return BEGIN_ARRAY; + } + if (first == ']') { + pos++; + peeked = END_ARRAY; + return END_ARRAY; + } + if (first == '{') { + pos++; + peeked = BEGIN_OBJECT; + return BEGIN_OBJECT; + } + if (first == '}') { + pos++; + peeked = END_OBJECT; + return END_OBJECT; + } + if (first == ':') { + pos++; + peeked = COLON; + return COLON; + } + if (first == ',') { + pos++; + peeked = COMMA; + return COMMA; + } + if (first == 't' && la(1) == 'r' && la(2) == 'u' && la(3) == 'e') { + pos += 4; + peeked = BOOLEAN_TRUE; + return BOOLEAN_TRUE; + } + if (first == 'f' && la(1) == 'a' && la(2) == 'l' && la(3) == 's' + && la(4) == 'e') { + pos += 5; + peeked = BOOLEAN_FALSE; + return BOOLEAN_FALSE; + } + if (first == 'n' && la(1) == 'u' && la(2) == 'l' && la(3) == 'l') { + pos += 4; + peeked = NULL; + return NULL; + } + if (first == '"') { + return checkString(); + } + if (WHITESPACE_CHARACTERS.indexOf(first) > -1) { + while (pos < json.length() && WHITESPACE_CHARACTERS.indexOf(json.charAt(pos)) > -1) { + pos++; + } + peeked = WHITESPACE; + return WHITESPACE; + } + if ("-0123456789".indexOf(first) > -1) { + return checkNumber(); + } + peeked = null; + Log.error("0xA0591 Unexpected end during lexing!"); + return null; + } + + /** + * Returns the character at the i-th position from the current position of the input. + * + * @param i + * @return + */ + protected char la(int i) { + return json.charAt(pos + i); + } + + /** + * returns the next token and removes it + * + * @return + */ + public JsonToken poll() { + JsonToken current = peek(); + if (null == current) { + Log.error("0xA0592 unexpected end of model during lexing!"); + return null; + } + else { + peeked = null; + return current; + } + } + + /** + * try to read a number starting at the current location + * + * @return + */ + protected JsonToken checkNumber() { + numbers.reset(); + while (pos < json.length() + && "0123456789eE-+.".indexOf(json.charAt(pos)) > -1 + && !numbers.hasError()) { + numbers.step(json.charAt(pos)); + pos++; + } + if (numbers.isInFinalState()) { + peeked = new JsonToken(NUMBER, numbers.getResult()); + } + else { + Log.error("0xA0593 Invalid number!"); + } + return peeked; + } + + /** + * checks, if the current input is a valid JSON String, i.e. if it is conform to the regex: + * String regex = "" + * + "\"" //start with '"' + * + "(" //begin iteration of characters + * + "[^\\\"\\\\]" //every character except '"' or "\" + * + "|\\\\u[0-9A-Fa-f]{4}" //or unicode escape sequence + * + "|\\\\[bfnrt\"/\\\\]" // or other escape sequence + * + ")*" //end iteration of characters + * + "\""; //end with '"' + * + * @return + */ + protected JsonToken checkString() { + int beginPos = pos; + StringBuilder result = new StringBuilder(); + pos++; //skip first quotes + while (pos < json.length()) { + char c = json.charAt(pos); + //check if valid escape sequence + if (c == '\\') { + result.append('\\'); + pos++; + c = json.charAt(pos); + result.append(c); + // if backslash and u occurs, 4 digits must follow + if (c == 'u') { + if ((DIGITS.indexOf(la(1)) != -1 && DIGITS.indexOf(la(2)) != -1 + && DIGITS.indexOf(la(3)) != -1 && DIGITS.indexOf(la(4)) != -1)) { + result.append(la(1)); + result.append(la(2)); + result.append(la(3)); + result.append(la(4)); + } + else { + Log.error( + "0xA0594 Invalid escape sequence in String during lexing! 'u' must be followed by four digits"); + } + pos = pos + 5; + } + // else, check whether other allowed escaped character + else if ("\\\"bfnrt".indexOf(c) != -1) { + pos++; + } + else { + Log.error("0xA0595 Invalid escape sequence in String during lexing! An escaped '" + c + + "' is not allowed"); + } + } + //else these are unescaped quotes, the string ends here and is valid + else if (c == '\"') { + pos++; + peeked = new JsonToken(STRING, result.toString()); + return peeked; + } + else { + //random valid character + result.append(c); + pos++; + } + } + return null; + } + + protected static final String WHITESPACE_CHARACTERS = " \t\n\f\r"; + + protected static final String DIGITS = "0123456789"; + +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/JsonParser.java b/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/JsonParser.java new file mode 100644 index 0000000000..476faf7c55 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/JsonParser.java @@ -0,0 +1,290 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symboltable.serialization; + +import de.monticore.symboltable.serialization.json.*; +import de.se_rwth.commons.logging.Log; + +/** + * Parses serialized JSON Strings into an intermediate JSON data structure. This data structure can + * then be used, e.g., to build Java objects with their builders. + */ +public class JsonParser { + + /** + * Parses a given String encoded in JSON to a {@link JsonElement}. This method should be used if + * the Json type (i.e., Object, Array,...) of the encoded JSON is unclear. + * + * @param s + * @return + */ + public static JsonElement parse(String s) { + return parseJson(new JsonLexer(s)); + } + + /** + * Parses a given String encoded in JSON to a {@link JsonObject}. Parsing of the String fails with + * an error if the encoded String cannot be parsed into a Json object. + * + * @param json + * @return + */ + public static JsonObject parseJsonObject(String json) { + JsonLexer lexer = new JsonLexer(json); + JsonToken t = lexer.poll(); + if (null == t || JsonTokenKind.BEGIN_OBJECT != t.getKind()) { + Log.error("0xA0589 Json objects must begin with a '{'!"); + return null; + } + return parseJsonObject(lexer); + } + + /** + * Parses a given String encoded in JSON to a {@link JsonArray}. Parsing of the String fails with + * an error if the encoded String cannot be parsed into a Json array. + * + * @param json + * @return + */ + public static JsonArray parseJsonArray(String json) { + JsonLexer lexer = new JsonLexer(json); + JsonToken t = lexer.poll(); + if (null == t || JsonTokenKind.BEGIN_ARRAY != t.getKind()) { + Log.error("0xA0590 Json arrays must begin with a '['!"); + return null; + } + return parseJsonArray(lexer); + } + + /** + * parses any json using the passed lexer. + * + * @param lexer + * @return + */ + public static JsonElement parseJson(JsonLexer lexer) { + while (lexer.hasNext()) { + JsonToken t = lexer.poll(); + switch (t.getKind()) { + case WHITESPACE: //ignore whitespaces + break; + case BEGIN_ARRAY: + return parseJsonArray(lexer); + case BEGIN_OBJECT: + return parseJsonObject(lexer); + case BOOLEAN: + return JsonElementFactory.createJsonBoolean(Boolean.parseBoolean(t.getValue())); + case NULL: + return JsonElementFactory.createJsonNull(); + case NUMBER: + return JsonElementFactory.createJsonNumber(t.getValue()); + case STRING: + return JsonElementFactory.createJsonString(t.getValue()); + case END_ARRAY: + case END_OBJECT: + case COLON: + case COMMA: + default: + Log.error( + "0xA0564 Invalid JSON token \"" + t + + "\". The serialized object is not well-formed! Discarding :" + lexer + .getRemainder()); + return null; + } + } + //this only occurs if string is only whitespace + return null; + } + + /** + * Parses a Json Object with the passed lexer + * + * @param lexer + * @return + */ + protected static JsonObject parseJsonObject(JsonLexer lexer) { + JsonObject result = JsonElementFactory.createJsonObject(); + while (lexer.hasNext()) { + JsonToken t = lexer.poll(); + switch (t.getKind()) { + case WHITESPACE: //ignore whitespaces + break; + case STRING: //look for key:value pairs + String memberName = t.getValue(); //first get the key + JsonToken colon = pollNextNonWhiteSpace(lexer); //that *must* be followed by colon + if (null == colon || JsonTokenKind.COLON != colon.getKind()) { + Log.error( + " 0xA0566 Invalid JSON token \"" + t + + "\". The serialized object is not well-formed! A member name must be followed by a colon"); + } + JsonElement memberValue = parseJson(lexer); //then parse any value + result.putMember(memberName, memberValue); // and add the member to result + JsonToken next = pollNextNonWhiteSpace( + lexer); //either object end is reached or a comma must follow + if (null == next) { + Log.error( + " 0xA0580 Invalid JSON structure. Unexpected end of object!"); + } + if (JsonTokenKind.END_OBJECT == next.getKind()) { + return result; + } + if (JsonTokenKind.COMMA != next.getKind()) { + Log.error(" 0xA0581 Invalid JSON structure. Missing comma in object!"); + } + JsonToken next2 = peekNextNonWhiteSpace( + lexer); //peek if next token is end of object after comma. + if (null == next2 || JsonTokenKind.END_OBJECT == next2.getKind()) { + Log.error( + " 0xA0582 Invalid JSON structure. Unexpected end of object after comma!"); + } + break; + case END_OBJECT: + return result; //only if empty object + case COMMA: + case BEGIN_ARRAY: + case BEGIN_OBJECT: + case BOOLEAN: + case NULL: + case NUMBER: + case COLON: + case END_ARRAY: + default: + Log.error( + " 0xA0583 Invalid JSON structure \"" + t + + "\". The serialized object is not well-formed! Ignoring remainder: " + lexer + .getRemainder()); + return null; + } + } + Log.error( + " 0xA0584 Invalid JSON structure. Unexpected end of object!"); + return null; + } + + /** + * Parses a Json array by using the passed lexer + * + * @param lexer + * @return + */ + protected static JsonArray parseJsonArray(JsonLexer lexer) { + JsonArray result = JsonElementFactory.createJsonArray(); + while (lexer.hasNext()) { + JsonToken t = lexer.peek(); + switch (t.getKind()) { + case WHITESPACE: //ignore whitespaces + lexer.poll(); + break; + case BEGIN_ARRAY: + case BEGIN_OBJECT: + case BOOLEAN: + case NUMBER: + case STRING: + case NULL: + JsonElement e = parseJson(lexer); + result.add(e); + JsonToken next = pollNextNonWhiteSpace(lexer); + if (null == next) { + Log.error( + " 0xA0585 Invalid JSON structure. Unexpected end of array!"); + } + if (JsonTokenKind.END_ARRAY == next.getKind()) { + return result; + } + if (JsonTokenKind.COMMA != next.getKind()) { + Log.error( + " 0xA0586 Invalid JSON token. Missing comma in array!"); + } + //else it is a comma that has been consumed already + JsonToken next2 = peekNextNonWhiteSpace( + lexer); //peek if next token is end of array after comma. + if (null == next2 || JsonTokenKind.END_ARRAY == next2.getKind()) { + Log.error( + " 0xA0587 Invalid JSON token. Unexpected end of array after comma!"); + } + break; + case END_ARRAY: + lexer.poll(); + return result; + case COMMA: + case COLON: + case END_OBJECT: + default: + Log.error( + "0xA0568 Invalid JSON token \"" + t + + "\". The serialized array is not well-formed!"); + return null; + } + } + Log.error( + " 0xA0588 Invalid JSON token. Unexpected end of array!"); + return null; + } + + /** + * If object member tracing is enabled, getter-methods of Json objects are tracked to identify, + * if any members have been forgotten during deserialization + */ + public static void enableObjectMemberTracing() { + JsonElementFactory.setInstance(new JsonElementFactory() { + @Override + protected JsonObject doCreateJsonObject() { + return new TraceableJsonObject(); + } + }); + } + + /** + * Disables object member tracing. + * + * @see JsonParser#enableObjectMemberTracing() + */ + public static void disableObjectMemberTracing() { + JsonElementFactory.setInstance(new JsonElementFactory()); + } + + static { + //by default, enableObjectMemberTracing + enableObjectMemberTracing(); + } + + /** + * reads all whitespace tokens until the next non-whitespace token. + * This is returned, but not consumed. If the method is called when the lexer has already reached + * the end of the document, it returns "null". If the end of the document occurs during + * consumption of whitespaces, a whitespace token is returned. + * + * @param lexer + * @return + */ + protected static JsonToken peekNextNonWhiteSpace(JsonLexer lexer) { + if (!lexer.hasNext()) { + return null; + } + JsonToken t = lexer.peek(); + while (lexer.hasNext() && lexer.peek().getKind() == JsonTokenKind.WHITESPACE) { + t = lexer.poll(); + } + return t; + } + + /** + * reads all whitespace tokens until the next non-whitespace token. + * This is returned and consumed. If the method is called when the lexer has already reached the + * end of the document, it returns "null". If the end of the document occurs during consumption + * of whitespaces, a whitespace token is returned. + * + * @param lexer + * @return + */ + protected static JsonToken pollNextNonWhiteSpace(JsonLexer lexer) { + if (!lexer.hasNext()) { + return null; + } + JsonToken t = lexer.poll(); + while (lexer.hasNext() && t.getKind() == JsonTokenKind.WHITESPACE) { + t = lexer.poll(); + } + return t; + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/JsonPrinter.java b/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/JsonPrinter.java new file mode 100644 index 0000000000..3e72b86ab6 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/JsonPrinter.java @@ -0,0 +1,599 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symboltable.serialization; + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.symboltable.serialization.json.JsonArray; +import de.monticore.symboltable.serialization.json.JsonElement; +import de.monticore.symboltable.serialization.json.JsonElementFactory; +import de.monticore.symboltable.serialization.json.JsonObject; +import de.se_rwth.commons.logging.Log; + +import java.util.ArrayDeque; +import java.util.Collection; +import java.util.Deque; +import java.util.Optional; +import java.util.function.Function; + +import static de.monticore.symboltable.serialization.json.JsonElementFactory.*; + +/** + * Facade for the {@link IndentPrinter} that is capable of printing JSON syntax only. It hides + * details on the concrete syntax of Json and performs basic well-formedness checks on the + * produced Json. + */ +public class JsonPrinter { + + public static boolean DEFAULT_BOOLEAN = false; + + public static int DEFAULT_NUMBER = 0; + + public static String DEFAULT_STRING = ""; + + protected static boolean enableIndentation = false; + + protected static boolean serializeDefaults = false; + + protected Deque currElements; + + //denotes the root object or array, otherwise it is empty + protected Optional root; + + /** + * Constructor for de.monticore.symboltable.serialization.JsonPrinter + * + * @param serializeDefaults + */ + public JsonPrinter(boolean serializeDefaults) { + this.currElements = new ArrayDeque<>(); + this.root = Optional.empty(); + JsonPrinter.serializeDefaults = serializeDefaults; + JsonElementFactory.setInstance(new JsonElementFactory()); + } + + /** + * Constructor for de.monticore.symboltable.serialization.JsonPrinter that does not print empty + * lists by default. This property can be enabled trough { @link #enableIndentation } + */ + public JsonPrinter() { + this(false); + } + + /** + * @return enableIndentation + */ + public static boolean isIndentationEnabled() { + return enableIndentation; + } + + /** + * Enables the indentation + */ + public static void enableIndentation() { + JsonPrinter.enableIndentation = true; + } + + /** + * Disables the indentation + */ + public static void disableIndentation() { + JsonPrinter.enableIndentation = false; + } + + /** + * @return serializeDefaults + */ + public static boolean isSerializingDefaults() { + return serializeDefaults; + } + + /** + * @param serializeDefaults + */ + public static void setSerializeDefaults(boolean serializeDefaults) { + JsonPrinter.serializeDefaults = serializeDefaults; + } + + /** + * Prints the begin of an object in Json notation. + */ + public void beginObject() { + if (!currElements.isEmpty() && currElements.peek().isJsonObject()) { + Log.error("0xA0600 JsonPrinter detected an invalid nesting of Json. " + + "A JSON object within a JSON object must be a member!"); + } + else { + currElements.push(createJsonObject()); + if (!root.isPresent()) { + root = Optional.of(currElements.peek()); + } + } + } + + /** + * Prints the begin of an object in Json notation as member or the current object. + */ + public void beginObject(String kind) { + JsonObject o = createJsonObject(); + getParentObject().putMember(kind, o); + currElements.push(o); + } + + /** + * Prints the end of an object in Json notation. + */ + public void endObject() { + if (!currElements.isEmpty() && currElements.peek().isJsonObject()) { + JsonObject o = currElements.poll().getAsJsonObject(); + addToArray(o); + } + else { + Log.error("0xA0611 JsonPrinter detected an invalid nesting of Json. " + + "Detected an unexpected end of an object!"); + } + } + + /** + * Prints the beginning of a collection in Json notation. + */ + public void beginArray() { + if (!currElements.isEmpty() && currElements.peek().isJsonObject()) { + Log.error("0xA0601 JsonPrinter detected an invalid nesting of Json. " + + "A JSON array within a JSON object must be a member!"); + } + else { + currElements.push(createJsonArray()); + if (!root.isPresent()) { + root = Optional.of(currElements.peek()); + } + } + } + + /** + * Prints the beginning of a collection in Json notation as member or the current object. + */ + public void beginArray(String kind) { + JsonArray a = createJsonArray(); + getParentObject().putMember(kind, a); + currElements.push(a); + } + + /** + * Prints the end of a collection in Json notation. + */ + public void endArray() { + if (!currElements.isEmpty() && currElements.peek().isJsonArray()) { + JsonArray o = currElements.poll().getAsJsonArray(); + addToArray(o); + } + else { + Log.error("0xA0612 JsonPrinter detected an invalid nesting of Json. " + + "Detected an unexpected end of an array!"); + } + } + + /** + * Prints a member with the passed name that is a collection of values as JSON array. + * The serialization of each value of the collection has to be passed as Function + * + * @param name + * @param values + * @param printValue + * @param + */ + public void array(String name, Collection values, Function printValue) { + if (!values.isEmpty() || serializeDefaults) { + beginArray(name); + for (T t : values) { + valueJson(printValue.apply(t)); + } + endArray(); + } + } + + /** + * Prints a collection of values as JSON array. The serialization of each value of the + * collection has to be passed as Function + * + * @param values + * @param printValue + * @param + */ + public void array(Collection values, Function printValue) { + beginArray(); + for (T t : values) { + valueJson(printValue.apply(t)); + } + endArray(); + } + + /** + * Prints a Json collection with the given kind as key and the given collection of object values. + * Empty lists are serialized only, if serializeEmptyLists() is activated via the constructor. To + * serialize the passed objects, their toString() method is invoked. Complex objects should be + * serialized separately, before they are passed as parameter to this method! + * + * @param kind The key of the Json attribute + * @param values The values of the Json attribute + */ + public void member(String kind, Collection values) { + beginArray(kind); + values.stream().forEach(o -> value(o)); + endArray(); + } + + /** + * Prints a Json attribute with the given kind as key and the given optional object value. To + * serialize the passed object if it is present, its toString() method is invoked. Absent + * optionals are serialized only, if serializeEmptyLists() is activated via the constructor. + * Complex objects should be serialized separately, before they are passed as parameter to this + * method! + * + * @param kind The key of the Json attribute + * @param value The value of the Json attribute + */ + public void member(String kind, Optional value) { + if (null != value && value.isPresent()) { + member(kind, value.get()); + } + else if (serializeDefaults) { + getParentObject().putMember(kind, createJsonNull()); + } + } + + /** + * Prints a Json member with the given kind as key and the given double value, which is a basic + * data type in Json. If the member value equals the default value and the serialization of + * default values is turned off, the member is not printed. + * + * @param kind The key of the Json attribute + * @param value The double value of the Json attribute + */ + public void member(String kind, double value) { + if (DEFAULT_NUMBER != value || serializeDefaults) { + memberNoDef(kind, value); + } + } + + public void memberNoDef(String kind, double value) { + JsonElement member = createJsonNumber(String.valueOf(value)); + getParentObject().putMember(kind, member); + } + + /** + * Prints a Json member with the given kind as key and the given long value, which is a basic data + * type in Json. If the member value equals the default value and the serialization of + * default values is turned off, the member is not printed. + * + * @param kind The key of the Json attribute + * @param value The long value of the Json attribute + */ + public void member(String kind, long value) { + if (DEFAULT_NUMBER != value || serializeDefaults) { + memberNoDef(kind, value); + } + } + + public void memberNoDef(String kind, long value) { + JsonElement member = createJsonNumber(String.valueOf(value)); + getParentObject().putMember(kind, member); + } + + /** + * Prints a Json member with the given kind as key and the given float value, which is a basic + * data type in Json. If the member value equals the default value and the serialization of + * default values is turned off, the member is not printed. + * + * @param kind The key of the Json attribute + * @param value The float value of the Json attribute + */ + public void member(String kind, float value) { + if (DEFAULT_NUMBER != value || serializeDefaults) { + memberNoDef(kind, value); + } + } + + public void memberNoDef(String kind, float value) { + JsonElement member = createJsonNumber(String.valueOf(value)); + getParentObject().putMember(kind, member); + } + + /** + * Prints a Json member with the given kind as key and the given int value, which is a basic data + * type in Json. If the member value equals the default value and the serialization of + * default values is turned off, the member is not printed. + * + * @param kind The key of the Json attribute + * @param value The int value of the Json attribute + */ + public void member(String kind, int value) { + if (DEFAULT_NUMBER != value || serializeDefaults) { + memberNoDef(kind, value); + } + } + + public void memberNoDef(String kind, int value) { + JsonElement member = createJsonNumber(String.valueOf(value)); + getParentObject().putMember(kind, member); + } + + /** + * Prints a Json member with the given kind as key and the given boolean value, which is a basic + * data type in Json. If the member value equals the default value and the serialization of + * default values is turned off, the member is not printed. + * + * @param kind The key of the Json attribute + * @param value The boolean value of the Json attribute + */ + public void member(String kind, boolean value) { + if (DEFAULT_BOOLEAN != value || serializeDefaults) { + memberNoDef(kind, value); + } + } + + public void memberNoDef(String kind, boolean value) { + JsonElement member = createJsonNumber(String.valueOf(value)); + getParentObject().putMember(kind, member); + } + + /** + * Prints a Json member with the given kind as key and the given String value, which is a basic + * data type in Json. If the member value equals the default value and the serialization of + * default values is turned off, the member is not printed. NOTE: if the parameter value is a + * serialized String, use the member(String kind, JsonPrinter value) method or the + * memberJson(String kind, String value) method instead! Otherwise the content is escaped twice! + * + * @param kind The key of the Json attribute + * @param value The boolean value of the Json attribute + */ + public void member(String kind, String value) { + if (DEFAULT_STRING != value || serializeDefaults) { + memberNoDef(kind, value); + } + } + + public void memberNoDef(String kind, String value) { + String escValue = escapeSpecialChars(value); + JsonElement member = createJsonString("\"" + escValue + "\""); + getParentObject().putMember(kind, member); + } + + /** + * Prints a Json member with the given kind as key and the given String value that is encoded + * in JSON. If the member value equals the default value and the serialization of default values + * is turned off, the member is not printed.NOTE: if the parameter value is NOT a serialized + * String, use the member(String kind, String value) method instead! Otherwise the content is not + * escaped! + * + * @param kind + * @param value + */ + public void memberJson(String kind, String value) { + if (DEFAULT_STRING != value || serializeDefaults) { + memberJsonNoDef(kind, value); + } + } + + public void memberJsonNoDef(String kind, String value) { + JsonElement member = createJsonString(value); + getParentObject().putMember(kind, member); + } + + /** + * Prints a Json member with the given kind as key and a Json value that is of an object type and + * therefore needs separat serialization. + * + * @param kind The key of the Json attribute + * @param value The boolean value of the Json attribute + */ + public void member(String kind, JsonPrinter value) { + memberJson(kind, value.getContent()); + } + + /** + * Prints a double as Json value. + * + * @param value The double value of the Json attribute + */ + public void value(double value) { + intenalNumberValue(String.valueOf(value)); + } + + /** + * Prints a long as Json value. + * + * @param value The long value of the Json attribute + */ + public void value(long value) { + intenalNumberValue(String.valueOf(value)); + } + + /** + * Prints a float as Json value. + * + * @param value The float value of the Json attribute + */ + public void value(float value) { + intenalNumberValue(String.valueOf(value)); + } + + /** + * Prints an int as Json value, if it deviates from the default boolean value or if + * * default values should be printed. + * + * @param value The int value of the Json attribute + */ + public void value(int value) { + intenalNumberValue(String.valueOf(value)); + } + + /** + * Prints a boolean value as Json value. + * + * @param value The boolean value of the Json attribute + */ + public void value(boolean value) { + if (!currElements.isEmpty() && currElements.peek().isJsonArray()) { + JsonArray parent = currElements.peek().getAsJsonArray(); + parent.add(createJsonBoolean(value)); + } + else if (!currElements.isEmpty() && currElements.peek().isJsonObject()) { + Log.error("0xA0606 JsonPrinter detected an invalid nesting of Json. " + + "Cannot add a numeric value `" + value + "` to the JSON object: `" + + toString() + "`"); + } + else { + currElements.push(createJsonBoolean(value)); + } + } + + /** + * Prints a String as Json value. NOTE: if the parameter value is a serialized String, use the + * value(JsonPrinter) method instead! Otherwise the content is escaped twice! + * + * @param value The String value of the Json attribute + */ + public void value(String value) { + intenalStringValue("\"" +escapeSpecialChars(value) +"\""); + } + + /** + * Prints a String that contains encoded Json. NOTE: if the parameter value is NOT a serialized + * String, use the value(String) method instead! Otherwise the content is not escaped properly! + * + * @param value The String encoded in JSON + */ + public void valueJson(String value) { + intenalStringValue(value); + } + + /** + * Prints a Json attribute that is of an object type and therefore needs separat serialization. + * + * @param value The JsonPrinter of the value object + */ + public void value(JsonPrinter value) { + intenalStringValue(value.getContent()); + } + + //////////////////////////////////////////////////////////////////////////////////////// + + /** + * Adds escape sequences for all characters that are escaped in Java Strings according to + * https://docs.oracle.com/javase/tutorial/java/data/characters.html + */ + protected String escapeSpecialChars(String input) { + return input + .replace("\\", "\\\\") // Insert a backslash character in the text at this point. + .replace("\t", "\\t") // Insert a tab in the text at this point. + .replace("\b", "\\b") // Insert a backspace in the text at this point. + .replace("\n", "\\n") // Insert a newline in the text at this point. + .replace("\r", "\\r") // Insert a carriage return in the text at this point. + .replace("\f", "\\f") // Insert a formfeed in the text at this point. + .replace("\'", "\\\'") // Insert a single quote character in the text at this point. + .replace("\"", "\\\""); // Insert a double quote character in the text at this point. + } + + protected void intenalNumberValue(String value) { + if (!currElements.isEmpty() && currElements.peek().isJsonArray()) { + JsonArray parent = currElements.peek().getAsJsonArray(); + parent.add(createJsonNumber(String.valueOf(value))); + } + else if (!currElements.isEmpty() && currElements.peek().isJsonObject()) { + Log.error("0xA0604 JsonPrinter detected an invalid nesting of Json. " + + "Cannot add a numeric value `" + value + "` to the JSON object: `" + + toString() + "`"); + } + else { + currElements.push(createJsonNumber(String.valueOf(value))); + } + } + + protected void intenalStringValue(String value) { + if (!currElements.isEmpty() && currElements.peek().isJsonArray()) { + JsonArray parent = currElements.peek().getAsJsonArray(); + parent.add(createJsonString(value)); + } + else if (!currElements.isEmpty() && currElements.peek().isJsonObject()) { + Log.error("0xA0603 JsonPrinter detected an invalid nesting of Json. " + + "Cannot add a String value `" + value + "` to the JSON object: `" + + toString() + "`"); + } + else { + currElements.push(createJsonString(value)); + } + } + + private JsonObject getParentObject() { + if (currElements.isEmpty()) { + Log.error("0xA0613 JsonPrinter detected an invalid nesting of Json. " + + "Cannot add a member as the first element of a Json String!"); + } + if (currElements.peek().isJsonObject()) { + return currElements.peek().getAsJsonObject(); + } + IndentPrinter p = new IndentPrinter(); + currElements.peek().print(p); + Log.error("0xA0602 JsonPrinter detected an invalid nesting of Json. " + + "Cannot add a child member to: `" + p.toString() + "`"); + return createJsonObject(); + } + + public void addToArray(JsonElement e){ + if (!currElements.isEmpty() && currElements.peek().isJsonArray()) { + JsonArray parent = currElements.peek().getAsJsonArray(); + parent.add(e); + } + else if (currElements.isEmpty() || currElements.peek().isJsonObject()) { + // no op here, because nested objects are handled elsewhere and + // it is not an error if a JsonObject or JsonArray is the "outermost" + // JSON element + } + else { + Log.error("0xA0653 JsonPrinter detected an invalid nesting of Json arrays. " + + "Cannot add `" + e + "` to the JSON : `" + + toString() + "`"); + } + } + + /** + * Resets everything printed so far + */ + public void clearBuffer() { + this.currElements = new ArrayDeque<>(); + this.root = Optional.empty(); + } + + /** + * Returns the current value of the Json code produced so far and performs basic + * checking for correct nestings + */ + public String getContent() { + if (currElements.size() > 1 || (currElements.size() == 1 && root.isPresent())) { + Log.error("0xA0615 JsonPrinter detected an invalid nesting of Json."); + } + return toString(); + } + + /** + * Returns the current value of the Json code produced so far. + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + if (currElements.isEmpty()) { + if (!root.isPresent()) { + return ""; + } + IndentPrinter p = new IndentPrinter(); + root.get().print(p); + return p.getContent(); + } + else { + IndentPrinter p = new IndentPrinter(); + currElements.peek().print(p); + return p.getContent(); + } + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/JsonToken.java b/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/JsonToken.java new file mode 100644 index 0000000000..d6a4ca9c62 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/JsonToken.java @@ -0,0 +1,85 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.symboltable.serialization; + +import de.se_rwth.commons.logging.Log; + +/** + * This class holds kind and, if present for this kind, also the value of a json token. + * For tokens without a value, this class has constants that can be used to avoid creating unnecassary objects + */ +public class JsonToken { + + public static final JsonToken BEGIN_OBJECT = new JsonToken(JsonTokenKind.BEGIN_OBJECT); + + public static final JsonToken END_OBJECT = new JsonToken(JsonTokenKind.END_OBJECT); + + public static final JsonToken BEGIN_ARRAY = new JsonToken(JsonTokenKind.BEGIN_ARRAY); + + public static final JsonToken END_ARRAY = new JsonToken(JsonTokenKind.END_ARRAY); + + public static final JsonToken COLON = new JsonToken(JsonTokenKind.COLON); + + public static final JsonToken COMMA = new JsonToken(JsonTokenKind.COMMA); + + public static final JsonToken NULL = new JsonToken(JsonTokenKind.NULL); + + // for performance reasons, forget the exact whitespace characters after lexing + public static final JsonToken WHITESPACE = new JsonToken(JsonTokenKind.WHITESPACE); + + public static final JsonToken BOOLEAN_TRUE = new JsonToken(JsonTokenKind.BOOLEAN, "true"); + + public static final JsonToken BOOLEAN_FALSE = new JsonToken(JsonTokenKind.BOOLEAN, "false"); + + protected JsonTokenKind kind; + + protected String value; + + /** + * create a token with the passed kind and the passed value + * @param kind + * @param value + */ + public JsonToken(JsonTokenKind kind, String value) { + this.kind = kind; + if (kind.hasValue()) { + this.value = value; + } + } + + /** + * returns a token with the passed kind + * @param kind + */ + public JsonToken(JsonTokenKind kind) { + this.kind = kind; + } + + /** + * returns the kind of the token + * @return + */ + public JsonTokenKind getKind() { + return kind; + } + + /** + * returns the value of the token if the token kind has a value. Throws an error otherwise + * @return + */ + public String getValue() { + if (kind.hasValue()) { + return this.value; + } + Log.error("0xA0596 Token " + kind + " cannot have a value!"); + return ""; + } + + @Override public String toString() { + if (kind.hasValue()) { + return "{" + kind.toString() + "=" + value + "}"; + } + return "{" + kind.toString() + "}"; + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/JsonTokenKind.java b/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/JsonTokenKind.java new file mode 100644 index 0000000000..10e95a795b --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/JsonTokenKind.java @@ -0,0 +1,46 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.symboltable.serialization; + +/** + * Enumeration of all token kinds that occur in the JSON language. Tokens of some kinds (such as + * a boolean that can be either 'true' or 'false') have a value, + * while other kinds have a static value (such as begin_object, + * which is always '{') that is not represented inside of the token. + */ +public enum JsonTokenKind { + STRING, + NUMBER, + BOOLEAN, + BEGIN_ARRAY, + END_ARRAY, + BEGIN_OBJECT, + END_OBJECT, + NULL, + COMMA, + COLON, + WHITESPACE; + + /** + * returns true, iff the token kind has a (variable) value. + * @return + */ + public boolean hasValue() { + switch (this) { + case STRING: + case NUMBER: + case BOOLEAN: + return true; + case BEGIN_ARRAY: + case END_ARRAY: + case BEGIN_OBJECT: + case END_OBJECT: + case NULL: + case COMMA: + case COLON: + case WHITESPACE: + default: + return false; + } + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/NumberLexer.java b/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/NumberLexer.java new file mode 100644 index 0000000000..2ab1739905 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/NumberLexer.java @@ -0,0 +1,285 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.symboltable.serialization; + +import de.se_rwth.commons.logging.Log; + +/** + * This class is used by the JsonLexer. It realizes a DFA to check whether a char sequence is a + * valid json number, i.e. if it conforms to the regex: + * "-?(0|([1-9][0-9]*))(\\.[0-9]+)?((e|E)(-|\\+)?[0-9]+)?" + */ +public class NumberLexer { + + /** + * enum with states within the lexing of a number token. The state is not visible outside of the + * number parser, therefore this enumeration is located within the number parser class. + */ + enum State { + INITIAL, + AFTER_SIGNUM, + AFTER_ZERO, + IN_NUMBER, + AFTER_POINT, + AFTER_POINT_AND_ONE_DIGIT, + AFTER_E, + AFTER_E_SIGNUM, + IN_EXPONENT_AFTER_ONE_DIGIT, + ERROR; + } + + protected State currentState; + + protected StringBuilder result; + + public NumberLexer() { + reset(); + } + + /** + * resets the current state to the initial state and resets the read input + */ + public void reset() { + this.currentState = State.INITIAL; + this.result = new StringBuilder(); + } + + /** + * returns the string of the number read so far + * + * @return + */ + public String getResult() { + return result.toString(); + } + + /** + * this realizes the automaton described in www.json.org + * + * @param input + */ + public void step(char input) { + result.append(input); + switch (currentState) { + case INITIAL: + readInitial(input); + return; + case AFTER_SIGNUM: + readAfterSignum(input); + return; + case AFTER_ZERO: + readAfterZero(input); + return; + case IN_NUMBER: + readInNumber(input); + return; + case AFTER_POINT: + readAfterPoint(input); + return; + case AFTER_POINT_AND_ONE_DIGIT: + readAfterPointAndOneDigit(input); + return; + case AFTER_E: + readAfterE(input); + return; + case AFTER_E_SIGNUM: + readAfterESignum(input); + return; + case IN_EXPONENT_AFTER_ONE_DIGIT: + readInExponentAndOneDigit(input); + return; + default: + Log.error( + "0xA0809 Unexpected character in number: " + input); + } + } + + protected void readInExponentAndOneDigit(char input) { + if ("0123456789".indexOf(input) > -1) { + // Stay in current state + } + else { + Log.error( + "0xA0808 invalid character in number: " + input + ". Expecting one of: '0123456789'"); + currentState = State.ERROR; + } + } + + protected void readAfterESignum(char input) { + if ("0123456789".indexOf(input) > -1) { + currentState = State.IN_EXPONENT_AFTER_ONE_DIGIT; + } + else { + Log.error( + "0xA0807 invalid character in number: " + input + ". Expecting one of: '0123456789'"); + currentState = State.ERROR; + } + } + + /** + * after an exponent character 'e', either the signum of the exponent oder a digit can follow + * + * @param input + */ + protected void readAfterE(char input) { + if ("+-".indexOf(input) > -1) { + currentState = State.AFTER_E_SIGNUM; + } + else if ("0123456789".indexOf(input) > -1) { + currentState = State.IN_EXPONENT_AFTER_ONE_DIGIT; + } + else { + Log.error( + "0xA0806 invalid character in number: " + input + + ". Expecting one of: '+-0123456789'"); + currentState = State.ERROR; + } + } + + /** + * after the decimal point and a conscutive digit, the parser is in a final state and any digits, + * or the exponent character can follow. + * + * @param input + */ + protected void readAfterPointAndOneDigit(char input) { + if ("0123456789".indexOf(input) > -1) { + // Stay in current state + } + else if ('e' == input || 'E' == input) { + currentState = State.AFTER_E; + } + else { + Log.error( + "0xA0805 invalid character in number: " + input + + ". Expecting one of: '0123456789eE'"); + currentState = State.ERROR; + } + } + + /** + * after the decimal point, any digit can follow. + * + * @param input + */ + protected void readAfterPoint(char input) { + if ("0123456789".indexOf(input) > -1) { + currentState = State.AFTER_POINT_AND_ONE_DIGIT; + } + else { + Log.error( + "0xA0804 invalid character in number: " + input + + ". Expecting one of: '0123456789' after a decimal point."); + currentState = State.ERROR; + } + } + + /** + * after the a minus character or a zero, any digits can follow, or a decimal point, or an exponent character + * + * @param input + */ + protected void readInNumber(char input) { + if ("0123456789".indexOf(input) > -1) { + //Stay in current state + } + else if ('.' == input) { + currentState = State.AFTER_POINT; + } + else if ('e' == input || 'E' == input) { + currentState = State.AFTER_E; + } + else { + Log.error("0xA0803 invalid character in number: " + input); + currentState = State.ERROR; + } + } + + /** + * after zero, any non-zero(!) digit can follow, or a decimal point or an exponent character. + * + * @param input + */ + protected void readAfterZero(char input) { + if ("123456789".indexOf(input) > -1) { + currentState = State.IN_NUMBER; + } + else if ('.' == input) { + currentState = State.AFTER_POINT; + } + else if ('e' == input || 'E' == input) { + currentState = State.AFTER_E; + } + else if (input == '0') { + Log.error("0xA0801 invalid character in number: " + input + + ". There must not be more than a single '0' at the beginning of a number"); + currentState = State.ERROR; + } + else { + Log.error("0xA0802 invalid character in number: " + input); + currentState = State.ERROR; + } + } + + protected void readAfterSignum(char input) { + if (input == '0') { + currentState = State.AFTER_ZERO; + } + else if ("123456789".indexOf(input) > -1) { + currentState = State.IN_NUMBER; + } + else if (input == '.') { + Log.error("0xA0598 invalid character in number: " + input + + ". There must be a digit before a '.'"); + currentState = State.ERROR; + } + else if (input == 'e' || input == 'E') { + Log.error("0xA0599 invalid character in number: " + input + + ". There must be a digit before an 'e'"); + currentState = State.ERROR; + } + else { + Log.error("0xA0800 invalid character in number: " + input); + currentState = State.ERROR; + } + } + + protected void readInitial(char input) { + if ('-' == input) { + currentState = State.AFTER_SIGNUM; + } + else if ('0' == input) { + currentState = State.AFTER_ZERO; + } + else if ("123456789".indexOf(input) > -1) { + currentState = State.IN_NUMBER; + } + else { + Log.error( + "0xA0597 invalid character in number: " + input + ". Expecting one of: '-0123456789'"); + currentState = State.ERROR; + } + } + + /** + * returns true, iff the number parser is in an error state + * + * @return + */ + public boolean hasError() { + return currentState == State.ERROR; + } + + /** + * returns true, iff the number parser is in a final state, i.e., if the string lexd so far is a valid number + * + * @return + */ + public boolean isInFinalState() { + return currentState == State.AFTER_ZERO + || currentState == State.IN_NUMBER + || currentState == State.AFTER_POINT_AND_ONE_DIGIT + || currentState == State.IN_EXPONENT_AFTER_ONE_DIGIT; + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/json/JsonArray.java b/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/json/JsonArray.java new file mode 100644 index 0000000000..81d379f46f --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/json/JsonArray.java @@ -0,0 +1,178 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symboltable.serialization.json; + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.symboltable.serialization.JsonPrinter; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.function.Consumer; + +/** + * Represents a Json Array, i.e., a list of any Json elements. These can be of different types. + */ +public class JsonArray implements JsonElement { + + protected List values; + + public JsonArray() { + this.values = new ArrayList<>(); + } + + /** + * @see de.monticore.symboltable.serialization.json.JsonElement#isJsonArray() + */ + @Override + public boolean isJsonArray() { + return true; + } + + /** + * @see de.monticore.symboltable.serialization.json.JsonElement#getAsJsonArray() + */ + @Override + public JsonArray getAsJsonArray() { + return this; + } + + public void forEach(Consumer action) { + values.forEach(action); + } + + /** + * @return elements + */ + public List getValues() { + return this.values; + } + + /** + * @param values the elements to set + */ + public void setValues(List values) { + this.values = values; + } + + /** + * @return + * @see java.util.List#size() + */ + public int size() { + return this.values.size(); + } + + /** + * @return + * @see java.util.List#isEmpty() + */ + public boolean isEmpty() { + return this.values.isEmpty(); + } + + /** + * @param o + * @return + * @see java.util.List#contains(java.lang.Object) + */ + public boolean contains(Object o) { + return this.values.contains(o); + } + + /** + * @param e + * @return + * @see java.util.List#add(java.lang.Object) + */ + public boolean add(JsonElement e) { + return this.values.add(e); + } + + /** + * @param o + * @return + * @see java.util.List#remove(java.lang.Object) + */ + public boolean remove(Object o) { + return this.values.remove(o); + } + + /** + * @param c + * @return + * @see java.util.List#addAll(java.util.Collection) + */ + public boolean addAll(Collection c) { + return this.values.addAll(c); + } + + /** + * @param index + * @return + * @see java.util.List#get(int) + */ + public JsonElement get(int index) { + return this.values.get(index); + } + + /** + * @param index + * @return + * @see java.util.List#remove(int) + */ + public JsonElement remove(int index) { + return this.values.remove(index); + } + + /** + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return print(new IndentPrinter()); + } + + @Override public String print(IndentPrinter p) { + boolean indent = JsonPrinter.isIndentationEnabled(); + if (values.isEmpty() && !JsonPrinter.isSerializingDefaults()) { + return p.getContent(); + } + + // print values of array with a buffer to check whether it is empty + IndentPrinter buffer = new IndentPrinter(); + buffer.setIndentation(p.getIndentation() + 1); + + // print each value with another buffer to check emptyness + IndentPrinter tmp = new IndentPrinter(); + tmp.setIndentation(p.getIndentation() + 1); + + String sep = ""; + for (JsonElement e : values) { + e.print(tmp); + if (!tmp.getContent().isEmpty()) { + buffer.print(sep + tmp.getContent()); + tmp.clearBuffer(); + sep = indent ? ",\n" : ","; + } + } + + if (!buffer.getContent().isEmpty() || JsonPrinter.isSerializingDefaults()) { + if (indent) { + p.println("["); + p.indent(); + p.print(buffer.getContent()); + p.println(); + p.unindent(); + p.print("]"); + } + else { + p.print("["); + p.print(buffer.getContent()); + p.print("]"); + } + } + + return p.getContent(); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/json/JsonBoolean.java b/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/json/JsonBoolean.java new file mode 100644 index 0000000000..7b9a6024ea --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/json/JsonBoolean.java @@ -0,0 +1,70 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symboltable.serialization.json; + +import de.monticore.prettyprint.IndentPrinter; + +/** + * Represents a Json Boolean. It can be true or - you probably guessed this - false. + */ +public class JsonBoolean implements JsonElement { + + protected boolean value; + + /** + * Constructor for de.monticore._symboltable.serialization.json.JsonBoolean + * + * @param value + */ + public JsonBoolean(boolean value) { + this.value = value; + } + + /** + * @see de.monticore.symboltable.serialization.json.JsonElement#isJsonBoolean() + */ + @Override + public boolean isJsonBoolean() { + return true; + } + + /** + * @see de.monticore.symboltable.serialization.json.JsonElement#getAsJsonBoolean() + */ + @Override + public JsonBoolean getAsJsonBoolean() { + return this; + } + + /** + * @return value + */ + public boolean getValue() { + return this.value; + } + + /** + * @param value the value to set + */ + public void setValue(boolean value) { + this.value = value; + } + + /** + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return value ? "true" : "false"; + } + + @Override public String print(IndentPrinter p) { + if (getValue()) { + p.print("true"); + } + else { + p.print("false"); + } + return p.getContent(); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/json/JsonElement.java b/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/json/JsonElement.java new file mode 100644 index 0000000000..133d580325 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/json/JsonElement.java @@ -0,0 +1,110 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symboltable.serialization.json; + +import de.monticore.prettyprint.IndentPrinter; +import de.se_rwth.commons.logging.Log; + +/** + * Common Interface for all Json metamodel Elements (e.g., JsonArray, JsonBoolean,...). By default, + * all methods for checking the concrete type of a metamodel element (e.g., isJsonArray()) return + * false. In each concrete type, the respective method must be overridden to return true. This + * avoids instanceof expressions. Similarly, the getAs methods (e.g., getAsJsonArray()) return an + * error if they are called. In each concrete type, the respective method must be overridden to + * return "this". This avoids down casts to the concrete type. + */ +public interface JsonElement { + + /** + * @return true iff this object is of type JsonObject and false otherwise. + */ + default public boolean isJsonObject() { + return false; + } + + /** + * @return true iff this object is of type JsonArray and false otherwise. + */ + default public boolean isJsonArray() { + return false; + } + + /** + * @return true iff this object is of type JsonBoolean and false otherwise. + */ + default public boolean isJsonBoolean() { + return false; + } + + /** + * @return true iff this object is of type JsonString and false otherwise. + */ + default public boolean isJsonString() { + return false; + } + + /** + * @return true iff this object is of type JsonNumber and false otherwise. + */ + default public boolean isJsonNumber() { + return false; + } + + /** + * @return true iff this object is of type JsonNull and false otherwise. + */ + default public boolean isJsonNull() { + return false; + } + + + /** + * @return this object as JsonObject if it is of this type and throws an error otherwise. + */ + default public JsonObject getAsJsonObject() { + Log.error("0xA0605 "+ this + " is not a Json Object!"); + return null; + } + + /** + * @return this object as JsonArray if it is of this type and throws an error otherwise. + */ + default public JsonArray getAsJsonArray() { + Log.error("0xA1606 "+ this + " is not a Json Array!"); + return null; + } + + /** + * @return this object as JsonBoolean if it is of this type and throws an error otherwise. + */ + default public JsonBoolean getAsJsonBoolean() { + Log.error("0xA0607 "+ this + " is not a Json Boolean!"); + return null; + } + + /** + * @return this object as JsonString if it is of this type and throws an error otherwise. + */ + default public JsonString getAsJsonString() { + Log.error("0xA0608 "+ this + " is not a Json String!"); + return null; + } + + /** + * @return this object as JsonNumber if it is of this type and throws an error otherwise. + */ + default public JsonNumber getAsJsonNumber() { + Log.error("0xA0609 "+ this + " is not a Json Number!"); + return null; + } + + /** + * @return this object as JsonNull if it is of this type and throws an error otherwise. + */ + default public JsonNull getAsJsonNull() { + Log.error("0xA0610 "+ this + " is not a Json Null!"); + return null; + } + + String print(IndentPrinter p); + +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/json/JsonElementFactory.java b/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/json/JsonElementFactory.java new file mode 100644 index 0000000000..5f69903a3e --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/json/JsonElementFactory.java @@ -0,0 +1,74 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symboltable.serialization.json; + +/** + * This factory creates instances of JsonElements. + * It can be used to instantiate subtypes of the Json + * classes instead of the default ones. + */ +public class JsonElementFactory { + + protected static final JsonNull NULL = new JsonNull(); + + protected static final JsonBoolean TRUE = new JsonBoolean(true); + + protected static final JsonBoolean FALSE = new JsonBoolean(false); + + public JsonElementFactory() { + } + + protected static JsonElementFactory instance; + + public static void setInstance(JsonElementFactory instance) { + JsonElementFactory.instance = instance; + } + + protected JsonArray doCreateJsonArray() { + return new JsonArray(); + } + + protected JsonBoolean doCreateJsonBoolean(boolean value) { + return value?TRUE:FALSE; + } + + protected JsonNull doCreateJsonNull() { + return NULL; + } + + protected JsonNumber doCreateJsonNumber(String value) { + return new JsonNumber(value); + } + + protected JsonObject doCreateJsonObject() { + return new JsonObject(); + } + + protected JsonString doCreateJsonString(String value) { + return new JsonString(value); + } + + public static JsonArray createJsonArray() { + return instance.doCreateJsonArray(); + } + + public static JsonBoolean createJsonBoolean(boolean value) { + return instance.doCreateJsonBoolean(value); + } + + public static JsonNull createJsonNull() { + return instance.doCreateJsonNull(); + } + + public static JsonNumber createJsonNumber(String value) { + return instance.doCreateJsonNumber(value); + } + + public static JsonObject createJsonObject() { + return instance.doCreateJsonObject(); + } + + public static JsonString createJsonString(String value) { + return instance.doCreateJsonString(value); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/json/JsonNull.java b/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/json/JsonNull.java new file mode 100644 index 0000000000..49e67d1022 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/json/JsonNull.java @@ -0,0 +1,42 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symboltable.serialization.json; + +import de.monticore.prettyprint.IndentPrinter; + +/** + * Represents a Json null, which is a value type in Json. This class exists for + * reasons of completeness, but you should avoid using it + * (cf. Hoare's "billion-dollar mistake") + */ +public class JsonNull implements JsonElement { + + public JsonNull() { + } + + /** + * @see de.monticore.symboltable.serialization.json.JsonElement#isJsonNull() + */ + @Override + public boolean isJsonNull() { + return true; + } + + /** + * @see de.monticore.symboltable.serialization.json.JsonElement#getAsJsonNull() + */ + @Override + public JsonNull getAsJsonNull() { + return this; + } + + @Override + public String toString() { + return "null"; + } + + @Override public String print(IndentPrinter p) { + p.print("null"); + return p.getContent(); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/json/JsonNumber.java b/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/json/JsonNumber.java new file mode 100644 index 0000000000..bcc8ae5d12 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/json/JsonNumber.java @@ -0,0 +1,107 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symboltable.serialization.json; + +import de.monticore.prettyprint.IndentPrinter; + +/** + * Represents a number in JSON. JSON does not distinguish different data types for numbers, + * therefore this class internally uses number parsers to return the number in a Java number data + * type of your choice. For example, to return the number as a long, call getNumberAsLong(). + */ +public class JsonNumber implements JsonElement { + + protected String value; + + /** + * Constructor for de.monticore._symboltable.serialization.json.JsonNumber + * + * @param value + */ + public JsonNumber(String value) { + super(); + this.value = value; + } + + /** + * @see de.monticore.symboltable.serialization.json.JsonElement#isJsonNumber() + */ + @Override + public boolean isJsonNumber() { + return true; + } + + /** + * @see de.monticore.symboltable.serialization.json.JsonElement#getAsJsonNumber() + */ + @Override + public JsonNumber getAsJsonNumber() { + return this; + } + + /** + * @return number + */ + public float getNumberAsFloat() { + return Float.parseFloat(value); + } + + /** + * @param value the number to set + */ + public void setNumber(float value) { + this.value = value + ""; + } + + /** + * @return number + */ + public double getNumberAsDouble() { + return Double.parseDouble(value); + } + + /** + * @param value the number to set + */ + public void setNumber(double value) { + this.value = value + ""; + } + + /** + * @return number + */ + public int getNumberAsInteger() { + return Integer.parseInt(value); + } + + /** + * @param value the number to set + */ + public void setNumber(int value) { + this.value = value + ""; + } + + /** + * @return number + */ + public long getNumberAsLong() { + return Long.parseLong(value); + } + + /** + * @param value the number to set + */ + public void setNumber(long value) { + this.value = value + ""; + } + + @Override + public String toString() { + return value; + } + + @Override public String print(IndentPrinter p) { + p.print(value); + return p.getContent(); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/json/JsonObject.java b/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/json/JsonObject.java new file mode 100644 index 0000000000..e066b06602 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/json/JsonObject.java @@ -0,0 +1,494 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symboltable.serialization.json; + +import de.monticore.prettyprint.IndentPrinter; +import de.monticore.symboltable.serialization.JsonPrinter; +import de.se_rwth.commons.logging.Log; + +import java.util.*; + +/** + * Json Objects contain members in form of name-value pairs. The name is a (unique) String, and the + * value any JsonElement. + */ +public class JsonObject implements JsonElement { + + protected Map members; + + public JsonObject() { + this.members = new LinkedHashMap<>(); + } + + /** + * As this is a Json object, return true + * + * @return + */ + @Override + public boolean isJsonObject() { + return true; + } + + /** + * As this is a Json object, return "this" + * + * @return + */ + @Override + public JsonObject getAsJsonObject() { + return this; + } + + /** + * A map with all members of this object + * + * @return attributes + */ + public Map getMembers() { + return this.members; + } + + /** + * @param members the attributes to set + */ + public void setMembers(Map members) { + this.members = members; + } + + /** + * @return + * @see java.util.Map#size() + */ + public int sizeMembers() { + return this.members.size(); + } + + /** + * @return + * @see java.util.Map#isEmpty() + */ + public boolean hasMembers() { + return this.members.isEmpty(); + } + + /** + * @param name + * @return + * @see java.util.Map#containsKey(java.lang.Object) + */ + public boolean hasMember(String name) { + return this.members.containsKey(name); + } + + /** + * @param name + * @return + * @see java.util.Map#get(java.lang.Object) + */ + public JsonElement getMember(String name) { + if (this.members.containsKey(name)) { + return this.members.get(name); + } + Log.error("0xA0570 Member \"" + name + "\" is not contained in Json object " + this); + return null; + } + + /** + * @param name + * @param value + * @return + * @see java.util.Map#put(java.lang.Object, java.lang.Object) + */ + public JsonElement putMember(String name, JsonElement value) { + if (null == value) { + Log.error( + "0xA0571 Cannot add the member \"" + name + "\" to the current Json object \"" + this + + "\", because its value is null!"); + return null; + } + return this.members.put(name, value); + } + + /** + * @param name + * @return + * @see java.util.Map#remove(java.lang.Object) + */ + public JsonElement removeMember(String name) { + return this.members.remove(name); + } + + /** + * @return + * @see java.util.Map#keySet() + */ + public Set getMemberNames() { + return this.members.keySet(); + } + + /** + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return print(new IndentPrinter()); + } + + //////////////////// Convenience Methods //////////////////// + + /** + * Checks whether there exists a member with the corresponding name and checks + * whether it is of type String. + * + * @param name The name of the possible JSON element + * @return true if the correctly typed member available, false otherwise + */ + public boolean hasStringMember(String name) { + return hasMember(name) && getMember(name).isJsonString(); + } + + /** + * This method returns the value of a String member of this object, if it exists, Otherwise, raises an error and returns null. + * + * @param name + * @return + */ + public String getStringMember(String name) { + if (hasStringMember(name)) { + return getMember(name).getAsJsonString().getValue(); + } + Log.error("0xA0572 \"" + name + "\" is not a Json String member of \"" + this + "\"!"); + return null; + } + + /** + * This method returns an Optional of a String member of this object, if it exists, Otherwise, + * returns an empty optional. + * + * @param name + * @return + */ + public Optional getStringMemberOpt(String name) { + if (hasStringMember(name)) { + return Optional.ofNullable(getMember(name).getAsJsonString().getValue()); + } + return Optional.empty(); + } + + /** + * Checks whether there exists a member with the corresponding name and checks + * whether it is of type JSON-Array. + * + * @param name The name of the possible JSON element + * @return true if the correctly typed member available, false otherwise + */ + public boolean hasArrayMember(String name) { + return hasMember(name) && getMember(name).isJsonArray(); + } + + /** + * This method returns the value of an Array member of this object, if it exists, Otherwise, raises an error and returns null. + * + * @param name + * @return + */ + public List getArrayMember(String name) { + if (hasArrayMember(name)) { + return getMember(name).getAsJsonArray().getValues(); + } + Log.error("0xA0573 \"" + name + "\" is not a Json Array member of \"" + this + "\"!"); + return null; + } + + /** + * This method returns an Optional of an Array member of this object, if it exists, Otherwise, + * returns an empty optional. + * + * @param name + * @return + */ + public Optional> getArrayMemberOpt(String name) { + if (hasArrayMember(name)) { + return Optional.ofNullable(getMember(name).getAsJsonArray().getValues()); + } + return Optional.empty(); + } + + /** + * Checks whether there exists a member with the corresponding name and checks + * whether it is of type JSON-Boolean. + * + * @param name The name of the possible JSON element + * @return true if the correctly typed member available, false otherwise + */ + public boolean hasBooleanMember(String name) { + return hasMember(name) && getMember(name).isJsonBoolean(); + } + + /** + * This method returns the value of a Boolean member of this object, if it exists, Otherwise, raises an error and returns null. + * + * @param name + * @return + */ + public boolean getBooleanMember(String name) { + if (hasBooleanMember(name)) { + return getMember(name).getAsJsonBoolean().getValue(); + } + Log.error("0xA0574 \"" + name + "\" is not a Json Boolean member of \"" + this + "\"!"); + return false; + } + + /** + * This method returns an Optional of a Boolean member of this object, if it exists, Otherwise, + * returns an empty optional. + * + * @param name + * @return + */ + public Optional getBooleanMemberOpt(String name) { + if (hasBooleanMember(name)) { + return Optional.ofNullable(getMember(name).getAsJsonBoolean().getValue()); + } + return Optional.empty(); + } + + /** + * Checks whether there exists a member with the corresponding name and checks + * whether it is of type JSON-Object. + * + * @param name The name of the possible JSON element + * @return true if the correctly typed member available, false otherwise + */ + public boolean hasObjectMember(String name) { + return hasMember(name) && getMember(name).isJsonObject(); + } + + /** + * This method returns the value of an Object member of this object, if it exists, Otherwise, raises an error and returns null. + * + * @param name + * @return + */ + public JsonObject getObjectMember(String name) { + if (hasObjectMember(name)) { + return getMember(name).getAsJsonObject(); + } + Log.error("0xA0575 \"" + name + "\" is not a Json Object member of \"" + this + "\"!"); + return null; + } + + /** + * This method returns an Optional of an Object member of this object, if it exists, Otherwise, + * returns an empty optional. + * + * @param name + * @return + */ + public Optional getObjectMemberOpt(String name) { + if (hasObjectMember(name)) { + return Optional.ofNullable(getMember(name).getAsJsonObject()); + } + return Optional.empty(); + } + + /** + * Checks whether there exists a member with the corresponding name and checks + * whether it is of type JSON-Integer. + * + * @param name The name of the possible JSON element + * @return true if the correctly typed member available, false otherwise + */ + public boolean hasIntegerMember(String name) { + return hasMember(name) && getMember(name).isJsonNumber(); + } + + /** + * This method returns the value of an Integer member of this object, if it exists, Otherwise, raises an error and returns null. + * + * @param name + * @return + */ + public int getIntegerMember(String name) { + if (hasIntegerMember(name)) { + return getMember(name).getAsJsonNumber().getNumberAsInteger(); + } + Log.error("0xA0576 \"" + name + "\" is not a Json Integer member of \"" + this + "\"!"); + return -1; + } + + /** + * This method returns an Optional of an Integer member of this object, if it exists, Otherwise, + * returns an empty optional. + * + * @param name + * @return + */ + public Optional getIntegerMemberOpt(String name) { + if (hasIntegerMember(name)) { + return Optional.ofNullable(getMember(name).getAsJsonNumber().getNumberAsInteger()); + } + return Optional.empty(); + } + + /** + * Checks whether there exists a member with the corresponding name and checks + * whether it is of type JSON-Double. + * + * @param name The name of the possible JSON element + * @return true if the correctly typed member available, false otherwise + */ + public boolean hasDoubleMember(String name) { + return hasMember(name) && getMember(name).isJsonNumber(); + } + + /** + * This method returns the value of an Double member of this object, if it exists, Otherwise, raises an error and returns null. + * + * @param name + * @return + */ + public double getDoubleMember(String name) { + if (hasIntegerMember(name)) { + return getMember(name).getAsJsonNumber().getNumberAsDouble(); + } + Log.error("0xA0578 \"" + name + "\" is not a Json Double member of \"" + this + "\"!"); + return -1; + } + + /** + * This method returns an Optional of an Double member of this object, if it exists, Otherwise, + * returns an empty optional. + * + * @param name + * @return + */ + public Optional getDoubleMemberOpt(String name) { + if (hasIntegerMember(name)) { + return Optional.ofNullable(getMember(name).getAsJsonNumber().getNumberAsDouble()); + } + return Optional.empty(); + } + + /** + * Checks whether there exists a member with the corresponding name and checks + * whether it is of type JSON-Float. + * + * @param name The name of the possible JSON element + * @return true if the correctly typed member available, false otherwise + */ + public boolean hasFloatMember(String name) { + return hasMember(name) && getMember(name).isJsonNumber(); + } + + /** + * This method returns the value of an Float member of this object, if it exists, Otherwise, raises an error and returns null. + * + * @param name + * @return + */ + public float getFloatMember(String name) { + if (hasIntegerMember(name)) { + return getMember(name).getAsJsonNumber().getNumberAsFloat(); + } + Log.error("0xA0579 \"" + name + "\" is not a Json Float member of \"" + this + "\"!"); + return -1; + } + + /** + * This method returns an Optional of an Double member of this object, if it exists, Otherwise, + * returns an empty optional. + * + * @param name + * @return + */ + public Optional getFloatMemberOpt(String name) { + if (hasIntegerMember(name)) { + return Optional.ofNullable(getMember(name).getAsJsonNumber().getNumberAsFloat()); + } + return Optional.empty(); + } + + /** + * Checks whether there exists a member with the corresponding name and checks + * whether it is of type JSON-Long. + * + * @param name The name of the possible JSON element + * @return true if the correctly typed member available, false otherwise + */ + public boolean hasLongMember(String name) { + return hasMember(name) && getMember(name).isJsonNumber(); + } + + /** + * This method returns the value of an Long member of this object, if it exists, Otherwise, raises an error and returns null. + * + * @param name + * @return + */ + public long getLongMember(String name) { + if (hasIntegerMember(name)) { + return getMember(name).getAsJsonNumber().getNumberAsLong(); + } + Log.error("0xA0565 \"" + name + "\" is not a Json Long member of \"" + this + "\"!"); + return -1; + } + + /** + * This method returns an Optional of an Long member of this object, if it exists, Otherwise, + * returns an empty optional. + * + * @param name + * @return + */ + public Optional getLongMemberOpt(String name) { + if (hasIntegerMember(name)) { + return Optional.ofNullable(getMember(name).getAsJsonNumber().getNumberAsLong()); + } + return Optional.empty(); + } + + @Override public String print(IndentPrinter p) { + boolean indent = JsonPrinter.isIndentationEnabled(); + if (members.isEmpty() && !JsonPrinter.isSerializingDefaults()) { + return p.getContent(); + } + + // print members of object with a buffer to check whether it is empty + IndentPrinter buffer = new IndentPrinter(); + buffer.setIndentation(p.getIndentation() + 1); + + // print the value of each member with another buffer to check emptiness + IndentPrinter tmp = new IndentPrinter(); + tmp.setIndentation(p.getIndentation() + 1); + + String sep = ""; + for (String k : members.keySet()) { + members.get(k).print(tmp); + if (!tmp.getContent().isEmpty()) { + buffer.print(sep + "\"" + k + (indent ? "\": " : "\":") + tmp.getContent()); + tmp.clearBuffer(); + sep = indent ? ",\n" : ","; + } + } + + if (!buffer.getContent().isEmpty() || JsonPrinter.isSerializingDefaults()) { + if (indent) { + p.println("{"); + p.indent(); + p.print(buffer.getContent()); + p.println(); + p.unindent(); + p.print("}"); + } + else { + p.print("{"); + p.print(buffer.getContent()); + p.print("}"); + } + } + + return p.getContent(); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/json/JsonString.java b/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/json/JsonString.java new file mode 100644 index 0000000000..4026700fd6 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/json/JsonString.java @@ -0,0 +1,66 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symboltable.serialization.json; + +import de.monticore.prettyprint.IndentPrinter; + +/** + * This realizes a Json String. + */ +public class JsonString implements JsonElement { + + protected String value; + + public JsonString(String value) { + this.value = value; + } + + /** + * @see de.monticore.symboltable.serialization.json.JsonElement#isJsonString() + */ + @Override + public boolean isJsonString() { + return true; + } + + /** + * @see de.monticore.symboltable.serialization.json.JsonElement#getAsJsonString() + */ + @Override + public JsonString getAsJsonString() { + return this; + } + + /** + * @return value + */ + public String getValue() { + return this.value; + } + + /** + * @param value the value to set + */ + public void setValue(String value) { + this.value = value; + } + + /** + * + * @return + * @see java.lang.String#length() + */ + public int length() { + return this.value.length(); + } + + @Override + public String toString() { + return value ; + } + + @Override public String print(IndentPrinter p) { + p.print(value); + return p.getContent(); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/json/TraceableJsonObject.java b/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/json/TraceableJsonObject.java new file mode 100644 index 0000000000..3e42cef66b --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/json/TraceableJsonObject.java @@ -0,0 +1,60 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symboltable.serialization.json; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * This class is a subtype of a JsonObject that traces, which members have been visited. This can be + * used, e.g., to track members that are stored but not yet taken into account for building up Java + * objects. + * + */ +public class TraceableJsonObject extends JsonObject { + + protected Set visitedMembers; + + /** + * Constructor for de.monticore.symboltable.serialization.json.TraceableJsonObject + */ + public TraceableJsonObject() { + visitedMembers = new HashSet<>(); + } + + /** + * @see de.monticore.symboltable.serialization.json.JsonObject#setMembers(java.util.Map) + */ + @Override + public void setMembers(Map members) { + visitedMembers = new HashSet<>(); + super.setMembers(members); + } + + /** + * @see de.monticore.symboltable.serialization.json.JsonObject#getMember(java.lang.String) + */ + @Override + public JsonElement getMember(String name) { + JsonElement result = super.getMember(name); + if (null != result) { + visitedMembers.add(name); + } + return result; + } + + /** + * This returns a collection of keys of members for which no getter method has been invoked yet. + * + * @return + */ + public Collection getUnvisitedMembers() { + //first create a copy of the set of member names + Set keySet = new HashSet<>(this.getMemberNames()); + //and then remove all members that have been visited + keySet.removeAll(visitedMembers); + return keySet; + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/json/UserJsonString.java b/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/json/UserJsonString.java new file mode 100644 index 0000000000..9b0eb35d47 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/symboltable/serialization/json/UserJsonString.java @@ -0,0 +1,19 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symboltable.serialization.json; + +import de.monticore.prettyprint.IndentPrinter; +import org.apache.commons.text.StringEscapeUtils; + +public class UserJsonString extends JsonString { + + public UserJsonString(String value) { + super(value); + } + + @Override + public String print(IndentPrinter p) { + p.print('"' + StringEscapeUtils.escapeJson(value) + '"'); + return p.getContent(); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/tf/ast/IAttributeNegation.java b/monticore-runtime/src/main/java/de/monticore/tf/ast/IAttributeNegation.java new file mode 100644 index 0000000000..0430b18ee0 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/tf/ast/IAttributeNegation.java @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.ast; + +/** + * interface that is implemented by generated ast classes that describe + * negations + * + */ +public interface IAttributeNegation extends ITFAttribute { + +} diff --git a/monticore-runtime/src/main/java/de/monticore/tf/ast/IAttributeOptional.java b/monticore-runtime/src/main/java/de/monticore/tf/ast/IAttributeOptional.java new file mode 100644 index 0000000000..bbee3d6e51 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/tf/ast/IAttributeOptional.java @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.ast; + +/** + * interface that is implemented by generated ast classes that describe + * optional attributes + * + */ +public interface IAttributeOptional extends ITFAttribute { + +} diff --git a/monticore-runtime/src/main/java/de/monticore/tf/ast/IAttributePattern.java b/monticore-runtime/src/main/java/de/monticore/tf/ast/IAttributePattern.java new file mode 100644 index 0000000000..6458888a20 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/tf/ast/IAttributePattern.java @@ -0,0 +1,15 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.ast; + +/** + * interface that is implemented by generated ast classes that describe + * attribute patterns + * + */ +public interface IAttributePattern extends ITFAttribute { + + default ITFElement getTFElement() { + return this; + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/tf/ast/IAttributeReplacement.java b/monticore-runtime/src/main/java/de/monticore/tf/ast/IAttributeReplacement.java new file mode 100644 index 0000000000..34d8e3d37c --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/tf/ast/IAttributeReplacement.java @@ -0,0 +1,23 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.ast; + +/** + * interface that is implemented by generated ast classes that describe + * attribute replacements + * + */ +public interface IAttributeReplacement extends ITFAttribute { + + ITFElement getLhs(); + + ITFElement getRhs(); + + boolean isPresentLhs(); + + boolean isPresentRhs(); + + default ITFElement getTFElement(){ + return isPresentLhs() ? getLhs() : getRhs(); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/tf/ast/IList.java b/monticore-runtime/src/main/java/de/monticore/tf/ast/IList.java new file mode 100644 index 0000000000..06daa18198 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/tf/ast/IList.java @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.ast; + +/** + * interface that is implemented by generated ast classes that describe lists + * + */ +public interface IList extends ITFObject { + +} diff --git a/monticore-runtime/src/main/java/de/monticore/tf/ast/INegation.java b/monticore-runtime/src/main/java/de/monticore/tf/ast/INegation.java new file mode 100644 index 0000000000..85df165251 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/tf/ast/INegation.java @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.ast; + +/** + * interface that is implemented by generated ast classes that describe + * attribute negations + * + */ +public interface INegation extends ITFObject { + +} diff --git a/monticore-runtime/src/main/java/de/monticore/tf/ast/IOptional.java b/monticore-runtime/src/main/java/de/monticore/tf/ast/IOptional.java new file mode 100644 index 0000000000..b4ca22145f --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/tf/ast/IOptional.java @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.ast; + +/** + * interface that is implemented by generated ast classes that describe optional + * nodes + * + */ +public interface IOptional extends ITFObject { + +} diff --git a/monticore-runtime/src/main/java/de/monticore/tf/ast/IPattern.java b/monticore-runtime/src/main/java/de/monticore/tf/ast/IPattern.java new file mode 100644 index 0000000000..56165f777c --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/tf/ast/IPattern.java @@ -0,0 +1,14 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.ast; + +/** + * interface that is implemented by generated ast classes that describe patterns + * + */ +public interface IPattern extends ITFObject { + + default ITFElement getTFElement() { + return this; + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/tf/ast/IReplacement.java b/monticore-runtime/src/main/java/de/monticore/tf/ast/IReplacement.java new file mode 100644 index 0000000000..db02865479 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/tf/ast/IReplacement.java @@ -0,0 +1,25 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.ast; + +/** + * interface that is implemented by generated ast classes that describe + * replacements + * + */ +public interface IReplacement extends ITFElement { + + ITFElement getLhs(); + + ITFElement getRhs(); + + boolean isPresentLhs(); + + boolean isPresentRhs(); + + default ITFElement getTFElement(){ + return isPresentLhs() ? getLhs() : getRhs(); + } + + IReplacementOperator getReplacementOp(); + +} diff --git a/monticore-runtime/src/main/java/de/monticore/tf/ast/IReplacementOperator.java b/monticore-runtime/src/main/java/de/monticore/tf/ast/IReplacementOperator.java new file mode 100644 index 0000000000..ef3c3a98c0 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/tf/ast/IReplacementOperator.java @@ -0,0 +1,22 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.ast; + + +/** + * interface that is implemented by replacementOperator + * replacements + * + */ +public interface IReplacementOperator { + + boolean isFirst(); + + boolean isLast(); + + boolean isDefault(); + + boolean isRelative(); + + boolean isInplace(); + +} diff --git a/monticore-runtime/src/main/java/de/monticore/tf/ast/ITFAttribute.java b/monticore-runtime/src/main/java/de/monticore/tf/ast/ITFAttribute.java new file mode 100644 index 0000000000..1b6c048fc5 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/tf/ast/ITFAttribute.java @@ -0,0 +1,11 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.ast; + +/** + * interface that is implemented by generated ast classes that describe + * attributes + * + */ +public interface ITFAttribute extends ITFElement { + +} diff --git a/monticore-runtime/src/main/java/de/monticore/tf/ast/ITFElement.java b/monticore-runtime/src/main/java/de/monticore/tf/ast/ITFElement.java new file mode 100644 index 0000000000..ec4c818982 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/tf/ast/ITFElement.java @@ -0,0 +1,15 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.ast; + +import de.monticore.ast.ASTNode; + +/** + * common interface for all AST objects in transformation rules + * + */ +public interface ITFElement extends ASTNode { + + ITFElement getTFElement(); + + +} diff --git a/monticore-runtime/src/main/java/de/monticore/tf/ast/ITFObject.java b/monticore-runtime/src/main/java/de/monticore/tf/ast/ITFObject.java new file mode 100644 index 0000000000..179048d82e --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/tf/ast/ITFObject.java @@ -0,0 +1,36 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.ast; + +import de.monticore.ast.ASTNode; +import de.se_rwth.commons.logging.Log; + +/** + * interface that is implemented by generated ast classes that describe + * instances of all transformation specific AST classes + * + */ +public interface ITFObject extends ITFElement { + + /** + * @return the AST class from the DSL that matches for this objects belong to + */ + Class _getTFElementType(); + + + /** + * @return if a name given for this object is present + */ + default boolean isPresentSchemaVarName(){ + return false; + } + + /** + * @return the name for this object as specified by the user ( + */ + default String getSchemaVarName() { + Log.error("ITFObject does not have a schema var name!"); + return null; + } + + +} diff --git a/monticore-runtime/src/main/java/de/monticore/tf/rule2od/Variable2AttributeMap.java b/monticore-runtime/src/main/java/de/monticore/tf/rule2od/Variable2AttributeMap.java new file mode 100644 index 0000000000..7b96172591 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/tf/rule2od/Variable2AttributeMap.java @@ -0,0 +1,99 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.rule2od; + + +import com.google.common.collect.Maps; +import de.monticore.tf.ast.ITFObject; + +import java.util.HashMap; +import java.util.Map; + +import static com.google.common.base.Preconditions.checkNotNull; + +public class Variable2AttributeMap { + + public Map getV2a() { + return Maps.newHashMap(v2a); + } + + public void setV2a( + Map v2a) { + this.v2a = v2a; + } + + private Map v2a = new HashMap<>(); + + public void addDeclaration(String variableName, ITFObject object, String attributeName) { + v2a.put(variableName, new AttributeEntry(object, attributeName)); + } + + public void addDeclaration(String variableName, ITFObject object, String attributeName, boolean listValued) { + v2a.put(variableName, new AttributeEntry(object, attributeName, listValued)); + } + + public boolean contains(String variableName) { + return v2a.containsKey(variableName); + } + + /** + * @return the object for the referenced attribute + */ + public ITFObject getObject(String variableName) { + return v2a.get(variableName).getObject(); + } + + /** + * @return the attribute name for the referenced attribute, null if the + * variable is unknown + */ + public String getAttributeName(String variableName) { + return v2a.get(variableName).getAttributeName(); + } + + public boolean isList(String variableName){ + return v2a.get(variableName).isListValued(); + } + + class AttributeEntry { + AttributeEntry(ITFObject object, String attributeName) { + super(); + checkNotNull(object); + checkNotNull(attributeName); + this.object = object; + this.attributeName = attributeName; + } + + AttributeEntry(ITFObject object, String attributeName, boolean listValued) { + super(); + checkNotNull(object); + checkNotNull(attributeName); + this.object = object; + this.attributeName = attributeName; + this.listValued = listValued; + } + + public ITFObject getObject() { + return object; + } + + public String getAttributeName() { + return attributeName; + } + + private ITFObject object; + private String attributeName; + + public boolean isListValued() { + return listValued; + } + + private boolean listValued = false; + } + + @Override + public Variable2AttributeMap clone() { + Variable2AttributeMap map = new Variable2AttributeMap(); + map.setV2a(getV2a()); + return map; + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/tf/runtime/ODRule.java b/monticore-runtime/src/main/java/de/monticore/tf/runtime/ODRule.java new file mode 100644 index 0000000000..e49ab85434 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/tf/runtime/ODRule.java @@ -0,0 +1,51 @@ +/* + * (c) https://github.com/MontiCore/monticore + */ + +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.runtime; + +import de.monticore.ast.ASTNode; +import de.monticore.generating.templateengine.GlobalExtensionManagement; + +import static de.monticore.generating.templateengine.reporting.Reporting.*; + +public abstract class ODRule { + + protected GlobalExtensionManagement glex = new GlobalExtensionManagement(); + + + public abstract boolean doPatternMatching(); + + public abstract void doReplacement(); + + public GlobalExtensionManagement getGlex() { + return glex; + } + + public void setGlex(GlobalExtensionManagement glex) { + this.glex = glex; + } + + public void reportChange(String transformation, ASTNode astNode, String attr, String from, String to){ + reportTransformationObjectChange(transformation, astNode, attr); + reportTransformationOldValue(transformation, from); + reportTransformationNewValue(transformation, to); + } + + public void reportDeletion(String transformation, ASTNode astNode){ + reportTransformationObjectDeletion(transformation, astNode); + } + + public void reportCreation(String transformation, ASTNode astNode){ + reportTransformationObjectCreation(transformation, astNode); + } + + public void reportMatch(String transformation, ASTNode astNode){ + reportTransformationObjectMatch(transformation, astNode); + + } + + + +} diff --git a/monticore-runtime/src/main/java/de/monticore/tf/runtime/SuccessfulReporter.java b/monticore-runtime/src/main/java/de/monticore/tf/runtime/SuccessfulReporter.java new file mode 100644 index 0000000000..22f39b20aa --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/tf/runtime/SuccessfulReporter.java @@ -0,0 +1,52 @@ +/* (c) https://github.com/MontiCore/monticore */ +/* + * (c) https://github.com/MontiCore/monticore + */ + + +package de.monticore.tf.runtime; + +import de.monticore.ast.ASTNode; +import de.monticore.generating.templateengine.reporting.commons.AReporter; +import de.monticore.generating.templateengine.reporting.commons.ReportingConstants; + +import java.io.File; + +/** * + */ + +public class SuccessfulReporter extends AReporter { + + final static String SIMPLE_FILE_NAME = "19_Successful"; + + public SuccessfulReporter(String outputDir, String modelName) { + super(outputDir + File.separator + ReportingConstants.REPORTING_DIR + + File.separator + modelName, SIMPLE_FILE_NAME, + ReportingConstants.REPORT_FILE_EXTENSION); + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportModelStart(ASTNode, String, String) + */ + @Override + public void reportModelStart(ASTNode ast, String modelName, String fileName) { + reportingHelper.deleteFile(SIMPLE_FILE_NAME, ReportingConstants.REPORT_FILE_EXTENSION); + } + + /** + * @see de.monticore.generating.templateengine.reporting.commons.DefaultReportEventHandler#reportModelEnd(String, String) + */ + @Override + public void reportModelEnd(String modelname, String filename) { + writeLine("#Successful generation"); + } + + /** + * @see AReporter#writeHeader() + */ + @Override + protected void writeHeader() { + } + + +} diff --git a/monticore-runtime/src/main/java/de/monticore/tf/runtime/ValueComparator.java b/monticore-runtime/src/main/java/de/monticore/tf/runtime/ValueComparator.java new file mode 100644 index 0000000000..5273a8cbd5 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/tf/runtime/ValueComparator.java @@ -0,0 +1,22 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.runtime; + +import java.util.Comparator; +import java.util.Map; + +public class ValueComparator implements Comparator { + + private Map data = null; + + public ValueComparator(Map data) { + this.data = data; + } + + @Override + public int compare(String o1, String o2) { + int value1 = (Integer) data.get(o1); + int value2 = (Integer) data.get(o2); + return (value1 > value2) ? -1 : (value2 >= value1) ? 1 : 0; + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/tf/runtime/matching/ModelTraversal.java b/monticore-runtime/src/main/java/de/monticore/tf/runtime/matching/ModelTraversal.java new file mode 100644 index 0000000000..0a24d9c00a --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/tf/runtime/matching/ModelTraversal.java @@ -0,0 +1,49 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.runtime.matching; + +import de.monticore.ast.ASTNode; +import de.monticore.visitor.ITraverser; + +import java.util.*; + +public class ModelTraversal { + + protected Map> cName2instances = new HashMap<>(); + protected List all = new ArrayList<>(); + protected Map parents= new HashMap<>(); + protected Stack currentparents = new Stack<>(); + + private final E traverser; + + protected ModelTraversal(E traverser) { + this.traverser = traverser; + } + + public Collection getInstances(String className) { + if (cName2instances.containsKey(className)) { + return cName2instances.get(className); + } + return new LinkedList<>(); + } + + public List getAll(){ + return all; + } + + public boolean containsKey(String key){ + return cName2instances.containsKey(key); + } + + public ASTNode getParent(ASTNode node){ + return parents.get(node); + } + + + public Map getParents(){ + return parents; + } + + public E getTraverser() { + return traverser; + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/tf/runtime/matching/ModelTraversalFactory.java b/monticore-runtime/src/main/java/de/monticore/tf/runtime/matching/ModelTraversalFactory.java new file mode 100644 index 0000000000..5e293cce34 --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/tf/runtime/matching/ModelTraversalFactory.java @@ -0,0 +1,33 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.runtime.matching; + +import de.monticore.visitor.ITraverser; + +public class ModelTraversalFactory { + + private static ModelTraversalFactory instance; + + public static ModelTraversalFactory getInstance() { + if (instance == null) { + instance = new ModelTraversalFactory(); + } + return instance; + } + + /** + * @return a {@link ModelTraversal} for the given model + */ + public ModelTraversalVisitor createVisitor(ModelTraversal modelTraversal) { + return new ModelTraversalVisitor(modelTraversal); + } + + public ModelTraversal create(java.util.function.Supplier traverserSupplier){ + return this.create(traverserSupplier.get()); + } + + public ModelTraversal create(E traverser){ + ModelTraversal modelTraversal = new ModelTraversal<>(traverser); + traverser.add4IVisitor(createVisitor(modelTraversal)); + return modelTraversal; + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/tf/runtime/matching/ModelTraversalVisitor.java b/monticore-runtime/src/main/java/de/monticore/tf/runtime/matching/ModelTraversalVisitor.java new file mode 100644 index 0000000000..8a98367d8c --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/tf/runtime/matching/ModelTraversalVisitor.java @@ -0,0 +1,38 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.tf.runtime.matching; + +import de.monticore.ast.ASTNode; +import de.monticore.visitor.IVisitor; + +import java.util.LinkedList; + + +public class ModelTraversalVisitor implements IVisitor { + + private final ModelTraversal modelTraversal; + + protected ModelTraversalVisitor( + ModelTraversal modelTraversal) { + this.modelTraversal = modelTraversal; + } + + @Override + public void visit(ASTNode node) { + if (!modelTraversal.currentparents.isEmpty()) { + modelTraversal.parents.put(node, modelTraversal.currentparents.peek()); + } + modelTraversal.currentparents.push(node); + String cName = node.getClass().getCanonicalName(); + modelTraversal.all.add(node); + if (!modelTraversal.cName2instances.containsKey(cName)) { + modelTraversal.cName2instances.put(cName, new LinkedList<>()); + } + modelTraversal.cName2instances.get(cName).add(node); + } + + @Override + public void endVisit(ASTNode node) { + modelTraversal.currentparents.pop(); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/utils/Link.java b/monticore-runtime/src/main/java/de/monticore/utils/Link.java new file mode 100644 index 0000000000..f111a2bffe --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/utils/Link.java @@ -0,0 +1,109 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.utils; + +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import javax.annotation.Nullable; + +import de.monticore.ast.ASTNode; + +import com.google.common.collect.Iterables; +import com.google.common.collect.Sets; + +import de.se_rwth.commons.TreeUtil; +import de.se_rwth.commons.Util; + +/** + * Represents a link between two ASTNodes. + *

+ * When writing transformations that translate from one AST to another, it is often cleaner not to + * translate all information in a single operation. Splitting this translation process however + * necessitates that sequentially executed translations always operate on the same source and target + * nodes. For this purpose a Link provides a way for the program to "remember" which source nodes + * are associated with which target nodes. + *

+ * Going beyond that, Links also form a tree that mirrors the structure in both ASTs. This is based + * on the assumption that the overall structure of both source and target AST will also be + * equivalent. + * + */ +public final class Link implements Iterable> { + + protected final S source; + + protected final T target; + + protected final Link parent; + + protected final Set> childLinks = new LinkedHashSet>(); + + public Link(S source, T target, @Nullable Link parent) { + this.source = source; + this.target = target; + this.parent = parent; + if (parent != null) { + parent.addChildLink(this); + } + } + + protected void addChildLink(Link childLink) { + childLinks.add(childLink); + } + + /** + * @return the source node of this Link + */ + public S source() { + return source; + } + + /** + * @return the target node of this Link + */ + public T target() { + return target; + } + + /** + * @return the parent Link of this Link + */ + public Link parent() { + return parent; + } + + /** + * @return the topmost Link in the tree to which this Link belongs + */ + public Link rootLink() { + List> parents = Util.listTillNull(this, Link::parent); + return Iterables.getLast(parents); + } + + @Override + public Iterator> iterator() { + Iterable> subtree = TreeUtil.preOrder(this, link -> link.childLinks); + return subtree.iterator(); + } + + /** + * Looks up all Links in the subtree spanned by this Link by type of source and target. You can + * use super classes like 'ASTNode.class' if you want to filter more loosely. + * + * @param sourceType the class of the source node + * @param targetType the class of the target node + * @return the set of all Links with the specified source and target types + */ + @SuppressWarnings(value = { "unchecked", "rawtypes" }) + public Set> getLinks(Class sourceType, + Class targetType) { + + Set matchingLinks = Sets.newLinkedHashSet(Iterables.filter(this, link -> + sourceType.isInstance(link.source) && targetType.isInstance(link.target))); + + return matchingLinks; + } +} diff --git a/monticore-runtime/src/main/java/de/monticore/visitor/IHandler.java b/monticore-runtime/src/main/java/de/monticore/visitor/IHandler.java new file mode 100644 index 0000000000..902e92dc8c --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/visitor/IHandler.java @@ -0,0 +1,54 @@ +/* (c) https://github.com/MontiCore/monticore */ + +/* generated from model BasicSymbols */ +/* generated by template core.Interface*/ + +package de.monticore.visitor; + + +import de.monticore.ast.ASTNode; +import de.monticore.symboltable.IScope; +import de.monticore.symboltable.IScopeSpanningSymbol; +import de.monticore.symboltable.ISymbol; +import de.monticore.symboltable.SymbolWithScopeOfUnknownKind; + +public interface IHandler { + + ITraverser getTraverser(); + + default void handle(ASTNode node) { + getTraverser().visit(node); + getTraverser().endVisit(node); + } + + default void handle(ISymbol symbol) { + getTraverser().visit(symbol); + getTraverser().endVisit(symbol); + } + + default void handle(IScopeSpanningSymbol symbol) { + getTraverser().visit(symbol); + getTraverser().endVisit(symbol); + } + + default void handle(IScope scope) { + getTraverser().visit(scope); + getTraverser().traverse(scope); + getTraverser().endVisit(scope); + } + + default void traverse(IScope scope) { + for (SymbolWithScopeOfUnknownKind s : scope.getLocalUnknownSymbols()) { + s.accept(getTraverser()); + } + } + + default void handle(SymbolWithScopeOfUnknownKind symbol) { + getTraverser().visit(symbol); + getTraverser().traverse(symbol); + getTraverser().endVisit(symbol); + } + + default void traverse(SymbolWithScopeOfUnknownKind symbol) {} + +} diff --git a/monticore-runtime/src/main/java/de/monticore/visitor/ITraverser.java b/monticore-runtime/src/main/java/de/monticore/visitor/ITraverser.java new file mode 100644 index 0000000000..6832906f3f --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/visitor/ITraverser.java @@ -0,0 +1,125 @@ +/* generated from model BasicSymbols */ +/* generated by template core.Interface*/ + +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.visitor; + + +import de.monticore.ast.ASTNode; +import de.monticore.symboltable.IScope; +import de.monticore.symboltable.IScopeSpanningSymbol; +import de.monticore.symboltable.ISymbol; +import de.monticore.symboltable.SymbolWithScopeOfUnknownKind; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +public interface ITraverser { + + default void add4IVisitor(IVisitor iVisitor) { + } + + default List getIVisitorList() { + return new ArrayList<>(); + } + + // ASTNode + default void handle(ASTNode node) { + visit(node); + // no traverse() for abstract classes, interfaces and enums, only concrete classes are traversed + endVisit(node); + } + + default void visit(ASTNode node) { + getIVisitorList().forEach(v -> v.visit(node)); + } + + default void endVisit(ASTNode node) { + getIVisitorList().forEach(v -> v.endVisit(node)); + } + + // ISymbol + default void handle(ISymbol symbol) { + visit(symbol); + // no traverse() for abstract classes, interfaces and enums, only concrete classes are traversed + endVisit(symbol); + } + + default void visit(ISymbol symbol) { + getIVisitorList().forEach(v -> v.visit(symbol)); + } + + default void endVisit(ISymbol symbol) { + getIVisitorList().forEach(v -> v.endVisit(symbol)); + } + + // IScopeSpanningSymbol + default void handle(IScopeSpanningSymbol symbol) { + visit(symbol); + // no traverse() for abstract classes, interfaces and enums, only concrete classes are traversed + endVisit(symbol); + } + + default void visit(IScopeSpanningSymbol symbol) { + getIVisitorList().forEach(v -> v.visit(symbol)); + } + + default void endVisit(IScopeSpanningSymbol symbol) { + getIVisitorList().forEach(v -> v.endVisit(symbol)); + } + + // IScope + default void handle(IScope scope) { + visit(scope); + // no traverse() for abstract classes, interfaces and enums, only concrete classes are traversed + endVisit(scope); + } + + default void visit(IScope scope) { + getIVisitorList().forEach(v -> v.visit(scope)); + } + + default void endVisit(IScope scope) { + getIVisitorList().forEach(v -> v.endVisit(scope)); + } + + default void traverse(IScope scope) { + for (SymbolWithScopeOfUnknownKind s : scope.getLocalUnknownSymbols()) { + s.accept(this); + } + } + + default void handle(SymbolWithScopeOfUnknownKind symbol) { + visit(symbol); + traverse(symbol); + endVisit(symbol); + } + + default void visit(SymbolWithScopeOfUnknownKind symbol) { + getIVisitorList().forEach(v -> v.visit(symbol)); + } + + default void endVisit(SymbolWithScopeOfUnknownKind symbol) { + getIVisitorList().forEach(v -> v.endVisit(symbol)); + } + + default void traverse(SymbolWithScopeOfUnknownKind symbol) {} + + Set getTraversedElements(); + + void setTraversedElements(Set traversedElements); + + default void addTraversedElement(Object element) { + getTraversedElements().add(element); + } + + default void addAllTraversedElements(Set elements) { + getTraversedElements().addAll(elements); + } + + default void clearTraversedElements() { + getTraversedElements().clear(); + } + +} diff --git a/monticore-runtime/src/main/java/de/monticore/visitor/IVisitor.java b/monticore-runtime/src/main/java/de/monticore/visitor/IVisitor.java new file mode 100644 index 0000000000..186024518f --- /dev/null +++ b/monticore-runtime/src/main/java/de/monticore/visitor/IVisitor.java @@ -0,0 +1,35 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.visitor; + + +import de.monticore.ast.ASTNode; +import de.monticore.symboltable.IScope; +import de.monticore.symboltable.ISymbol; +import de.monticore.symboltable.IScopeSpanningSymbol; + +public interface IVisitor { + + default void endVisit(ASTNode node) { + } + + default void visit(ASTNode node) { + } + + default void visit(ISymbol symbol) { + } + + default void endVisit(ISymbol symbol) { + } + + default void visit(IScope scope) { + } + + default void endVisit(IScope scope) { + } + + default void visit(IScopeSpanningSymbol symbol) { + } + + default void endVisit(IScopeSpanningSymbol symbol) { + } +} diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/CheckConditions.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/CheckConditions.ftl new file mode 100644 index 0000000000..bdfd4cb02f --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/CheckConditions.ftl @@ -0,0 +1,38 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +<#-- for each object a checkConditions method is created --> +<#list hierarchyHelper.getMandatoryMatchObjects(ast.getPattern().getLHSObjectsList()) as object> + <#if !object.isListObject()> + private boolean checkConditions_${object.getObjectName()}(${object.getType()} cand){ + // if there are dependency objects and they are not null and the condition does not hold return false + <#list ast.getPattern().getObjectConditionsList() as condition> + <#-- test if the condition concerns the object the method is created for--> + <#if condition.getObjectName() = object.getObjectName()> + <#--create null-checks for all dependencies and the condition-check--> + if(<#if condition.isPresentDependency()>${condition.getDependency().getContent()}_cand != null && + ${condition.conditionString}){ + return false; + } + + + + <#if subConstraints??> + <#list subConstraints as subConstraint> + <#if subConstraint.isDependendOn(object)> + <#if !subConstraint.isOptionalInOrPresent()> + if(!isSubConstraintValid_${subConstraint?index}( + <#list subConstraint.dependVars as dependency> + <#if dependency?index gt 0>, + <#if dependency.getObjectName() != object.getObjectName()>${dependency.getObjectName()}_cand + <#else>cand + + )) { + return false; + } + + + + + return true; + } + + diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/CheckConstraints.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/CheckConstraints.ftl new file mode 100644 index 0000000000..81188a081a --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/CheckConstraints.ftl @@ -0,0 +1,18 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +private boolean checkConstraints() { + + boolean result = true; +<#if subConstraints??> + <#list subConstraints as subConstraint> + <#if subConstraint.isOptionalInOrPresent()> + result = result && isSubConstraintValid_${subConstraint?index}( + <#list subConstraint.dependVars as dependency> + <#if dependency?index gt 0>, + ${dependency.getObjectName()}_cand + ); + + + + + return result; +} diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/CheckSubConstraints.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/CheckSubConstraints.ftl new file mode 100644 index 0000000000..4e6feaa12d --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/CheckSubConstraints.ftl @@ -0,0 +1,38 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +<#-- for each subConstraint a checkSubConstraint method is created --> +<#if subConstraints??> + <#list subConstraints as subConstraint> + <#if subConstraint.dependVars??> + private boolean isSubConstraintValid_${subConstraint?index}( + <#list subConstraint.dependVars as dependVar> + <#if dependVar?index gt 0>, + ${dependVar.getType()} ${dependVar.getObjectName()} + + ) { + + <#-- Create java-optionals for elements of type not and optional --> + <#list subConstraint.dependVars as dependVar> + <#if hierarchyHelper.isWithinOptionalStructure(dependVar.getObjectName())> + Optional${"\l"+dependVar.getType()+"\g"} ${dependVar.getObjectName()}_candAsOptional = Optional.ofNullable(${dependVar.getObjectName()}); + <#elseif hierarchyHelper.isWithinNegativeStructure(dependVar.getObjectName())> + Optional${"\l"+dependVar.getType()+"\g"} ${dependVar.getObjectName()}_candAsOptional = Optional.ofNullable(${dependVar.getObjectName()}); + + + + if(<#list subConstraint.dependVars as dependVar> + <#-- check for null only if element is not of type not or optional --> + <#if hierarchyHelper.isWithinOptionalStructure(dependVar.getObjectName())> + <#elseif hierarchyHelper.isWithinNegativeStructure(dependVar.getObjectName())> + <#else> + ${dependVar.getObjectName()} != null && + + + !(${subConstraint.getConstrExpr()})){ + return false; + } + + return true; + } + + + diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/ClassMatch.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/ClassMatch.ftl new file mode 100644 index 0000000000..5cdf2c34d2 --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/ClassMatch.ftl @@ -0,0 +1,56 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +<#assign mandatoryObjects = hierarchyHelper.getMandatoryObjectsWithoutOptAndListChilds(ast.getPattern().getLHSObjectsList())> +<#assign matchingObjects = hierarchyHelper.getMandatoryObjectsWithoutOptAndListChilds(ast.getPattern().getMatchingObjectsList())> +<#assign matchingListObjects = hierarchyHelper.getListObjects(ast.getPattern().getLHSObjectsList())> + +public class Match { + private Match( +<#list mandatoryObjects as object> + <#if !object.isListObject()> ${object.getType()} + <#else>${object.getListtype()} + + ${object.getObjectName()} + <#if object_has_next>, +) { +<#list mandatoryObjects as object> + <#if hierarchyHelper.isWithinOptionalStructure(object.getObjectName())> + this.${object.getObjectName()} = Optional.ofNullable(${object.getObjectName()}); + <#else> + this.${object.getObjectName()} = ${object.getObjectName()}; + +} + +<#list matchingObjects as object> + <#assign isWithinOpt = hierarchyHelper.isWithinOptionalStructure(object.getObjectName())> + private <#if isWithinOpt>Optional< + <#if !object.isListObject()> ${object.getType()} + <#else>${object.getListtype()} + <#if isWithinOpt>>${object.getObjectName()}; + + +// before variables for undo operations +<#list ast.getReplacement().getChangesList() as change> + <#if change.isPresentValue()><#assign valueName = change.getValue()><#else><#assign valueName = change.getOldValue()> + <#if change.isPrimitiveType() || change.isValueStringList()> + <#if !change.isObjectWithinList()> + private ${change.getType()} ${change.getObjectName()}_${change.getAttributeName()}_before; + <#else> + private Map<${change.getObjectType()}, ${change.getType()?cap_first}> ${change.getObjectName()}_${change.getAttributeName()}_before = new HashMap(); + + <#elseif change.isObjectWithinList() > + <#if change.isAttributeIterated()> + private int ${change.getObjectName()}_${valueName}_before_pos; + private Map<${change.getGenericType()}, Integer> ${change.getObjectName()}_${valueName}_before = new HashMap(); + <#else> + private Map<${change.getObjectType()}, ${change.getGenericType()}> ${change.getObjectName()}_${valueName}_before = new HashMap(); + + <#else> + <#if change.isAttributeIterated()> + private int ${change.getObjectName()}_${valueName}_before_pos; + private Map<${change.getGenericType()}, Integer> ${change.getObjectName()}_${valueName}_before = new HashMap(); + <#else> + private ${change.getGenericType()} ${change.getObjectName()}_${valueName}_before; + + + +} diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/ClassMatchList.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/ClassMatchList.ftl new file mode 100644 index 0000000000..b97cf1d3ad --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/ClassMatchList.ftl @@ -0,0 +1,57 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +<#list ast.getPattern().getLHSObjectsList() as list> + <#if list.isListObject()> + <#assign mandatoryObjects = hierarchyHelper.getListChilds(ast.getPattern().getLHSObjectsList(), list)> + <#assign matchingObjects = hierarchyHelper.getListChilds(ast.getPattern().getMatchingObjectsList(), list)> +public class Match${list.getObjectName()}{ + private Match${list.getObjectName()}( + <#list mandatoryObjects as object> + <#if !object.isListObject()> ${object.getType()} + <#else>${object.getListtype()} + ${object.getObjectName()} + <#if object_has_next>, + ){ + <#list mandatoryObjects as object> + <#if hierarchyHelper.isWithinOptionalStructure(object.getObjectName())> + this.${object.getObjectName()} = Optional.ofNullable(${object.getObjectName()}); + <#else> + this.${object.getObjectName()} = ${object.getObjectName()}; + + } + <#list matchingObjects as object> + <#assign isWithinOpt = hierarchyHelper.isWithinOptionalStructure(object.getObjectName())> + private <#if isWithinOpt>Optional< + <#if !object.isListObject()> ${object.getType()} + <#else>${object.getListtype()}> + + <#if isWithinOpt>> ${object.getObjectName()}; + private List ${object.getObjectName()}_temp_candidates; + + private Stack backtracking; +} + + <#list mandatoryObjects as listchild> + // Method for checkConditions to get the Elements of the List to compare while Matching + private List<${listchild.getType()}> get_${listchild.getObjectName()}_temp_cands() { + List<${listchild.getType()}> ${listchild.getObjectName()} = new ArrayList<${listchild.getType()}>(); + ListIterator ${list.getObjectName()}It = ${list.getObjectName()}_candidates.listIterator(); + while(${list.getObjectName()}It.hasNext()) { + Match${list.getObjectName()} ${list.getObjectName()} = (Match${list.getObjectName()})${list.getObjectName()}It.next(); + <#assign isInOpt = hierarchyHelper.isWithinOptionalStructure(listchild.getObjectName())> + <#if isInOpt>if(${list.getObjectName()}.${listchild.getObjectName()}.isPresent()) { + ${listchild.getObjectName()}.add(${list.getObjectName()}.${listchild.getObjectName()}<#if isInOpt>.get()); + <#if isInOpt>} + } + return ${listchild.getObjectName()}; + } + + + //Method for checking if the given object is already matched by the list + private boolean isMatchedBy${list.getObjectName()} (ASTNode cand) { + return + <#list mandatoryObjects as listchild>get_${listchild.getObjectName()}_temp_cands().contains(cand) + <#if listchild_has_next> || + ; + } + + diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/ConstantCode.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/ConstantCode.ftl new file mode 100644 index 0000000000..72340d26dd --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/ConstantCode.ftl @@ -0,0 +1,69 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +<#assign deleteObjects = hierarchyHelper.getMandatoryDeleteObjects(ast.getReplacement().getDeleteObjectsList())> +<#assign changeObjects = hierarchyHelper.getMandatoryChangeObjects(ast.getReplacement().getChangesList())> +<#assign matchObjects = hierarchyHelper.getMandatoryMatchObjects(ast.getPattern().getMatchingObjectsList())> +<#assign optionalMatchObjects = hierarchyHelper.getOptionalMatchObjects(ast.getPattern().getLHSObjectsList())> + + private List hostGraph; + private GlobalExtensionManagement glex = new GlobalExtensionManagement(); + private List allMatches; + private boolean doReplacementExecuted = false; + <#-- for each object creates a _candidates, _candidates_temp nodelist and an _cand object--> + + // Matches + <#list matchObjects as matchObject> + ${tc.include("de.monticore.tf.odrules.constantcode.HandleMatchObject", matchObject)} + + <#list ast.getVariableList() as variable> + // variables + protected boolean ${variable.getName()}_is_fix = false; + private ${variable.getType()} ${variable.getName()}; + + private ModelTraversal t = ModelTraversalFactory.getInstance().create((java.util.function.Supplier)${grammarName}Mill::inheritanceTraverser); + <#list ast.getPattern().getAssocList() as association> + private mc.ast.MCAssociation ${association.getName()}; + + private Stack searchPlan; + // searchplans for optional structures + <#list ast.getPattern().getLHSObjectsList() as lhsObject> + <#if lhsObject.isOptObject() || lhsObject.getType()?ends_with("IOptional") || lhsObject.isListObject()> + private Stack searchPlan_${lhsObject.getObjectName()}; + + + +<#-- // Changes + <#list changeObjects as changeObject> + ${tc.include("de.monticore.tf.odrules.constantcode.HandleChangeObject", changeObject)} + --> + +<#-- // Deletes + <#list deleteObjects as deleteObject> + ${tc.include("de.monticore.tf.odrules.constantcode.HandleDeleteObject", deleteObject)} + --> + + public void reportChange(ASTNode astNode, String attr, String from, String to) { + reportTransformationObjectChange("${ast.getClassname()}", astNode, attr); + reportTransformationOldValue("${ast.getClassname()}", from); + reportTransformationNewValue("${ast.getClassname()}", to); + } + + public void reportDeletion(ASTNode astNode){ + reportTransformationObjectDeletion("${ast.getClassname()}", astNode); + } + + public void reportCreation(ASTNode astNode){ + reportTransformationObjectCreation("${ast.getClassname()}", astNode); + } + + public void reportMatch(ASTNode astNode){ + reportTransformationObjectMatch("${ast.getClassname()}", astNode); + } + + public List getMatches(){ + return allMatches; + } + + public void doAll(){ + doPatternMatching(); + doReplacement(); + } diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/Constructor.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/Constructor.ftl new file mode 100644 index 0000000000..b80510be15 --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/Constructor.ftl @@ -0,0 +1,54 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +public ${ast.getClassname()}(List hostGraph) { + this.hostGraph = hostGraph; +} + +public ${ast.getClassname()}(ASTNode... hostGraph){ + this.hostGraph = Lists.newArrayList(hostGraph); +} + +public ${ast.getClassname()}(GlobalExtensionManagement glex, ASTNode... hostGraph){ + this.hostGraph = Lists.newArrayList(hostGraph); + this.glex = glex; +} + +public ${ast.getClassname()}(GlobalExtensionManagement glex,ASTNode astNode){ + this(astNode,glex); +} + +public ${ast.getClassname()}(ASTNode astNode) { + hostGraph = new ArrayList<>(); + hostGraph.add(astNode); + ReportManager.ReportManagerFactory factory = new ReportManager.ReportManagerFactory() { + @Override + public ReportManager provide(String modelName) { + ReportManager reports = new ReportManager("target/generated-sources"); + TransformationReporter transformationReporter = new TransformationReporter( + "target/generated-sources/reports/transformations", modelName, new ReportingRepository(new ASTNodeIdentHelper())); + reports.addReportEventHandler(transformationReporter); + return reports; + } + }; + + Reporting.init("target/generated-sources", "target/generated-sources", factory); + Reporting.on("${ast.getClassname()}"); +} + +public ${ast.getClassname()}(ASTNode astNode, GlobalExtensionManagement glex) { + hostGraph = new ArrayList<>(); + hostGraph.add(astNode); + this.glex = glex; + ReportManager.ReportManagerFactory factory = new ReportManager.ReportManagerFactory() { + @Override + public ReportManager provide(String modelName) { + ReportManager reports = new ReportManager("target/generated-sources"); + TransformationReporter transformationReporter = new TransformationReporter( + "target/generated-sources/reports/transformations", modelName, new ReportingRepository(new ASTNodeIdentHelper())); + reports.addReportEventHandler(transformationReporter); + return reports; + } + }; + + Reporting.init("target/generated-sources/reports/transformations", "target/generated-sources", factory); + Reporting.on("${ast.getClassname()}"); +} diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/DoPatternMatching.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/DoPatternMatching.ftl new file mode 100644 index 0000000000..70ea520ca7 --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/DoPatternMatching.ftl @@ -0,0 +1,94 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +<#assign mandatoryObjects = hierarchyHelper.getMandatoryObjectsWithoutOptAndListChilds(ast.getPattern().getLHSObjectsList())> +<#macro commaSeperatedNames> + <#list mandatoryObjects as object>${object.getObjectName()}_cand + <#if object_has_next>, + + + +public boolean doPatternMatching() { + Reporting.reportTransformationStart("${ast.getClassname()}"); + boolean foundMatch = true; + // indicates whether this rule is currently backtracking + // (this will skip all attempts to match negative nodes) + boolean isBacktracking = true; + boolean isBacktrackingNegative = false; + for(ASTNode a: hostGraph){ + a.accept(t.getTraverser()); + } + if (searchPlan == null) { + searchPlan = findSearchPlan(); + splitSearchplan(); // for OptList structures + isBacktracking = false; + } + Stack backtracking = new Stack(); + Stack backtrackingNegative = new Stack(); + String nextNode = null; + while(!searchPlan.isEmpty()) { + nextNode = searchPlan.pop(); + <#--creates an if statement for each object for matching the object--> +<#list hierarchyHelper.getMandatoryObjectsWithoutListChilds(ast.getPattern().getLHSObjectsList()) as object> + <#if object.isListObject()> + ${tc.includeArgs("de.monticore.tf.odrules.dopatternmatching.HandleListObject", object, [false])} + <#elseif object.isOptObject()> + ${tc.includeArgs("de.monticore.tf.odrules.dopatternmatching.HandleOptObject", object, [false])} + <#elseif object.isNotObject()> + ${tc.includeArgs("de.monticore.tf.odrules.dopatternmatching.HandleNotObject", object, [false])} + <#else> + ${tc.includeArgs("de.monticore.tf.odrules.dopatternmatching.HandleNormalObject", object, [false])} + + <#if object_has_next>else + + if (!isBacktrackingNegative) { + if (searchPlan.isEmpty()) { + if (!checkConstraints()) { + if (backtracking.isEmpty()) { + // no match of the pattern can be found + foundMatch = false; + break; + } else { + // start backtracking + isBacktrackingNegative = true; + // put all negative elements on the searchPlan + <#list hierarchyHelper.getMandatoryObjectsWithoutListChilds(ast.getPattern().getLHSObjectsList()) as object> + <#if object.isNotObject()> + searchPlan.push(backtracking.pop()); + + + // also put the last not-negative element on the searchPlan + searchPlan.push(backtracking.pop()); + } + } + } + } + } + allMatches = new ArrayList + (); + // create a replacement candidate if a match was found + if (foundMatch) { + Match match = new Match(<@commaSeperatedNames/>); + <#list ast.getPattern().getLHSObjectsList() as object> + <#if !object.isNotObject() && !object.isListObject() && !object.isOptObject() && !hierarchyHelper.isWithinOptionalStructure(object.getObjectName()) && !hierarchyHelper.isWithinListStructure(object.getObjectName())> + if (${object.getObjectName()}_cand != null) { + Reporting.reportTransformationObjectMatch("${ast.getClassname()}",${object.getObjectName()}_cand); + } + if (${object.getObjectName()}_cand != null) { + Reporting.reportTransformationObjectMatch("${ast.getClassname()}",${object.getObjectName()}_cand); + } + + + if (nextNode != null) { + searchPlan.push(nextNode); + } + allMatches.add(match); + } + return foundMatch; +} + +private void clearNegativeObjects() { + <#list ast.getPattern().getLHSObjectsList() as object> + <#if object.isNotObject() && !hierarchyHelper.isWithinListStructure(object.getObjectName())> + ${object.getObjectName()}_cand = null; + + +} diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/DoPatternMatchingList.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/DoPatternMatchingList.ftl new file mode 100644 index 0000000000..3d6c3c0a6d --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/DoPatternMatchingList.ftl @@ -0,0 +1,300 @@ +<#-- (c) https://github.com/MontiCore/monticore --> + +<#list ast.getPattern().getLHSObjectsList() as structure> +<#-- We are only interested in List structures here --> + <#if structure.isListObject()> + <#assign mandatoryObjects = hierarchyHelper.getListChilds(ast.getPattern().getLHSObjectsList(), structure)> + <#assign allObjects = hierarchyHelper.getListChildsWithOptionals(ast.getPattern().getLHSObjectsList(), structure)> + <#-- <#assign allObjects = hierarchyHelper.getListChildsWithOptionals(ast.getPattern().getLHSObjectsList(), structure)> --> + <#-- This call omits optionals! <#assign mandatoryObjects = hierarchyHelper.getListChilds(ast.getPattern().getLHSObjectsList(), structure)> --> + + <#-- <<#assign mandatoryObjects = hierarchyHelper.getListChildsWithOptionals(ast.getPattern().getLHSObjectsList(), structure)> --> + + <#macro commaSeperatedNames> + <#list mandatoryObjects as object>${object.getObjectName()}_cand<#if object_has_next>, + +public boolean doPatternMatching_${structure.getObjectName()}(boolean isParentBacktracking) { + // indicates whether this rule is currently backtracking + // (this will skip all attempts to match negative nodes) + boolean isBacktracking = isParentBacktracking; + boolean isBacktrackingNegative = false; + + Stack backtracking = new Stack(); + Stack backtrackingNegative = new Stack(); + Stack searchPlan = new Stack(); + boolean foundmatch = true; + String nextNode = null; + if(is_${structure.getObjectName()}_fix) { + // The List is given, just write it in the cand + foundmatch = false; + } else if (!isParentBacktracking) { + // if the Parent is not Backtracking find a complete new List + ${structure.getObjectName()}_candidates = new ArrayList(); + } + + // SetUp Last Matching Process if ParentIsBacktracking + if(isParentBacktracking) { + // Get Last List Object + Match${structure.getObjectName()} match = ${structure.getObjectName()}_candidates.get(${structure.getObjectName()}_candidates.size()-1); + ${structure.getObjectName()}_candidates.remove(${structure.getObjectName()}_candidates.size()-1); + // Load the Objects and Their temp_candidates + <#list mandatoryObjects as object> + ${object.getObjectName()}_cand = match.${object.getObjectName()}<#if hierarchyHelper.isWithinOptionalStructure(object.getObjectName())>.get(); + ${object.getObjectName()}_candidates_temp = match.${object.getObjectName()}_temp_candidates; + + // Get the BacktrackingStack + backtracking = match.backtracking; + // Clear the Last Object and put it on the searchPlan + if (!backtracking.isEmpty()) { + <#list mandatoryObjects as object> + if (backtracking.peek().equals("${object.getObjectName()}")) { + ${object.getObjectName()}_cand = null; + } + + searchPlan.push(backtracking.pop()); + } + } + + while(foundmatch) { + // If the parent was Backtracking don't load a new searchPlan + if (!isBacktracking) { + searchPlan = (Stack) searchPlan_${structure.getObjectName()}.clone(); + } + while(!searchPlan.isEmpty()){ + nextNode = searchPlan.pop(); + <#--creates an if statement for each object for matching the object--> + <#list allObjects as object> + <#if object.isListObject()> + if(nextNode.equals("${object.getObjectName()}_$List")){ + // this is a list object + if(isBacktrackingNegative){ + isBacktracking = true; + isBacktrackingNegative = false; + clear${structure.getObjectName()}NegativeObjects(); + } + // Start ListMatching and test if match was found + if(!doPatternMatching_${object.getObjectName()}(isBacktracking)){ + //if no object is found, test if backtracking stack is empty + if(backtracking.isEmpty()){ + //no match of the pattern can be found + foundmatch = false; + break; + }else{ + // start backtracking + isBacktracking = true; + //put object back on stack + searchPlan.push(nextNode); + //put the first object of the backtracking stack + searchPlan.push(backtracking.pop()); + } + } else { + // Else stop backtracking + isBacktracking = false; + //put object on backtracking stack + backtracking.push(nextNode); + // update candidates for next object to match + if (!searchPlan.isEmpty()) { + findActualCandidates(searchPlan.peek()); + } + } + + <#elseif object.isOptObject()> + if(nextNode.equals("${object.getObjectName()}")) { + // this is an optional object + if(doPatternMatching_${object.getObjectName()}(isBacktrackingNegative)) { + + if(isBacktrackingNegative){ + isBacktracking = true; + isBacktrackingNegative = false; + clear${structure.getObjectName()}NegativeObjects(); + // put object back on stack + searchPlan.push(nextNode); + // put the first object of the backtracking stack + searchPlan.push(backtracking.pop()); + }else{ + isBacktracking = false; + backtracking.push(nextNode); + } + + // update candidates for next object to match + if (!searchPlan.isEmpty()) { + findActualCandidates(searchPlan.peek()); + } + } + else { + // the pattern matching of an optional structure will always return true + // (even if no match was found), except in the case that we're + // backtracking because of negative nodes and have no more + // candidates to match + + // if no object is found, test if backtracking stack is empty + if (backtracking.isEmpty()) { + // no match of the pattern can be found + foundmatch = false; + break; + } + else { + // start backtracking + isBacktracking = true; + // put object back on stack + searchPlan.push(nextNode); + // put the first object of the backtracking stack + searchPlan.push(backtracking.pop()); + } + } + + <#elseif object.isNotObject()> + if(nextNode.equals("${object.getObjectName()}")){ + // this is a negative object + // reset candidates list + if(!isBacktracking){ + if (!isBacktrackingNegative) { + ${object.getObjectName()}_candidates_temp = new ArrayList<>(${object.getObjectName()}_candidates); + } + //try to find a match + ${object.getObjectName()}_cand = match_${object.getObjectName()}(); + //test if match does not exist + if(${object.getObjectName()}_cand == null){ + //if no object ist found, test if backtracking stack is empty + if(backtrackingNegative.isEmpty()){ + //no match of negative elements can be found go on with lists + foundmatch = true; + isBacktrackingNegative = false; + backtracking.push(nextNode); + while (!searchPlan.isEmpty() && !searchPlan.peek().endsWith("_$List")) { + backtracking.push(searchPlan.pop()); + } + }else{ + // start backtracking + isBacktrackingNegative = true; + //put object back on stack + searchPlan.push(nextNode); + //put the first object of the backtracking stack + searchPlan.push(backtrackingNegative.pop()); + //reset candidates list + ${object.getObjectName()}_candidates_temp = new ArrayList<>(${object.getObjectName()}_candidates); + } + }else{ + + //update candidates for next object to match + if(!searchPlan.isEmpty()){ + //put object on backtracking stack + backtrackingNegative.push(nextNode); + //set backtracking back to false + isBacktrackingNegative = false; + findActualCandidates(searchPlan.peek()); + } else { + // start backtracking + isBacktrackingNegative = true; + //put object back on stack + searchPlan.push(nextNode); + while(!backtrackingNegative.empty()){ + searchPlan.push(backtrackingNegative.pop()); + } + if(!backtracking.isEmpty()){ + searchPlan.push(backtracking.pop()); + } + //reset candidates list + ${object.getObjectName()}_candidates_temp = new ArrayList<>(${object.getObjectName()}_candidates); + + } + } + }else{ + searchPlan.push(nextNode); + searchPlan.push(backtracking.pop()); + } + <#else><#-- normal object --> + if(nextNode.equals("${object.getObjectName()}")){ + if(isBacktrackingNegative){ + isBacktracking = true; + isBacktrackingNegative = false; + clear${structure.getObjectName()}NegativeObjects(); + } + if (!isBacktracking) { + ${object.getObjectName()}_candidates_temp = new ArrayList<>(${object.getObjectName()}_candidates); + } + //try to find a match + ${object.getObjectName()}_cand = match_${object.getObjectName()}(); + //test if match was found + if(${object.getObjectName()}_cand == null){ + //if no object ist found, test if backtracking stack is empty + if(backtracking.isEmpty()){ + //no match of the pattern can be found + foundmatch = false; + break; + }else{ + // start backtracking + isBacktracking = true; + //put object back on stack + searchPlan.push(nextNode); + //put the first object of the backtracking stack + searchPlan.push(backtracking.pop()); + //reset candidates list + ${object.getObjectName()}_candidates_temp = new ArrayList<>(${object.getObjectName()}_candidates); + } + }else{ + // stop backtracking + isBacktracking = false; + //put object on backtracking stack + backtracking.push(nextNode); + //update candidates for next object to match + if(!searchPlan.isEmpty()){ + findActualCandidates(searchPlan.peek()); + } + } + + }<#if object_has_next>else + + + if(!isBacktrackingNegative){ + if(searchPlan.isEmpty()){ + if(!checkConstraints()){ + if(backtracking.isEmpty()){ + //no match of the pattern can be found + foundmatch = false; + break; + }else{ + // start backtracking + isBacktrackingNegative = true; + //put all negative elements on the searchPlan + <#list allObjects as object> + <#if object.isNotObject()> + searchPlan.push(backtracking.pop()); + + + //also put the last not-negative element on the searchPlan + searchPlan.push(backtracking.pop()); + } + } + } + } + } + //create a replacement candidate if a match was found + if(foundmatch) { + Match${structure.getObjectName()} match = new Match${structure.getObjectName()}(<@commaSeperatedNames/>); + match.backtracking = (Stack) backtracking.clone(); + <#list mandatoryObjects as o>// save context of every object and then clear it + match.${o.getObjectName()}_temp_candidates = ${o.getObjectName()}_candidates_temp; + ${o.getObjectName()}_cand = null; + + ${structure.getObjectName()}_candidates.add(match); + backtracking.clear(); + } + } + if(${structure.getObjectName()}_candidates.isEmpty()) { + return false; + } + ${structure.getObjectName()}_cand = ${structure.getObjectName()}_candidates; + return true; +} + +private void clear${structure.getObjectName()}NegativeObjects(){ + <#list mandatoryObjects as object> + <#if object.isNotObject() && hierarchyHelper.isWithinListStructure(object.getObjectName())> + ${object.getObjectName()}_cand = null; + + +} + + + diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/DoPatternMatchingOptional.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/DoPatternMatchingOptional.ftl new file mode 100644 index 0000000000..9d7e2a3e61 --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/DoPatternMatchingOptional.ftl @@ -0,0 +1,43 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +<#-- We are only interested in optional structures here --> +<#list hierarchyHelper.getOptionalMatchObjects(ast.getPattern().getLHSObjectsList()) as structure> + +protected boolean doPatternMatching_${structure.getObjectName()}(boolean isParentBacktrackingNegative) { + // indicates whether this rule is currently backtracking + // (this will skip all attempts to match lists or negative nodes) + boolean isBacktracking = false; + boolean isBacktrackingNegative = isParentBacktrackingNegative; + + Stack backtracking = new Stack(); + Stack backtrackingNegative = new Stack(); + Stack searchPlan = (Stack) searchPlan_${structure.getObjectName()}.clone(); + + <#list structure.getInnerLinkObjectNamesList() as elem> + <#if hierarchyHelper.isNoOptionalName(ast.getPattern().getLHSObjectsList(), elem)> + ${elem}_cand = null; + + + + String nextNode = null; + while(!searchPlan.isEmpty()){ + nextNode = searchPlan.pop(); + <#--creates an if statement for each object for matching the object--> + + <#-- <#list ast.getPattern().getLHSObjectsList() as object> --> + <#list hierarchyHelper.getInnerLinkObjectsLHS(ast.getPattern().getLHSObjectsList(), structure) as object> + <#if object.isListObject()> + ${tc.includeArgs("de.monticore.tf.odrules.dopatternmatching.HandleListObject", object, [true])} + <#elseif object.isOptObject()> + ${tc.includeArgs("de.monticore.tf.odrules.dopatternmatching.HandleOptObject", object, [true])} + <#elseif object.isNotObject()> + ${tc.includeArgs("de.monticore.tf.odrules.dopatternmatching.HandleNotObject", object, [true])} + <#else> + ${tc.includeArgs("de.monticore.tf.odrules.dopatternmatching.HandleNormalObject", object, [true])} + + <#if object_has_next>else + + } + + return true; +} + diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/DoReplacement.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/DoReplacement.ftl new file mode 100644 index 0000000000..33ff0dc847 --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/DoReplacement.ftl @@ -0,0 +1,28 @@ +<#-- (c) https://github.com/MontiCore/monticore --> + +public void doReplacement() { + + for(Match m:allMatches){ + + // assign values +<#list ast.getAssignmentsList() as assignment> + ${assignment} + + + // create objects +${tc.include("de.monticore.tf.odrules.doreplacement.CreateObjects")} + + // update attributes +${tc.include("de.monticore.tf.odrules.doreplacement.ChangeAttributeValues")} + + // execute do statements +${ast.getDoStatement()} + + Reporting.flush(hostGraph.get(0)); + + doReplacementExecuted = true; + + //do it only for the first match + break; + } +} diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/FindActualCandidates.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/FindActualCandidates.ftl new file mode 100644 index 0000000000..2baca17310 --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/FindActualCandidates.ftl @@ -0,0 +1,12 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +private void findActualCandidates(String peek) { +<#-- for each object create a methodcall to updates candidates--> + //update the candidates for the object with the name that is saved in peek +<#list hierarchyHelper.getMandatoryMatchObjects(ast.getPattern().getLHSObjectsList()) as object><#if !object.isListObject()> + if (peek.equals("${object.getObjectName()}")) { + ${object.getObjectName()}_candidates = find_${object.getObjectName()}_candidates(); + } <#if object_has_next>else + + + +} diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/FindCandidates.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/FindCandidates.ftl new file mode 100644 index 0000000000..813d0bd4f9 --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/FindCandidates.ftl @@ -0,0 +1,42 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +<#list hierarchyHelper.getMandatoryMatchObjects(ast.getPattern().getMatchingObjectsList()) as object> + <#if !object.isListObject()> + +private List find_${object.getObjectName()}_candidates(){ + <#--This can only be applied on non not-objects--> + <#if !object.isNotObject()> + // test if object is set fix + if (is_${object.getObjectName()}_fix) { + // if object is set return only the set candidates + return ${object.getObjectName()}_candidates; + } + + <#--tests if ODlinks are used--> + <#if (ast.getPattern().getLinkConditionsList()?size >0)> + <#--creates an if-statement for each condition for the object the method is created for--> + <#list ast.getPattern().getLinkConditionsList() as link> + <#if link.getObjectName() == object.getObjectName()> + + if(${link.getDependency().getContent()}_cand != null){ + <#-- for ODlinks of type link--> + <#if link.getLinktype() == "link"> + if(${link.getConditionString()} == null){ + return new ArrayList<>(); + } else { + return ${link.getConditionString()}; + } + } + + <#--for ODLinks of type composition--> + <#if link.getLinktype() = "composition" > + ${link.getConditionString()} + } + + + + + return ${object.getObjectName()}_candidates; + } + + + diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/FindSearchPlan.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/FindSearchPlan.ftl new file mode 100644 index 0000000000..73fd806d4c --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/FindSearchPlan.ftl @@ -0,0 +1,95 @@ +<#-- (c) https://github.com/MontiCore/monticore --> + + private Stack findSearchPlan() { + Stack searchPlan = new Stack<>(); + <#--creates a counter for each object--> + <#list ast.getPattern().getLHSObjectsList() as object> + <#if object.isNotObject() > + int count_${object.getObjectName()} = (int) (0.9 * Integer.MAX_VALUE); + <#elseif object.isOptObject() > + int count_${object.getObjectName()} = (int) (0.7 * Integer.MAX_VALUE); + <#elseif object.isListObject() > + int count_${object.getObjectName()} = (int) (0.8 * Integer.MAX_VALUE); + <#elseif dependVars?keys?seq_contains(object.getObjectName())> + int count_${object.getObjectName()} = 0; + count_${object.getObjectName()} = count_${object.getObjectName()} - ${dependVars[object.getObjectName()]}; + <#else> + int count_${object.getObjectName()} = (int) (0.1 * Integer.MAX_VALUE); + + + + <#--set candidate lists for predefined parameter values--> + <#list ast.getPattern().getLHSObjectsList() as object> + <#if !object.isOptObject() && !object.isListObject()> + <#if object.isNotObject() || object.isOptObject() || object.isListObject()> + ${object.getObjectName()}_candidates = new ArrayList<>(); + <#else> + if (!is_${object.getObjectName()}_fix) { + ${object.getObjectName()}_candidates = new ArrayList<>(); + } else { + count_${object.getObjectName()} = 1; + } + + + + + <#--creates a candidates list for each object--> + <#list ast.getPattern().getTypesList() as type> + <#if type != "de.monticore.tf.ast.IOptional" && type != "de.monticore.tf.ast.IList"> + // count occurrences of object types for the costs + if (t.containsKey("${type}")){ + for (ASTNode cand : t.getInstances("${type}")){ + <#list ast.getPattern().getLHSObjectsList() as object> + <#if object.type = type> + <#if object.isNotObject()> + if (checkConditions_${object.getObjectName()}((${type})cand)){ + ${object.getObjectName()}_candidates.add(cand); + } + <#else> + if (!is_${object.getObjectName()}_fix && checkConditions_${object.getObjectName()}((${type})cand)) { + count_${object.getObjectName()}++; + ${object.getObjectName()}_candidates.add(cand); + } + + + + } + } else { + for (ASTNode cand : t.getAll()) { + if (cand instanceof ${type}) { + <#list ast.getPattern().getLHSObjectsList() as object> + <#if object.type = type> + <#if object.isNotObject()> + if (checkConditions_${object.getObjectName()}((${type})cand)) { + ${object.getObjectName()}_candidates.add(cand); + } + <#else> + if (!is_${object.getObjectName()}_fix && checkConditions_${object.getObjectName()}((${type})cand)) { + count_${object.getObjectName()}++; + ${object.getObjectName()}_candidates.add(cand); + } + + + + } + } + } + + + + Map unsortedData = new HashMap<>(); + <#list ast.getPattern().getLHSObjectsList() as object> + unsortedData.put("${object.getObjectName()}<#if object.isListObject()>_$List", + count_${object.getObjectName()}); + + + TreeMap sortedData = new TreeMap<>(new ValueComparator(unsortedData)); + sortedData.putAll(unsortedData); + + // create searchPlan stack + for (String s : sortedData.keySet()) { + searchPlan.add(s); + } + + return searchPlan; + } diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/HostGraphElementGetters.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/HostGraphElementGetters.ftl new file mode 100644 index 0000000000..ca8dc2868e --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/HostGraphElementGetters.ftl @@ -0,0 +1,124 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +<#assign mandatoryObjects = hierarchyHelper.getMandatoryObjectsWithoutOptAndListChilds(ast.getPattern().getMatchingObjectsList())> +<#list mandatoryObjects as object> + // ${object.getObjectName()} + <#if !object.isListObject()> + <#if hierarchyHelper.isWithinOptionalStructure(object.getObjectName())> + public Optional<${object.getType()}> get_${object.getObjectName()}() { + return getMatches().get(0).${object.getObjectName()}; + } + <#else> + public ${object.getType()} get_${object.getObjectName()}() { + return getMatches().get(0).${object.getObjectName()}; + } + + + +<#list hierarchyHelper.getListObjects(ast.getPattern().getMatchingObjectsList()) as list> +// ${list.getObjectName()} +<#-- ListObjects and their childs--> + <#assign ListTree = hierarchyHelper.getListTree(list.getObjectName())> + <#if hierarchyHelper.isWithinOptionalStructure(list.getObjectName())> + public Optional> get_${list.getObjectName()}() { + <#if !hierarchyHelper.isListChild(list)> + return getMatches().get(0).${list.getObjectName()}; + <#else> + List ${list.getObjectName()} = new ArrayList(); + <#assign currentlevel = "getMatches().get(0)"> + <#list ListTree as level> + if(${currentlevel}.${level}.isPresent()) + for (Match${level} ${level}: ${currentlevel}.${level}.get()) { + <#assign currentlevel = level> + + if(${currentlevel}.${list.getObjectName()}.isPresent()) { + ${list.getObjectName()}.add(${currentlevel}.${list.getObjectName()}.get()); + } + <#list ListTree as level> + } + } + + if (!${list.getObjectName()}.isEmpty()) { + return Optional.ofNullable(${list.getObjectName()}); + } else { return Optional.ofNullable(null); } + + } + <#assign listchilds = hierarchyHelper.getListChilds(ast.getPattern().getMatchingObjectsList(), list)> + <#list listchilds as listchild> + public Optional> get_${listchild.getObjectName()}() { + List<${listchild.getType()}> ${listchild.getObjectName()} = new ArrayList<${listchild.getType()}>(); + <#assign currentlevel = "getMatches().get(0)"> + <#list hierarchyHelper.getListTree(listchild.getObjectName()) as level> + if (${currentlevel}.${level}.isPresent()) { + for (Match${level} ${level}: ${currentlevel}.${level}.get()) { + <#assign currentlevel = level> + + if(${currentlevel}.${listchild.getObjectName()}.isPresent()) { + ${listchild.getObjectName()}.add(${currentlevel}.${listchild.getObjectName()}.get()); + } + <#list hierarchyHelper.getListTree(listchild.getObjectName()) as level> + } + } + + if(!${listchild.getObjectName()}.isEmpty()) { + return Optional.ofNullable(${listchild.getObjectName()}); + } else { return Optional.ofNullable(null); } + } + + <#else> + public List get_${list.getObjectName()}() { + <#if !hierarchyHelper.isListChild(list)> + return getMatches().get(0).${list.getObjectName()}; + <#else> + ${list.getObjectName()} = new ArrayList(); + <#assign currentlevel = "getMatches().get(0)"> + <#list ListTree as level> + for (Match${level} ${level}: ${currentlevel}.${level}) { + <#assign currentlevel = level> + + ${list.getObjectName()}.add(${currentlevel}.${list.getObjectName()}); + <#list ListTree as level> + } + + return ${list.getObjectName()}; + + } + + <#assign listchilds = hierarchyHelper.getListChilds(ast.getPattern().getMatchingObjectsList(), list)> + <#list listchilds as listchild> + <#if hierarchyHelper.isWithinOptionalStructure(listchild.getObjectName())> + public List + > get_${listchild.getObjectName()}() { + List + > ${listchild.getObjectName()} = new ArrayList + >(); + <#else> + public List<${listchild.getType()}> get_${listchild.getObjectName()}() { + List<${listchild.getType()}> ${listchild.getObjectName()} = new ArrayList<${listchild.getType()}>(); + + <#assign currentlevel = "getMatches().get(0)"> + <#list hierarchyHelper.getListTree(listchild.getObjectName()) as level> + for (Match${level} ${level}: ${currentlevel}.${level}) { + <#assign currentlevel = level> + + ${listchild.getObjectName()}.add(${currentlevel}.${listchild.getObjectName()}); + <#list hierarchyHelper.getListTree(listchild.getObjectName()) as level> + } + + return ${listchild.getObjectName()}; + } + + + + +<#list ast.getVariableList() as variable> + <#if hierarchyHelper.isWithinOptionalStructure(variable.getName())> + public Optional<${variable.getType()}> get_${variable.getName()}() { + return this.${variable.getName()}; + } + <#else> + public ${variable.getType()} get_${variable.getName()}() { + return this.${variable.getName()}; + } + + + diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/MatchStates.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/MatchStates.ftl new file mode 100644 index 0000000000..4633f13212 --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/MatchStates.ftl @@ -0,0 +1,66 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +<#assign mandatoryObjects = hierarchyHelper.getMandatoryMatchObjects(ast.getPattern().getLHSObjectsList())> +<#assign listObjects = hierarchyHelper.getListObjects(ast.getPattern().getLHSObjectsList())> +<#macro commaSeperatedNames object> + <#list hierarchyHelper.getMandatoryObjectsWithoutOptAndListChilds(ast.getPattern().getLHSObjectsList()) as o> + <#if object.getObjectName() = o.getObjectName()>cand + <#else>${o.getObjectName()}_cand + + <#if o_has_next>, + + +<#macro commaSeperatedObjects object> + <#list hierarchyHelper.getMandatoryObjectsWithoutOptAndListChilds(ast.getPattern().getLHSObjectsList()) as o> + <#if !o.isNotObject()> + <#if !(object.getObjectName() = o.getObjectName())> + <#if !hierarchyHelper.isWithinOptionalStructure(o.getObjectName()) && !o.isListObject()> + ${o.getObjectName()}_cand == null || + + + + + +<#assign listChildObjects = hierarchyHelper.getListChilds(ast.getPattern().getLHSObjectsList())> +<#macro commaSeperatedListObjects object> + <#list listChildObjects as o> + <#if !o.isNotObject()><#if !(object.getObjectName() = o.getObjectName())> + <#if !hierarchyHelper.isWithinOptionalStructure(o.getObjectName()) && !o.isListObject()> + ${o.getObjectName()}_cand == null || + + + + + +<#list mandatoryObjects as object> +<#--creates a match method for each object--> + <#if !object.isListObject() > + private ${object.getType()} match_${object.getObjectName()}(){ + //test if there are candidates for the object + while(!${object.getObjectName()}_candidates_temp.isEmpty()){ + if(${object.getObjectName()}_candidates_temp.get(0) instanceof ${object.getType()}) { + ${object.getType()} cand = (${object.getType()})${object.getObjectName()}_candidates_temp.get(0); + + //test if candidate matches the conditions for this object + if(checkConditions_${object.getObjectName()}(cand) + <#list mandatoryObjects as o> + <#if object.getObjectName() != o.getObjectName() && !ast.getFoldingHash()[object.getObjectName()]?seq_contains(o.getObjectName())> + <#if !o.isListObject() && !o.isNotObject() && !hierarchyHelper.isListChild(o)> + && cand != (ASTNode) ${o.getObjectName()}_cand + + + + <#list listObjects as list> + && (${list.getObjectName()}_candidates == null || !isMatchedBy${list.getObjectName()}(cand)) + ){ + if (<@commaSeperatedObjects object/><@commaSeperatedListObjects object/>true) { + ${object.getObjectName()}_candidates_temp.remove(0); + return cand; + } + } + } + ${object.getObjectName()}_candidates_temp.remove(0); + } + return null; + } + + diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/ParameterSetters.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/ParameterSetters.ftl new file mode 100644 index 0000000000..63d7d3a322 --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/ParameterSetters.ftl @@ -0,0 +1,35 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +<#list hierarchyHelper.getMandatoryMatchObjects(ast.getPattern().getMatchingObjectsList()) as object> + <#if !object.isNotObject() && !object.isListObject() && !hierarchyHelper.isListChild(object)> + public void set_${object.getObjectName()}(${object.getType()} node) { + if (node != null) { + this.${object.getObjectName()}_candidates = new ArrayList<>(1); + ${object.getObjectName()}_candidates.add(node); + this.is_${object.getObjectName()}_fix = true; + } + } + + <#if object.isListObject() && !hierarchyHelper.isListChild(object)> + public void set_${object.getObjectName()}(List node) { + if (node != null) { + this.${object.getObjectName()}_candidates = new ArrayList(node); + this.is_${object.getObjectName()}_fix = true; + } + } + + <#if !object.isNotObject() && hierarchyHelper.isListChild(object)> + public void set_${object.getObjectName()} (List<${object.getType()}> nodes) { + if(nodes != null && !nodes.isEmpty()) { + this.${object.getObjectName()}_candidates = new ArrayList<>(nodes); + this.is_${object.getObjectName()}_fix = true; + } + } + + + +<#list ast.getVariableList() as variable> + public void set_${variable.getName()}(${variable.getType()} ${variable.getName()}) { + this.${variable.getName()}_is_fix = true; + this.${variable.getName()} = ${variable.getName()}; + } + diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/SplitSearchplan.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/SplitSearchplan.ftl new file mode 100644 index 0000000000..43f256360d --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/SplitSearchplan.ftl @@ -0,0 +1,21 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +<#assign optListObjects = hierarchyHelper.getOptListObjects(ast.getPattern().getLHSObjectsList())> + + private void splitSearchplan() { +<#list optListObjects as object> + searchPlan_${object.getObjectName()} = new Stack(); + + + // split the searchPlan + for (int i = 0; i < searchPlan.size(); i++) { + String obj = searchPlan.get(i); + +<#list optListObjects as object> + if (<#list object.getInnerLinkObjectNamesList() as elem>obj.equals("${elem}<#if hierarchyHelper.isListObject(elem)>_$List")<#if elem_has_next> || ) { + searchPlan.remove(i); + i--; + searchPlan_${object.getObjectName()}.push(obj); + } <#if object_has_next>else + + } + } diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/TransformationClass.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/TransformationClass.ftl new file mode 100644 index 0000000000..ac3a2063d1 --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/TransformationClass.ftl @@ -0,0 +1,43 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +@SuppressWarnings("unused") +public class ${ast.getClassname()} extends ODRule { + + ${tc.include("de.monticore.tf.odrules.ClassMatch")} + + ${tc.include("de.monticore.tf.odrules.ClassMatchList")} + + ${tc.include("de.monticore.tf.odrules.ConstantCode")} + + ${tc.include("de.monticore.tf.odrules.ParameterSetters")} + + ${tc.include("de.monticore.tf.odrules.HostGraphElementGetters")} + + ${tc.include("de.monticore.tf.odrules.Constructor")} + + ${tc.include("de.monticore.tf.odrules.DoReplacement")} + + ${tc.include("de.monticore.tf.odrules.UndoReplacement")} + + ${tc.include("de.monticore.tf.odrules.DoPatternMatching")} + + ${tc.include("de.monticore.tf.odrules.DoPatternMatchingOptional")} + + ${tc.include("de.monticore.tf.odrules.DoPatternMatchingList")} + + ${tc.include("de.monticore.tf.odrules.CheckConstraints")} + + ${tc.include("de.monticore.tf.odrules.CheckSubConstraints")} + + ${tc.include("de.monticore.tf.odrules.MatchStates")} + + ${tc.include("de.monticore.tf.odrules.CheckConditions")} + + ${tc.include("de.monticore.tf.odrules.FindSearchPlan")} + + ${tc.include("de.monticore.tf.odrules.SplitSearchplan")} + + ${tc.include("de.monticore.tf.odrules.FindCandidates")} + + ${tc.include("de.monticore.tf.odrules.FindActualCandidates")} + +} diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/TransformationUnit.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/TransformationUnit.ftl new file mode 100644 index 0000000000..541fdf61de --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/TransformationUnit.ftl @@ -0,0 +1,27 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +package ${hierarchyHelper.getPackageName()}; + +import java.util.*; +import java.util.Optional; + +import de.monticore.ast.ASTNode; +import de.monticore.tf.runtime.ODRule; +import de.monticore.tf.runtime.ValueComparator; +import de.monticore.tf.runtime.matching.ModelTraversal; +import de.monticore.tf.runtime.matching.ModelTraversalFactory; +import com.google.common.collect.Lists; +import static com.google.common.collect.Lists.*; +import static de.se_rwth.commons.StringTransformations.*; +import com.google.common.base.*; +import de.se_rwth.commons.logging.Log; +import de.monticore.generating.templateengine.reporting.Reporting; +import static de.monticore.generating.templateengine.reporting.Reporting.*; +import de.monticore.generating.templateengine.*; +import de.monticore.generating.templateengine.reporting.commons.ReportManager; +import de.monticore.generating.templateengine.reporting.commons.ReportingRepository; +import de.monticore.generating.templateengine.reporting.reporter.TransformationReporter; +import de.monticore.generating.templateengine.reporting.commons.ASTNodeIdentHelper; + +<#list hierarchyHelper.getCustomImports() as customImport> ${customImport} + +${tc.include("de.monticore.tf.odrules.TransformationClass")} diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/UndoReplacement.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/UndoReplacement.ftl new file mode 100644 index 0000000000..a151dc1ea3 --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/UndoReplacement.ftl @@ -0,0 +1,29 @@ +<#-- (c) https://github.com/MontiCore/monticore --> + +public void undoReplacement() { + + if(!doReplacementExecuted) { + Log.warn("undoReplacement was called before doReplacement was executed. This may lead to unspecified behaviour."); + } + + for(Match m:allMatches){ + + // assign values +<#list ast.getAssignmentsList() as assignment> + ${assignment} + + + // undo update attributes +${tc.include("de.monticore.tf.odrules.undoreplacement.ChangeAttributeValues")} + + // execute do statements +${ast.getUndoStatement()} + + Reporting.flush(hostGraph.get(0)); + + doReplacementExecuted = false; + + //undo only for the first match + break; + } +} diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/constantcode/HandleChangeObject.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/constantcode/HandleChangeObject.ftl new file mode 100644 index 0000000000..3cf604f052 --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/constantcode/HandleChangeObject.ftl @@ -0,0 +1,34 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +<#assign changeObject = ast> +<#if !changeObject.attributeIterated> + // not iterated + <#if hierarchyHelper.isLhsListChild(changeObject.getObjectName())> + private java.util.Map< + <#if changeObject.isPresentObjectType()>${changeObject.getObjectType()} + <#else>ASTNode + , + <#if changeObject.isPresentBoxingType()>${changeObject.getBoxingType()} + <#else>${changeObject.getType()} + > _${changeObject.getObjectName()}_${changeObject.getAttributeName()}__before; + <#else> + private ${changeObject.getType()} _${changeObject.getObjectName()}_${changeObject.getAttributeName()}__before; + + <#if changeObject.composite && changeObject.isPresentValue()&& !changeObject.isCopy()> + // composite, value is present + private ASTNode _${changeObject.getObjectName()}_${changeObject.getAttributeName()}__before_parent; + private ASTNode _${changeObject.getValue()}__before; + private ASTNode _${changeObject.getValue()}__before_parent; + private int _${changeObject.getValue()}__before_in_List = -1; + +<#elseif changeObject.isPresentValue()&& !changeObject.isCopy()> + // iterated, value is present + <#if hierarchyHelper.isLhsListChild(changeObject.getValue())> + private java.util.List<${changeObject.getType()}> _${changeObject.getValue()}__before; + private java.util.Map<${changeObject.getType()}, ASTNode> _${changeObject.getValue()}__before_parent; + private java.util.Map<${changeObject.getType()}, Integer> _${changeObject.getValue()}__before_in_List; + <#else> + private ${changeObject.getType()} _${changeObject.getValue()}__before; + private ASTNode _${changeObject.getValue()}__before_parent; + private int _${changeObject.getValue()}__before_in_List = -1; + + diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/constantcode/HandleDeleteObject.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/constantcode/HandleDeleteObject.ftl new file mode 100644 index 0000000000..0991afd21f --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/constantcode/HandleDeleteObject.ftl @@ -0,0 +1,21 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +<#assign deleteObject = ast> +<#if hierarchyHelper.isLhsListChild(deleteObject.getName())> + private java.util.List<${deleteObject.getType()}> _${deleteObject.getName()}__before; + private java.util.Map<${deleteObject.getType()}, ASTNode> _${deleteObject.getName()}__before_parent; + private java.util.Map<${deleteObject.getType()},Integer> _${deleteObject.getName()}__before_in_List; +<#else> + private ${deleteObject.getType()} _${deleteObject.getName()}__before; + private ASTNode _${deleteObject.getName()}__before_parent; + private int _${deleteObject.getName()}__before_in_List = -1; + + +<#list deleteObject.possibleParents?keys as possibleParent> + <#list deleteObject.possibleParents[possibleParent] as possibleAttribute> + <#if !deleteObject.isList()> + private boolean _${deleteObject.getName()}__before_in_${possibleParent}_${possibleAttribute} = false; + <#else> + private java.util.Map<${deleteObject.getType()}, Boolean> _${deleteObject.getName()}__before_in_${possibleParent}_${possibleAttribute}; + + + diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/constantcode/HandleMatchObject.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/constantcode/HandleMatchObject.ftl new file mode 100644 index 0000000000..01ac193dfb --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/constantcode/HandleMatchObject.ftl @@ -0,0 +1,12 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +<#assign matchObject = ast> +<#if matchObject.isListObject()> + private ${matchObject.getListtype()} ${matchObject.getObjectName()}_candidates, ${matchObject.getObjectName()}_candidates_temp; + private ${matchObject.getListtype()} ${matchObject.getObjectName()}_cand; +<#else> + private List ${matchObject.getObjectName()}_candidates, ${matchObject.getObjectName()}_candidates_temp; + private ${matchObject.getType()} ${matchObject.getObjectName()}_cand; + +<#if !matchObject.isNotObject()> + private boolean is_${matchObject.getObjectName()}_fix = false; + diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/dopatternmatching/HandleListObject.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/dopatternmatching/HandleListObject.ftl new file mode 100644 index 0000000000..40c156ec11 --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/dopatternmatching/HandleListObject.ftl @@ -0,0 +1,45 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${signature("isOptional")} + +<#assign listObject = ast> +if (nextNode.equals("${listObject.getObjectName()}_$List")) { + // this is a list object + if (isBacktrackingNegative) { + isBacktracking = true; + isBacktrackingNegative = false; + clearNegativeObjects(); + } + + // Start ListMatching and test if match was found + if (!doPatternMatching_${listObject.getObjectName()}(isBacktracking)) { + // if no object is found, test if backtracking stack is empty + if (backtracking.isEmpty()) { + // no match of the pattern can be found + <#if isOptional> + if (isParentBacktrackingNegative) { + //Can not find a new Match, signal the parent to backtrack + return false; + } + <#else> + foundMatch = false; + + break; + } else { + // start backtracking + isBacktracking = true; + // put object back on stack + searchPlan.push(nextNode); + // put the first object of the backtracking stack + searchPlan.push(backtracking.pop()); + } + } else { + // Else stop backtracking + isBacktracking = false; + // put object on backtracking stack + backtracking.push(nextNode); + // update candidates for next object to match + if (!searchPlan.isEmpty()) { + findActualCandidates(searchPlan.peek()); + } + } +} diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/dopatternmatching/HandleNormalObject.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/dopatternmatching/HandleNormalObject.ftl new file mode 100644 index 0000000000..8c881f21b0 --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/dopatternmatching/HandleNormalObject.ftl @@ -0,0 +1,51 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${signature("isOptional")} + +<#assign normalObject = ast> +if (nextNode.equals("${normalObject.getObjectName()}")) { + if(isBacktrackingNegative) { + isBacktracking = true; + isBacktrackingNegative = false; + clearNegativeObjects(); + } + if (!isBacktracking) { + ${normalObject.getObjectName()}_candidates_temp = new ArrayList<>(${normalObject.getObjectName()}_candidates); + } + <#if isOptional> + // exit condition for optional structures + else if(${normalObject.getObjectName()}_candidates_temp.isEmpty() && isParentBacktrackingNegative) { + return false; + } + + // try to find a match + ${normalObject.getObjectName()}_cand = match_${normalObject.getObjectName()}(); + // test if match was found + if(${normalObject.getObjectName()}_cand == null) { + // if no object ist found, test if backtracking stack is empty + if(backtracking.isEmpty()) { + // no match of the pattern can be found + <#if !isOptional> + foundMatch = false; + + break; + } else { + // start backtracking + isBacktracking = true; + // put object back on stack + searchPlan.push(nextNode); + // put the first object of the backtracking stack + searchPlan.push(backtracking.pop()); + // reset candidates list + ${normalObject.getObjectName()}_candidates_temp = new ArrayList<>(${normalObject.getObjectName()}_candidates); + } + } else { + // stop backtracking + isBacktracking = false; + // put object on backtracking stack + backtracking.push(nextNode); + // update candidates for next object to match + if(!searchPlan.isEmpty()){ + findActualCandidates(searchPlan.peek()); + } + } +} diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/dopatternmatching/HandleNotObject.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/dopatternmatching/HandleNotObject.ftl new file mode 100644 index 0000000000..2a4455392c --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/dopatternmatching/HandleNotObject.ftl @@ -0,0 +1,63 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${signature("isOptional")} + +<#assign notObject = ast> +if (nextNode.equals("${notObject.getObjectName()}")) { + // this is a negative object, reset candidates list + if (!isBacktracking) { + if (!isBacktrackingNegative) { + ${notObject.getObjectName()}_candidates_temp = new ArrayList<>(${notObject.getObjectName()}_candidates); + } + // try to find a match + ${notObject.getObjectName()}_cand = match_${notObject.getObjectName()}(); + // test if match does not exist + if (${notObject.getObjectName()}_cand == null) { + // if no object ist found, test if backtracking stack is empty + if (backtrackingNegative.isEmpty()) { + // no match of negative elements can be found go on with lists + <#if !isOptional> + foundMatch = true; + + isBacktrackingNegative = false; + backtracking.push(nextNode); + while (!searchPlan.isEmpty() && !searchPlan.peek().endsWith("_$List")) { + backtracking.push(searchPlan.pop()); + } + } else { + // start backtracking + isBacktrackingNegative = true; + // put object back on stack + searchPlan.push(nextNode); + // put the first object of the backtracking stack + searchPlan.push(backtrackingNegative.pop()); + // reset candidates list + ${notObject.getObjectName()}_candidates_temp = new ArrayList<>(${notObject.getObjectName()}_candidates); + } + } else { + // update candidates for next object to match + if(!searchPlan.isEmpty()) { + // put object on backtracking stack + backtrackingNegative.push(nextNode); + // set backtracking back to false + isBacktrackingNegative = false; + findActualCandidates(searchPlan.peek()); + } else { + // start backtracking + isBacktrackingNegative = true; + // put object back on stack + searchPlan.push(nextNode); + while(!backtrackingNegative.empty()) { + searchPlan.push(backtrackingNegative.pop()); + } + if(!backtracking.isEmpty()) { + searchPlan.push(backtracking.pop()); + } + // reset candidates list + ${notObject.getObjectName()}_candidates_temp = new ArrayList<>(${notObject.getObjectName()}_candidates); + } + } + } else { + searchPlan.push(nextNode); + searchPlan.push(backtracking.pop()); + } +} diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/dopatternmatching/HandleOptObject.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/dopatternmatching/HandleOptObject.ftl new file mode 100644 index 0000000000..d42f782530 --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/dopatternmatching/HandleOptObject.ftl @@ -0,0 +1,46 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${signature("isOptional")} + +<#assign optObject = ast> +if (nextNode.equals("${optObject.getObjectName()}")) { + // this is an optional object + if (doPatternMatching_${optObject.getObjectName()}(isBacktrackingNegative)) { + // Experimental + if (isBacktrackingNegative) { + isBacktracking = true; + isBacktrackingNegative = false; + clearNegativeObjects(); + // put object back on stack + searchPlan.push(nextNode); + // put the first object of the backtracking stack + searchPlan.push(backtracking.pop()); + } else { + isBacktracking = false; + backtracking.push(nextNode); + } + + // update candidates for next object to match + if (!searchPlan.isEmpty()) { + findActualCandidates(searchPlan.peek()); + } + } else { + // the pattern matching of an optional structure will always return true + // (even if no match was found), except in the case that we're + // backtracking because of negative nodes and have no more candidates to match + // if no object is found, test if backtracking stack is empty + if (backtracking.isEmpty()) { + // no match of the pattern can be found + <#if !isOptional> + foundMatch = false; + + break; + } else { + // start backtracking + isBacktracking = true; + // put object back on stack + searchPlan.push(nextNode); + // put the first object of the backtracking stack + searchPlan.push(backtracking.pop()); + } + } +} diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/doreplacement/ChangeAttributeValueList.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/doreplacement/ChangeAttributeValueList.ftl new file mode 100644 index 0000000000..23b3fee861 --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/doreplacement/ChangeAttributeValueList.ftl @@ -0,0 +1,85 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +// in a list is changed + +<#if ast.attributeIterated && !ast.isPresentValue()> + // attribute is a list + // no value is given -> deletion + <#if ast.oldValueWithinOpt> + for (Optional<${ast.getType()}> d : ${ast.getOldValueGetter()}) { + if(d.isPresent()) { + m.${ast.getObjectName()}_${ast.getOldValue()}_before.put(d.get(), ${ast.getObjectGetter()}.get(${ast.getOldValueGetter()}.indexOf(d)).${ast.getGetter()}().indexOf(d.get())); + ${ast.getObjectGetter()}.get(${ast.getOldValueGetter()}.indexOf(d)).${ast.getGetter()}().remove(d.get()); + } + } + <#else> + for (${ast.getType()} d : ${ast.getOldValueGetter()}) { + m.${ast.getObjectName()}_${ast.getOldValue()}_before.put(d, ${ast.getObjectGetter()}.get(${ast.getOldValueGetter()}.indexOf(d)).${ast.getGetter()}().indexOf(d)); + ${ast.getObjectGetter()}.get(${ast.getOldValueGetter()}.indexOf(d)).${ast.getGetter()}().remove(d); + } + +<#elseif ast.attributeIterated && !ast.isValueWithinList() > + // in a list is changed + // attribute is a list + // Left side in a List but right side is not + for(int i = 0; i < ${ast.getObjectGetter()}.size(); i++){ + ${ast.getType()} d_copy = ${ast.getValueGetter()}.deepClone(); + ${ast.getObjectGetter()}.get(i).${ast.getSetter()}(d_copy); + m.${ast.getObjectName()}_${ast.getValue()}_before.put(d_copy, ${ast.getObjectGetter()}.get(i).${ast.getGetter()}().indexOf(d_copy)); + } +<#elseif ast.attributeIterated && !ast.copy> + // attribute is a list + // a value is given -> change to new objects + for (${ast.getType()} d : ${ast.getValueGetter()}) { + ${ast.getObjectGetter()}.get(${ast.getValueGetter()}.indexOf(d)).${ast.getSetter()}(d); + m.${ast.getObjectName()}_${ast.getValue()}_before.put(d, ${ast.getObjectGetter()}.get(${ast.getValueGetter()}.indexOf(d)).${ast.getGetter()}().indexOf(d)); + } +<#elseif ast.attributeIterated && ast.copy> + // attribute is a list + // copy attribute + for (${ast.getType()} d : ${ast.getValueGetter()}) { + ${ast.getType()} d_copy = d.deepClone(); + ${ast.getObjectGetter()}.get(${ast.getValueGetter()}.indexOf(d)).${ast.getSetter()}(d_copy); + m.${ast.getObjectName()}_${ast.getValue()}_before.put(d_copy, ${ast.getObjectGetter()}.get(${ast.getValueGetter()}.indexOf(d)).${ast.getGetter()}().indexOf(d_copy)); + } +<#elseif !ast.attributeIterated && !ast.isPresentValue()> + // single attribute (no list) + // no value was given -> deletion + for (${ast.getType()} d : ${ast.getOldValueGetter()}) { + ${ast.getObjectType()} ${ast.getObjectName()} = ${ast.getObjectGetter()}.get(${ast.getOldValueGetter()}.indexOf(d)); + <#if ast.attributeOptional>if (${ast.getObjectName()}.${ast.getGetIsPresent()}) { + m.${ast.getObjectName()}_${ast.getOldValue()}_before.put(${ast.getObjectName()}, ${ast.getObjectName()}.${ast.getGetter()}()); + <#if ast.attributeOptional>} + ${ast.getObjectName()}.${ast.getSetter()}Absent(); + + } + + +<#elseif !ast.attributeIterated && !ast.isValueWithinList()> + // single attribute (not in a list) + // Not possible, the right side hast to be in a list when the left side is + for(int i = 0; i < ${ast.getObjectGetter()}.size(); i++){ + <#if ast.attributeOptional>if (${ast.getObjectGetter()}.get(i).${ast.getGetIsPresent()}) + m.${ast.getObjectName()}_${ast.getValue()}_before.put(${ast.getObjectGetter()}.get(i), ${ast.getObjectGetter()}.get(i).${ast.getGetter()}()); + ${ast.getObjectGetter()}.get(i).${ast.getSetter()}(${ast.getValueGetter()}); + } +<#elseif !ast.attributeIterated && !ast.copy> + // single attribute (not in a list) + // a value is given -> change from to new objects + for (${ast.getType()} d : ${ast.getValueGetter()}) { + ${ast.getObjectType()} ${ast.getObjectName()} = ${ast.getObjectGetter()}.get(${ast.getValueGetter()}.indexOf(d)); + <#if ast.attributeOptional>if (${ast.getObjectName()}.${ast.getGetIsPresent()}) { + m.${ast.getObjectName()}_${ast.getValue()}_before.put(${ast.getObjectName()}, ${ast.getObjectName()}.${ast.getGetter()}()); + <#if ast.attributeOptional>} + ${ast.getObjectName()}.${ast.getSetter()}(d); + } +<#elseif !ast.attributeIterated && ast.copy> + // single attribute (not in a list) + // Make a copy + for (${ast.getType()} d : ${ast.getValueGetter()}) { + ${ast.getObjectType()} ${ast.getObjectName()} = ${ast.getObjectGetter()}.get(${ast.getValueGetter()}.indexOf(d)); + <#if ast.attributeOptional>if (${ast.getObjectName()}.${ast.getGetIsPresent()}) { + m.${ast.getObjectName()}_${ast.getValue()}_before.put(${ast.getObjectName()}, ${ast.getObjectName()}.${ast.getGetter()}()); + <#if ast.attributeOptional>} + ${ast.getObjectName()}.${ast.getSetter()}(d.deepClone()); + } + diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/doreplacement/ChangeAttributeValueSingleCompositeInList.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/doreplacement/ChangeAttributeValueSingleCompositeInList.ftl new file mode 100644 index 0000000000..b690710597 --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/doreplacement/ChangeAttributeValueSingleCompositeInList.ftl @@ -0,0 +1,56 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${signature("ruleClassName")} + +// Composition points on an object in a list +<#if ast.attributeIterated && !ast.isPresentValue()> + // attribute is a list + // no value is given -> deletion + <#if ast.isOldValueWithinOpt()>if(${ast.getOldValueGetter()?replace(".get()",".isPresent()")}) + for (${ast.getType()} d : ${ast.getOldValueGetter()}) { + m.${ast.getObjectName()}_${ast.getOldValue()}_before.put(d, ${ast.getObjectGetter()}.${ast.getGetter()}().indexOf(d)); + ${ast.getObjectGetter()}.${ast.getGetter()}().remove(d); + } +<#elseif ast.attributeIterated && !ast.copy> + // attribute is a list + // a value is given -> change to new objects + <#if ast.isValueWithinOpt()>if(${ast.getValueGetter()?replace(".get()",".isPresent()")}) + for (${ast.getType()} d : ${ast.getValueGetter()}) { + ${ast.getObjectGetter()}.${ast.getSetter()}(d); + m.${ast.getObjectName()}_${ast.getValue()}_before.put(d, ${ast.getObjectGetter()}.${ast.getGetter()}().indexOf(d)); + } +<#elseif ast.attributeIterated && ast.copy> + // attribute is a list + // Make a copy + <#if ast.isValueWithinOpt()>if(${ast.getValueGetter()?replace(".get()",".isPresent()")}) + for (${ast.getType()} d : ${ast.getValueGetter()}) { + ${ast.getType()} d_copy = d.deepClone(); + ${ast.getObjectGetter()}.${ast.getSetter()}(d_copy); + m.${ast.getObjectName()}_${ast.getValue()}_before.put(d_copy, ${ast.getObjectGetter()}.${ast.getGetter()}().indexOf(d_copy)); + } +<#elseif !ast.attributeIterated && !ast.isPresentValue()> + // single attribute (no list) + // no value is given -> deletion + <#if ast.isOldValueWithinOpt()>if(${ast.getOldValueGetter()?replace(".get()",".isPresent()")}) + for (${ast.getType()} d : ${ast.getOldValueGetter()}) { + m.${ast.getObjectName()}_${ast.getOldValue()}_before = d; + ${ast.getObjectGetter()}.${ast.getSetter()}Absent(); + } +<#elseif !ast.attributeIterated && !ast.copy> + // single attribute (no list) + // a different value was given, thus, change the object + <#if ast.isValueWithinOpt()>if(${ast.getValueGetter()?replace(".get()",".isPresent()")}) + for (${ast.getType()} d : ${ast.getValueGetter()}) { + <#if ast.attributeOptional>if(${ast.getObjectGetter()}.${ast.getGetIsPresent()}) + m.${ast.getObjectName()}_${ast.getValue()}_before = ${ast.getObjectGetter()}.${ast.getGetter()}(); + ${ast.getObjectGetter()}.${ast.getSetter()}(d); + } +<#elseif !ast.attributeIterated && ast.copy> + // single attribute (no list) + // Make a copy + <#if ast.isValueWithinOpt()>if(${ast.getValueGetter()?replace(".get()",".isPresent()")}) + for (${ast.getType()} d : ${ast.getValueGetter()}) { + <#if ast.attributeOptional>if(${ast.getObjectGetter()}.${ast.getGetIsPresent()}) + m.${ast.getObjectName()}_${ast.getValue()}_before = ${ast.getObjectGetter()}.${ast.getGetter()}(); + ${ast.getObjectGetter()}.${ast.getSetter()}(d.deepClone()); + } + diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/doreplacement/ChangeAttributeValueSingleCompositeNotInList.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/doreplacement/ChangeAttributeValueSingleCompositeNotInList.ftl new file mode 100644 index 0000000000..b5f16515ab --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/doreplacement/ChangeAttributeValueSingleCompositeNotInList.ftl @@ -0,0 +1,88 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${signature("ruleClassName")} + + +// composition points on an object not in a list +Reporting.reportTransformationObjectChange("${ruleClassName}",${ast.getObjectGetter()}, "${ast.getAttributeName()}"); + +<#if ast.attributeIterated && !ast.isPresentValue()> + // attribute is a list + // no value is given -> deletion + <#if ast.isOldValueWithinOpt()>if(m.${ast.getOldValue()}.isPresent()) { + + if (${ast.getOldValueGetter()} != null) { + Reporting.reportTransformationOldValue("${ruleClassName}",${ast.getOldValueGetter()}); + } + m.${ast.getObjectName()}_${ast.getOldValue()}_before_pos = ${ast.getObjectGetter()}.${ast.getGetter()}().indexOf(${ast.getOldValueGetter()}); + ${ast.getObjectGetter()}.${ast.getUnsetter()}(${ast.getOldValueGetter()}); + + <#if ast.isOldValueWithinOpt()>} + +<#elseif ast.attributeIterated && !ast.copy > + // attribute is a list + <#if ast.isValueWithinOpt()>if(m.${ast.getValue()}.isPresent()) { + // a value is given -> add new object + + <#if ast.isPresentInsertPosition()>pos = ${ast.getInsertPosition()}; + ${ast.getObjectGetter()}.${ast.getSetter()}( + <#if ast.isPresentInsertPosition()>pos, + ${ast.getValueGetter()} + ); + + <#if ast.isValueWithinOpt()>} + +<#elseif ast.attributeIterated && ast.copy > + // attribute is a list + // attribute needs to be copied + <#if ast.isValueWithinOpt()>if(m.${ast.getValue()}.isPresent()) { + + ${ast.getType()} d = ${ast.getValueGetter()}.deepClone(); + <#if ast.isPresentInsertPosition()> + pos = ${ast.getInsertPosition()}; + m.${ast.getObjectName()}_${ast.getValue()}_before.put( d, pos); + <#else> + m.${ast.getObjectName()}_${ast.getValue()}_before.put( d, ${ast.getObjectGetter()}.${ast.getGetter()}().size()); + + + ${ast.getObjectGetter()}.${ast.getSetter()}( + <#if ast.isPresentInsertPosition()>pos, + d + ); + + <#if ast.isValueWithinOpt()>} + +<#elseif !ast.attributeIterated && !ast.isPresentValue()> + // single attribute (no list) + // no value should be set, thus, it is a deletion. + <#if ast.isOldValueWithinOpt()>if(m.${ast.getOldValue()}.isPresent()) { + + // deletion of a list or single object + // setting null here results in an Optional.empty() + <#if ast.isAttributeOptional()> + m.${ast.getObjectName()}_${ast.getOldValue()}_before = ${ast.getObjectGetter()}.${ast.getGetter()}(); + ${ast.getObjectGetter()}.${ast.getSetter()}(null); + + + <#if ast.isOldValueWithinOpt()>} + +<#elseif !ast.attributeIterated && ast.isPresentValue()> + // single attribute (no list) + // a different value was given, thus, change the object + <#if ast.isValueWithinOpt()>if (m.${ast.getValue()}.isPresent()) { + + <#if ast.isAttributeOptional()> + if(${ast.getObjectGetter()}.${ast.getGetIsPresent()}){ + + + m.${ast.getObjectName()}_${ast.getValue()}_before = ${ast.getObjectGetter()}.${ast.getGetter()}(); + if(${ast.getObjectGetter()}.${ast.getGetter()}() != null) { + Reporting.reportTransformationOldValue("${ruleClassName}",${ast.getObjectGetter()}.${ast.getGetter()}()); + } + + <#if ast.isAttributeOptional()>} + + ${ast.getObjectGetter()}.${ast.getSetter()}(${ast.getValueGetter()}<#if ast.copy>.deepClone()); + + <#if ast.isValueWithinOpt()>} + + diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/doreplacement/ChangeAttributeValueSinglePrimitive.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/doreplacement/ChangeAttributeValueSinglePrimitive.ftl new file mode 100644 index 0000000000..7985e0dd34 --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/doreplacement/ChangeAttributeValueSinglePrimitive.ftl @@ -0,0 +1,36 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${signature("ruleClassName")} + +// primitive type, String or String list +<#if ast.isObjectWithinList()> + // in a list is changed + for (${ast.getObjectType()} d : ${ast.getObjectGetter()}) { + m.${ast.getObjectName()}_${ast.getAttributeName()}_before.put(d, d.${ast.getGetter()}()); + d.${ast.getSetter()}(${ast.getValue()}); + } +<#else> + // single attribute (not in a list) + Reporting.reportTransformationObjectChange("${ruleClassName}",${ast.getObjectGetter()}, "${ast.getAttributeName()}"); + + <#if ast.isPresentOldValue()> + Reporting.reportTransformationOldValue("${ruleClassName}",<#if ast.composite>m.${ast.getOldValueGetter()?keep_after("m.")}<#if ast.isValueStringList()>.toString()); + + + <#if ast.isAttributeOptional()> + if (${ast.getObjectGetter()}.isPresent${ast.getAttributeName()?cap_first}()){ + + + <#if !ast.isPrimitiveType()> + if(${ast.getObjectGetter()}.${ast.getGetter()}() != null) { + Reporting.reportTransformationOldValue("${ruleClassName}",${ast.getObjectGetter()}.${ast.getGetter()}()<#if ast.isValueStringList()>.toString()); + } + <#else> + Reporting.reportTransformationOldValue("${ruleClassName}",""+${ast.getObjectGetter()}.${ast.getGetter()}()<#if ast.isValueStringList()>.toString()); + + + <#if ast.isAttributeOptional()> + } + + m.${ast.getObjectName()}_${ast.getAttributeName()}_before = ${ast.getObjectGetter()}.${ast.getGetter()}(); + ${ast.getObjectGetter()}.${ast.getSetter()}(${ast.getValue()}); + diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/doreplacement/ChangeAttributeValues.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/doreplacement/ChangeAttributeValues.ftl new file mode 100644 index 0000000000..a719269f2d --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/doreplacement/ChangeAttributeValues.ftl @@ -0,0 +1,44 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +<#assign mandatoryChanges = hierarchyHelper.getChangeObjectsWhithoutCreate(ast.getReplacement())> +// apply changes +int pos; +<#list mandatoryChanges as change> + + <#if change.isObjectWithinOpt()>if(m.${change.getObjectName()}.isPresent()) { + + + <#if change.isPrimitiveType() || change.isValueStringList() > + ${tc.includeArgs("de.monticore.tf.odrules.doreplacement.ChangeAttributeValueSinglePrimitive", change, [ast.getClassname()])} + <#elseif change.composite && change.isObjectWithinList() > + ${tc.include("de.monticore.tf.odrules.doreplacement.ChangeAttributeValueList", change)} + <#elseif change.composite && !change.isValueListObject() && !change.isOldValueWithinList() && !change.isValueWithinList()> + ${tc.includeArgs("de.monticore.tf.odrules.doreplacement.ChangeAttributeValueSingleCompositeNotInList", change, [ast.getClassname()])} + <#elseif change.composite> + ${tc.includeArgs("de.monticore.tf.odrules.doreplacement.ChangeAttributeValueSingleCompositeInList", change, [ast.getClassname()])} + + + + <#if change.isPresentValue() && !change.isObjectWithinList()> + <#if change.isValueWithinOpt()>if(m.${change.getValue()}.isPresent()) { + //TODO find a way for lists + <#if !change.isObjectWithinList()> + Reporting.reportTransformationNewValue("${ast.getClassname()}",<#if change.composite>m.${change.getValue()}<#if change.isValueWithinOpt()>.get()<#if change.isValueStringList()>.toString()); + + } + + + + <#if change.isObjectWithinOpt()> + } else { + // no new objects should be created + <#if !change.isPrimitiveType() && !change.isValueStringList() && !change.isOldValueWithinList() && !change.isValueWithinList() > + <#if change.isValueWithinOpt()>m.${change.getValue()} = Optional.empty(); + <#if change.isOldValueWithinOpt()>m.${change.getOldValue()} = Optional.empty(); + + } + + + + + + diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/doreplacement/CreateObjects.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/doreplacement/CreateObjects.ftl new file mode 100644 index 0000000000..e314350bb8 --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/doreplacement/CreateObjects.ftl @@ -0,0 +1,63 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +// get all required LHS values +<#list ast.getReplacement().getRequirementsList() as requirement> + ${requirement.type} _${requirement.getObject()}_${requirement.attribute} = m.${requirement.getObject()}.${requirement.getGetter()}(); + + +<#list ast.getReplacement().getCreateObjectsList() as create> +<#assign isWithinOpt = hierarchyHelper.isWithinOptionalStructure(create.getName())> +<#assign isWithinList = hierarchyHelper.isWithinListStructure(create.getName())> + + + +<#if !isWithinList> +if (!is_${create.getName()}_fix) { + <#if create.getFactoryName() != "__missing"> + ${create.getType()}Builder builder = ${create.getFactoryName()}.${create.getSimpleType()?keep_after("AST")?uncap_first}Builder(); + <#list ast.getReplacement().getChangesList() as change> + <#if change.getObjectName() == create.getName()> + <#if change.isPresentValue()> + <#if change.isPrimitiveType()> + <#assign changeGetValue = change.getValue()> + <#elseif change.isValueStringList()> + <#assign changeGetValue = change.getValue()> + <#else> + <#assign changeGetValue = "m.${change.getValue()}"> + <#if hierarchyHelper.isWithinOptionalStructure(change.getObjectName()) || change.valueWithinOpt> + if(${changeGetValue}.isPresent()) + <#assign changeGetValue += ".get()"> + + + builder.${change.getSetter()}(${changeGetValue}); + <#else> + builder.${change.getSetter()}(); + + + + m.${create.getName()} = <#if isWithinOpt>Optional.of(builder.build()<#if isWithinOpt>); + <#list ast.getReplacement().getChangesList() as change> + <#if change.getObjectName() == create.getName()> + Reporting.reportTransformationObjectChange("${ast.getClassname()}",m.${create.getName()}<#if isWithinOpt>.get(), "${change.getAttributeName()}"); + + +<#else> + // TODO: There exists no builder for ${create.getType()}s - check if this is set from external + +} else { + m.${create.getName()} = <#if isWithinOpt>Optional.of((${create.getType()}) ${create.getName()}_candidates.get(0)<#if isWithinOpt>); +} +Reporting.reportTransformationObjectCreation("${ast.getClassname()}",m.${create.getName()}<#if isWithinOpt>.get()); +<#else> +<#assign listParent = hierarchyHelper.getListParent(create.getName())> +if (!is_${create.getName()}_fix) { + for (Match${listParent} list : get_${listParent}()) { + list.${create.getName()} = <#if isWithinOpt>Optional.of(${create.getFactoryName()}.create${create.getSimpleType()}()<#if isWithinOpt>); + } +} else { + for (Match${listParent} list : get_${listParent}()) { + list.${create.getName()} = <#if isWithinOpt>Optional.of((${create.getType()}) ${create.getName()}_candidates.get(get_${listParent}().indexOf(list))<#if isWithinOpt>); + } +} +//TODO find a way for list objects Reporting.reportTransformationObjectCreation("${ast.getClassname()}",get_${create.getName()}()<#if isWithinOpt>.get()); + + diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/undoreplacement/ChangeAttributeValueList.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/undoreplacement/ChangeAttributeValueList.ftl new file mode 100644 index 0000000000..43f81baa75 --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/undoreplacement/ChangeAttributeValueList.ftl @@ -0,0 +1,81 @@ +<#-- (c) https://github.com/MontiCore/monticore --> + // a composition is changed + +<#if ast.attributeIterated && !ast.isPresentValue()> + // in a list is changed + // attribute is a list + // no value is given -> undo deletion + <#if ast.oldValueWithinOpt> + for (int i = ${ast.getOldValueGetter()}.size() - 1; i >= 0; i--) { + Optional<${ast.getType()}> d = ${ast.getOldValueGetter()}.get(i); + if (d.isPresent()) { + ${ast.getObjectGetter()}.get(${ast.getOldValueGetter()}.indexOf(d)).${ast.getGetter()}().add(m.${ast.getObjectName()}_${ast.getOldValue()}_before.get(d.get()), d.get()); + } + } + <#else> + for (int i = ${ast.getOldValueGetter()}.size() - 1; i >= 0; i--) { + ${ast.getType()} d = ${ast.getOldValueGetter()}.get(i); + ${ast.getObjectGetter()}.get(${ast.getOldValueGetter()}.indexOf(d)).${ast.getGetter()}().add(m.${ast.getObjectName()}_${ast.getOldValue()}_before.get(d), d); + } + +<#elseif ast.attributeIterated && !ast.isValueWithinList() > + // in a list is changed + // attribute is a list + // Left side in a List but right side is not + for(int i = 0; i < ${ast.getObjectGetter()}.size(); i++){ + ${ast.getType()} d_copy = (${ast.getType()}) m.${ast.getObjectName()}_${ast.getValue()}_before.keySet().iterator().next(); + ${ast.getObjectGetter()}.get(i).${ast.getUnsetter()}(d_copy); + } +<#elseif ast.attributeIterated && !ast.copy> + // in a list is changed + // attribute is a list + // a value was given -> undo change + for (${ast.getType()} d : m.${ast.getObjectName()}_${ast.getValue()}_before.keySet()) { + ${ast.getObjectGetter()}.get(${ast.getValueGetter()}.indexOf(d)).${ast.getUnsetter()}(d); + } +<#elseif ast.attributeIterated && ast.copy> + // in a list is changed + // attribute is a list + // delete copied items + for (${ast.getType()} d : ${ast.getValueGetter()}) { + for (${ast.getType()} d_copy : m.${ast.getObjectName()}_${ast.getValue()}_before.keySet()) { + ${ast.getObjectGetter()}.get(${ast.getValueGetter()}.indexOf(d)).${ast.getUnsetter()}(d_copy); + } + } + + +<#elseif !ast.attributeIterated && !ast.isPresentValue()> + // single attribute (no list) + // no value was given -> undo deletion + for (${ast.getObjectType()} d : m.${ast.getObjectName()}_${ast.getOldValue()}_before.keySet()) { + d.${ast.getSetter()}(m.${ast.getObjectName()}_${ast.getOldValue()}_before.get(d)); + } +<#elseif !ast.attributeIterated && !ast.isValueWithinList()> + // single attribute (not in a list) + // Not possible, the right side hast to be in a list when the left side is + for (${ast.getObjectType()} d : m.${ast.getObjectName()}_${ast.getValue()}_before.keySet()) { + d.${ast.getSetter()}(m.${ast.getObjectName()}_${ast.getValue()}_before.get(d)); + } +<#elseif !ast.attributeIterated && !ast.copy> + // single attribute (not in a list) + // a value is given -> undo change + for (${ast.getType()} d : ${ast.getValueGetter()}) { + ${ast.getObjectType()} ${ast.getObjectName()} = ${ast.getObjectGetter()}.get(${ast.getValueGetter()}.indexOf(d)); + if (m.${ast.getObjectName()}_${ast.getValue()}_before.containsKey(${ast.getObjectName()})) { + ${ast.getObjectName()}.${ast.getSetter()}(m.${ast.getObjectName()}_${ast.getValue()}_before.get(${ast.getObjectName()})); + } else { + ${ast.getObjectName()}.${ast.getSetter()}Absent(); + } + } +<#elseif !ast.attributeIterated && ast.copy> + // single attribute (not in a list) + // undo copy + for (${ast.getType()} d : ${ast.getValueGetter()}) { + ${ast.getObjectType()} ${ast.getObjectName()} = ${ast.getObjectGetter()}.get(${ast.getValueGetter()}.indexOf(d)); + if (m.${ast.getObjectName()}_${ast.getValue()}_before.containsKey(${ast.getObjectName()})) { + ${ast.getObjectName()}.${ast.getSetter()}(m.${ast.getObjectName()}_${ast.getValue()}_before.get(${ast.getObjectName()})); + } else { + ${ast.getObjectName()}.${ast.getSetter()}Absent(); + } + } + diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/undoreplacement/ChangeAttributeValueSingleCompositeInList.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/undoreplacement/ChangeAttributeValueSingleCompositeInList.ftl new file mode 100644 index 0000000000..a1a1e918f9 --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/undoreplacement/ChangeAttributeValueSingleCompositeInList.ftl @@ -0,0 +1,52 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${signature("ruleClassName")} + +// Composition points on an object in a list +<#if ast.attributeIterated && !ast.isPresentValue()> + // attribute is a list + // restore old values + for (int i = ${ast.getOldValueGetter()}.size() - 1; i >= 0; i--) { + ${ast.getType()} d = ${ast.getOldValueGetter()}.get(i); + ${ast.getObjectGetter()}.${ast.getGetter()}().add(m.${ast.getObjectName()}_${ast.getOldValue()}_before.get(d), d); + } +<#elseif ast.attributeIterated && !ast.copy> + // attribute is a list + // a value was given -> change to old objects + for (${ast.getType()} d : m.${ast.getObjectName()}_${ast.getValue()}_before.keySet()) { + ${ast.getObjectGetter()}.${ast.getUnsetter()}(d); + } +<#elseif ast.attributeIterated && ast.copy> + // attribute is a list + // remove old copies + for (${ast.getType()} d : m.${ast.getObjectName()}_${ast.getValue()}_before.keySet()) { + ${ast.getObjectGetter()}.${ast.getUnsetter()}(d); + } +<#elseif !ast.attributeIterated && !ast.isPresentValue()> + // single attribute (no list) + // undo deletion + if (m.${ast.getObjectName()}_${ast.getOldValue()}_before != null) { + ${ast.getObjectGetter()}.${ast.getSetter()}(m.${ast.getObjectName()}_${ast.getOldValue()}_before); + } +<#elseif !ast.attributeIterated && !ast.copy> + // single attribute (no list) + // undo change + if (m.${ast.getObjectName()}_${ast.getValue()}_before != null) { + ${ast.getObjectGetter()}.${ast.getSetter()}(m.${ast.getObjectName()}_${ast.getValue()}_before); + } + <#if ast.attributeOptional> + else { + ${ast.getObjectGetter()}.${ast.getSetter()}Absent(); + } + +<#elseif !ast.attributeIterated && ast.copy> + // single attribute (no list) + // undo copy + if (m.${ast.getObjectName()}_${ast.getValue()}_before != null) { + ${ast.getObjectGetter()}.${ast.getSetter()}(m.${ast.getObjectName()}_${ast.getValue()}_before); + } + <#if ast.attributeOptional> + else { + ${ast.getObjectGetter()}.${ast.getSetter()}Absent(); + } + + diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/undoreplacement/ChangeAttributeValueSingleCompositeNotInList.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/undoreplacement/ChangeAttributeValueSingleCompositeNotInList.ftl new file mode 100644 index 0000000000..ddb1c9ce5f --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/undoreplacement/ChangeAttributeValueSingleCompositeNotInList.ftl @@ -0,0 +1,76 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${signature("ruleClassName")} + + +// composition points on an object not in a list +Reporting.reportTransformationObjectChange("${ruleClassName}",${ast.getObjectGetter()}, "${ast.getAttributeName()}"); + +<#if ast.attributeIterated && !ast.isPresentValue()> + // attribute is a list + // no value was given -> undo deletion + <#if ast.isOldValueWithinOpt()>if(m.${ast.getOldValue()}.isPresent()) { + + if (${ast.getOldValueGetter()} != null) { + Reporting.reportTransformationOldValue("${ruleClassName}",${ast.getOldValueGetter()}); + } + ${ast.getObjectGetter()}.${ast.getSetter()}( + m.${ast.getObjectName()}_${ast.getOldValue()}_before_pos, + ${ast.getOldValueGetter()}); + + <#if ast.isOldValueWithinOpt()>} + +<#elseif ast.attributeIterated && !ast.copy > + // attribute is a list + <#if ast.isValueWithinOpt()>if(m.${ast.getValue()}.isPresent()) { + // a value was given -> remove from new object + + ${ast.getObjectGetter()}.${ast.getUnsetter()}( + ${ast.getValueGetter()} + ); + + <#if ast.isValueWithinOpt()>} + +<#elseif ast.attributeIterated && ast.copy > + // attribute is a list + // value was copied, remove copy + <#if ast.isValueWithinOpt()>if(m.${ast.getValue()}.isPresent()) { + + <#if ast.isPresentInsertPosition()>pos = ${ast.getInsertPosition()}; + + ${ast.getObjectGetter()}.${ast.getUnsetter()}( + m.${ast.getObjectName()}_${ast.getValue()}_before.keySet().iterator().next() + ); + + <#if ast.isValueWithinOpt()>} + +<#elseif !ast.attributeIterated && !ast.isPresentValue()> + // single attribute (no list) + <#if ast.isOldValueWithinOpt()>if(m.${ast.getOldValue()}.isPresent()) { + + //undo deletion of a list or single object + <#if ast.isAttributeOptional()> + ${ast.getObjectGetter()}.${ast.getSetter()}(m.${ast.getObjectName()}_${ast.getOldValue()}_before); + + + <#if ast.isOldValueWithinOpt()>} + +<#elseif !ast.attributeIterated && ast.isPresentValue()> + // single attribute (no list) + // a different value was given, but change it back + <#if ast.isValueWithinOpt()>if (m.${ast.getValue()}.isPresent()) { + + <#if ast.isAttributeOptional()> + if(${ast.getObjectGetter()}.${ast.getGetIsPresent()}){ + + + if(${ast.getObjectGetter()}.${ast.getGetter()}() != null) { + Reporting.reportTransformationOldValue("${ruleClassName}",${ast.getObjectGetter()}.${ast.getGetter()}()); + } + + <#if ast.isAttributeOptional()>} + + ${ast.getObjectGetter()}.${ast.getSetter()}(m.${ast.getObjectName()}_${ast.getValue()}_before); + + <#if ast.isValueWithinOpt()>} + + diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/undoreplacement/ChangeAttributeValueSinglePrimitive.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/undoreplacement/ChangeAttributeValueSinglePrimitive.ftl new file mode 100644 index 0000000000..7dba98605b --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/undoreplacement/ChangeAttributeValueSinglePrimitive.ftl @@ -0,0 +1,35 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${signature("ruleClassName")} + +// primitive type, String or String list +<#if ast.isObjectWithinList()> + // in a list is changed + for (${ast.getObjectType()} d : ${ast.getObjectGetter()}) { + d.${ast.getSetter()}(m.${ast.getObjectName()}_${ast.getAttributeName()}_before.get(d)); + } +<#else> + // single attribute (not in a list) + Reporting.reportTransformationObjectChange("${ruleClassName}",${ast.getObjectGetter()}, "${ast.getAttributeName()}"); + + <#if ast.isPresentOldValue()> + Reporting.reportTransformationOldValue("${ruleClassName}",<#if ast.composite>m.${ast.getOldValueGetter()?keep_after("m.")}<#if ast.isValueStringList()>.toString()); + + + <#if ast.isAttributeOptional()> + if (${ast.getObjectGetter()}.isPresent${ast.getAttributeName()?cap_first}()){ + + + <#if !ast.isPrimitiveType()> + if(${ast.getObjectGetter()}.${ast.getGetter()}() != null) { + Reporting.reportTransformationOldValue("${ruleClassName}",${ast.getObjectGetter()}.${ast.getGetter()}()<#if ast.isValueStringList()>.toString()); + } + <#else> + Reporting.reportTransformationOldValue("${ruleClassName}",""+${ast.getObjectGetter()}.${ast.getGetter()}()<#if ast.isValueStringList()>.toString()); + + + <#if ast.isAttributeOptional()> + } + + + ${ast.getObjectGetter()}.${ast.getSetter()}(m.${ast.getObjectName()}_${ast.getAttributeName()}_before); + diff --git a/monticore-runtime/src/main/resources/de/monticore/tf/odrules/undoreplacement/ChangeAttributeValues.ftl b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/undoreplacement/ChangeAttributeValues.ftl new file mode 100644 index 0000000000..ce7676b8fb --- /dev/null +++ b/monticore-runtime/src/main/resources/de/monticore/tf/odrules/undoreplacement/ChangeAttributeValues.ftl @@ -0,0 +1,22 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +<#assign mandatoryChanges = hierarchyHelper.getChangeObjectsWhithoutCreate(ast.getReplacement())> +// apply changes +int pos; +<#list mandatoryChanges as change> + <#if change.isObjectWithinOpt()>if(m.${change.getObjectName()}.isPresent()) { + + <#if change.isPrimitiveType() || change.isValueStringList() > + ${tc.includeArgs("de.monticore.tf.odrules.undoreplacement.ChangeAttributeValueSinglePrimitive", change, [ast.getClassname()])} + <#elseif change.composite && change.isObjectWithinList()> + ${tc.include("de.monticore.tf.odrules.undoreplacement.ChangeAttributeValueList", change)} + <#elseif change.composite && !change.isOldValueWithinList() && !change.valueWithinList> + ${tc.includeArgs("de.monticore.tf.odrules.undoreplacement.ChangeAttributeValueSingleCompositeNotInList", change, [ast.getClassname()])} + <#elseif change.composite> + ${tc.includeArgs("de.monticore.tf.odrules.undoreplacement.ChangeAttributeValueSingleCompositeInList", change, [ast.getClassname()])} + + + <#if change.isObjectWithinOpt()>} + + + + diff --git a/monticore-runtime/src/test/java/de/monticore/ast/ASTCNodeMock.java b/monticore-runtime/src/test/java/de/monticore/ast/ASTCNodeMock.java new file mode 100644 index 0000000000..566b9136be --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/ast/ASTCNodeMock.java @@ -0,0 +1,38 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.ast; + +import com.google.common.collect.Lists; +import de.monticore.symboltable.IScope; + +import java.util.List; + +/** + * Mock for ASTCNode. + * + */ +public class ASTCNodeMock extends ASTCNode { + + public static final ASTCNode INSTANCE = new ASTCNodeMock(); + + private List children = Lists.newArrayList(); + + public void addChild(ASTNode child) { + children.add(child); + } + + + @Override public IScope getEnclosingScope() { + return null; + } + + /** + * @see de.monticore.ast.ASTCNode#deepClone() + */ + @Override + public ASTNode deepClone() { + return null; + } + + +} diff --git a/monticore-runtime/src/test/java/de/monticore/ast/ASTNodeMock.java b/monticore-runtime/src/test/java/de/monticore/ast/ASTNodeMock.java new file mode 100644 index 0000000000..ade0033fca --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/ast/ASTNodeMock.java @@ -0,0 +1,487 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.ast; + +import java.util.Optional; +import java.util.Spliterator; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; +import java.util.stream.Stream; + +import com.google.common.collect.Lists; +import de.monticore.ast.ASTNode; +import de.monticore.ast.Comment; +import de.monticore.symboltable.IScope; +import de.se_rwth.commons.SourcePosition; + +import java.util.Collection; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; + +/** + * Mock for ASTNode. DOES NOT IMPLEMENT ANY OF THE METHODS. + * + */ +public class ASTNodeMock implements ASTNode { + + public static final ASTNode INSTANCE = new ASTNodeMock(); + + /** + * @see de.monticore.ast.ASTNode#deepClone() + */ + @Override + public ASTNode deepClone() { + return null; + } + + /** + * @see de.monticore.ast.ASTNode#get_SourcePositionEnd() + */ + @Override + public SourcePosition get_SourcePositionEnd() { + return null; + } + + /** + * @see de.monticore.ast.ASTNode#set_SourcePositionEnd(de.se_rwth.commons.SourcePosition) + */ + @Override + public void set_SourcePositionEnd(SourcePosition end) { + } + + @Override + public void set_SourcePositionEndAbsent() { + + } + + @Override + public boolean isPresent_SourcePositionEnd() { + return false; + } + + /** + * @see de.monticore.ast.ASTNode#get_SourcePositionStart() + */ + @Override + public SourcePosition get_SourcePositionStart() { + return null; + } + + /** + * @see de.monticore.ast.ASTNode#set_SourcePositionStart(de.se_rwth.commons.SourcePosition) + */ + @Override + public void set_SourcePositionStart(SourcePosition start) { + } + + @Override + public void set_SourcePositionStartAbsent() { + + } + + @Override + public boolean isPresent_SourcePositionStart() { + return false; + } + + @Override public IScope getEnclosingScope() { + return null; + } + + /** + * @see de.monticore.ast.ASTNode#equalAttributes(java.lang.Object) + */ + @Override + public boolean equalAttributes(Object o) { + return false; + } + + /** + * @see de.monticore.ast.ASTNode#equalsWithComments(java.lang.Object) + */ + @Override + public boolean equalsWithComments(Object o) { + return false; + } + + /** + * @see de.monticore.ast.ASTNode#deepEquals(java.lang.Object) + */ + @Override + public boolean deepEquals(Object o) { + return false; + } + + /** + * @see de.monticore.ast.ASTNode#deepEqualsWithComments(java.lang.Object) + */ + @Override + public boolean deepEqualsWithComments(Object o) { + return false; + } + + /** + * @see de.monticore.ast.ASTNode#deepEquals(java.lang.Object, boolean) + */ + @Override + public boolean deepEquals(Object o, boolean forceSameOrder) { + return false; + } + + /** + * @see de.monticore.ast.ASTNode#deepEqualsWithComments(java.lang.Object, boolean) + */ + @Override + public boolean deepEqualsWithComments(Object o, boolean forceSameOrder) { + return false; + } + + @Override + public void clear_PreComments() { } + + @Override + public boolean add_PreComment(Comment precomment) { + return false; + } + + @Override + public boolean addAll_PreComments(Collection precomments) { + return false; + } + + @Override + public boolean contains_PreComment(Object element) { + return false; + } + + @Override + public boolean containsAll_PreComments(Collection element) { + return false; + } + + @Override + public boolean isEmpty_PreComments() { + return false; + } + + @Override + public Iterator iterator_PreComments() { + + return null; + } + + @Override + public boolean remove_PreComment(Object element) { + return false; + } + + @Override + public boolean removeAll_PreComments(Collection element) { + return false; + } + + @Override + public boolean retainAll_PreComments(Collection element) { + return false; + } + + @Override + public int size_PreComments() { + return 0; + } + + @Override + public Comment[] toArray_PreComments(Comment[] array) { + return null; + } + + @Override + public boolean removeIf_PreComment(Predicate filter) { + return false; + } + + @Override + public Spliterator spliterator_PreComments() { + return null; + } + + @Override + public Stream stream_PreComments() { + return null; + } + + @Override + public Stream parallelStream_PreComments() { + return null; + } + + @Override + public void forEach_PreComments(Consumer action) { + } + + @Override + public void add_PreComment(int index, Comment precomment) { + } + + @Override + public boolean addAll_PreComments(int index, Collection precomments) { + return false; + } + + @Override + public Comment get_PreComment(int index) { + return null; + } + + @Override + public int indexOf_PreComment(Object element) { + return 0; + } + + @Override + public int lastIndexOf_PreComment(Object element) { + return 0; + } + + @Override + public boolean equals_PreComments(Object element) { + return false; + } + + @Override + public int hashCode_PreComments() { + return 0; + } + + @Override + public ListIterator listIterator_PreComments() { + return null; + } + + @Override + public Comment remove_PreComment(int index) { + return null; + } + + @Override + public List subList_PreComments(int start, int end) { + return null; + } + + @Override + public void replaceAll_PreComments(UnaryOperator operator) { + } + + @Override + public void sort_PreComments(Comparator comparator) { + } + + @Override + public void set_PreCommentList(List preComments) { + } + + @Override + public List get_PreCommentList() { + return null; + } + + @Override + public void clear_PostComments() { + } + + @Override + public boolean add_PostComment(Comment postcomment) { + return false; + } + + @Override + public boolean addAll_PostComments(Collection postcomments) { + return false; + } + + @Override + public boolean contains_PostComment(Object element) { + return false; + } + + @Override + public boolean containsAll_PostComments(Collection element) { + return false; + } + + @Override + public boolean isEmpty_PostComments() { + return false; + } + + @Override + public Iterator iterator_PostComments() { + return null; + } + + @Override + public boolean remove_PostComment(Object element) { + return false; + } + + @Override + public boolean removeAll_PostComments(Collection element) { + return false; + } + + @Override + public boolean retainAll_PostComments(Collection element) { + return false; + } + + @Override + public int size_PostComments() { + return 0; + } + + @Override + public Comment[] toArray_PostComments(Comment[] array) { + return null; + } + + @Override + public boolean removeIf_PostComment(Predicate filter) { + return false; + } + + @Override + public Spliterator spliterator_PostComments() { + return null; + } + + @Override + public Stream stream_PostComments() { + return null; + } + + @Override + public Stream parallelStream_PostComments() { + return null; + } + + @Override + public void forEach_PostComments(Consumer action) { + } + + @Override + public void add_PostComment(int index, Comment postcomment) { + } + + @Override + public boolean addAll_PostComments(int index, Collection postcomments) { + return false; + } + + @Override + public Comment get_PostComment(int index) { + return null; + } + + @Override + public int indexOf_PostComment(Object element) { + return 0; + } + + @Override + public int lastIndexOf_PostComment(Object element) { + return 0; + } + + @Override + public boolean equals_PostComments(Object element) { + return false; + } + + @Override + public int hashCode_PostComments() { + return 0; + } + + @Override + public ListIterator listIterator_PostComments() { + return null; + } + + @Override + public Comment remove_PostComment(int index) { + return null; + } + + @Override + public List subList_PostComments(int start, int end) { + return null; + } + + @Override + public void replaceAll_PostComments(UnaryOperator operator) { + } + + @Override + public void sort_PostComments(Comparator comparator) { + } + + @Override + public void set_PostCommentList(List postComments) { + } + + @Override + public List get_PostCommentList() { + return null; + } + + /** + * @see de.monticore.ast.ASTNode#listIterator_PreComments(int) + */ + @Override + public ListIterator listIterator_PreComments(int index) { + return null; + } + + /** + * @see de.monticore.ast.ASTNode#set_PreComment(int, de.monticore.ast.Comment) + */ + @Override + public Comment set_PreComment(int index, Comment precomment) { + return null; + } + + /** + * @see de.monticore.ast.ASTNode#toArray_PreComments() + */ + @Override + public Object[] toArray_PreComments() { + return null; + } + + /** + * @see de.monticore.ast.ASTNode#listIterator_PostComments(int) + */ + @Override + public ListIterator listIterator_PostComments(int index) { + return null; + } + + /** + * @see de.monticore.ast.ASTNode#set_PostComment(int, de.monticore.ast.Comment) + */ + @Override + public Comment set_PostComment(int index, Comment postcomment) { + return null; + } + + /** + * @see de.monticore.ast.ASTNode#toArray_PostComments() + */ + @Override + public Object[] toArray_PostComments() { + return null; + } +} diff --git a/monticore-runtime/src/test/java/de/monticore/ast/CommentBuilderTest.java b/monticore-runtime/src/test/java/de/monticore/ast/CommentBuilderTest.java new file mode 100644 index 0000000000..efa290f7bf --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/ast/CommentBuilderTest.java @@ -0,0 +1,33 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.ast; + +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class CommentBuilderTest { + + @Before + public void before() { + LogStub.init(); + Log.enableFailQuick(false); + } + + @Test + public void positiveTest() { + assertEquals("super comment", new CommentBuilder().setText("super comment").build().getText()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test(expected=IllegalStateException.class) + public void negativeTest() { + final CommentBuilder commentBuilder = new CommentBuilder(); + assertFalse(commentBuilder.isValid()); + commentBuilder.build(); + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-runtime/src/test/java/de/monticore/cocos/helper/Assert.java b/monticore-runtime/src/test/java/de/monticore/cocos/helper/Assert.java new file mode 100644 index 0000000000..64b388e0d2 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/cocos/helper/Assert.java @@ -0,0 +1,75 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.cocos.helper; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.Collection; + +import com.google.common.base.Joiner; + +import de.se_rwth.commons.logging.Finding; + +/** + * Helper for testing CoCos. + * + */ +public class Assert { + + /** + * Asserts that each of the expectedErrors is found at least once in the + * actualErrors. + * + * @param expectedErrors + * @param actualErrors + */ + public static void assertErrors(Collection expectedErrors, + Collection actualErrors) { + String actualErrorsJoined = "\nactual Errors: \n\t" + Joiner.on("\n\t").join(actualErrors); + for (Finding expectedError : expectedErrors) { + boolean found = actualErrors.stream() + .filter(s -> s.buildMsg().contains(expectedError.buildMsg())).count() >= 1; + assertTrue("The following expected error was not found: " + expectedError + + actualErrorsJoined, found); + } + } + + /** + * Asserts that each of the messages of expectedErrors is found at least once + * in any of the actualErrors. The check omits other fields of the errors. + * + * @param expectedErrors + * @param actualErrors + */ + public static void assertErrorMsg(Collection expectedErrors, + Collection actualErrors) { + String actualErrorsJoined = "\nactual Errors: \n\t" + Joiner.on("\n\t").join(actualErrors); + for (Finding expectedError : expectedErrors) { + + boolean found = actualErrors.stream().filter( + f -> f.getMsg().equals(expectedError.getMsg()) + ).count() >= 1; + assertTrue("The following expected error was not found: " + expectedError + + actualErrorsJoined, found); + } + } + + /** + * Asserts that there are exactly as many actual errors as expected. + * + * @param expectedErrors + * @param actualErrors + */ + public static void assertEqualErrorCounts(Collection expectedErrors, + Collection actualErrors) { + String actualErrorsJoined = "\nactual Errors: \n\t" + Joiner.on("\n\t").join(actualErrors); + String expectedErrorsJoined = "\nexpected Errors: \n\t" + + Joiner.on("\n\t").join(expectedErrors); + assertEquals( + "Expected " + expectedErrors.size() + " errors, but found " + actualErrors.size() + + "." + expectedErrorsJoined + actualErrorsJoined, expectedErrors.size(), + actualErrors.size()); + + } +} diff --git a/monticore-runtime/src/test/java/de/monticore/generating/GeneratorEngineMock.java b/monticore-runtime/src/test/java/de/monticore/generating/GeneratorEngineMock.java new file mode 100644 index 0000000000..190e5168d4 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/GeneratorEngineMock.java @@ -0,0 +1,31 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating; + +import java.util.List; +import java.util.Map; + +import com.google.common.collect.Maps; + +import de.monticore.ast.ASTNode; + +/** + * Mock for {@link GeneratorEngine}. Can always be used instead + * of {@link GeneratorEngine}. + * + */ +public class GeneratorEngineMock extends GeneratorEngine { + + private Map> handledNodesAndTemplates = Maps.newHashMap(); + + + public GeneratorEngineMock(GeneratorSetup generatorSetup) { + super(generatorSetup); + } + + + public Map> getHandledNodesAndTemplates() { + return this.handledNodesAndTemplates; + } + +} diff --git a/monticore-runtime/src/test/java/de/monticore/generating/GeneratorEngineTest.java b/monticore-runtime/src/test/java/de/monticore/generating/GeneratorEngineTest.java new file mode 100644 index 0000000000..2b362eecda --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/GeneratorEngineTest.java @@ -0,0 +1,111 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating; + +import com.google.common.collect.Lists; +import de.monticore.ast.ASTNodeMock; +import de.monticore.generating.templateengine.FreeMarkerTemplateEngineMock; +import de.monticore.generating.templateengine.FreeMarkerTemplateMock; +import de.monticore.io.FileReaderWriter; +import de.monticore.io.FileReaderWriterMock; +import de.monticore.io.paths.MCPath; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.File; +import java.nio.file.Paths; + +import static de.monticore.generating.GeneratorEngine.existsHandwrittenClass; +import static org.junit.Assert.*; + +/** + * Tests for {@link de.monticore.generating.GeneratorEngine}. + * + */ +public class GeneratorEngineTest { + + @Before + public void before() { + LogStub.init(); + Log.enableFailQuick(false); + } + + @Test + public void testGenerateInFile() { + ASTNodeMock node = new ASTNodeMock(); + + final GeneratorSetup setup = new GeneratorSetup(); + setup.setOutputDirectory(new File("target1")); + FreeMarkerTemplateEngineMock freeMarkerTemplateEngine = new FreeMarkerTemplateEngineMock(setup.getConfig()); + setup.setFreeMarkerTemplateEngine(freeMarkerTemplateEngine); + FileReaderWriterMock fileHandler = new FileReaderWriterMock(); + setup.setFileHandler(fileHandler); + + GeneratorEngineMock generatorEngine = new GeneratorEngineMock(setup); + + generatorEngine.generate("the.Template", Paths.get("a/GenerateInFile.test"), node); + + assertEquals(1, freeMarkerTemplateEngine.getProcessedTemplates().size()); + FreeMarkerTemplateMock template = freeMarkerTemplateEngine.getProcessedTemplates().iterator().next(); + assertTrue(template.isProcessed()); + assertEquals("the.Template", template.getName()); + + assertEquals(1, fileHandler.getStoredFilesAndContents().size()); + assertTrue(fileHandler.getStoredFilesAndContents().containsKey(Paths.get + (new File("target1/a/GenerateInFile.test").getAbsolutePath()))); + assertTrue(Log.getFindings().isEmpty()); + } + + @AfterClass + public static void resetFileReaderWriter() { + FileReaderWriter.init(); + } + + @Test + public void testGenerateStringBuilder() { + final GeneratorSetup setup = new GeneratorSetup(); + FreeMarkerTemplateEngineMock freeMarkerTemplateEngine = new FreeMarkerTemplateEngineMock(setup.getConfig()); + setup.setFreeMarkerTemplateEngine(freeMarkerTemplateEngine); + FileReaderWriterMock fileHandler = new FileReaderWriterMock(); + setup.setFileHandler(fileHandler); + + GeneratorEngineMock generatorEngine = new GeneratorEngineMock(setup); + + StringBuilder sb = generatorEngine.generateNoA("the.Template"); + assertTrue(sb.length()>0); + + assertEquals(1, freeMarkerTemplateEngine.getProcessedTemplates().size()); + FreeMarkerTemplateMock template = freeMarkerTemplateEngine.getProcessedTemplates().iterator().next(); + assertTrue(template.isProcessed()); + assertEquals("the.Template", template.getName()); + + assertEquals(0, fileHandler.getStoredFilesAndContents().size()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testWrongPath() { + final GeneratorSetup setup = new GeneratorSetup(); + FileReaderWriterMock fileHandler = new FileReaderWriterMock(); + File file = new File("doesnotexist"); + setup.setAdditionalTemplatePaths(Lists.newArrayList(file)); + setup.setFileHandler(fileHandler); + FreeMarkerTemplateEngineMock freeMarkerTemplateEngine = new FreeMarkerTemplateEngineMock(setup.getConfig()); + assertEquals(1, Log.getFindingsCount()); + assertEquals("0xA1020 Unable to load templates from non-existent path doesnotexist", Log.getFindings().get(0).getMsg()); + } + + @Test + public void testExistHWC() { + String classname = "test.A"; + String notExistName = "test.B"; + + assertTrue(existsHandwrittenClass(new MCPath("src/test/resources/hwc"), classname)); + assertFalse(existsHandwrittenClass(new MCPath("src/test/resources/hwc"), notExistName)); + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/CodeHookPointMock.java b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/CodeHookPointMock.java new file mode 100644 index 0000000000..839c0062e8 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/CodeHookPointMock.java @@ -0,0 +1,49 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine; + +import java.util.List; + +import de.monticore.generating.templateengine.CodeHookPoint; +import de.monticore.generating.templateengine.TemplateController; +import de.monticore.ast.ASTNode; + +/** + * Mock for {@link CodeHookPoint} + * + */ +public class CodeHookPointMock extends CodeHookPoint { + + private String returnValue; + + /** + * Constructor for mc.codegen.CodeHookPointMock + * @param returnValue + */ + CodeHookPointMock(String returnValue) { + super(); + this.returnValue = returnValue; + } + + /** + * @see mc.codegen.CodeHookPoint#processValue(mc.codegen.TemplateController, de.monticore.ast.ASTNode) + */ + @Override + public String processValue(TemplateController controller, ASTNode ast) { + return returnValue; + } + + @Override + public String processValue(TemplateController controller, List args) { + return returnValue; + } + + /** + * @see de.monticore.generating.templateengine.HookPoint#processValue(de.monticore.generating.templateengine.TemplateController, de.monticore.ast.ASTNode, java.util.List) + */ + @Override + public String processValue(TemplateController controller, ASTNode node, List args) { + return returnValue; + } + +} diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/DefaultImpl.java b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/DefaultImpl.java new file mode 100644 index 0000000000..8936f697d3 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/DefaultImpl.java @@ -0,0 +1,5 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.generating.templateengine; + +public class DefaultImpl implements IDefaultImpl { +} diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/FreeMarkerTemplateEngineMock.java b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/FreeMarkerTemplateEngineMock.java new file mode 100644 index 0000000000..b65386803d --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/FreeMarkerTemplateEngineMock.java @@ -0,0 +1,54 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine; + +import java.util.Set; + +import com.google.common.collect.Sets; + +import de.monticore.generating.templateengine.freemarker.FreeMarkerTemplateEngine; +import freemarker.template.Configuration; +import freemarker.template.Template; + +/** + * Mock for {@link FreeMarkerTemplateEngine}. Note that this mock CANNOT always substitute + * {@link FreeMarkerTemplateEngine}, since it just keeps track of templates that need to be + * processed, but does NOT process them. Consequently, expressions in templates, such as + * ${tc.write(...)}, are not invoked. + * + */ +public class FreeMarkerTemplateEngineMock extends FreeMarkerTemplateEngine { + + private Set processedTemplates = Sets.newLinkedHashSet(); + + public FreeMarkerTemplateEngineMock(Configuration configuration) { + super(configuration); + } + + /** + * @see de.monticore.generating.templateengine.freemarker.FreeMarkerTemplateEngine#loadTemplate(java.lang.String) + */ + @Override + public Template loadTemplate(String qualifiedTemplateName) { + return FreeMarkerTemplateMock.of(qualifiedTemplateName); + } + + /** + * @see de.monticore.generating.templateengine.freemarker.FreeMarkerTemplateEngine#run(java.lang.StringBuilder, + * java.lang.Object, freemarker.template.Template) + */ + @Override + public void run(StringBuilder buffer, Object data, Template template) { + FreeMarkerTemplateMock processedTemplate = FreeMarkerTemplateMock.of(template.getName()); + processedTemplate.process(data, null); + + processedTemplates.add(processedTemplate); + + buffer.append("Content of template: " + template.getName()); + } + + public Set getProcessedTemplates() { + return this.processedTemplates; + } + +} diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/FreeMarkerTemplateMock.java b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/FreeMarkerTemplateMock.java new file mode 100644 index 0000000000..62ead0e18d --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/FreeMarkerTemplateMock.java @@ -0,0 +1,62 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine; + +import java.io.IOException; +import java.io.StringReader; +import java.io.Writer; + +import freemarker.template.Configuration; +import freemarker.template.Template; + +/** + * Mock for FreeMarker templates (see {@link Template}). + * + */ +public class FreeMarkerTemplateMock extends Template { + + private Object data; + + private boolean isProcessed = false; + + public static FreeMarkerTemplateMock of(String name) { + try { + return new FreeMarkerTemplateMock(name); + } + catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Constructor for de.monticore.generating.templateengine.controller.FreeMarkerTemplateMock + * + * @param name + * @throws IOException + */ + FreeMarkerTemplateMock(String name) throws IOException { + super(name, new StringReader(""), new Configuration()); + } + + public boolean isProcessed() { + return isProcessed; + } + + /** + * @see freemarker.template.Template#process(java.lang.Object, java.io.Writer) + */ + @Override + public void process(Object rootMap, Writer out) { + isProcessed = true; + + this.data = rootMap; + } + + /** + * @return The data passed to the template. Should usually be SimpleHash + */ + public Object getData() { + return this.data; + } + +} diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/GeneratorSetupMock.java b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/GeneratorSetupMock.java new file mode 100644 index 0000000000..ecf20d49a7 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/GeneratorSetupMock.java @@ -0,0 +1,16 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.generating.templateengine; + +import de.monticore.generating.GeneratorSetup; + +public class GeneratorSetupMock extends GeneratorSetup { + + /** + * @see de.monticore.generating.GeneratorSetup#getNewTemplateController(java.lang.String) + */ + @Override + public TemplateControllerMock getNewTemplateController(String templateName) { + return new TemplateControllerMock(this, templateName); + } + +} diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/GlobalExtensionManagementGlobalVarsTest.java b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/GlobalExtensionManagementGlobalVarsTest.java new file mode 100644 index 0000000000..c0920c1c52 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/GlobalExtensionManagementGlobalVarsTest.java @@ -0,0 +1,94 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine; + +import de.monticore.ast.ASTNodeMock; +import de.monticore.generating.GeneratorEngine; +import de.monticore.generating.GeneratorSetup; +import de.monticore.io.FileReaderWriter; +import de.monticore.io.FileReaderWriterMock; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.File; +import java.util.ArrayList; + +import static de.monticore.generating.templateengine.TestConstants.TEMPLATE_PACKAGE; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * Tests for {@link GlobalExtensionManagement}. + * + */ +public class GlobalExtensionManagementGlobalVarsTest { + + private TemplateControllerMock tc; + private GlobalExtensionManagement glex; + + + @Before + public void before() { + LogStub.init(); + Log.enableFailQuick(false); + } + + @Before + public void setup() { + glex = new GlobalExtensionManagement(); + + GeneratorSetup config = new GeneratorSetup(); + config.setGlex(glex); + config.setFileHandler(new FileReaderWriterMock()); + config.setOutputDirectory(new File("dummy")); + config.setTracing(false); + tc = new TemplateControllerMock(config, ""); + assertTrue(Log.getFindings().isEmpty()); + } + + @AfterClass + public static void resetFileReaderWriter() { + FileReaderWriter.init(); + } + + @Test + public void testGlobalVars() { + glex.defineGlobalVar("test", "test"); + glex.defineGlobalVar("asd", new String("asd")); + + StringBuilder output = tc.include(TEMPLATE_PACKAGE + "GlobalVars"); + assertEquals("testasd", output.toString().replaceAll("\\s+", "")); + + glex.changeGlobalVar("asd", new String("aaa")); + output = tc.include(TEMPLATE_PACKAGE + "GlobalVars"); + assertEquals("testaaa", output.toString().replaceAll("\\s+", "")); + + glex.defineGlobalVar("liste", new ArrayList<>()); + glex.addToGlobalVar("liste", new String("a")); + glex.addToGlobalVar("liste", new String("b")); + glex.addToGlobalVar("liste", new String("c")); + output = tc.include(TEMPLATE_PACKAGE + "GlobalListVars"); + assertEquals("abc", output.toString().replaceAll("\\s+", "")); + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void testVariables4() { + GeneratorSetup s = new GeneratorSetup(); + s.setTracing(false); + GeneratorEngine ge = new GeneratorEngine(s); + ASTNodeMock ast = new ASTNodeMock(); + + // override same variable + String res = ge.generate(TEMPLATE_PACKAGE + "TestVariables4", ast).toString(); + + assertEquals("A:16B:38C:555", res.replaceAll("\\r\\n|\\r|\\n", "")); + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/IDefaultImpl.java b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/IDefaultImpl.java new file mode 100644 index 0000000000..c376e43b7c --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/IDefaultImpl.java @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.generating.templateengine; + +public interface IDefaultImpl { + + default String print() { + return "A"; + } +} diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/ObjectFactoryTest.java b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/ObjectFactoryTest.java new file mode 100644 index 0000000000..c0d779e84f --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/ObjectFactoryTest.java @@ -0,0 +1,64 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine; + +import java.util.ArrayList; +import java.util.List; + +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import de.monticore.generating.templateengine.ObjectFactory; + +import static org.junit.Assert.*; + +public class ObjectFactoryTest { + + @Before + public void before() { + LogStub.init(); + Log.enableFailQuick(false); + } + + @Test + public void testInstanciationWithDefaultConstructor() { + Object obj = ObjectFactory.createObject("java.lang.String"); + assertNotNull(obj); + assertEquals(String.class, obj.getClass()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testInstanciationWithParams() { + List params = new ArrayList(); + params.add("myContent"); + Object obj = ObjectFactory.createObject("java.lang.String", params); + assertNotNull(obj); + assertEquals(String.class, obj.getClass()); + assertEquals(obj, "myContent"); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testInstanciationWithTypesAndParams() { + List> paramTypes = new ArrayList>(); + paramTypes.add(char[].class); + paramTypes.add(Integer.TYPE); + paramTypes.add(Integer.TYPE); + + List params = new ArrayList(); + params.add("Say yes!".toCharArray()); + params.add(4); + params.add(3); + + Object obj = ObjectFactory.createObject("java.lang.String", paramTypes, params); + assertNotNull(obj); + assertEquals(obj.getClass(), (new String()).getClass()); + assertEquals(obj, "yes"); + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/TemplateAliasingTest.java b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/TemplateAliasingTest.java new file mode 100644 index 0000000000..74ab675c98 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/TemplateAliasingTest.java @@ -0,0 +1,430 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine; + +import com.google.common.base.Joiner; +import de.monticore.ast.ASTCNode; +import de.monticore.ast.ASTNode; +import de.monticore.generating.GeneratorSetup; +import de.monticore.generating.templateengine.freemarker.alias.Alias; +import de.monticore.io.FileReaderWriter; +import de.monticore.io.FileReaderWriterMock; +import de.monticore.symboltable.IScope; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; +import java.util.*; + +import static org.junit.Assert.*; + +public class TemplateAliasingTest { + + private static final File TARGET_DIR = new File("target"); + private static final int NUMBER_ALIASES = 20; + public static final String ALIASES_PACKAGE = "de.monticore.generating.templateengine.templates.aliases."; + + + private TemplateController tc; + + private GeneratorSetup config; + + + @Before + public void init() { + LogStub.init(); + Log.enableFailQuick(false); + } + + @Before + public void setup() { + FileReaderWriterMock fileHandler = new FileReaderWriterMock(); + FileReaderWriter.init(fileHandler); + + config = new GeneratorSetup(); + config.setOutputDirectory(TARGET_DIR); + config.setTracing(false); + + tc = new TemplateController(config, ""); + + LogStub.getPrints().clear(); + } + + @AfterClass + public static void resetFileReaderWriter() { + FileReaderWriter.init(); + } + + @Test + public void testIncludeAlias() { + StringBuilder templateOutput = + tc.include(ALIASES_PACKAGE + "IncludeAlias"); + assertEquals("Plain is included.", templateOutput.toString()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testIncludeDispatching(){ + StringBuilder templateOutput = + tc.include(ALIASES_PACKAGE + "IncludeDispatching"); + + assertEquals( + "String argument\n" + + "Plain is included.\n" + + "Plain is included.\n" + + "\n" + + "List argument\n" + + "Plain is included.Plain is included.\n" + + "Plain is included.Plain is included.", templateOutput.toString()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testInclude2Alias() { + String content = "Content of ast"; + StringBuilder templateOutput = + tc.include(ALIASES_PACKAGE + "Include2Alias", new AliasTestASTNodeMock(content)); + assertEquals(content, templateOutput.toString()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testIncludeArgsAndSignatureAlias(){ + StringBuilder templateOut = + tc.include(ALIASES_PACKAGE + "IncludeArgsAndSignatureAlias"); + + assertEquals("Name is Charly, age is 30, city is Aachen", templateOut.toString()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testSignature(){ + StringBuilder templateOut = + tc.includeArgs(ALIASES_PACKAGE + "SignatureAliasWithThreeParameters", "Max Mustermann", "45", "Berlin"); + + assertEquals("Name is Max Mustermann, age is 45, city is Berlin", templateOut.toString()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testSimpleDefineHookPoint() throws IOException { + tc.getGeneratorSetup().setTracing(true); + AliasTestASTNodeMock ast = new AliasTestASTNodeMock("c1"); + AliasTestASTNodeMock alternativeAst = new AliasTestASTNodeMock("c2"); + + String templateName = ALIASES_PACKAGE + "DefineHookPointAlias"; + StringBuilder templateOut = + tc.includeArgs(templateName, ast, Collections.singletonList(alternativeAst)); + + assertEquals("/* generated by template de.monticore.generating.templateengine.templates.aliases.DefineHookPointAlias*/\n" + + "/* Hookpoint: WithoutAst */\n" + + "/* Hookpoint: WithAst */\n" + + "/* Hookpoint: WithAlternativeAst */", templateOut.toString()); + + GlobalExtensionManagement glex = tc.getGeneratorSetup().getGlex(); + + glex.bindHookPoint("WithoutAst", new StringHookPoint("a1")); + templateOut = + tc.includeArgs(templateName, ast, Collections.singletonList(alternativeAst)); + assertEquals("/* generated by template de.monticore.generating.templateengine.templates.aliases.DefineHookPointAlias*/\n" + + "a1\n" + + "/* Hookpoint: WithAst */\n" + + "/* Hookpoint: WithAlternativeAst */", templateOut.toString()); + + + glex.bindHookPoint("WithAst", new TemplateStringHookPoint("${ast.content}")); + templateOut = + tc.includeArgs(templateName, ast, Collections.singletonList(alternativeAst)); + assertEquals("/* generated by template de.monticore.generating.templateengine.templates.aliases.DefineHookPointAlias*/\n" + + "a1\n" + + "/* generated by template template*/\n" + + "c1\n" + + "/* Hookpoint: WithAlternativeAst */", templateOut.toString()); + + tc.getGeneratorSetup().setTracing(false); + glex.bindHookPoint("WithAlternativeAst", new TemplateStringHookPoint("${ast.content}")); + templateOut = + tc.includeArgs(templateName, ast, Collections.singletonList(alternativeAst)); + assertEquals("a1\n" + + "c1\n" + + "c2", templateOut.toString()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testDefineHookPointWithDefaultAlias() throws IOException { + AliasTestASTNodeMock ast = new AliasTestASTNodeMock("c1"); + AliasTestASTNodeMock alternativeAst = new AliasTestASTNodeMock("c2"); + + String templateName = ALIASES_PACKAGE + "DefineHookPointWithDefaultAlias"; + StringBuilder templateOut = + tc.includeArgs(templateName, ast, Collections.singletonList(alternativeAst)); + + assertEquals("default text 1\n" + + "default text 2\n" + + "default text 3", templateOut.toString()); + + GlobalExtensionManagement glex = tc.getGeneratorSetup().getGlex(); + + glex.bindHookPoint("WithoutAst", new StringHookPoint("a1")); + templateOut = + tc.includeArgs(templateName, ast, Collections.singletonList(alternativeAst)); + assertEquals("a1\n" + + "default text 2\n" + + "default text 3", templateOut.toString()); + + + glex.bindHookPoint("WithAst", new TemplateStringHookPoint("${ast.content}")); + templateOut = + tc.includeArgs(templateName, ast, Collections.singletonList(alternativeAst)); + assertEquals("a1\n" + + "c1\n" + + "default text 3", templateOut.toString()); + + glex.bindHookPoint("WithAlternativeAst", new TemplateStringHookPoint("${ast.content}")); + templateOut = + tc.includeArgs(templateName, ast, Collections.singletonList(alternativeAst)); + assertEquals("a1\n" + + "c1\n" + + "c2", templateOut.toString()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testBindHookPointAlias(){ + tc.getGeneratorSetup().setTracing(true); + AliasTestASTNodeMock ast = new AliasTestASTNodeMock("c1"); + AliasTestASTNodeMock alternativeAst = new AliasTestASTNodeMock("c2"); + + String templateName = ALIASES_PACKAGE + "BindHookPointAlias"; + + StringBuilder templateOut = + tc.includeArgs(templateName, ast, Arrays.asList(alternativeAst)); + + assertEquals("/* generated by template de.monticore.generating.templateengine.templates.aliases.BindHookPointAlias*/\n" + + "/* generated by template de.monticore.generating.templateengine.templates.aliases.DefineHookPointAlias*/\n" + + "bound\n" + + "/* Hookpoint: WithAst */\n" + + "/* Hookpoint: WithAlternativeAst */", templateOut.toString()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testRequiredGlobalVarValid() { + GlobalExtensionManagement glex = tc.getGeneratorSetup().getGlex(); + try { + glex.setGlobalValue("a", "a"); + StringBuilder templateOut = + tc.include(ALIASES_PACKAGE + "RequiredGlobalVarAlias"); + }finally { + glex.getGlobalData().remove("a"); + } + + assertTrue("Log not empty!\n" + LogStub.getPrints(), LogStub.getPrints().isEmpty()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testRequiredGlobalVarInvalid() { + tc.include(ALIASES_PACKAGE + "RequiredGlobalVarAlias"); + assertFalse("Log is empty but error was expected!", LogStub.getPrints().isEmpty()); + } + + + @Test + public void testRequiredGlobalVarsValid() { + GlobalExtensionManagement glex = tc.getGeneratorSetup().getGlex(); + try { + glex.setGlobalValue("a", "a"); + glex.setGlobalValue("b", "b"); + glex.setGlobalValue("c", "c"); + StringBuilder templateOut = + tc.include(ALIASES_PACKAGE + "RequiredGlobalVarsAlias"); + }finally { + glex.getGlobalData().remove("a"); + glex.getGlobalData().remove("b"); + glex.getGlobalData().remove("c"); + } + + assertTrue("Log not empty!\n" + LogStub.getPrints(), LogStub.getPrints().isEmpty()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testRequiredGlobalVarsInvalid() { + StringBuilder templateOut = + tc.include(ALIASES_PACKAGE + "RequiredGlobalVarsAlias"); + + assertFalse("Log empty, expected error", LogStub.getPrints().isEmpty()); + } + + @Test + public void testLogAliases() { + assertTrue(config.getAliases().isEmpty()); + tc.include(ALIASES_PACKAGE + "LogAliases"); + assertAliases(tc, NUMBER_ALIASES); + + Collection expectedLogs = Arrays.asList( + "Info Message", + "Warn Message", + "Error Message" + ); + + assertEquals(3, LogStub.getPrints().size()); + assertErrors(expectedLogs, LogStub.getPrints()); + } + + + @Test + public void testExistsHookPoint(){ + StringBuilder templateOut = + tc.include(ALIASES_PACKAGE + "ExistsHookPointAlias"); + assertEquals("false\nfalse", templateOut.toString()); + + config.getGlex().bindHookPoint("hp1", new StringHookPoint("a")); + templateOut = + tc.include(ALIASES_PACKAGE + "ExistsHookPointAlias"); + assertEquals("true\nfalse", templateOut.toString()); + + config.getGlex().bindHookPoint("hp2", new StringHookPoint("a")); + templateOut = + tc.include(ALIASES_PACKAGE + "ExistsHookPointAlias"); + assertEquals("true\ntrue", templateOut.toString()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testAddToGlobalVarsAliasUndefinedVariable(){ + tc.include(ALIASES_PACKAGE + "AddToGlobalVarAlias"); + assertTrue(LogStub.getPrints().stream().anyMatch(s -> s.contains("0xA8124"))); + } + + @Test + public void testAddToGlobalVarsAliasValid(){ + GlobalExtensionManagement glex = config.getGlex(); + glex.defineGlobalVar("a", new ArrayList()); + glex.defineGlobalVar("b", new ArrayList()); + + tc.include(ALIASES_PACKAGE + "AddToGlobalVarAlias"); + + assertTrue(LogStub.getPrints().isEmpty()); + + List aList = (List) glex.getGlobalVar("a"); + List bList = (List) glex.getGlobalVar("b"); + + assertEquals(2, aList.size()); + assertEquals(1, bList.size()); + + assertEquals("item 1", aList.get(0)); + assertEquals("item 2", aList.get(1)); + assertEquals("item 3", bList.get(0)); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testChangeGlobalVarAlias(){ + GlobalExtensionManagement glex = config.getGlex(); + glex.defineGlobalVar("a", "unchanged"); + + tc.include(ALIASES_PACKAGE + "ChangeGlobalVarAlias"); + + assertEquals("changed", glex.getGlobalVar("a")); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testDefineGlobalVarAliasValid(){ + LogStub.getPrints().clear(); + GlobalExtensionManagement glex = config.getGlex(); + tc.include(ALIASES_PACKAGE + "DefineGlobalVarAlias"); + + glex.requiredGlobalVar("a"); + + assertTrue("Log is not empty, messages: " + LogStub.getPrints(), LogStub.getPrints().isEmpty()); + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void testDefineGlobalVarAliasExistsAlready(){ + GlobalExtensionManagement glex = config.getGlex(); + glex.defineGlobalVar("a", "?"); + tc.include(ALIASES_PACKAGE + "DefineGlobalVarAlias"); + + assertFalse(LogStub.getPrints().isEmpty()); + } + + @Test + public void testGetGlobalVarAliasValid(){ + GlobalExtensionManagement glex = config.getGlex(); + glex.defineGlobalVar("a", "value"); + StringBuilder templateOut = + tc.include(ALIASES_PACKAGE + "GetGlobalVarAlias"); + + assertEquals("value", templateOut.toString()); + assertTrue(LogStub.getPrints().isEmpty()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testGetGlobalVarAliasInvalid(){ + StringBuilder templateOut = + tc.include(ALIASES_PACKAGE + "GetGlobalVarAlias"); + + assertEquals("unset", templateOut.toString()); + assertTrue(Log.getFindings().isEmpty()); + } + + + + /** + * Asserts that each of the expectedErrors is found at least once in the + * actualErrors. + * + * @param expectedErrors + * @param actualErrors + */ + private static void assertErrors(Collection expectedErrors, + Collection actualErrors) { + String actualErrorsJoined = "\nactual Errors: \n\t" + Joiner.on("\n\t").join(actualErrors); + for (String expectedError : expectedErrors) { + boolean found = actualErrors.stream().filter(s -> s.contains(expectedError)).count() >= 1; + assertTrue("The following expected error was not found: " + expectedError + + actualErrorsJoined, found); + } + } + + private void assertAliases(TemplateController tc, int expectedNumberAliases) { + List aliases = config.getAliases(); + assertNotNull(aliases); + assertEquals(expectedNumberAliases, aliases.size()); + } + + public static class AliasTestASTNodeMock extends ASTCNode { + private final String content; + + public AliasTestASTNodeMock(String content) { + this.content = content; + } + + public String getContent(){ + return content; + } + + @Override + public IScope getEnclosingScope() { + return null; + } + + @Override + public ASTNode deepClone() { + return null; + } + } + +} diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/TemplateControllerHookPointsTest.java b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/TemplateControllerHookPointsTest.java new file mode 100644 index 0000000000..673b5cef87 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/TemplateControllerHookPointsTest.java @@ -0,0 +1,324 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine; + +import static de.monticore.generating.templateengine.TestConstants.TEMPLATE_PACKAGE; +import static org.junit.Assert.*; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; + +import de.monticore.io.FileReaderWriter; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import de.monticore.generating.GeneratorSetup; +import de.monticore.io.FileReaderWriterMock; + +/** + * Tests hook point methods of {@link TemplateController} + * + * + */ +public class TemplateControllerHookPointsTest { + + private TemplateController tc; + private GlobalExtensionManagement glex; + + @Before + public void before() { + LogStub.init(); + Log.enableFailQuick(false); + } + + @Before + public void setup() { + glex = new GlobalExtensionManagement(); + + GeneratorSetup config = new GeneratorSetup(); + config.setGlex(glex); + config.setFileHandler(new FileReaderWriterMock()); + config.setOutputDirectory(new File("dummy")); + config.setTracing(false); + tc = new TemplateControllerMock(config, ""); + } + + @AfterClass + public static void resetFileReaderWriter() { + FileReaderWriter.init(); + } + + @Test + public void testUndefinedHook() { + tc.getGeneratorSetup().setTracing(true); + assertEquals("/* Hookpoint: hp1 */", glex.defineHookPoint(tc, "hp1")); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testDefaultHook() { + tc.getGeneratorSetup().setTracing(false); + String hpValue; + hpValue = glex.defineHookPointWithDefault(tc, "hp1", "default"); + assertEquals("default", hpValue); + glex.bindHookPoint("hp1", new StringHookPoint("value of hp1")); + hpValue = glex.defineHookPointWithDefault(tc, "hp1", "default"); + assertEquals("value of hp1", hpValue); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testSetStringHook() { + String hpValue = null; + + + // define new hook point hp1 + glex.bindHookPoint("hp1", new StringHookPoint("value of hp1")); + hpValue = glex.defineHookPoint(tc, "hp1"); + assertNotNull(hpValue); + assertEquals("value of hp1", hpValue); + + // overwrite value of hook point hp1 + glex.bindHookPoint("hp1", new StringHookPoint("new value of hp1")); + hpValue = glex.defineHookPoint(tc, "hp1"); + assertNotNull(hpValue); + assertEquals("new value of hp1", hpValue); + + // define new hook point hp2 + glex.bindHookPoint("hp2", new StringHookPoint("value of hp2")); + hpValue = glex.defineHookPoint(tc, "hp2"); + assertNotNull(hpValue); + assertEquals("value of hp2", hpValue); + // hp1 still exists + assertEquals("new value of hp1", glex.defineHookPoint(tc, "hp1")); + } + + + @Test + public void testSetTemplateHook() { + String hpValue = null; + + // define new hook point hp1 + glex.bindHookPoint("hp1", new TemplateHookPoint(TEMPLATE_PACKAGE + "HelloWorld")); + hpValue = glex.defineHookPoint(tc, "hp1"); + assertNotNull(hpValue); + assertEquals("Hello World!", hpValue); + + // overwrite value of hook point hp1 + glex.bindHookPoint("hp1", new TemplateHookPoint(TEMPLATE_PACKAGE + "HowAreYou")); + hpValue = glex.defineHookPoint(tc, "hp1"); + assertNotNull(hpValue); + assertEquals("How Are You?", hpValue); + + // define new hook point hp2 + glex.bindHookPoint("hp2", new TemplateHookPoint(TEMPLATE_PACKAGE + "HelloWorld")); + hpValue = glex.defineHookPoint(tc, "hp2"); + assertNotNull(hpValue); + assertEquals("Hello World!", hpValue); + // hp1 still exists + assertEquals("How Are You?", glex.defineHookPoint(tc, "hp1")); + } + + @Test + public void testSetCodeHook() { + String hpValue = null; + + // define new hook point hp1 + CodeHookPointMock command = new CodeHookPointMock("command1"); + glex.bindHookPoint("hp1", command); + hpValue = glex.defineHookPoint(tc, "hp1"); + assertNotNull(hpValue); + assertEquals("command1", hpValue); + + // overwrite value of hook point hp1 + command = new CodeHookPointMock("command2"); + glex.bindHookPoint("hp1", command); + hpValue = glex.defineHookPoint(tc, "hp1"); + assertNotNull(hpValue); + assertEquals("command2", hpValue); + + // overwrite value of hook point hp1 + command = new CodeHookPointMock("command3"); + glex.bindHookPoint("hp2", command); + hpValue = glex.defineHookPoint(tc, "hp2"); + assertNotNull(hpValue); + assertEquals("command3", hpValue); + // hp1 still exists + assertEquals("command2", glex.defineHookPoint(tc, "hp1")); + } + + @Test + public void testStringTemplateCodeHookCombinations() { + final String hp = "hp"; + + glex.bindHookPoint(hp, new StringHookPoint("StringHook")); + assertEquals("StringHook", glex.defineHookPoint(tc, hp)); + + glex.bindHookPoint(hp, new TemplateHookPoint(TEMPLATE_PACKAGE + "A")); + assertEquals("A", glex.defineHookPoint(tc, hp)); + + CodeHookPointMock command = new CodeHookPointMock("command"); + glex.bindHookPoint(hp, command); + assertEquals("command", glex.defineHookPoint(tc, hp)); + + glex.bindHookPoint(hp, new TemplateHookPoint(TEMPLATE_PACKAGE + "A")); + assertEquals("A", glex.defineHookPoint(tc, hp)); + + glex.bindHookPoint(hp, new StringHookPoint("StringHook")); + assertEquals("StringHook", glex.defineHookPoint(tc, hp)); + } + + @Test + public void testStringHookInSubtemplate() { + assertEquals("TopStringHook Hello Brave New World!", tc.include(TEMPLATE_PACKAGE + "TopStringHook").toString()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testTemplateHookInSubtemplate() { + assertEquals("TopTemplateHook A", tc.include(TEMPLATE_PACKAGE + "TopTemplateHook").toString()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testBeforeTemplates() { + assertEquals("A", tc.include(TEMPLATE_PACKAGE + "A").toString()); + + glex.setBeforeTemplate(TEMPLATE_PACKAGE + "A", new TemplateHookPoint(TEMPLATE_PACKAGE + "B")); + assertEquals("BA", tc.include(TEMPLATE_PACKAGE + "A").toString()); + + // previously set template is overwritten + glex.setBeforeTemplate(TEMPLATE_PACKAGE + "A", new TemplateHookPoint(TEMPLATE_PACKAGE + "C")); + assertEquals("CA", tc.include(TEMPLATE_PACKAGE + "A").toString()); + + // pass a list of templates + glex.setBeforeTemplate(TEMPLATE_PACKAGE + "A", Arrays.asList( + new TemplateHookPoint(TEMPLATE_PACKAGE + "B"), + new TemplateHookPoint(TEMPLATE_PACKAGE + "C"))); + assertEquals("BCA", tc.include(TEMPLATE_PACKAGE + "A").toString()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testAfterTemplates() { + assertEquals("A", tc.include(TEMPLATE_PACKAGE + "A").toString()); + + glex.setAfterTemplate(TEMPLATE_PACKAGE + "A", new TemplateHookPoint(TEMPLATE_PACKAGE + "B")); + assertEquals("AB", tc.include(TEMPLATE_PACKAGE + "A").toString()); + + // previously set template is overwritten + glex.setAfterTemplate(TEMPLATE_PACKAGE + "A", new TemplateHookPoint(TEMPLATE_PACKAGE + "C")); + assertEquals("AC", tc.include(TEMPLATE_PACKAGE + "A").toString()); + + // pass a list of templates + glex.setAfterTemplate(TEMPLATE_PACKAGE + "A", Arrays.asList( + new TemplateHookPoint(TEMPLATE_PACKAGE + "B"), + new TemplateHookPoint(TEMPLATE_PACKAGE + "C"))); + assertEquals("ABC", tc.include(TEMPLATE_PACKAGE + "A").toString()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testAddAfterTemplates() { + assertEquals("A", tc.include(TEMPLATE_PACKAGE + "A").toString()); + + glex.addAfterTemplate(TEMPLATE_PACKAGE + "A", new TemplateHookPoint(TEMPLATE_PACKAGE + "B")); + assertEquals("AB", tc.include(TEMPLATE_PACKAGE + "A").toString()); + + // previously set template is not overwritten + glex.addAfterTemplate(TEMPLATE_PACKAGE + "A", new TemplateHookPoint(TEMPLATE_PACKAGE + "C")); + assertEquals("ABC", tc.include(TEMPLATE_PACKAGE + "A").toString()); + } + + @Test + public void testReplaceTemplate() { + StringBuilder r = tc.include(TEMPLATE_PACKAGE + "A"); + assertEquals("A", r.toString()); + + // self-replacement + glex.replaceTemplate(TEMPLATE_PACKAGE + "A", new TemplateHookPoint(TEMPLATE_PACKAGE + "A")); + r = tc.include(TEMPLATE_PACKAGE + "A"); + assertEquals("A", r.toString()); + + glex.replaceTemplate(TEMPLATE_PACKAGE + "A", new TemplateHookPoint(TEMPLATE_PACKAGE + "B")); + r = tc.include(TEMPLATE_PACKAGE + "A"); + assertEquals("B", r.toString()); + + // previously set template is overwritten + glex.replaceTemplate(TEMPLATE_PACKAGE + "A", new TemplateHookPoint(TEMPLATE_PACKAGE + "C")); + r = tc.include(TEMPLATE_PACKAGE + "A"); + assertEquals("C", r.toString()); + + // pass a list of templates + glex.replaceTemplate(TEMPLATE_PACKAGE + "A", Arrays.asList( + new TemplateHookPoint(TEMPLATE_PACKAGE + "B"), + new TemplateHookPoint(TEMPLATE_PACKAGE + "C"))); + r = tc.include(TEMPLATE_PACKAGE + "A"); + assertEquals("BC", r.toString()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testBeforeReplaceAfterCombinations() { + assertEquals("A", tc.include(TEMPLATE_PACKAGE + "A").toString()); + + glex.setBeforeTemplate(TEMPLATE_PACKAGE + "A", new TemplateHookPoint(TEMPLATE_PACKAGE + "B")); + assertEquals("BA", tc.include(TEMPLATE_PACKAGE + "A").toString()); + + glex.setAfterTemplate(TEMPLATE_PACKAGE + "A", new TemplateHookPoint(TEMPLATE_PACKAGE + "C")); + assertEquals("BAC", tc.include(TEMPLATE_PACKAGE + "A").toString()); + + glex.replaceTemplate(TEMPLATE_PACKAGE + "A", new TemplateHookPoint(TEMPLATE_PACKAGE + "C")); + assertEquals("BCC", tc.include(TEMPLATE_PACKAGE + "A").toString()); + + glex.replaceTemplate(TEMPLATE_PACKAGE + "A", new TemplateHookPoint(TEMPLATE_PACKAGE + "B")); + assertEquals("BBC", tc.include(TEMPLATE_PACKAGE + "A").toString()); + + // replacing B has no effect on A + glex.replaceTemplate(TEMPLATE_PACKAGE + "B", new TemplateHookPoint(TEMPLATE_PACKAGE + "C")); + assertEquals("BBC", tc.include(TEMPLATE_PACKAGE + "A").toString()); + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void testBeforeReplaceAfterInSubtemplates() { + assertEquals("TopA A", tc.include(TEMPLATE_PACKAGE + "TopA").toString()); + + glex.setBeforeTemplate(TEMPLATE_PACKAGE + "A", new TemplateHookPoint(TEMPLATE_PACKAGE + "B")); + assertEquals("TopA BA", tc.include(TEMPLATE_PACKAGE + "TopA").toString()); + + glex.replaceTemplate(TEMPLATE_PACKAGE + "A", new TemplateHookPoint(TEMPLATE_PACKAGE + "C")); + assertEquals("TopA BC", tc.include(TEMPLATE_PACKAGE + "TopA").toString()); + + glex.setAfterTemplate(TEMPLATE_PACKAGE + "A", new TemplateHookPoint(TEMPLATE_PACKAGE + "B")); + assertEquals("TopA BCB", tc.include(TEMPLATE_PACKAGE + "TopA").toString()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testTemplateStringHook() throws IOException{ + // define new hook point hp1 + glex.bindHookPoint("hp1", new TemplateStringHookPoint("<#if true>true")); + String hpValue = glex.defineHookPoint(tc, "hp1"); + assertNotNull(hpValue); + assertEquals("true", hpValue); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testDefineHookPointWithArgs() { + glex.bindHookPoint("hp1", new TemplateHookPoint(TEMPLATE_PACKAGE + "SignatureWithOneParameter")); + String hpValue = glex.defineHookPoint(tc, "hp1", "A"); + assertEquals("Name is A", hpValue); + + glex.bindHookPoint("hp1", new TemplateHookPoint(TEMPLATE_PACKAGE + "SignatureWithThreeParameters")); + hpValue = glex.defineHookPoint(tc, "hp1", "B", 42, "LA"); + assertEquals("Name is B, age is 42, city is LA", hpValue); + } +} diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/TemplateControllerMock.java b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/TemplateControllerMock.java new file mode 100644 index 0000000000..a037744baa --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/TemplateControllerMock.java @@ -0,0 +1,31 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine; + +import de.monticore.generating.GeneratorSetup; + +/** + * This class helps to test internals of {@link TemplateController}. + * + */ +public class TemplateControllerMock extends TemplateController { + + + /** + * Constructor for mc.codegen.ExtendedTemplateControllerForTesting + * @param setup + * @param templatename + */ + protected TemplateControllerMock(GeneratorSetup setup, String templatename) { + super(setup, templatename); + } + + /** + * @return + */ + public TemplateControllerMock getSubController() { + return null; + } + + +} diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/TemplateControllerSignatureUsageTest.java b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/TemplateControllerSignatureUsageTest.java new file mode 100644 index 0000000000..ea3f07ced3 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/TemplateControllerSignatureUsageTest.java @@ -0,0 +1,131 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine; + +import static de.monticore.generating.templateengine.TestConstants.TEMPLATE_PACKAGE; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.File; + +import de.monticore.io.FileReaderWriter; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.*; + +import com.google.common.collect.Lists; + +import de.monticore.generating.GeneratorSetup; +import de.monticore.generating.templateengine.freemarker.MontiCoreFreeMarkerException; +import de.monticore.io.FileReaderWriterMock; + +/** + * Tests for parameterized calls of the {@link TemplateController} + * + */ + +public class TemplateControllerSignatureUsageTest { + + private TemplateControllerMock tc; + private GlobalExtensionManagement glex; + + @Before + public void before() { + LogStub.init(); + Log.enableFailQuick(false); + } + + @Before + public void setup() { + glex = new GlobalExtensionManagement(); + + GeneratorSetup config = new GeneratorSetup(); + config.setGlex(glex); + config.setFileHandler(new FileReaderWriterMock()); + config.setOutputDirectory(new File("dummy")); + config.setTracing(false); + tc = new TemplateControllerMock(config, ""); + } + + @AfterClass + public static void resetFileReaderWriter() { + FileReaderWriter.init(); + } + + + // ================================================= + // Tests with templates + // ================================================= + + @Test + public void testSignatureWithOneParameter() { + StringBuilder output = tc.includeArgs(TEMPLATE_PACKAGE + "SignatureWithOneParameter", Lists.newArrayList("Charly")); + + assertTrue(Log.getFindings().isEmpty()); + assertEquals("Name is Charly", output.toString()); + } + + @Test + public void testSignatureWithThreeParameters() { + StringBuilder output = tc.includeArgs(TEMPLATE_PACKAGE + "SignatureWithThreeParameters", + Lists.newArrayList("Charly", "30", "Aachen")); + + assertEquals("Name is Charly, age is 30, city is Aachen", output.toString()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testSignatureWithManyParameters() { + StringBuilder output = tc.includeArgs(TEMPLATE_PACKAGE + "SignatureWithManyParameters", + Lists.newArrayList("Charly", "30", "Aachen", "52062", "Engineer", "No friends")); + + assertEquals("Name=Charly, age=30, city=Aachen, zip=52062, job=Engineer, friends=No friends", output.toString()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testNestedSignatureCalls() { + StringBuilder output = tc.includeArgs(TEMPLATE_PACKAGE + "NestedSignatureCalls", + Lists.newArrayList("T1")); + + assertEquals("T1 -> Name is T2", output.toString()); + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void testWrongNumberOfArguments() { + try { + tc.includeArgs(TEMPLATE_PACKAGE + "SignatureWithOneParameter", + Lists.newArrayList("Charly", "tooMuch")); + fail("Argument list is too long."); + } catch (MontiCoreFreeMarkerException e) { + assertTrue(e.getCause() instanceof IllegalArgumentException); + } + assertTrue(Log.getFindings().isEmpty()); + } + + @Ignore + @Test + public void testArgumentsAreOnlyVisibleInIncludedTemplate() { + StringBuilder templateOutput = tc.includeArgs(TEMPLATE_PACKAGE + "ArgumentsAreOnlyVisibleInIncludedTemplate", + Lists.newArrayList("Charly")); + + assertEquals("Hello Charly\nSorry, what was your name?", templateOutput.toString()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testParameterizedInclusionUsage() { + StringBuilder templateOutput = tc.include(TEMPLATE_PACKAGE + "ParameterizedInclusionUsage"); + + assertEquals( + "Name is Charly\n" + + "Name is Charly, age is 30, city is Aachen\n" + + "Name=Charly, age=30, city=Aachen, zip=52062, job=Engineer, friends=No friends" + , templateOutput.toString()); + assertTrue(Log.getFindings().isEmpty()); + } +} \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/TemplateControllerTest.java b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/TemplateControllerTest.java new file mode 100644 index 0000000000..5e1b15c568 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/TemplateControllerTest.java @@ -0,0 +1,184 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine; + +import com.google.common.collect.Lists; +import de.monticore.ast.ASTNodeMock; +import de.monticore.generating.GeneratorSetup; +import de.monticore.io.FileReaderWriter; +import de.monticore.io.FileReaderWriterMock; +import de.monticore.io.paths.MCPath; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import freemarker.template.Template; +import org.junit.*; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +import static de.monticore.generating.templateengine.TestConstants.TEMPLATE_PACKAGE; +import static org.junit.Assert.*; + +/** + * Tests for {@link TemplateController}. + */ +public class TemplateControllerTest { + + private static final File TARGET_DIR = new File("targetDir"); + + private static final Path HWC_DIR = Paths.get("src", "test", "resources", "hwc"); + + private TemplateControllerMock tc; + + private FreeMarkerTemplateEngineMock freeMarkerTemplateEngine; + + private FileReaderWriterMock fileHandler; + + @Before + public void before() { + LogStub.init(); + Log.enableFailQuick(false); + } + + @Before + public void setup() { + + final GeneratorSetupMock setup = new GeneratorSetupMock(); + + freeMarkerTemplateEngine = new FreeMarkerTemplateEngineMock(setup.getConfig()); + fileHandler = new FileReaderWriterMock(); + FileReaderWriter.init(fileHandler); + setup.setOutputDirectory(TARGET_DIR); + setup.setFreeMarkerTemplateEngine(freeMarkerTemplateEngine); + setup.setHandcodedPath(new MCPath(HWC_DIR)); + setup.setFileHandler(fileHandler); + setup.setTracing(false); + + tc = setup.getNewTemplateController(""); + } + + @AfterClass + public static void resetFileReaderWriter() { + FileReaderWriter.init(); + } + + @Ignore + @Test + public void testImplicitAstPassing() { + assertNull(tc.getAST()); + + tc.include(TEMPLATE_PACKAGE + "A"); + assertNull(tc.getAST()); + + // pass ast explicit + tc.include(TEMPLATE_PACKAGE + "A", ASTNodeMock.INSTANCE); + + assertNotNull(tc.getAST()); + assertSame(ASTNodeMock.INSTANCE, tc.getAST()); + assertTrue(Log.getFindings().isEmpty()); + + } + + @Test + public void testWriteArgs() { + String TEMPLATE_NAME = "the.Template"; + tc.writeArgs(TEMPLATE_NAME, "path.to.file", ".ext", ASTNodeMock.INSTANCE, new ArrayList<>()); + + assertEquals(1, freeMarkerTemplateEngine.getProcessedTemplates().size()); + FreeMarkerTemplateMock template = freeMarkerTemplateEngine.getProcessedTemplates().iterator() + .next(); + assertTrue(template.isProcessed()); + assertEquals(TEMPLATE_NAME, template.getName()); + assertNotNull(template.getData()); + + assertEquals(1, fileHandler.getStoredFilesAndContents().size()); + + Path writtenFilePath = Paths.get(TARGET_DIR.getAbsolutePath(), "path/to/file.ext"); + assertTrue(fileHandler.getStoredFilesAndContents().containsKey(writtenFilePath)); + assertEquals("Content of template: " + TEMPLATE_NAME, + fileHandler.getContentForFile(writtenFilePath.toString()).get()); + assertTrue(Log.getFindings().isEmpty()); + } + + + + @Test + public void testDefaultMethods() { + GlobalExtensionManagement glex = new GlobalExtensionManagement(); + + fileHandler = new FileReaderWriterMock(); + GeneratorSetup config = new GeneratorSetup(); + config.setGlex(glex); + config.setFileHandler(fileHandler); + config.setOutputDirectory(TARGET_DIR); + config.setTracing(false); + // .externalTemplatePaths(new File[]{}) + TemplateController tc = new TemplateControllerMock(config, ""); + DefaultImpl def = new DefaultImpl(); + StringBuilder result = tc + .includeArgs(TEMPLATE_PACKAGE + "DefaultMethodCall", Lists.newArrayList(def)); + assertNotNull(result); + assertEquals("A", result.toString().trim()); + FileReaderWriter.init(); + assertTrue(Log.getFindings().isEmpty()); + } + + /** + * + * tests if comments are being generated for the Blacklisted Templates using the normal + * Template Constructor + */ + @Test + public void testBlacklisteTemplatesI() throws IOException { + // init Template Controller under test + GeneratorSetup setup = new GeneratorSetup(); + TemplateController tc = new TemplateController(setup, ""); + + // init test data + String templateNameI = "foo"; + String templateNameII = "bar"; + Template templateI = new Template(templateNameI, "", null); + Template templateII = new Template(templateNameII, "", null); + List blackList = new ArrayList<>(); + blackList.add(templateNameI); + + // configure Template Controller with black list + tc.setTemplateBlackList(blackList); + + assertEquals(1, tc.getTemplateBlackList().size()); + assertFalse(tc.isTemplateNoteGenerated(templateI)); + assertTrue(tc.isTemplateNoteGenerated(templateII)); + + assertTrue(Log.getFindings().isEmpty()); + } + /** + * + * tests if comments are being generated for the Blacklisted Templates using the second Template + * Constructor + */ + @Test + public void testBlacklistTemplatesII() throws IOException { + // init Template Controller with black-list under test + List blackList = new ArrayList<>(); + GeneratorSetup setup = new GeneratorSetup(); + TemplateController tc = new TemplateController(setup, "",blackList); + + // init test data + String templateNameI = "foo"; + String templateNameII = "bar"; + blackList.add(templateNameI); + Template templateI = new Template(templateNameI, "", null); + Template templateII = new Template(templateNameII, "", null); + + + assertEquals(1, tc.getTemplateBlackList().size()); + assertFalse(tc.isTemplateNoteGenerated(templateI)); + assertTrue(tc.isTemplateNoteGenerated(templateII)); + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/TemplateLoggerTest.java b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/TemplateLoggerTest.java new file mode 100644 index 0000000000..bd4decdbc2 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/TemplateLoggerTest.java @@ -0,0 +1,79 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine; + +import static de.monticore.generating.templateengine.TestConstants.TEMPLATE_PACKAGE; +import static org.junit.Assert.*; + +import java.io.File; +import java.io.FileReader; + +import de.monticore.io.FileReaderWriter; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import de.monticore.generating.GeneratorSetup; +import de.monticore.generating.templateengine.freemarker.FreeMarkerTemplateEngine; +import de.monticore.io.FileReaderWriterMock; + +/** + * A simple unit test invoking a template which uses the new template logger. + * + */ +public class TemplateLoggerTest { + + private static final File TARGET_DIR = new File("target"); + + private TemplateControllerMock tc; + + private GlobalExtensionManagement glex; + + private FileReaderWriterMock fileHandler; + + @Before + public void before() { + LogStub.init(); + Log.enableFailQuick(false); + } + + /** + * Sets up the template controller for this 'test'. This is copied entirely + * from {@link TemplateControllerTest} but uses the production + * {@link FreeMarkerTemplateEngine} instead of the mock in order to have the + * template actually executed. Otherwise the log statements would do nothing. + */ + @Before + public void setup() { + glex = new GlobalExtensionManagement(); + + fileHandler = new FileReaderWriterMock(); + FileReaderWriter.init(fileHandler); + + GeneratorSetup config = new GeneratorSetup(); + config.setGlex(glex); + config.setOutputDirectory(TARGET_DIR); + config.setTracing(false); + // .externalTemplatePaths(new File[]{}) + tc = new TemplateControllerMock(config, ""); + } + + @AfterClass + public static void resetFileReaderWriter() { + FileReaderWriter.init(); + } + + /** + * Executes a test templates which invokes the template logger. + */ + @Test + public void demonstrateTemplateLogging() { + StringBuilder result = tc.include(TEMPLATE_PACKAGE + "Log"); + assertNotNull(result); + assertEquals("A", result.toString().trim()); + } + +} diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/TestConstants.java b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/TestConstants.java new file mode 100644 index 0000000000..88f272e3cd --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/TestConstants.java @@ -0,0 +1,9 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine; + +public class TestConstants { + + public static final String TEMPLATE_PACKAGE = "de.monticore.generating.templateengine.templates."; + +} diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/reporting/commons/ReportingStringHelperTest.java b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/reporting/commons/ReportingStringHelperTest.java new file mode 100644 index 0000000000..74f8706ee1 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/reporting/commons/ReportingStringHelperTest.java @@ -0,0 +1,43 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.generating.templateengine.reporting.commons; + +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.*; +import de.se_rwth.commons.logging.Log; + +public class ReportingStringHelperTest { + + @Before + public void before() { + LogStub.init(); + Log.enableFailQuick(false); + } + + @Test + public void testReportingStringHelper() { + assertFalse(ReportingHelper + .formatStringToReportingString( + "{this.height \n\t= builder.getHeight();this.width = builder.getWidth();addAllPhotoMessages(builder.getPhotoMessages());addAllTags(builder.getTags());}", + 60).contains("\t")); + + assertEquals(60, ReportingHelper + .formatStringToReportingString( + "{this.height \n\t= builder.getHeight();this.width = builder.getWidth();addAllPhotoMessages(builder.getPhotoMessages());addAllTags(builder.getTags());}", + 60).length()); + + assertEquals(6, ReportingHelper + .formatStringToReportingString( + "abcd", + 60).length()); + + assertEquals(7, ReportingHelper + .formatStringToReportingString( + "{this.height \n\t= builder.getHeight();this.width = builder.getWidth();addAllPhotoMessages(builder.getPhotoMessages());addAllTags(builder.getTags());}", + 5).length()); + assertTrue(Log.getFindings().isEmpty()); + } +} diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/reporting/commons/StatisticsHandlerTest.java b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/reporting/commons/StatisticsHandlerTest.java new file mode 100644 index 0000000000..4426e19064 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/reporting/commons/StatisticsHandlerTest.java @@ -0,0 +1,26 @@ +package de.monticore.generating.templateengine.reporting.commons; + +import de.monticore.generating.templateengine.reporting.commons.StatisticsHandler; +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class StatisticsHandlerTest { + @Test + public void validSHash() { + String content = "SomeString with many characters? + ?+._ü1^^"; + String SHASH = StatisticsHandler.getSHASH(content); + + assertTrue(StatisticsHandler.isValidSHASH(SHASH, content)); + } + @Test + public void invalidSHash() { + String content = "SomeString with many characters? + ?+._ü1^^"; + String SHASH = StatisticsHandler.getSHASH(content); + + String differentContent = "AnotjherString"; + assertFalse(StatisticsHandler.isValidSHASH(SHASH, differentContent)); + } + +} \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/A.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/A.ftl new file mode 100644 index 0000000000..1bbcd0dde7 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/A.ftl @@ -0,0 +1,2 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +A \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/ArgumentsAreOnlyVisibleInIncludedTemplate.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/ArgumentsAreOnlyVisibleInIncludedTemplate.ftl new file mode 100644 index 0000000000..16e45b11d7 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/ArgumentsAreOnlyVisibleInIncludedTemplate.ftl @@ -0,0 +1,4 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${tc.signature(["name"])}<#t> +Hello ${name} +${tc.include("PrintNameIfExists")} \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/B.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/B.ftl new file mode 100644 index 0000000000..3efdafc860 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/B.ftl @@ -0,0 +1,2 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +B \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/C.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/C.ftl new file mode 100644 index 0000000000..5d0cb7281e --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/C.ftl @@ -0,0 +1,2 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +C \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/DefaultMethodCall.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/DefaultMethodCall.ftl new file mode 100644 index 0000000000..e873d4f6b2 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/DefaultMethodCall.ftl @@ -0,0 +1,3 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${tc.signature("def")} +${def.print()} \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/GlobalListVars.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/GlobalListVars.ftl new file mode 100644 index 0000000000..a48a8e5e51 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/GlobalListVars.ftl @@ -0,0 +1,8 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +<#if getGlobalVar("liste")??> + <#list getGlobalVar("liste") as l> + ${l} + +<#else> +${error("global variable liste does not exist")} + \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/GlobalVars.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/GlobalVars.ftl new file mode 100644 index 0000000000..9aa08224fb --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/GlobalVars.ftl @@ -0,0 +1,12 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +<#if getGlobalVar("test")??> +${getGlobalVar("test")} +<#else> +${error("global test variable does not exist")} + + +<#if getGlobalVar("asd")??> +${getGlobalVar("asd")} +<#else> +${error("global asd variable does not exist")} + \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/HelloWorld.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/HelloWorld.ftl new file mode 100644 index 0000000000..2944881020 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/HelloWorld.ftl @@ -0,0 +1,2 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +Hello World! \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/HookCall.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/HookCall.ftl new file mode 100644 index 0000000000..9afa713e1a --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/HookCall.ftl @@ -0,0 +1,2 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${defineHookPoint("hp")} \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/HowAreYou.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/HowAreYou.ftl new file mode 100644 index 0000000000..13bee70dfb --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/HowAreYou.ftl @@ -0,0 +1,2 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +How Are You? \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/Log.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/Log.ftl new file mode 100644 index 0000000000..91a63f2c7a --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/Log.ftl @@ -0,0 +1,4 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${tc.info("A message from a template.", "a.template")} +A +${tc.warn("A warning from a template.")} \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/NestedSignatureCalls.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/NestedSignatureCalls.ftl new file mode 100644 index 0000000000..5add041cb1 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/NestedSignatureCalls.ftl @@ -0,0 +1,3 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${tc.signature("name")}<#t> +${name} -> ${tc.includeArgs("SignatureWithOneParameter", ["T2"])} \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/ParameterizedInclusionUsage.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/ParameterizedInclusionUsage.ftl new file mode 100644 index 0000000000..34555ca880 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/ParameterizedInclusionUsage.ftl @@ -0,0 +1,4 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${tc.includeArgs("SignatureWithOneParameter", "Charly")}<#-- one argument (no list) --> +${tc.includeArgs("SignatureWithThreeParameters","Charly", "30", "Aachen")}<#-- two arguments (no list) --> +${tc.includeArgs("SignatureWithManyParameters",["Charly", "30", "Aachen", "52062", "Engineer", "No friends"])}<#-- 6 arguments (list) --> \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/PrintNameIfExists.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/PrintNameIfExists.ftl new file mode 100644 index 0000000000..91e5a9cfe2 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/PrintNameIfExists.ftl @@ -0,0 +1,6 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +<#if name??> +${name} is a beautiful name.<#t> +<#else> +Sorry, what was your name?<#t> + \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/SignatureWithManyParameters.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/SignatureWithManyParameters.ftl new file mode 100644 index 0000000000..293f3870c8 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/SignatureWithManyParameters.ftl @@ -0,0 +1,3 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${tc.signature(["name", "age", "city", "zip", "job", "friends"])}<#t> +Name=${name}, age=${age}, city=${city}, zip=${zip}, job=${job}, friends=${friends} \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/SignatureWithOneParameter.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/SignatureWithOneParameter.ftl new file mode 100644 index 0000000000..def4feac1f --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/SignatureWithOneParameter.ftl @@ -0,0 +1,3 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${tc.signature("name")}<#t> +Name is ${name} \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/SignatureWithThreeParameters.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/SignatureWithThreeParameters.ftl new file mode 100644 index 0000000000..33d6cfc98a --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/SignatureWithThreeParameters.ftl @@ -0,0 +1,3 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${tc.signature("name", "age", "city")}<#t> +Name is ${name}, age is ${age}, city is ${city} \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/TestVariables4.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/TestVariables4.ftl new file mode 100644 index 0000000000..8a458fa497 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/TestVariables4.ftl @@ -0,0 +1,6 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${glex.defineGlobalVar("v1",16)} +${glex.defineGlobalVar("v2",glex.getGlobalVar("v1",333)+22)} +A:${glex.getGlobalVar("v1",553)} +B:${glex.getGlobalVar("v2",554)} +C:${glex.getGlobalVar("vX",555)} diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/TopA.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/TopA.ftl new file mode 100644 index 0000000000..6a6d85afe2 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/TopA.ftl @@ -0,0 +1,2 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +TopA ${tc.include("de.monticore.generating.templateengine.templates.A")} \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/TopStringHook.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/TopStringHook.ftl new file mode 100644 index 0000000000..86c2f6b656 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/TopStringHook.ftl @@ -0,0 +1,2 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${glex.bindHookPoint("hp", tc.instantiate("de.monticore.generating.templateengine.StringHookPoint", ["Hello Brave New World!"]))}TopStringHook ${tc.include("de.monticore.generating.templateengine.templates.HookCall")} \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/TopTemplateHook.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/TopTemplateHook.ftl new file mode 100644 index 0000000000..1d98174890 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/TopTemplateHook.ftl @@ -0,0 +1,2 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${glex.bindHookPoint("hp",tc.instantiate("de.monticore.generating.templateengine.TemplateHookPoint", ["de.monticore.generating.templateengine.templates.A"])) }TopTemplateHook ${tc.include("de.monticore.generating.templateengine.templates.HookCall")} \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/AddToGlobalVarAlias.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/AddToGlobalVarAlias.ftl new file mode 100644 index 0000000000..2bc41d9078 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/AddToGlobalVarAlias.ftl @@ -0,0 +1,4 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${addToGlobalVar("a", "item 1")} +${addToGlobalVar("a", "item 2")} +${addToGlobalVar("b", "item 3")} \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/BindHookPointAlias.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/BindHookPointAlias.ftl new file mode 100644 index 0000000000..4f08f63797 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/BindHookPointAlias.ftl @@ -0,0 +1,4 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${signature("alternativeAst")}<#t> +${bindHookPoint("WithoutAst", tc.instantiate("de.monticore.generating.templateengine.StringHookPoint", ["bound"]))}<#t> +${includeArgs("DefineHookPointAlias", alternativeAst )} \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/ChangeGlobalVarAlias.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/ChangeGlobalVarAlias.ftl new file mode 100644 index 0000000000..602dbd2b67 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/ChangeGlobalVarAlias.ftl @@ -0,0 +1,2 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${changeGlobalVar("a", "changed")} \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/DefineGlobalVarAlias.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/DefineGlobalVarAlias.ftl new file mode 100644 index 0000000000..3b7b6a5ec8 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/DefineGlobalVarAlias.ftl @@ -0,0 +1,2 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${defineGlobalVar("a", "defined")} \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/DefineHookPointAlias.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/DefineHookPointAlias.ftl new file mode 100644 index 0000000000..f0ff814d65 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/DefineHookPointAlias.ftl @@ -0,0 +1,5 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${signature("alternativeAst")}<#t> +${defineHookPoint("WithoutAst")} +${defineHookPoint("WithAst", ast)} +${defineHookPoint("WithAlternativeAst", alternativeAst)} \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/DefineHookPointWithDefaultAlias.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/DefineHookPointWithDefaultAlias.ftl new file mode 100644 index 0000000000..b5a30e3002 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/DefineHookPointWithDefaultAlias.ftl @@ -0,0 +1,5 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${signature("alternativeAst")}<#t> +${defineHookPointWithDefault("WithoutAst", "default text 1")} +${defineHookPointWithDefault3("WithAst", ast, "default text 2")} +${defineHookPointWithDefault3("WithAlternativeAst", alternativeAst, "default text 3")} \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/ExistsHookPointAlias.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/ExistsHookPointAlias.ftl new file mode 100644 index 0000000000..acb5934c5c --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/ExistsHookPointAlias.ftl @@ -0,0 +1,3 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${existsHookPoint("hp1")?c} +${existsHookPoint("hp2")?c} \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/GetGlobalVarAlias.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/GetGlobalVarAlias.ftl new file mode 100644 index 0000000000..62815e753d --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/GetGlobalVarAlias.ftl @@ -0,0 +1,2 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${getGlobalVar("a")!"unset"} \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/GlobalListVarsAlias.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/GlobalListVarsAlias.ftl new file mode 100644 index 0000000000..a48a8e5e51 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/GlobalListVarsAlias.ftl @@ -0,0 +1,8 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +<#if getGlobalVar("liste")??> + <#list getGlobalVar("liste") as l> + ${l} + +<#else> +${error("global variable liste does not exist")} + \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/Include2Alias.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/Include2Alias.ftl new file mode 100644 index 0000000000..4bcf2c12ed --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/Include2Alias.ftl @@ -0,0 +1,2 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${include2("WithAst", ast)}<#t> \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/IncludeAlias.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/IncludeAlias.ftl new file mode 100644 index 0000000000..077690b9ef --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/IncludeAlias.ftl @@ -0,0 +1,2 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${include("Plain")}<#t> \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/IncludeArgsAlias.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/IncludeArgsAlias.ftl new file mode 100644 index 0000000000..7ff425a705 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/IncludeArgsAlias.ftl @@ -0,0 +1,2 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${includeArgs("WithAst", ast)} \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/IncludeArgsAndSignatureAlias.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/IncludeArgsAndSignatureAlias.ftl new file mode 100644 index 0000000000..7d06b3f393 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/IncludeArgsAndSignatureAlias.ftl @@ -0,0 +1,2 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${includeArgs("de.monticore.generating.templateengine.templates.aliases.SignatureAliasWithThreeParameters", "Charly", "30", "Aachen")} \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/IncludeDispatching.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/IncludeDispatching.ftl new file mode 100644 index 0000000000..7f57422fc3 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/IncludeDispatching.ftl @@ -0,0 +1,12 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +<#-- old include macro, renamed --> +<#function includeOld templ> + <#return tc.include(templ)> + +String argument +${includeOld("Plain")} +${include("Plain")} + +List argument +${includeOld(["Plain", "Plain"])} +${include(["Plain", "Plain"])} \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/LogAliases.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/LogAliases.ftl new file mode 100644 index 0000000000..3e1e50d6be --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/LogAliases.ftl @@ -0,0 +1,6 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${trace("Trace Message", "Trace Logger")} +${debug("Debug Message", "Debug Logger")} +${info("Info Message", "Info Logger")} +${warn("Warn Message")} +${error("Error Message")} \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/Plain.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/Plain.ftl new file mode 100644 index 0000000000..79631b6c3e --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/Plain.ftl @@ -0,0 +1,2 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +Plain is included. \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/RequiredGlobalVarAlias.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/RequiredGlobalVarAlias.ftl new file mode 100644 index 0000000000..549e9c2372 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/RequiredGlobalVarAlias.ftl @@ -0,0 +1,2 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${requiredGlobalVar("a")} \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/RequiredGlobalVarsAlias.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/RequiredGlobalVarsAlias.ftl new file mode 100644 index 0000000000..389b55c7b1 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/RequiredGlobalVarsAlias.ftl @@ -0,0 +1,2 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${requiredGlobalVars("a", "b", "c")} \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/SignatureAliasWithThreeParameters.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/SignatureAliasWithThreeParameters.ftl new file mode 100644 index 0000000000..485551cdd2 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/SignatureAliasWithThreeParameters.ftl @@ -0,0 +1,3 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${signature("name", "age", "city")}<#t> +Name is ${name}, age is ${age}, city is ${city} \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/WithAst.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/WithAst.ftl new file mode 100644 index 0000000000..a4d4c2b4eb --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templateengine/templates/aliases/WithAst.ftl @@ -0,0 +1,2 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${ast.getContent()}<#t> \ No newline at end of file diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templates/ClassProd.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templates/ClassProd.ftl new file mode 100644 index 0000000000..de011c3138 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templates/ClassProd.ftl @@ -0,0 +1,2 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +ClassProd: ${ast.getName()} diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templates/First.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templates/First.ftl new file mode 100644 index 0000000000..aae4160f4d --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templates/First.ftl @@ -0,0 +1,2 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +Hello, my name is First. diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templates/GrammarTest.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templates/GrammarTest.ftl new file mode 100644 index 0000000000..840479a990 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templates/GrammarTest.ftl @@ -0,0 +1,2 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +Grammar: ${ast.getName()} diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templates/InterfaceProd.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templates/InterfaceProd.ftl new file mode 100644 index 0000000000..aaf4236b4e --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templates/InterfaceProd.ftl @@ -0,0 +1,2 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +InterfaceProd: ${ast.getName()} diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templates/MainClassProd.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templates/MainClassProd.ftl new file mode 100644 index 0000000000..a0a83f21f5 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templates/MainClassProd.ftl @@ -0,0 +1,2 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${tc.write("ClassProd", "path.of." + ast.getName(), ast)} diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templates/MainGrammar.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templates/MainGrammar.ftl new file mode 100644 index 0000000000..31efeacfa2 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templates/MainGrammar.ftl @@ -0,0 +1,2 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${tc.write("GrammarTest", "path.of." + ast.getName(), ast)} diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templates/MainInterfaceProd.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templates/MainInterfaceProd.ftl new file mode 100644 index 0000000000..727f49e3e9 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templates/MainInterfaceProd.ftl @@ -0,0 +1,2 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${tc.write("InterfaceProd", "path.of." + ast.getName(), ast)} diff --git a/monticore-runtime/src/test/java/de/monticore/generating/templates/MainTest.ftl b/monticore-runtime/src/test/java/de/monticore/generating/templates/MainTest.ftl new file mode 100644 index 0000000000..dbc0f70f12 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/generating/templates/MainTest.ftl @@ -0,0 +1,2 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${tc.write("First", "path.of.file", ast)} diff --git a/monticore-runtime/src/test/java/de/monticore/io/FileFinderTest.java b/monticore-runtime/src/test/java/de/monticore/io/FileFinderTest.java new file mode 100644 index 0000000000..7280e4634f --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/io/FileFinderTest.java @@ -0,0 +1,311 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.io; + + +import de.monticore.io.paths.MCPath; +import de.se_rwth.commons.Names; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.apache.commons.io.filefilter.RegexFileFilter; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.File; +import java.io.FileFilter; +import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class FileFinderTest { + + @Before + public void init() { + Log.init(); + Log.enableFailQuick(false); + } + + @Before + public void setup() { + Log.clearFindings(); + } + + @Test + public void testGetFiles1() { + // getFiles() should find 4 Models, using Regex + String fileExt = ".*sym"; + String qualifiedModelName = "de.monticore.io.Model1"; + List entries = new ArrayList<>(); + entries.add(Paths.get("src","test","models")); + entries.add(Paths.get("src","test","resources")); + MCPath mp = new MCPath(entries); + List files = find(mp, qualifiedModelName, fileExt); + assertEquals(4, files.size()); + List absolutePath = files.stream().map(f ->f.toString()).collect(Collectors.toList()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testGetFiles2() { + //getFiles() should find 1 Model, no regex. + String fileExt = "mc4"; + String qualifiedModelName = "de.monticore.io.Model2"; + List entries = new ArrayList<>(); + entries.add(Paths.get("src", "test", "models")); + entries.add(Paths.get("src", "test", "resources")); + MCPath mp = new MCPath(entries); + Optional file = mp.find(qualifiedModelName, fileExt); + assertTrue(file.isPresent()); + assertTrue(file.get().toString().endsWith("de/monticore/io/Model2.mc4")); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testGetFiles3() { + // getFiles should find no Files. + String fileExt = "mc4"; + String qualifiedModelName = "de.monticore.io.Model23"; + List entries = new ArrayList<>(); + entries.add(Paths.get("src", "test", "models")); + entries.add(Paths.get("src", "test", "resources")); + MCPath mp = new MCPath(entries); + Optional url = mp.find(qualifiedModelName, fileExt); + assertFalse(url.isPresent()); + assertTrue(Log.getFindings().isEmpty()); + } + + private void assertFalse(boolean present) { + } + + @Test + public void testGetFiles4() { + //getFiles() should find 2 Models, using correct File extension. + String fileExt = "cdsym"; + String qualifiedModelName = "de.monticore.io.Model1"; + List entries = new ArrayList<>(); + entries.add(Paths.get("src","test","models")); + entries.add(Paths.get("src","test","resources")); + MCPath mp = new MCPath(entries); + List files = find(mp, qualifiedModelName, fileExt); + assertEquals(2, files.size()); + List absolutePath = files.stream().map(f ->f.toString()).collect(Collectors.toList()); + assertTrue(absolutePath.get(0).endsWith("de/monticore/io/Model1.cdsym")); + assertTrue(absolutePath.get(1).endsWith("de/monticore/io/Model1.cdsym")); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testGetFiles5() { + // getFiles() should not find any Models, using a Wrong File Extension + String fileExt = "fdsym"; + String qualifiedModelName = "de.monticore.io.Model1"; + List entries = new ArrayList<>(); + entries.add(Paths.get("src", "test", "models")); + entries.add(Paths.get("src", "test", "resources")); + MCPath mp = new MCPath(entries); + Optional url = mp.find(qualifiedModelName, fileExt); + assertFalse(url.isPresent()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testGetFiles6(){ + //getFiles() should find 4 Models, using Regex. + String fileExt = ".*dsym"; + String qualifiedModelName = "de.monticore.io.Model1"; + List entries = new ArrayList<>(); + entries.add(Paths.get("src","test","models")); + entries.add(Paths.get("src","test","resources")); + MCPath mp = new MCPath(entries); + List files = find(mp, qualifiedModelName, fileExt); + assertEquals(4, files.size()); + List absolutePath = files.stream().map(f ->f.toString()).collect(Collectors.toList()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testGetFiles7() { + // getFiles() should not find any Models, using a Wrong File Extension + String fileExt = "xcdsym"; + String qualifiedModelName = "de.monticore.io.Model1"; + List entries = new ArrayList<>(); + entries.add(Paths.get("src", "test", "models")); + entries.add(Paths.get("src", "test", "resources")); + MCPath mp = new MCPath(entries); + Optional url = mp.find(qualifiedModelName, fileExt); + assertFalse(url.isPresent()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testGetFiles8() { + // getFiles() should find 0 Models, using Regex + String fileExt = "sym"; + String qualifiedModelName = "de.monticore.io.Model1"; + List entries = new ArrayList<>(); + entries.add(Paths.get("src","test","models")); + entries.add(Paths.get("src","test","resources")); + MCPath mp = new MCPath(entries); + List files = find(mp, qualifiedModelName, fileExt); + assertEquals(0, files.size()); + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void testFindFiles1() { + //findFiles() throws an Error because it finds more than 1 Model. + String fileExt = ".*sym"; + String qualifiedModelName = "de.monticore.io.Model1"; + List entries = new ArrayList<>(); + entries.add(Paths.get("src","test","models")); + entries.add(Paths.get("src","test","resources")); + MCPath mp = new MCPath(entries); + Optional files = mp.find(qualifiedModelName, fileExt); + assertTrue(Log.getFindings().get(0).getMsg().startsWith("0xA1294")); + + } + + @Test + public void testFindFiles2() { + //findFiles() finds a Model, no previously loaded Files. + String fileExt = "mc4"; + String qualifiedModelName = "de.monticore.io.Model2"; + List entries = new ArrayList<>(); + entries.add(Paths.get("src","test","models")); + entries.add(Paths.get("src","test","resources")); + MCPath mp = new MCPath(entries); + Optional files = mp.find(qualifiedModelName, fileExt); + assertTrue(files.isPresent()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testFindFiles3() { + //findFiles() finds no Model because it's already loaded. + String fileExt = "mc4"; + String qualifiedModelName = "de.monticore.io.Model2"; + List entries = new ArrayList<>(); + entries.add(Paths.get("src","test","models")); + entries.add(Paths.get("src","test","resources")); + MCPath mp = new MCPath(entries); + Optional files = mp.find(qualifiedModelName, fileExt); + assertFalse(files.isPresent()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testFindFiles4() { + //findFiles() test with wrong Model name. + String fileExt = "mc4"; + String qualifiedModelName = "de.monticore.io.Model22"; + List entries = new ArrayList<>(); + entries.add(Paths.get("src","test","models")); + entries.add(Paths.get("src","test","resources")); + MCPath mp = new MCPath(entries); + Optional files = mp.find(qualifiedModelName, fileExt); + assertFalse(files.isPresent()); + assertTrue(Log.getFindings().isEmpty()); + } + + + @Test + public void testFindFiles5() { + //fileFiles() finds 2 Models, with the same Name but in different ModelPath-Entries. + String fileExt = ".*4"; + String qualifiedModelName = "de.monticore.io.Model2"; + List entries = new ArrayList<>(); + entries.add(Paths.get("src","test","models")); + entries.add(Paths.get("src","test","resources")); + MCPath mp = new MCPath(entries); + List files = find(mp, qualifiedModelName, fileExt); + assertEquals(2, files.size()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testFindFile1() { + //findFile() throws an Error because it expects 1 Models and finds 2 instead. + String fileExt = ".*4"; + String qualifiedModelName = "de.monticore.io.Model2"; + List entries = new ArrayList<>(); + entries.add(Paths.get("src","test","models")); + entries.add(Paths.get("src","test","resources")); + MCPath mp = new MCPath(entries); + Optional files = mp.find(qualifiedModelName, fileExt); + assertTrue(Log.getFindings().get(0).getMsg().startsWith("0xA1294")); + } + + @Test + public void testFindFile2() { + //findFile() finds 1 Model. + String fileExt = "mc4"; + String qualifiedModelName = "de.monticore.io.Model2"; + List entries = new ArrayList<>(); + entries.add(Paths.get("src","test","models")); + entries.add(Paths.get("src","test","resources")); + MCPath mp = new MCPath(entries); + Optional files = mp.find(qualifiedModelName, fileExt); + assertTrue(files.isPresent()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testFindFile3() { + //findFile does not find any Model because it's already loaded. + String fileExt = "mc4"; + String qualifiedModelName = "de.monticore.io.Model2"; + List entries = new ArrayList<>(); + entries.add(Paths.get("src","test","models")); + entries.add(Paths.get("src","test","resources")); + MCPath mp = new MCPath(entries); + Optional files = mp.find(qualifiedModelName, fileExt); + assertFalse(files.isPresent()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testFindFile4() { + //findFile() does not find any Model, Wrong Model name. + String fileExt = "mc4"; + String qualifiedModelName = "de.monticore.io.Model22"; + List entries = new ArrayList<>(); + entries.add(Paths.get("src","test","models")); + entries.add(Paths.get("src","test","resources")); + MCPath mp = new MCPath(entries); + Optional files = mp.find(qualifiedModelName, fileExt); + assertFalse(files.isPresent()); + assertTrue(Log.getFindings().isEmpty()); + } + + public List find(MCPath mp, String qualifiedName, String fileExtRegEx) { + // calculate the folderPath (e.g., "foo/bar") and fileNameRegEx (e.g., "Car.*sym") + String folderPath = Names.getPathFromQualifiedName(qualifiedName); + String fileNameRegEx = Names.getSimpleName(qualifiedName) + "\\." + fileExtRegEx; + + // initialize a file filter filtering for the regular expression + FileFilter filter = new RegexFileFilter(fileNameRegEx); + + List resolvedURLs = new ArrayList<>(); + for (Path p : mp.getEntries()) { + File folder = p.resolve(folderPath).toFile(); //e.g., "src/test/resources/foo/bar" + if (folder.exists() && folder.isDirectory()) { + // perform the actual file filter on the folder and collect result + Arrays.stream(folder.listFiles(filter)) + .map(f -> mp.toURL(folder.toPath().resolve(f.getName()))) + .filter(Optional::isPresent) + .forEach(f -> resolvedURLs.add(f.get())); + } + } + return resolvedURLs; + } +} + diff --git a/monticore-runtime/src/test/java/de/monticore/io/FileReaderWriterMock.java b/monticore-runtime/src/test/java/de/monticore/io/FileReaderWriterMock.java new file mode 100644 index 0000000000..dbea794949 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/io/FileReaderWriterMock.java @@ -0,0 +1,39 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.io; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; + +import java.util.Optional; +import com.google.common.collect.Maps; + +import de.monticore.io.FileReaderWriter; + +public class FileReaderWriterMock extends FileReaderWriter { + + private Map storedFilesAndContents = Maps.newHashMap(); + + /** + * @see de.monticore.io.FileReaderWriter#_storeInFile(java.nio.file.Path, String) + */ + @Override + public void _storeInFile(Path targetPath, String content) { + storedFilesAndContents.put(targetPath, content); + } + + public Optional getContentForFile(String filePath) { + Path p = Paths.get(filePath); + if (storedFilesAndContents.containsKey(p)) { + return Optional.of(storedFilesAndContents.get(p)); + } + + return Optional.empty(); + } + + public Map getStoredFilesAndContents() { + return this.storedFilesAndContents; + } + +} diff --git a/monticore-runtime/src/test/java/de/monticore/io/FileReaderWriterTest.java b/monticore-runtime/src/test/java/de/monticore/io/FileReaderWriterTest.java new file mode 100644 index 0000000000..ffaabcd558 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/io/FileReaderWriterTest.java @@ -0,0 +1,53 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.io; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +public class FileReaderWriterTest { + + Path testPath = Paths.get("target/test/FileHandlertest.txt"); + + String testContent = "Hello World"; + + @Before + public void before() { + LogStub.init(); + Log.enableFailQuick(false); + } + + @After + public void tearDown() { + try { + Files.deleteIfExists(testPath); + } + catch (IOException e) { + throw new IllegalStateException(e); + } + } + + /** + * Implicitly tests read and write in one assert. + */ + @Test + public void testFileHandler() { + FileReaderWriter.init(); + FileReaderWriter.storeInFile(testPath, testContent); + assertEquals(testContent, FileReaderWriter.readFromFile(testPath)); + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-runtime/src/test/java/de/monticore/io/paths/MCPathTest.java b/monticore-runtime/src/test/java/de/monticore/io/paths/MCPathTest.java new file mode 100644 index 0000000000..5f0b9defff --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/io/paths/MCPathTest.java @@ -0,0 +1,198 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.io.paths; + +import de.se_rwth.commons.logging.Finding; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; + +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import static org.junit.Assert.*; + +public class MCPathTest { + + @Before + public void setup(){ + Log.init(); + Log.enableFailQuick(false); + } + + @Test + public void testFindWithOneArgument(){ + MCPath mp = new MCPath(); + mp.addEntry(Paths.get("src/test/resources")); + Optional logback = mp.find("logback.groovy"); + Optional grammar = mp.find("de/monticore/io/Model3.mc4"); + Optional nonExistent = mp.find("Test.mc4"); + + mp.addEntry(Paths.get("src/test/resources/jar/Test.jar")); + Optional fileInJar = mp.find("de/monticore/MCBasics.mc4"); + assertTrue(logback.isPresent()); + assertTrue(grammar.isPresent()); + assertTrue(fileInJar.isPresent()); + assertFalse(nonExistent.isPresent()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testFindWithTwoArguments(){ + MCPath mp = new MCPath(); + mp.addEntry(Paths.get("src/test/resources")); + Optional logback = mp.find("logback", "groovy"); + Optional grammar = mp.find("de.monticore.io.Model3", "mc4"); + Optional nonExistent = mp.find("Test", "mc4"); + Optional logback2 = mp.find("logback", "g.*"); + Optional grammar2 = mp.find("de.monticore.io.Model3", ".*4"); + Optional nonExistent2 = mp.find("Test", "m.4"); + + assertTrue(logback.isPresent()); + assertTrue(grammar.isPresent()); + assertFalse(nonExistent.isPresent()); + + assertTrue(logback2.isPresent()); + assertTrue(grammar2.isPresent()); + assertFalse(nonExistent2.isPresent()); + + mp.addEntry(Paths.get("src/test/resources/jar/Test.jar")); + Optional fileInJar = mp.find("de.monticore.MCBasics", "mc4"); + Optional fileInJar2 = mp.find("de.monticore.MCBasics", "m.4"); + Optional fileNotInJar = mp.find("MCBasics", "m.4"); + assertTrue(fileInJar.isPresent()); + assertTrue(fileInJar2.isPresent()); + assertFalse(fileNotInJar.isPresent()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testAddEntry(){ + MCPath mp = new MCPath(); + Path resources = Paths.get("src/test/resources"); + Path models = Paths.get("src/test/models"); + mp.addEntry(resources); + assertEquals(1, mp.getEntries().size()); + mp.addEntry(models); + assertEquals(2, mp.getEntries().size()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testRemoveEntry(){ + MCPath mp = new MCPath(); + Path resources = Paths.get("src/test/resources"); + mp.addEntry(resources); + assertEquals(1, mp.getEntries().size()); + mp.removeEntry(resources); + assertTrue(mp.isEmpty()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testGetEntries(){ + MCPath mp = new MCPath(); + Path resources = Paths.get("src/test/resources"); + Path models = Paths.get("src/test/models"); + Path java = Paths.get("src/test/java"); + mp.addEntry(resources); + mp.addEntry(models); + mp.addEntry(java); + Collection paths = mp.getEntries(); + assertEquals(3, paths.size()); + assertTrue(paths.contains(resources.toAbsolutePath())); + assertTrue(paths.contains(models.toAbsolutePath())); + assertTrue(paths.contains(java.toAbsolutePath())); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testIsEmpty(){ + MCPath mp = new MCPath(); + assertTrue(mp.isEmpty()); + Path resources = Paths.get("src/test/resources"); + mp.addEntry(resources); + assertFalse(mp.isEmpty()); + mp.removeEntry(resources); + assertTrue(mp.isEmpty()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testToString() throws MalformedURLException { + MCPath mp = new MCPath(); + Path resources = Paths.get("src/test/resources"); + Path models = Paths.get("src/test/models"); + mp.addEntry(resources); + mp.addEntry(models); + assertEquals("[" + resources.toUri().toURL().toString() + ", " + + models.toUri().toURL().toString() + "]" ,mp.toString()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testToPath() throws MalformedURLException { + Path resources = Paths.get("src/test/resources"); + URL url = resources.toUri().toURL(); + Optional result = MCPath.toPath(url); + assertTrue(result.isPresent()); + assertEquals(result.get(), resources.toAbsolutePath()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testToURL() throws URISyntaxException { + Path resources = Paths.get("src/test/resources"); + Optional result = MCPath.toURL(resources); + assertTrue(result.isPresent()); + assertEquals(result.get().toURI(), resources.toUri()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testReportAmbiguity() throws MalformedURLException { + List urlList = new ArrayList<>(); + URL resources = Paths.get("src/test/resources").toUri().toURL(); + urlList.add(resources); + urlList.add(resources); + MCPath.reportAmbiguity(urlList, "src/test/resources"); + List findings = Log.getFindings().stream().filter(f -> f.getMsg().startsWith("0xA1294")).collect(Collectors.toList()); + assertEquals(1, findings.size()); + assertEquals("0xA1294 The following entries for the file `" + "src/test/resources" + "` are ambiguous:" + + "\n" + "{" + resources.toString() + ",\n" + resources.toString() + "}", findings.get(0).getMsg()); + } + @Test + @Ignore("$JAVA_HOME must be set & might be an arbitrary version (e.g. intelliJ can use its own JDK)." + + "Path to might differ between java version") + public void testShouldFind(){ + String jdk = System.getenv("JAVA_HOME").replaceAll("\\\\", "/") + "/jre/lib/rt.jar"; + MCPath mp = new MCPath(jdk); + assertTrue(mp.find("java/util/List.class").isPresent()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testShouldNotFind(){ + MCPath mp = new MCPath(""); + assertFalse(mp.find("java/util/List.class").isPresent()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testShouldNotFind2(){ + MCPath mp = new MCPath("this/is/a/test"); + assertFalse(mp.find("java/util/List.class").isPresent()); + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-runtime/src/test/java/de/monticore/symboltable/serialization/JsonLexerTest.java b/monticore-runtime/src/test/java/de/monticore/symboltable/serialization/JsonLexerTest.java new file mode 100644 index 0000000000..3535b074a3 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/symboltable/serialization/JsonLexerTest.java @@ -0,0 +1,144 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symboltable.serialization; + +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import static de.monticore.symboltable.serialization.JsonTokenKind.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class JsonLexerTest { + + @Before + public void before() { + LogStub.init(); + Log.enableFailQuick(false); + } + + @Test + public void testNumberWhitespaces() { + JsonLexer lexer = new JsonLexer("12.5 3e9"); + assertEquals("12.5", lexer.poll().getValue()); + assertEquals(WHITESPACE, lexer.poll().getKind()); + assertEquals("3e9", lexer.poll().getValue()); + assertEquals(false, lexer.hasNext()); + + lexer = new JsonLexer("1\t2.5"); + assertEquals("1", lexer.poll().getValue()); + assertEquals(WHITESPACE, lexer.poll().getKind()); + assertEquals("2.5", lexer.poll().getValue()); + assertEquals(false, lexer.hasNext()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testJsonObjects() { + JsonLexer lexer = new JsonLexer("{\"foo\":\"b a r\"}"); + assertEquals(BEGIN_OBJECT, lexer.poll().getKind()); + assertEquals(STRING, lexer.poll().getKind()); + assertEquals(COLON, lexer.poll().getKind()); + assertEquals(STRING, lexer.poll().getKind()); + assertEquals(END_OBJECT, lexer.poll().getKind()); + assertEquals(false, lexer.hasNext()); + + lexer = new JsonLexer("{\"a\":2,\"b\":false}"); + assertEquals(BEGIN_OBJECT, lexer.poll().getKind()); + assertEquals(STRING, lexer.poll().getKind()); + assertEquals(COLON, lexer.poll().getKind()); + assertEquals(NUMBER, lexer.poll().getKind()); + assertEquals(COMMA, lexer.poll().getKind()); + assertEquals(STRING, lexer.poll().getKind()); + assertEquals(COLON, lexer.poll().getKind()); + assertEquals(BOOLEAN, lexer.poll().getKind()); + assertEquals(END_OBJECT, lexer.poll().getKind()); + assertEquals(false, lexer.hasNext()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testJsonArray() { + JsonLexer lexer = new JsonLexer("[\"fo\\\"o\",\"\\b\\\\ar\"]"); + assertEquals(BEGIN_ARRAY, lexer.poll().getKind()); + assertEquals(STRING, lexer.poll().getKind()); + assertEquals(COMMA, lexer.poll().getKind()); + assertEquals(STRING, lexer.poll().getKind()); + assertEquals(END_ARRAY, lexer.poll().getKind()); + assertEquals(false, lexer.hasNext()); + + lexer = new JsonLexer("[\"\\\"a\",-2.4e-5,\"b\",null]"); + assertEquals(BEGIN_ARRAY, lexer.poll().getKind()); + assertEquals(STRING, lexer.poll().getKind()); + assertEquals(COMMA, lexer.poll().getKind()); + assertEquals(NUMBER, lexer.poll().getKind()); + assertEquals(COMMA, lexer.poll().getKind()); + assertEquals(STRING, lexer.poll().getKind()); + assertEquals(COMMA, lexer.poll().getKind()); + assertEquals(NULL, lexer.poll().getKind()); + assertEquals(END_ARRAY, lexer.poll().getKind()); + assertEquals(false, lexer.hasNext()); + + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testSinglelexer() { + checkSingleToken(":", COLON); + checkSingleToken(",", COMMA); + checkSingleToken("null", NULL); + checkSingleToken("[", BEGIN_ARRAY); + checkSingleToken("]", END_ARRAY); + checkSingleToken("{", BEGIN_OBJECT); + checkSingleToken("}", END_OBJECT); + checkSingleToken(" ", WHITESPACE); + checkSingleToken(" \t\n ", WHITESPACE); + checkSingleToken("true", BOOLEAN); + checkSingleToken("false", BOOLEAN); + checkSingleToken("0", NUMBER); + checkSingleToken("10.23", NUMBER); + checkSingleToken("-1.2", NUMBER); + checkSingleToken("-10e5", NUMBER); + checkSingleToken("10E50", NUMBER); + checkSingleToken("-10e-5", NUMBER); + checkSingleToken("10E-50", NUMBER); + checkSingleToken("-10e+5", NUMBER); + checkSingleToken("123456789E+50", NUMBER); + checkSingleToken("-10.2e+5", NUMBER); + checkSingleToken("-2.4e-5", NUMBER); + checkSingleToken("\"foo\"", STRING); + checkSingleToken("\"foo\"", STRING); + checkSingleToken("\"true\"", STRING); + checkSingleToken("\"foo foo \"", STRING); + checkSingleToken("\"\\u1234\"", STRING); + checkSingleToken("\"\\u12345\"", STRING); + checkSingleToken("\"\\b\"", STRING); + checkSingleToken("\"\\f\"", STRING); + checkSingleToken("\"\\n\"", STRING); + checkSingleToken("\"\\r\"", STRING); + checkSingleToken("\"\\t\"", STRING); + checkSingleToken("\"\\\"\"", STRING); + checkSingleToken("\"foo \\b\\f\\r\\n\\t\\\" foo \"", STRING); + + assertTrue(Log.getFindings().isEmpty()); + } + + protected void checkSingleToken(String json, JsonTokenKind expectedTokenKind) { + JsonLexer lexer = new JsonLexer(json); + JsonToken actual = lexer.poll(); + assertEquals(expectedTokenKind, actual.getKind()); + if (actual.getKind().hasValue()) { + String value = actual.getValue(); + if (actual.getKind() == STRING) { + value = "\"" + value + "\""; + } + assertEquals(json, value); + } + assertEquals(false, lexer.hasNext()); + } + +} diff --git a/monticore-runtime/src/test/java/de/monticore/symboltable/serialization/JsonParserTest.java b/monticore-runtime/src/test/java/de/monticore/symboltable/serialization/JsonParserTest.java new file mode 100644 index 0000000000..6839b70a18 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/symboltable/serialization/JsonParserTest.java @@ -0,0 +1,82 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symboltable.serialization; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import de.monticore.symboltable.serialization.json.JsonObject; + +import java.util.function.Function; + +public class JsonParserTest { + + @Before + public void before() { + LogStub.init(); + Log.enableFailQuick(false); + } + + @Test + public void testSimpleInvalidObject() { + Log.enableFailQuick(false); + testParseErroneousJson(JsonParser::parseJsonObject,"{\"foo\":false,,\"bar\":3}"); + testParseErroneousJson(JsonParser::parseJsonObject,"{\"foo\":false,\"bar\":3,}"); + testParseErroneousJson(JsonParser::parseJsonObject,"{\"foo\" :false\"bar\":3}"); + testParseErroneousJson(JsonParser::parseJsonObject,"{\"foo\"false}"); + testParseErroneousJson(JsonParser::parseJsonObject,"{4711}"); + testParseErroneousJson(JsonParser::parseJsonObject,"{false"); + testParseErroneousJson(JsonParser::parseJsonObject,"{\"false\" : \"noending\""); + Log.getFindings().clear(); + } + + @Test + public void testSimpleInvalidArray() { + Log.enableFailQuick(false); + testParseErroneousJson(JsonParser::parseJsonArray, "[\"foo\",false,,\"bar\":3]"); + testParseErroneousJson(JsonParser::parseJsonArray,"[\"foo\",false,\"bar\":3,]"); + testParseErroneousJson(JsonParser::parseJsonArray,"[\"foo\" ,false \"bar\" 3]"); + testParseErroneousJson(JsonParser::parseJsonArray,"[\"foo\"false]"); + testParseErroneousJson(JsonParser::parseJsonArray,"[47 11]"); + testParseErroneousJson(JsonParser::parseJsonArray,"[,false"); + testParseErroneousJson(JsonParser::parseJsonArray,"[fal se"); + testParseErroneousJson(JsonParser::parseJsonArray,"[\"false\", \"noending\""); + Log.getFindings().clear(); + } + + protected void testParseErroneousJson(Function f, String json){ + Log.getFindings().clear(); + try{ + f.apply(json); + } + catch (RuntimeException e){ + //Ignore exceptions that occur because fail quick is disabled + } + assertTrue(Log.getErrorCount()>0); + } + + @Test + public void testSimpleObject() { + JsonObject result = JsonParser.parseJsonObject("{\"foo\":false,\"bar\":3,\"bla\":\"yes\",\"blub\":3.4}"); + assertTrue(result.getMember("foo").isJsonBoolean()); + assertEquals(false, result.getMember("foo").getAsJsonBoolean().getValue()); + + assertTrue(result.getMember("bar").isJsonNumber()); + assertEquals(3, result.getMember("bar").getAsJsonNumber().getNumberAsInteger()); + + assertTrue(result.getMember("bla").isJsonString()); + assertEquals("yes", result.getMember("bla").getAsJsonString().getValue()); + + assertTrue(result.getMember("blub").isJsonNumber()); + assertEquals(3.4, result.getMember("blub").getAsJsonNumber().getNumberAsDouble(), 0.1); + + assertTrue( JsonParser.parseJsonObject("{}").isJsonObject()); + assertTrue(Log.getFindings().isEmpty()); + } + +} diff --git a/monticore-runtime/src/test/java/de/monticore/symboltable/serialization/JsonPrinterSecurityTest.java b/monticore-runtime/src/test/java/de/monticore/symboltable/serialization/JsonPrinterSecurityTest.java new file mode 100644 index 0000000000..9e20f57094 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/symboltable/serialization/JsonPrinterSecurityTest.java @@ -0,0 +1,116 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symboltable.serialization; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import de.monticore.symboltable.serialization.json.JsonArray; +import de.monticore.symboltable.serialization.json.JsonObject; + +/** + * This test checks whether injection of objects into serialization and deserialization is avoided + * correctly. + * + */ +public class JsonPrinterSecurityTest { + + + @Before + public void before() { + LogStub.init(); + Log.enableFailQuick(false); + } + + @Before + public void disableIndentation() { + JsonPrinter.enableIndentation(); + } + + @Test + public void test() { + // create test subject: + // Bar + // |- Bar1 + // |- Bar1.1 + // |- Bar2 (with injected bar 2.1) + Foo bar = new Foo("Bar"); + Foo bar1 = new Foo("Bar1"); + Foo bar11 = new Foo("Bar1.1"); + Foo bar2 = new Foo( + "Bar2\",\"children\":[{\"name\":\"Bar2.1\",\"name2\":\"Bar2.1\"}],\"name2\":\"Bar2\"},{\"name\":\"Bar2"); + bar1.children.add(bar11); + bar.children.add(bar1); + bar.children.add(bar2); + + // use JSONPrinter to produce json stirng + String s = printFoo(bar); + JsonObject o = JsonParser.parseJsonObject(s); + + assertEquals("Bar", getName(o)); + assertEquals(2, getChildren(o).size()); + + JsonObject b1 = getChildren(o).get(0).getAsJsonObject(); + assertEquals("Bar1", getName(b1)); + assertEquals(1, getChildren(b1).size()); + + JsonObject b11 = getChildren(b1).get(0).getAsJsonObject(); + assertEquals("Bar1.1", getName(b11)); + assertEquals(false, b11.hasMember("children")); + + JsonObject b2 = getChildren(o).get(1).getAsJsonObject(); + assertTrue(getName(b2).startsWith("Bar2")); + // without escaping, Bar2 would contain the injected child Bar2.1 + assertEquals(false, b2.hasMember("children")); + assertTrue(Log.getFindings().isEmpty()); + } + + protected JsonArray getChildren(JsonObject foo) { + assertEquals(true, foo.hasMember("children")); + assertEquals(true, foo.getMember("children").isJsonArray()); + return foo.getMember("children").getAsJsonArray(); + } + + protected String getName(JsonObject foo) { + assertEquals(true, foo.hasMember("name")); + assertEquals(true, foo.getMember("name").isJsonString()); + return foo.getMember("name").getAsJsonString().getValue(); + } + + protected String printFoo(Foo f) { + JsonPrinter p = new JsonPrinter(); + p.beginObject(); + p.member("name", f.name); + if (!f.children.isEmpty()) { + p.beginArray("children"); + f.children.stream().forEach(F -> p.valueJson(printFoo(F))); + p.endArray(); + } + p.member("name2", f.name); + p.endObject(); + return p.getContent(); + } + + class Foo { + String name; + + List children; + + String name2; + + public Foo(String name) { + this.name = name; + this.children = new ArrayList<>(); + this.name2 = name; + } + } + +} diff --git a/monticore-runtime/src/test/java/de/monticore/symboltable/serialization/JsonPrinterTest.java b/monticore-runtime/src/test/java/de/monticore/symboltable/serialization/JsonPrinterTest.java new file mode 100644 index 0000000000..95d515af81 --- /dev/null +++ b/monticore-runtime/src/test/java/de/monticore/symboltable/serialization/JsonPrinterTest.java @@ -0,0 +1,281 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.symboltable.serialization; + +import com.google.common.collect.Lists; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Optional; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * Tests the JsonPrinter + */ +public class JsonPrinterTest { + + @Before + public void before() { + LogStub.init(); + Log.enableFailQuick(false); + } + + @Before + public void disableIndentation() { + JsonPrinter.disableIndentation(); + } + + @Test + public void testOmitEmptyArray() { + JsonPrinter printer = new JsonPrinter(); + printer.beginObject(); + printer.member("name", "anArtifactScopeName"); + printer.beginArray("kindHierarchy"); + printer.endArray(); + printer.beginArray("symbols"); + printer.value("foo"); + printer.endArray(); + printer.endObject(); + + String serialized = printer.getContent(); + assertTrue(null != JsonParser.parseJsonObject(serialized)); + assertTrue(!serialized.contains("kindHierarchy")); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testEscapeSequences() { + JsonPrinter printer = new JsonPrinter(); + printer.value("\"\t\\\n\'"); + assertEquals("\"\\\"\\t\\\\\\n\\'\"", printer.toString()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testEmptyObject() { + JsonPrinter printer = new JsonPrinter(true); + printer.beginObject(); + printer.endObject(); + assertEquals("{}", printer.toString()); + + printer = new JsonPrinter(false); + printer.beginObject(); + printer.endObject(); + assertEquals("", printer.toString()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testDefaultString() { + JsonPrinter printer = new JsonPrinter(); + printer.beginObject(); + printer.member("s", ""); + printer.endObject(); + assertEquals("", printer.toString()); + + printer = new JsonPrinter(true); + printer.beginObject(); + printer.member("s", ""); + printer.endObject(); + assertEquals("{\"s\":\"\"}", printer.toString()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testDefaultInt() { + JsonPrinter printer = new JsonPrinter(); + printer.beginObject(); + printer.member("i", 0); + printer.endObject(); + assertEquals("", printer.toString()); + + printer = new JsonPrinter(true); + printer.beginObject(); + printer.member("i", 0); + printer.endObject(); + assertEquals("{\"i\":0}", printer.toString()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testDefaultBoolean() { + JsonPrinter printer = new JsonPrinter(); + printer.beginObject(); + printer.member("b", false); + printer.endObject(); + assertEquals("", printer.toString()); + + printer = new JsonPrinter(true); + printer.beginObject(); + printer.member("b", false); + printer.endObject(); + assertEquals("{\"b\":false}", printer.toString()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testEmptyList() { + JsonPrinter printer = new JsonPrinter(true); + printer.beginArray(); + printer.endArray(); + assertEquals("[]", printer.toString()); + + printer = new JsonPrinter(true); + printer.beginObject(); + printer.beginArray("emptyList"); + printer.endArray(); + printer.endObject(); + assertEquals("{\"emptyList\":[]}", printer.toString()); + + printer = new JsonPrinter(false); + printer.beginArray(); + printer.endArray(); + assertEquals("", printer.toString()); + + printer = new JsonPrinter(false); + printer.beginObject(); + printer.beginArray("emptyList"); + printer.endArray(); + printer.endObject(); + assertEquals("", printer.toString()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testBasicTypeAttributes() { + JsonPrinter printer = new JsonPrinter(); + printer.beginObject(); + printer.member("booleanAttribute", true); + printer.endObject(); + assertEquals("{\"booleanAttribute\":true}", printer.toString()); + + printer = new JsonPrinter(); + printer.beginObject(); + printer.member("intAttribute", -1); + printer.endObject(); + assertEquals("{\"intAttribute\":-1}", printer.toString()); + + printer = new JsonPrinter(); + printer.beginObject(); + printer.member("floatAttribute", 47.11f); + printer.endObject(); + assertEquals("{\"floatAttribute\":47.11}", printer.toString()); + + printer = new JsonPrinter(); + printer.beginObject(); + printer.member("doubleAttribute", 47.11); + printer.endObject(); + assertEquals("{\"doubleAttribute\":47.11}", printer.toString()); + + printer = new JsonPrinter(); + printer.beginObject(); + printer.member("longAttribute", 123456789L); + printer.endObject(); + assertEquals("{\"longAttribute\":123456789}", printer.toString()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testOptionalAndList() { + JsonPrinter printer = new JsonPrinter(true); + printer.beginObject(); + printer.member("optionalAttribute", Optional.of("presentOptional")); + printer.endObject(); + assertEquals("{\"optionalAttribute\":\"presentOptional\"}", printer.toString()); + + printer = new JsonPrinter(false); + printer.beginObject(); + printer.member("optionalAttribute", Optional.of("presentOptional")); + printer.endObject(); + assertEquals("{\"optionalAttribute\":\"presentOptional\"}", printer.toString()); + + printer = new JsonPrinter(true); + printer.beginObject(); + printer.member("optionalAttribute", Optional.empty()); + printer.endObject(); + assertEquals("{\"optionalAttribute\":null}", printer.toString()); + + printer = new JsonPrinter(false); + printer.beginObject(); + printer.member("optionalAttribute", Optional.empty()); + printer.endObject(); + assertEquals("", printer.toString()); + + printer = new JsonPrinter(true); + printer.beginObject(); + printer.member("listAttribute", new ArrayList<>()); + printer.endObject(); + assertEquals("{\"listAttribute\":[]}", printer.toString()); + + printer = new JsonPrinter(false); + printer.beginObject(); + printer.member("listAttribute", new ArrayList<>()); + printer.endObject(); + assertEquals("", printer.toString()); + + printer = new JsonPrinter(true); + printer.beginObject(); + printer.member("listAttribute", Lists.newArrayList("first", "second")); + printer.endObject(); + assertEquals("{\"listAttribute\":[\"first\",\"second\"]}", printer.toString()); + + printer = new JsonPrinter(false); + printer.beginObject(); + printer.member("listAttribute", Lists.newArrayList("first", "second")); + printer.endObject(); + assertEquals("{\"listAttribute\":[\"first\",\"second\"]}", printer.toString()); + assertTrue(Log.getFindings().isEmpty()); + } + + @Test + public void testInvalidNestings() { + + Log.clearFindings(); + JsonPrinter printer = new JsonPrinter(); + printer.beginObject(); + printer.beginObject(); + printer.endObject(); + printer.getContent(); + assertEquals(1, Log.getFindings().size()); + + Log.clearFindings(); + printer = new JsonPrinter(); + printer.beginObject(); + printer.endObject(); + printer.endObject(); + printer.getContent(); + assertEquals(1, Log.getFindings().size()); + + Log.clearFindings(); + printer = new JsonPrinter(); + printer.beginArray(); + printer.beginArray(); + printer.endArray(); + printer.getContent(); + assertEquals(1, Log.getFindings().size()); + + Log.clearFindings(); + printer = new JsonPrinter(); + printer.beginArray(); + printer.endArray(); + printer.endArray(); + printer.getContent(); + assertEquals(1, Log.getFindings().size()); + + Log.clearFindings(); + printer = new JsonPrinter(); + printer.beginObject(); + printer.beginArray(); + printer.endArray(); + printer.endArray(); + printer.endObject(); + printer.getContent(); + assertEquals(3, Log.getFindings().size()); + } + +} diff --git a/monticore-runtime/src/test/models/de/monticore/io/Model1.cdsym b/monticore-runtime/src/test/models/de/monticore/io/Model1.cdsym new file mode 100644 index 0000000000..d663f5e325 --- /dev/null +++ b/monticore-runtime/src/test/models/de/monticore/io/Model1.cdsym @@ -0,0 +1,2 @@ +/* (c) https://github.com/MontiCore/monticore */ +TEST Model for FileFinde \ No newline at end of file diff --git a/monticore-runtime/src/test/models/de/monticore/io/Model2.cd4 b/monticore-runtime/src/test/models/de/monticore/io/Model2.cd4 new file mode 100644 index 0000000000..d663f5e325 --- /dev/null +++ b/monticore-runtime/src/test/models/de/monticore/io/Model2.cd4 @@ -0,0 +1,2 @@ +/* (c) https://github.com/MontiCore/monticore */ +TEST Model for FileFinde \ No newline at end of file diff --git a/monticore-runtime/src/test/models/de/monticore/io/Model2.mc4 b/monticore-runtime/src/test/models/de/monticore/io/Model2.mc4 new file mode 100644 index 0000000000..d663f5e325 --- /dev/null +++ b/monticore-runtime/src/test/models/de/monticore/io/Model2.mc4 @@ -0,0 +1,2 @@ +/* (c) https://github.com/MontiCore/monticore */ +TEST Model for FileFinde \ No newline at end of file diff --git a/monticore-runtime/src/test/resources/de/monticore/io/Model1.cdsym b/monticore-runtime/src/test/resources/de/monticore/io/Model1.cdsym new file mode 100644 index 0000000000..d2dd742af5 --- /dev/null +++ b/monticore-runtime/src/test/resources/de/monticore/io/Model1.cdsym @@ -0,0 +1,2 @@ +/* (c) https://github.com/MontiCore/monticore */ +TEST MODEL FOR FileFinder \ No newline at end of file diff --git a/monticore-runtime/src/test/resources/de/monticore/io/Model1.dsym b/monticore-runtime/src/test/resources/de/monticore/io/Model1.dsym new file mode 100644 index 0000000000..d2dd742af5 --- /dev/null +++ b/monticore-runtime/src/test/resources/de/monticore/io/Model1.dsym @@ -0,0 +1,2 @@ +/* (c) https://github.com/MontiCore/monticore */ +TEST MODEL FOR FileFinder \ No newline at end of file diff --git a/monticore-runtime/src/test/resources/de/monticore/io/Model1.sdsym b/monticore-runtime/src/test/resources/de/monticore/io/Model1.sdsym new file mode 100644 index 0000000000..d2dd742af5 --- /dev/null +++ b/monticore-runtime/src/test/resources/de/monticore/io/Model1.sdsym @@ -0,0 +1,2 @@ +/* (c) https://github.com/MontiCore/monticore */ +TEST MODEL FOR FileFinder \ No newline at end of file diff --git a/monticore-runtime/src/test/resources/de/monticore/io/Model3.mc4 b/monticore-runtime/src/test/resources/de/monticore/io/Model3.mc4 new file mode 100644 index 0000000000..d2dd742af5 --- /dev/null +++ b/monticore-runtime/src/test/resources/de/monticore/io/Model3.mc4 @@ -0,0 +1,2 @@ +/* (c) https://github.com/MontiCore/monticore */ +TEST MODEL FOR FileFinder \ No newline at end of file diff --git a/monticore-runtime/src/test/resources/de/monticore/parsing/SimpleStateChart.mc4 b/monticore-runtime/src/test/resources/de/monticore/parsing/SimpleStateChart.mc4 new file mode 100644 index 0000000000..741aff9abf --- /dev/null +++ b/monticore-runtime/src/test/resources/de/monticore/parsing/SimpleStateChart.mc4 @@ -0,0 +1,16 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.parsing; + +grammar SimpleStateChart { + + StateChart = Member*; + + interface Member; + + State implements Member = "state" Name; + + Transition implements Member = "from" from:Name "->" "to" to:Name; + + +} diff --git a/monticore-runtime/src/test/resources/de/monticore/parsing/Statechart.mc4 b/monticore-runtime/src/test/resources/de/monticore/parsing/Statechart.mc4 new file mode 100644 index 0000000000..49a3da2d52 --- /dev/null +++ b/monticore-runtime/src/test/resources/de/monticore/parsing/Statechart.mc4 @@ -0,0 +1,42 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package de.monticore.parsing; + +grammar StatechartDSL { + +options { + parser "k=3;" + lexer "k=2; " + +} + + Statechart implements SCStructure= "statechart" Name "{" (States:State | Transitions:Transition | UserCode:Code)* "}"; + + + EntryAction= "entry" ":" Block:BlockStatement; + + ExitAction= "exit" ":" Block:BlockStatement; + + + State implements SCStructure = "state" Name ("<<" (Initial:["initial"] | Final:["final"])* ">>")? + ( ("{ options{}" (EntryAction:EntryAction)? (ExitAction:ExitAction)? (States:State | Transitions:Transition)* "}") | ";") ; + + Transition = From:Name "->" To:Name + (":" (Event:Name ( "(" (Arguments:Argument ( "," Arguments:Argument)*) ")" )? )? + ("[" Guard: Expression "]")? + ("/" Action: BlockStatement)? ";" + | ";"); + + Argument= ParamType:Name ParamName:Name; + + Code= "code" Code: Classbody ; + + interface SCStructure; + + external BlockStatement; + + external Expression; + + external Classbody; + + } diff --git a/monticore-runtime/src/test/resources/de/monticore/symboltable/languagecomposition/embedding/Entity.cla b/monticore-runtime/src/test/resources/de/monticore/symboltable/languagecomposition/embedding/Entity.cla new file mode 100644 index 0000000000..bb2b43b195 --- /dev/null +++ b/monticore-runtime/src/test/resources/de/monticore/symboltable/languagecomposition/embedding/Entity.cla @@ -0,0 +1,3 @@ +/* (c) https://github.com/MontiCore/monticore */ + +// Note that this file will not be parsed. It is only used by a parser mock. diff --git a/monticore-runtime/src/test/resources/de/monticore/symboltable/languagecomposition/family/Cla.cla b/monticore-runtime/src/test/resources/de/monticore/symboltable/languagecomposition/family/Cla.cla new file mode 100644 index 0000000000..bb2b43b195 --- /dev/null +++ b/monticore-runtime/src/test/resources/de/monticore/symboltable/languagecomposition/family/Cla.cla @@ -0,0 +1,3 @@ +/* (c) https://github.com/MontiCore/monticore */ + +// Note that this file will not be parsed. It is only used by a parser mock. diff --git a/monticore-runtime/src/test/resources/de/monticore/symboltable/languagecomposition/family/Sc.sc b/monticore-runtime/src/test/resources/de/monticore/symboltable/languagecomposition/family/Sc.sc new file mode 100644 index 0000000000..bb2b43b195 --- /dev/null +++ b/monticore-runtime/src/test/resources/de/monticore/symboltable/languagecomposition/family/Sc.sc @@ -0,0 +1,3 @@ +/* (c) https://github.com/MontiCore/monticore */ + +// Note that this file will not be parsed. It is only used by a parser mock. diff --git a/monticore-runtime/src/test/resources/hwc/test/A.java b/monticore-runtime/src/test/resources/hwc/test/A.java new file mode 100644 index 0000000000..4116d6b1c1 --- /dev/null +++ b/monticore-runtime/src/test/resources/hwc/test/A.java @@ -0,0 +1,7 @@ +/* (c) https://github.com/MontiCore/monticore */ + +package test; + +public class A { + +} \ No newline at end of file diff --git a/monticore-runtime/src/test/resources/jar/Test.jar b/monticore-runtime/src/test/resources/jar/Test.jar new file mode 100644 index 0000000000..1ff271f4db Binary files /dev/null and b/monticore-runtime/src/test/resources/jar/Test.jar differ diff --git a/monticore-runtime/src/test/resources/logback.groovy b/monticore-runtime/src/test/resources/logback.groovy new file mode 100644 index 0000000000..3c95e7453d --- /dev/null +++ b/monticore-runtime/src/test/resources/logback.groovy @@ -0,0 +1,26 @@ +/* (c) https://github.com/MontiCore/monticore */ + +// this is a very user friendly console appender +// which only outputs level >= INFO +appender("CONSOLE", ConsoleAppender) { + filter(ch.qos.logback.classic.filter.ThresholdFilter) { + level = INFO + } + encoder(PatternLayoutEncoder) { + pattern = "%-7([%level]) %message%n" + } +} + +def bySecond = timestamp("yyyy-MM-dd-HHmmss") +def out = System.getProperty("MC_OUT") == null ? "target" : System.getProperty("MC_OUT") + +// this is a rather technically detailed file appender +appender("FILE", FileAppender) { + file = "${out}/monticore.test.${bySecond}.log" + encoder(PatternLayoutEncoder) { + pattern = "%date{yyyy-MM-dd HH:mm:ss} %-7([%level]) %logger{26} %message%n" + } +} + +// everything with level >= DEBUG is logged to the file (see above) +root(DEBUG, ["FILE", "CONSOLE"]) diff --git a/monticore-runtime/src/test/resources/modelloader/modeljar.jar b/monticore-runtime/src/test/resources/modelloader/modeljar.jar new file mode 100644 index 0000000000..3929a22dfb Binary files /dev/null and b/monticore-runtime/src/test/resources/modelloader/modeljar.jar differ diff --git a/monticore-runtime/src/test/resources/modelloader/modelpath/models/A.cla b/monticore-runtime/src/test/resources/modelloader/modelpath/models/A.cla new file mode 100644 index 0000000000..e69de29bb2 diff --git a/monticore-runtime/src/test/resources/modelloader/modelpath/models/B.cla b/monticore-runtime/src/test/resources/modelloader/modelpath/models/B.cla new file mode 100644 index 0000000000..e69de29bb2 diff --git a/monticore-runtime/src/test/resources/modelloader/modelpath/models/B.dex b/monticore-runtime/src/test/resources/modelloader/modelpath/models/B.dex new file mode 100644 index 0000000000..e69de29bb2 diff --git a/monticore-runtime/src/test/resources/modelloader/modelpath/models/B.ext b/monticore-runtime/src/test/resources/modelloader/modelpath/models/B.ext new file mode 100644 index 0000000000..e69de29bb2 diff --git a/monticore-runtime/src/test/resources/modelloader/modelpath/models/D.cla b/monticore-runtime/src/test/resources/modelloader/modelpath/models/D.cla new file mode 100644 index 0000000000..e69de29bb2 diff --git a/monticore-runtime/src/test/resources/modelloader/modelpath/models/E.cla b/monticore-runtime/src/test/resources/modelloader/modelpath/models/E.cla new file mode 100644 index 0000000000..e69de29bb2 diff --git a/monticore-runtime/src/test/resources/modelloader/modelpath2/models/A.sc b/monticore-runtime/src/test/resources/modelloader/modelpath2/models/A.sc new file mode 100644 index 0000000000..e38ac71f3c --- /dev/null +++ b/monticore-runtime/src/test/resources/modelloader/modelpath2/models/A.sc @@ -0,0 +1,2 @@ +/* (c) https://github.com/MontiCore/monticore */ + diff --git a/monticore-runtime/src/test/resources/modelloader/modelpath2/models/D.cla b/monticore-runtime/src/test/resources/modelloader/modelpath2/models/D.cla new file mode 100644 index 0000000000..e69de29bb2 diff --git a/monticore-runtime/src/test/resources/modelpathtest/path1/ambiguousfile.txt b/monticore-runtime/src/test/resources/modelpathtest/path1/ambiguousfile.txt new file mode 100644 index 0000000000..2ba5f5fcba --- /dev/null +++ b/monticore-runtime/src/test/resources/modelpathtest/path1/ambiguousfile.txt @@ -0,0 +1 @@ +testfile for the modelpath test diff --git a/monticore-runtime/src/test/resources/modelpathtest/path1/unambiguousfile.txt b/monticore-runtime/src/test/resources/modelpathtest/path1/unambiguousfile.txt new file mode 100644 index 0000000000..2ba5f5fcba --- /dev/null +++ b/monticore-runtime/src/test/resources/modelpathtest/path1/unambiguousfile.txt @@ -0,0 +1 @@ +testfile for the modelpath test diff --git a/monticore-runtime/src/test/resources/modelpathtest/path2/ambiguousfile.txt b/monticore-runtime/src/test/resources/modelpathtest/path2/ambiguousfile.txt new file mode 100644 index 0000000000..2ba5f5fcba --- /dev/null +++ b/monticore-runtime/src/test/resources/modelpathtest/path2/ambiguousfile.txt @@ -0,0 +1 @@ +testfile for the modelpath test diff --git a/monticore-runtime/src/test/resources/paths/1/AFile.ftl b/monticore-runtime/src/test/resources/paths/1/AFile.ftl new file mode 100644 index 0000000000..7ad0450643 --- /dev/null +++ b/monticore-runtime/src/test/resources/paths/1/AFile.ftl @@ -0,0 +1 @@ +<#-- (c) https://github.com/MontiCore/monticore --> diff --git a/monticore-runtime/src/test/resources/paths/1/AFile.txt b/monticore-runtime/src/test/resources/paths/1/AFile.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/monticore-runtime/src/test/resources/paths/1/a/AFile.ftl b/monticore-runtime/src/test/resources/paths/1/a/AFile.ftl new file mode 100644 index 0000000000..7ad0450643 --- /dev/null +++ b/monticore-runtime/src/test/resources/paths/1/a/AFile.ftl @@ -0,0 +1 @@ +<#-- (c) https://github.com/MontiCore/monticore --> diff --git a/monticore-runtime/src/test/resources/paths/1/a/AFile.txt b/monticore-runtime/src/test/resources/paths/1/a/AFile.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/monticore-runtime/src/test/resources/paths/2/a/AFile.ftl b/monticore-runtime/src/test/resources/paths/2/a/AFile.ftl new file mode 100644 index 0000000000..7ad0450643 --- /dev/null +++ b/monticore-runtime/src/test/resources/paths/2/a/AFile.ftl @@ -0,0 +1 @@ +<#-- (c) https://github.com/MontiCore/monticore --> diff --git a/monticore-runtime/src/test/resources/paths/2/a/AFile.txt b/monticore-runtime/src/test/resources/paths/2/a/AFile.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/overrides/main.html b/overrides/main.html new file mode 100644 index 0000000000..11db0eec11 --- /dev/null +++ b/overrides/main.html @@ -0,0 +1,24 @@ + +{% extends "base.html" %} + +{% block extrahead %} + {{ super() }} + + + +{% endblock %} + + diff --git a/scripts/ftlAnalysis.sh b/scripts/ftlAnalysis.sh new file mode 100644 index 0000000000..890d944c4d --- /dev/null +++ b/scripts/ftlAnalysis.sh @@ -0,0 +1,260 @@ +#!/bin/bash +# (c) https://github.com/MontiCore/monticore +# +# Derive some infos about the templates in use +# +# Arguments +# 1: list of directories to search for templates, separated by ":" +# 2: starting node (Qualified name, like "mc.umlp.Bla") +# 3: storage for the intermediate result (relation) +# +# Result: +# (1) List of all Files (ftl and calculators) considered +# (2) Info about their statical use (= reachability) +# "UNUSED" means: the file is not used within +# +# Shortcomming: +# (1) java Files that are Calculators are identified by +# containing the match "extends *[a-zA-Z.]*Calculator" +# which may not be the case +# +# (2) Filenames may be computed within the ftl, such that a static +# analysis does not detect all filenames +# +# (3) Filenames may be statically present, but not used in the same +# source, e.g. they can be stored in variables and called in sub-templates. +# +# ($!) wegen der "grep-basierten" syntaktischen Analyse werden einige Dateien +# nicht richtig erkannt. So werden manche Java-Files nicht als Calculatoren erkannt, +# obwohl sie welche sind und manche Java-Dateien einbezogen, die mit instantiate +# in den Templates benutzt werden (das füu falschen Warnungen "dangling" reference. +# Deshalb: ist die dangling reference war nung fuer Java-Files abgeschaltet. +# +# potential call: +# scripts/ftlAnalysis.sh $HOME/workspace/dex/gtr/src:$HOME/workspace/dex/use/src configure.StartAllOutput ~/tmp/relation.txt +# + +# from here we compute the reachable Names: +### dir=$HOME/workspace/dex/gtr/src +dirs=$1 + +# extract directory list, space separated +dirs2=`echo $dirs | sed "s!:! !g"` + +### startNode="configure.StartAllOutput" +startNode=$2 + +### relfile=$HOME/akt/relation.txt +relfile=$3 + +rootdir=`pwd` + +rm -rf $relfile # start fresh + +echo "start" $startNode >> $relfile +echo "## ftlAnalysis" +echo " ------------------------------------------------" +echo "### List of java calculators considered:" +echo " ------------------------------------------------" + +# iterate over the given directories & files within +for dir in $dirs2 +do + cd $dir + echo "iterate over directory for java-files:" $dir "
" + for i in `find . -print | grep -v ".svn" | grep java` + do + # sucht alle Klassen, die Calculatoren sind und von ftl aufzurufen + # waeren + ipkg=`echo $i | sed 's!^./!!g' | sed 's!.java!!g' | sed 's!/!.!g'` + grep "extends *[a-zA-Z.]*Calculator" $i > /dev/null + if test $? -eq 0 + then + echo "**java node**" $ipkg "
" + echo "node" $ipkg >> $relfile + fi + done + cd $rootdir +done + + +echo " ------------------------------------------------" +echo "### List of templates considered:" +echo " ------------------------------------------------" + +# iterate over the given directories & files within +for dir in $dirs2 +do + cd $dir + echo "iterate over directory for ftl-files:" $dir "
" + for i in `find . -print | grep -v ".svn" | grep ftl` + do + # filtert nach strings, die nach filenamen aussehen + # und fuegt den enthaltenden Dateinamen vorne dazu + ipkg=`echo $i | sed 's!^./!!g' | sed 's!.ftl!!g' | sed 's!/!.!g'` + echo "**ftl node**" $ipkg "
" + echo "node" $ipkg >> $relfile + + cat $i \ + | grep '"' - \ + | sed 's/<#-.*//g' - \ + | sed 's/^[^"]*"//g' - \ + | sed 's/"[^"]*"/\n/g' - \ + | sed 's/"[^"]*$//g' - \ + | grep '^[a-zA-Z][a-zA-Z0-9]*\.[a-zA-Z][a-zA-Z0-9]*' \ + | sed 's!^!'$ipkg' --> !g' - \ + >> $relfile + done + cd $rootdir +done + +echo " ------------------------------------------------" +echo "### List of all nodes with static reachability infos" +echo " " +echo " UNUSED means, file (ftl/java) is not statically(!) identifyable" +echo " to be used when invoking " $startNode +echo " Care: the filename may be calculated during runtime" +echo " e.g. myconfigure.StartCDEXT-* are calculated." +echo " (we cannot check this statically)" +echo " Furthermore: static inclusion/knowledge of a file does not " +echo " necessarily mean it is called during each execution." +echo " " +echo " (s:?,t:?) means: the file calls s other files" +echo " and is called by t other files" +echo " (s=source; t=target)" +echo " " +echo " For a concrete list of who calls whom see:" +echo ' grep "\--" /tmp/relation.txt | sort -u' +echo " " +echo " ------------------------------------------------" +echo " " +echo " " + +# compute the transitive closure (and with it the list of +# files referenced from the starter + +# +# $relfile contains the following content: +# "start NAME" defines the start node (line 1 only) +# "node NAME" defines a node +# "NAME --> NAME" defines an arc +# +# Variables: +# strn[i] contains NAMES (we use indices internally) +# nums[s] maps a name to its index +# +# m[i,j] matrix storing how often an arc from i to j is defined +# (=0, means no link; >=1 means there is a link) +# then also holds the transitive closure +# r[i,j] original relation (no transitive clseure) +# +# target[n] counts how often a node is target in an edge +# source[n] counts how often a node is source in an edge +# nodeexists[n] 1 when the node exists (otherwise it is a dangling pointer) +# + +cat $relfile | awk ' + BEGIN { ncount = 0; } + /start/{ startStr=$2; start=addStr($2); + m[start,start] = 1; } + /node/{ n=addStr($2); nodeexists[n]=1; } + /-->/ { lcount++; + n1 = addStr($1); n2 = addStr($3); + # fill matrix + m[n1,n2] = 1; + r[n1,n2] = r[n1,n2] + 1; + source[n1]++; target[n2]++; + } + + END { + print "Summary:
#of node: " ncount "
"; + print " #of arcs: " lcount "
"; + print "StartNode: " strn[start] "
"; + print " "; + transclosure(m); + for (i in strn) { + if( m[start,i] >= 1 ) + x = " "; + else + x = "**UNUSED**"; + if( nodeexists[i] >= 1 ) + dangling = ""; + else + dangling = "DANGLING REF (was not followed)!"; + printf "%6s %-50s (s:%d,t:%d) %s
\n", x, strn[i], source[i], target[i], dangling; + } + + print " "; + print " ------------------------------------------------"; + print "### The relation: Source --> Target (+ Number of inclusions)"; + print " ------------------------------------------------"; + print " "; + for (i in strn) { + printf "**source** %s\n
", strn[i]; + for (j in strn) { + if(r[i,j] >= 2) { + printf " --> %s (%d)
\n", strn[j], r[i,j]; + } else if (r[i,j] >= 1) { + printf " --> %s
\n", strn[j]; + } + } + } + + print " "; + print " ------------------------------------------------"; + print "### The inverse relation: Target --> Source"; + print " ------------------------------------------------"; + print " "; + for (i in strn) { + printf "**target** %s\n
", strn[i]; + for (j in strn) { + if(r[j,i] >= 1) { + printf " <-- %s
\n", strn[j]; + } + } + } + + } + + # calculate the transitive closure (Warshall) + function transclosure(m) + { + for(k = 1; k <=ncount; k++) { + for(i = 1; i <=ncount; i++) { + if(m[i,k] == 1) { + for(j = 1; j <=ncount; j++) { + if(m[k,j] == 1) m[i,j] = 1; + } + } + } + } + } + + # map string to a number in: nums + # and store inverse maping in strn + function addStr(s) + { + n = 0; + if (s in nums) { + n = nums[s]; + } + if (n==0) + { n = ++ncount + nums[s] = n; + strn[n] = s; + } + return n; + } + + # copy an array + function copy(a,b) + { + split("", b); # empty b + for(i in a) { b[i] = a[i]; } + } +' + +echo " " +echo "(EOF)" + + diff --git a/scripts/preprocessing.sh b/scripts/preprocessing.sh new file mode 100644 index 0000000000..8f45a9261b --- /dev/null +++ b/scripts/preprocessing.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# (c) https://github.com/MontiCore/monticore +# script for all preprocessing steps of the pages job +# is used to have uniform bases for both gitlab and github pages +# is used from '.gitlab-ci.yml'(gitlab) and '.travis.yml'(github) +# +# execute report scripts and print output to *.md file, to use these in pages +#sh docs/scripts/errorList.sh '../../' 'target/site/errorList' > docs/scripts/ErrorList.md +#sh docs/scripts/detailedErrorList.sh '../../' 'target/site/detailedErrorList' > docs/scripts/DetailedErrorList.md +#sh docs/scripts/findDoubleFileNames.sh './' 'target/site/findDoubleFileNames' > docs/scripts/FindDoubleFileNames.md +#sh docs/scripts/ftlAnalysis.sh './' 'configure.StartAllOutput' 'target/site/ftlAnalysis' > docs/scripts/FtlAnalysis.md +#echo "[INFO] Executed report scripts for pages" +# +# move all directories that contain *.md files to the docs folder +# because mkdocs can only find *.md files there +mkdir docs/docs +mv docs/*.md docs/docs +mv docs/further_docs docs/docs/further_docs +mv monticore-grammar docs/monticore-grammar +mv monticore-runtime docs/monticore-runtime +mv 00.org docs/00.org +mv *.md docs/ +mv *.png docs/ +mv img/ docs/ +echo "[INFO] Moved *.md files to 'docs' folder" +# +# remove all occurences of '[[_TOC_]]' in markdown files +# because mkdocs already renders its own toc +for file in $(find ./docs -type f -name "*.md") +do + sed -i 's/\[\[_TOC_\]\]//' $file + perl -pi -e 's/\[([^\[\]\(\)]*)\]\([^\[\]\(\)]*git.rwth-aachen.de[^\[\]\(\)]*?\)/$1/g' $file +done +echo "[INFO] Removed all occurrences of '[[_TOC_]]' in *.md files" +echo "[INFO] Removed all links to https://git.rwth-aachen.de in *.md files" diff --git a/search/search_index.json b/search/search_index.json new file mode 100644 index 0000000000..db0ddb2786 --- /dev/null +++ b/search/search_index.json @@ -0,0 +1 @@ +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Home","text":"

"},{"location":"#monticore-language-workbench-and-development-tool-framework","title":"MontiCore - Language Workbench and Development Tool Framework","text":"

MontiCore is a language workbench for the efficient development of domain-specific languages (DSLs). It processes an extended grammar format which defines the DSL and generates Java components for processing the DSL documents. Examples for these components are parsers, AST classes, symboltables or pretty printers. This enables a user to rapidly define a language and use it together with the MontiCore-framework to build domain-specific tools.

Some MontiCore advantages are the reusability of predefined language components, conservative extension and composition mechanisms, and an optimal integration of handwritten code into the generated tools. Its grammar languages are comfortable to use.

Start here for developing with MontiCore.

[HKR21] Katrin H\u00f6lldobler, Oliver Kautz, Bernhard Rumpe: MontiCore Language Workbench and Library Handbook: Edition 2021. Shaker, 2021.

"},{"location":"#a-teaser-for-monticore","title":"A Teaser for MontiCore","text":"

To show a little of MontiCore's capabilities, the following (incomplete) grammar might help:

grammar MyStatemachine extends Automata,                  // MontiCore grammar \n                               MCBasicTypes, SetExpressions, MCCommonLiterals {     \n  start Automaton;\n\n  // overriding a nonterminal (to add optional conditions):\n  Transition = from:Name@State \":\" Expression? \"->\" to:Name@State;\n\n  // add new variants of expressions\n  LogicalNotExpr implements Expression = \"!\" Expression;\n\n  XorExpr        implements Expression =\n        left:Expression \"xor\" right:Expression;\n\n  scope LetExpr  implements Expression =\n        \"let\" (VarDeclaration || \",\")+ \"in\" Expression;\n\n  symbol VarDeclaration = MCType? Name \"=\" Expression ;\n}\n

The grammar language has a variety of mechanisms to define new nonterminals using constants \"!\", brackets (..), optionals ?, lists *, repetitions (..||..)+, etc. The grammar builds an extended version of Statemachines reusing existing grammar components, here Automata, MCBasicTypes, SetExpressions and MCCommonLiterals. The grammar has 5 productions introducing 4 new nonterminals and overrides Transition, which is inherited from Automata. Transition additionally has an optional Expression? as firing condition. LogicalNotExpr, XorExpr, and LetExpr extend the already existing Expression nonterminal and add new forms of expressions.

LetExpr introduces a new local variable, which is visible only in that scope (indicated by keyword). VarDeclaration defines the new place to define symbols (that have a Name). There is an extensive infrastructure to manage the definition of names, visibility, etc.

MontiCore compiles the above grammar into 78 classes with in total 18629 lines of code that define the complete frontend and a larger part of the backend of a statemachine processor. We now can write statemachines like:

statemachine PingPong {                                         // MyStatemachine\n  state Ping, Pong;\n  Ping : (speed > 14km/h && !missedBall) -> Pong\n}\n

MontiCore provides versions of expressions that use SI Units like 240km/h or 14.2 m/s^2, but also Java expressions like 2_000_000 and other variants including appropriate type checks. We include these forms of expressions by importing their grammars.

Please note that in both cases (extension and overwriting existing nonterminals), we do not touch nor copy/paste the predefined grammars, but achieve an out-of-the-box reuse. Out-of-the-box reuse also includes reuse of predefined typechecks, code generation, etc. They only need to be extended to the added variants. Please also note that PlusExpr is mutually left-recursive. -- Yes, that works in MontiCore 6.

"},{"location":"#quick-start","title":"Quick Start","text":"
$ cd /usr/local\n$ wget www.monticore.de/download/aut.tar.gz\n$ tar -xf aut.tar.gz\n$ cd mc-workspace\n$ wget www.monticore.de/download/monticore-cli.jar\n$ java -jar monticore-cli.jar -g Automata.mc4 -hcp hwc/ -mp monticore-cli.jar\n$ javac -cp monticore-cli.jar -sourcepath \"src/;out/;hwc/\" src/automata/AutomataTool.java\n$ java -cp \"src/;out/;hwc/;monticore-cli.jar\" automata.AutomataTool example/PingPong.aut PingPong.autsym\n
"},{"location":"#monticore-has-a-relaxed-3-level-license","title":"MontiCore has a Relaxed 3-Level License","text":"

Informal summary: The MontiCore Language Workbench deals with three levels of code (MontiCore LWB, tool derivates, product code). Each has its own licenses: (1) Product code generated by a MontiCore tool derivate is absolutely free for each form of use including commercial use without any license restriction. (2) Tool derivates created using the MontiCore language workbench mention that it is built using MontiCore. There is no other restriction. (BSD 3 Clause license) (3) Adaptations of MontiCore should mention MontiCore and results published back into this repository (LGPL license).

For details see Licenses.

"},{"location":"#more-information-about-monticore","title":"More Information about MontiCore","text":"
  • MontiCore handbook. The handbook describes how to use MontiCore as an out-of-the-box language workbench, but also as grey box tooling framework. It thus also gives an overview over a number of core mechanisms of MontiCore.

  • List of core grammars. MontiCore concentrates on reuse. It therefore offers a set of predefined language components, usually identified through an appropriate component grammar allowing to define your own language as a composition of reusable assets efficiently. reusable assets are among others: several sets of literals, expressions, types, and statements, which are freely composable.

  • List of languages. This is a list of languages that can be used out of the box. Some of them are in development, others rather stable. Several of these languages are inspired by the UML/P (see [Rum16,Rum17]). These complete languages are usually composed of a number of language components.

  • This project is freely available software; you can redistribute the MontiCore language workbench according to the rules described in the licensing section.

  • Contributing to MontiCore: MontiCore is originally developed by the Software Engineering group at RWTH. Meanwhile, it is maintained by several groups and individual persons. If you want to contribute to MontiCore, please create a fork and issue corresponding pull requests. The RWTH and the Stuttgart development teams will review and merge changes. (A more open process for common development is currently in discussion and depends on interests from further people/groups.) You may also ask to become a member of the project.

  • If questions appear e.g. on building an interpreter, please contact monticore@se-rwth.de.

"},{"location":"#further-information","title":"Further Information","text":"
  • see also MontiCore handbook
  • MontiCore Reference Languages - Languages Built Using MontiCore
  • Build MontiCore - How to Build MontiCore
  • Getting Started - How to start using MontiCore
  • Changelog - Release Notes
  • FAQ - FAQ
  • Licenses - MontiCore 3-Level License
  • Project root: MontiCore @github
  • List of languages
  • MontiCore Core Grammar Library
  • Best Practices
  • Publications about MBSE and MontiCore
"},{"location":"00.org/Explanations/CHANGELOG/","title":"Changelog","text":""},{"location":"00.org/Explanations/CHANGELOG/#release-notes","title":"Release Notes","text":""},{"location":"00.org/Explanations/CHANGELOG/#monticore-750","title":"MontiCore 7.5.0","text":"

released: 02.05.2023

"},{"location":"00.org/Explanations/CHANGELOG/#additions","title":"Additions","text":"
  • type dispatcher for type-safe instance checks and casting
  • pretty-printer generator
  • parser generator produces code for the new rule \"replcaekeyword\"
  • new coco NoForbiddenProdName
  • TOP mechanism for generated ANTLr parser classes
  • add class StreamType which can be used to add Stream symbol with corresponding functions to the global scope
  • Additions to AccessModifiers
  • add class StaticAccessModifier to filter whether a symbol is considered as static or not.
  • add class WriteableAccessModifier to filter whether a symbol is considered as writable or not.
  • add class CompoundAccessModifier to compose multiple access modifier, e.g. public + static
  • Additions to the TypeCheck
  • add new class TypeRelations to provide typecheck methods in a non static fashion. The TypeCheck class now delegates to the implementation
  • add new class SymTypeOfUnion to store the type of a union of types
  • add new class SymTypeOfIntersection to store the type of an intersection of types
  • add new interface ISymTypeVisitor to traverse SymTypeExpressions
  • add new class SymTypeDeepCloneVisitor to clone SymTypeExpressions
  • add new class SymTypeBoxingVisitor to box SymTypeExpressions. This implemenation fixes issues over the methods within the SymTypeExpression classes
  • add new class SymTypeUnboxingVisitor to unbox SymTypeExpressions. This implemenation fixes issues over the methods within the SymTypeExpression classes
  • add new class SymTypeNormalizeVisitor to normalize SymTypeExpressions. This is required to check for compatibility between SymTypeExpressions, especially regarding union and intersection types
"},{"location":"00.org/Explanations/CHANGELOG/#changes","title":"Changes","text":"
  • Gradle projects containing multiple MCTasks can now use parallel builds
  • The MontiCore Gradle Plugin ensures that MCTasks are not run in parallel, other tasks, like compile, are run in parallel
"},{"location":"00.org/Explanations/CHANGELOG/#fixes","title":"Fixes","text":"
  • parser generation for optional keywords with usage name
  • Overriding/Imlementing use of lexical productions
  • in OOScopes, accessmodifier can be used to filter symbols
"},{"location":"00.org/Explanations/CHANGELOG/#monticore-740","title":"MontiCore 7.4.0","text":"

released: 14.11.2022

"},{"location":"00.org/Explanations/CHANGELOG/#additions_1","title":"Additions","text":"
  • new grammar rule replacekeyword
  • add attribute derived to FieldSymbol
  • generate XParserInfo
  • Used by generated language servers
  • Can be queried for additional info about specific parser states of the generated ANTLR parser
  • Contains methods stateHasUsageNameY to check the usage name of the nonterminal associated with a parser state
  • Contains methods stateReferencesZSymbol to check the referenced symbol kind of Name nonterminal associated with a parser state
  • Additions to the TypeCheck
  • add new abstract classes AbstractDerive and AbstractSynthesize with basic functionality for FullDerivers and FullSynthesizers
  • add new class TypeCalculator that can be used to derive SymTypeExpressions from Expressions and synthesize them from types
  • add SymTypeObscure: new SymTypeExpression that is used when the SymTypeExpression for an expression could not be derived
  • add SymTypeFunction to store the type of a function
  • add the functionality for Function chaining, allowing to call functions returned by other functions
  • add varargs support for CallExpressions
  • add deriver for LambdaExpressions
  • Add grammar MCFunctionTypes to write the type of a function in a model
  • Add ExpressionStatementIsValid CoCo
  • Add grammars LambdaExpressions and StreamExpressions
"},{"location":"00.org/Explanations/CHANGELOG/#changes_1","title":"Changes","text":"
  • Java 11
  • Gradle 7 compatibility
  • delete deprecated method deepClone in ASTNodes
  • use CDGenerator (cdanalysis)
  • Visitor Pattern: Introduce state-based traversal of symbol table
  • Allows for combined AST and symbol table traversal from global and artifact scopes
  • Comes with integrated stand-alone symbol table traversal
  • TypeCheck Refactoring
  • rename currentResult to result in TypeCheckResult
  • split TypeCheck facade into TypeCalculator (used to derive SymTypeExpressions from Expressions and synthesize them from types) and TypeCheck (static functions that are useful when checking for type compatibility or for comparing SymTypeExpressions)
  • rename SymTypeConstant to SymTypePrimitive
  • Deriver now evaluate all subexpressions of an expression and do not stop at the first error
  • do not log multiple errors for the same error: If an error occurs in the derivation of a subexpression and this error leads to another error in the derivation of the expression itself, do not log another error
  • remove the name of CallExpression
  • rework the calculation of CallExpression, NameExpression and FieldAccessExpression
  • rework TypeCheck error messages to make them more clear
"},{"location":"00.org/Explanations/CHANGELOG/#fixes_1","title":"Fixes","text":"
  • close all jars used to load models via MCPath
  • gradle clean should no longer fail because of dangling opened grammar jars
  • TypeCheck
  • fix an error in the WildCardTypeArgument where ? super and ? extends were swapped
  • fix TypeCheck not logging an error for NameExpression and FieldAccessExpression in isolation
  • make short compatible to byte
"},{"location":"00.org/Explanations/CHANGELOG/#monticore-730","title":"MontiCore 7.3.0","text":"

released: 04.04.2022

"},{"location":"00.org/Explanations/CHANGELOG/#additions_2","title":"Additions","text":"
  • add cocos for lexical mode
  • add coco for Expression
  • add cocos for JavaLight
  • new methods putSymbolDeSer, putXYSymbolDeSer and loadFileForModelNamed (GlobalScope Interface)
  • new method getToken (MCParser)
  • use CD4C in 02experiment.configTemplate
"},{"location":"00.org/Explanations/CHANGELOG/#changes_2","title":"Changes","text":"
  • rename generated classes XYCLI -> XYTool
"},{"location":"00.org/Explanations/CHANGELOG/#monticore-720","title":"MontiCore 7.2.0","text":"

released: 13.09.2021

"},{"location":"00.org/Explanations/CHANGELOG/#changes_3","title":"Changes","text":"
  • Several modes can now be specified in a grammar (for further explanations see ANTLR). For the grammars, one file is now generated for lexer rules and one for the parser rules.
  • delete deprecated classes: ModelPath, IterablePath, ModelCoordinate, ModelCoordinateImpl, ModelCoordinates, and FileFinder
  • The symbol table now stores for productions whether a production is left-recursive.
  • In the log class the dependency to ch.qos.logback:logback-core was removed
"},{"location":"00.org/Explanations/CHANGELOG/#monticore-710","title":"MontiCore 7.1.0","text":"

released: 05.07.2021

"},{"location":"00.org/Explanations/CHANGELOG/#additions_3","title":"Additions","text":"
  • introduced language-specific CLI generation
  • New class MCPath that manages a set of path entries. The class is used, e.g., for realizing symbol paths, model paths, handcoded paths, and template paths. MCPath replaces the classes IterablePath and ModelPath.
  • Different modes can now be defined for lexical tokens in grammar. The corresponding generator will only be available in the next version.
"},{"location":"00.org/Explanations/CHANGELOG/#changes_4","title":"Changes","text":"
  • the methods serialize and deserialize of the class XDeSer were moved to the class XSymbols2Json
  • The following classes are marked as deprecated and will be removed in the near future: ModelPath, IterablePath, ModelCoordinate, ModelCoordinateImpl, ModelCoordinates, and FileFinder
"},{"location":"00.org/Explanations/CHANGELOG/#fixes_2","title":"Fixes","text":""},{"location":"00.org/Explanations/CHANGELOG/#monticore-700","title":"MontiCore 7.0.0","text":"

released: 08.04.2021

"},{"location":"00.org/Explanations/CHANGELOG/#additions_4","title":"Additions","text":"
  • resolveXSubKinds(..) resolves for local symbols of all subkinds of a symbol kind X. This method is used by the implementation of the resolveXLocally(..) method. It enables proper handling of symbol kind hierarchies during symbol resolution beyond the borders of a language.
  • new annotation @NonConservative for productions
  • add configTemplate (-cf) mechanism to add a freemarker template for customizing the generation processed
  • add two predefined groovy hook points (-gh1 and -gh2) in the monticore_standard.groovy for injecting custom groovy scripts into the workflow
"},{"location":"00.org/Explanations/CHANGELOG/#changes_5","title":"Changes","text":"
  • move grammars OCLExpressions and SetExpressions into OCL-project for further development
  • DefsTypeBasic was moved to test. There are now only methods for creating symbols. Use the BasicSymbolsMill to create the basic data types like int, ...
  • deserialize(String) method of scope DeSer classes is realized as default implementation in IDeSer interface
  • deserialize(String) method of symbol DeSer classes is realized as default implementation in ISymbolDeSer interface
  • deserializeAddons() and serializeAddons() methods of scopes are realized as empty default implementation in IDeSer interface
  • If deserialization encounters a symbol kind for which no DeSer is contained in the symbol Deser map in global scopes, a warning is produced instead of an error
  • Boolean isShadowing property of scopes is only serialized if its value is \"true\". Deserialization assumes a default value of \"false\" if the property is not contained in a serialized scope
  • deserialize(String) method of symbol DeSers do not produce errors if the serialized kind deviates from the symbol kind that the DeSer is originally engineered for
  • The TypeCheck was reworked
  • The interface ITypesCalculator was renamed to IDerive and can now be used similar to the ISynthesize interface
  • no SymbolSurrogates are created anymore by the TypeCheck. The Synthesize-Classes will now log an error if a type cannot be resolved
  • SymTypeExpressions now have the method printFullName to print their full name
  • The class TypeCheck now needs one IDerive and one ISynthesize for its constructor instead of only one of them
  • The class DeriveSymTypeOfBSCommonExpressions, which does not pay attention to modifiers like static or private, can now be used as an alternative for the class DeriveSymTypeOfCommonExpressions
"},{"location":"00.org/Explanations/CHANGELOG/#fixes_3","title":"Fixes","text":"
  • Symbols with hierarchical symbol kinds are not serialized multiple times anymore.
"},{"location":"00.org/Explanations/CHANGELOG/#monticore-670","title":"MontiCore 6.7.0","text":"

released: 26.01.2021

"},{"location":"00.org/Explanations/CHANGELOG/#additions_5","title":"Additions","text":"
  • Add new CLI for the MontiCore generator engine
"},{"location":"00.org/Explanations/CHANGELOG/#changes_6","title":"Changes","text":"
  • The context conditions use the new traverser infrastructure. This leads to small changes in the api. The return value of the method addCoCo is void.
  • Attribute fileExt in GlobalScopes now refers to a regular expression for file extensions of symbol table files. The default value of the attribute is \"*sym\", which usually includes symbol files of all MontiCore languages. Attention: If your language used the \"setFileExt\" method in previous versions of MontiCore to set the file extension of the model file (e.g., to \"aut\"), this will cause problems now as the symbol files of the language have differen file extensions (e.g., \"autsym). To fix this, it is sufficient to remove all invocations of \"setFileExt\" from the handwritten source code.
  • For scopes, artifact scopes, and global scopes: Moved abstract methods that do not have a language- specific name or (argument, return) type from language-specific interface to MontiCore-runtime interfaces
  • new experiment \"strules\" demonstrating the use of symbolrules and scoperules
  • deserialize methods in SymTypeExpressionDeSers do not have an enclosingScope argument anymore. Internally, it uses the singleton global scope instead.
  • renamed serializeAdditionalSSymbolAttributes in Symbols2Json class to serializeAddons and moved to scope and symbol DeSers.
  • XScopeDeSer is renamed to XDeSer
  • In Symbols2Json classes:
  • now implementss Visitor2
  • new attribute \"XTraverser traverser\" with getter and setter
  • Removed attribute \"realThis\" with getter and setter
  • New constructor with two arguments XTraverser and JsonPrinter
  • New zero args constructor
  • Removed constructor with single JsonPrinter argument
  • New attributes of all known symbol DeSers and current scope DeSers
  • New method \"protected void init()\", initializing the DeSer attributes with the GlobalScope and the traverser with symbols2json of inherited languages
  • adjusted store method to use traverser
  • visit methods for symbols delegate to serialize method of the symbol DeSer
  • visit and endVisit methods for scope interface and artifact scope interface print object stub and delegate serialization to scope DeSers
  • DeSers do not have an attribute of Symbols2Json class anymore, instead it is passed as argument in the serialize methods
  • Default values of built-in types that occur in attributes of symbolrules or scoperules are omitted during serialization and deserialization. The defaults are as follows:
  • Boolean : false
  • String : \"\"
  • Numeric types: 0 (and 0L and 0.0 and 0.0f)
  • For symbolrule and scoperule attributes with non-built-in data type, no Log.error is thrown at execution time of the serialize method call anymore. Instead, these methods (and then, their classes as well) are generated abstract to yield compilation errors instead.
  • New interface IDeSer that all symbol and scope DeSers implement.
  • GlobalScopes manage a map with all relevant DeSers. The map maps the serialized (symbol or scope) kind to the DeSer that (de)serialized this kind. This mechanism can be used to exchange the DeSer for a specific kind of symbol or scope.
  • Scope DeSers have new serialize methods without Symbols2Json argment that can be used for for serializing (artifact) scopes for, e.g., unit tests
  • removed the generation of XPhasedSymbolTableCreatorDelegator classes
  • Experiments now use ScopesGenitor-infrastructure instead of SymbolTableCreator-infrastructure
"},{"location":"00.org/Explanations/CHANGELOG/#fixes_4","title":"Fixes","text":"
  • The initMe and reset methods of the mill now initialize and reset all attributes properly

  • The CD4Analysis keywords ordered, composition, association, targetimport and classdiagram can be used in grammars again

"},{"location":"00.org/Explanations/CHANGELOG/#monticore-660","title":"MontiCore 6.6.0","text":"

released: 03.12.2020

"},{"location":"00.org/Explanations/CHANGELOG/#additions_6","title":"Additions","text":"
  • The mill of a language now provides a method parser() to get the parser of the language
    • mill initialization allows to reconfigure the mill to provide a parser for a sublanguage
    • parser delegator XForYParser are generated that extend a parser of a super language and delegate to the parser of the current language
    • Due to multiple inheritance, delegation and subclasses are used in combination
  • experiments now showcase the use of traversers
  • add coco (checks if additional attributes are declared twice)
  • added built-in primitive types to the mills of grammars that extend the grammar BasicSymbols. Add to Mill by executing BasicSymbolsMill.initializePrimitives()
"},{"location":"00.org/Explanations/CHANGELOG/#changes_7","title":"Changes","text":"
  • The generated parser uses the builder instead of the factory. This means that in grammars the variable _aNode is no longer available. Use instead _builder.
  • Multiple renamings and signature changes regarding the deser infrastructure
  • renamed XSymbolTablePrinter to XSymbols2Json
  • moved load and store methods form XScopeDeSer to XSymbols2Json
  • removed enclosing scope as method argument of symbol deser methods, as global scope shall be used instead
  • renamed deserializeAdditionalSSymbolAttributes to deserializeAddons
  • renamed deserializeAdditionalXScopeAttributes and deserializeAdditionalXScopeAttributes to deserializeAddons
  • added the JSON printer as a parameter to the methods of XScopeDeSer, SSymbolDeSer und XSymbols2Json
  • XScopeDeSer, SSymbolDeSer und XSymbols2Json are no longer available via the mill. The constructors can be used instead.
  • Scope builder have been removed as they did not support multiple inheritance, scope creation methods of the mill should be used instead
  • Shortened the name of the scope creation methods in the mill from xScope, xGlobalScope and xArtifactScope to scope, globalScope and artifactScope
  • Shortened the name of the modelFileExtension attribute in the XGlobalScope class to fileExt
  • renamed XScopeSkeletonCreator and XScopeSkeletonCreatorDelegator to XScopesGenitor and XScopesGenitorDelegator
  • Deprecated the XPhasedSymbolTableCreatorDelegator, will be removed without replacement in a future release
  • PrettyPrinters and other visitors in monticore-grammar now use the new Traverser infrastructure instead of the old Visitor infrastructure
  • generated XScopeGenitor and XScopeGenitorDelegator now use the new Traverser infrastructure instead of the old Visitor infrastructure
  • Changes to resolving
  • if name of a topLevelSymbol in ArtifactScope = name of ArtifactScope: qualify symbols in spanned scopes of the topLevelSymbol like before with <topLevelSymbolName>.<symbolName>
  • if name of a topLevelSymbol in ArtifactScope != name of ArtifactScope: qualify symbols in spanned scope of the topLevelSymbol with <ArtifactScopeName>.<topLevelSymbolName>.<symbolName>
  • Traverser now support lists of Visitor2 interfaces instead of only one instance
  • Rename accessor of Traverser from addXVisitor to add4X
  • Methods returning referenced symbols save the symbols instead of the surroogates
"},{"location":"00.org/Explanations/CHANGELOG/#fixes_5","title":"Fixes","text":"
  • Traverser now properly delegate to handlers as intended
  • ScopeSkeletonCreator now properly use the mill to create scope instances to ensure substitution via the mill pattern
  • Fixed a bug where the SymbolSurrogates wrongly qualified their fullName
  • The clear method of the GlobalScope now deletes all symbols stored in the GlobalScope
  • Serializing symbolrule attributes of Strings now works properly
"},{"location":"00.org/Explanations/CHANGELOG/#monticore-660_1","title":"MontiCore 6.6.0","text":"

released: 11.11.2020

"},{"location":"00.org/Explanations/CHANGELOG/#additions_7","title":"Additions","text":"
  • added an experiment hwDeSers showcasing serialization and deserialization
  • added an experiment hooks showcasing hook point usage
  • IncCheck provided by the MontiCore Gradle Plugin now considers local super grammar changes to trigger new generation
  • Added new Traverser generation to replace the visitor infrastructure in a future release
    • XTraverser
    • XTraverserImplementation
    • XVisitor2
    • XHandler
  • Added new ScopeSkeletonCreator generation to replace the SymbolTableCreator in a future release and to enable a phased symboltable creation
    • XScopeSkeletonCreator
    • XScopeSkeletonCreatorDelegator
    • XPhasedSymbolTableCreatorDelegator
  • Added methods to directly obtain instances of the following classes in the mill (instead of their builders)
    • XSymbolTableCreator
    • XSymbolTableCreatorDelegator
    • XScopeSkeletonCreator
    • XScopeSkeletonCreatorDelegator
    • XPhasedSymbolTableCreatorDelegator
    • XScopeDeSer
    • XSymbolDeSer
    • XSymbolTablePrinter
    • IXScope
    • IXArtifactScope
"},{"location":"00.org/Explanations/CHANGELOG/#changes_8","title":"Changes","text":"
  • MontiCore now uses Gradle as build tool
  • some tasks have been introduced for the comfortable control of frequent activities, e.g., buildMC, assembleMC that can be found in the build.gradle
  • relocated the EMF related subprojects:
    • monticore-emf-grammar to monticore-grammar-emf
    • monticore-emf-runtime to monticore-runtime-emf
  • relocated integration tests and experiments:
    • monticore-generator/it to monticore-test/it
    • monticore-generator/it/experiments to monticore-test/01.experiments
    • monticore-generator/it/02.experiments to monticore-test/02.experiments
    • monticore-grammar/monticore-grammar-it to monticore-test/monticore-grammar-it
  • Remove the generation of XModelloader. Languages should now use XScopeDeSer to load symbol tables instead.
  • Removed the generation of the following builder classes (also from the Mill; alternative solutions described below)
    • XSymbolTableCreatorBuilder
    • XSymbolTableCreatorDelegatorBuilder
    • XScopeDeSerBuilder
    • XSymbolDeSerBuilder
    • XSymbolTablePrinterBuilder
  • renamed IXResolvingDelegate to IXResolver
  • outsourced Type expressions for arrays to a separate grammar
  • was FullGenericTypes, is now MCArrayTypes
  • outsourced initialization for arrays to a separate grammar
  • was MCVarDeclarationStatements, is now MCArrayStatements
  • In a composed language, mills of super languages now provide scope instances (scope, global scope and artifact scope) for the composed language
  • non-existing template paths now result in an error instead of a warning
  • Set current visitor infrastructure to deprecated
  • Integrate new visitor infrastructure (i.e., traverser) into XMill to enable re-usability of visitors via language inheritance
  • Set SymbolTableCreator, SymbolTableCreatorDelegator and their builder to deprecated
  • Integrate new ScopeSkeletonCreator, ScopeSkeletonCreatorDelegator and PhasedSymbolTableCreatorDelegator into Mill
  • Added a method clear to the GlobalScope that clears its cache and its resolvers and empties its ModelPath
"},{"location":"00.org/Explanations/CHANGELOG/#fixes_6","title":"Fixes","text":"
  • Fixed that global variable changes in child templates were not changed in parents
  • Fixed handling of optional names of symbols in symbol table creator
  • Fixed an issue where surrogates hide symbol attributes
"},{"location":"00.org/Explanations/CHANGELOG/#monticore-640","title":"MontiCore 6.4.0","text":"

released: 12.10.2020

"},{"location":"00.org/Explanations/CHANGELOG/#additions_8","title":"Additions","text":"
  • extended the generated incCheck files to contain information about local super grammars
    • the sh-file is now able to trigger generation if local super grammars are changed
    • the incCheck method provided by the plugin will support this behavior as well
    • will only be available in the next release
  • extended the mill to manage the global scope instance centrally
  • added comfort methods for creating modifiers to the ModifierBuilder
    • ModifierBuilder().PUBLIC() short for ModifierBuilder().setPublic(true)
  • added MCShadowingJavaBlock to MCCommonStatements
    • standard MCJavaBlock is no longer shadowing
  • added a class diagram to the reports that represents the generated data structure for the given grammar (ast, symbol table visitors, etc.)
  • added simple BreakStatement to MCCommonStatements
  • added an include2 alias for the template controller method for including templates in conjunction with templates arguments
"},{"location":"00.org/Explanations/CHANGELOG/#changes_9","title":"Changes","text":"
  • CLI does no longer check whether a generation is needed (this should be handled by the build tool)
  • rephrased messages for non-conservative extension (added super grammar name)
  • added a context condition to prevent list of names in nonterminal production marked as symbols
  • might be supported in a future version of MontiCore
  • moved XForYMills to a subpackage to reduce noise (subpackage: _auxiliary)
  • deprecated the generated enum f\u00fcr constants
    • will be removed without replacement in a future release
  • moved EnhancedForControl production from JavaLight to MCCommonStatements as it is commonly used
  • standard MCJavaBlock is no longer shadowing
  • renamed BreakStatement in MCLowLevelStatements to LabelledBreakStatement
  • ForStatement now spans a non-exporting, ordered scope
  • shortened generated error codes to have 5 digits only
  • renamed MethOrConstr to JavaMethod in JavaLight
  • MontiCore Gradle plugin is no longer shipped as a fat jar
"},{"location":"00.org/Explanations/CHANGELOG/#fixes_7","title":"Fixes","text":"
  • Fixed error code calculation for generated error messages to no longer be random
  • Fixed the report for involved files to contain handwritten files that were considered
    • will only be available in the next release
  • Fixed an issue where reports did not contain meaningful names for elements such as class diagram classes or interfaces
"},{"location":"00.org/Explanations/CHANGELOG/#monticore-630","title":"MontiCore 6.3.0","text":"

released: 16.09.2020

"},{"location":"00.org/Explanations/CHANGELOG/#additions_9","title":"Additions","text":"
  • added @Override annotation for nonterminal production to state that this production overrides a super grammars' production
  • overriding without annotation leads to a warning
  • using the annotation for a production that does not override an existing nonterminal results in an error
  • added a context condition to ensure that external production do not have ast rules
  • added DiagramSymbol in BasicSymbols
  • introduced generated interfaces for GlobalScope and ArtifactScope
"},{"location":"00.org/Explanations/CHANGELOG/#changes_10","title":"Changes","text":"
  • serialization of symtype expression now serializes full name of symtype instead of simple name
  • class ASTNodes is now deprecated and its usages in the generator are removed
  • visitors no longer provide visit methods for concrete scope classes but their interfaces instead
  • SymTypeExpression no longer use surrogates but TypeSymbols instead
  • reverted changes to appended s for list attributes made in previous release
  • moved initialization of symbols to the endVisit method of the SymbolTableCreator
"},{"location":"00.org/Explanations/CHANGELOG/#fixes_8","title":"Fixes","text":"
  • Fixed missing sourcecode position for overriding warning
  • Fixed an issue where the inheritance hierarchy was no considered correctly when overriding a nonterminal
"},{"location":"00.org/Explanations/CHANGELOG/#monticore-620","title":"MontiCore 6.2.0","text":"

released: 21.07.2020

"},{"location":"00.org/Explanations/CHANGELOG/#additions_10","title":"Additions","text":"
  • added isFinal to OOType in OOSymbols
  • extended the mill such that builder for DeSer related classes are provided by the mill
  • added support for symbol usages in NonterminalSeperator
    • example: Bar = (bla:Name@Foo || \",\" )+;
  • added reports for the symbol table structure of the processed grammar
  • added isReadOnly to Variable in BasicSymbols
  • added isElliptic to Method in TypeSymbols
  • added a context condition to warn if keywords consist of numbers only
    • these numbers will be tokenized as keywords instead of numbers
  • added splittoken to express that the listed tokens should be split and not handled as a single token
    • example: splittoken \":::\"; results in three token :
  • added nokeyword to express that the listed keywords should not be handled as tokens
  • example: nokeyword \"automaton\", \"state\"; means that automaton and state should not be handled as keywords
  • introduced symbol inheritance
"},{"location":"00.org/Explanations/CHANGELOG/#changes_11","title":"Changes","text":"
  • renamed de.monticore.type.TypeSymbols to de.monticore.symbols.OOSymbols
  • renamed de.monticore.type.BasicTypeSymbols to de.monticore.symbols.BasicSymbols
  • reworked appended s for list attributes
  • renamed SymbolLoader to SymbolSurrogate
  • Surrogates are now subclasses of their corresponding symbols
  • MCJavaBlock in MCCommonStatements now spans a shadowing, non-exporting, ordered scope
  • MethodDeclaration and ConstructorDeclaration in JavaLight use MCJavaBlock instead of MCBlockStatement
  • Label in MCLowLevelStatement now is a symbol
  • VarDecl in MCVarDeclarationStatements no longer exists
    • DeclaratorId now produces FieldSymbols
  • removed isParameter and isVariable from Field in TypeSymbols
  • the language class is no longer generated
  • moved creator expressions to JavaClassExpression
  • moved PlusExpression and MinusExpression from AssignmentExpressions to CommonExpressions
"},{"location":"00.org/Explanations/CHANGELOG/#fixes_9","title":"Fixes","text":"

Fixed an issue where super and subtype comparison was wrong in type check Fixed handling of capital letters in grammar package * using capital letters now produces a warning * Fixed an issue were setAbsent methods in the generated SymbolBuilder where not properly overridden * Fixed that non-shadowing scopes where not handled as intended

"},{"location":"00.org/Explanations/CHANGELOG/#monticore-610","title":"MontiCore 6.1.0","text":"

released: 07.05.2020

"},{"location":"00.org/Explanations/CHANGELOG/#monticore-600","title":"MontiCore 6.0.0","text":"
  • Uses CD4Analysis 1.5.0
  • replace get*opt methods with get*
  • bugfixing
"},{"location":"00.org/Explanations/CHANGELOG/#monticore-5401","title":"MontiCore 5.4.0.1","text":"
  • Uses CD4Analysis 1.4.0
  • add generation of serializers for grammars
  • add SymbolLoader
  • remove SymbolReferences
  • add DeSers for TypeSymbols
  • improved TypeCheck
  • replace getName methods with printType methods
"},{"location":"00.org/Explanations/CHANGELOG/#monticore-530","title":"MontiCore 5.3.0","text":"
  • Uses CD4Analysis 1.3.20.2
  • new Generator based on Decorator-Pattern
  • add Translation classes
  • add grammar it-tests
  • move TypesCalculator to TypeCheck, create derive classes and synthesize classes
  • add TypeSymbols and SymTypeExpression structure
  • added DeSers for SymTypeExpressions
  • added keyword \"key\" for KeyTerminals
"},{"location":"00.org/Explanations/CHANGELOG/#monticore-520","title":"MontiCore 5.2.0","text":"
  • add \"List\"-Suffix to attribute name
"},{"location":"00.org/Explanations/CHANGELOG/#monticore-510","title":"MontiCore 5.1.0","text":"
  • Remove the dependency to JavaDSL, add JavaLight
  • Uses CD4Analysis 1.3.19
  • added grammar TypeSymbols
  • renamed SymbolDelegateList to SymbolResolvingDelegateList
  • add methods for scoperule-attributes in interfaces
  • add MCTypeVisitor to transform ASTTypes to TypeExpressions
  • add Groovy Plugin
  • add MontiCore Statements at de.monticore.statements
"},{"location":"00.org/Explanations/CHANGELOG/#monticore-506","title":"MontiCore 5.0.6","text":"
  • The IncGen-reports are stored in the source code directory
  • Removed MutableScope
  • IncGen-Reports are stored
  • Removed deprecated keyword ast (use astrule) in *.mc4
  • Add visitors for symbol table
  • Enable TOP mechanism for visitors
  • add SymbolRules and ScopeRules
  • renamed MCBasicLiterals to MCCommonLiterals, add MCLiteralsBasis
  • move literals to package de.monticore.literals
  • renamed ShiftExpressions to BitExpressions
"},{"location":"00.org/Explanations/CHANGELOG/#monticore-503","title":"MontiCore 5.0.3","text":"
  • Use the following emf coordinates (MB):
  • group: org.eclipse.emf
  • version: 2.15.0
  • artifact: org.eclipse.emf.ecore | org.eclipse.emf.ecore.xmi | org.eclipse.emf.common
  • The runtime environment may need the following dependency (group: org.eclipse.platform; artifacitId: org.eclipse.equinox.common; version: 3.10.0)
  • splitted Types.mc4 in MCBasicTypes, MCCollectionTypes, MCSimpleGenericTypes and MCFullGenericTypes
  • moved expressions to de.monticore.expressions and added expressions
"},{"location":"00.org/Explanations/CHANGELOG/#monticore-502","title":"MontiCore 5.0.2","text":"
  • Generated by the MontiCore version 5.0.1
  • Uses JavaDSL 4.3.13, Cd4Analysis 1.3.16, se-commons 1.7.9
  • Introduce deprecated annotation in grammars (#2215)
  • Serialization of symobls
  • Add reporter IncGenCheckReporter
  • Configuration of the report path
  • Specific resolving methods in generated scope classes
  • Bugfixes
"},{"location":"00.org/Explanations/CHANGELOG/#monticore-501","title":"MontiCore 5.0.1","text":"
  • Generated by the MontiCore version 5.0.0
  • Uses JavaDSL 4.3.12, Cd4Analysis 1.3.13, se-commons 1.7.9
  • Bugfixes
  • New methods defineHookPointWithDefault in GlobalExtensionManagement (MB)
  • new method cmpToken in MCParser (MB)
  • every (non-)terminal defined in an interface must be present in the implementing production (including Name and Usage Name) (CoCo) (BS)
  • to ensure that any terminal (with a specific name) has to be implemented, use an empty string, e.g. interface Expression = operator=\"\";
  • new methods are generated for the referenced symbol and definition and the definition is saved in an attribute (generated wenn you write sth. like \"Name@Symbol\") (NP)
  • coco that gives a warning if you do not extend conservative (NP)
  • coco that attributes with the same usage Name have to reference the same symbol (NP)
  • SpannedScope and Symbol Methods in ASTNode set to deprecated (NP)
"},{"location":"00.org/Explanations/CHANGELOG/#monticore-500","title":"MontiCore 5.0.0","text":"
  • Generated by the MontiCore version 4.5.5.1
  • Uses JavaDSL 4.3.11, Cd4Analysis 1.3.13, se-commons 1.7.8
  • Changed name building for list attributes in grammars (x:Name* -> getXList)
  • Changed api for GlobalExtensionMangament and TemplateController (see reference manual)
  • New api for AST nodes (constructor, getter and setter for lists and optional attributes, ...)
  • Builder classes for AST nodes are external now. Signatures are similar to those in the corresponding AST node, except those methods which set or add something, these return the Builder itself (which allows method chaining)
  • Changed default script to noemf for the generation of MontiCore. If you want to use emf you can generate monticore-grammar and Java-DSL with the profile \"emf\". This profile also creates the emf jars. You are also able to test the integration-tests with the profile \u201cemf-it-tests\u201d, which contains extra tests for the generation with emf.
  • Parsed grammars are not stored as class diagram anymore. CD is only stored as report.
  • Removed deprecated method filter(ResolvingInfo resolvingInfo, List symbols) use filter(ResolvingInfo, Collection) instead
  • Removed deprecated method filter(ResolvingInfo resolvingInfo, String name, List symbols) use filter(ResolvingInfo, String, Map) instead
  • Removed deprecated method create(Class symbolClass, SymbolKind symbolKind) use create(SymbolKind) instead
  • Removed deprecated method getSymbols use getLocalSymbols instead
  • Removed deprecated method resolve(SymbolPredicate predicate) use resolveMany(String, SymbolKind, Predicate) instead
  • Removed deprecated method define use add instead
  • Removed deprecated method resolve(ResolvingInfo resolvingInfo, String name, SymbolKind kind, AccessModifier modifier)
  • Removed deprecated method checkIfContinueWithEnclosing use checkIfContinueWithEnclosingScope instead
  • Removed deprecated method addResolver use addFilter(String, ResolvingFilter) instead
  • Removed deprecated method addTopScopeResolver use addDefaultFilter instead
  • Removed deprecated method addTopScopeResolvers use addDefaultFilters instead
  • Removed deprecated method getTopScopeResolvingFilters use getDefaultFilters instead
  • Removed deprecated constructer CommonResolvingFilter(Class symbolClass, SymbolKind targetKind) use CommonResolvingFilter(SymbolKind) instead
  • Removed deprecated method continueWithScope and continueWithEnclosingScope
  • Removed class FaildLoadingSymbol
  • Removed deprecated method putInScopeAndLinkWithAst use addToScopeAndLinkWithNode instead
  • Removed deprecated constructer CommonModelingLanguage(String, String, SymbolKind) use CommonModelingLanguage(String, String) instead
  • Removed deprecated method addResolver use addResolvingFilter instead
  • Removed deprecated method addResolver use addResolvingFilter instead
  • Removed deprecated method getResolvers use getResolvingFilters instead
  • Removed deprecated method loadAmbiguousModelAndCreateSymbolTable use loadModelsIntoScope instead
  • Removed deprecated method loadAmbiguousModels use loadModels instead
  • Removed deprecated method defineHookPoint(String) use glex.defineHookPoint instead
  • Removed deprecated enum ParserExecution
  • Removed deprecated method getParserTarget
  • Removed deprecated method setParserTarget
  • "},{"location":"00.org/Explanations/CHANGELOG/#further-information","title":"Further Information","text":"
    • Project root: MontiCore @github
    • MontiCore documentation
    • List of languages
    • MontiCore Core Grammar Library
    • Best Practices
    • Publications about MBSE and MontiCore
    • Licence definition
    "},{"location":"00.org/Explanations/FAQ/","title":"FAQ","text":""},{"location":"00.org/Explanations/FAQ/#faq-for-monticore","title":"FAQ for MontiCore","text":""},{"location":"00.org/Explanations/FAQ/#using-maven","title":"Using Maven","text":"
    1. Eclipse shows me an error stating that my project configuration is not up-to-date.
    2. I get an error saying something about Lifecycle Mappings.
    3. Maven build fails because of a missing JDK path.
    4. Maven build for de.monticore.parent project fails in eclipse
    5. I have a very weird problem, seriously, very weird ...
    6. My .m2 folder does not exist.
    7. I get a strange error telling me that something is wrong with the UTF8 encoding.
    8. Changes I made on one module are not reflected in another module.
    1. Eclipse shows me an error stating that my project configuration is not up-to-date. Right-click that project and select Maven -> Update Project Configuration.

    2. I get an error saying something about Lifecycle Mappings. Install the m2e extensions mentioned in the developer tutorial.

    3. Maven build fails because of a missing JDK path. Change the installed runtime JREs to the installed JDK. Go to Window -> Preferences -> Installed JREs. Add the JDK path and select it to be the default one.

    4. Maven build for de.monticore.parent project fails in eclipse. Go to Window -> Preferences -> General -> Workspace. Disable \"Build automatically\" preference.

    5. I have a very weird problem, seriously, very weird ... Right-click that project and select Maven -> Update Project Configuration.

    6. My .m2 folder does not exist. Folders in Windows with a leading \".\" can only be created using the command line. Start the command line and type in \"mkdir .m2\" in your home folder.

    7. I get a strange error telling me that something is wrong with the UTF8 encoding. Change the UTF8 encoding by clicking on Window -> Preferences. Then, select the item as shown below and change the values accordingly.

    8. Changes I made on one module are not reflected in another module. Remember that all Maven modules are independent units. By default, they are not directly imported into each other. Instead, Maven resolves dependencies between projects by selecting packages (e.g.jar files) produced by these modules from your local Maven dependency repository. To make the latest version of a module available through this repository, you have to explicitly install it. If you execute an install on an aggregating POM-project, all child modules will be built with the current state of their depending projects as Maven always builds a hierarchy of modules in order of their mutual dependencies. However, if you are working in Eclipse, the workbench can import modules live. This feature is called \"Workspace resolution\" and is enabled by default for Eclipse automatic project builders. Nevertheless, if you build a module using a Run Configuration you have to explicitly activate \"Resolve Workspaces artifacts\".

    "},{"location":"00.org/Explanations/FAQ/#further-information","title":"Further Information","text":"
    • MontiCore project - MontiCore
    • Project root: MontiCore @github
    • MontiCore documentation
    • List of languages
    • MontiCore Core Grammar Library
    • Best Practices
    • Publications about MBSE and MontiCore
    • Licence definition
    "},{"location":"00.org/Explanations/StatusOfGrammars/","title":"StatusOfGrammars","text":""},{"location":"00.org/Explanations/StatusOfGrammars/#monticore-grammar-status-plans-an-overview","title":"MontiCore Grammar Status Plans - an Overview","text":"

    MontiCore uses grammars as primary mechanism to describe DSLs and DSL components. The extended grammar format allows to compose language components by (1) inheriting, (2) extending, (3) embedding and (4) aggregating grammars (see the reference manual for details). From the grammars a lot of infrastructructure is generated, that is as well composable, can be extended with handwrittten code and most imprtandly, these extensions and the grammar composition are compatible, which leads to optimal forms of reuse.

    To improve understanding, what will happen with a grammar, we define the following set of stati and mention the status of each grammar, both in the explanation and in the grammar itself:

    "},{"location":"00.org/Explanations/StatusOfGrammars/#status-of-a-grammar","title":"Status of a Grammar","text":"
    1. MontiCore stable: Such a grammar is meant to be stable in the further development of MontiCore. The grammar is tested and assumed to be of high quality. It may rarely happen that smaller extensions are made in a conservative form, which means that (1) composition with any other grammars, (2) extensions and adaptations and (3) handwritten extensions will still work.

    2. Beta: In Stabilization: Such a grammar is in the process of becoming stable. One might already include the grammar, but some changes may still appear. (See task list for potential changes.)

    3. Alpha: Intention to become stable: Such a grammar is relatively fresh, but intended to become stable and useful. Changes may occur, e.g. when restructuring or bug fixing. Or it may be taken out of the process and become one of the following:

    4. Example: The grammar serves as working example, but will not have high priority on keeping the grammar up to date. One might use it as inspiration for their own developments.

    5. Deprecated: The grammar should not be used anymore, it is deprecated, and only there for compatibility. Normally a newer version of the content exists in other, often decomposed grammars, allowing to configure which part of the grammar to be used. Deprecated grammars are not listed in any overview.

    "},{"location":"00.org/Explanations/StatusOfGrammars/#marking-the-status-of-grammars","title":"Marking the Status of Grammars","text":"

    A comment of the following form within the grammar defines this status:

    1. /* This is a MontiCore stable grammar. * Adaptations -- if any -- are conservative. */
    2. /* Beta-version: This is intended to become a MontiCore stable grammar. */
    3. /* Alpha-version: This is intended to become a MontiCore stable grammar. */ (but sometimes also omitted)
    "},{"location":"00.org/Explanations/StatusOfGrammars/#further-information","title":"Further Information","text":"
    • Project root: MontiCore @github
    • MontiCore documentation
    • List of languages
    • MontiCore Core Grammar Library
    • Best Practices
    • Publications about MBSE and MontiCore
    • Licence definition
    "},{"location":"00.org/Licenses/LICENSE-BSD3CLAUSE/","title":"LICENSE BSD3CLAUSE","text":"

    Copyright (c) MontiCore*, All rights reserved.

    Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

    1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

    2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

    3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

    * belongs to RWTH and RIDT

    "},{"location":"00.org/Licenses/LICENSE-LGPL/","title":"LICENSE LGPL","text":""},{"location":"00.org/Licenses/LICENSE-LGPL/#gnu-lesser-general-public-license","title":"GNU LESSER GENERAL PUBLIC LICENSE","text":"

    Version 3, 29 June 2007

    Copyright (C) 2007 Free Software Foundation, Inc. http://fsf.org/ Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.

    This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below.

    1. Additional Definitions.

    As used herein, \"this License\" refers to version 3 of the GNU Lesser General Public License, and the \"GNU GPL\" refers to version 3 of the GNU General Public License.

    \"The Library\" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below.

    An \"Application\" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library.

    A \"Combined Work\" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the \"Linked Version\".

    The \"Minimal Corresponding Source\" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version.

    The \"Corresponding Application Code\" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work.

    1. Exception to Section 3 of the GNU GPL.

    You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL.

    1. Conveying Modified Versions.

    If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version:

    a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or

    b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy.

    1. Object Code Incorporating Material from Library Header Files.

    The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following:

    a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License.

    b) Accompany the object code with a copy of the GNU GPL and this license document.

    1. Combined Works.

    You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following:

    a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License.

    b) Accompany the Combined Work with a copy of the GNU GPL and this license document.

    c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document.

    d) Do one of the following:

       0. Convey the Minimal Corresponding Source under the terms of this\n   License, and the Corresponding Application Code in a form\n   suitable for, and under terms that permit, the user to\n   recombine or relink the Application with a modified version of\n   the Linked Version to produce a modified Combined Work, in the\n   manner specified by section 6 of the GNU GPL for conveying\n   Corresponding Source.\n\n   1. Use a suitable shared library mechanism for linking with the\n   Library.  A suitable mechanism is one that (a) uses at run time\n   a copy of the Library already present on the user's computer\n   system, and (b) will operate properly with a modified version\n   of the Library that is interface-compatible with the Linked\n   Version.\n

    e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 5d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 5d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.)

    1. Combined Libraries.

    You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following:

    a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License.

    b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work.

    1. Revised Versions of the GNU Lesser General Public License.

    The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.

    Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License \"or any later version\" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation.

    If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library.

    "},{"location":"00.org/Licenses/LICENSE-MONTICORE-3-LEVEL/","title":"License","text":""},{"location":"00.org/Licenses/LICENSE-MONTICORE-3-LEVEL/#monticore-3-level-license-model","title":"MontiCore 3-Level License Model","text":"

    MontiCore is a language workbench for an efficient development of domain-specific languages (DSLs). All the code available in these GitHub MontiCore projects is published under three levels of licenses as discussed below.

    For a full use of generated code in commercial and any other forms of projects, the finally generated code is, completely freely available, even though the main workbench itself has restrictions.

    "},{"location":"00.org/Licenses/LICENSE-MONTICORE-3-LEVEL/#license-overview","title":"License Overview","text":"

    The MontiCore Language Workbench deals with three levels of code:

    • (Level 3) MontiCore: the main library constituting the LWB,

    • (Level 2) tool derivates that are to a large extent generated by the MontiCore LWB, and

    • (Level 1) product code that is finally generated by tool derivates. This also includes analytical results, thus as results of consistency checks, code smells, test infrastructures, etc.

    Each level has its own and more relaxing license:

    • (Level 1) Product code: the generated product code is absolutely free for each form of use including commercial use without any mentioning and thus without any restriction from MontiCore.

    • (Level 2) Tool derivate: when a tool is derived using the MontiCore language workbench, then the result falls under the pretty liberal BSD 3 Clause license (see BSD-3-Clause).

    • (Level 3) MontiCore: the main LWB components are published in GitHub under the LGPL license (see LGPL V3.0).

    As a consequence using MontiCore during development is rather liberal and the final products do not have any restriction.

    Please note that this license level model holds for the MontiCore LWB and all related projects published in GitHub. For artefacts available from other sources, different licenses may apply. E.g. developers of tools may impose their own form of restrictions i.e. licenses on their tools respectively the results generated by these tools. Artefacts directly made available from RWTH Aachen and not published in GitHub are for the concretely granted purpose only and are not do be made public at all.

    As usual in software development: For statistics, scientific reasons, quality and performance improvement, the tools occasionally send the fully anonymous statistics report (see file) to the developers.

    "},{"location":"00.org/Licenses/LICENSE-MONTICORE-3-LEVEL/#monticore-3-level-license-on-files","title":"MontiCore 3-Level License on Files","text":"

    This repository for the MontiCore language workbench contains three kinds of artifacts:

    • Java-files that are executed in the MontiCore LWB. They are under LGPL licence.

    • Java-files that belong to the runtime environment (RTE) and are thus copied to the generated code. They are under BSD 3 Clause license.

    • Templates executed during generation of tool code. They are also only under BSD 3 Clause license, because parts of them are copied to the generated code.

    "},{"location":"00.org/Licenses/LICENSE-MONTICORE-3-LEVEL/#monticore-3-level-license-on-tool-and-language-repositories","title":"MontiCore 3-Level License on Tool and Language Repositories","text":"

    Other MontiCore repositories contain complete or incomplete tools as well as MontiCore language components. They contain again three kinds of artifacts:

    • Grammars that are used to define language components in the MontiCore LWB. They are under LGPL licence, but these can be extended by own grammars freely.

    • Java-files that are executed in the tool (belonging to the tool RTE). They are under BSD 3 Clause license.

    • Java-files that belong to the product runtime environment (RTE) are completely free without restriction.

    • Templates executed by the tool during generation of product code. They also are completely free without restriction, because parts of them are copied to the generated code.

    As a result, a tool derivate (level 2) does not contain any LGPL code, but only BSD 3 Clause code. Executing the tool derivate then produces completely free code (level 1).

    If questions appear e.g. on using MontiCore itself in a product or building an interpreter, please contact monticore@se-rwth.de.

    "},{"location":"00.org/Licenses/LICENSE-MONTICORE-3-LEVEL/#underlying-licenses","title":"Underlying Licenses","text":"

    The MontiCore 3 Level license is built on:

    • LGPL V3.0

    • BSD-3-Clause

    Please also note the general disclaimer from the BSD 3 Clause license on liability, etc.

    "},{"location":"00.org/Licenses/LICENSE-MONTICORE-3-LEVEL/#included-software","title":"Included Software","text":"

    This product includes the following software, both having their own licenses, compatible with MontiCores licenses:

    • AntLR
    • FreeMarker
    "},{"location":"00.org/Licenses/LICENSE-MONTICORE-3-LEVEL/#further-information","title":"Further Information","text":"
    • see also MontiCore Reference Manual

    • MontiCore project - MontiCore

    "},{"location":"docs/BestPractices-CLI/","title":"BestPractices CLI","text":""},{"location":"docs/BestPractices-CLI/#monticore-best-practices-designing-tools-for-command-line-interfaces","title":"MontiCore Best Practices - Designing Tools for Command Line Interfaces","text":"

    Some DSLs require a tool to enable general accessibility via the command line interface (CLI). When designing a tool, we recommend some standard guidelines.

    "},{"location":"docs/BestPractices-CLI/#designing-a-tool","title":"Designing a Tool","text":"

    The tool provides a general interface for the functionalities developed for a language. This includes all features such as parsing of models, saving and loading of symbol tables, pretty printing, reporting, or export as object diagram.

    "},{"location":"docs/BestPractices-CLI/#default-options","title":"Default Options","text":"

    The available options are of course language-specific. However, we suggest some default arguments for standardized access.

    -h,--help                    Prints this help dialog\n-i,--input <file>            Reads the (mandatory) source file resp. the\n                             contents of the model\n-path <dirlist>              Sets the artifact path for imported symbols, space separated\n-modelpath <dirlist>         Sets the artifact path for imported models, space separated\n-pp,--prettyprint <file>     Prints the AST to stdout or the specified output \n                             file (optional)\n-s, --symboltable <file>     Serializes and prints the symbol table to stdout \n                             or the specified output file (optional) \n-r,--report <dir>            Prints reports of the parsed artifact to the\n                             specified directory (optional). Available reports\n                             are language-specific\n-o,--output <dir>            Path of generated files (optional)\n-so,--syntaxobjects <file>   Prints an object diagram of the AST to stdout or\n                             the specified file (optional)\n-sc,--script <file>          Advanced configuration 2: through a groovy script \n                             that allows to adapt and extend the tool workflow (optional) \n                             (only some tools provide groovy scripting)\n-ct, --configtemplate        Advanced configuration 1: through a Freemarker template\n                             that allows to adapt the generation process (optional)\n                             (only some tools provide a template configuration call)\n

    An example of a complete yet relatively small tool example can be found in the JSON project.

    Some explanation to the arguments: * The tool is meant for handling one individual model (-i) and store the results appropriately in files. * Typical results are * (1) generated files (-o) that are used in the next step of the build process (e.g. for compilation). * (2) the symboltable (-s) that is then used by other tools to import symbols * (3) reports (-r) and internal information (-so), like the AST of the parsed model usable for developers to understand what happened * (4) and potentially also internal information on used input and generated output files that allows the calling build script to understand whether a redo is needed (as part of a larger incremental and efficient development process). * Directories in -path are separated via spaces, i.e. each path is an argument on its own. Example: -path a/b x/y. * Directories in the above options -path, -o describe the root structure that is further refined by packages (like in Java). That means with -path a/b x/y the actual symboltable for a Statechart de.mine.Door is found in a/b/de/mine/Door.scsym or x/y/de/mine/Door.scsym (in that order) * Languages typically only load other symbols rather than other models. Therefore, the argument -path that identifies only paths containing symbols should be implemented by most languages, whereas the argument -modelpath for identifying paths containing models is typically not required. * Groovy-scripting (-sc, --script): A Groovy Script is meant to describe the tool internal workflow. It controls parsing, symbol construction, reporting, code generation etc. This kind of scripting should only become necessary when various alternative configurations are possible. Thus, not every tool provides Groovy scripting. * Template-scripting (-ct, --configtemplate): It is possible to add a custom template script right before the full generation process starts. This template is useful to customize the generation process e.g. by defining hook points and thus injection more templates or switching verbosity on/off.

    "},{"location":"docs/BestPractices-CLI/#usage-of-the-tool-jar","title":"Usage of the Tool-JAR","text":"

    A note to the tool usage: Tools do not organize the correct order of their calls. If embedded in a larger build process, an appropriate gradle (preferred) or make it is useful for incremental efficiency.

    This organisation is above the tool, due to the efficiency of the (grade or make) buildscript itself, which must be able to decide, whether a redo is needed. If the tool was called to decide that, too much time was already wasted.

    For a build script to decide whether to call the tool or not, a tool call should (and actually MontiCore does) provide among others a list of files it used for input.

    "},{"location":"docs/BestPractices-CLI/#automatically-generating-a-tool-jar","title":"Automatically Generating a Tool-JAR","text":"

    Note to the tool development: To automatically derive an executable JAR from the Gradle build process for the corresponding tool, the following template can be used.

    // all in one tool-jar\nshadowJar {\n    manifest {\n        attributes \"Main-Class\": \"de.monticore.${archiveBaseName.get().capitalize()}Tool\"\n    }\n    archiveFileName = \"MC${archiveBaseName.get()}.${archiveExtension.get()}\"\n    minimize()\n    archiveClassifier = \"mc-tool\"\n}\n\njar.dependsOn shadowJar\n
    This blueprint can be used in the build.gradle script to derive a JAR for the tool class and its provided command line functionalities. The packed JAR already contains all the necessary dependencies. The template defines the main class and name of the JAR. To foster automated reuse, the template has already been configured to generate a suitable JAR for each language project without manual adjustments. However, this requires adhering to the following conventions: * The name of the main class is equal to the language project name (usually defined in the settings.gradle) with the suffix Tool. Furthermore, the first letter of the main class is always capitalized to adhere to the Java code conventions * The package of the main class is de.monticore * The generated JAR can be found in 'target/libs'

    Example: For a language project MyLang we have to implement the MyLangTool.java located in the package de.monticore. This automatically generates the executable JAR MCMyLang.jar

    In general, the template can be customized by specifying the corresponding main class and JAR name definitions. However, we recommend to use the predefined automatic approach.

    "},{"location":"docs/BestPractices-CLI/#functional-approach","title":"Functional Approach","text":"

    When implementing the tool, we recommend a functional paradigm to provide the desired functionalities, as the too class is not about data structures but only exists to make functions available. In this case it would be counterproductive to store the arguments of the available functions as attributes. Instead, it makes more sense to pass these arguments as parameters when calling the respective methods. This yields several advantages:

    • Values that have not yet been set do not have to be displayed with Optionals
    • As a result. tedious unwrapping of Optionals with corresponding error messages is no longer necessary
    • get/set methods for attributes are not required
    • Facilitates reusability of modular functions

    Of course, there are always trade-offs, but a more explicit functional way of thinking should be considered more intensively, especially when it is not about data structures but about the functions. For instance, if intermediate results are stored for efficiency reasons, this might a good argument to do it differently.

    "},{"location":"docs/BestPractices-CLI/#further-information","title":"Further Information","text":"
    • Project root: MontiCore @github
    • MontiCore documentation
    • List of languages
    • MontiCore Core Grammar Library
    • Best Practices
    • Publications about MBSE and MontiCore
    • License definition
    "},{"location":"docs/BestPractices-Errors/","title":"BestPractices Errors","text":""},{"location":"docs/BestPractices-Errors/#monticore-best-practices-understanding-errors-defining-errors","title":"MontiCore Best Practices - Understanding Errors, Defining Errors","text":"

    Errors happen. Some happen because of faults in the code (we call that internal errors), some happen because we haven't explained well how to use MontiCore and how to use the generated code.

    Here we try to add information how to handle occurring errors. We use the error code for an easier identification. Error codes start with 0xand use 5(!) hex characters and thus should be at the same time memorizable (because not completely unknown, but still not so common that they could be taken for something else).

    "},{"location":"docs/BestPractices-Errors/#handling-errors-0x","title":"Handling Errors 0x.....","text":""},{"location":"docs/BestPractices-Errors/#how-to-use-expressions-0xa0129","title":"How to use Expressions (0xA0129)","text":"
    • Expression is a predefined nonterminal in the MontiCore basic grammars. Because of the infix notation of some operators and similar challenges, it is usually not possible to use a subset of the expressions only. For example use of ConditionalExpression may lead to a parser generation error (i.e. 0xA0129).
    • Solutions:
    • Use nonterminal Expression and forbid all unwanted alternatives through context conditions.
    • Think of allowing more general expressions?
    • If especially the syntax of if . then . else . shall be reused, why not define this in a new nonterminal and ignore that the same syntactic constructs were already available in another production.
    • Defined by: CKi, BR.
    "},{"location":"docs/BestPractices-Errors/#further-information","title":"Further Information","text":"
    • Project root: MontiCore @github
    • MontiCore documentation
    • List of languages
    • MontiCore Core Grammar Library
    • Best Practices
    • Publications about MBSE and MontiCore
    • License definition
    "},{"location":"docs/BestPractices-Language-Design/","title":"BestPractices Language Design","text":""},{"location":"docs/BestPractices-Language-Design/#monticore-best-practices-designing-languages","title":"MontiCore Best Practices - Designing Languages","text":"

    MontiCore provides a number of options to design languages, access and modify the abstract syntax tree, and produce output files.

    Some general questions on how to design a complete languages are addressed here.

    "},{"location":"docs/BestPractices-Language-Design/#designing-a-language","title":"Designing A Language","text":""},{"location":"docs/BestPractices-Language-Design/#correct-language-vs-superset","title":"Correct language vs. superset?","text":"
    • When you know that the incoming model will be correct, because they are generated by algorithm, you can decide to pass a (slight) superset
    • This may simplify the development process for two reasons: (a) you may derive a simpler grammar and (b) you may omit definitions of context conditions.
    • But beware: (a) situations may change and manually changed models might come in or (b) the is adapted by an ill-behaving pre-processor or (c) the model may come in a wrong version.
    • This applies mainly for unreadable languages, such as JSON or XML.
    • Defined by: BR
    "},{"location":"docs/BestPractices-Language-Design/#versioning-an-evolving-language","title":"Versioning an evolving language?","text":"
    • When languages evolve, models may become invalid, because certain (now obligatory) parts are missing, or old keywords are used.
    • We generally believe that a language that is made for long-lasting models should not embody its version in the models (i.e. like Java, C++ and other GPLs and unlike XML dialects).
    • When evolving a language, you should only evolve it in conservative form, i.e.
    • All new elements are optional by .?, .* or offer new alternatives (old | new)
    • Old elements or keywords are not simply removed, but forbidden by coco warnings, marking them as deprecated for a while.
    • Downward compatibility of newer models, however, is not useful. We can safely enforce developers should normally use the newest versions of their tools.
    • Defined by: BR
    "},{"location":"docs/BestPractices-Language-Design/#language-design-in-the-large","title":"Language Design in the Large","text":""},{"location":"docs/BestPractices-Language-Design/#making-transitively-inherited-grammars-explicit","title":"Making Transitively Inherited Grammars Explicit?","text":"
    • When the grammar inclusion hierarchy becomes larger, there will be redundancy. In:
        grammar A { .. } ;\n  grammar B extends A { .. } ;\n  grammar C extends A,B { .. } ;\n  grammar D extends B { .. } ;\n
      Grammars C and D actually include the same nonterminals.
    • If A is made explicit, you have more information right at hand, but also larger grammars. It is a matter of taste.
    • A recommendation: when you use nonterminals from A explicitly, then also make the extension explicit. However, be consistent.
    "},{"location":"docs/BestPractices-Language-Design/#how-to-achieve-modularity-in-the-sense-of-decoupling","title":"How to Achieve Modularity (in the Sense of Decoupling)","text":"
    • Modularity in general is an important design principle. In the case of model-based code generation, modularity involves the following dimensions:
      1. Modelling languages
      2. Models
      3. Generator
      4. Generated code
      5. Runtime-Environment (RTE) including imported standard libraries
      6. Software architecture (of the overall system), software stack
    • These dimensions are not orthogonal, but also not completely interrelated. The actual organisation will depend on the form of project.
    • A weak form of modularity would be to organize things in well understood substructures such as packages. A deeper form of modularity deals with possibility for individual reuse and thus an explicit decoupling of individual components. We aim for decoupling (even if developed in the same git project).
    • Modularity also deals with extensibility and adaptation.
    • A principle for adaptation for the generator, the generated code, and the RTE is to design each of them like a framework with explicit extension points. Extension points may be (empty) hook methods to be filled, Java interfaces to be implemented and their objects injected to the code e.g., via factories, builders or simply method parameters.
    • A principle for modularity for the generator, the generated code, and the RTE is to design parts of them as independent library functions (or larger: components) that can be used if needed.
    • We recommend to modularize whenever complexity overwhelms or extensibility and adaptability are important:
      1. MontiCore has powerful techniques for adaptation, extension and composition of modelling languages (through their grammars). See the handbook.
      2. MontiCore has powerful techniques for the aggregation of models -- using the same principles as programming languages, namely allowing to keep the models independent (and thus storable, versionable, reusable) artifacts, while they are semantically and through the generator technology well integrated. The appropriate approach is based on using foreign models, e.g., through import statements and sharing symbol infrastructures as described in the handbook.
      3. The generator provides (a) many Java classes and methods that can be overridden (b) Freemarker templates hook points to extend and replace templates, and (c) can be customized using a groovy script. The generator itself is often structured along the software architecture / stack, e.g., in frontend, application backend, database, transport layer, etc.
      4. The generated code must be designed appropriately by the generator designer, by generating builders, mills, etc. for each form of product - quite similar to MontiCore itself. The generated code is usually structured along the components or sub-systems that the software architecture defines.
      5. The RTE is probably well-designed if it is usable in a normal framework.
    • Please note: it is not easy to design modularity and extensibility from beginning. Framework design has shown that this is an iterative optimizing process. It must be avoided to design too many extension elements into the system from the beginning, because this adds a lot of complexity.
    • Defined by: BR
    "},{"location":"docs/BestPractices-Language-Design/#realizing-embedding-through-an-interface-nonterminal-extension-point","title":"Realizing Embedding through an Interface Nonterminal Extension Point","text":"

    Consider the following scenario: A language Host defines an extension point through an interface nonterminal.

    grammar Host { A = I*; interface I; }\n

    Another language Embedded, that has no connection to the Host language, defines a class nonterminal E.

    grammar Embedded { E = \"something\"; }\n

    MontiCore provides alternative solutions to embed the language Embedded into the language Host at the extension point I. All solutions presented here require to implement a new grammar G that extends the grammars Embedded and Host reuses the start nonterminal of the Host grammar:

    grammar G extends Host, Embedded { start A; }\n

    The connection between extension point and extension is performed by an additional grammar rule in the grammar G. This can be realized in one of the following ways each one of which has its own advantages and disadvantages:

    1. Embedding through overriding of extension rule and implementing extension point:
      • E implements I;
      • Advantage: simple embedding rule
      • Disadvantage: does not work in combination with inheritance of extension rule
      • Should therefore only be used, if E is not used anywhere else (= in not other language that is potentially used in combination with this language)
    2. Embedding through extending extension rule and implementing extension point rule:
      • IE extends E implements I = \"something\";
      • Advantage: does work in combination with inheritance of extension rule
      • Disadvantage: cloning of RHS of the extension rule can produce inconsistencies if E is changed
      • Can be used if it is assured that this rule is adjusted whenever E is changed, e.g., by assuming that E is not modified at all
    3. Embedding through implementing extension point rule and providing extension on right-hand side:
      • IE implements I = E;
      • Advantage: does work in combination with inheritance of extension rule
      • Disadvantage: introduces new level of indirection in the AST that invalidates the check whether the required abstract syntax (RHS of interface nonterminal) is present
      • Should therefore not be used, if the interface has a right-hand side
    4. Defined by: AB
    "},{"location":"docs/BestPractices-Language-Design/#recurring-language-components","title":"Recurring Language Components","text":""},{"location":"docs/BestPractices-Language-Design/#the-import-statements","title":"The import statements","text":"
    • Many models depend on other models from which they receive symbols they can rely on. To define this kind of dependencies using import statements is convenient and well known (e.g., from Java). We thus suggest to use the import statement in the spirit of Java.
    • import aName at the first sight means that a specific class with the qualified name aName is used. In reality, however, Java has a very convenient convention that class aName is always defined in the artifact (i.e. file) with the same name aName.java and the needed symbol table is part of aName.class. So an import statement actually locates an artifact.
    • As a consequence, we suggest:
      • import aModelName refers to an artifact with name aModelName -- regardless which kind of model is defined there.
      • All the symbols exported by the artifact aModelName are imported when using the import statement import aModelName.
      • The imported artifact provides the desired symbols, typically stored through an earlier tool execution in a symbol file aModelName.sym.
      • The symbol file may have specific extensions, such as autsymor cdsym.
      • Selective import (known from Java), such as import aName.innerClass should be possible, but currently no such showcase has been made yet (beyond Java).
      • The import statement is only used to make symbols available in their simple form. It is usually not intended to explicate a single dependency, e.g., a configuration model that depends on exactly one base model. Like in Java, where you import an artifact and then still explicitly extend the contained class.
    • It is methodically of interest to store at most one artifact with the same qualified name (although it is not per se forbidden to have more). Java then also uses the first occurring class in its classpath only.
    • In a heterogeneous language setting, it may be necessary to map symbols from a source to a target form (e.g., state symbols to Java enum constants or state pattern classes). There are three main options for this task:
      1. Store in the desired target symbol form upon creating the symbol file. Has some problems: (1) increases dependencies between tools, (2) potentially several files need to be stored.
      2. Adapt the imported symbols upon loading (recommended).
      3. Use an explicit transformation tool between the two model processing tools to map the initially stored symbol file to the desired format.
    "},{"location":"docs/BestPractices-Language-Design/#version-number-in-language-variants","title":"Version number in language variants","text":"
    • As an important rule:
      • Do not include version numbers in the DSL explicitly.
    • The reason is that whenever you do a tooling update, all the models that have been defined before are suddenly not valid anymore and have to be adapted. Java has very carefully ensured that updates in the language are extensions only and thus all old Java files are still validated with new Java compilers (with the one exception: new keyword assert).
    • If your language is still very volatile against disruptive changes, it may be an option at the beginning, but should be avoided with the first real release.
    • It is a burden to manage version numbers and downward compatibility through all the versioning, especially if language components evolve with their own versioning.
    • MontiCore provides a theory of conservative extension to avoid explicit version controlling within the language.
    • And if needed: MontiCore and their tools provide extensive checks of wellformedness (i.e. context conditions), on each update a fully automated consistency check of all existing models should be easily establishable.
    "},{"location":"docs/BestPractices-Language-Design/#further-information","title":"Further Information","text":"
    • Project root: MontiCore @github
    • MontiCore documentation
    • List of languages
    • MontiCore Core Grammar Library
    • Best Practices
    • Publications about MBSE and MontiCore
    • License definition
    "},{"location":"docs/BestPractices-Symbols-Scopes/","title":"BestPractices Symbols Scopes","text":""},{"location":"docs/BestPractices-Symbols-Scopes/#monticore-best-practices-symbols-scopes-symboltables","title":"MontiCore Best Practices - Symbols, Scopes, Symboltables","text":"

    MontiCore provides a number of options to design languages, access and modify the abstract syntax tree, and produce output files.

    The newest MontiCore release gives powerful capabilities to define and use symbols. Symbols, scopes, and symboltables are somewhat complex to design, but powerful in their use.

    "},{"location":"docs/BestPractices-Symbols-Scopes/#designing-symbols-scopes-and-symboltables","title":"Designing Symbols, Scopes and SymbolTables","text":""},{"location":"docs/BestPractices-Symbols-Scopes/#how-to-define-a-symbol-usage-without-a-given-symbol-definition","title":"How to define a Symbol Usage without a given Symbol Definition","text":"
    grammar E { \n  A = Name@S; \n  symbol S = Name; \n}\n
    • If you want to use a special form of symbol that shall neither be defined inside the grammar of a language, nor shall it be imported.
    • We can define symbols of kind S in the grammar in a grammar rule that is never reached by the parser from the start production. Through this, MontiCore generates:
    • symbol table infrastructure for handling S symbols
    • symbol table infrastructure for resolving these in E scopes, and
    • integration of S symbols with the AST of A.
    • However, S symbols are not automatically instantiated. This has to be described manually, e.g., by extending the symbol table creator or via providing an adapter translating a foreign symbol into an S symbol.
    • This can be used, e.g., in these scenarios:
    • A name of a certain kind is introduced automatically the first time it occurs in a model. If it occurs more than once, all other occurrences of the name do not introduce new symbols. (e.g., this happens with features in FDs, and works because features do not have a body.)
    • A name in a language E refers to a named element of another language, but the language shall be decoupled from E. Therefore, E introduces a symbol S and an adapter maps other, foreign symbols to S symbols.
    • Defined by: AB, BR
    "},{"location":"docs/BestPractices-Symbols-Scopes/#symbol-definition-prepared-for-reuse","title":"Symbol Definition prepared for Reuse","text":"

    grammar E { \n  symbol Bla = \"bla\" Name AnotherNT; \n}\n
    * has the effect that three things are defined: (a) concrete syntax, abstract syntax with (b) AST element ASTBla and (c) a symbol BlaSymbol. * Reuse of the symbol BlaSymbol currently only works together with a reuse of the syntax too, i.e.

    grammar F extends E { \n  Blubb extends Bla = \"blubb\" Name; \n}\n
    would for example be illegal, because the conservative extension paradigm enforces AnotherNT to be included in Blubb as well. * To allow individual reuse of symbol BlaSymbol we recommend to restructure its definition into an interface that does not preclude create syntax and only a minimal constraint on the abstract syntax:

    grammar E { \n  symbol interface Bla = Name; \n  Bla2 implements Bla = \"bla\" Name AnotherNT; \n}\ngrammar F extends E { \n  Blubb implements Bla = \"blubb\" Name; \n}\n
    • Please note that MontiCore allows that a nonterminal implements multiple interfaces. However, only one of them may carry the symbol keyboard property, because the newly defined symbol then is also a subclass of the inherited symbol (in Java).
    "},{"location":"docs/BestPractices-Symbols-Scopes/#loading-deserializing-symbols-of-unknown-symbol-kinds","title":"Loading (DeSerializing) Symbols of Unknown Symbol Kinds","text":"

    Specific languages (e.g., CD) may provide specific symbols, of specific kinds. A symbol import of these symbols into another language L1 has to cope with potentially unknown kinds of symbols, even though the super kind could be known. E.g., TypeSymbol is extended by CDTypeSymbol providing e.g., additional visibility information. Upon loading an CD-symboltable into an L1-tool it may be that neither AST-class CDTypeSymbol nor superclass information about it is available. But, the symbols of the unknown kind should (and can) be loaded as symbols of a more abstract kind.

    Loading the symbols of the unknown kind as symbols of the specific known kind is possible in multiple ways. Options would be 1. adapt the L1-tool to know about the new symbols, or 2. the L1-tool has been written in such a way that new classes can be added through appropriate class loading, or 3. the L1-tool is configurable in handling unknown symbol kinds as explained below.

    "},{"location":"docs/BestPractices-Symbols-Scopes/#loading-symbols-as-symbols-of-another-kind","title":"Loading Symbols as Symbols of Another Kind","text":"

    Symbols of an unknown source kind (e.g., CDTypeSymbol) may easily be loaded as symbols of a known kind (e.g., TypeSymbol) when the source kind provides all mandatory attributes (i.e. those without defaults) of the symbol class. This is especially the case if the source kind is a subclass of the known kind.

    This behavior can be configured in the global scope by calling the method putSymbolDeser(String, ISymbolDeser), where the unknown source kind is encoded as string (here: CDTypeSymbol) and is mapped to an appropriate DeSer (here for TypeSymbol). For instance the call would be putSymbolDeSer(\"de.monticore.cdbasis._symboltable.CDTypeSymbol\", new TypeSymbolDeSer()).

    Because the global scope is a singleton, this configuration can be e.g., called in or shortly after constructing the global scope. However, this would still encode the name of the unknown symbol kind in the L1-tool, although it prevents any actual dependency to the imported tools.

    The method can also be called from a CLI to dynamically configure the deserialization, e.g., the information be fed to the L1-tool via parameters, e.g., like

      java L2Tool --typeSymbol=de.monticore.cdbasis._symboltable.CDTypeSymbol\n              --functionSymbol=de.monticore.cdbasis._symboltable.CDMethodSymbol\n

    "},{"location":"docs/BestPractices-Symbols-Scopes/#converting-stored-symbol-tables","title":"Converting Stored Symbol Tables","text":"

    If the unknown symbol kinds do have different attributes or some extra information needs to be calculated in the new symbols, then either the L1-tool needs to be adapted or the serialized symbol table can be transformed to another serialized symbol table where the kind information is transformed as required as an intermediate step between the tools providing and reading the symbol tables.

    "},{"location":"docs/BestPractices-Symbols-Scopes/#further-information","title":"Further Information","text":"
    • Project root: MontiCore @github
    • MontiCore documentation
    • List of languages
    • MontiCore Core Grammar Library
    • Best Practices
    • Publications about MBSE and MontiCore
    • License definition
    "},{"location":"docs/BestPractices-Syntax-Design/","title":"BestPractices Syntax Design","text":""},{"location":"docs/BestPractices-Syntax-Design/#monticore-best-practices-concrete-and-abstract-syntax","title":"MontiCore Best Practices - Concrete and Abstract Syntax","text":"

    MontiCore provides a number of options to design languages, access and modify the abstract syntax tree, and produce output files.

    This (currently unsorted and evolving) list of practices discusses solutions that we identified and applied as well as alternatives and their specific advantages and drawbacks. The list also mentions where the solutions have been found and where they have been applied first.

    This file is partially temporary and also contains compact (incomplete) solutions. More detailed descriptions of best practices can be found in the MontiCore handbook. Some of the best practices here will also be incorporated in the next version of the reference manual.

    "},{"location":"docs/BestPractices-Syntax-Design/#designing-concrete-and-abstract-syntax","title":"Designing Concrete and Abstract Syntax","text":""},{"location":"docs/BestPractices-Syntax-Design/#specific-keywords-that-shall-be-used-as-normal-names-elsewhere","title":"Specific keywords that shall be used as normal names elsewhere","text":"
    • A = \"foo\" B introduces foo as a keyword that cannot be used as an ordinary (variable) name anymore. To prevent that we may use:
    • A = key(\"foo\") B instead, which introduces foo only at that specific point.
    • In general, we use all Java keywords as permanent, but abstain from other permanent keywords, especially if they are only used for a specific purpose in a composable sublanguage, like in in the OCL.
    • Defined by: BR
    "},{"location":"docs/BestPractices-Syntax-Design/#complex-token-clashing-with-other-uses-of-sub-tokens","title":"Complex Token clashing with other uses of sub-tokens","text":"
    • For example <- is supposed to be used as arrow, but in an expression 3<-10 is also syntactically allowed.
    • The problem: as soon as \"<-\" is defined as a token in any part of the current or any extended grammars, the expression 3<-10 would not be parsed as 3 < -10 anymore.
    • Solutions:
      1. We might decompose the token to \"<\" \"-\" which in its consequence means that we put more burden to the context-free parser and less to the regular scanner. (\"scannerless parsing\")
        • Drawback: spaces would now be allowed inbetween.
      2. Decompose the token to {noSpace(2)}? \"<\" \"-\". This (slightly hacking approach) prevents spaces between two tokens.
    • The challenge: when designing a language component, we don't know yet what further uses will bring. This may include sub-tokens to come up with new interactions. This would require an (already defined) grammar with the complex token to be adapted afterwards (and thus conflict with the library idea).
    • Remark: A forthcoming enhancement will provide an improved solution, keeping parsing efficiency and compositionality of grammars.
    • Defined by: BR
    "},{"location":"docs/BestPractices-Syntax-Design/#extension-forms-in-a-component-grammar","title":"Extension forms in a component grammar","text":"

    A component grammar is meant for extension. MontiCore therefore provides five(!) mechanisms that can be used when a sub-grammar shall extend a super-grammar. The solutions are briefly discussed here:

    "},{"location":"docs/BestPractices-Syntax-Design/#1-interface-in-the-super-grammar","title":"1. Interface in the super-grammar","text":"
    • Introduce an interface and allow building of sub-nonterminals in sub-grammars.
      component grammar A {  \n  interface X;\n  N = \"bla\" X \"blubb\";\n}\ngrammar B extends A {\n  Y implements X = \"specific\" \"thing\"\n}\n
    • Advantage: Multiple extensions are possible at the same time. An NT Y can also implement multiple interfaces (like in Java).
    • Disadvantage: the designer of A explicitly has to design the hole (extension point) X and add it into the production.
    "},{"location":"docs/BestPractices-Syntax-Design/#2-overriding-empty-nonterminal-from-the-super-grammar","title":"2. Overriding (empty) nonterminal from the super-grammar","text":"
    • Use a normal nonterminal X and override it in a sub-grammar.
      component grammar A {  \n  X = \"\";\n  N = \"bla\" X \"blubb\";\n}\ngrammar B extends A {\n  @Override\n  X = \"my\" \"thing\";\n}\n
    • Advantage: Default implementation \"\" exists, no explicit filling needed.
    • Disadvantage:
      1. The designer of A explicitly has to design the hole (extension point) X and inject it into other places.
      2. Only one overriding alternative possible (i.e. multiple overriding in subgrammars are allowed, but only the most specific resides).
    "},{"location":"docs/BestPractices-Syntax-Design/#3-extending-nonterminal-from-the-super-grammar","title":"3. Extending nonterminal from the super-grammar.","text":"
    • Use an empty normal nonterminal X and extend it in a sub-grammar.
      component grammar A {  \n  X = ;\n  N = \"bla\" X \"blubb\";\n}\ngrammar B extends A {\n  Y extends X = \"this\";\n}\n
    • Advantage: Default implementation \"\" exists, no explicit filling needed.
    • Disadvantage: The designer of A explicitly has to design the hole (extension point) X and inject it into other places.
    • Care: Extension still allows the (empty) alternative X.
    "},{"location":"docs/BestPractices-Syntax-Design/#4-using-external-nonterminals-in-the-super-grammar","title":"4. Using external nonterminals in the super-grammar.","text":"
    • Mark nonterminal X as external.

      component grammar A {  \n  external X;\n  N = \"bla\" X \"blubb\";\n}\ngrammar B extends A {\n  X = \"your\";\n}\n

    • Advantage: Explicitely marks a nonterminal as hole (extension point) in the grammar.

      • Please observe that interface terminals may or not may be meant to be extended in sub-grammars. external is clearer here.
    • Disadvantage:
      1. Leads to more objects in the AST. Both classes a.X and b.X are instantiated and a.X only links to b.X.
      2. Only one filling of the hole is possible.
    "},{"location":"docs/BestPractices-Syntax-Design/#5-overriding-the-whole-production","title":"5. Overriding the whole production.","text":"
    • If you don't want to add a hole at any possible place of extension:
      component grammar A {  \n  N = \"bla\" \"blubb\";\n}\ngrammar B extends A {\n  @Override\n  N = \"bla\" \"my\" \"blubb\" \"now\";\n}\n
    • Advantage: Compact definition. No \"framework thinking\" needed (no need to forecast all potential extension points)
    • Disadvantage:
      1. The entire production is overriden (some redundancy).
      2. Only one overriding alternative possible.
    • Combinations are possible. Dependent on the anticipated forms of adaptations option 1, 2, 3 and 5 are in use.
    • Defined by: BR
    "},{"location":"docs/BestPractices-Syntax-Design/#avoid-empty-nonterminals-if-body-is-known","title":"Avoid empty nonterminals (if body is known)","text":"
    • From the two variants:

      A = \"bla\" B? C*;\nB = \"B's body\" ;\nC = \"C's body\" ;\n
      and
      A = \"bla\" B C;\nB = (\"B's body\")? ;\nC = (\"C's body\")* ;\n
      we generally prefer the first one, i.e. add multiplicities when using a nonterminal.

    • This is a matter of taste, but useful to keep this consistent.

    • Sometimes exceptions are useful.
    • Defined by: SVa, BR
    "},{"location":"docs/BestPractices-Syntax-Design/#avoid-complex-tokens-1","title":"Avoid complex tokens (1)","text":"
    • The token definitions can only define regular expressions. Furthermore, the token parser (i.e. the lexer) does not consider backtracking.
    • If combinations of characters may be split into several token sequences this leads to problems. E.g. in 3-2 and (-2) the - has different roles. Unfortunately these problems also occur when composing languages that make excessive use of (conflicting) token definitions.
    • Solution: instead of defining a complex token like
        token NegativeNat = \"-\" Digits;\n
      we split the token and allow individual parsing into nonterminals:
        NegativeNat = negative:[\"-\"] Digits {noSpace()}? \n
      (where we assume Digits is a given token).
    • As a workaround, we use the semantic predicate {noSpace()}? that ensures that between the two last processed token there is no space inbetween. If one of the tokens is optional we have to split the alternatives:

      SignedNatLiteral = \n        (negative:[\"-\"]) Digits {noSpace()}? |\n                         Digits;  \n

    • Adding a handcoded function like getValue() via astrule or the TOP-mechanism allows to use SignedNatLiteral like a token.

    • Scannerless parsing is a principle where the tokens are reduced to simple characters (or character classes, such as [a-z]). Scannerless parsing generally avoids this kinds of problems, but is way slower. This kind of solution tries to mediate between the two extremes benefitting from both approaches.
    • Defined by: MB, in: MCCommonLiterals.mc4 and other literals grammars.
    "},{"location":"docs/BestPractices-Syntax-Design/#avoid-complex-tokens-2","title":"Avoid complex tokens (2)","text":"
    • Same general problem. In language composition conflicting tokens may lead to issues.
    • For example Java allows 42. as a literal of type float. UML allows to define cardinalities like [42..44]. Composition clashes.
    • Solution: In a Java sublanguage we split the token:

      SignedBasicFloatLiteral =\n   ... \n   | Digits \".\" {noSpace()}? ... ;\n

    • This will ensure that [42..44] will be parsed like [ 42 .. 44 ] in a language composition as well.

    • It generally seems that overly complex composed tokens may lead to issues especially if the language allows compact models. Suboptimal tokens may be e.g. \"[[\" (vs. nested lists), or \"<-\" (vs. 3 < -2).
    • Defined by: MC team.
    "},{"location":"docs/BestPractices-Syntax-Design/#how-to-define-keyword-enumerations","title":"How to define keyword enumerations","text":"
    • A finite set of keyword-based alternatives can be defined in several forms:
    • Standard three keywords act as alternative:
      N = ([\"public\"] | [\"protected\"] | [\"private\"]) ;\n
    • Effects:
      1. not extensible without overriding and repetition
      2. introduces boolean flags, where only one can be true at a time
    • Use an enumeration nonterminal
      enumeration E = \"public\" | \"protected\" | \"private\" ;\nN = E ;\n
    • Effects:
      1. not extensible
    • Use an interface and subclasses with almost empty body:
      interface E ;\nP1 implements E = \"public\"    ;\nP2 implements E = \"protected\" ;\nP3 implements E = \"private\"   ;\nN = E ;\n
    • Effects:
      1. very extensible in various ways (even beyond mere keywords)
      2. visitor can easily address the keywords (i.e. by visit(P1) ...)
      3. Disadvantage: Clumsy notation and visitors are always needed.
    • Defined by: SVa, BR.
    "},{"location":"docs/BestPractices-Syntax-Design/#common-ast-access-to-syntactically-similar-nonterminals","title":"Common AST-Access to Syntactically Similar Nonterminals","text":"
    • Sometimes the following occurs (e.g. in associations of CDs or interactions of SD):

      A = X Y Z;\nB = Z Y X;\n

    • The concrete syntax differs (in order), but the syntactic concepts are the same.

    • To allow common access, a common interface nonterminal is introduced that is not used in the grammar directly. This doesn't change the concrete syntax but allows common AST access:

      interface F = X Y Z;      // order is irrelevant\nA implements F = X Y Z;\nB implements F = Z Y X;\n

    • Defined by: BR.

    "},{"location":"docs/BestPractices-Syntax-Design/#how-and-when-to-use-names-for-nonterminals","title":"How and when to use Names for Nonterminals","text":"
    • Normally names like expr:Exprcan be avoided which makes a grammar easier to read and more concise, i.e. Expr alone has the same effect.
    • There may be two reasons to use a name:
      1. Nonterminal X occurs several times and we want to distinguish: left:Expr \"*\" right:Expr
      2. We can also use the name to describe the purpose of the nonterminal, i.e.
         MyVariable implements Variable = Name \"=\" initial:Expression;\n
        vs:
         MyParameter implements Variable = Name \"=\" default:Expression;\n
    • Defined by: BR.
    "},{"location":"docs/BestPractices-Syntax-Design/#further-information","title":"Further Information","text":"
    • Project root: MontiCore @github
    • MontiCore documentation
    • List of languages
    • MontiCore Core Grammar Library
    • Best Practices
    • Publications about MBSE and MontiCore
    • License definition
    "},{"location":"docs/BestPractices/","title":"Best Practices","text":""},{"location":"docs/BestPractices/#monticore-best-practices-a-guide-for-small-solutions","title":"MontiCore Best Practices - A Guide For Small Solutions","text":"

    MontiCore provides a number of options to design languages, access and modify the abstract syntax tree, and produce output files.

    This (currently unsorted and evolving) list of practices discusses solutions that we identified and applied as well as alternatives and their specific advantages and drawbacks. The list also mentions where the solutions have been found and where they have been applied first.

    The list is subdivided into several MD files tackling various language design areas.

    The list is partially temporary and also contains compact (incomplete) solutions. More detailed descriptions of best practices can be found in the MontiCore handbook. Some of the best practices here will also be incorporated in the next version of the handbook.

    • Designing A Language

    includes: Language Design in the Large

    • Designing Concrete and Abstract Syntax

    • Handling Errors 0x.....

    • Designing Symbols, Scopes and SymbolTables

    • Designing Tools for Command Line Interfaces

    • Generating Code with Templates (no practice defined here yet)

    "},{"location":"docs/BestPractices/#further-information","title":"Further Information","text":"
    • Project root: MontiCore @github
    • MontiCore documentation
    • List of languages
    • MontiCore Core Grammar Library
    • Best Practices
    • Publications about MBSE and MontiCore
    • License definition
    "},{"location":"docs/BuildMontiCore/","title":"BuildMontiCore","text":"

    "},{"location":"docs/BuildMontiCore/#monticore-language-workbench-and-development-tool-framework","title":"MontiCore - Language Workbench And Development Tool Framework","text":"
    • MontiCore Handbook.
    "},{"location":"docs/BuildMontiCore/#general-disclaimer","title":"General disclaimer","text":"

    (Repeated from the BSD 3 Clause license):

    This software is provided by the copyright holders and contributors \"as is\" and any expressed or implied warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall the copyright holder or contributors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services, loss of use, data, or profits, or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this software, even if advised of the possibility of such damage.

    "},{"location":"docs/BuildMontiCore/#included-software","title":"Included Software","text":"

    This product includes the following software: * AntLR * FreeMarker

    "},{"location":"docs/BuildMontiCore/#contribution","title":"Contribution","text":"

    When you want to contribute: Please make sure that your complete workspace only uses UNIX line endings (LF) and all files are UTF-8 without BOM. On Windows, you should configure git to not automatically replace LF with CRLF during checkout by executing the following configuration:

    git config --global core.autocrlf input\n
    "},{"location":"docs/BuildMontiCore/#build-monticore","title":"Build MontiCore","text":"

    MontiCore is currently built using Gradle.

    Please note that from the top level build script, not everything is built and all tests executed. It is a deliberate decision, to exclude some of the longer lasting tasks.

    • build the productive code (including the unit tests, ~8 min)
    • gradle buildMC
    • skipping the unit tests: gradle assembleMC
    • run integration tests (which are not included in the unit tests, ~30 min)
    • all integration tests
      • gradle testIT
    • Integration tests of the generator:
      • gradle -p monticore-test/it build
    • EMF Integration tests of the generator (only test collection not included in testIt):
      • gradle -p monticore-test/it build -PbuildProfile=emf
    • Experiments (from the Handbook) as integration tests:
      • gradle -p monticore-test/01.experiments build and
      • gradle -p monticore-test/02.experiments build
    • Grammar integration tests:
      • gradle -p monticore-test/monticore-grammar-it build
    • clean:
    • call gradle clean
    • cleaning integration tests:
      • using gradle gradle clean within the corresponding subproject (see above)
    "},{"location":"docs/BuildMontiCore/#further-information","title":"Further Information","text":"
    • Project root: MontiCore @github
    • MontiCore documentation
    • List of languages
    • MontiCore Core Grammar Library
    • Best Practices
    • Publications about MBSE and MontiCore
    • License definition
    "},{"location":"docs/DevelopedLanguages/","title":"Languages and Language Components","text":""},{"location":"docs/DevelopedLanguages/#languages-and-language-components-developed-with-monticore","title":"Languages and Language Components Developed with MontiCore","text":"

    The MontiCore language workbench has been under development for a while already and of course has been used by our group to develop many languages. Not all of those languages are publicly available and some of these languages are equipped with tools based on MontiCore 5.

    Many of these languages are composed of sublanguages and thus potentially build on each other. The available languages can be used as is, but also be adapted, extended and further composed.

    Please also have a look at our literature references for further information on many of the languages.

    "},{"location":"docs/DevelopedLanguages/#information-about-languages-and-language-components","title":"Information about Languages and Language Components","text":"
    • MontiCore Handbook. The handbook describes how to use MontiCore as an out-of-the-box language workbench), but also as a grey box tooling framework. It thus also gives an overview over a number of core mechanisms of MontiCore.

    • List of MontiCore core Language Components. MontiCore concentrates on reuse. It therefore offers a set of predefined language components where the main artifact is usually a component grammar. Reusing these language components allows language developers to define their own language as a composition of reusable assets efficiently. Reusable assets describe among others several sets of literals, expressions and types, which are relatively freely composable.

    • List of languages. This is a another list of newer MontiCore languages that can be used out of the box or also composed. Many of them already are rather stable, but some of them also undergo a lively development and enhancement. These complete languages are usually composed of a number of language components.

    • MontiCore topic list Describes various research topics which MontiCore builds on or where MontiCore has been used as foundation.

    "},{"location":"docs/DevelopedLanguages/#github-available-languages","title":"Github Available Languages","text":"
    • Class Diagrams
    • Sequence Diagrams
    • JSON
    • Feature Diagrams
    • SI Units
    • automaton
    • EmbeddedMontiArc
    • Object Diagrams
    "},{"location":"docs/DevelopedLanguages/#further-languages-eg-used-in-scientific-and-industrial-projects","title":"Further Languages (e.g. used in scientific and industrial projects)","text":"
    • MontiArc ADL is an architectural definition language for component and connector models with enhanced connection facilities, hierarchical decomposition etc. and provides a simulator [HRR12,BHH+17,Wor16,Hab16].

    • MontiArcAutomaton ADL is an extension of the architectural definition language MontiArc using automata to describe behavior. Some applications e.g. are of robotics, production, or InternetOfThings. [BKRW17a,HKR+16,BRW16a,Wor16].

    • UML/P is a derivation from UML, especially suited for agile development. See language definition and usage method in [Rum17,Rum16,Sch12].

      • UML/P Class Diagrams for data structures
      • UML/P Object Diagrams for exemplaric situations: usable for constructive development as well as testing
      • OCL/P as Java-variant of the OCL with a nice logic, set-comprehension etc.
      • UML/P Statecharts for behavior
      • UML/P Sequence Diagrams for interaction
      • Activity Diagrams for workflows and requirements (an extension to the books)
    • Delta-MontiArc [HRRS12,HKR+11,HRRS11] is a DSL for expressing deltas on MontiArc component definitions which allows to model software product lines in a bottom up way.

    • MontiArcHV [HRR+11] allows specifying component variability fully integrated within the component hierarchy located at variation points in component definitions.

    • Java as full language as well as source for Java expressions, statements, attribute or method definitions.

    • FeatureDSL is a DSL for feature diagrams in software product line approaches.

    • DeltaCD is a DSL for expressing deltas on class diagrams which allows to model software product lines in a bottom up way

    • Aerospace Constraint Specification Language is a DSL used to specify critical situations in an airspace including airplanes, weather, flight conditions and much more. [ZPK+11]

    • clArc DSL Family: [PR13]

      • Cloud Architecture Description Language: used to model of architectures of cloud-based systems; based on MontiArc.
      • Target Description Language: used to model the infrastructure architecture of cloud-based systems.
      • Mapping Description Language: used to model deployments between software and infrastructure architectures.
      • Architecture Scenario Description Language: used to model scenario-based test cases for software architectures.
    • I/O-TestDSL for the definition of stream-based, input-output related black-box tests for architecture definition languages like MontiArc.

    • LightRocks, a modelling language for robotic assembly processes.

    • cdViews is a DSL used to model partial views on class diagrams

    • RBAC for Role-Based Access Control in enterprise information systems.

    • MontiWis [[RR13,Rei16]] is a family of DSLs for the model-based, generative development of web information systems among others based on class diagrams, activity diagrams and views.

    • HQL: Hibernate Query Language that maps to hibernate based executions.

    • SQL the well known DB query language; used for embedding e.g. into other languages.

    • XML the basic infrastructure for all XML dialects

    • CarOLO DSLs for autonomic driving. This among others contains a DSL for defining road scenarios with moving vehicles as well a obstacles suited for laser, lidar, radar and camera sensors. This languages are part of the Darpa Urban Challenge 2007. [BR12b,BR12,Ber10,BR09].

    • ProcEd a Web-based Editing Solution for Domain-Specific Process-Engineering [BGR09].

    • MontiWeb a modular development approach for Web Information Systems (which was later succeeded by MontiWIS) [DRRS09].

    • C++ and its sublanguages for expressions, statements and definitions (but no generic types, no defines).

    • MontiCore itself uses a family of DSLs for the definition of DSLs, i.e., their grammars. [HR17]

    "},{"location":"docs/DevelopedLanguages/#further-information","title":"Further Information","text":"
    • Project root: MontiCore @github
    • MontiCore documentation
    • List of languages
    • MontiCore Core Grammar Library
    • Best Practices
    • Publications about MBSE and MontiCore
    • License definition
    "},{"location":"docs/Download/","title":"Downloads","text":"

    The following tools for MontiCore can be used from the command line and thus e.g. well be embedded in scripting. Their languages as well as related tooling are currently available for download:

    Artifact Description Download MontiCore Tool Tool for processing grammars. Link MontiCore Runtime MontiCore's runtime library. Link Automaton Example Project Example project that can be used with the MontiCore CLI tool. Link Automaton Example Project in Gradle Example project that can be used with Gradle. Link CD Tool Tool for a Class Diagram language. Link FACT Tool Tool for a Feature Diagram language. Link FeatureConfiguration Tool Tool for a Feature Diagram language. Link FeatureConfigurationPartial Tool Tool for a Feature Diagram language. Link FeatureDiagram Tool Tool for a Feature Diagram language. Link MLC Tool Tool for grouping MontiCore language components. Link OCL Tool Tool for an Object Constraint Language. Link OD4Data Tool Tool for an Object Diagram language. Link OD4Report Tool Tool for an Object Diagram language. Link SD4Development Tool Tool for a Sequence Diagram language. Link Statecharts Tool Tool for a Statechart language. Link JSON Tool Tool for a JSON language. Link XML Tool Tool for an XML language. Link

    Please note the MontiCore 3-Level License of these tools.

    "},{"location":"docs/Download/#further-information","title":"Further Information","text":"
    • see also MontiCore handbook
    • MontiCore Reference Languages - Languages Built Using MontiCore
    • Build MontiCore - How to Build MontiCore
    • Getting Started - How to start using MontiCore
    • Changelog - Release Notes
    • FAQ - FAQ
    • Licenses - MontiCore 3-Level License
    • Project root: MontiCore @github
    • List of languages
    • MontiCore Core Grammar Library
    • Best Practices
    • Publications about MBSE and MontiCore
    "},{"location":"docs/GettingStarted/","title":"Getting Started","text":""},{"location":"docs/GettingStarted/#getting-started-with-monticore","title":"Getting Started with MontiCore","text":"

    This page describes the technical installation and usage of MontiCore for language developers. This page further inspects a simple example grammar and the Java classes and other artifacts generated from this grammar. After installing MontiCore as described on this page, it can be used to develop new modeling languages and generators as described in subsequent chapters.

    MontiCore provides a command line interface (CLI) tool and can easily be used with Gradle. The Gradle integration enables developers to easily employ MontiCore in commonly used integrated development environments (IDEs), such as Eclipse and IntelliJ IDEA. We strongly recommend to work through the section about the CLI tool first. The CLI section contains information about an example MontiCore project and the files generated by MontiCore. It also shortly explains some key features of MontiCore.

    Detailed information about all configuration options that can be used in the MontiCore CLI tool and in MontiCore Gradle projects are explained in Chapter 16 of the handbook. More information about the example Automata language are available in Chapter 21 of the handbook.

    "},{"location":"docs/GettingStarted/#prerequisites-installing-the-java-development-kit","title":"Prerequisites: Installing the Java Development Kit","text":"

    We start with the JDK: Please perform the following steps to install the Java Development Kit (JDK) and validate that the installation was successful:

    • Install a JDK with at least version 11 provided by Oracle or OpenJDK.
    • Make sure the environment variable JAVA_HOME points to the installed JDK, and not to the JRE, e.g., the following would be good:
      • /user/lib/jvm/java-11-openjdk on UNIX or
      • C:\\Program Files\\Java\\jdk-11.* on Windows. You will need this in order to run the Java compiler for compiling the generated Java source files.
    • Also make sure that the system variable is set such that the Java compiler can be used from any directory. JDK installations on UNIX systems do this automatically. On Windows systems, the bin directory of the JDK installation needs to be appended to the PATH variable, e.g. %PATH%;%JAVA_HOME%.
    • Test whether the setup was successful. Open a command line shell in any directory. Execute the command javac -version. If this command is recognized and the shell displays the version of the installed JDK (e.g., javac 11.0.5), then the setup was successful.

    Now we have the prerequisites to run MontiCore from the command line. The JDK installation is also required for using MontiCore with Gradle.

    "},{"location":"docs/GettingStarted/#install-and-use-the-monticore-command-line-interface","title":"Install and Use the MontiCore Command Line Interface","text":"

    This section describes instructions to perform the following first steps to use MontiCore as an CLI tool:

    • Installation of the MontiCore distribution file.
    • Grammar inspection
    • Running the MontiCore generator
    • Compiling the product
    • Running the product, i.e. the Automata tool with an example model example/PingPong.aut.
    "},{"location":"docs/GettingStarted/#installation","title":"Installation","text":"

    For installing MontiCore, perform the following steps:

    • Download the example Automata MontiCore project:
      // MontiCore zip distribution source\nhttps://www.monticore.de/download/monticore.tar.gz\n
    • Unzip the archive. The unzipped files include a directory called mc-workspace containing the executable MontiCore tool monticore.jar along with a directory src containing handwritten Automata DSL infrastructure, a directory hwc containing handwritten code that is incorporated into the generated code, and a directory example containing an example model of the Automata DSL.
      // MontiCore zip distribution content in directory mc-workspace\nAutomata.mc4\nmonticore.jar\nmonticore-rt.jar\nsrc/automata/AutomataTool.java\nsrc/automata/visitors/CountStates.java\nsrc/automata/prettyprint/PrettyPrinter.java\nsrc/automata/cocos/AtLeastOneInitialAndFinalState.java\nsrc/automata/cocos/StateNameStartsWithCapitalLetter.java\nsrc/automata/cocos/TransitionSourceExists.java\nhwc/automata/_ast/ASTState.java\nhwc/automata/_symboltable/AutomatonSymbol.java\nhwc/automata/_symboltable/AutomataSymbols2Json.java\nhwc/automata/_symboltable/AutomatonSymbolDeser.java\nhwc/automata/_symboltable/AutomataGlobalScope.java\nexample/PingPong.aut\n
    "},{"location":"docs/GettingStarted/#inspect-the-example-grammar","title":"Inspect the Example Grammar","text":"

    MontiCore is a language workbench. It supports developers in developing modular modelling languages. The core of MontiCore is its grammar modelling language (cf.\u00a0Chapter 4 of the MontiCore handbook), which is used by developers for modelling context-free grammars. A MontiCore grammar defines (parts of) the abstract and concrete syntax of a language. Each grammar contains nonterminals, production rules, and may extend other grammars. At most one rule is marked as the start rule.

    It is a key feature of MontiCore that it allows a grammar to reuse and extend other grammars. In an extension all the nonterminals defined in the extended grammars can be reused or even overridden. This form of extension allows to achieve several effects:

    • Language (i.e. grammar) components can be reused and integrated in larger languages, composed of several components.
    • Individual nonterminals can be reused (like classes) from a library.
    • A given language can be extended, allowing to add additional alternatives inside a language.

    Component grammars and grammar extensions are detailedly discussed in Chapter 4 of the MontiCore handbook.

    grammar Automata extends de.monticore.MCBasics {\n\nsymbol scope Automaton =\n\"automaton\" Name \"{\" (State | Transition)* \"}\" ;\n\nsymbol State =\n\"state\" Name\n    ((\"<<\" [\"initial\"] \">>\" ) | (\"<<\" [\"final\"] \">>\" ))*\n    ( (\"{\" (State | Transition)* \"}\") | \";\") ;\n\n  Transition =\n    from:Name \"-\" input:Name \">\" to:Name \";\" ;\n}\n
    Listing 2.2: The Automata grammar.

    In the following, we inspect the MontiCore grammar of the Automata language. Navigate your file explorer to the unzipped mc-workspace directory. The directory contains the file Automata.mc4. This file contains the MontiCore grammar depicted in Listing 2.2. MontiCore grammars end with .mc4.

    The definition of a MontiCore grammar starts with the keyword grammar, followed by the grammar's name. In this example, the grammar is called Automata. The grammar's name is optionally followed by the keyword extends and a list of grammars that are extended by the grammar. In this example, the Automata grammar extends the grammar de.monticore.MCBasics.

    Tip 2.3 MontiCore Key Feature: Composition

    The MontiCore language workbench allows to compose language components by composing grammars and also to reuse all infrastructure, such as context conditions, symbol table infrastructure, generator parts and handwritten extensions.

    In the example the Automata grammar extends the grammar de.monticore.MCBasics and thus reuses its functionality.

    MontiCore comes with an extensive library of predefined language components.

    Grammars can also have a package and import other grammars. If a grammar has a package, then the package declaration must be the first statement in the grammar and is of the form package QualifiedName where package is a keyword and QualifiedName is an arbitrary qualified name (e.g. de.monticore). The optional grammar imports follow the package definition. Every import is of the form import QualifiedName. The Automata example grammar file does neither contain a package declaration nor imports. The grammar extended by the Automata grammar is specified by its fully qualified name.

    As usual in context-free grammars, production rules have a left-hand side and a right-hand side. The left-hand side contains the possibly annotated name of a nonterminal. The left-hand side is followed by the terminal = and the right-hand side. Nonterminal names start with an upper-case letter. For instance, the Automata grammar contains the nonterminals Automaton, State, and Transition. A single nonterminal can be provided with the start keyword. Then, the nonterminal is the starting symbol of the grammar. If no nonterminal is marked with start, then the first nonterminal of the grammar becomes the starting symbol by default. In the Automata grammar, the Automaton nonterminal is the starting symbol.

    The other possible keywords for nonterminals influence the generated classes for the abstract syntax tree as well as the generated symbol table infrastructure. Details can be found in Chapter 4 and Chapter 9 of the MontiCore handbook. For example, the Automaton nonterminal is marked with symbol and scope. The keyword symbol makes the MontiCore generator generate a symbol class for the nonterminal. Intuitively stated, the keyword scope instructs the MontiCore generator to construct a symbol table infrastructure that opens a scope when the production is processed. The following sections explain the effects of specifying the Automaton nonterminal with the keywords symbol and scope in more detail. Terminals are surrounded by quotation marks. The Automata grammar, for example, inter alia contains the terminals automaton, state, {, }, and ;.

    The right-hand sides of grammar productions consist of nonterminals, terminals, and semantic predicates, may use cardinalities (*, +, ?), and introduce alternatives via the terminal | as known from regular expressions. Details can be found in Chapter 4 of the MontiCore handbook. The right-hand side of the production defining the nonterminal Automaton, for example, uses the terminal automaton and the nonterminals Name, State, and Transition. The nonterminal Name is not defined in the grammar Automata. Thus, it must be defined in one of the extended grammars. In this case, Name is defined in the grammar MCBasics and is reused by the grammar Automata. For distinguishing different usages of nonterminals on right-hand sides, they can be named. For example, the right-hand side of the production defining the nonterminal Transition uses the Name nonterminal twice. The first usage is named input and the second usage is named to. MontiCore also supports interface and external nonterminals for introducing extension points as detailedly described in Chapter 4 of the MontiCore handbook. However, the example grammar does not use these concepts.

    automaton PingPong {\nstate NoGame <<initial>>;\nstate Ping;\nstate Pong <<final>>;\n\n  NoGame - startGame > Ping;\n\n  Ping - stopGame > NoGame;\n  Pong - stopGame > NoGame;\n\n  Ping - returnBall > Pong;\n  Pong - returnBall > Ping;\n}\n
    Listing 2.4: A model conforming to the Automata grammar.

    Listing 2.4 depicts an example model conforming to the Automata grammar in its concrete syntax. It depicts a simple game of Ping Pong. The automaton consists of three states: the initial state NoGame, such as the states Ping and Pong, for identifying on which side the ball is located. Initially, the automaton starts in the state NoGame. The game starts at the corresponding event. During a run, the automaton switches states by returning the ball from one side to the other. Additionally, it can be stopped at each stage of the game, resulting in the initial configuration. You can find the model in the file PingPong.aut contained in the example directory of the unzipped mc-workspace directory.

    "},{"location":"docs/GettingStarted/#run-monticore","title":"Run MontiCore","text":"

    The MontiCore generator takes a MontiCore grammar as input and generates an infrastructure for processing models conforming to the grammar. When a grammar E extends another grammar G, then all the infrastructure generated for the grammar G is reused and only the extending part from E is generated.

    Tip 2.5 Infrastructure Generated by MontiCore MontiCore itself as well as the infrastructure generated by the MontiCore generator are implemented in Java. This infrastructure includes:
    • a parser for parsing models conforming to the grammar and transforming textual models into abstract syntax tree instances abstracting from the concrete syntax.
    • a symbol table infrastructure to handle the symbols introduced or used by models conforming to the grammar. The symbol table infrastructure is used for resolving dependencies between model elements that are possibly defined in different files.
    • a context-condition checking framework for checking well-formedness rules that cannot be captured by context-free languages.
    • a visitor infrastructure for traversing models respectively their abstract syntax instances. The abstract syntax of a model consists of its internal representation as an abstract syntax tree abstracting from the concrete syntax of the model (the instance of the data structure obtained from parsing) and the symbol table of the model.
    • a mill infrastructure for retrieving objects for language processing, such as parsers, builders for abstract syntax trees, visitors and objects for the symbol tables of the language. A mill serves as a dynamic factory, adapting to the current modeling language. The possibility to configure the mills is crucial for reusing the functionality implemented for a sublanguage (cf. Section 5.9, Section 5.10.2, and Section 11.5 for details).
    • a code generating framework that extends the FreeMarker template engine [Fre21] by various modularity enhancing features.

    For executing MontiCore using the Automata grammar as input, perform the following steps:

    1. Open a command line shell and change the working directory to the unzipped directory (mc-workspace).
    2. Execute the following command in order to generate the language infrastructure of the Automata DSL:
      java -jar monticore.jar -g Automata.mc4 -hcp hwc/ src/ -mp monticore-rt.jar\n
      The only required argument Automata.mc4 denotes the input grammar that shall be processed by MontiCore. The processing includes the generation of the language infrastructure. Using the option -hcp enables specifying the path to a directory containing the handwritten code that is to be incorporated into the generated infrastructure. In this case, passing the argument hwc/ to the option -hcp makes MontiCore consider the handwritten code located in the directory hwc/. Providing handwritten code enables to easily incorporate additional functionality into the generated code. For example, this enables developers to extend generated abstract syntax classes as detailedly described in (cf. Section 5.10 of the MontiCore handbook). Passing the argument -mp enables specifying the paths to directories or archives containing paths to grammars and Java classes that are imported by the processed grammar and the related tooling. In this case, the archive monticore-rt.jar contains the grammars and handwritten extensions of the monticore standard library. More information about the standard library can be found in Chapters 17- 20 of the handbook.

    Executing the command launches MontiCore, which results in the executing of the following steps:

    1. The specified grammar is parsed and processed by MontiCore.
    2. Java source files for the corresponding DSL infrastructure are generated into the default output directory out. This infrastructure consists of the directories
      • out/automata/ containing the mill (cf. Section 5.9, Section 5.10.2, Section 11.5).
      • out/automata/_ast containing the abstract syntax tree data structure (cf. Chapter 5 of the MontiCore handbook).
      • out/automata/_auxiliary containing adapted mills of sublanguages, which are required for configuring the mills of sublanguages (cf. Chapter 11 of the MontiCore handbook).
      • out/automata/_cocos containing the infrastructure for context conditions (cf. Chapter 10 of the MontiCore handbook).
      • out/automata/_od containing the infrastructure for printing object diagrams for reports produced during processing the models.
      • out/automata/_parser containing the generated parsers, which are based on ANTLR (cf. Chapter 6 of the MontiCore handbook).
      • out/automata/_symboltable containing the infrastructure for the symbol table (cf. Chapter 6 of the MontiCore handbook).
      • out/automata/_visitor containing the infrastructure for visitors (cf. Chapter 9 of the MontiCore handbook).
      • out/reports/automata containing reports created during the processing of the grammar.
    3. The output directory also contains a log file of the executed generation process with the generation time in its name.

    In the following, we review the classes and interfaces generated from the Automata grammar that are relevant for language engineers in more detail. We do not review the classes and interfaces that are only internally relevant for MontiCore and are usually not intended to be used by language engineers.

    "},{"location":"docs/GettingStarted/#abstract-syntax-tree-data-structure","title":"Abstract Syntax Tree Data Structure","text":"

    The tree data structure is generated into the directory out/automata/_ast. Details about the generation of AST classes can be found in (cf. Chapter 5 of the MontiCore handbook). For each nonterminal contained in the grammar, the MontiCore generator produces AST and corresponding builder classes. The AST classes implement the abstract syntax tree data structure.

    The builder classes implement the builder pattern for constructing instances of the respective AST classes as usual. For example, the class ASTAutomaton is the AST class generated for the Automaton nonterminal (cf.\u00a0Listing 2.2) and the class ASTAutomatonBuilder is the corresponding generated builder class.

    Parts of the AST data structure generated for the Automata grammar.

    Figure 2.6: Parts of the AST data structure generated for the Automata grammar.

    The contents of the AST and builder classes are generated systematically from the grammar. The attributes of each AST class resemble the right-hand side of the corresponding production rule. In the following, we mainly speak of attributes, but please be aware that all attributes come fully equipped with access and modification methods, which should normally be used.

    For instance, Figure 2.6 depicts parts of the generated AST infrastructure for the Automata grammar. The class ASTAutomaton contains the attributes name, states, and transitions. The AST class does not contain an attribute for the terminal automaton as it is part of every word conforming to the production of the Automaton nonterminal. The type of the attribute name is String whereas the attributes states and transitions are lists of the types of the AST classes corresponding to the used nonterminals. This is the case because exactly one Name is parsed with the right-hand side of the production of the nonterminal Automaton, whereas multiple states and transitions can be parsed.

    The ASTAutomaton class further contains the attributes symbol, spannedScope, and enclosingScope. These attributes are specific to the symbol table of Automata models and are used for linking the symbol table of a model with its abstract syntax tree. Details can be found in Chapter 9 of the MontiCore handbook.

    Tip 2.7 Generated Symbols and Scopes in the AST

    Each AST class contains access to the enclosingScope.

    When a production contains the keyword symbol, the generated AST class contains the attribute symbol (see Chapter 9 of the MontiCore handbook).

    Keyword scope indicates that a nonterminal also defines a new local scope, stored in attribute spannedScope.

    The parser builds the abstract syntax tree of a model and the available scope genitor creates the symbol table of the model, consisting of symbols and scopes.

    The ASTAutomaton class further contains several straight-forward methods for checking different instances for equality and accessing the attributes. Similar to the ASTAutomaton class, the ASTAutomatonBuilder class contains attributes resembling the right-hand side of the corresponding production. It further contains methods for changing the values of the attributes (e.g., addState), checking whether the AST instance that would be constructed from the current builder state is valid (cf.\u00a0isValid), and for building the AST instance corresponding to the builder's state (cf.\u00a0build). The contents of the other AST and Builder classes are constructed analogously.

    Tip 2.8 Handwritten AST Class Extensions

    If the generator detects that an AST class for a nonterminal is already implemented in the handwritten code, then it produces a corresponding TOP AST class instead.

    This TOP mechanism allows developers to add handwritten extensions to any generated class, while reusing the generated TOP class via extension.

    This gives a very close integration between handwritten and generated code that even adapts builders accordingly, while preventing the very bad habit of performing manual changes to the generated code.

    Option -hcp tells the generator where to look for handwritten integrations.

    The following section presents the methods of the classes for parsing textual models (possibly stored in files) into AST class instances at runtime. For now, it suffices for you to understand that (1) MontiCore generates an extensible AST data structure that resembles the nonterminals and productions of the grammar in a straight-forward way and (2) that all models of a grammar have an AST data structure representation for internal processing.

    "},{"location":"docs/GettingStarted/#parser","title":"Parser","text":"

    The infrastructure is generated into the directory out/automata/_parser. Details about the generated parsers and their uses are described in Chapter 6 of the MontiCore handbook.

    Parts of the class AutomataParser generated from the Automata grammar.

    Figure 2.9: Parts of the class AutomataParser generated from the Automata grammar.

    Parts of the generated class AutomataParser are depicted in Figure 2.9. The class implements the generated parser for the Automata grammar. Usually, developers are solely concerned with the methods parse(String) and parse_String(String). For now, it suffices if you remember that parsing textual Automata models stored in files is possible by calling the method parse(String) of an AutomataParser object with the fully qualified name of the file as input.

    Tip 2.10 Methods for Parsing

    The class AutomataParser contains the methods

    • parse(Reader r),
    • parse(String filename), and
    • parse_String(String content).

    All of the methods return an object of type Optional<ASTAutomaton>, where absence means failure of parsing and errors have been issued.

    For each nonterminal in the grammar, the class further contains methods for parsing a sub-model described by this nonterminal.

    "},{"location":"docs/GettingStarted/#symbol-table","title":"Symbol Table","text":"

    The infrastructure is generated into the directory out/automata/_symboltable. Details about the generated symbol table infrastructure and its use are described in Chapter 9 of the MontiCore handbook. The symbol table infrastructure is used for resolving cross-references concerning information defined in different model elements that are potentially defined in different models stored in different files.

    Figure 2.11: The scope classes generated from the `Automata` grammar. Tip 2.12 Scope Classes

    For the Automata grammar, the generator produces the classes

    • AutomataScope,
    • AutomataArtifactScope, and
    • AutomataGlobalScope

    as well as respective interfaces. The relationships between these classes and interfaces are depicted in Figure 2.11.

    The singleton AutomataGlobalScope contains all AutomataArtifactScopes of all loaded Automata artifacts. AutomataScopes represent scopes spanned inside of models.

    Figure 2.13: Parts of the symbol classes generated from the Automata grammar.

    Figure 2.13 depicts parts of the symbol classes generated for the Automata grammar. As the nonterminal State is annotated with symbol in the Automata grammar, the generator produces the class StateSymbol. The StateSymbol class, inter alia, contains the attributes name, enclosingScope, and spannedScope. The attribute name stores the name of the symbol. The attributes enclosingScope and spannedScope store the enclosing and spanned scopes of the symbol. The class further contains methods for accessing and setting the attributes. For all symbol classes, the MontiCore generator also produces builder classes (e.g., AutomataArtifactScopeBuilder and StateSymbolBuilder).

    Tip 2.14 Extending Symbol Classes

    It is possible to add further methods and attributes in two ways:

    • adding a symbol rule in the grammar (described in Chapter 9 of the MontiCore handbook) or
    • using the TOP mechanism applied to the generated symbols.

    The generated class AutomataScopesGenitor is responsible for creating the scope structure of Automata artifacts and linking the scope structure with the corresponding AST nodes. For this task, it provides the method createFromAST that takes an ASTAutomaton instance as input and returns an IAutomataArtifactScope instance. The returned IAutomataArtifactScope instance can be added as a subscope to the (during runtime unique and administrated by the mill) AutomataGlobalScope instance.

    Developers can create visitors for complementing the symbol table (creating symbols and filling the extensions introduced via symbol rules or the TOP mechanism) of an Automata artifact. After creating the scope structure, the visitor should be used to traverse the AST instance of the artifact for complementing the symbols and scopes. The following sections explain the generated visitor infrastructure in more detail.

    Optional<AutomatonSymbol> resolveAutomaton(String name)\nList<AutomatonSymbol> resolveAutomatonMany(String name)\nOptional<StateSymbol> resolveState(String name)\nList<StateSymbol> resolveStateMany(String name)\n
    Listing 2.15: Different resolve methods.

    For each nonterminal annotated with symbol in the grammar Automata, the scope interfaces contain a symbol-specific resolve method taking a string as input. The method can be called to resolve symbol instances by their names. The name given as input to a resolve method should be as qualified as needed to find the symbol. For instance, Listing 2.15 lists the signatures of four of the resolve methods provided by the interface IAutomataScope.

    For now, it suffices for you to understand that (1) MontiCore generates an extensible symbol table data structure that resembles the scope and symbol structure as specified in the grammar in a straight-forward way and (2) that all models of a grammar have a symbol table data structure representation for internal processing and (3) that symbols can be resolved from scopes via calling the resolve methods.

    "},{"location":"docs/GettingStarted/#deserialization-of-symbol-tables","title":"(De)Serialization of Symbol Tables","text":"

    MontiCore also supports the serialization and deserialization of symbol tables. The (de)serialization is crucial for incremental code generation and efficient language composition via aggregation. Details about this are explained in Chapter 7 and Chapter 9 of the MontiCore handbook.

    For the (de)serialization, the generator produces the class AutomataSymbol2Json. It provides the public methods store and load. The former can be used to serialize IAutomataScope instances into their string representations encoded in JSON and persisting these to a file at a location that is passed as method argument. The latter can be used to load a stored IAutomataScope into its objects representation. For now, it suffices that you understand which methods to call for the (de)serialization.

    "},{"location":"docs/GettingStarted/#visitor","title":"Visitor","text":"Figure 2.16: Parts of the visitor infrastructure generated from the Automata grammar

    The infrastructure is generated into the directory out/automata/_visitor. Details about the generated visitor infrastructure are described in Chapter 8 of the MontiCore handbook. For each grammar, the generator systematically produces several classes and interfaces implementing the visitor infrastructure. For the Automata grammar, for example, the generator produces the interfaces AutomataTraverser, AutomataVisitor2, and AutomataHandler and the class AutomataTraverserImplementation. The relationships between these interfaces and classes are depicted in Figure 2.16.

    The interfaces Traverser, Visitor2 and Handler together realize the Visitor pattern. Conceptually, the traverser is the entry point for traversing. The traverser manages visitors for the different sublanguages and realizes the default traversing strategy. Whenever an AST node is traversed, the traverser delegates the visit to the corresponding visitor implementation. If a special traversal is to be implemented that differs from the default, it is possible to add handlers to the traverser that realize the alternative traversal. For a more detailed explanation consider reading Chapter 8 of the MontiCore handbook.

    Tip 2.17 Visitors

    MontiCore provides the visitor pattern in a detangled and thus flexible variant.

    AutomataTraverser is traversing the AST. AutomataVisitor2 contain the actual functionalities, added through subclassing. Many visitors can be added to the traverser for parallel execution via the method add4Automata.

    The visitors are compositional, allowing to maximize reuse of visitors from sublanguages, and they can be adapted through the TOP mechanism.

    For example, the handwritten class PrettyPrinter, which can be found in the directory mc-workspace/src/automata/prettyprint, implements functionality for pretty printing an Automata model, which is given by its abstract syntax tree. Listing 2.18 depicts the attributes and the constructor of the class. The PrettyPrinter class implements the AutomataHandler interface. Its constructor instantiates a printer (a helper for printing indented strings) and retrieves an AutomataTraverser object from the mill (which is explained later on). It sets the handler of the traverser to itself. This ensures that the pretty printer becomes the handler of the traverser. We will execute it in a following section.

    public class PrettyPrinter implements AutomataHandler {\nprivate final IndentPrinter printer;\nprivate AutomataTraverser traverser;\n\npublic PrettyPrinter() {\nthis.printer = new IndentPrinter();\nthis.traverser = AutomataMill.traverser();\ntraverser.setAutomataHandler(this);\n}\n// further methods\n}\n
    Listing 2.18: Attributes and constructor of the PrettyPrinter for the Automata language.

    For now, you should understand that (1) for implementing visitors it is often sufficient to implement the visitor interfaces and to add them to a traverser and (2) custom traversals can be realized by implementing handlers and adding those to the traverser.

    "},{"location":"docs/GettingStarted/#context-conditions","title":"Context Conditions","text":"

    The infrastructure is generated into the directory out/automata/_cocos. Details about the generated context condition infrastructure are described in Chapter 10 of the MontiCore handbook.

    For each nonterminal of a grammar, the generator produces a context condition interface for implementing context conditions for this nonterminal. For the Automata grammar, for example, the generator produced the interface AutomataASTStateCoCo. The interface solely contains the method check(ASTState). Each class implementing the interface should represent a predicate over subtrees of abstract syntax trees starting at a node with the type corresponding to the nonterminal.

    The check method should be implemented such that it reports an error or a warning if the input node does not satisfy the predicate. Thus, context conditions implement well-formedness rules that cannot be captured by context-free grammars (or that are intentionally not captured by the grammar to achieve a specific AST data structure). For producing the error or warning, the static methods error and warning of the MontiCore runtime class Log should be used.

    For the Automata grammar, the generator also produced the class AutomataCocoChecker. For each nonterminal of the grammar, the class contains a method for adding context condition instances to an AutomataCocoChecker instance. For checking whether an AST node satisfies all registered context conditions, the method checkAll can be called with the AST node as input. Calling the method makes the checker traverse the abstract syntax tree and check whether each node satisfies the context conditions registered for the node. Thus, AutomataCocoChecker instances represent sets of context conditions that are required to be satisfied by abstract syntax tree instances.

    For now, you should understand that (1) implementing context conditions is possible via implementing the generated CoCo interfaces and (2) context conditions can be checked via instantiating the Checker class, adding the CoCos, and calling the checkAll method.

    "},{"location":"docs/GettingStarted/#mill-as-factory-for-builders","title":"Mill as Factory for Builders","text":"

    The for the Automata language is generated into the directory out/automata/. Details about the generated mill and the mill pattern in general are described in Section 11.5. The generated mill class AutomataMill is responsible for providing ready to use and correct parser, scope genitor, scope, and builder instances. The mill of each language is a singleton.

    Tip 2.19 Mill Use and Automatic Initialization

    A mill is a factory for builders and other commonly used functions, such as parsers or visitors. The mill was introduced to ensure compositionality of languages, while retaining reusability of functions developed for sublanguages.

    Only one mill instance exists, even though in composed languages it is available under several static signatures. Let language G2 extend another language G1. Then G2Mill initializes the G1Mill appropriately, such that all the code of the sublanguage G1 can be reused in the tools developed for the language G2, even when creating new AST nodes, symbols, etc.

    Cool mechanism and the developers don't have to bother.

    public static IAutomataGlobalScope globalScope()\npublic static IAutomataArtifactScope artifactScope()\npublic static IAutomataScope scope()\npublic static AutomataScopesGenitor scopesGenitor ()\npublic static AutomataScopesGenitorDelegator scopesGenitorDelegator()\npublic static ASTAutomatonBuilder automatonBuilder()\npublic static AutomatonSymbolBuilder automatonSymbolBuilder()\npublic static AutomataParser parser()\npublic static AutomataTraverser traverser ()\n
    Listing 2.20: Some method of the AutomataMill.

    Developers should retrieve all instances of the classes and interfaces provided by the mill by using the mill. Instances of the classes and interfaces that are provided by the mill should never be instantiated manually. Otherwise, it may be the case that not all of the code implemented for the language can be reused as expected in other languages extending the language. Listing 2.20 shows some signatures of the methods of the AutomataMill.

    Tip 2.12 Mill Methods

    A mill provides public static methods for retrieving the instances of the parsers, scope genitors, scopes, and builders. For that is acts like a factory. Because a mill is realized using the static delegator pattern (cf. Section 11.1), it still can be adapted at will.

    This combines the advantage of general availability with the advantage of being able to override the functions.

    For now, you should understand that (1) the methods of the mill should be used for creating ready to use and correct parser, scope genitor, scope, and builder instances and (2) how to call these methods.

    "},{"location":"docs/GettingStarted/#compile-the-target","title":"Compile the Target","text":"

    Section 2.2.3 of the MontiCore handbook describes how to generate the desired Java code from a MontiCore grammar. For these Java classes, generated for the Automata DSL, execute the following command in the mc-workspace:

    With Powershell on Windows

    javac -cp monticore-rt.jar -sourcepath \"src/;out/;hwc/\" `\n                                  src/automata/AutomataTool.java\n
    With Bash on Unix
    javac -cp monticore-rt.jar -sourcepath \"src/:out/:hwc/\" \\\nsrc/automata/AutomataTool.java\n
    With cmd on Windows
    javac -cp monticore-rt.jar -sourcepath \"src/;out/;hwc/\" ^\n                                 src/automata/AutomataTool.java\n

    Please note, on Unix systems paths are separated using \":\" (colon) instead of semicolons.

    Providing the option -cp with the argument monticore-cli.jar makes the Java compiler consider the compiled MontiCore runtime classes contained in the file monticore-cli.jar.

    The option -sourcepath enables to specify paths to directories containing the source files that should be considered during the compilation.

    In this case, executing the command makes the Java compiler consider all generated classes located in and all handwritten classes located in src and hwc. The last argument instructs the Java compiler to compile the class src/automata/AutomataTool.java.

    Please note that the structure of the handwritten classes follows the package layout of the generated code, i.e. there are the following sub directories (Java packages):

    • src/automata contains the top-level language realization for using the generated DSL infrastructure. In this case the class src/automata/AutomataTool.java constitutes a main class executable for processing automata models with the automata DSL.
    • src/automata/cocos contains infrastructure for context condition of the automata DSL.
    • src/automata/prettyprint contains an exemplary use of the generated visitor infrastructure for processing parsed models for pretty printing.
    • src/automata/visitors contains an exemplary analysis using the visitor infrastructure. The exemplary analysis counts the states contained in the parsed automata model.
    • hwc/automata/_ast contains an exemplary usage of the handwritten code integration mechanism for modifying the AST for the automata DSL. Details about the integration mechanism are described in Section 5.10.
    • hwc/automata/_symboltable contains handwritten extensions of the generated symbol table infrastructure. Details about implementing handwritten symbol table infrastructure extensions are described in Chapter 9 of the MontiCore handbook.

    Please, also do not mix the code for the Automata tool vs. the code for the final product, generated from that tool, although both have a similar package structure.

    We already described the contents of the directories hwc/automata/_ast and hwc/automata/_symboltable in the previous section. They contain handwritten extensions of the abstract syntax of the Automata language.

    public class CountStates implements AutomataVisitor2 {\nprivate int count = 0;\n\n@Override\npublic void visit(ASTState node) {\ncount++;\n}\n\npublic int getCount() {\nreturn count;\n}\n}\n
    Listing 2.22: The CountStates visitor implementation

    The directory src/automata/visitors contains the file CountStates.java. The class is depicted in Listing 2.22. It implements a simple visitor for counting the number of states contained in an Automata model. To this effect, it implements the AutomataVisitor2 interface. It has an attribute count of type int for storing the current number of counted nodes. It overrides the visit method for ASTState to increase the counter whenever a state is visited.

    The directory src/automata/cocos contains the context-condition implementations for the Automata language.

    public class AtLeastOneInitialAndFinalState\nimplements AutomataASTAutomatonCoCo {\n@Override\npublic void check(ASTAutomaton automaton) {\nboolean initialState = false;\nboolean finalState = false;\n\nfor (ASTState state : automaton.getStateList()) {\nif (state.isInitial()) {\ninitialState = true;\n}\nif (state.isFinal()) {\nfinalState = true;\n}\n}\n\nif (!initialState || !finalState) {\n// Issue error...\nLog.error(\"0xA0116 An automaton must have at least one initial \n                 and one final state.\",\nautomaton.get_SourcePositionStart());\n}\n}\n}\n
    Listing 2.23: Context condition implementation for checking that there exist at least one initial and at least one final state.

    Listing 2.23 depicts the class AtLeastOneInitialAndFinalState. The class implements a context condition for checking whether an Automata model contains at least one initial and at least one final state. To this effect, the class implements the interface AutomataASTAutomatonCoCo. The class StateNameStartsWithCapitalLetter is implemented similarly.

    public class TransitionSourceExists\nimplements AutomataASTTransitionCoCo {\n\n@Override\npublic void check(ASTTransition node) {\n\nIAutomataScope enclosingScope = node.getEnclosingScope();\nOptional<StateSymbol> sourceState =\nenclosingScope.resolveState(node.getFrom());\n\nif (!sourceState.isPresent()) {\n// Issue error...\nLog.error(\n\"0xADD03 Source state of transition missing.\",\nnode.get_SourcePositionStart());\n}\n}\n}\n
    Listing 2.24: Context condition implementation for checking that states used in transitions exist.

    Listing 2.24 presents the implementation of the class TransitionSourceExists. The class implements a context condition for checking whether the source states used in transitions are defined. To this effect, the class uses the resolving mechanisms of the symbol table. For each transition, the context conditions tries to resolve the state symbol corresponding to the source state of the transition. If the resolving fails for the state, then the context condition logs an error.

    The class AutomataTool is the main class of the Automata language. It is defined in the file AutomataTool.java contained in the directory src/automata.

    public class AutomataTool extends AutomataToolTOP {\n// main method missing in this listing\n\npublic ASTAutomaton parse(String model) {\ntry {\nAutomataParser parser = new AutomataParser() ;\nOptional<ASTAutomaton> optAutomaton = parser.parse(model);\n\nif (!parser.hasErrors() && optAutomaton.isPresent()) {\nreturn optAutomaton.get();\n}\nLog.error(\"0xEE840 Model could not be parsed.\");\n}\ncatch (RecognitionException | IOException e) {\nLog.error(\"0xEE640 Failed to parse \" + model, e);\n}\nSystem.exit(1);\nreturn null;\n}\n}\n
    Listing 2.25: Methods for parsing and creating symbol tables.

    Listing 2.25 presents the implementation of the method parse of the AutomataTool class which can be used for parsing Automata models. The class extends the generated abstract superclass AutomataToolTOP that provides methods to be used in the run method. One example is the method createSymbolTable that uses the global scope and genitors available in the mill to create a symbol table for Automata.

    public static void main(String[] args) {\n// delegate main to instantiatable method for better integration,\n// reuse, etc.\nnew AutomataTool().run(args);\n}\n\npublic void run(String[] args) {\n\n// use normal logging (no DEBUG, TRACE)\nAutomataMill.init();\nLog.ensureInitalization();\n\nOptions options = initOptions();\ntry {\n//create CLI Parser and parse input options from commandline\nCommandLineParser cliparser = new org.apache.commons.cli.DefaultParser();\nCommandLine cmd = cliparser.parse(options, args);\n\n//help: when --help\nif (cmd.hasOption(\"h\")) {\nprintHelp(options);\n//do not continue, when help is printed.\nreturn;\n}\n//version: when --version\nelse if (cmd.hasOption(\"v\")) {\nprintVersion();\n//do not continue when help is printed\nreturn;\n}\n\nLog.info(\"Automata DSL Tool\", \"AutomataTool\");\n\nif (cmd.hasOption(\"i\")) {\nString model = cmd.getOptionValue(\"i\");\nfinal ASTAutomaton ast = parse(model);\nLog.info(model + \" parsed successfully!\", \"AutomataTool\");\n\nAutomataMill.globalScope().setFileExt(\"aut\");\nIAutomataArtifactScope modelTopScope = createSymbolTable(ast);\n// can be used for resolving things in the model\nOptional<StateSymbol> aSymbol = modelTopScope.resolveState(\"Ping\");\nif (aSymbol.isPresent()) {\nLog.info(\"Resolved state symbol \\\"Ping\\\"; FQN = \"\n+ aSymbol.get().toString(),\n\"AutomataTool\");\n} else {\nLog.info(\"This automaton does not contain a state called \\\"Ping\\\";\",\n\"AutomataTool\");\n}\nrunDefaultCoCos(ast);\n\nif(cmd.hasOption(\"s\")){\nString storeLocation = cmd.getOptionValue(\"s\");\nstoreSymbols(modelTopScope, storeLocation);\n}\n\n// analyze the model with a visitor\nCountStates cs = new CountStates();\nAutomataTraverser traverser = AutomataMill.traverser();\ntraverser.add4Automata(cs);\nast.accept(traverser);\nLog.info(\"Automaton has \" + cs.getCount() + \" states.\", \"AutomataTool\");\nprettyPrint(ast,\"\");\n}else{\nprintHelp(options);\n}\n} catch (ParseException e) {\n// e.getMessage displays the incorrect input-parameters\nLog.error(\"0xEE752 Could not process AutomataTool parameters: \" + e.getMessage());\n}\n}\n
    Listing 2.26: Main method of the AutomataTool class

    The AutomataTool provides a main method, which can be called from the command line. The implementation of the method is depicted in Listing 2.26. It expects two inputs. The first is the name of a file containing an Automata model. The second input is the name of the file in which the tool should store the symbol table of the model given as first input.

    The method

    • parses the input model,
    • creates the symbol table,
    • resolves a state,
    • executes context conditions,
    • stores the symbol table by using the serialization,
    • executes the visitor for counting the states, and
    • pretty prints the model to the standard output.

    Inspect the main method and try to understand the implementation for the executed tasks. Read the above descriptions again if necessary.

    "},{"location":"docs/GettingStarted/#run-the-tool","title":"Run the Tool","text":"

    The previous command compiles the handwritten and generated code including the Automata tool class AutomataTool. For running the Automata DSL tool, execute the following command:

    With Powershell on Windows

    java -cp \"src/;out/;hwc/;monticore-rt.jar\" `\n                    automata.AutomataTool -i example/PingPong.aut `\n                    -s st/PingPong.autsym\n
    With Bash on Unix
    java -cp \"src/:out/:hwc/:monticore-rt.jar\" \\\nautomata.AutomataTool -i example/PingPong.aut \\\n-s st/PingPong.autsym\n
    With cmd on Windows
    java -cp \"src/;out/;hwc/;monticore-rt.jar\" ^\n                   automata.AutomataTool -i example/PingPong.aut ^\n                   -s st/PingPong.autsym\n

    Please note again, on Unix systems paths are separated using \":\" (colon) instead of semicolons. Executing the command runs the Automata DSL tool.

    Using the option -cp makes the Java interpreter consider the compiled classes contained in the paths specified by the argument.

    The argument automata.AutomataTool makes the Java interpreter execute the main method of the class automata.AutomataTool contained in the directory src.

    The argument example/PingPong.aut is passed to the main method of the Automata DSL tool class as input. Inspect the output on the command line, which displays log messages concerning the processing of the example Automata model.

    The last argument st/PingPong.autsym is also passed to the main method. It makes the tool store the serialized symbol table of the input model into the file example/PingPong.aut.

    The shipped example Automata DSL (all sources contained in mc-workspace/src and mc-workspace/hwc) can be used as a starting point for creating your own language. It can easily be altered to specify your own DSL by adjusting the grammar and the handwritten Java sources and rerunning MontiCore as described above.

    "},{"location":"docs/GettingStarted/#using-monticore-via-gradle-from-the-command-line","title":"Using MontiCore via Gradle From the Command Line","text":"

    It is possible to execute MontiCore via the MontiCore plugin. A detailed description about using the MontiCore Gradle plugin is given in Chapter 16 of the MontiCore handbook. This section describes the execution of MontiCore via a Gradle plugin from the command line shell by example. Before you start, install gradle and make sure that you can use it from the command line.

    The shipped example Automata DSL can be used as a starting point and can be downloaded here:

    https://www.monticore.de/download/Automaton.zip\n

    The build script (file build.gradle) can easily be adapted for creating build scripts for other languages. For executing MontiCore via the Gradle plugin from the command line shell by example of the Automata DSL, perform the following steps:

    1. Download the Automata example.
    2. Unzip the downloaded zip file into an arbitrary directory.
    3. Open a shell and change your working directory to the directory in which you unzipped the downloaded file (the directory containing the file build.gradle).
    4. Execute Gradle in the shell:
      • If you are using a Windows shell, execute the command gradle build.
      • If you are using a Unix shell, execute the command ./gradle build.

    When executing the above commands, MontiCore launches, which results in the execution of the following steps:

    1. The grammars specified in the build.gradle are incrementally parsed and processed by MontiCore.
    2. Java source files for the corresponding DSL infrastructure are generated into the default output directory ../target/generated-sources/monticore/sourcecode. The contents of this generated directory are equal to the contents of the generated directory out as described in .
    "},{"location":"docs/GettingStarted/#using-monticore-in-eclipse","title":"Using MontiCore in Eclipse","text":"

    The MontiCore plugin can be used in Eclipse. Section 2.4.1 describes the process of setting up Eclipse. Section 2.4.2 presents how to import the example project in Eclipse. Finally, Section 2.4.3 explains how the MontiCore Gradle plugin can be executed in Eclipse.

    "},{"location":"docs/GettingStarted/#setting-up-eclipse","title":"Setting up Eclipse","text":"

    Before you import the example project and run MontiCore as a Gradle plugin, please make sure that a current version of the Gradle plugin is installed in Eclipse. When installing a new version of Eclipse, the Gradle plugin is installed by default. If the Gradle plugin is not yet integrated into your Eclipse installation, download the latest Eclipse version or perform the following steps to install the Eclipse plugin:

    1. Download and install Eclipse (or use an existing one).
    2. Open Eclipse.
    3. Install the needed Plugins.
      • Help > Eclipse Marketplace...
      • Type 'gradle' in the search box and click Enter.
      • Install the 'Buildship Gradle Integration' plugin.
    4. Make sure to configure Eclipse to use an JDK instead of an JRE.
      • Window > Preferences > Java > Installed JREs.
    "},{"location":"docs/GettingStarted/#importing-the-example","title":"Importing the Example","text":"

    The shipped example Automata DSL can be used as a starting point. Once imported into Eclipse, it can easily be altered to specify your own DSL by adjusting the grammar and the handwritten Java sources and rerunning MontiCore as described in Section 2.4.3. To import the example, perform the following steps:

    1. Download and unzip the Automata example:
      http://www.monticore.de/download/Automaton.zip\n
    2. Open Eclipse and select
      • File > Import > Gradle (if you are required to choose a Gradle version, then choose version 7.4.1) > Existing Gradle Projects > Next.
      • Click on the Browse.. button and import the directory that contains the file build.gradle from the Automata example.
    "},{"location":"docs/GettingStarted/#running-monticore","title":"Running MontiCore","text":"

    To execute the MontiCore Gradle plugin, perform the following steps:

    • Select the Gradle Task menu (at the top or bottom, depending on your installed Eclipse version).
    • There select automaton > build > build (double click).

    This makes Eclipse execute the MontiCore Gradle plugin as described in Section 2.3 of the MontiCore handbook . After installing and executing MontiCore in Eclipse, your workspace should look similar to Figure 2.27.

    Figure 2.27: Eclipse after importing the example project and executing MontiCore"},{"location":"docs/GettingStarted/#using-monticore-in-intellij-idea","title":"Using MontiCore in IntelliJ IDEA","text":"

    The MontiCore plugin can be used in IDEA. Section 2.5.1 describes the process of setting up IntelliJ IDEA. Afterwards, Section 2.5.2 presents how to import the example project in Eclipse. Finally, Section 2.5.3 explains how the MontiCore Gradle plugin can be executed in IntelliJ IDEA.

    "},{"location":"docs/GettingStarted/#setting-up-intellij-idea","title":"Setting up IntelliJ IDEA","text":"

    For setting up IntelliJ IDEA, perform the following steps:

    1. Download and install IntelliJ IDEA (or use your existing installation).
      • Hint for Students: You get the Ultimate version of IntelliJ IDEA for free.
    2. Open IntelliJ IDEA.
    "},{"location":"docs/GettingStarted/#importing-the-example_1","title":"Importing the Example","text":"

    The shipped example Automata DSL can be used as a starting point. Once imported into IntelliJ IDEA, it can easily be altered to specify your own DSL by adjusting the grammar and the handwritten Java sources and rerunning MontiCore as described in Section 2.5.3. For importing the example, perform the following steps:

    1. Download and unzip the Automata example:
      http://www.monticore.de/download/Automaton.zip\n
    2. In the IDE select: File > Open.
    3. Select the directory containing the build.gradle (if you are required to choose a Gradle version, then choose version 6.7.1).
    "},{"location":"docs/GettingStarted/#running-monticore_1","title":"Running MontiCore","text":"

    To execute the MontiCore Gradle plugin, perform the following steps:

    • Select the Gradle Projects menu on the right.
    • From there select automaton > Tasks> build > build (double click).

    This makes IntelliJ IDEA execute the Gradle plugin as described in Section 2.3. If you do not see the Gradle Projects menu yet, right-click on the build.gradle file and select 'Import Gradle Project'. Now the Gradle Projects menu should occur on the right side and you can follow the above mentioned steps for the execution. After installing and executing MontiCore in IntelliJ IDEA, your workspace should look similar to Figure 2.28.

    Figure 2.28: IntelliJ IDEA after importing the example project and executing MontiCore Fre21 FreeMarker website. https://freemarker.apache.org/, 2021."},{"location":"docs/GettingStarted/#using-monticore-with-gitpod","title":"Using MontiCore with GitPod","text":"

    Installing all the prerequisites and an IDE can take some time. Alternatively to this, you can use Gitpod, an open-source Kubernetes application for ready-to-code developer environments. It already has all the prerequisites and an operational Web IDE similar to Microsoft's Visual Studio Code installed. You need to login with an existing GitHub account to use it.

    This link can be used to access the Gitpod project for the Automata language. First, an environment for the project with the proper Java and Gradle version will be prepared and initialized automatically. After that, you will be directed to the Web IDE. The project will be built with Gradle first, and after that it is ready-to-use. The Web IDE also has a built-in terminal which can be used to build the project via gradle build or execute other tasks.

    The Web IDE can be used to change existing project files, such as the Automata grammar or the handwritten classes for the language. Simply navigate to the grammars or classes in the file explorer on the left-hand side of the IDE and edit the files. This makes experimenting with MontiCore possible. The changes will be compiled by the IDE immediately and compilation errors will be marked with red color. To run the project, execute the command gradle build in the terminal.

    You will notice that the link to the Gitpod project is generated and always has the same pattern. An example for a link is https://indigo-ostrich-8psdfoer.ws-eu18.gitpod.io. After 30 minutes of non-use, Gitpod will \"freeze\" the environment. It can be reactivated by using the same link to access it. The environment is reactivated, and you do not even need to rebuild the project with Gradle to use the project again.

    "},{"location":"docs/GettingStarted/#further-information","title":"Further Information","text":"
    • Project root: MontiCore @github
    • MontiCore documentation
    • List of languages
    • MontiCore Core Grammar Library
    • Best Practices
    • Publications about MBSE and MontiCore
    • License definition
    "},{"location":"docs/Languages/","title":"List of Languages","text":""},{"location":"docs/Languages/#monticore-languages-of-level-ii-an-overview","title":"MontiCore Languages of Level II - an Overview","text":"

    MontiCore is a language workbench with an explicit notion of language components. It uses grammars to describe textual DSLs. MontiCore uses an extended grammar format that allows to compose language components via inheritance, embedding and aggregation (see the handbook for details).

    A language component is mainly represented through (1) the grammar describing concrete and abstract syntax of the language, (2) Java-classes implementing specific functionalities, and (3) Freemarker-Templates helping to print a model to text. However, language components are often identified with their main component grammar.

    Language components are currently organized in two levels: In this list you mainly find grammars for complete (but also reusable and adaptable) languages (Level II). A list of grammar components with individual reusable nonterminals is also available in the MontiCore core project (development status) (Level I).

    The following list contains the language grammars found in the MontiCore projects, such as cd4analysis/cd4analysis. They are usually contained in project folders like src/main/grammars/ and organized in packages like de.monticore.cd. Publicly available MontiCore projects are hosted at https://github.com/MontiCore.

    "},{"location":"docs/Languages/#list-of-languages","title":"List of Languages","text":""},{"location":"docs/Languages/#class-diagram-for-analysis-cd4a-monticore-stable","title":"Class Diagram For Analysis (CD4A) (MontiCore stable)","text":"
    • CD4A is the textual representation to describe UML class diagrams (it uses the UML/P variant).
    • CD4A covers classes, interfaces, inheritance, attributes with types, visibilities, and all kinds of associations and composition, including qualified and ordered associations. Classes can be placed in different packages. An example:
      classdiagram MyLife { \n  abstract class Person {\n    int age;\n    Date birthday;\n    List<String> nickNames;\n  }\n  package com.universityLib {\n    <<myStereotype>> class Student extends Person {\n      StudentStatus status;\n    }\n    enum StudentStatus { ENROLLED, FINISHED; }\n  }\n\n  composition Person -> Address [*]  {ordered};\n  association [0..2] Person (parent) <-> (child) Person [*];\n  association phonebook Person [String] -> PhoneNumber ;\n}\n
    • CD4A focuses on the analysis phase in typical data-driven development projects and is therefore mainly for data modelling. Consequently, it omits method signatures and complex generics. The primary use of the CD4A language is therefore data modelling. The CD4A language opens various possibilities for the development of data structures, database tables as well as data transport infrastructures in cloud and distributed systems.
    • Main grammar de.monticore.cd.CD4Analysis and detailed description
    "},{"location":"docs/Languages/#class-diagram-for-code-cd4code-monticore-stable","title":"Class Diagram for Code (CD4Code) (MontiCore stable)","text":"
    • CD4Code describes UML class diagrams.
    • CD4Code is a conservative extension of CD4A, which includes method signatures. An example:
      classdiagram MyLife2 {\n  // like CD4A but also allows:\n  class Person {\n    protected List<Person> closestFriends(int n);\n    void addFriend(Person friends...);\n    <<myStereotype>> void relocate();\n  }\n}\n
    • CD4Code is often used as tool-internal AST that allows to map any kind of source models to a class/attribute/method/association based intermediate structure, before it is printed e.g. as Java code. For example a transformation sequence could be:
    • MontiCoreCLI: Grammar -> Grammar AST encoded in CD4Code -> Decoration for custom behavior -> Java code
    • Statechart -> State pattern encoded in CD4Code -> Decoration by monitoring methods -> Java code.
    • Main grammar de.monticore.cd.CD4Code and detailed description (see Section CD4Code)
    "},{"location":"docs/Languages/#feature-diagrams-monticore-stable","title":"Feature Diagrams (MontiCore stable)","text":"
    • Language for feature models and feature configurations.
    • Feature diagrams are used to model (software) product lines and their variants.
    • Feature configurations select a subset of features of a feature model to describe a product of the product line. An example:
      featurediagram MyPhones {\n  Phone -> Memory & OS & Camera? & Screen;\n  Memory -> Internal & External?;\n  Internal -> [1..2] of {Small, Medium, Large};\n  OS -> iOS ^ Android;\n  Screen -> Flexible | FullHD;\n\n  Camera requires (iOS && External) || Android ;\n}\n
      Rules F -> ... have a parent feature (left-hand side) and its child features (right-hand side). Operators are: optional feature ?, and &, or |, xor ^, and subset cardinality constraints, like [1..2] of .... Further, a feature model may define cross-tree constraints using logic operators and &&, or ||, implication requires, etc.
    • Main grammar FeatureDiagram and detailed description
    "},{"location":"docs/Languages/#gui-dsl-monticore-stable-not-publicly-available-links-are-private","title":"GUI DSL: (MontiCore stable) - not publicly available (links are private)","text":"
    • Language for textual definition of Graphical User Interfaces of Web Applications
    • GUI DSL covers GUI elements and relevant configuration, which include layout elements, widgets, their style definition and references to data sources.
    • Language is mainly used to describe GUI of Web Applications. The models of the language represents graphical views or their parts, omitting smaller details of style definition and simplifying connection between graphical elements and data sources.
    • Currently, new version of the GUIDSL is being developed:
    • Basis grammar GUIBasis includes constructs for general visualization component definitions, control statements and components for layout description.
    • Example models can be found in the same repository.
    • Main grammar GUIDSL includes basic concepts and more specific implementation of component configuration.
    • In projects legacy version is currently used:
    • Examples: MaCoCo, Ford
    • Main grammar GUIDSL includes definitions of MontiGem visualisation components, which are based on abstract concepts, described in core grammar GUIDSLCore. Detailed description and documentation.
    "},{"location":"docs/Languages/#monticore-grammar-monticore-stable","title":"MontiCore Grammar (MontiCore Stable)","text":"
    • Language for MontiCore Grammars itself. It can be understood as meta language, but also used as ordinary language.
    • Its main use currently: A MontiCore grammar defines the concrete syntax and the abstract syntax of a textual language. Examples: All languages on this page are defined using MontiCore grammars and thus conform to this Grammar.
    • Main features: Define nonterminals and their productions in EBNF, lexical token as regular expressions.
    • Most important extensions to standard grammars:
    • Abstract, interface and external productions allow to define extensible component grammars (object-oriented grammar style).
    • Inherited productions can be redefined (overwritten) as well as conservatively extended.
    • Symbol and scope infrastructure is defined by simple keywords.
    • Symbols definition places can be introduced and symbol referencing places defined, such that for standard cases automatically symbol tables can be added.
    • Additional attributes and methods can be added to the abstract syntax only.
    • Various elements, such as semantic predicates and actions can be defined in the same style as the underlying ANTLR.
    • MontiCore grammars can be left recursive and even allow mutual recursion. This is e.g. useful for expression hierarchies.
    • Additional elements, such as enum productions and comfortable operations for grammar definitions exist.
    • Main grammars de.monticore.grammar.Grammar defines the language with some open parameters and de.monticore.grammar.Grammar_WithConcepts binds the external, imported expressions, method bodies, etc.
    • Detailed description in the MontiCore Handbook.
    "},{"location":"docs/Languages/#json-monticore-stable","title":"JSON (MontiCore Stable)","text":"
    • The MontiCore language for parsing JSON artifacts. An example:
      { \"Alice\": {\n    \"fullname\": \"Alice Anderson\",\n    \"address\": {\n      \"postal_code\": 10459, \n      \"street\": \"Beck Street\",\n      \"number\": 56              }  },\n  \"Bob\": { ... },\n  \"Caroll\": { ... }, ...\n}\n
    • The JSON grammar adheres to the common JSON standard and allows parsing arbitrary JSON artifacts for further processing.
    • Actually the grammar represents a slight superset to the official JSON standard. It is intended for parsing JSON-compliant artifacts. Further well-formedness checks are not included, because we assume to parse correctly produced JSON documents only.
    • Please note that JSON (like XML or ASCII) is primarily a carrier language. The concrete JSON dialect and the question, how to recreate the real objects / data structures, etc. behind the JSON tree structure is beyond this grammar, but can be applied to the AST defined here.
    • Main grammar de.monticore.lang.JSON and detailed description
    "},{"location":"docs/Languages/#montiarc-monticore-stable","title":"MontiArc (MontiCore Stable)","text":"
    • MontiArc is an architecture and behavior modeling language and framework that provides a platform independent structure and behavior modeling language with an extensible code generation framework.
    • MontiArc covers components their ports, connectors between components and embedded statecharts for component behavior description.
    • Statecharts define states and transitions with conditions on the incoming messages as well as transition actions. An example:
      component InteriorLight {                           // MontiArc language\n  port in Boolean lightSignal,          // ports\n       in Boolean doorSignal\n       out OnOff status;\n  ORGate or;                            // used subcomponents\n  lightSignal -> or.a;                  // connectors\n  doorSignal -> or.b;\n  or.c -> cntr.signal;\n  component LightController cntr {      // freshly defined subcomponent \n    port in OnOff signal,\n         out OnOff status;\n    statechart {                        // with behavior by a Statechart\n      initial state Off / {status = OFF};\n      state On;\n      Off -> On [ signal == true ] / {status = ON}\n      On -> Off [ signal == false ] / {status = OFF}\n    }\n  }\n  cntr.status -> status;\n}\n
    • MontiArc's main goal is to provide a textual notation for Component&Connector diagrams, which is used quite often in various variants in industry. E.g. SysML's BDD, UML's component composition diagrams use the same paradigm.
    • MontiArc does not define data types for their signals, but assumes that these types can be imported (e.g. from a class diagram).
    • MontiArc itself also has no timing predefined, but for a complete language a concrete timing, such as formally grounded by Focus, should be added.
    • Main grammar MontiArc.mc4 and detailed description
    "},{"location":"docs/Languages/#oclp-monticore-stable","title":"OCL/P (MontiCore Stable)","text":"
    • OCL/P is the textual representation of the UML OCL standard, adapted with Java-like syntax. Its main goal is the usage in combination with other languages like CD4A or Object Diagrams as an integrated part of that languages.
    • OCL/P allows to define invariants and pre-/post-conditions in the known OCL style plus some extensions, such as a generalized let construction. Furthermore, it offers a large set expressions to model constraints. These expressions include Java expressions, set operations, list operations etc., completely covering the OCL standard concepts, but extend it e.g. by set comprehensions known from Haskell, a typesafe cast or a transitive closure operator. An example shows several of the above-mentioned syntactic features:

      ocl Bookshop {\n  context Shop s inv CustomerPaysBeforeNewOrder:      // invariant\n    forall Customer c in s.customers:                 // quantifiers available\n      c.allowedToOrder implies !exists Invoice i in s.invoices:\n        i.customer == c && i.moneyPayed < i.invoiceAmount ;\n\n  // Method specification for selling a book\n  context Invoice Stock.sellBook(String iban, int discountPercent, Customer c) \n    let availableBooks =                              // set comprehension\n          { book | Book book in booksInStock, book.iban == iban }\n    pre:  !availableBooks.isEmpty &&                  // precondition\n          c.allowedToOrder;\n    post: let discount = (100 - discountPercent)/100; // postcondition, let\n              b = result.soldBook                     // result variable \n          in                                        \n              !(b isin booksInStock) &&\n              booksInStock.size@pre == booksInStock.size + 1 &&  // @pre\n              result.invoiceAmount == b.price * discount;  // result variable \n}\n

    • The OCL language component contains four grammars:

    • OCL,
    • OCLExpressions,
    • OptionalOperators, and
    • SetExpressions.
    • The detailed description provides an in-depth guide for language engineers.
    "},{"location":"docs/Languages/#object-diagrams-monticore-stable","title":"Object Diagrams (MontiCore Stable)","text":"
    • OD is a language for textual denotation of object diagrams. The OD language has several purposes (when combined with appropriate language extensions):
    • specification language for object structures (as part of the UML/P)
    • store and transport of data sets (e.g. the artifact analysis toolchain), and
    • report format for the MontiCore tool infrastructure.
    • OD covers named and anonymous objects, object types, links, attributes, attribute values, lists, maps, and visibilities. Special data types, such as Date allow comfortable definition and reading of ODs. For a comfortable definition, objects may be nested into trees while easily retaining their full graph structure. An example:
      objectdiagram MyFamily {\n  alice:Person {\n    age = 29;\n    cars = [\n      :BMW {\n        color = BLUE;\n      },\n      tiger:Jaguar {\n        color = RED;\n        length = 5.3; \n      }\n    ];\n  };\n  bob:Person {\n    nicknames = [\"Bob\", \"Bobby\", \"Robert\"];\n    cars = [tiger];\n  };\n  link married alice <-> bob;\n}\n
    • If ODs are used as specification technique, e.g. for tests or forbidden situations, a more expressive version of expressions can be used for values (e.g. by composing ODs with JavaExpressions). Furthermore, only interesting attributes need to be defined (underspecification) and conformity to a CD4A model can be checked.
    • The ODs differ from JSON structures, e.g., in the possibility to give the object a name as it is the case for tiger, or alice enabling the definition real graph structures.
    • Main grammars (directly usable):
      • OD4Data
      • OD4Development
      • OD4Report
    • Main grammar components:
      • DateLiterals
      • ODBasis
      • ODAttribute
      • ODLink
    • Detailed description
    "},{"location":"docs/Languages/#sequence-diagrams-monticore-stable","title":"Sequence Diagrams (MontiCore stable)","text":"
    • A textual sequence diagram (SD) language.
    • Detailed description
    • The project includes grammars, a symbol table infrastructure, a PrettyPrinter, and various CoCos for typechecking.
    • The language is divided into the two grammars SDBasis and SD4Development.
    • The grammar SDBasis is a component grammar providing basic SD language features.
    • The grammar SD4Development extends the grammar SDBasis with concepts used in UML/P SDs.
    • SD4Development supports modeling objects, method calls, returns, exception throws, dynamic object instantiation, various match modifiers for objects (free, initial, visible, complete), lifelines with activation regions, static method calls, intermediate variable declarations by using OCL, and conditions by using OCL.
    • The grammars can easily be extended by further interactions and object modifiers.
    • The following depicts a simple SD in its textual syntax.
      sequencediagram AuctionTest {\n  kupfer912: Auction;         // Interacting objects\n  bidPol: BiddingPolicy;\n  timePol: TimingPolicy;\n                              // Interaction sequence\n  kupfer912 -> bidPol  : validateBid(bid)\n  bidPol -> kupfer912  : return BiddingPolicy.OK;\n  kupfer912 -> timePol : newCurrentClosingTime(kupfer912, bid) \n  timePol -> kupfer912 : return t;\n  assert t.timeSec == bid.time.timeSec + extensionTime;\n}\n
    "},{"location":"docs/Languages/#si-units-monticore-stable","title":"SI Units (MontiCore Stable)","text":"
    • The international system of units (SI units) is a physical unit system widely used in the entire world. It is based on the basis units s, m, kg, A, K, mol, cd, provides a variety of derived units, and can be refined using prefixes such as m(milli), k(kilo), etc.
    • The SI Unit project aims to deliver SI units to MontiCore-based languages with expressions. It provides a grammar for all types of SI units and prefixes usable for type definition.
    • Second, it provides the SI Unit literals, such as 5 km as expression values and a language for SI unit types, such as km/h or km/h<long>. Some examples:
        km/h speed = 5 m / 27 s                         // variable definition using type km/h\n  speed = (3 * 4m  +  17km/h * 10h) / 3.5h        // values with SI unit types\n  \u00b0C/s<float> coolingSpeed;                       // types (\u00b0C/s) with precision (float)\n  g/mm^2<int> pressure; \n  Map<Location,\u00b0C> temperatures;                  // nesting of types \n
    • The SI unit literals integrate with MontiCore's expressions and the SI Unit types integrate with MontiCore's type system. The SI unit language remains fully type safe.
    • The math version uses km/h as idealistic full precision real number, while the computing version allows to contrain the precision with km/h<long>.
    • Main grammar components:
      • SI units
      • SI unit literals
      • SI unit types for math
      • SI unit types for computations
      • (other alternatives are possible; SI has not standardized anything here)
    • Example projects:
      • SI Java
    • detailed description
    "},{"location":"docs/Languages/#statecharts-monticore-stable","title":"Statecharts (MontiCore stable)","text":"
    • A set of language variants for Statecharts (UML-like or also embedded SysML-like).
    • It is possible to define syntactically simpler or more complex and comfortable forms of statecharts using a subset of the eleven provided language components. Two complete Statechart language variants are composed for direct usability.
    • A compact teaser for one variant of the Statechart languages:
      statechart Door {\n  state Opened\n  initial state Closed\n  state Locked\n\n  Opened -> Closed close() /\n  Closed -> Opened open(1) / {ringTheDoorBell();}\n  Closed -> Locked timeOut(n) / { lockDoor(); } [doorIsLocked]\n  Locked -> Closed [isAuthorized() && doorIsLocked] unlock() /\n}\n
    • This example models the different states of a door: Opened, Closed, and Locked. A transition is triggered e.g. by function/method call close() that changes a from a state Opened to state Closed.
    • Transitions can have actions, such as {ringDoorBell();} containing in this case Java statements, or preconditions, such as [ ... ] containing a Boolean expression.
    • State invariants and transition preconditions are defined using Expressions and entry/exit/transition actions are defined using Statements.
    • A Statechart may also have hierarchically decomposed states and other forms of events (not shown here).
    • Detailed description
    "},{"location":"docs/Languages/#sysml_2-alpha-intention-to-become-stable-not-publicly-available-links-are-private","title":"SysML_2 (Alpha: Intention to become stable) - not publicly available (links are private)","text":"
    • MontiCore languages for parsing artifacts of the SysML 2 language family. Examples:
      package 'Vehicles' {                      // a SysML block diagram\n  private import ScalarValues::*; \n  block Vehicle; \n  block Truck is Vehicle; \n  value type Torque is ISQ::TorqueValue; \n}\n
      package 'Coffee' {                      // a SysML activity diagram\n  activity BrewCoffee (in beans : CoffeeBeans, in, water : Water, out coffee : Coffee) { \n    bind grind::beans = beans;\n    action grind : Grind (in beans, out powder);\n    flow grind::powder to brew::powder;\n    bind brew::water = water;\n    action brew : Brew (in powder, in water, out coffee); \n    bind brew::coffee = coffee;\n  }\n}\n
    • The SysML 2 grammars adhere to the general upcoming SysML 2 specification (which is still under improvement currently).
    • Actually these grammars represents a slight superset to the official SysML 2 standard. It is intended for parsing SysML 2-compliant models. Well-formedness checks are kept to a minimum, because we assume to parse correctly produced SysML 2 models only.
    • MontiCore's SysML 2 is a language family that comes with a textual representation to describe SysML 2 diagrams with respect to the standard.
    • SysML 2 covers ADs, BDDs, IBDs, PackageDiagrams, ParametricDiagrams, RequirementDiagrams, SDs, SMDs, UseCaseDiagrams, and general SysMLBasics
    • Main grammars and detailed description
    "},{"location":"docs/Languages/#tagging-alpha-intention-to-become-stable-not-publicly-available-links-are-private","title":"Tagging (Alpha: Intention to become stable) - not publicly available (links are private)","text":"
    • Tags are known e.g. from the UML and SysML and mainly used to add extra information to a model element. Normally tags (and stereotypes) are inserted within the models, which over time pollutes the models, especially when different sets of tags are needed for different technical platforms.
    • MontiCore offers a solution that separates a model and its tags into distinct artifacts. Several independent tagging artifacts can be added without any need to adapt the core model. This allows fo reuse even of fixed library models.
    • The tagging artifacts are dependent on two factors:
    • First, tags can be added to named elements of the base model. It is of great help that we have an elegant symbol mechanism included in the MontiCore generator.
    • Second, the set of allowed tags can be constrained, by an explicit definition of allowed tag types and tag values and an explicit declaration on which kinds of symbols a tag may be attached to.
    • Consequently, tagging is not a single language, but a method to automatically and schematically derive languages:
      • A tagging schema language TSL (dependent on the available symbol types of the base grammar)
      • a tagging language TL (dependent on the tag schema models written in TSL)
    • Because tagging models can e.g. be used as configuration techniques in a code generator, appropriate infrastructure is generated as well.
    • Some tagging language examples
    • Although concrete languages (and their grammars) are themselves generated, there is a main grammar ocl.monticore.lang.Tagging, where the tagging language is derived from. See also detailed description
    "},{"location":"docs/Languages/#use-case-diagrams-monticore-stable","title":"Use Case Diagrams (MontiCore stable)","text":"
    • A textual use case diagram (UCD) language.
    • Detailed description
    • The project includes a grammar, a symbol table infrastructure, and a semantic differencing operator.
    • The language is defined by the grammar UCD.
    • It supports modeling actors, use cases, preconditions, associations between actors and use cases, extend relations between use cases with guards, include relations between use cases, and specialization relations between actors and use cases.
    • The grammars can easily be extended.
    • The following depicts a simple UCD in its textual syntax.
      usecasediagram Example {\n  @Player --\n    Play,\n    Pay,\n    ChangeProfilePicture;\n\n  @AndroidPlayer specializes Player;\n  @IOSPlayer specializes Player;\n\n  @Server --\n    ShowAd,\n    RegisterScore;\n\n  ShowAd extend Play [!isPremium];\n  RegisterScore extend Play;\n\n  abstract Pay include CheckPremium;\n  CreditCard specializes Pay;\n  Bank specializes Pay;\n  ChangeProfilePicture [isPremium];\n}\n
    "},{"location":"docs/Languages/#xml-monticore-stable","title":"XML (MontiCore Stable)","text":"
    • The MontiCore language for parsing XML artifacts. An example:
      <Calendar>\n  <Appointment name=\"lunch\">\n    <Date>24.04.2020</Date>\n    <Time>11:30</Time>\n    <Location>cafeteria</Location>\n  </Appointment>\n</Calendar>\n
    • The XML grammar adheres to the common XML standard and allows parsing arbitrary XML artifacts for further processing.
    • Actually the grammar represents a slight superset to the official XML standard. It is intended for parsing XML-compliant artifacts. Further well-formedness checks are not included, because we assume to parse correctly produced XML documents only.
    • Please note that XML (like JSON or ASCII) is mainly a carrier language. The concrete XML dialect and the question, how to recreate the real objects / data structures, etc. behind the XML structure is beyond this grammar, but can be applied to the AST defined here.
    • Main grammar de.monticore.lang.XML and detailed description
    "},{"location":"docs/Languages/#javalight-monticore-stable","title":"JavaLight (MontiCore Stable)","text":"
    • This is a reduced version of the Java language. JavaLight is meant to be used to integrate simplified Java-like parts in modeling languages but not to parse complete Java implementations.
    • It provides Java's attribute and method definitions, statements and expressions, but does not provide class or interface definitions and also no wildcards in the type system.
    • One main usage of JavaLight is in the Grammar-language to model e.g. Java methods. An example:
      public void print(String name) {\n  System.out.println(\"Hello \" + name);\n}\n
    • Main grammar de.monticore.JavaLight and detailed description.
    "},{"location":"docs/Languages/#java-beta-in-stabilization-not-publicly-available-links-are-private","title":"Java (Beta: In Stabilization) - not publicly available (links are private)","text":"
    • This is the full Java' Language (as Opposed to JavaLight).
    • Main Grammar JavaDSL and detailed description.
    "},{"location":"docs/Languages/#further-information","title":"Further Information","text":"
    • Project root: MontiCore @github
    • MontiCore documentation
    • List of languages
    • MontiCore Core Grammar Library
    • Best Practices
    • Publications about MBSE and MontiCore
    • License definition
    "},{"location":"docs/Publications/","title":"Publications","text":"

    Redirecting to https://www.se-rwth.de/research/MontiCore/

    "},{"location":"docs/further_docs/Impressum/","title":"Impressum","text":""},{"location":"docs/further_docs/Impressum/#impressum","title":"Impressum","text":"

    According to German law, each website needs to have an \"impressum\", declaring among other things who is responsible for the content. A few things are currently unclear:

    1. Is only github (in total) a website or each group withing github, or each project?
    2. What if the project is open source, do then all (German) contributors have to add an impressum?
    3. If the webpage is hosted outside Germany (like github), does German law then apply at all?

    Although a lot of things are unclear and the text below doesn't totally fit, we have opted to be on the safe side and add our Impressum below. Please note that this is an open source project and each contributor may change any artifact of the project -- even this text -- so we do not claim any responsibility or credits for the project, even so we are currently having the role of the curators.

    "},{"location":"docs/further_docs/Impressum/#herausgeberin","title":"Herausgeberin","text":"

    Herausgegeben im Auftrag des Rektors der Rheinisch-Westf\u00e4lischen Technischen Hochschule (RWTH) Aachen.

    RWTH Aachen \\ Templergraben 55

    52062 Aachen (Hausanschrift) \\ 52056 Aachen (Postanschrift)

    Telefon: +49 241 80 1 \\ Telefax: +49 241 80 92312

    Internet: www.rwth-aachen.de

    Die RWTH Aachen ist eine K\u00f6rperschaft des \u00f6ffentlichen Rechts. Sie wird durch den Dr. rer. nat. Dr. h. c. mult. Ulrich R\u00fcdiger vertreten.

    "},{"location":"docs/further_docs/Impressum/#zustandige-aufsichtsbehorde","title":"Zust\u00e4ndige Aufsichtsbeh\u00f6rde","text":"

    Ministerium f\u00fcr Kultur und Wissenschaft des Landes Nordrhein Westfalen, V\u00f6lklinger Stra\u00dfe 49, 40221 D\u00fcsseldorf.

    "},{"location":"docs/further_docs/Impressum/#umsatzsteuer-identifikationsnummer","title":"Umsatzsteuer-Identifikationsnummer","text":"

    Gem\u00e4\u00df Par. 27 a Umsatzsteuergesetz: DE 121689807

    "},{"location":"docs/further_docs/Impressum/#inhaltliche-verantwortlichkeit","title":"Inhaltliche Verantwortlichkeit","text":"

    Webmaster / Webredaktion: \\ Bernhard Rumpe \\ E-Mail: webmaster@se-rwth.de

    Die Webseiten der Zentralen Hochschulverwaltung, der Zentralen Einrichtungen, der Gruppenvertretungen, der Institute, Lehrst\u00fchle sowie Lehr- und Forschungsgebiete werden von den von diesen beauftragten Webkoordinatorinnen und Webkoordinatoren beziehungsweise deren Webmastern eigenst\u00e4ndig betreut. Details zu Fragen der Haftung sind in der Datenschutzerkl\u00e4rung nachzulesen.

    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/","title":"Overview","text":""},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#monticore-grammars-for-expressions-literals-and-types-an-overview","title":"MontiCore Grammars for Expressions, Literals and Types - an Overview","text":"

    MontiCore is a language workbench. It uses an extended grammar format as primary mechanism to describe DSLs. This format allows to compose language components by grammar (1) inheritance, (2) extension, (3) embedding and (4) aggregating. Please refer the MontiCore handbook for more details.

    MontiCore bundles a generator that can produce lots of infrastructure from MontiCore grammars. Like grammars, this infrastructure is composable. and can also be extended with handwrittten code. Most importantly, these extensions and the grammar composition are compatible which leads to optimal forms of reuse.

    This documentation presents a library of language components provided by the core of the MontiCore project together with short descriptions and status information (Status of Grammars). The components are mainly defined by a primary grammar plus associated Java and template files.

    The presented components are mainly based on the grammars in the MontiCore/monticore project. They are organized in the following packages under the monticore-grammar/src/main/grammars/ folder hierarchy:

    • de.monticore
    • de.monticore.expressions
    • de.monticore.literals
    • de.monticore.statements
    • de.monticore.symbols
    • de.monticore.types

    Additionally, the documentation presents some expression/type related language components in projects that extend MontiCore's core languages. For more languages and language components, follow this link.

    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#general-list-of-grammars-in-package-demonticore","title":"General: List of Grammars in package de.monticore","text":""},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#mcbasicsmc4-stable","title":"MCBasics.mc4 (stable)","text":"
    • This grammar defines basic rules for naming, spacing, and Java-like comments being useful in many languages.
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#types-list-of-grammars-in-package-demonticoretypes","title":"Types: List of Grammars in package de.monticore.types","text":"

    These grammars generally deal with type definitions and build on each other. Some snipets for type definitions:

    grammars          some examples\nMCBasicTypes      boolean  byte  short  int\n                  long  char  float  double\n                  void  Person  a.b.Person\n                  import a.b.Foo.*;\nMCCollectionTypes List<.>   Set<.>\n                  Optional<.>   Map<.,.>\nMCSimpleGenericTypes\n                  Foo<.>  a.b.Bar<.,..,.>\nMCFullGenericTypes\n                  Foo<? extends .>\n                  Foo<? super .>\nMCArrayTypes      Person[]  int[][]\nSI Unit types     km/h  km/h<long>\nRegExType         R\"[a-z][0-9*]\"\n
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#mcbasictypesmc4-stable","title":"MCBasicTypes.mc4 (stable)","text":"
    • This grammar defines basic types and thus eases the reuse of type structures in languages similar to Java. However, the type definitions in the grammar are somewhat simplified, e.g., they do not comprise generics.
    • The grammar contains types from Java, e.g., primitives, void, and classes (sometimes referred to as \"reference types\").
    • Example type definitions: boolean, byte, short, int, long, char, float, double, void, Person, a.b.Person, import a.b.Foo.*;.
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#mccollectiontypesmc4-stable","title":"MCCollectionTypes.mc4 (stable)","text":"
    • This grammar defines four generic types: List<A>, Map<A,B>, Set<A> and Optional<A> on top of basic types.
    • These four generic types correspond to a typical predefined set of generic types used, e.g., in connection with UML class diagrams or the OCL. UML associations typically have those association multiplicities which is why the generic types are of general interest.
    • The grammar eases the reuse of type structures in languages similar to Java that are somewhat simplified, e.g., by omitting general generics.
    • Example type definitions: List<.>, Set<.>, Optional<.>, Map<.,.>.
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#mcsimplegenerictypesmc4-stable","title":"MCSimpleGenericTypes.mc4 (stable)","text":"
    • This grammar provides rules for the definition of custom generic types such as Blubb<A>, Bla<B,C>, Foo<Blubb<D>>.
    • The grammar covers a wide range of uses for generic types. Unlike Java, it does however not cover type restrictions on arguments.
    • Example type definitions: Foo<.>, a.b.Bar<.,..,.>.
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#mcfullgenerictypesmc4-stable","title":"MCFullGenericTypes.mc4 (stable)","text":"
    • This grammar completes type definitions to support the full Java type system including wildcards on generic types like Blubb<? extends A>.
    • A general advice: When you are not sure that you need this kind of types, use a simpler version from above. Type checking is tricky.
    • Example type definitions: Foo<? extends .>, Foo<? super .>.
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#mcarraytypesmc4-stable","title":"MCArrayTypes.mc4 (stable)","text":"
    • The grammar provides means to define arrays.
    • Arrays are orthogonal to the generic extensions and may thus be combined with any of the above grammars.
    • Example type definitions: Person[], int[][].
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#siunittypes4mathmc4-for-physical-si-units-stable","title":"SIUnitTypes4Math.mc4 for Physical SI Units (stable)","text":"

    The known units s, m, kg, A, K, mol, cd from the international system of units (SI Units) and their combinations, such as km/h or mg, etc. can be used as ordinary types (instead of only numbers). The typecheck is extended to prevent e.g. assignment of a weight to a length variable or to add appropriate conversion, e.g. when a km/h-based velocity is e.g. stored in a m/s-based variable.

    The grammar resides in the MontiCore/SIunits project.

    • Example type definitions: km/h
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#siunittypes4computingmc4-for-physical-si-units-stable","title":"SIUnitTypes4Computing.mc4 for Physical SI Units (stable)","text":"

    Includes the types from SIUnitTypes4Math(see above), like km/h, but also allows to add a resolution, such as km/h<int>. Here SI Unit types, like km/h<.>, are used as generic type constructor that may take a number type, such as int, long, double, float as argument.

    The grammar resides in the MontiCore/SIunits project.

    • Example type definitions: km/h<long>
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#regextypemc4-stable","title":"RegExType.mc4 (stable)","text":"

    Embedded in R\"...\" a regular expressions can be used as ordinary type to constrain the values allowed for stored variables, attributes, parameters. Types are e.g. , such as R\"[a-z]\" (single character) or R\"^([01][0-9]|2[0-3])$\" (hours). A typecheck for these types can only be executed at runtime and e.g. issue exceptions (or trigger repair functions) if violated. The static typecheck only uses String as underlying carrier type.

    This grammar resides in the MontiCore/RegEx project.

    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#symbols-list-of-grammars-in-package-demonticoresymbols","title":"Symbols: List of Grammars in package de.monticore.symbols","text":"

    These two grammars do not provide syntax themselves, but characterize important forms of symbols, that will be used in the type and the expression grammars to define shared kinds of symbols.

    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#basicsymbolsmc4-stable","title":"BasicSymbols.mc4 (stable)","text":"
    • This grammar defines symbols for Types (of all kinds), Functions, Variables and TypeVariables.
    • The defined symbols are of general form and can be used in functional, OO and other contexts. They do not preculde a concrete syntax and do not yet embody OO specifics.
    • Remark: This grammar is not intended to define concrete or abstract syntax, but the infrastructure for symbols.
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#oosymbolsmc4-stable","title":"OOSymbols.mc4 (stable)","text":"
    • This grammar defines symbols for objectoriented Types, Methods, and Fields by mainly extending the symbols defined in BasicTypeSymbols.
    • The newly defined symbols extend the general ones by typical objectoriented features, such as private, static, etc. Again they do not preculde a concrete syntax.
    • Remark: This grammar is not intended to define concrete or abstract syntax, but the infrastructure for symbols in objectoriented context.
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#expressions-list-of-grammars-in-package-demonticoreexpressions","title":"Expressions: List of Grammars in package de.monticore.expressions","text":"

    Expressions are defined in several grammars forming a (nonlinear) hierarchy, so that developers can choose the optimal grammar they want to build on for their language and combine these with the appropriate typing infrastructure.

    This modularity of expressions and associated types greatly eases the reuse of type structures in languages similar to Java. Some snipets for operators defined in expressions:

    grammar        operators and examples in this grammar\nCommonExp:     /  %  +  -  <=  >=  ==  >  <  !=  ~.  !.  .?.:.\n               &&  ||  ~. \nAssigementExp: ++  --  =  +=  -=  *=  /=  &=  |=  ^=  >>=  >>>=  <<=  %=\nBitExp:        &  |  ^  <<  >>  >>>\nOclExp:        implies  <=>  |  &  forall  exists  let.in. .@pre  .[.]  .**\n               Set{.|.}\nSetExp:        .isin.  .in.  union  intersect  setand  setor\n               { item | specifier }\nOptionalOps:   ?:  ?<=  ?>=  ?<  ?>  ?==  ?!=  ?~~   ?!~ \nLambdaExp:     i->i   (a,b)->a+b\nSIUnits:       5km  3,2m/s  22l  2.400J  \nJavaClass:     this  .[.]  (.).  super  .instanceof.\n
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#expressionsbasismc4-stable","title":"ExpressionsBasis.mc4 (stable)","text":"
    • This grammar defines core interfaces for expressions and imports the kinds of symbols necessary.
    • The symbols are taken over from the TypeSymbols grammar (see below).
    • A hierarchy of conservative extensions to this grammar realize these interfaces in various forms.
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#commonexpressionsmc4-stable","title":"CommonExpressions.mc4 (stable)","text":"
    • This grammar defines a typical standard set of operations for expressions.
    • This is a subset of Java as well as OCL/P, mainly for arithmetic, comparisons, variable use (v), attribute use (o.att), method call (foo(arg,arg2)) and brackets (exp).
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#assignmentexpressionsmc4-stable","title":"AssignmentExpressions.mc4 (stable)","text":"
    • This grammar defines all Java expressions that have side effects.
    • This includes assignment expressions like =, +=, etc. and suffix and prefix expressions like ++, --, etc.
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#bitexpressionsmc4-stable","title":"BitExpressions.mc4 (stable)","text":"
    • This grammar defines a typical standard set of operations for expressions.
    • This is a subset of Java for binary expressions like <<, >>, >>>, &, ^ and |
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#oclexpressionsmc4-stable","title":"OCLExpressions.mc4 (stable)","text":"
    • This grammar defines expressions typical to UMLs OCL . OCL expressions can savely be composed if with other forms of expressions given in the MontiCore core project (i.e. as conservative extension).
    • It contains various logical operations, such as quantifiers, the let and the @pre construct, and a transitive closure for associations, as discussed in [Rum17,Rum17].
    • This grammar resides in the MontiCore/OCL project.
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#setexpressionsmc4-stable","title":"SetExpressions.mc4 (stable)","text":"
    • This grammar defines set expressions like set union, intersection etc. these operations are typical for a logic with set operations, like UML's OCL. These operators are usually infix and are thus more intuitive as they allow math oriented style of specification.
    • Most of these operators are in principle executable, so it might be interesting to include them in a high level programming language (see e.g. Haskell)
    • This grammar resides in the MontiCore/OCL project.
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#optionaloperatorsmc4-stable","title":"OptionalOperators.mc4 (stable)","text":"
    • This grammar defines nine operators dealing with optional values, e.g. defined by java.lang.Optional. The operators are also called Elvis operators.
    • E.g.: val ?: 42 equals to val.isPresent() ? val.get() : 42
    • x ?>= y equals x.isPresent() ? x.get() >= y : false
    • This grammar resides in the MontiCore/OCL project.
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#lambdaexpressionsmc4-beta","title":"LambdaExpressions.mc4 (beta)","text":"
    • This grammar defines lambda expressions. Lambda expression define anonymous functions that can be passed around as values e.g. to higher-order functions and that can be evaluated on arguments of appropriate types.
    • Lambda expressions are fully typed (see e.g. in Haskell) and can be nested.
    • This is only the subset of Java's lambda expressions that allows to define a functional programming style (thus preventing side effects).
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#siunitsmc4-for-physical-si-units-stable","title":"SIUnits.mc4 for Physical SI Units (stable)","text":"
    • This grammar the international system of units (SI units), based on the basis units s, m, kg, A, K, mol, cd, provides a variety of derived units, and can be refined using prefixes such as m(milli), k(kilo), etc.
    • The SI Unit grammar provides an extension to expressions, but also to the typing system, e.g. types such as km/h or km/h<long>, and literals, such as e.g. 5.3 km/h.
    • The grammars reside in the MontiCore/SIunits project
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#javaclassexpressionsmc4-stable","title":"JavaClassExpressions.mc4 (stable)","text":"
    • This grammar defines Java specific class expressions like super, this, type cast, etc.
    • This grammar should only be included, when a mapping to Java is intended and the full power of Java should be available in the modeling language.
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#literals-list-of-grammars-in-package-demonticoreliterals","title":"Literals: List of Grammars in package de.monticore.literals","text":"

    Literals are the basic elements of expressions, such as numbers, strings, truth values. Some snipets:

    grammar           examples of this grammar\nMCCommonLit       3  -3  2.17  -4  true  false  'c' \n                  3L  2.17d  2.17f  0xAF  \"string\"  \n                  \"str\\uAF01\\u0001\"  null\nMCJavaLiterals    999_999  0x3F2A  0b0001_0101  0567  1.2e-7F\nSIUnitLiterals    5.3km/h  7mg\n
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#mcliteralsbasismc4-stable","title":"MCLiteralsBasis.mc4 (stable)","text":"
    • This grammar defines core interface for literals.
    • Several conservative extensions to this grammar realize various forms of literals.
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#mccommonliteralsmc4-stable","title":"MCCommonLiterals.mc4 (stable)","text":"
    • This grammar defines the typical literals for an expression language, such as characters: 'c', Strings \"text\", booleans: \"true\", \"null\", or numbers 10, -23, 48l, 23.1f.
    • Strings and characters use the Java-like escapes like \"\\n\".
    • Each defined nonterminal is extended by a conversion function getValue() of appropriate type and a retrieve function getSource() for a text representation of the literal.
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#mcjavaliteralsmc4-stable","title":"MCJavaLiterals.mc4 (stable)","text":"
    • This grammar defines Java compliant literals and builds on MCCommonLiterals.
    • The scope of this grammar is to ease the reuse of literals structures in Java-like sublanguages.
    • The grammar contains literals from Java, e.g., Boolean, Char, String, ....
    • Please note that Java (and this grammar) has an extended syntax e.g. for integers using underscores or other kinds of encodings. They parse e.g. 999_999, 0x3F2A, or 0b10100.
    • Like above getValue() and getSource() allow to retrive the content as value resp. as text string.
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#siunitliteralsmc4-for-physical-si-units-stable","title":"SIUnitLiterals.mc4 for Physical SI Units (stable)","text":"

    Provides concrete values, such as 5.3 km/hor 7 mg for the international system of units (SI Units). The grammar resides in the MontiCore/SIunits project.

    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#statements-list-of-grammars-in-package-demonticorestatements","title":"Statements: List of Grammars in package de.monticore.statements","text":"

    Statements are the constructive part of programs: They allow to change variables, call functions, send messages etc. The following hierarchy of statement definitions should allow the developers to choose needed forms of statements and extend it by their own additional needs. The provided list of statements is inspired by Java (actually subset of Java). Some example statements:

    int i;   int j = 2;                     Person p[] = { foo(3+7), p2, ...}\nif (.) then . else .                    for ( i = .; .; .) {.}\nwhile (.) .                             do . while (.)\nswitch (.) { case .: .; default: .}\nfoo(1,2,3)                              return .                                \nassert . : \"...\"\ntry {.} catch (.) {.} finally {.}       throw .           \nbreak .                                 continue .\nlabel:                                  private  static  final  native ...\n
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#mcstatementsbasismc4-stable","title":"MCStatementsBasis.mc4 (stable)","text":"
    • This grammar defines the core interface for statements.
    • A hierarchy of conservative extensions to this grammar is provided below.
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#mccommonstatementsmc4-stable","title":"MCCommonStatements.mc4 (stable)","text":"
    • This grammar defines typical statements, such as method calls (which are actually expressions), assignment of variables, if, for, while, switch statements, and blocks.
    • This embodies a complete structured statement language, however does not provide return, assert, exceptions, and low-level constructs like break.
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#mcassertstatementsmc4-stable","title":"MCAssertStatements.mc4 (stable)","text":"
    • This grammar defines exactly the assert statement as known from Java.
    • It can be used independently of other Java statements.
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#mcexceptionstatementsmc4-stable","title":"MCExceptionStatements.mc4 (stable)","text":"
    • This grammar defines the exception statements.
    • This includes Java try with catch and finally, as well as throw.
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#mcsynchronizedstatementsmc4-stable","title":"MCSynchronizedStatements.mc4 (stable)","text":"
    • This grammar defines the Java-like synchronized statement.
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#mclowlevelstatementsmc4-stable","title":"MCLowLevelStatements.mc4 (stable)","text":"
    • This grammar defines three low-level statements that Java provides.
    • It contains the break and continue statements and the possibility to label a statement.
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#mcreturnstatementsmc4-stable","title":"MCReturnStatements.mc4 (stable)","text":"
    • This grammar defines the Java-like return statement.
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#mcfulljavastatementsmc4-stable","title":"MCFullJavaStatements.mc4 (stable)","text":"
    • This grammar defines all Java statements.
    • This is neither a generalized approximation nor a restricted overapproximation, but exact.
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#further-grammars-in-package-demonticore","title":"Further grammars in package de.monticore","text":"

    several other grammars are also available:

    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#regularexpressionsmc4-stable","title":"RegularExpressions.mc4 (stable)","text":"
    • This grammar defines regular expressions (RegEx) as used in Java (see e.g. java.util.regex.Pattern).
    • It provides common regex tokens such as
    • character classes, e.g., lowercase letters ([a-z]), the letters a, b, and c ([abc])
    • anchors, e.g., start of line (^), end of line ($), word boundary (\\b),
    • quantifiers, e.g., zero or one (?), zero or more (*), exactly 3 ({3}),
    • RegEx also supports to capture groups and referencing these captured groups in replacements.
    • For example, ^([01][0-9]|2[0-3]):[0-5][0-9]$ matches all valid timestamps in HH:MM format.
    • The main nonterminal RegularExpression is not part of the expression hierarchy and thus regular expressions are not used as ordinary values. Instead the nonterminal RegularExpression is can be used in aother places of a language e.g. we do that as additional restriction for String values in input/output channels in architectural langages.
    • This grammar resides in the MontiCore/RegEx project
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#cardinalitymc4-stable","title":"Cardinality.mc4 (stable)","text":"
    • This grammar defines UML Cardinalities of forms *, [n..m] or [n..*].
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#completenessmc4-stable","title":"Completeness.mc4 (stable)","text":"
    • This grammar defines completeness information in UML like ..., (c), but also (...,c).
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#umlmodifiermc4-stable","title":"UMLModifier.mc4 (stable)","text":"
    • The grammar contains the modifiers that UML provides.
    • This includes public private, protected, final, abstract, local, derived, readonly, and static, but also the compact syntactic versions +, #, -, / and ? (for readonly).
    • UML modifiers are not identical to Java modifiers (e.g. native or threadsafe are missing.)
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#umlstereotypemc4-stable","title":"UMLStereotype.mc4 (stable)","text":"
    • This grammars defines Stereotypes like <<val1,val2=\"text\",...>>
    • Methods contains(name), getValue(name) assist Stereotype retrieval.
    • Values may only be of type String. The real value unfortunately in UML is only encoded as String.
    • We suggest to use a tagging infrastructure that even allows to type the possible forms of tags.
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#mccommonmc4-stable","title":"MCCommon.mc4 (stable)","text":"
    • This grammar composes typical UML-like grammar components.
    • This includes Cardinality, Completeness, UMLModifier, and UMLStereotype.
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#javalightmc4-stable","title":"JavaLight.mc4 (stable)","text":"
    int age = 3+x; \nList<Person> myParents;\n\n@Override\npublic int print(String name, Set<Person> p) {\n  int a = 2 + name.length();\n  if(a < p.size()) {\n    System.out.println(\"Hello \" + name);\n  }\n  return a;\n}\n
    • JavaLight is a subset of Java that MontiCore itself uses as intermediate language for the code generation process.
    • JavaLight doesn't provide all forms of classes (e.g., inner classes) and reduces the type system to normal generic types. However, that is sufficient for all code generated by MontiCore.
    • JavaLight supports Java expressions (including anonymous classes), Java statements as relevant to MontiCore code generation, method declaration, constructors, constants, interface methods, and annotations.
    • JavaLight composes the grammars CommonExpressions, AssignmentExpressions, JavaClassExpressions, MCCommonStatements, MCBasicTypes, and OOSymbols.
    • JavaLight can be used for other generator tools than MontiCore, especially as its core templates are reusable and new templates for customized method bodies can be added using MontiCore's Hook-Mechanisms.
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#monticore-example-grammars-for-the-interested-reader","title":"MontiCore Example Grammars for the Interested Reader","text":"

    The monticore-grammar/src/main/examples folder hosts the following example grammars:

    • StringLiterals.mc4
    • MCHexNumbers.mc4
    • MCNumbers.mc4
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/Grammars/#further-information","title":"Further Information","text":"
    • Project root: MontiCore @github
    • MontiCore documentation
    • List of languages
    • MontiCore Core Grammar Library
    • Best Practices
    • Publications about MBSE and MontiCore
    • Licence definition
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/JavaLight/","title":"JavaLight","text":""},{"location":"monticore-grammar/src/main/grammars/de/monticore/JavaLight/#javalight","title":"JavaLight","text":"

    The JavaLight language defines a subset of the Java programming language. The JavaLight language is dedicated for embedding Java language elementsin arbitrary DSLs. It is therfore defined in a compositional style, i.e. for black box reuse (without need for copy-paste).

    The JavaLight language introduces * all forms of attribute declarations, * all forms of method declarations (including constructors), * all forms of Java expressions (including those with side effects, such as i++, but without anonymous classes), * almost all Java statements, with the exception of statements for exception handling, continue- and break-statement, and synchronization, which are omitted because there are many DSLs, where these are of no use; * and it allows to import usable types, method, and attribute symbols, which may be predefined or imported from of Java-like models.

    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/JavaLight/#example","title":"Example","text":"

    int age = 3+x; \nList<Person> myParents;\n\n@Override\npublic int print(String name, Set<Person> p) {\n  int a = 2 + name.length();\n  if(a < p.size()) {\n    System.out.println(\"Hello \" + name);\n  }\n  return a;\n}\n
    The example shows a Java method with one parameter and three statements. Expressions are supported in all their forms.

    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/JavaLight/#grammar","title":"Grammar","text":"
    • The main grammar file is de.monticore.JavaLight. It deals with the definition of the method and constructor signatures, and the annotations, while it reuses MontiCore's library components AssignmentExpressions, JavaClassExpressions, MCCommonStatements, and MCArrayStatements for expressions and statements.
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/JavaLight/#extension-of-javalight","title":"Extension of JavaLight","text":"

    JavaLight is designed for easy black-box reuse. It can be extended by domain specific constructs, such as 1. special statements for message processing (c?x; c!3+x), 2. statements for testing such as Hoare-like asserts or pre/postconditions, or 3. additional logical or otherwise interesting expression operators (forall x in Set:). 4. The omitted Java-special statements, such as eception handling, can also be addded through a predefined language library component.

    JavaLight is fully compatible with various expression language components that MontiCore's library provides. These extensions can simply be added by MontiCore's language composition mechanism (see Handbook).

    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/JavaLight/#embedding-of-javalight-expression-or-statement","title":"Embedding of JavaLight Expression or Statement","text":"

    JavaLight's expressions can be embedded as expression sublanguage in any other interesting domain specific language, such as Automata, Activity Diagrams, etc. (even MontiCore's primary language uses this). The statements can also be embedded as sublanguage e.g. as actions in StateCharts.

    JavaLight is a strict subset of the Java programming language and thus can be mapped directly to itself when generating code for Java.

    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/JavaLight/#parser","title":"Parser","text":"
    • JavaLight is a component grammar. To retrieve a parser it is to be embedded into a full grammar.
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/JavaLight/#symboltable-and-symbol-classes","title":"Symboltable and Symbol classes","text":"
    • JavaLight introduces the JavaMethodSymbol extending the existing MethodSymbol for general object-oriented types. The JavaMethodSymbol class carries the additional attributes:
    • annotations,
    • exceptions,
    • and Booleans for isEllipsisParameterMethod, isFinal, isAbstract, isSynchronized, isNative, and isStrictfp.
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/JavaLight/#symbols-imported-and-exported","title":"Symbols (imported and exported)","text":"
    • Import: the following symbols can be imported from outside, when the symbol table in the embedding language provides these symbols:
    • VariableSymbol for attributes and otherwise accessible variables.
    • MethodSymbol for method and constructor calls (this includes also JavaMethodSymbol).
    • TypeSymbols for using types, e.g., defined via classes, interfaces, and enums.
    • Symbol definition and export: It is possible to define new symbols, for attributes, methods, and constructors. The provided symbol table will include them as
    • VariableSymbol for attributes
    • MethodSymbol for methods and constructors and thus will make the accessibility of these symbols available outside the JavaLight sub-models.
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/JavaLight/#functionality","title":"Functionality","text":"

    As usual functionality is implemented in a compositional form, which means that in a composed language these functions should be largely reusable as pre-compiled black-boxes.

    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/JavaLight/#context-conditions","title":"Context Conditions","text":"

    JavaLight defines the following CoCos: * ConstructorFormalParametersDifferentName checks if all input parameters of a constructor have distinct names. * ConstructorModifiersValid checks that a constructor is not marked as abstract, final, static, or native. * ConstructorNoAccessModifierPair checks that no duplicate visibility occurs for a constructor. * ConstructorNoDuplicateModifier checks that no duplicate modifier occurs for a constructor. * MethodAbstractAndOtherModifiers checks that an abstract method is not marked as private, static, final, native, or synchronized. * MethodBodyAbsence ensures that a method body may only be absent for abstract or native methods. * MethodBodyPresence checks that a method with a present method body is neither abstract nor native. * MethodExceptionThrows ensures that thrown exceptions are of type java.lang.Throwable. * MethodFormalParametersDifferentName checks if all input parameters of a method have distinct names. * MethodNoDuplicateModifier checks that no duplicate modifier occurs for a method. * MethodNoNativeAndStrictfp checks that method is not marked as native and strictfp.

    The CoCos of embedded languages, such as statements and expressions are defined there and reused as black-boxes.

    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/JavaLight/#prettyprinter","title":"PrettyPrinter","text":"
    • The basic pretty printer for JavaLight is de.monticore.prettyprint.JavaLightPrettyPrinter

    • When the expression language is used as high-level language, it might make sense to map attribute access to get-functions respectively also use set-functions for modification. This can be done using a more elaborated pretty printer.

    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/JavaLight/#further-information","title":"Further Information","text":"
    • Project root: MontiCore @github
    • MontiCore documentation
    • List of languages
    • MontiCore Core Grammar Library
    • Best Practices
    • Publications about MBSE and MontiCore
    • License definition
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/expressions/Expressions/","title":"Expressions","text":""},{"location":"monticore-grammar/src/main/grammars/de/monticore/expressions/Expressions/#monticore-expression-language-modules","title":"MontiCore - Expression-Language Modules","text":"

    MC Expressions can be used to formulate mathematical and programmatic expressions from a set of literals. MC Expressions are based on a system of modular and pluggable grammar parts.

    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/expressions/Expressions/#given-expression-languages-in-monticore","title":"Given Expression languages in MontiCore","text":"

    Currently, MontiCore comprises the following expression languages:

    • ExpressionsBasis: Basis for all other expression languages. Supports names and literals.
    • AssignmentExpressions: Extends ExpressionsBasis with basic assignments.
    • CommonExpressions: Extends ExpressionsBasis with common expressions like + and -.
    • BitExpressions: Extends ExpressionsBasis with bit expressions like & or <<.
    • LambdaExpressions: Extends ExpressionBasis with lambda expressions like a -> a + 2.
    • JavaClassExpressions: Extends CommonExpressions with Java expressions like new.

    The OCL project defines additional expression languages:

    • OCL-OCLExpressions: Extends ExpressionsBasis to introduce OCL to MontiCore.
    • OCL-SetExpressions: Extends ExpressionsBasis for working with sets using OCL.

    Furthermore, composite SI unit expressions are defined in the SI Units project:

    • SI Units (can be used to parse primitive units as well as their products, quotients, and powers)
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/expressions/Expressions/#using-expressions","title":"Using Expressions","text":"

    To use one or more of the existing expression languages in your MontiCore-based language its grammar needs to extend those expression languages.

    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/expressions/Expressions/#creating-your-own-expression-language","title":"Creating your own Expression language","text":"

    There are some expressions you need desperately and that are not covered by the existing expression languages? In this case, you can create a new grammar that extends at least ExpressionsBasis. In the extending grammar, you are now free to add your own expressions which however must implement the Expression interface from ExpressionsBasis grammar. To then include the new expressions in a language let it extend the corresponding grammar. See here for an example.

    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/expressions/Expressions/#further-information","title":"Further Information","text":"
    • Project root: MontiCore @github
    • MontiCore documentation
    • List of languages
    • MontiCore Core Grammar Library
    • Best Practices
    • Publications about MBSE and MontiCore
    • Licence definition
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/literals/Literals/","title":"Literals","text":""},{"location":"monticore-grammar/src/main/grammars/de/monticore/literals/Literals/#monticore-literals","title":"MontiCore - Literals","text":"

    Literals are the basis to parse numbers, strings and other atomic language elements. The language module MCLiteralBasis defines the root nonterminal Literal but no other terminals representing literal terms. MCCommonLiterals and MCJavaLiterals define concrete literal terms that can be included in a MontiCore-based language as desired.

    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/literals/Literals/#grammar-mccommonliteralsmc4","title":"Grammar MCCommonLiterals.mc4","text":"

    This grammar includes the following parser rules:

    • NullLiteral: Recognizes null.
    • BooleanLiteral: Recognizes true and false.
    • CharLiteral: Recognizes a, ... , Z.
    • StringLiteral: Recognizes \"...\".
    • NatLiteral: Recognizes literals like 123.
    • SignedNatLiteral: Recognizes literals like -13.
    • BasicLongLiteral: Recognizes literals like 6L and 6l.
    • SignedBasicLongLiteral: Recognizes literals like -6L, -6l, 6L?, and 6l.
    • BasicFloatLiteral: Recognizes literals like 1.2F and 1.2f.
    • SignedBasicFloatLiteral: Recognizes literals like -1.2F, -1.2f, 1.2F, and 1.2f.
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/literals/Literals/#grammar-mcjavaliteralsmc4","title":"Grammar MCJavaLiterals.mc4","text":"

    This grammar extends MCCommonLiterals and includes the following parser rules:

    • IntLiteral: Recognizes literals like 123, 0734, 1001001, and 0x1a.
    • LongLiteral: Recognizes literals like 2L, 0734l, 1001001L, and 0x1al.
    • FloatLiteral: Recognizes literals like 1.23F and 1.23E4f.
    • DoubleLiteral: Recognizes literals like 1.23, 1.23d, 1.23E4D.
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/literals/Literals/#further-information","title":"Further Information","text":"
    • Project root: MontiCore @github
    • MontiCore documentation
    • List of languages
    • MontiCore Core Grammar Library
    • Best Practices
    • Publications about MBSE and MontiCore
    • Licence definition
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/types/Types/","title":"Types","text":""},{"location":"monticore-grammar/src/main/grammars/de/monticore/types/Types/#monticore-types","title":"MontiCore - Types","text":"

    Type systems are available in a variety of (programming) languages and facilitate programming because they allow for detecting typing errors already at compile time. To express type usages in MontiCore-based languages a language component hierarchy for type modeling was developed. The hierarchy consists of the following language components:

    • MCBasicTypes
    • MCCollectionTypes
    • MCSimpleGenericTypes
    • MCFullGenericTypes
    • MCArrayTypes
    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/types/Types/#mcbasictypes","title":"MCBasicTypes","text":"

    MCBasicTypes is the most basic language component. It provides the central interface nonterminal MCType. Additionally, it defines nonterminals that enable modeling primitive types as well as qualified and non-qualified types. Furthermore, the component comprises a rule that covers return types which can be MCTypes or voids. In general, the component represents a relativity small, yet useful, collection of rules for type modeling that supports statements such as int, Person, and java.lang.String.

    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/types/Types/#mccollectiontypes","title":"MCCollectionTypes","text":"

    This language component builds upon MCBasicTypes and enables to model four kinds of generics:

    • Set
    • List
    • Map
    • Optional

    These generics cannot be nested as the purpose of the MCCollectionTypes language component is the provisioning of some commonly used collection types whose functionality is limited to support the construction of high-level models. With the language component types such as List<Integer>, Set<char>, or Map<java.lang.String, Person> become expressible.

    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/types/Types/#mcsimplegenerictypes","title":"MCSimpleGenericTypes","text":"

    This language component extends the MCCollectionTypes component to allow the expression of types with custom generics of arbitrary classes with arbitrary arguments. When using the component, types such as Person<String> or Map<Person<String>, Integer> are expressible. Please note that these types still do not cover all possible Java types as Java additionally supports inner types of generic types. Specifically, types such as a.b.C<D>.E.F<G>.H are not expressible by MCSimpleGenericTypes.

    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/types/Types/#mcfullgenerictypes","title":"MCFullGenericTypes","text":"

    This language component extends the MCSimpleGenericTypes component to allow the expression of inner generic types of arbitrary classes with arbitrary arguments including wild card types. When using this language component, types such as Person<?>, Map<Person<String>, ? extends Person>, and a.b.C<D>.E.F<G>.H are expressible.

    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/types/Types/#mcarraytypes","title":"MCArrayTypes","text":"

    This language component provides rules to express array types like Person[] or int[][]. As array types are orthogonal to generic types, MCArrayTypes can be combined with any of the above language components.

    "},{"location":"monticore-grammar/src/main/grammars/de/monticore/types/Types/#further-information","title":"Further Information","text":"
    • Project root: MontiCore @github
    • MontiCore documentation
    • List of languages
    • MontiCore Core Grammar Library
    • Best Practices
    • Publications about MBSE and MontiCore
    • Licence definition
    "},{"location":"monticore-grammar/src/main/java/de/monticore/types/check/TypeCheck/","title":"TypeCheck","text":"

    In MontiCore, the TypeCheck is used to calculate the SymTypeExpression of a set of expressions, types and literals. This is made possible by traversing the AST of an expression, type or literal, calculating the SymTypeExpression of its subexpressions, -types or -literals and combining them to the SymTypeExpression of the main expression, type or literal.

    "},{"location":"monticore-grammar/src/main/java/de/monticore/types/check/TypeCheck/#given-infrastructure-in-monticore","title":"Given infrastructure in MontiCore","text":"
    • TypeCheck (facade for using the TypeCheck)
    • DeriveSymTypeOfExpression (calculate a SymTypeExpression for the expressions in the grammar ExpressionsBasis)
    • DeriveSymTypeOfCommonExpressions (calculate a SymTypeExpression for the expressions in the grammar CommonExpressions)
    • DeriveSymTypeOfAssignmentExpressions (calculate a SymTypeExpression for the expressions in the grammar AssignmentExpressions)
    • DeriveSymTypeOfBitExpressions (calculate a SymTypeExpression for the expressions in the grammar BitExpressions)
    • DeriveSymTypeOfLiterals (calculate a SymTypeExpression for the literals in the grammar LiteralsBasis)
    • DeriveSymTypeOfMCCommonLiterals (calculate a SymTypeExpression for the literals in the grammar MCCommonLiterals)
    • DeriveSymTypeOfMCJavaLiterals (calculate a SymTypeExpression for the literals in the grammar MCJavaLiterals)
    • SynthesizeSymTypeFromMCBasicTypes (calculate a SymTypeExpression for the types in the grammar MCBasicTypes)
    • SynthesizeSymTypeFromMCCollectionTypes (calculate a SymTypeExpression for the types in the grammar MCCollectionTypes)
    • SynthesizeSymTypeFromMCSimpleGenericTypes (calculate a SymTypeExpression for the types in the grammar MCSimpleGenericTypes)
    • SynthesizeSymTypeFromMCFullGenericTypes (calculate a SymTypeExpression for the types in the grammar MCFullGenericTypes)
    • BasicSymbols (defines the symbols needed for the symboltable)
    • OOSymbols (specialization of the BasicSymbols for object-oriented languages)
    • SymTypeExpression (result of the TypeCheck, represents type usage)
    • SymTypeArray (subclass of SymTypeExpression, represents arrays)
    • SymTypePrimitive (subclass of SymTypeExpression, represents primitive types)
    • SymTypeOfGenerics (subclass of SymTypeExpression, represents generic types)
    • SymTypeOfObject (subclass of SymTypeExpression, represents non-primitive types without type arguments)
    • SymTypeVariable (subclass of SymTypeExpression, represents type variables)
    • SymTypeOfNull (subclass of SymTypeExpression, represents the null type)
    • SymTypeVoid (subclass of SymTypeExpression, represents the void type)
    • SymTypeOfWildcard (subclass of SymTypeExpression, represents wildcard types)
    • SymTypeExpressionFactory (factory for creating the subclasses of SymTypeExpression)
    "},{"location":"monticore-grammar/src/main/java/de/monticore/types/check/TypeCheck/#what-is-the-difference-between-typesymbols-and-symtypeexpressions","title":"What is the difference between TypeSymbols and SymTypeExpressions?","text":"

    The TypeCheck uses the TypeSymbols of the BasicSymbols grammar and the handwritten SymTypeExpressions. While they are very similar, there is a big difference between them and when to use them. The TypeSymbols represent a type definition (example in Java: class List) while the SymTypeExpressions represent a type usage (example in Java: List). There is only one type definition, but there can be many type usages. The SymTypeExpression knows its corresponding TypeSymbol (like the type usage knows its definition) and can refer to its methods and fields.

    So when talking about a type definition, a type symbol, which can be stored and is present only once in the symboltable, has to be used. A SymTypeExpression is not stored in the symboltable, but refers to the definition of its type (its corresponding TypeSymbol) in the symboltable. Thus, a SymTypeExpression can be used multiple times and represents the usage of a type.

    "},{"location":"monticore-grammar/src/main/java/de/monticore/types/check/TypeCheck/#the-typecheck-facade","title":"The TypeCheck facade","text":"

    The TypeCheck class of MontiCore is used as a facade to make TypeChecking easier for the user. It needs a Derive-Class and/or a Synthesize-Class to be instantiated. Here is a list of the methods and functions of the TypeCheck. * SymTypeExpression typeOf(ASTExpression expr) (uses the Derive-Class to derive a SymTypeExpression from an ASTExpression) * SymTypeExpression typeOf(ASTLiteral lit) (uses the Derive-Class to derive a SymTypeExpression from an ASTLiteral) * SymTypeExpression symTypeFromAST(ASTType type) (uses the Synthesize-Class to derive a SymTypeExpression from an ASTType) * static boolean compatible(SymTypeExpression left, SymTypeExpression right) (checks if the type of the right SymTypeExpression would be assignable to a variable with the type of the left SymTypeExpression) * static boolean isSubTypeOf(SymTypeExpression sub, SymTypeExpression sup) (checks if the type of sub is a subtype of the type of sup) * static boolean isInt(SymTypeExpression sym) (there are methods like this for all primitive types, checks if the SymTypeExpression represents the type 'int')

    "},{"location":"monticore-grammar/src/main/java/de/monticore/types/check/TypeCheck/#how-does-the-typecheck-work","title":"How does the TypeCheck work?","text":"

    The TypeCheck can be used to derive a SymTypeExpression from an expression or type. For example, when using an ASTPlusExpression '3+4', the TypeCheck returns a SymTypeExpression representing the type 'int'. You can use the ASTPlusExpression as a parameter for the method typeOf of the TypeCheck facade. This method delegates the calculation to your Derive-Class. First it derives the SymTypeExpression of the subexpressions '3' and '4' (the LiteralExpressions used in the PlusExpression) and then it calculates the SymTypeExpression of the whole PlusExpression by combining the SymTypeExpressions of its subexpressions in the context of the '+' operator.

    In general, the derivation of SymTypeExpressions for expressions first calculates the SymTypeExpressions of its subexpressions and (depending on the results) then combines these SymTypeExpression adequately to one SymTypeExpression for the whole expression.

    Deriving the SymTypeExpression of a type is often easier than deriving the SymTypeExpression of an expression. The ASTMCPrimitiveType 'int' will result in a SymTypeExpression 'int', the ASTMCBasicGenericType 'java.util.Map' will result in a SymTypeExpression 'java.util.Map'.

    The derivation of types is very similar to the derivation of expressions with regard to subtypes and subexpressions. If a type has subtypes (like type arguments), then the SymTypeExpressions representing these subtypes will be calculated first. They can be used to put together the SymTypeExpression of the whole type.

    Because both types and expressions are converted into SymTypeExpressions, you can compare them. This is useful in the next example:

    int a = \"Hello\";\n

    The SymTypeExpression derived from the ASTPrimitiveType 'int' will be 'int', the SymTypeExpression derived from the ASTLiteralExpression \"Hello\" will be 'String'. In a CoCo, you can check if they are compatible by using the function 'compatible' of the TypeCheck facade.

    "},{"location":"monticore-grammar/src/main/java/de/monticore/types/check/TypeCheck/#i-want-to-write-a-coco-for-my-language-that-uses-the-typecheck-how","title":"I want to write a CoCo for my language that uses the TypeCheck - How?","text":"

    You can use the TypeCheck facade. The facade needs a Derive-Class (for expressions and literals) and/or a Synthesize-Class (for types) and calculates the SymTypeExpression of your given expressions/literals/types. Create a DelegatorVisitor which combines all expression grammars and literal grammars used by your language. The DelegatorVisitor needs to implement the Interface IDerive. Use this Delegator as Derive-Class in the TypeCheck facade. The Synthesize-Class depends on the types grammar you use (see above-mentioned classes). For an example of the Delegator-Visitor see here. If you want to create a Derive-Class for your expression/literal grammar, you have to extend the Derive-Class of the supergrammar and implement the standard visitor of your language. There you can override the traverse methods for your expressions/literals so that it calculates the SymTypeExpression of the expression/literal (see implementation in one of the above-mentioned classes). You can add your visitor to the DelegatorVisitor later on. For an example of the Derive-Class for one language see here. For an example of the Synthesize-Class for one language see here Writing a CoCo to check the correctness of your type/expression/literal should be easy now that you have the TypeCheck facade to use. Just use the correct Derive-Class and/or the correct Synthesize-Class as parameters and check if the SymTypeExpression of your expression or type is correctly calculated. Example for a CoCo:

    @Override\npublic void check(ASTExpression expr){\nYourDeriveClass deriveClass = new YourDeriveClass(...); //instance of your Derive-Class\nYourSynthesizeClass synthesizeClass = new YourSynthesizeClass(...); //instance of your Synthesize-Class\nTypeCheck check = new TypeCheck(synthesizeClass,deriveClass); //instance of the TypeCheck-facade, parameters are your Synthesize-Class and your Derive-Class\nif(!\"double\".equals(check.typeOf(expr).print())){ //test if your expression is of the correct type (here: double)\nLog.error(...); //your specified error message\n}\n}\n

    An example for the case that a plus expression needs to return 'int' can be found here.

    "},{"location":"monticore-grammar/src/main/java/de/monticore/types/check/TypeCheck/#further-information","title":"Further Information","text":"
    • Project root: MontiCore @github
    • MontiCore documentation
    • List of languages
    • MontiCore Core Grammar Library
    • Best Practices
    • Publications about MBSE and MontiCore
    • Licence definition
    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/","title":"Symboltable","text":""},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#documentation-of-the-symbol-table-infrastructure","title":"Documentation of the Symbol Table Infrastructure","text":""},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#conceptual-model-of-symbol-tables","title":"Conceptual Model of Symbol Tables","text":"
    • What is a symbol?
    • What is a symbol kind?
    • What is a scope?
    • What are properties of scopes?
    • What is symbol resolution?
    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#define-the-symbol-table-of-a-language-via-its-grammar","title":"Define the Symbol Table of a Language via its Grammar","text":"
    • Indicate that a nonterminal defines a symbol
    • Indicate that a nonterminal spans a scope
    • Indicate that a nonterminal uses the name of a symbol
    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#runtime-environment-for-symbol-table-infrastructure","title":"Runtime Environment for Symbol Table Infrastructure","text":"

    This section explains classes and interfaces that are part of the MontiCore runtime environment.

    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#symbol-table-infrastructure-interfaces","title":"Symbol Table Infrastructure Interfaces","text":"

    Most of the interfaces of the MontiCore runtime enviroenment are super types of generated classes or interfaces that are explained here.

    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#iscope-interface","title":"IScope Interface","text":"

    This interface is the super type of the generated scope interfaces and thus, it is also transitive of global scope interfaces and artifact scope interfaces. IScope contains signatures for methods realizing the scope's connection to its environment (i.e., AST classes, sub scopes, enclosing scopes). Generated, language-specific scope interfaces refine the types of these methods.

    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#iartifactscope-interface","title":"IArtifactScope Interface","text":"

    The IArtifactScope interface is an interface that all generated language-specific artifact scope interfaces extend. It provides an abstract method for obtaining an artifact scope's package as String. All further methods have either language-specific arguments or return types and are, thus, introduced in the language-specific artifact scope interfaces.

    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#iglobalscope-interface","title":"IGlobalScope Interface","text":"

    The IGlobalScope interface is an interface that all generated language-specific global scope interfaces extend. It provides an abstract method for obtaining the global scope's Modelpath. All further methods have either language-specific arguments or return types and are, thus, introduced in the language-specific global scope interfaces.

    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#isymbol-interface","title":"ISymbol Interface","text":"

    The ISymbol interface is an interface that all generated language-specific symbol classes implement. It provides the signatures for methods to obtaining the symbol's name, its package, its fully-qualified name, and its enclosing scope, and its AST node. Further, the interface contains the signatures of methods for getting and setting the access modifier of the symbol and default implementations for getting the source position of the symbol. It also includes a static method for sorting a collection of symbols by their source position, which is handy for realizing the semantics of ordered scopes. All further methods have either language-specific arguments or return types and are, thus, introduced in the specific symbol classes.

    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#isymbolpredicate-interface","title":"ISymbolPredicate Interface","text":"

    An ISymbolPredicate is a predicate of a symbol and is used for filtering the results of symbol resolution. This is explained in more detail in [HR17]. The MontiCore runtime contains the class IncludesAccessModifierSymbolPredicate, which is an implementation of a symbol predicate for filtering symbols based on their access modifier.

    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#iscopespanningsymbol-interface","title":"IScopeSpanningSymbol Interface","text":"

    The IScopeSpanningSymbol interface extends the interface ISymbol and adds a method signature for obtaining the scope that this symbol spans. Symbols that span a scope (which is the case, e.g., if the respective nonterminal in the grammar is annotated with both the keywords symbol and scope) implement this interface instead of the ISymbol interface.

    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#modifiers","title":"Modifiers","text":"

    The modifiers contained in the MontiCore runtime implement the interface AccessModifier, which again is a Modifier. Out of the box, MontiCore supports the two access modifier implementations BasicAccessModifier and NoAccessModifier. Further, more sophisticated access modifiers have to be engineered individually, dedicated to their use for a specific modeling language.

    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#json-infrastructure-for-symbol-table-serialization","title":"JSON Infrastructure for Symbol Table Serialization","text":"

    The MontiCore runtime contains classes that are required for serializing and deserializing symbol tables to Json-encoded Strings. The following explains these in short:

    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#jsonprinter-class","title":"JsonPrinter Class","text":"

    The class JsonPrinter wraps the concrete syntax of Json. It is an API for building Json-encoded String via a series of method calls.

    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#jsonparser-class","title":"JsonParser Class","text":"

    The class JsonParser parses a Json-encoded String into an instance of the Json abstract syntax model. The central method of this class is the static method JsonElement parse(String s).

    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#json-parsing-infrastructure","title":"Json Parsing Infrastructure","text":"

    Besides the JsonParser class, the MontiCore runtime contains more classes required for translating JSON-encoded Strings into instances of the Json abstract syntax model. The class JsonLexer lexes an input String into a sequence of JsonToken instances. JsonToken instances realizes tokens that have a certain kind in form of a value from the enumeration JsonTokenKind. The NumberLexer is able to lex all kinds of valid numbers encoded in Json.

    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#json-model","title":"Json Model","text":"

    The MontiCore runtime contains a model of the abstract syntax of JSON that is used by the JsonParser and the JsonPrinter for serialization of symbol tables. Individual classes exist for the different abstract syntax types of JSON.

    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#jsondesers-class","title":"JsonDeSers Class","text":"

    The class JsonDeSers contains constants and static methods that support the generated language-specific symbol and scope DeSer classes.

    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#generated-symbol-table-infrastructure","title":"Generated Symbol Table Infrastructure","text":"

    MontiCore generates large parts of the symbol table infrastructure that is strongly typed for each MontiCore language. The following gives a short and technical introduction of each of these generated classes, interfaces, and enums. The concepts behind each of these infrastructure part if explained in the MontiCore Reference Manual [HR17].

    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#infrastructure-generated-per-language","title":"Infrastructure Generated per Language","text":"

    This section explains all parts of the symbol table infrastructure that MontiCore generates once per language. For scopes, artifact scopes, and global scopes, MontiCore separated classes and interfaces. The interfaces follow the (multiple) inheritance of the grammars and realized most behavior in form of default method implementations. The classes implement the interface and manage access to attributes.

    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#scope-interface","title":"Scope Interface","text":"

    For each language, MontiCore generates a scope interface. The scope interface prescribes all public methods of the scope class and realized some methods as default implementations. The hierarchy of MontiCore grammars is also reflected in the hierarchy of scope interfaces. To realize the multiple inheritance of MontiCore in Java, the scope interface is separated from the scope class. If a language inherits from one or more grammars, the scope interface of the language extends all scope interfaces of the inherited languages. Otherwise, if a language does not inherit from any language, the scope interface extends the IScope interface from the MontiCore runtime.

    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#scope-class","title":"Scope Class","text":"

    The scope class is generated for each MontiCore language. It implements the scope interface of the language and realizes scope attributes as well as method implementations that realize direct access to scope attributes.

    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#scope-builder-class","title":"Scope Builder Class","text":"

    MontiCore generated builder classes for each scope class. The instances of the builders are available through the language's mill. With the builder, the attributes of the scope class can be initialized and a new instance of the scope can be created.

    We highly recommend instantiating scope classes only through the builder obtained via the mill. All other forms of instantiations will prohibit reconfiguration through sublanguages.

    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#artifactscope-interface","title":"ArtifactScope Interface","text":"

    The artifact scope interface is generated once for each MontiCore language. It extends the scope interface of the language and the artifact scope interface of the MontiCore runtime. Artifact scopes are instantiated once for each processed artifact and, conceptually, slightly differ from scopes established within a model. To this end, their realization overrides some methods of the scope interface with a special behavior and adds new methods.

    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#artifactscope-class","title":"ArtifactScope Class","text":"

    MontiCore generates a single artifact scope class for each language that extends the scope class of the language and implements the artifact scope interface of the language.

    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#artifactscope-builder-class","title":"ArtifactScope Builder Class","text":"

    MontiCore generated builder classes for each artifact scope class. The instances of the builders are available through the language's mill. With the builder, the attributes of the artifact scope class can be initialized and a new instance of the artifact scope can be created.

    We highly recommend instantiating artifact scope classes only through the builder obtained via the mill. All other forms of instantiations will prohibit reconfiguration through sublanguages.

    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#globalscope-interface","title":"GlobalScope Interface","text":"

    Similar to artifact scope interfaces, global scope interfaces extends the scope interface of the language. Additionally, they implement the global scope interfaces of their parent languages or the IGlobalScope of the MontiCore runtime if the languages do not inherit from another language.

    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#globalscope-class","title":"GlobalScope Class","text":"

    The global scope class is generated for each MontiCore language and realizes the concrete global scope of a language. It extends the scope class and implements the global scope iterface of the language.

    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#globalscope-builder-class","title":"GlobalScope Builder Class","text":"

    MontiCore generated builder classes for each global scope class. The instances of the builders are available through the language's mill. With the builder, the attributes of the global scope class can be initialized and a new instance of the global scope can be created.

    We highly recommend instantiating global scope classes only through the builder obtained via the mill. All other forms of instantiations will prohibit reconfiguration through sublanguages.

    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#symboltablecreator-interface","title":"SymbolTableCreator Interface","text":"

    TODO: SymbolTableCreator Interface is about to be changed

    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#symboltablecreator-class","title":"SymbolTableCreator Class","text":"

    TODO: SymbolTableCreator Class is about to be changed

    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#common-symbol-interface","title":"Common Symbol Interface","text":"

    The common symbol interface of a language extends the MontiCore runtime class ISymbol and provides methods for the connection to the enclosing scope and the visitor of the language. As these are specifically typed for each language, the common symbol interface is generated. All symbol classes of a language implement the common symbol interface.

    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#symboltableprinter","title":"SymbolTablePrinter","text":"

    The symbol table printer traverses the scope tree of an artifact using a visitor and serializes it in form of a JSON-encoded String. Traversal typically begins with an artifact scope. In each scope, the local symbols are visited and serialized. If a symbol spans a scope, the spanned scope is visited while visiting the symbol. It, therefore, realizes traversal of the scope tree along the enclosingScope <-> localSymbols and the symbol <-> spannedScope associations. Symbol table printers are used by ScopeDeSers and SymbolDeSers. For language composition, the symbol table printers of individual languages are combined with a delegator visitor in the DeSer classes.

    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#scopedeser","title":"ScopeDeSer","text":"

    The scope deser class provides methods realizing the loading and storing of scope objects of a language. Besides this, it realizes the deserialization of scopes. The deserialization of symbols within this scope is delegated to the respective symbol DeSers and serialization of symbols and scopes is delegated to the symbol table printer. The reason for this separation is that employing a visitor is suitable for serialization, but not for deserialization. The latter would visit elements of the abstract syntax of JSON, such as a Json object, and would require a large number of case distinctions within handling different objects that can be serialized as a Json object. Combining the visitor-based serialization and the deserialization into a single class would be inefficient in terms of compilation time.

    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#generated-per-symbol","title":"Generated per Symbol","text":"

    This section explains parts of the symbol table infrastructure that MontiCore generates once for each symbol of a language.

    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#symbol-class","title":"Symbol Class","text":"

    For each symbol of the language, MontiCore generates a symbol class that implements the common symbol interface of the language. The symbol class realizes symbols of a certain kind. For example, the class StateSymbol realizes the kind StateSymbol and objects of this class are concrete symbols. A symbol kind can inherit from at most one other symbol kind. This is reflected in the symbol classes by extending the class of the super kind.

    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#symbol-builder-class","title":"Symbol Builder Class","text":"

    MontiCore generated builder classes for each symbol. The instances of the builders are available through the language's mill. With the builder, the attributes of the symbol class can be initialized and a new instance of the symbol can be created.

    We highly recommend instantiating symbol classes only through the builder obtained via the mill. All other forms of instantiations will prohibit reconfiguration through sublanguages, e.g., in case the symbol production is overridden in the grammar.

    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#symbol-deser","title":"Symbol DeSer","text":"

    The symbol DeSer classes are generated for each symbol and realize serialization and deserialization of symbols of a certain kind. The serialization is visitor-based and, thus, delegated to the symbol table printer. Symbol DeSers are used by scope DeSers to realize the deserialization of symbols and as such, are reused for all languages that inherit from the current language. As serialization and deserialization of individual symbols is rarely triggered manually, no load and store methods exist in symbol DeSer classes.

    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#symbol-surrogate-class","title":"Symbol Surrogate Class","text":"

    Symbol surrogate classes extend the generated symbol classes and realize lazy loading of symbls of this kind. Surrogates have a delegate of the symbol class that is empty during initialization of the surrogate., where only the enclosing scope and the name are set. They further define a method for resolving the symbol with the on demand. Symbol surrogates must only be if both of the following two conditions are met: 1. If on type level, a symbol has an attribute of another symbol, the attribute may be initialized with the surrogate as the symbol's subtype. 2. If on instance level, the symbol definition of the Surrogates must never be used to simplify instantiation of local symbols, i.e., of symbols that are contained in a single model for which the symbol table currently is build. In this case, it is always possible to split symbol table creation into multiple phases: In the first phase, all symbol definitions instantiate symbol class objects, for which the symbol attributes are not instantiated yet. In a later phase, the symbol attributes are filled with values.

    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#symbol-surrogate-builder","title":"Symbol Surrogate Builder","text":"

    MontiCore generated builder classes for each symbol surrogate. The instances of the builders are available through the language's mill. With the builder, the attributes of the symbol surrogate class can be initialized and a new instance of the symbol surrogate can be created.

    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#resolvers","title":"Resolvers","text":"

    MontiCore generates a resolver interface for each symbol kind of a language. Resolvers have a method for resolving adapted symbol kinds. Language engineers can develop concrete resolving delegates that implement a resolver interface. Such classes can be added to the global scope of a language to integrate resolving for adapted symbols into the resolution process. For example, an automata language defines the generated resolver interface IStateSymbolResolver. This interface can be used by language engineers to implement a CDClass2StateResolver class implementing the interface that resolves, for example, for symbols of a CD class whenever resolving for state symbols is invoked. The result of this is typically an adapter symbol, which adapts the foreign symbol (e.g., CDClassSymbol) to the expected symbol (e.g., StateSymbol).

    "},{"location":"monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/#further-information","title":"Further Information","text":"
    • Project root: MontiCore @github
    • MontiCore documentation
    • List of languages
    • MontiCore Core Grammar Library
    • Best Practices
    • Publications about MBSE and MontiCore
    • Licence definition
    "}]} \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 0000000000..2a5ab4f7c9 --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,138 @@ + + + + https://monticore.github.io/monticore/ + 2023-08-06 + daily + + + https://monticore.github.io/monticore/00.org/Explanations/CHANGELOG/ + 2023-08-06 + daily + + + https://monticore.github.io/monticore/00.org/Explanations/FAQ/ + 2023-08-06 + daily + + + https://monticore.github.io/monticore/00.org/Explanations/StatusOfGrammars/ + 2023-08-06 + daily + + + https://monticore.github.io/monticore/00.org/Licenses/LICENSE-BSD3CLAUSE/ + 2023-08-06 + daily + + + https://monticore.github.io/monticore/00.org/Licenses/LICENSE-LGPL/ + 2023-08-06 + daily + + + https://monticore.github.io/monticore/00.org/Licenses/LICENSE-MONTICORE-3-LEVEL/ + 2023-08-06 + daily + + + https://monticore.github.io/monticore/docs/BestPractices-CLI/ + 2023-08-06 + daily + + + https://monticore.github.io/monticore/docs/BestPractices-Errors/ + 2023-08-06 + daily + + + https://monticore.github.io/monticore/docs/BestPractices-Language-Design/ + 2023-08-06 + daily + + + https://monticore.github.io/monticore/docs/BestPractices-Symbols-Scopes/ + 2023-08-06 + daily + + + https://monticore.github.io/monticore/docs/BestPractices-Syntax-Design/ + 2023-08-06 + daily + + + https://monticore.github.io/monticore/docs/BestPractices/ + 2023-08-06 + daily + + + https://monticore.github.io/monticore/docs/BuildMontiCore/ + 2023-08-06 + daily + + + https://monticore.github.io/monticore/docs/DevelopedLanguages/ + 2023-08-06 + daily + + + https://monticore.github.io/monticore/docs/Download/ + 2023-08-06 + daily + + + https://monticore.github.io/monticore/docs/GettingStarted/ + 2023-08-06 + daily + + + https://monticore.github.io/monticore/docs/Languages/ + 2023-08-06 + daily + + + https://monticore.github.io/monticore/docs/Publications/ + 2023-08-06 + daily + + + https://monticore.github.io/monticore/docs/further_docs/Impressum/ + 2023-08-06 + daily + + + https://monticore.github.io/monticore/monticore-grammar/src/main/grammars/de/monticore/Grammars/ + 2023-08-06 + daily + + + https://monticore.github.io/monticore/monticore-grammar/src/main/grammars/de/monticore/JavaLight/ + 2023-08-06 + daily + + + https://monticore.github.io/monticore/monticore-grammar/src/main/grammars/de/monticore/expressions/Expressions/ + 2023-08-06 + daily + + + https://monticore.github.io/monticore/monticore-grammar/src/main/grammars/de/monticore/literals/Literals/ + 2023-08-06 + daily + + + https://monticore.github.io/monticore/monticore-grammar/src/main/grammars/de/monticore/types/Types/ + 2023-08-06 + daily + + + https://monticore.github.io/monticore/monticore-grammar/src/main/java/de/monticore/types/check/TypeCheck/ + 2023-08-06 + daily + + + https://monticore.github.io/monticore/monticore-runtime/src/main/java/de/monticore/symboltable/Symboltable/ + 2023-08-06 + daily + + \ No newline at end of file diff --git a/sitemap.xml.gz b/sitemap.xml.gz new file mode 100644 index 0000000000..d68bac9fab Binary files /dev/null and b/sitemap.xml.gz differ diff --git a/stylesheets/extra.css b/stylesheets/extra.css new file mode 100644 index 0000000000..36b3280353 --- /dev/null +++ b/stylesheets/extra.css @@ -0,0 +1,20 @@ +/* (c) https://github.com/MontiCore/monticore */ +.md-header { + background-color: #006BA5; +} +:root { + --md-primary-fg-color: #006BA5; +} +.tip { + border: 2px solid grey; + border-radius: 5px; + padding: 10px; + margin-bottom: 5px; +} +.tip-header { + font-size: larger; + border-bottom: 2px solid grey; +} +.bibliography { + empty-cells: hide; +}