Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 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
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