From 903cdeabfbe60a9cdcbe9cfa7a8d435c6badaa60 Mon Sep 17 00:00:00 2001 From: caran Date: Tue, 28 May 2024 12:46:31 +0200 Subject: [PATCH] WIP --- YangParser/Generator/Log.cs | 28 ++- YangParser/Generator/YangGenerator.cs | 219 ++++++++++-------- YangParser/SemanticModel/Augment.cs | 6 +- YangParser/SemanticModel/Base.cs | 9 +- YangParser/SemanticModel/Builtins/Bits.cs | 11 +- .../Builtins/BuiltinTypeReference.cs | 16 ++ .../SemanticModel/Builtins/Decimal64.cs | 2 +- .../SemanticModel/Builtins/Enumeration.cs | 10 +- .../Builtins/IdentityReference.cs | 26 +-- YangParser/SemanticModel/Builtins/Int16.cs | 2 +- YangParser/SemanticModel/Builtins/Int32.cs | 2 +- YangParser/SemanticModel/Builtins/Int64.cs | 2 +- YangParser/SemanticModel/Builtins/Int8.cs | 2 +- .../SemanticModel/Builtins/LeafReference.cs | 8 +- YangParser/SemanticModel/Builtins/String.cs | 10 +- YangParser/SemanticModel/Builtins/Uint16.cs | 2 +- YangParser/SemanticModel/Builtins/Uint32.cs | 2 +- YangParser/SemanticModel/Builtins/Uint64.cs | 2 +- YangParser/SemanticModel/Builtins/Uint8.cs | 2 +- YangParser/SemanticModel/Builtins/Union.cs | 12 +- YangParser/SemanticModel/DefaultValue.cs | 76 +++--- YangParser/SemanticModel/Grouping.cs | 41 ++-- YangParser/SemanticModel/IStatement.cs | 4 +- YangParser/SemanticModel/Identity.cs | 19 +- YangParser/SemanticModel/Leaf.cs | 6 +- YangParser/SemanticModel/LeafList.cs | 11 +- YangParser/SemanticModel/Module.cs | 91 ++++++-- YangParser/SemanticModel/Output.cs | 2 +- YangParser/SemanticModel/Range.cs | 4 +- YangParser/SemanticModel/Statement.cs | 33 ++- .../SemanticModel/StatementExtensions.cs | 159 +++++++++---- YangParser/SemanticModel/Submodule.cs | 55 ++++- YangParser/SemanticModel/Type.cs | 19 +- YangParser/SemanticModel/Uses.cs | 3 +- yang-compiler.sln.DotSettings.user | 4 + 35 files changed, 573 insertions(+), 327 deletions(-) create mode 100644 yang-compiler.sln.DotSettings.user diff --git a/YangParser/Generator/Log.cs b/YangParser/Generator/Log.cs index 2bb0868..ee984bc 100644 --- a/YangParser/Generator/Log.cs +++ b/YangParser/Generator/Log.cs @@ -1,12 +1,32 @@ +using System; using System.Diagnostics; +using System.IO; using System.Text; namespace YangParser.Generator; public static class Log { - private static StringBuilder m_builder = new(); - public static void Clear() => m_builder.Clear(); - public static void Write(string message) => m_builder.AppendLine(message); - public static string Get() => m_builder.ToString(); + private static DateTime Start; + private static FileStream? m_stream; + private static FileStream Stream + { + get + { + if (m_stream is null) Start = DateTime.Now; + return m_stream ??= new FileStream(@"C:\tmp\YangGenerator\log", FileMode.Create, FileAccess.Write, FileShare.ReadWrite); + } + } + + public static void Clear() + { + m_writer?.Flush(); + // m_stream?.Dispose(); + // m_writer?.Dispose(); + // m_stream = null; + // m_writer = null; + } + private static StreamWriter? m_writer; + private static StreamWriter writer => m_writer ??= new StreamWriter(Stream); + public static void Write(string message) => writer.WriteLine($"{(DateTime.Now - Start).TotalSeconds:F2}: " + message); } \ No newline at end of file diff --git a/YangParser/Generator/YangGenerator.cs b/YangParser/Generator/YangGenerator.cs index 713cb48..88b28bc 100644 --- a/YangParser/Generator/YangGenerator.cs +++ b/YangParser/Generator/YangGenerator.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.IO; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Text; @@ -42,7 +43,6 @@ public void Initialize(IncrementalGeneratorInitializationContext context) private void MakeClasses(SourceProductionContext context, ImmutableArray> models) { - Log.Clear(); try { Dictionary topLevels = new(); @@ -76,21 +76,19 @@ private void MakeClasses(SourceProductionContext context, ImmutableArray().Select(i => i.Argument))}"); - - + Log.Write("IncludeSubmodules Complete"); //Replace Uses by their respective groupings UnwrapUses(context, compilation); + Log.Write("UnwrapUses Complete"); foreach (var module in compilation.Children.OfType()) { try { - context.AddSource(module.Filename, Clean(module.ToCode())); + WriteFile(context, module.Filename, Clean(module.ToCode())); } catch (Exception e) { - context.AddSource(module.Filename + ".errors", + WriteFile(context, module.Filename + ".errors", $"#error Exception when generating code for {module.Filename}" + "\n/*\n" + e.Message + "\n" + e.StackTrace + "\n*/"); } @@ -98,11 +96,26 @@ private void MakeClasses(SourceProductionContext context, ImmutableArray()) { - var usings = module.Unwrap().OfType().ToArray(); - foreach (var use in usings) + foreach (var use in module.Uses) { if (use.IsUnderGrouping()) { @@ -252,13 +264,13 @@ private static void ReportDiagnostics(SourceProductionContext context, ResultOrE private static readonly DiagnosticDescriptor SemanticError = new DiagnosticDescriptor("YANG0002", "Semantic Error", "Semantic Error: {0}", "SemanticModel", DiagnosticSeverity.Error, true); -// private void MakeClasses(SourceProductionContext context, AdditionalText text) -// { -// if (!Parse(context, text, out var parsed)) return; -// if (!MakeSemanticModel(context, parsed, out var statement)) return; -// if (statement is not Module module) return; -// context.AddSource(module.Filename, module.ToCode()); -// } + // private void MakeClasses(SourceProductionContext context, AdditionalText text) + // { + // if (!Parse(context, text, out var parsed)) return; + // if (!MakeSemanticModel(context, parsed, out var statement)) return; + // if (statement is not Module module) return; + // context.AddSource(module.Filename, module.ToCode()); + // } private static ResultOrException MakeSemanticModel(ResultOrException statement) @@ -291,80 +303,101 @@ private static ResultOrException Parse(AdditionalText text) private void AddAttributesClass(IncrementalGeneratorPostInitializationContext context) { - context.AddSource("YangModules/Attributes/Yang.Attributes.cs", """ - using System; - namespace Yang.Attributes; - [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] - public class RevisionAttribute(string date) : Attribute - { - public string Date { get; } = date; - } + var fileName = "YangModules/Attributes/Yang.Attributes.cs"; + var contents = """ + using System; + namespace Yang.Attributes; + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public class RevisionAttribute(string date) : Attribute + { + public string Date { get; } = date; + } + + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] + public class PresenceAttribute(string meaning) : Attribute + { + public string Meaning { get; } = meaning; + } + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public class ProvidesFeatureAttribute(string flag) : Attribute + { + public string FeatureFlag { get; } = flag; + } + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class IfFeatureAttribute(string flag) : Attribute + { + public string FeatureFlag { get; } = flag; + } + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class ReferenceAttribute(string reference) : Attribute + { + public string Reference { get; } = reference; + } + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class WhenAttribute(string xPath) : Attribute + { + public string XPath { get; } = xPath; + } + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class TargetAttribute(string xPath) : Attribute + { + public string XPath { get; } = xPath; + } + [AttributeUsage(AttributeTargets.All, AllowMultiple = false)] + public class KeyAttribute(params string[] value) : Attribute + { + public string[] Value { get; } = value; + } + [AttributeUsage(AttributeTargets.All, AllowMultiple = false)] + public class MinElementsAttribute(int value) : Attribute + { + public int Value { get; } = value; + } + [AttributeUsage(AttributeTargets.All, AllowMultiple = false)] + public class MaxElementsAttribute(int value) : Attribute + { + public int Value { get; } = value; + } + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class InheritsAttribute(string baseName) : Attribute + { + public string BaseName { get; } = baseName; + } + [AttributeUsage(AttributeTargets.All, AllowMultiple = false)] + public class OrderedByAttribute(string value) : Attribute + { + public string Value { get; } = value; + } + [AttributeUsage(AttributeTargets.All, AllowMultiple = false)] + public class NotConfigurationData : Attribute; - [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] - public class PresenceAttribute(string meaning) : Attribute - { - public string Meaning { get; } = meaning; - } - [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] - public class ProvidesFeatureAttribute(string flag) : Attribute - { - public string FeatureFlag { get; } = flag; - } - [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] - public class IfFeatureAttribute(string flag) : Attribute - { - public string FeatureFlag { get; } = flag; - } - [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] - public class ReferenceAttribute(string reference) : Attribute - { - public string Reference { get; } = reference; - } - [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] - public class WhenAttribute(string xPath) : Attribute - { - public string XPath { get; } = xPath; - } - [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] - public class TargetAttribute(string xPath) : Attribute - { - public string XPath { get; } = xPath; - } - [AttributeUsage(AttributeTargets.All, AllowMultiple = false)] - public class KeyAttribute(params string[] value) : Attribute - { - public string[] Value { get; } = value; - } - [AttributeUsage(AttributeTargets.All, AllowMultiple = false)] - public class MinElementsAttribute(int value) : Attribute - { - public int Value { get; } = value; - } - [AttributeUsage(AttributeTargets.All, AllowMultiple = false)] - public class MaxElementsAttribute(int value) : Attribute - { - public int Value { get; } = value; - } - [AttributeUsage(AttributeTargets.All, AllowMultiple = false)] - public class OrderedByAttribute(string value) : Attribute - { - public string Value { get; } = value; - } - [AttributeUsage(AttributeTargets.All, AllowMultiple = false)] - public class NotConfigurationData : Attribute; + public class InstanceIdentifier(string path) + { + public string Path { get; } = path; + } + public interface IChannel + { + string Send(string xml); + } + public interface IXMLSource + { + string ToXML(); + } + """; + context.AddSource(fileName, contents); - public class InstanceIdentifier(string path) - { - public string Path { get; } = path; - } - public interface IChannel - { - string Send(string xml); - } - public interface IXMLSource - { - string ToXML(); - } - """); + Log.Write($"Writing file {fileName}"); + var file = "C:/tmp/YangGenerator/" + fileName; + var dir = System.IO.Path.GetDirectoryName(file); +#pragma warning disable RS1035 // Do not use APIs banned for analyzers + if (Directory.Exists(dir) == false) + { + Directory.CreateDirectory(dir); + } +#pragma warning restore RS1035 // Do not use APIs banned for analyzers + using var fs = new FileStream(file, FileMode.Create); + using var writer = new StreamWriter(fs); + writer.Write(contents); + Log.Clear(); } } \ No newline at end of file diff --git a/YangParser/SemanticModel/Augment.cs b/YangParser/SemanticModel/Augment.cs index 45a78ff..c4fab7b 100644 --- a/YangParser/SemanticModel/Augment.cs +++ b/YangParser/SemanticModel/Augment.cs @@ -4,7 +4,7 @@ namespace YangParser.SemanticModel; -public class Augment : Statement +public class Augment : Statement, IUnexpandable { public Augment(YangStatement statement) : base(statement) { @@ -55,4 +55,8 @@ public override string ToCode() */ """; } + protected override void ValidateParent() + { + this.GetModule()?.Augments.Add(this); + } } \ No newline at end of file diff --git a/YangParser/SemanticModel/Base.cs b/YangParser/SemanticModel/Base.cs index 9d93e46..5482e6f 100644 --- a/YangParser/SemanticModel/Base.cs +++ b/YangParser/SemanticModel/Base.cs @@ -4,13 +4,18 @@ namespace YangParser.SemanticModel; -public class Base : Statement +public class Base : Statement, IUnexpandable { public Base(YangStatement statement) : base(statement) { if (statement.Keyword != Keyword) throw new SemanticError($"Non-matching Keyword '{statement.Keyword}', expected {Keyword}", statement); - + } public const string Keyword = "base"; + override public string ToCode() + { + Parent?.Attributes.Add("Inherits(\"" + Argument + "\")"); + return string.Empty; + } } diff --git a/YangParser/SemanticModel/Builtins/Bits.cs b/YangParser/SemanticModel/Builtins/Bits.cs index 40ae0f9..3e037f5 100644 --- a/YangParser/SemanticModel/Builtins/Bits.cs +++ b/YangParser/SemanticModel/Builtins/Bits.cs @@ -2,19 +2,18 @@ namespace YangParser.SemanticModel.Builtins; -public class Bits() : BuiltinType("bits", s => +public class Bits() : BuiltinType("bits", statement => { - var name = Statement.MakeName(s.Parent!.Argument); - var bits = s.Children.OfType().ToArray(); - var others = s.Children.Except(bits); + var name = BuiltinTypeReference.TypeName(statement); + var bits = statement.Children.OfType().ToArray(); var strings = bits.Select(e => e.ToCode()); - foreach (var child in others) + foreach (var child in statement.Children) { child.ToCode(); } var definition = $$""" - {{s.DescriptionString}}{{s.AttributeString}} + {{statement.DescriptionString}}{{statement.AttributeString}} public enum {{name}} { {{Statement.Indent(string.Join("\n", strings))}} diff --git a/YangParser/SemanticModel/Builtins/BuiltinTypeReference.cs b/YangParser/SemanticModel/Builtins/BuiltinTypeReference.cs index 9ca0d93..3b565f2 100644 --- a/YangParser/SemanticModel/Builtins/BuiltinTypeReference.cs +++ b/YangParser/SemanticModel/Builtins/BuiltinTypeReference.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; @@ -42,6 +43,10 @@ public static bool IsBuiltin(Type type, out string? cSharpType, out string? defi return false; } + public static bool IsBuiltinKeyword(string keyword) + { + return m_builtIns.Any(b => b.Name == keyword); + } public static string DefaultPattern(IStatement statement, IEnumerable staticFields, IEnumerable constructorStatements, @@ -64,4 +69,15 @@ public class {{typeName}} } """; } + public static string TypeName(IStatement type) + { + string Postfix = string.Empty; + var parent = type.Parent!; + while (IsBuiltinKeyword(parent.Argument)) + { + Postfix += Array.IndexOf(parent.Children, type); + parent = parent.Parent!; + } + return Statement.MakeName(parent.Argument) + Postfix; + } } \ No newline at end of file diff --git a/YangParser/SemanticModel/Builtins/Decimal64.cs b/YangParser/SemanticModel/Builtins/Decimal64.cs index 8cb0e3d..faf7457 100644 --- a/YangParser/SemanticModel/Builtins/Decimal64.cs +++ b/YangParser/SemanticModel/Builtins/Decimal64.cs @@ -3,7 +3,7 @@ namespace YangParser.SemanticModel.Builtins; public class Decimal64() : BuiltinType("decimal64", statement => { if (!statement.TryGetChild(out var range)) return ("double", null); - var name = Statement.MakeName(statement.Parent!.Argument); + var name = BuiltinTypeReference.TypeName(statement); return (name, BuiltinTypeReference.DefaultPattern(statement, [], [range!.GetConstructorValidation()], "long", name)); }); \ No newline at end of file diff --git a/YangParser/SemanticModel/Builtins/Enumeration.cs b/YangParser/SemanticModel/Builtins/Enumeration.cs index 3ed1347..6c50c9c 100644 --- a/YangParser/SemanticModel/Builtins/Enumeration.cs +++ b/YangParser/SemanticModel/Builtins/Enumeration.cs @@ -2,11 +2,11 @@ namespace YangParser.SemanticModel.Builtins; -public class Enumeration() : BuiltinType("enumeration", (s) => +public class Enumeration() : BuiltinType("enumeration", (statement) => { - var name = Statement.MakeName(s.Parent!.Argument); - var enums = s.Children.OfType().ToArray(); - var others = s.Children.Except(enums); + var name = BuiltinTypeReference.TypeName(statement); + var enums = statement.Children.OfType().ToArray(); + var others = statement.Children.Except(enums); var strings = enums.Select(e => e.ToCode()); foreach (var child in others) { @@ -14,7 +14,7 @@ public class Enumeration() : BuiltinType("enumeration", (s) => } var definition = $$""" - {{s.DescriptionString}}{{s.AttributeString}} + {{statement.DescriptionString}}{{statement.AttributeString}} public enum {{name}} { {{Statement.Indent(string.Join("\n", strings))}} diff --git a/YangParser/SemanticModel/Builtins/IdentityReference.cs b/YangParser/SemanticModel/Builtins/IdentityReference.cs index e41c598..651a5fa 100644 --- a/YangParser/SemanticModel/Builtins/IdentityReference.cs +++ b/YangParser/SemanticModel/Builtins/IdentityReference.cs @@ -3,20 +3,20 @@ namespace YangParser.SemanticModel.Builtins; -public class IdentityReference() : BuiltinType("identityref", s => +public class IdentityReference() : BuiltinType("identityref", statement => { - var inherits = s.Children.OfType().Select(Statement.InterfaceName).ToArray(); - if (inherits.Length == 1) - { - var value = inherits[0]; - return (value, null); - } - - var inheritance = inherits.Length == 0 ? string.Empty : " : " + string.Join(", ", inherits); - var name = Statement.MakeName(s.Parent!.Argument); - var definition = $""" - {s.DescriptionString}{s.AttributeString} - public class {name}{inheritance}; + var inherits = statement.Children.OfType().Select(x => '"' + x.Argument + '"').ToArray(); + var name = BuiltinTypeReference.TypeName(statement); + var definition = $$""" + {{statement.DescriptionString}}{{statement.AttributeString}} + public class {{name}} + { + public string Value { get; } + public static string[] Bases = [{{string.Join(", ", inherits)}}]; + public {{name}}(string input) => Value = input; + public static implicit operator string({{name}} input) => input.Value; + public static implicit operator {{name}}(string input) => new(input); + } """; return (name, definition); }); \ No newline at end of file diff --git a/YangParser/SemanticModel/Builtins/Int16.cs b/YangParser/SemanticModel/Builtins/Int16.cs index 99f208c..c7248c4 100644 --- a/YangParser/SemanticModel/Builtins/Int16.cs +++ b/YangParser/SemanticModel/Builtins/Int16.cs @@ -3,7 +3,7 @@ namespace YangParser.SemanticModel.Builtins; public class Int16() : BuiltinType("int16", statement => { if (!statement.TryGetChild(out var range)) return ("short", null); - var name = Statement.MakeName(statement.Parent!.Argument); + var name = BuiltinTypeReference.TypeName(statement); return (name, BuiltinTypeReference.DefaultPattern(statement, [], [range!.GetConstructorValidation()], "short", name)); }); \ No newline at end of file diff --git a/YangParser/SemanticModel/Builtins/Int32.cs b/YangParser/SemanticModel/Builtins/Int32.cs index c6c092f..2320bcc 100644 --- a/YangParser/SemanticModel/Builtins/Int32.cs +++ b/YangParser/SemanticModel/Builtins/Int32.cs @@ -3,7 +3,7 @@ namespace YangParser.SemanticModel.Builtins; public class Int32() : BuiltinType("int32", statement => { if (!statement.TryGetChild(out var range)) return ("int", null); - var name = Statement.MakeName(statement.Parent!.Argument); + var name = BuiltinTypeReference.TypeName(statement); return (name, BuiltinTypeReference.DefaultPattern(statement, [], [range!.GetConstructorValidation()], "int", name)); }); \ No newline at end of file diff --git a/YangParser/SemanticModel/Builtins/Int64.cs b/YangParser/SemanticModel/Builtins/Int64.cs index f6607ef..63c8178 100644 --- a/YangParser/SemanticModel/Builtins/Int64.cs +++ b/YangParser/SemanticModel/Builtins/Int64.cs @@ -3,7 +3,7 @@ namespace YangParser.SemanticModel.Builtins; public class Int64() : BuiltinType("int64", statement => { if (!statement.TryGetChild(out var range)) return ("long", null); - var name = Statement.MakeName(statement.Parent!.Argument); + var name = BuiltinTypeReference.TypeName(statement); return (name, BuiltinTypeReference.DefaultPattern(statement, [], [range!.GetConstructorValidation()], "long", name)); }); \ No newline at end of file diff --git a/YangParser/SemanticModel/Builtins/Int8.cs b/YangParser/SemanticModel/Builtins/Int8.cs index 2be1228..5ed2002 100644 --- a/YangParser/SemanticModel/Builtins/Int8.cs +++ b/YangParser/SemanticModel/Builtins/Int8.cs @@ -3,7 +3,7 @@ namespace YangParser.SemanticModel.Builtins; public class Int8() : BuiltinType("int8", statement => { if (!statement.TryGetChild(out var range)) return ("sbyte", null); - var name = Statement.MakeName(statement.Parent!.Argument); + var name = BuiltinTypeReference.TypeName(statement); return (name, BuiltinTypeReference.DefaultPattern(statement, [], [range!.GetConstructorValidation()], "sbyte", name)); }); \ No newline at end of file diff --git a/YangParser/SemanticModel/Builtins/LeafReference.cs b/YangParser/SemanticModel/Builtins/LeafReference.cs index b3f9fef..55f44ad 100644 --- a/YangParser/SemanticModel/Builtins/LeafReference.cs +++ b/YangParser/SemanticModel/Builtins/LeafReference.cs @@ -2,12 +2,12 @@ namespace YangParser.SemanticModel.Builtins; -public class LeafReference() : BuiltinType("leafref", (s) => +public class LeafReference() : BuiltinType("leafref", (statement) => { - var path = (Path)s.Children.First(c => c is Path); - var name = Statement.MakeName(s.Parent!.Argument); + var path = (Path)statement.Children.First(c => c is Path); + var name = BuiltinTypeReference.TypeName(statement); var definition = $$""" - {{s.DescriptionString}}{{s.AttributeString}} + {{statement.DescriptionString}}{{statement.AttributeString}} public class {{name}}() : InstanceIdentifier("{{path.Argument}}"); """; return (name, definition); diff --git a/YangParser/SemanticModel/Builtins/String.cs b/YangParser/SemanticModel/Builtins/String.cs index e5d13c8..09baaa7 100644 --- a/YangParser/SemanticModel/Builtins/String.cs +++ b/YangParser/SemanticModel/Builtins/String.cs @@ -4,13 +4,13 @@ namespace YangParser.SemanticModel.Builtins; -public class String() : BuiltinType("string", s => +public class String() : BuiltinType("string", statement => { - var hasPattern = s.TryGetChild(out var pattern); - var hasLength = s.TryGetChild(out var length); + var hasPattern = statement.TryGetChild(out var pattern); + var hasLength = statement.TryGetChild(out var length); if (hasPattern || hasLength) { - var name = Statement.MakeName(s.Parent!.Argument); + var name = BuiltinTypeReference.TypeName(statement); List staticFields = new(); List constructorStatements = new(); if (hasPattern) @@ -29,7 +29,7 @@ public class String() : BuiltinType("string", s => constructorStatements.Add(length!.GetConstructorValidation()); } - return (name, BuiltinTypeReference.DefaultPattern(s, staticFields, constructorStatements, "string", name)); + return (name, BuiltinTypeReference.DefaultPattern(statement, staticFields, constructorStatements, "string", name)); } return ("string", null); diff --git a/YangParser/SemanticModel/Builtins/Uint16.cs b/YangParser/SemanticModel/Builtins/Uint16.cs index 6303dfd..2eb6eeb 100644 --- a/YangParser/SemanticModel/Builtins/Uint16.cs +++ b/YangParser/SemanticModel/Builtins/Uint16.cs @@ -3,7 +3,7 @@ namespace YangParser.SemanticModel.Builtins; public class Uint16() : BuiltinType("uint16", statement => { if (!statement.TryGetChild(out var range)) return ("ushort", null); - var name = Statement.MakeName(statement.Parent!.Argument); + var name = BuiltinTypeReference.TypeName(statement); return (name, BuiltinTypeReference.DefaultPattern(statement, [], [range!.GetConstructorValidation()], "ushort", name)); }); \ No newline at end of file diff --git a/YangParser/SemanticModel/Builtins/Uint32.cs b/YangParser/SemanticModel/Builtins/Uint32.cs index 1b4e7d9..6c0792d 100644 --- a/YangParser/SemanticModel/Builtins/Uint32.cs +++ b/YangParser/SemanticModel/Builtins/Uint32.cs @@ -3,7 +3,7 @@ namespace YangParser.SemanticModel.Builtins; public class Uint32() : BuiltinType("uint32", statement => { if (!statement.TryGetChild(out var range)) return ("uint", null); - var name = Statement.MakeName(statement.Parent!.Argument); + var name = BuiltinTypeReference.TypeName(statement); return (name, BuiltinTypeReference.DefaultPattern(statement, [], [range!.GetConstructorValidation()], "uint", name)); }); \ No newline at end of file diff --git a/YangParser/SemanticModel/Builtins/Uint64.cs b/YangParser/SemanticModel/Builtins/Uint64.cs index 783cb2a..3897db1 100644 --- a/YangParser/SemanticModel/Builtins/Uint64.cs +++ b/YangParser/SemanticModel/Builtins/Uint64.cs @@ -3,7 +3,7 @@ namespace YangParser.SemanticModel.Builtins; public class Uint64() : BuiltinType("uint64", statement => { if (!statement.TryGetChild(out var range)) return ("ulong", null); - var name = Statement.MakeName(statement.Parent!.Argument); + var name = BuiltinTypeReference.TypeName(statement); return (name, BuiltinTypeReference.DefaultPattern(statement, [], [range!.GetConstructorValidation()], "ulong", name)); }); \ No newline at end of file diff --git a/YangParser/SemanticModel/Builtins/Uint8.cs b/YangParser/SemanticModel/Builtins/Uint8.cs index 97b0499..5fafb27 100644 --- a/YangParser/SemanticModel/Builtins/Uint8.cs +++ b/YangParser/SemanticModel/Builtins/Uint8.cs @@ -6,6 +6,6 @@ namespace YangParser.SemanticModel.Builtins; public class Uint8() : BuiltinType("uint8", statement => { if (!statement.TryGetChild(out var range)) return ("byte", null); - var name = Statement.MakeName(statement.Parent!.Argument); + var name = BuiltinTypeReference.TypeName(statement); return (name, BuiltinTypeReference.DefaultPattern(statement, [], [range!.GetConstructorValidation()], "byte", name)); }); \ No newline at end of file diff --git a/YangParser/SemanticModel/Builtins/Union.cs b/YangParser/SemanticModel/Builtins/Union.cs index 0769fdc..32cc083 100644 --- a/YangParser/SemanticModel/Builtins/Union.cs +++ b/YangParser/SemanticModel/Builtins/Union.cs @@ -7,25 +7,27 @@ namespace YangParser.SemanticModel.Builtins; public class Union() : BuiltinType("union", s => { var options = s.Children.OfType().ToArray(); - var sourceName = Statement.MakeName(s.Parent!.Argument); + var sourceName = BuiltinTypeReference.TypeName(s); List types = []; List declarations = []; foreach (var option in options) { - types.Add(option.Name!.Replace("Union", sourceName + Array.IndexOf(options, option))); + var typeName = option.Name!; + types.Add(typeName); if (option.Definition != null) { - declarations.Add(option.Definition.Replace("Union", sourceName + Array.IndexOf(options, option))); + var declaration = option.Definition; + declarations.Add(declaration); } } - var varName = (string t) => Statement.Capitalize(t).Split(':').Last() + "Value"; + var varName = (string t) => Statement.Capitalize(t).Split(':', '.').Last() + "Value"; var name = sourceName; var definition = $$""" {{s.DescriptionString}}{{s.AttributeString}} public class {{name}} { - {{Statement.Indent(string.Join("\n", types.Select(typeName => $"private {name}({typeName} input){{ {varName(typeName)} = input; }}")))}} + {{Statement.Indent(string.Join("\n", types.Select(typeName => $"public {name}({typeName} input){{ {varName(typeName)} = input; }}")))}} {{Statement.Indent(string.Join("\n", types.Select(typeName => $"public {typeName}? {varName(typeName)} {{ get; }}")))}} {{Statement.Indent(string.Join("\n", types.Select(typeName => $"public static implicit operator {typeName}({name} input) => input.{varName(typeName)} ?? throw new InvalidOperationException(\"Union was not of effective type '{typeName}'\");")))}} {{Statement.Indent(string.Join("\n", types.Select(typeName => $"public static implicit operator {name}({typeName} input) => new {name}(input);")))}} diff --git a/YangParser/SemanticModel/DefaultValue.cs b/YangParser/SemanticModel/DefaultValue.cs index 531d54b..4ffea6f 100644 --- a/YangParser/SemanticModel/DefaultValue.cs +++ b/YangParser/SemanticModel/DefaultValue.cs @@ -1,7 +1,9 @@ using System; using System.Linq; +using System.Text.RegularExpressions; using YangParser.Generator; using YangParser.Parser; +using YangParser.SemanticModel.Builtins; namespace YangParser.SemanticModel; @@ -17,39 +19,59 @@ public DefaultValue(YangStatement statement) : base(statement) public override string ToCode() { - var components = Argument.Split(':'); - string prefix = string.Empty; - string value = components.Last(); - if (components.Length > 1) - { - prefix = components[0] + ":"; - } - - var source = this.FindReference(Argument); - if (prefix == string.Empty) - { - prefix = source?.GetInheritedPrefix() + ":"; - } - - // Log.Write($"Default Value Location for '{Argument}': {source?.GetType()} {source?.Argument}, prefix: {prefix}"); + string prefix = Argument.Prefix(out var value); + var type = Parent!.GetChild(); + return GetTypeSpecification(prefix, value, type); + } - switch (source) + private string GetTypeSpecification(string prefix, string value, Type type) + { + switch (type.Argument) { - case Enum @enum: - return prefix + ((Type)@enum!.Parent!).Name + "." + MakeName(value); - case Identity: - var localName = MakeName(Argument); - Addendum = $"public class {localName.Split(':').Last()}Implementation : {InterfaceName(localName)};"; - return "new " + prefix + localName.Split(':').Last() + "Implementation()"; + case "bits": + case "enumeration": + return prefix + type.Name + "." + MakeName(value); + case "identityref": + return $"\"{Argument}\""; + case "string": + return $"\"{Argument}\""; + case "boolean": + return Argument.ToLower(); + case "uint8": + case "uint16": + case "uint32": + case "uint64": + case "int8": + case "int16": + case "int32": + case "int64": + case "decimal64": + return Argument; + case "union": + if (onlyNumbers.Match(Argument).Success) + { + return $"new({Argument})"; + } + var enumeration = Parent!.GetChild().SearchDownwards(Argument); + if (enumeration != null) + { + return prefix + Parent!.GetChild().Name + "." + enumeration.Ancestor()!.Name + "." + MakeName(Argument); + } + return $"new(\"{Argument}\")"; default: - return Parent!.Argument switch + var source = this.FindReference(type.Argument); + if (source is not null) + { + return GetTypeSpecification(prefix, value, source.GetChild()); + } + if (onlyNumbers.Match(Argument).Success) { - "string" => $"\"{Argument}\"", - "boolean" => Argument.ToLower(), - _ => Argument - }; + return $"new({Argument})"; + } + return $"new(\"{Argument}\")"; } } + private static Regex onlyNumbers = new Regex(@"^[0-9\.]+$"); public string? Addendum { get; private set; } } \ No newline at end of file diff --git a/YangParser/SemanticModel/Grouping.cs b/YangParser/SemanticModel/Grouping.cs index 9334706..ee16495 100644 --- a/YangParser/SemanticModel/Grouping.cs +++ b/YangParser/SemanticModel/Grouping.cs @@ -77,6 +77,22 @@ public IStatement[] WithUse(Uses use) inner.Expand(); } + //Propogate usings upwards + if (use.GetModule() is Module target) + { + if (this.GetModule() is Module source) + { + if (source == target) return Children; + foreach (var pair in source.Usings) + { + if (!target.Usings.ContainsKey(pair.Key)) + { + // Log.Write($"Adding prefix {pair.Key} to '{target.Argument}' from '{source.Argument}'"); + target.Usings[pair.Key] = pair.Value; + } + } + } + } var containingModule = this.GetModule(); if (containingModule is null) { @@ -84,29 +100,14 @@ public IStatement[] WithUse(Uses use) } else { - foreach (var child in Children) - { - containingModule.ExpandPrefixes(child); - } + containingModule.Expand(); } - // //Propogate usings upwards - // if (use.GetModule() is Module target) - // { - // if (this.GetModule() is Module source) - // { - // if (source == target) return Children; - // foreach (var pair in source.Usings) - // { - // if (!target.Usings.ContainsKey(pair.Key)) - // { - // Log.Write($"Adding prefix {pair.Key} to '{target.Argument}' from '{source.Argument}"); - // target.Usings[pair.Key] = pair.Value; - // } - // } - // } - // } return Children; } + protected override void ValidateParent() + { + this.GetModule()?.Groupings.Add(this); + } } \ No newline at end of file diff --git a/YangParser/SemanticModel/IStatement.cs b/YangParser/SemanticModel/IStatement.cs index 60ddf1a..e606586 100644 --- a/YangParser/SemanticModel/IStatement.cs +++ b/YangParser/SemanticModel/IStatement.cs @@ -39,4 +39,6 @@ public interface IAttributeSource : IStatement { string AttributeName { get; } bool Active { get; } -} \ No newline at end of file +} + +public interface IUnexpandable : IStatement; \ No newline at end of file diff --git a/YangParser/SemanticModel/Identity.cs b/YangParser/SemanticModel/Identity.cs index b933136..c1cd379 100644 --- a/YangParser/SemanticModel/Identity.cs +++ b/YangParser/SemanticModel/Identity.cs @@ -29,26 +29,9 @@ public override string ToCode() { child.ToCode(); } - - var inherits = Children.OfType() - .Select(Selector).ToArray(); - var inheritance = inherits.Length == 0 ? string.Empty : " : " + string.Join(", ", inherits); return $""" - public interface I{MakeName(Argument)}BaseIdentity{inheritance}; {DescriptionString}{AttributeString} - public class I{MakeName(Argument)} : I{MakeName(Argument)}BaseIdentity; + public static string {MakeName(Argument)}Identity = "{Argument}"; """; } - - private string Selector(Base b) - { - if (b.Argument.Contains(":")) - { - var parts = b.Argument.Split(':'); - parts[parts.Length - 1] = "I" + MakeName(parts[parts.Length - 1]) + "BaseIdentity"; - return string.Join(":", parts); - } - - return "I" + MakeName(b.Argument) + "BaseIdentity"; - } } \ No newline at end of file diff --git a/YangParser/SemanticModel/Leaf.cs b/YangParser/SemanticModel/Leaf.cs index efed141..842a26d 100644 --- a/YangParser/SemanticModel/Leaf.cs +++ b/YangParser/SemanticModel/Leaf.cs @@ -61,11 +61,9 @@ public override string ToCode() var name = MakeName(Argument); var typeName = Type.Name; var definition = Type.Definition; - if (definition != null) + if (typeName == name) { - typeName += "Type"; - definition = definition.Replace(" " + Type.Name!, " " + typeName) - .Replace("(" + Type.Name!, "(" + typeName); + name += "Value"; } return $$""" diff --git a/YangParser/SemanticModel/LeafList.cs b/YangParser/SemanticModel/LeafList.cs index 6bd7b51..c8c41a9 100644 --- a/YangParser/SemanticModel/LeafList.cs +++ b/YangParser/SemanticModel/LeafList.cs @@ -49,13 +49,18 @@ public override string ToCode() var defaulting = defaultValue is null ? string.Empty : $"= {defaultValue}"; var name = MakeName(Argument); - var typeName = TypeName(Type); string addendum = string.Empty; + var typeName = Type.Name; + var definition = Type.Definition; + if (typeName == name) + { + name += "Value"; + } return $$""" {{addendum}} {{DescriptionString}}{{AttributeString}} - public{{KeywordString}}{{Type.Name}}[]? {{name}} { get; set; } {{defaulting}} - {{Type.Definition}} + public{{KeywordString}}{{typeName}}[]? {{name}} { get; set; } {{defaulting}} + {{definition}} {{Default?.Addendum}} """; } diff --git a/YangParser/SemanticModel/Module.cs b/YangParser/SemanticModel/Module.cs index 79ac677..82eff53 100644 --- a/YangParser/SemanticModel/Module.cs +++ b/YangParser/SemanticModel/Module.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using YangParser.Generator; using YangParser.Parser; namespace YangParser.SemanticModel; @@ -11,20 +12,41 @@ public Module(YangStatement statement) : base(statement) if (statement.Keyword != Keyword) throw new SemanticError($"Non-matching Keyword '{statement.Keyword}', expected {Keyword}", statement); XmlNamespace = Children.First(child => child is Namespace); - Usings = new(); - foreach (var import in Children.OfType()) + var localPrefix = this.GetChild().Argument; + var localNS = MakeNamespace(Argument) + ".YangNode."; + Usings = new() { - var use = MakeNamespace(import.Argument) + ".YangNode."; - var prefix = import.GetChild().Argument; - Usings[prefix] = use; - } + [localPrefix] = localNS + }; + Namespace = localNS; - Usings[this.GetChild().Argument] = MakeNamespace(Argument) + ".YangNode."; + foreach (var child in this.Unwrap()) + { + if (child is Uses use) + { + Uses.Add(use); + } + if (child is Grouping grouping) + { + Groupings.Add(grouping); + } + if (child is Augment augment) + { + Augments.Add(augment); + } + if (child is Import import) + { + Imports.Add(import); + var reference = MakeNamespace(import.Argument) + ".YangNode."; + var prefix = import.GetChild().Argument; + Usings[prefix] = reference; + } + } } public IStatement XmlNamespace { get; set; } public Dictionary Usings { get; } - + public string Namespace { get; private set; } public override ChildRule[] PermittedChildren { get; } = [ new ChildRule(AnyXml.Keyword, Cardinality.ZeroOrMore), @@ -43,7 +65,7 @@ public Module(YangStatement statement) : base(statement) new ChildRule(List.Keyword, Cardinality.ZeroOrMore), new ChildRule(Leaf.Keyword, Cardinality.ZeroOrMore), new ChildRule(LeafList.Keyword, Cardinality.ZeroOrMore), - new ChildRule(Namespace.Keyword, Cardinality.Required), + new ChildRule(SemanticModel.Namespace.Keyword, Cardinality.Required), new ChildRule(Notification.Keyword, Cardinality.ZeroOrMore), new ChildRule(Organization.Keyword), new ChildRule(Prefix.Keyword, Cardinality.Required), @@ -51,7 +73,7 @@ public Module(YangStatement statement) : base(statement) new ChildRule(Revision.Keyword, Cardinality.ZeroOrMore), new ChildRule(Rpc.Keyword, Cardinality.ZeroOrMore), new ChildRule(TypeDefinition.Keyword, Cardinality.ZeroOrMore), - new ChildRule(Uses.Keyword, Cardinality.ZeroOrMore), + new ChildRule(SemanticModel.Uses.Keyword, Cardinality.ZeroOrMore), new ChildRule(YangVersion.Keyword), ]; @@ -62,7 +84,7 @@ public override string ToCode() { string ns = MakeNamespace(Argument); - var nodes = Children.Select(child => child.ToCode()).ToArray(); + var nodes = Children.Select(child => child.ToCode()).Select(Indent).ToArray(); var raw = $$""" using System; using System.Collections.Generic; @@ -76,7 +98,7 @@ namespace {{ns}}; public class YangNode { {{string.Join("\n\t", Usings.Select(p => $"//Importing {p.Value} as {p.Key}"))}} - {{string.Join("\n\t", nodes.Select(Indent))}} + {{string.Join("\n\t", nodes)}} } """; raw = ReplacePrefixes(raw); @@ -94,26 +116,51 @@ private string ReplacePrefixes(string raw) return raw; } + private bool IsExpanded = false; + public void Expand() + { + if (IsExpanded) return; + foreach (var child in Children) + { + ExpandPrefixes(child); + } + IsExpanded = true; + } - public void ExpandPrefixes(IStatement statement) + private void ExpandPrefixes(IStatement statement) { - foreach (var prefix in Usings.Keys) + if (statement is not IUnexpandable) { - statement.Argument = statement.Argument.Replace(prefix + ":", Usings[prefix]); - if (statement is KeywordReference keywordReference) + + if (!statement.Argument.Contains(' ') && !statement.Argument.Contains('(') && !statement.Argument.Contains('[')) + //Only occurs in string arguments, which are unaffected by prefixes, + // and in function calls, which are unaffected by prefixes + // or in regex expressions, which are unaffected by prefixes { - if (keywordReference.ReferenceNamespace == prefix) + + var argPrefix = statement.Argument.Split(':'); + if (argPrefix.Length > 1 && argPrefix.Length < 3) //ignore cases where there are multiple colons, since that's an XML-namespace reference { - keywordReference.ReferenceNamespace = Usings[prefix]; + if (Usings.ContainsKey(argPrefix[0])) + { + statement.Argument = statement.Argument.Replace(argPrefix[0] + ":", Usings[argPrefix[0]]); + } + else + { + Log.Write($"No prefix found for {statement.Argument} in module {Argument}"); + } } } } - foreach (var child in statement.Children) { ExpandPrefixes(child); } } + public List Uses { get; } = []; + public List Groupings { get; } = []; + public List Augments { get; } = []; + public List Imports { get; } = []; // public string Capability // { @@ -134,5 +181,9 @@ public void ExpandPrefixes(IStatement statement) public interface ITopLevelStatement : IStatement { public Dictionary Usings { get; } - public void ExpandPrefixes(IStatement statement); + public void Expand(); + public List Uses { get; } + public List Groupings { get; } + public List Augments { get; } + public List Imports { get; } } \ No newline at end of file diff --git a/YangParser/SemanticModel/Output.cs b/YangParser/SemanticModel/Output.cs index 126630b..351b142 100644 --- a/YangParser/SemanticModel/Output.cs +++ b/YangParser/SemanticModel/Output.cs @@ -38,7 +38,7 @@ public override string ToCode() public class {{MakeName(Parent!.Argument)}}Output { {{string.Join("\n\t", Children.Select(child => Indent(child.ToCode())))}} - public static {{MakeName(Parent!.Argument)}}Output Parse(string xml) => new(); //TODO + public static {{MakeName(Parent!.Argument)}}Output Parse(string xml) => default!; //TODO } """; } diff --git a/YangParser/SemanticModel/Range.cs b/YangParser/SemanticModel/Range.cs index 945efad..c7e446c 100644 --- a/YangParser/SemanticModel/Range.cs +++ b/YangParser/SemanticModel/Range.cs @@ -25,7 +25,7 @@ public Range(YangStatement statement) : base(statement) { var components = entry.Replace("..", "|").Split('|'); var lower = double.Parse(components[0]); - var upper = components[1] == "max" ? double.MaxValue : double.Parse(components[1]); + var upper = components[1] == "max" ? double.NaN : double.Parse(components[1]); return (lower, upper); } @@ -35,7 +35,7 @@ public Range(YangStatement statement) : base(statement) public string GetConstructorValidation() { - var qualifiers = Bounds.Select(bound => $"input is >= {bound.Item1} and <= {bound.Item2}"); + var qualifiers = Bounds.Select(bound => double.IsNaN(bound.Item2) ? $"input >= {bound.Item1}" : $"input is >= {bound.Item1} and <= {bound.Item2}"); var all = string.Join("||", qualifiers); var hasError = this.TryGetChild(out var errorMessage); var hasTag = this.TryGetChild(out var appTag); diff --git a/YangParser/SemanticModel/Statement.cs b/YangParser/SemanticModel/Statement.cs index ca90b97..6f7c5f7 100644 --- a/YangParser/SemanticModel/Statement.cs +++ b/YangParser/SemanticModel/Statement.cs @@ -73,18 +73,13 @@ public static string MakeName(string argument) var replacement = component.Replace(".", "dot"); argument = argument.Replace(component, replacement); } - - var unexpanded = argument.Contains(':'); - var prefixing = argument.Split(':', '.'); - argument = prefixing.Last(); - - foreach (var section in argument.Split('-', ' ', '/', '.')) + var prefix = argument.Prefix(out var value); + var addColon = !prefix.Contains('.') && !string.IsNullOrWhiteSpace(prefix); + foreach (var section in value.Split('-', ' ', '/', '.')) { output.Append(Capitalize(section)); } - - prefixing[prefixing.Length - 1] = output.ToString(); - var result = string.Join(unexpanded ? ":" : ".", prefixing).Replace("*", "Any"); + var result = (prefix + (addColon ? ":" : "") + output.ToString()).Replace("*", "Any"); return result; } @@ -172,20 +167,20 @@ protected void ValidateChildren(YangStatement statement) switch (allowed.Cardinality) { case Cardinality.Required when occurances.TryGetValue(allowed.Keyword, out var count): - { - if (count == 1) break; - throw new SemanticError( - $"Child of type {allowed.Keyword} can only exist once in {GetType()}", statement); - } + { + if (count == 1) break; + throw new SemanticError( + $"Child of type {allowed.Keyword} can only exist once in {GetType()}", statement); + } case Cardinality.Required: throw new SemanticError( $"Child of type {allowed.Keyword} must exist in type {GetType()}", statement); case Cardinality.ZeroOrOne when occurances.TryGetValue(allowed.Keyword, out var count): - { - if (count <= 1) break; - throw new SemanticError( - $"Child of type {allowed.Keyword} can only exist up to once in {GetType()}", statement); - } + { + if (count <= 1) break; + throw new SemanticError( + $"Child of type {allowed.Keyword} can only exist up to once in {GetType()}", statement); + } case Cardinality.ZeroOrOne: case Cardinality.ZeroOrMore: break; diff --git a/YangParser/SemanticModel/StatementExtensions.cs b/YangParser/SemanticModel/StatementExtensions.cs index 0978d7e..d8a598a 100644 --- a/YangParser/SemanticModel/StatementExtensions.cs +++ b/YangParser/SemanticModel/StatementExtensions.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using System.Text; using YangParser.Generator; namespace YangParser.SemanticModel; @@ -80,16 +81,15 @@ public static IEnumerable Unwrap(this IStatement source) public static IStatement? FindSourceFor(this IStatement source, string prefix) { var module = source.Root(); - var imports = module?.Unwrap().OfType(); + var imports = module.Children.OfType().SelectMany(module => module.Imports); return imports?.FirstOrDefault(import => import.GetChild().Argument == prefix); } - public static Grouping? FindGrouping(this Uses source, IStatement module) + public static Grouping? FindGrouping(this Uses source, ITopLevelStatement module) { - var key = source.Argument.Split(':').Last(); - foreach (var child in module.Unwrap()) + _ = source.Argument.Prefix(out var key); + foreach (var grouping in module.Groupings) { - if (child is not Grouping grouping) continue; if (grouping.Argument == key) { return grouping; @@ -120,10 +120,9 @@ public static Grouping GetGrouping(this Uses use) use.Source); } - if (use.Argument.Contains(":")) //Is 'outside-of-tree' + var prefix = use.Argument.Prefix(out var name); + if (!string.IsNullOrEmpty(prefix)) //Is 'outside-of-tree' { - var components = use.Argument.Split(':'); - var prefix = components.First(); if (prefix == use.GetInheritedPrefix()) { var grouping = use.FindGrouping(module); @@ -136,34 +135,25 @@ public static Grouping GetGrouping(this Uses use) return grouping; } - else + else if (!prefix.Contains(".")) { var import = use.FindSourceFor(prefix); if (import is null) { throw new SemanticError( - $"Could not find an import statement with prefix {prefix} for 'uses {use.Argument}' in module '{module.Argument}'", - use.Source); - } - - var source = use.Root().Children.OfType() - .FirstOrDefault(m => m.Argument == import.Argument); - if (source is null) - { - throw new SemanticError($"Could not find a module with the key {import.Argument}", import.Source); - } - - var grouping = use.FindGrouping(source); - - if (grouping is null) - { - throw new SemanticError( - $"Could not find a grouping statement to use for 'uses {use.Argument}' in module '{source.Argument}' from prefix '{prefix}', import was {Statement.SingleLine(import.ToString())}", + $"Could not find an import statement with prefix '{prefix}' for 'uses {use.Argument}' in module '{module.Argument}'", use.Source); } + var source = use.Root().Children.OfType().FirstOrDefault(m => m.Argument == import.Argument) ?? throw new SemanticError($"Could not find a module with the key {import.Argument}", import.Source); + var grouping = use.FindGrouping(source) ?? throw new SemanticError($"Could not find a grouping statement to use for 'uses {use.Argument}' in module '{source.Argument}' from prefix '{prefix}', import was {Statement.SingleLine(import.ToString())}", use.Source); return grouping; } + else + { + var source = use.Root().Children.OfType().FirstOrDefault(m => m.Namespace == prefix) ?? throw new SemanticError($"Could not find a module with the key {prefix}", use.Source); + var grouping = use.FindGrouping(source); + } } var findGrouping = use.FindGrouping(module); @@ -184,34 +174,64 @@ public static void Expand(this Uses use) var parent = use.Parent; parent!.Replace(use, grouping.WithUse(use)); } + public static string Prefix(this string argument, out string name) + { + if (argument.Contains(":")) + { + var components = argument.Split(':'); + name = components[1]; + return components[0]; + } + else if (argument.Contains(".")) + { + var components = argument.Split('.'); + StringBuilder builder = new(); + for (int i = 0; i < components.Length - 1; i++) + { + builder.Append(components[i]); + builder.Append("."); + } + name = components.Last(); + return builder.ToString(); + + } + name = argument; + return string.Empty; - public static IStatement? FindReference(this IStatement source, string reference) + } + static Dictionary<(System.Type, string), IStatement?> _cache = []; + public static T? FindReference(this IStatement source, string reference) where T : IStatement { - var components = reference.Split(':'); - IStatement? module; - string name; - if (components.Length == 1) + var key = (typeof(T), reference); + if (_cache.TryGetValue(key, out var cached)) + { + return (T?)cached; + } + var prefix = reference.Prefix(out var name); + if (string.IsNullOrEmpty(prefix)) { - module = source.GetModule(); - name = components[0]; + var value = source.SearchDownwards(name) ?? source.SearchUpwards(name); + _cache[key] = value; + return value; } else { - var prefix = components[0]; if (source.GetInheritedPrefix() == prefix) { - module = source.GetModule(); - name = components[1]; + var value = source.SearchDownwards(name) ?? source.SearchUpwards(name); + _cache[key] = value; + return value; } - else + else if(!prefix.Contains('.')) { + IStatement? module; var import = source.FindSourceFor(prefix); var moduleName = import?.Argument; if (import is null) { Log.Write( $"Failed to find import for '{reference}' from module '{source.GetModule()?.Argument}', available imports are {string.Join(",", source.GetModule()?.Unwrap().OfType().Select(i => $"[{i.GetChild().Argument} -> {i.Argument}]") ?? [])}"); - return null; + return default; } module = source.Root().Children.FirstOrDefault(c => c.Argument == moduleName); @@ -219,17 +239,66 @@ public static void Expand(this Uses use) { Log.Write( $"Failed to find module for '{moduleName}'"); - return null; + return default; } - - name = components[1]; + var value = module.SearchDownwards(name) ?? module.SearchUpwards(name); + _cache[key] = value; + return value; + } + else + { + var module = source.Root().Children.OfType().FirstOrDefault(m => m.Namespace == prefix); + if (module is null) + { + Log.Write( + $"Failed to find module for '{prefix}'"); + return default; + } + var value = module.SearchDownwards(name) ?? module.SearchUpwards(name); + _cache[key] = value; + return value; + } + } + } + public static T? Ancestor(this IStatement source) where T : IStatement + { + while (source.Parent is not null) + { + source = source.Parent; + if (source is T t) + { + return t; } } + return default; + } + public static T? SearchDownwards(this IStatement source, string argument, params IStatement[] except) where T : IStatement + { + if (source.Argument == argument && source is T t && source is not DefaultValue) + { + return t; + } + foreach (var child in source.Children.Except(except)) + { + var result = SearchDownwards(child, argument); + if (result is not null) + { + return result; + } + } - var value = module?.Unwrap().FirstOrDefault(c => c.Argument == name && c is not DefaultValue && c != source); - value ??= source.Root().Unwrap() - .FirstOrDefault(c => c.Argument == name && c is not DefaultValue && c != source); - return value; + return default; + } + public static T? SearchUpwards(this IStatement source, string argument) where T : IStatement + { + if (source.Argument == argument && source is T t && source is not DefaultValue) + { + return t; + } + if (source.Parent is null) return default; + var result = SearchDownwards(source.Parent, argument, source); + if (result is not null) return result; + return source.Parent.SearchUpwards(argument); } } \ No newline at end of file diff --git a/YangParser/SemanticModel/Submodule.cs b/YangParser/SemanticModel/Submodule.cs index dc5d2b6..31459bb 100644 --- a/YangParser/SemanticModel/Submodule.cs +++ b/YangParser/SemanticModel/Submodule.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using YangParser.Generator; using YangParser.Parser; namespace YangParser.SemanticModel; @@ -18,8 +19,37 @@ public Submodule(YangStatement statement) : base(statement) var prefix = import.GetChild().Argument; Usings[prefix] = use; } + foreach (var child in this.Unwrap()) + { + if (child is Uses use) + { + Uses.Add(use); + } + if (child is Grouping grouping) + { + Groupings.Add(grouping); + } + if (child is Augment augment) + { + Augments.Add(augment); + } + if (child is Import import) + { + Imports.Add(import); + } + } } + private bool IsExpanded = false; + public void Expand() + { + if (IsExpanded) return; + foreach (var child in Children) + { + ExpandPrefixes(child); + } + IsExpanded = true; + } public override ChildRule[] PermittedChildren { get; } = [ @@ -47,7 +77,7 @@ public Submodule(YangStatement statement) : base(statement) new ChildRule(Revision.Keyword, Cardinality.ZeroOrMore), new ChildRule(Rpc.Keyword, Cardinality.ZeroOrMore), new ChildRule(TypeDefinition.Keyword, Cardinality.ZeroOrMore), - new ChildRule(Uses.Keyword, Cardinality.ZeroOrMore), + new ChildRule(SemanticModel.Uses.Keyword, Cardinality.ZeroOrMore), new ChildRule(YangVersion.Keyword, Cardinality.Required), ]; @@ -60,25 +90,34 @@ public override string ToCode() public Dictionary Usings { get; } - public void ExpandPrefixes(IStatement statement) + private void ExpandPrefixes(IStatement statement) { - foreach (var prefix in Usings.Keys) + + if (!statement.Argument.Contains(" ")) { - statement.Argument = statement.Argument.Replace(prefix + ":", Usings[prefix]); - if (statement is KeywordReference keywordReference) + var argPrefix = statement.Argument.Split(':'); + if (argPrefix.Length > 1 && argPrefix.Length < 3) //ignore cases where there are multiple colons, since that's an XML-namespace reference { - if (keywordReference.ReferenceNamespace == prefix) + if (Usings.ContainsKey(argPrefix[0])) + { + statement.Argument = statement.Argument.Replace(argPrefix[0] + ":", Usings[argPrefix[0]]); + } + else { - keywordReference.ReferenceNamespace = Usings[prefix]; + Log.Write($"No prefix found for {argPrefix[0]} in {Argument}"); } } } - foreach (var child in statement.Children) { ExpandPrefixes(child); } } + public List Uses { get; } = []; + public List Groupings { get; } = []; + public List Augments { get; } = []; + public List Imports { get; } = []; + } public class BelongsTo : Statement diff --git a/YangParser/SemanticModel/Type.cs b/YangParser/SemanticModel/Type.cs index 1765dbc..bf04d93 100644 --- a/YangParser/SemanticModel/Type.cs +++ b/YangParser/SemanticModel/Type.cs @@ -39,14 +39,12 @@ public string? Definition if (m_definition != null) return m_definition; if (!BuiltinTypeReference.IsBuiltin(this, out var typeName, out var definition)) { - if (this.FindReference(Argument) != null) return null; - m_name = MakeName(Parent!.Argument) + "Type"; + if (this.FindReference(Argument) != null) return null; m_definition = BuiltinTypeReference.DefaultPattern(this, [], [], TypeName(this), m_name); return m_definition; } - - m_definition = definition; m_name = typeName; + m_definition = definition; return m_definition; } } @@ -62,17 +60,16 @@ public string? Name m_name = typeName; return m_name; } - - var components = Argument.Split(':'); - string prefix = components.Length > 1 ? components[0] + ":" : string.Empty; - var reference = this.FindReference(Argument); + var prefix = Argument.Prefix(out var value); + var reference = this.FindReference(Argument); if (reference is TypeDefinition def) { - return prefix + MakeName(def.Argument); + if (prefix.Contains('.')) return prefix + MakeName(def.Argument); + else return prefix + ":" + MakeName(def.Argument); } - if (reference != null) return MakeName(Argument); - m_name = MakeName(Parent!.Argument) + "Type"; + if (reference != null && !BuiltinTypeReference.IsBuiltinKeyword(Argument)) return MakeName(Argument); + m_name = BuiltinTypeReference.TypeName(this); return m_name; } } diff --git a/YangParser/SemanticModel/Uses.cs b/YangParser/SemanticModel/Uses.cs index 37d998d..5a8fad4 100644 --- a/YangParser/SemanticModel/Uses.cs +++ b/YangParser/SemanticModel/Uses.cs @@ -1,10 +1,11 @@ using System; using System.Linq; +using YangParser.Generator; using YangParser.Parser; namespace YangParser.SemanticModel; -public class Uses : Statement +public class Uses : Statement, IUnexpandable { public Uses(YangStatement statement) : base(statement) { diff --git a/yang-compiler.sln.DotSettings.user b/yang-compiler.sln.DotSettings.user new file mode 100644 index 0000000..6af721b --- /dev/null +++ b/yang-compiler.sln.DotSettings.user @@ -0,0 +1,4 @@ + + <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from &lt;Tests&gt;\&lt;Compiler.Tests&gt;" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <Project Location="C:\Projects\yang\yang-compiler\Compiler.Tests" Presentation="&lt;Tests&gt;\&lt;Compiler.Tests&gt;" /> +</SessionState> \ No newline at end of file