Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
d502688
[WIP]: KEBNF files are parsed and turn into object graph
antoineatstariongroup Feb 18, 2026
f2d35e2
[WIP] First draft of Code-Gen
antoineatstariongroup Feb 19, 2026
76ba227
[WIP] Added prefix on assingment, gonna start implement logic for bui…
antoineatstariongroup Feb 19, 2026
5a7d46c
[WIP] Improved G4 and process TerminalElement rules
antoineatstariongroup Feb 20, 2026
865aff3
[WIP] Eased the value_literal on G4
antoineatstariongroup Feb 20, 2026
68c92f5
[WIP] All rules have method, some logic put on rule Element processing
antoineatstariongroup Feb 25, 2026
1d61fbf
Supports assignment for bool and enum
antoineatstariongroup Feb 26, 2026
de4ae2f
[WIP] Most assignment case handle, including collection
antoineatstariongroup Mar 3, 2026
0cd6371
[WIP] Implementation of hand-coded conditional checks
antoineatstariongroup Mar 4, 2026
d247538
[WIP] Enhance generaion but missing logic (FeatureMembership issue)
antoineatstariongroup Mar 5, 2026
a312e31
[WIP] ix FeatureMembershipTextualNotationBuilder
antoineatstariongroup Mar 5, 2026
a27b1b4
[WIP] Correctly implemented reference assignement
antoineatstariongroup Mar 6, 2026
6ee629e
[WIP] Reference property with ValueLiteral supported
antoineatstariongroup Mar 6, 2026
bdada01
[WIP] Supports last assignement case
antoineatstariongroup Mar 6, 2026
080e891
[WIP] Supports everything except Multiple Alternatives
antoineatstariongroup Mar 6, 2026
d8638ef
[WIP] Supports alternatives composed of all TerminalElement
antoineatstariongroup Mar 9, 2026
d68b54b
[WIP] Switch case for NonTerminalElement while multiple alternative i…
antoineatstariongroup Mar 10, 2026
64d946e
[WIP] Supports assigment with groupElement
antoineatstariongroup Mar 11, 2026
db36811
[WIP] All Assignment cases supported in multiple alternatives
antoineatstariongroup Mar 11, 2026
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");
}
}
}
41 changes: 27 additions & 14 deletions SysML2.NET.CodeGenerator/Extensions/ClassExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,15 @@

namespace SysML2.NET.CodeGenerator.Extensions
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;

using uml4net.Classification;
using uml4net.Extensions;
using uml4net.Packages;
using uml4net.StructuredClassifiers;

/// <summary>
/// Extension methods for <see cref="IClass"/> interface
/// Extension methods for <see cref="IClass" /> interface
/// </summary>
public static class ClassExtensions
{
Expand All @@ -40,10 +37,10 @@ public static class ClassExtensions
/// or interface implementations EXCLUDING IsDerived
/// </summary>
/// <param name="class">
/// The <see cref="IClass"/> from which to query the properties
/// The <see cref="IClass" /> from which to query the properties
/// </param>
/// <returns>
/// A <see cref="ReadOnlyCollection{T}"/> of <see cref="IProperty"/>
/// A <see cref="ReadOnlyCollection{T}" /> of <see cref="IProperty" />
/// </returns>
public static ReadOnlyCollection<IProperty> QueryAllNonDerivedProperties(this IClass @class)
{
Expand All @@ -59,10 +56,10 @@ public static ReadOnlyCollection<IProperty> QueryAllNonDerivedProperties(this IC
/// redefined in the context of the current class
/// </summary>
/// <param name="class">
/// The <see cref="IClass"/> from which to query the properties
/// The <see cref="IClass" /> from which to query the properties
/// </param>
/// <returns>
/// A <see cref="ReadOnlyCollection{T}"/> of <see cref="IProperty"/>
/// A <see cref="ReadOnlyCollection{T}" /> of <see cref="IProperty" />
/// </returns>
public static ReadOnlyCollection<IProperty> QueryAllNonDerivedNonRedefinedProperties(this IClass @class)
{
Expand All @@ -78,7 +75,7 @@ public static ReadOnlyCollection<IProperty> QueryAllNonDerivedNonRedefinedProper
/// Counts and returns to amount of non derived properties
/// </summary>
/// <param name="class">
/// The subject <see cref="IClass"/>
/// The subject <see cref="IClass" />
/// </param>
/// <returns>
/// the amount of non derived properties
Expand All @@ -92,7 +89,7 @@ public static int CountAllNonDerivedProperties(this IClass @class)
/// Counts and returns to amount of non derived properties
/// </summary>
/// <param name="class">
/// The subject <see cref="IClass"/>
/// The subject <see cref="IClass" />
/// </param>
/// <returns>
/// the amount of non derived properties
Expand All @@ -103,20 +100,36 @@ public static int CountAllNonDerivedNonRedefinedProperties(this IClass @class)
}

/// <summary>
/// Query the name of the internal interface to implement for an <see cref="IClass"/>
/// Query the name of the internal interface to implement for an <see cref="IClass" />
/// </summary>
/// <param name="umlClass">The <see cref="IClass"/></param>
/// <param name="umlClass">The <see cref="IClass" /></param>
/// <returns>The name of the internal interface to implement, if any</returns>
public static string QueryInternalInterfaceName(this IClass umlClass)
{
var classifiers = umlClass.QueryAllGeneralClassifiers();

if (classifiers.Any(x => x.Name == "Relationship"))
{
return "IContainedRelationship";
}

return classifiers.Any(x => x.Name == "Element") ? "IContainedElement" : string.Empty;
}

/// <summary>
/// Asserts that an <see cref="IClass" /> is a subclass of another
/// </summary>
/// <param name="umlClass">The <see cref="IClass" /> to check</param>
/// <param name="potentialSuperClass">
/// The <see cref="IClass" /> that is potentially a super class of
/// <paramref name="umlClass" />
/// </param>
/// <returns>
/// True if the <paramref name="potentialSuperClass" /> is a super class of the <paramref name="umlClass" />
/// </returns>
public static bool QueryIsSubclassOf(this IClass umlClass, IClass potentialSuperClass)
{
return umlClass.QueryAllGeneralClassifiers().Contains(potentialSuperClass);
}
}
}
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;
}
}
}
Loading
Loading