Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,467 changes: 1,467 additions & 0 deletions Resources/KerML-textual-bnf.kebnf

Large diffs are not rendered by default.

1,705 changes: 1,705 additions & 0 deletions Resources/SysML-textual-bnf.kebnf

Large diffs are not rendered by default.

104 changes: 104 additions & 0 deletions Resources/kebnf.g4
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Grammar

grammar kebnf;

specification : (NL)* rule_definition+ EOF ;

rule_definition
: name=UPPER_ID (params=parameter_list)? (COLON target_ast=UPPER_ID)? ASSIGN rule_body=alternatives SEMICOLON? NL+
;

parameter_list
: LPAREN param_name=ID COLON param_type=ID RPAREN
;

alternatives
: alternative (PIPE alternative)*
;

alternative
: element*
;

element
: assignment
| non_parsing_assignment
| non_parsing_empty
| cross_reference
| group
| terminal
| non_terminal
| value_literal
;

assignment
: property=dotted_id op=(ASSIGN | ADD_ASSIGN | BOOL_ASSIGN) (prefix=TILDE)?content=element_core (suffix=suffix_op)?
;

non_parsing_assignment
: LBRACE property=dotted_id op=(ASSIGN | ADD_ASSIGN) val=value_literal RBRACE
;

non_parsing_empty
: LBRACE RBRACE
;

cross_reference
: TILDE? LBRACK ref=ID RBRACK
;

group
: LPAREN alternatives RPAREN (suffix=suffix_op)?
;

terminal
: val=SINGLE_QUOTED_STRING (suffix=suffix_op)?
;

non_terminal
: name=UPPER_ID (suffix=suffix_op)?
;

element_core
: cross_reference
| group
| terminal
| non_terminal
| value_literal
;

dotted_id
: ID (DOT ID)*
;

suffix_op : '*' | '+' | '?' ;

value_literal : ID | INT | STRING | '[QualifiedName]' | SINGLE_QUOTED_STRING;

// Lexer
ASSIGN : '::=' | '=' ;
ADD_ASSIGN : '+=' ;
BOOL_ASSIGN : '?=' ;
PIPE : '|' ;
COLON : ':' ;
SEMICOLON : ';' ;
COMMA : ',' ;
LPAREN : '(' ;
RPAREN : ')' ;
LBRACK : '[' ;
RBRACK : ']' ;
LBRACE : '{' ;
RBRACE : '}' ;
DOT : '.' ;
TILDE : '~' ;

