Skip to content

Commit

Permalink
Handles both top level and node-data notifications
Browse files Browse the repository at this point in the history
  • Loading branch information
carl-andersson-at-westermo committed Jun 10, 2024
1 parent 30e399f commit d262a83
Show file tree
Hide file tree
Showing 11 changed files with 524 additions and 358 deletions.
283 changes: 203 additions & 80 deletions TestData/YangSource/ExampleYangServer.cs

Large diffs are not rendered by default.

8 changes: 0 additions & 8 deletions YangParser/Generator/YangGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -475,19 +475,11 @@ public static async Task ExpectOkRpcReply(XmlReader reader, int messageID)
throw new Exception($"Expected stream to start with a <rpc-reply> element with message id {messageID} & \"urn:ietf:params:xml:ns:netconf:base:1.0\" but got {reader.NodeType}: {reader.Name} in {reader.NamespaceURI}");
}
await reader.ReadAsync();
while(reader.NodeType == XmlNodeType.Whitespace)
{
await reader.ReadAsync();
}
if(reader.NodeType != XmlNodeType.Element || reader.Name != "ok")
{
throw new Exception($"Expected <ok/> element {reader.NodeType}: {reader.Name}");
}
await reader.ReadAsync();
while(reader.NodeType == XmlNodeType.Whitespace)
{
await reader.ReadAsync();
}
if(reader.NodeType != XmlNodeType.EndElement)
{
throw new Exception($"Expected </rpc-reply> closing element {reader.NodeType}: {reader.Name}");
Expand Down
117 changes: 3 additions & 114 deletions YangParser/SemanticModel/Action.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using YangParser.Parser;

namespace YangParser.SemanticModel;

public class Action : Statement, IXMLParseable
public class Action : NodeDataStatement, IXMLParseable
{
public Action(YangStatement statement) : base(statement)
{
Expand Down Expand Up @@ -33,104 +31,6 @@ public Action(YangStatement statement) : base(statement)
new ChildRule(TypeDefinition.Keyword, Cardinality.ZeroOrMore),
];

private string? _targetPath;
public string TargetPath => _targetPath ??= GetTargetPath();

private string GetTargetPath()
{
StringBuilder entries = new StringBuilder();
entries.Append(TargetName);
var parent = Parent;
while (parent is not null)
{
if (parent is Module) break;
if (parent is IXMLParseable parseable)
{
entries.Insert(0, parseable.TargetName! + "?.");
}
else if (parent is List list)
{
var content = entries.ToString();
entries.Insert(0,
list.TargetName +
$"?.FirstOrDefault({list.ClassName.ToLower()} => {list.ClassName.ToLower()}?.{content} != null)?.");
}
else
{
throw new SemanticError(
$"Could not describe full target path of action {Argument}: encountered unknown {parent.GetType().Name} {parent.Argument}",
parent.Source);
}

parent = parent.Parent;
}

return entries.ToString();
}

private IXMLParseable QualifiedRoot()
{
var parent = Parent;
while (parent is not Module && parent is not null)
{
if (parent.Parent is Module)
{
if (parent is not IXMLParseable parseable)
{
throw new SemanticError(
$"Action {Argument}: qualified root '{parent.GetType().Name} {parent.Argument}' was not Parseable or readable",
Source);
}

return parseable;
}

parent = parent.Parent;
}

if (parent is null or Module)
{
throw new SemanticError($"Action {Argument}: qualified root was null or a module", Source);
}

if (parent is not IXMLParseable xmlParseable)
{
throw new SemanticError(
$"Action {Argument}: qualified root '{parent.GetType().Name} {parent.Argument}' was not Parseable or readable",
Source);
}

return xmlParseable;
}

private string FullyQualifiedNamespace()
{
var parent = Parent;
List<string> classChain = new();
while (parent is not Module && parent is not null)
{
switch (parent)
{
case IXMLParseable xml:
classChain.Insert(0, xml.ClassName);
break;
case IXMLReadValue readValue:
classChain.Insert(0, readValue.ClassName);
break;
}

parent = parent.Parent;
}

if (parent is Module module)
{
classChain.Insert(0, "YangNode");
classChain.Insert(0, MakeNamespace(module.Argument));
}

return string.Join(".", classChain);
}

public string ServerDeclaration => ReturnType + " On" + MakeName(Argument) + $"({QualifiedRootName} root, " +
(Ingoing is null
? FullyQualifiedNamespace() + " target"
Expand Down Expand Up @@ -253,17 +153,6 @@ public class {{ClassName}}
""";
}

private string? _rootName;

public string QualifiedRootName =>
_rootName ??=
MakeNamespace(Root.GetModule()!.Argument) + ".YangNode." +
Root.ClassName;

private IXMLParseable? _root;
public IXMLParseable Root => _root ??= QualifiedRoot();


protected override void ValidateParent()
{
var parent = Parent;
Expand Down Expand Up @@ -304,7 +193,7 @@ protected override void ValidateParent()

private string? _target;

public string TargetName => _target ??= MakeName(Argument) + "ActionNode";
public override string TargetName => _target ??= MakeName(Argument) + "ActionNode";

public string WriteCall =>
Ingoing is not null
Expand Down Expand Up @@ -333,5 +222,5 @@ Ingoing is not null
: "await reader.ReadAsync();";

private string? _className;
public string ClassName => _className ??= MakeName(Argument) + "Class";
public override string ClassName => _className ??= MakeName(Argument) + "Class";
}
20 changes: 18 additions & 2 deletions YangParser/SemanticModel/Builtins/BuiltinTypeReference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,23 @@ public static bool IsBuiltinKeyword(string keyword)
return m_builtIns.Any(b => b.Name == keyword);
}

public static string Stringification(Type type, string targetName)
{
var toString = targetName + ".ToString()!";
switch (type.Argument)
{
case "bits":
case "enumeration":
case "identityref":
toString = $"GetEncodedValue({targetName})";

break;
default: break;
}

return toString;
}

public static string DefaultPattern(IStatement statement, IEnumerable<string> staticFields,
IEnumerable<string> constructorStatements,
string baseTypeName, string typeName)
Expand Down Expand Up @@ -152,7 +169,6 @@ public override string ToString()

private static string GetText(string argument) => $$"""
await reader.ReadAsync();
while(reader.NodeType == XmlNodeType.Whitespace) await reader.ReadAsync();
if(reader.NodeType != XmlNodeType.Text)
{
throw new Exception($"Expected token in ParseCall for '{{argument}}' to be text, but was '{reader.NodeType}'");
Expand All @@ -164,7 +180,6 @@ private static string EndElement(string argument) => $$"""
{
await reader.ReadAsync();
while(reader.NodeType == XmlNodeType.Whitespace) await reader.ReadAsync();
if(reader.NodeType != XmlNodeType.EndElement)
{
throw new Exception($"Expected token in ParseCall for '{{argument}}' to be an element closure, but was '{reader.NodeType}'");
Expand Down Expand Up @@ -290,6 +305,7 @@ public static string ValueParsing(Type type, string typeName)
var p = prefix.Contains('.') ? prefix : prefix + ":";
return $"return {p}Get{local}Value(value);";
}

return $"return {typeName}.Parse(value);";
default:
return $"return {typeName}.Parse(value);";
Expand Down
5 changes: 4 additions & 1 deletion YangParser/SemanticModel/Builtins/Union.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public class Union() : BuiltinType("union", s =>
List<string> types = [];
List<string> declarations = [];
List<string> switches = [];
List<string> stringifications = [];
foreach (var option in options)
{
var typeName = option.Name!;
Expand All @@ -24,6 +25,8 @@ public class Union() : BuiltinType("union", s =>
switches.Add($$"""
try { {{BuiltinTypeReference.ValueParsing(option, typeName)}} } catch(Exception ex) { errors += " " + ex.Message; }
""");
stringifications.Add(
$"if({VariableName(typeName)} is not null) return {BuiltinTypeReference.Stringification(option, VariableName(typeName))};");
}

var name = sourceName;
Expand All @@ -38,7 +41,7 @@ public class {{name}}
{{Statement.Indent(string.Join("\n", declarations))}}
public override string? ToString()
{
{{Statement.Indent(Statement.Indent(string.Join("\n", types.Select(typeName => $"if({VariableName(typeName)} is not null) return {VariableName(typeName)}.ToString();"))))}}
{{Statement.Indent(Statement.Indent(string.Join("\n", stringifications)))}}
return string.Empty;
}
public static {{name}} Parse(string value)
Expand Down
23 changes: 21 additions & 2 deletions YangParser/SemanticModel/CompilationUnit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public override string ToCode()

Argument = "root";
Dictionary<string, List<string>> ActionCases = [];
Dictionary<string, List<string>> NotificationCases = [];
foreach (var module in Children.OfType<Module>())
{
foreach (var action in module.Actions)
Expand All @@ -52,6 +53,23 @@ public override string ToCode()

list.Add(action.ReceiveCase);
}

foreach (var notification in module.Notifications.Where(n => n.IsTopLevel == false))
{
var caseName = $$"""
case "{{notification.Root.XmlObjectName}}":
{
var {{notification.Root.TargetName}} = await {{notification.QualifiedRootName}}.ParseAsync(reader);
""";
if (!NotificationCases.TryGetValue(caseName, out var list))
{
NotificationCases[caseName] = [];
list = NotificationCases[caseName];
}

list.Add(notification.ReceiveCase);
}
}

return $$"""
Expand Down Expand Up @@ -132,14 +150,15 @@ public static async Task ReceiveAction(this IYangServer server, XmlReader reader
await reader.ReadAsync();
switch(reader.Name)
{
{{Indent(Indent(Indent(string.Join("\n", ActionCases.Select(c => c.Key + Indent(string.Join("\n", c.Value)) + $"\n}}\nthrow new Exception(\"Could not find valid action\");")))))}}
{{Indent(Indent(Indent(string.Join("\n", ActionCases.Select(c => c.Key + Indent(string.Join("\n", c.Value)) + "\n}\nthrow new Exception(\"Could not find valid action\");")))))}}
}
}
public static async Task ReceiveNotification(this IYangServer server, XmlReader reader, DateTime eventTime)
{
switch(reader.Name)
{
{{Indent(Indent(Indent(string.Join("\n", Children.OfType<Module>().SelectMany(m => m.Notifications).Select(rpc => rpc.ReceiveCase).Distinct()))))}}
{{Indent(Indent(Indent(string.Join("\n", Children.OfType<Module>().SelectMany(m => m.Notifications.Where(n => n.IsTopLevel)).Select(rpc => rpc.ReceiveCase).Distinct()))))}}
{{Indent(Indent(Indent(string.Join("\n", NotificationCases.Select(c => c.Key + Indent(string.Join("\n", c.Value)) + "\n}\nthrow new Exception(\"Could not find valid notification\");")))))}}
}
}
}
Expand Down
Loading

0 comments on commit d262a83

Please sign in to comment.