- MPS Language ↔ LionCore M3
- MPS Language → JSON
- MPS Instance Model ↔ JSON
We should not have any static method calls inside the core logic. Every required interface should be a constructor parameter.
Rationale: Simplifies testing, and potentially the compatibility to different LionWeb versions.
This includes core MPS classes like MetaAdapterFactory
— we wrap them in an interface (IMetaAdapterFactoryHelper
).
Rationale: Uniform code structure.
We accept the trade-off that classes have lots of constructor parameters. As mitigation, we provide facades.
Each class should perform exactly one task.
For example, we split up mapping keys (MpsCompleteKeyMapper
) from encoding keys (EncodeToLionWebKeyConverter
).
Each class performs their single task on all parts of a language, e.g. Concepts, Properties, Enumerations, etc.
A lot of these classes implement IKeyMapper
Rationale: Simplifies testing, and allows to combine the tasks in different ways.
We accept the trade-off of deep call stacks (i.e. lots of indirection).
As mitigation, we provide wrapper classes that combine several tasks with daisy-chaining (e.g. CompositeGuaranteedMapper
) or delegation (e.g. ADelegateKeyMapper
) patterns.
We convert representations in two phases: First, we create all required target representations (e.g. Nodes, Concepts, Containments). Afterwards, we link the target representations according to the source links.
The alternative would be to create a target representation, and immediately create+link all linked target representations.
Rationale: We chose the former approach, as the latter is harder to debug, especially in combination with the "single task" principle mentioned above.
We accept the trade-off that we have to hold all target representations in memory.
We have to deal with default or special values (e.g. built-in primitive types) in every converter.
We list them in central types like ILionCoreConstants
and IJsonConstants
in the same order.
In each converter we can get the appropriate two lists and correlate them by index.
Rationale: Index-based mapping is error-prone, but we need every possible combination between representations typically once. Maintaining all of these maps would be more effort, and error-prone than carefully ordered lists.
Example: We list primitive types as
-
List<PrimitiveType>[getBoolean(), getInteger(), getString(), getJSON()]
inIJsonConstants.listPrimitiveTypes()
-
List<node<PrimitiveType>>[lcBooleanType(), lcIntegerType(), lcStringType(), lcJsonType()]
inILionCoreConstants.listLcPrimitiveTypes()
, -
List<string>[idBooleanType(), idIntegerType(), idStringType(), idJsonType()]
inILionCoreConstants.listSPrimitiveTypeIds()
We use annotations @NotNull
and @Nullable
on all single-valued return types and parameters of public methods.
"single-valued" are all types that are not collections, sequences, iterables, etc.
Rationale: MPS typechecker and generated run-time code helps a lot in dealing with potential null
values.
Our converters should be robust, so they have to deal with null
values at all possible places anyway.
We write technical documentation as JavaDoc on all classes and public methods. We can omit the JavaDoc if and only if the class'/method’s purpose, usage, assumptions, and limitations are clear and obvious from its name, without looking at the implementation. Include comparisons to similar, but slightly different classes/methods.
Rationale: Especially with above’s "single task" principle, lots of classes look very similar. Even after glancing through the code their differences might not be obvious — we need to explain it.
Except NullPointerException
, we should only use custom specializations of RuntimeException
.
Rationale: We want to distinguish between properly handled exceptional situations (e.g. user tries to import instances of non-existent concepts) from potential coding issues (e.g. IndexOutOfBoundException
because we mismatched default values).
Our converters are riddled with special cases. There only way we can handle them consistently without regressions is very good test coverage.
- LionCore MPS M3
-
Module
io.lionweb.mps.m3
contains LionCore meta-meta-model as MPS language. Mainly used to test conversion between MPS and LionCore concepts.
- LionCore Java M3
-
Module
org.lionweb.lionweb.java
contains LionCore meta-meta-model as Java classes. Used to serialize / unserialize LionWeb M2 and M1 models to / from JSON.
- Language structure
-
Module
jetbrains.mps.lang.structure
contains MPS meta-meta-model as MPS language. Used to define languages inside MPS.
SLanguage
interfaces-
Model
org.jetbrains.mps.openapi.language
contains MPS meta-meta-model as Java interfaces. Used by MPS to represent deployed language interfaces. IImportedLanguage
s-
MPS is very picky how to change already existing languages. This interface separates converting to an MPS language from importing the MPS language.
ExistingImportedLanguage
-
Applies only changes from the imported language to an already existing MPS language.
ReadonlyImportedLanguage
-
Represents existing MPS languages that cannot be changed (e.g. because they are part of plugins, or core MPS). Any detected change from the imported language throws an exception.
NewImportedLanguage
-
Simply creates an imported, not yet existing language.
MPS represents languages in lots of differnt ways, depending on the context. There’s always more than one way how to get from one representation to the other, but these ways are not equal — especially in case of half-broken languages.
Use MpsLanguageConverter to convert between all representations.
It choses the most successful way, and clearly reports success (either by potential @Nullable
result or throws IllegalStateException
).
Note
|
The following representations are only used internally in MPS. The diagram shows how to get from one representation to another. |
link:mps-language-classes.puml[role=include]
Language
class-
Model
jetbrains.mps.smodel
contains a language as module. SLanguageAdapter
class-
Model
jetbrains.mps.smodel.adapter.structure
(and contained models) contain MPS meta-meta-model as Java classes. LanguageRuntime
class-
Model
jetbrains.mps.smodel.language
contains the runtime representation of a language with all its aspects.
SLanguageId
class-
Model
jetbrains.mps.smodel.adapter.ids
contains a unique identifier for a language.
- LionCore2JsonConverter
-
Exports LionWeb M2
Language
s expressed in MPS languageio.lionweb.mps.m3
to LionWeb JSONLanguage
s. - Json2LionCoreConverter
-
Imports LionWeb JSON
Language
s to LionWeb M2Language
s expressed in MPS languageio.lionweb.mps.m3
. - Mps2LionCoreConverter
-
Converts MPS language structure models to to LionWeb M2
Language
s expressed in MPS languageio.lionweb.mps.m3
. The converted language does not need to be deployed. - LionCore2MpsConverter
-
Converts LionWeb M2
Language
s expressed in MPS languageio.lionweb.mps.m3
toAImportedLanguage
s. - Language2LionCoreConverter
-
Converts deployed MPS
SLanguage
s to to LionWeb M2Language
s expressed in MPSlanguage io.lionweb.mps.m3
.- IndirectLanguage2LionCoreConverter
-
Converts the transitive closure of deployed MPS
SLanguage
s to LionWeb M2Language
s expressed in MPSlanguage io.lionweb.mps.m3
.
- Language2JsonConverter
-
Converts MPS
SLanguage
s to LionWeb JSONLanguage
s. The source of this converter are compiled languages inside MPS.- IndirectLanguage2JsonConverter
-
Converts the transitive closure of MPS
SLanguage
s to LionWeb JSONLanguage
s. - FineGrainedClosureLanguage2JsonConverter
-
Converts the minimally required concepts of MPS
SLanguage
s to LionWeb JSONLanguage
s.
- Json2LanguageConverter
-
Converts LionWeb JSON
Language
s to compiledSLanguage
s present in MPS. Fails if any part of the source language is not present in MPS.
- LionWeb2MpsConverter
-
Converts a sequence of
SerializedNode
s (originating from parsed LionWeb JSON) to MPSSNode
s. Handles everySerializedNode
as a new MPSSNode
. Assumes all used languages are present as built languages in MPS.- MergingLionWeb2MpsConverter
-
Converts a sequence of
SerializedNode
s (originating from parsed LionWeb JSON) to MPSSNode
s. Merges existingSerializedNode
with MPSSNode
s if they have the same node id. Assumes all used languages are present as built languages in MPS.
- AMps2LionWebConverter
-
Converts MPS
SNode
s to LionWeb JSONSerializedNode
s. Subclasses decide which nodes besides the input nodes should be processed:- ClosureMps2LionWebConverter
-
Converts the transitive closure of all MPS
SNode
s listed in the constructor, all descendants, and all references to LionWeb JSONSerializedNode
s. - DescendantMps2LionWebConverter
-
Converts all MPS
SNode
s listed in the constructor, and all descendants, to LionWeb JSONSerializedNode
s. - ListedMps2LionWebConverter
-
Converts only the MPS
SNode
s listed in the constructor to LionWeb JSONSerializedNode
s.
- ILionCoreConstants
-
Access to constants like built-in elements in different language representations.
- IJsonConstants
-
Access to constants like built-in elements in Java JSON.
- IdEncoder
-
Encodes and decodes with Base64_url.
- ILanguageDependsOnFinder
-
Finds all languages extended and/or needed by a language.
- LionWebAttributeFinder
-
We store additional information (like concept’s keys) in
SNodeAttribute
s. This class helps accessing them. - MpsLanguageConverter
-
Easy access to all the conversions between language representations.
- AnnotationFinder
-
Identifies LionWeb Annotation (aka MPS Attribute) Concepts.
- BuiltinsUsage
-
Identifies factual MPS language dependencies that are substituted by LionWeb builtins dependencies.
- M1Serializer
-
Serializes instance level (M1) nodes.
- M2Serializer
-
Serializes language level (M2) nodes.
- Deserializer
-
Deserializes JSON nodes of any level (M1/M2).
Solution io.lionweb.mps.server.plugin
adds some endpoints to the MPS http server:
/lionweb/bulk
-
LionWeb bulk protocol implementation that serves the MPS repository. Supports GET and POST requests.
Possible URLs:
- minimal
-
http://127.0.0.1:63320/lionweb/bulk?modelRef=r:e8b696c3-e87f-426c-ad83-fa5eb08bdac7( )
(Note the pair of parenthesis enclosing a whitespace at the end.)
- including name
- including project
-
project
parameter can also be used with minimal model id.
/lionweb/language
-
LionWeb bulk protocol (read-only) that serves MPS languages in LionCore. Supports GET requests. Addresses languages by their MPS module id.
- minimal
-
http://127.0.0.1:63320/lionweb/language?moduleRef=97ef2b8d-23e1-433e-8d23-48f916dd314d( )
(Note the pair of parenthesis enclosing a whitespace at the end.)
- including name
- including project
-
project
parameter can also be used with minimal module id.
/lionweb/language/key
-
LionWeb bulk protocol (read-only) that serves MPS languages in LionCore. Supports GET requests. Addresses languages by their key.
Caution
|
This part of lionweb-mps is currently not maintained. We might reactivate it in the future. |
interface IImportedLanguage { getLanguage(): SLanguage getRootNodes(): node[] apply(ILanguageCreator): string[] } abstract class AImportedLanguage implements IImportedLanguage { new(Metamodel, SLanguage, LionCoreConstants) getLanguage(): SLanguage getRootNodes(): node[] register(): ILionCore2MpsMap convert(ILionCore2MpsMap) link(ILionCore2MpsMap) getMap(): ILionCore2MpsMap } abstract class ADeltaImportedLanguage extends AImportedLanguage { new(Metamodel, SLanguage, Language, model, LionCoreConstants) getDeltas(): IDelta getModel(): model } class ReadonlyImportedLanguage extends ADeltaImportedLanguage class ExistingImportedLanguage extends ADeltaImportedLanguage class NewImportedLanguage extends ADeltaImportedLanguage
Caution
|
This part of lionweb-mps is currently not maintained. We might reactivate it in the future. |
interface IDelta abstract class ALanguageDelta implements IDelta { changedLanguage: SLanguage } class RenameLanguageDelta extends ALanguageDelta { oldName: string newName: string } abstract class ANodeDelta implements IDelta { changedNode: SNode } abstract class AParentedDelta extends ANodeDelta { parent: SNode changedLink: SContainmentLink } class AddDelta extends AParentedDelta { getNew(): node } class RemoveDelta extends AParentedDelta { getRemmoved(): node }
interface IDelta abstract class ANodeDelta implements IDelta { changedNode: SNode } class ChangeConceptDelta extends ANodeDelta { oldConcept: SAbstractConcept newConcept: SAbstractConcept } class ChangeLinkDelta extends ANodeDelta { oldValue: SNode newValue: SNode changedLink: SAbstractLink } class ChangePropertyDelta extends ANodeDelta { oldValue: string newValue: string changedLink: SProperty } class MoveModelDelta extends ANodeDelta { oldModel: SModel newModel: SModel oldParent: SNode } class MoveParentDelta extends ANodeDelta { oldParent: SNode newParent: SNode }