UPPER_ID : [A-Z] [a-zA-Z0-9_]* ;
ID : [a-zA-Z_][a-zA-Z0-9_]* ;
SINGLE_QUOTED_STRING : '\'' (~['\\] | '\\' .)* '\'' ;
INT : [0-9]+ ;
STRING : '\'' ( ~['\\] | '\\' . )* '\'' ;

COMMENT : '//' ~[\r\n]* -> skip ;
WS : [ \t]+ -> skip ;
CONTINUATION : '\r'? '\n' [ \t]+ -> skip ;
NL : '\r'? '\n' ;
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// -------------------------------------------------------------------------------------------------
// <copyright file="UmlCoreTextualNotationBuilderGeneratorTestFixture.cs" company="Starion Group S.A.">
//
// Copyright 2022-2026 Starion Group S.A.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// </copyright>
// ------------------------------------------------------------------------------------------------

namespace SysML2.NET.CodeGenerator.Tests.Generators.UmlHandleBarsGenerators
{
using System.IO;
using System.Linq;
using System.Threading.Tasks;

using NUnit.Framework;

using SysML2.NET.CodeGenerator.Generators.UmlHandleBarsGenerators;
using SysML2.NET.CodeGenerator.Grammar;
using SysML2.NET.CodeGenerator.Grammar.Model;

[TestFixture]
public class UmlCoreTextualNotationBuilderGeneratorTestFixture
{
private DirectoryInfo umlPocoDirectoryInfo;
private UmlCoreTextualNotationBuilderGenerator umlCoreTextualNotationBuilderGenerator;
private TextualNotationSpecification textualNotationSpecification;

[OneTimeSetUp]
public void OneTimeSetup()
{
var directoryInfo = new DirectoryInfo(TestContext.CurrentContext.TestDirectory);

var path = Path.Combine("UML", "_SysML2.NET.Core.UmlCoreTextualNotationBuilderGenerator");

this.umlPocoDirectoryInfo = directoryInfo.CreateSubdirectory(path);
this.umlCoreTextualNotationBuilderGenerator = new UmlCoreTextualNotationBuilderGenerator();

var textualRulesFolder = Path.Combine(TestContext.CurrentContext.TestDirectory, "datamodel");
var kermlRules = GrammarLoader.LoadTextualNotationSpecification(Path.Combine(textualRulesFolder, "KerML-textual-bnf.kebnf"));
var sysmlRules = GrammarLoader.LoadTextualNotationSpecification(Path.Combine(textualRulesFolder, "SysML-textual-bnf.kebnf"));

var combinesRules = new TextualNotationSpecification();
combinesRules.Rules.AddRange(sysmlRules.Rules);

foreach (var rule in kermlRules.Rules.Where(rule => combinesRules.Rules.All(r => r.RuleName != rule.RuleName)))
{
combinesRules.Rules.Add(rule);
}

this.textualNotationSpecification = combinesRules;
}

[Test]
public async Task VerifyCanGenerateTextualNotation()
{
await Assert.ThatAsync(() => this.umlCoreTextualNotationBuilderGenerator.GenerateAsync(GeneratorSetupFixture.XmiReaderResult, this.textualNotationSpecification, this.umlPocoDirectoryInfo), Throws.Nothing);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// -------------------------------------------------------------------------------------------------
// <copyright file="TextualNotationSpecificationVisitorTestFixture.cs" company="Starion Group S.A.">
//
// Copyright 2022-2026 Starion Group S.A.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// </copyright>
// ------------------------------------------------------------------------------------------------

namespace SysML2.NET.CodeGenerator.Tests.Grammar
{
using System;
using System.IO;
using System.Linq;

using Antlr4.Runtime;

using NUnit.Framework;

using SysML2.NET.CodeGenerator.Grammar;
using SysML2.NET.CodeGenerator.Grammar.Model;

[TestFixture]
public class TextualNotationSpecificationVisitorTestFixture
{
[Test]
[TestCase("KerML-textual-bnf.kebnf")]
[TestCase("SysML-textual-bnf.kebnf")]
public void VerifyCanParseGrammar(string modelName)
{
var filePath = Path.Combine(TestContext.CurrentContext.TestDirectory, "datamodel",modelName );

var stream = CharStreams.fromPath(filePath);
var lexer = new kebnfLexer(stream);
var tokens = new CommonTokenStream(lexer);
var parser = new kebnfParser(tokens);

var tree = parser.specification();
var explorer = new TextualNotationSpecificationVisitor();
var result = (TextualNotationSpecification)explorer.Visit(tree);
var rules = result.Rules;

using (Assert.EnterMultipleScope())
{
Assert.That(rules, Is.Not.Null);
Assert.That(rules, Is.Not.Empty);
Assert.That(rules.DistinctBy(x => x.RuleName), Is.EquivalentTo(rules));
}

Console.WriteLine($"Found {rules.Count} rules");
}
}
}
33 changes: 33 additions & 0 deletions SysML2.NET.CodeGenerator/Extensions/NamedElementExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@

namespace SysML2.NET.CodeGenerator.Extensions
{
using System;
using System.Linq;

using uml4net.CommonStructure;
using uml4net.SimpleClassifiers;

/// <summary>
/// Extension class for <see cref="INamedElement"/>
Expand All @@ -40,5 +42,36 @@ public static string QueryNamespace(this INamedElement namedElement)
var namespaces = qualifiedNameSpaces.Skip(1).Take(qualifiedNameSpaces.Length - 2);
return string.Join('.', namespaces);
}

/// <summary>
/// Query the fully qualified type name (Namespace + Type name).
/// </summary>
/// <param name="namedElement">The specific <see cref="INamedElement"/>that should have the fully qualified type name computed</param>
/// <param name="namespacePart">A specific namespace part (POCO/DTO distinction)</param>
/// <param name="targetInterface">Asserts if the type should be the interface name or not</param>
/// <returns>The fully qualified type name</returns>
public static string QueryFullyQualifiedTypeName(this INamedElement namedElement, string namespacePart = "POCO", bool targetInterface = true)
{
ArgumentNullException.ThrowIfNull(namedElement);
ArgumentException.ThrowIfNullOrWhiteSpace(namespacePart);

var typeName = "SysML2.NET.Core.";

if (namedElement is not IEnumeration)
{
typeName += $"{namespacePart}.";
}

typeName += namedElement.QueryNamespace();
typeName += ".";

if (namedElement is not IEnumeration && targetInterface)
{
typeName += "I";
}

typeName += namedElement.Name;
return typeName;
}
}
}
44 changes: 44 additions & 0 deletions SysML2.NET.CodeGenerator/Extensions/PropertyExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,5 +92,49 @@ public static bool QueryPropertyIsPartOfNonDerivedCompositeAggregation(this IPro

return property.Opposite is { IsComposite: true, IsDerived: false };
}

/// <summary>
/// Queries the content of a IF statement for non-empty values
/// </summary>
/// <param name="property">The property that have to be used to produce the content</param>
/// <param name="variableName">The name of the name</param>
/// <returns>The If Statement content</returns>
public static string QueryIfStatementContentForNonEmpty(this IProperty property, string variableName)
{
var propertyName = property.QueryPropertyNameBasedOnUmlProperties();

if (property.QueryIsEnumerable())
{
return $"{variableName}.MoveNext()";
}

if (property.QueryIsReferenceProperty())
{
return $"{variableName}.{propertyName} != null";
}

if (property.QueryIsNullableAndNotString())
{
return $"{variableName}.{propertyName}.HasValue";
}

if (property.QueryIsString())
{
return $"!string.IsNullOrWhiteSpace({variableName}.{propertyName})";
}

if (property.QueryIsBool())
{
return $"{variableName}.{propertyName}";
}

if (property.QueryIsEnum())
{
var defaultValue = property.QueryIsEnumPropertyWithDefaultValue() ? $"{property.Type.QueryFullyQualifiedTypeName()}.{property.QueryDefaultValueAsString().CapitalizeFirstLetter()}" : ((IEnumeration)property.Type).OwnedLiteral[0].Name;
return $"{variableName}.{propertyName} != {defaultValue}";
}

return "THIS WILL PRODUCE COMPILE ERROR";
}
}
}
Loading
Loading