Skip to content

Commit 12bdb2f

Browse files
Lexical rules code gen
1 parent c8d33c5 commit 12bdb2f

16 files changed

Lines changed: 879 additions & 156 deletions
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// -------------------------------------------------------------------------------------------------
2+
// <copyright file="UmlCoreLexicalRulesGeneratorTestFixture.cs" company="Starion Group S.A.">
3+
//
4+
// Copyright 2022-2026 Starion Group S.A.
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// </copyright>
19+
// ------------------------------------------------------------------------------------------------
20+
21+
namespace SysML2.NET.CodeGenerator.Tests.Generators.UmlHandleBarsGenerators
22+
{
23+
using System.IO;
24+
using System.Threading.Tasks;
25+
26+
using NUnit.Framework;
27+
28+
using SysML2.NET.CodeGenerator.Generators.UmlHandleBarsGenerators;
29+
using SysML2.NET.CodeGenerator.Grammar;
30+
using SysML2.NET.CodeGenerator.Grammar.Model;
31+
32+
[TestFixture]
33+
public class UmlCoreLexicalRulesGeneratorTestFixture
34+
{
35+
private DirectoryInfo outputDirectoryInfo;
36+
private UmlCoreLexicalRulesGenerator generator;
37+
private TextualNotationSpecification textualNotationSpecification;
38+
39+
[OneTimeSetUp]
40+
public void OneTimeSetup()
41+
{
42+
var directoryInfo = new DirectoryInfo(TestContext.CurrentContext.TestDirectory);
43+
44+
var path = Path.Combine("UML", "_SysML2.NET.Core.UmlCoreLexicalRulesGenerator");
45+
46+
this.outputDirectoryInfo = directoryInfo.CreateSubdirectory(path);
47+
this.generator = new UmlCoreLexicalRulesGenerator();
48+
49+
var textualRulesFolder = Path.Combine(TestContext.CurrentContext.TestDirectory, "datamodel");
50+
51+
// The lexical support classes (Keywords, SymbolicKeywordKind, SymbolicKeywordKindExtensions)
52+
// are built from the SysML grammar only — the KerML grammar is intentionally excluded so
53+
// the emitted set of tokens matches the SysML v2 specification surface.
54+
this.textualNotationSpecification = GrammarLoader.LoadTextualNotationSpecification(Path.Combine(textualRulesFolder, "SysML-textual-bnf.kebnf"));
55+
}
56+
57+
[Test]
58+
public async Task VerifyCanGenerateLexicalRules()
59+
{
60+
await Assert.ThatAsync(() => this.generator.GenerateAsync(GeneratorSetupFixture.XmiReaderResult, this.textualNotationSpecification, this.outputDirectoryInfo), Throws.Nothing);
61+
}
62+
}
63+
}

SysML2.NET.CodeGenerator/Extensions/PropertyExtension.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public static string QueryPropertyNameBasedOnUmlProperties(this IProperty proper
7272
{
7373
ArgumentNullException.ThrowIfNull(property);
7474

75-
return property.IsDerived || property.IsDerivedUnion ? StringExtensions.LowerCaseFirstLetter(property.Name) : StringExtensions.CapitalizeFirstLetter(property.Name);
75+
return property.IsDerived || property.IsDerivedUnion ? property.Name.LowerCaseFirstLetter() : property.Name.CapitalizeFirstLetter();
7676
}
7777

7878
/// <summary>
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
// -------------------------------------------------------------------------------------------------
2+
// <copyright file="StringExtensions.cs" company="Starion Group S.A.">
3+
//
4+
// Copyright 2022-2026 Starion Group S.A.
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// </copyright>
19+
// ------------------------------------------------------------------------------------------------
20+
21+
namespace SysML2.NET.CodeGenerator.Extensions
22+
{
23+
using System.Text;
24+
25+
/// <summary>
26+
/// String helpers used by the grammar and lexical-rules code-generation passes.
27+
/// </summary>
28+
public static class StringExtensions
29+
{
30+
/// <summary>
31+
/// Tests whether a string follows the SysML lexical-rule naming convention: uppercase
32+
/// letters, digits and underscores only, with at least one letter (e.g.
33+
/// <c>RESERVED_KEYWORD</c>, <c>DEFINED_BY</c>, <c>STRING_VALUE</c>).
34+
/// </summary>
35+
/// <param name="value">The string to test</param>
36+
/// <returns><c>true</c> when the string matches the all-uppercase snake-case convention</returns>
37+
public static bool IsAllUpperSnake(this string value)
38+
{
39+
if (string.IsNullOrEmpty(value))
40+
{
41+
return false;
42+
}
43+
44+
var hasLetter = false;
45+
46+
foreach (var character in value)
47+
{
48+
if (char.IsLetter(character))
49+
{
50+
hasLetter = true;
51+
52+
if (!char.IsUpper(character))
53+
{
54+
return false;
55+
}
56+
}
57+
else if (character != '_' && !char.IsDigit(character))
58+
{
59+
return false;
60+
}
61+
}
62+
63+
return hasLetter;
64+
}
65+
66+
/// <summary>
67+
/// Tests whether the supplied string contains any letter.
68+
/// </summary>
69+
/// <param name="value">The string to test</param>
70+
/// <returns><c>true</c> when at least one letter is present</returns>
71+
public static bool ContainsAnyLetter(this string value)
72+
{
73+
if (string.IsNullOrEmpty(value))
74+
{
75+
return false;
76+
}
77+
78+
foreach (var character in value)
79+
{
80+
if (char.IsLetter(character))
81+
{
82+
return true;
83+
}
84+
}
85+
86+
return false;
87+
}
88+
89+
/// <summary>
90+
/// Converts a <c>SNAKE_CASE</c> identifier into <c>PascalCase</c>
91+
/// (e.g. <c>DEFINED_BY</c> → <c>DefinedBy</c>).
92+
/// </summary>
93+
/// <param name="value">The snake-case identifier</param>
94+
/// <returns>The PascalCase form</returns>
95+
public static string ToPascalCaseFromSnake(this string value)
96+
{
97+
if (string.IsNullOrEmpty(value))
98+
{
99+
return value;
100+
}
101+
102+
var builder = new StringBuilder(value.Length);
103+
var capitalizeNext = true;
104+
105+
foreach (var character in value)
106+
{
107+
if (character == '_')
108+
{
109+
capitalizeNext = true;
110+
continue;
111+
}
112+
113+
builder.Append(capitalizeNext ? char.ToUpperInvariant(character) : char.ToLowerInvariant(character));
114+
capitalizeNext = false;
115+
}
116+
117+
return builder.ToString();
118+
}
119+
}
120+
}

0 commit comments

Comments
 (0)