Merge pull request #8 from Bertec/feature/codegen-fixes-1

Backend Fixes Part 1
This commit is contained in:
c80k 2019-08-29 21:22:43 +02:00 committed by GitHub
commit e68be281ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 997 additions and 247 deletions

View File

@ -11,6 +11,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Capnp.Net.Runtime.Tests.Std
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Capnp.Net.Runtime.Tests.Core21", "Capnp.Net.Runtime.Tests.Core21\Capnp.Net.Runtime.Tests.Core21.csproj", "{58E8FFC8-D207-4B0F-842A-58ED9D3D9EEF}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "capnpc-csharp.tests", "capnpc-csharp.tests\capnpc-csharp.tests.csproj", "{B77AC567-E232-4072-85C3-8689566BF3D4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -33,6 +35,10 @@ Global
{58E8FFC8-D207-4B0F-842A-58ED9D3D9EEF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{58E8FFC8-D207-4B0F-842A-58ED9D3D9EEF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{58E8FFC8-D207-4B0F-842A-58ED9D3D9EEF}.Release|Any CPU.Build.0 = Release|Any CPU
{B77AC567-E232-4072-85C3-8689566BF3D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B77AC567-E232-4072-85C3-8689566BF3D4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B77AC567-E232-4072-85C3-8689566BF3D4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B77AC567-E232-4072-85C3-8689566BF3D4}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -0,0 +1,123 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace capnpc_csharp.Tests.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("capnpc_csharp.Tests.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] schema_with_offsets_capnp {
get {
object obj = ResourceManager.GetObject("schema_with_offsets_capnp", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] UnitTest1_capnp {
get {
object obj = ResourceManager.GetObject("UnitTest1_capnp", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] UnitTest10_capnp {
get {
object obj = ResourceManager.GetObject("UnitTest10_capnp", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] UnitTest2_capnp {
get {
object obj = ResourceManager.GetObject("UnitTest2_capnp", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] UnitTest20_capnp {
get {
object obj = ResourceManager.GetObject("UnitTest20_capnp", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] UnitTest3_capnp {
get {
object obj = ResourceManager.GetObject("UnitTest3_capnp", resourceCulture);
return ((byte[])(obj));
}
}
}
}

View File

@ -0,0 +1,139 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="UnitTest10_capnp" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\UnitTest10.capnp.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="UnitTest20_capnp" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\UnitTest20.capnp.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="UnitTest3_capnp" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\UnitTest3.capnp.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="UnitTest2_capnp" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\UnitTest2.capnp.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="UnitTest1_capnp" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\UnitTest1.capnp.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="schema_with_offsets_capnp" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\schema-with-offsets.capnp.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
</root>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,9 @@
@0x93d3c4d19cd84a4c;
enum Enumerant {
byte @0;
}
struct Foo {
foo @0: UInt8;
}

View File

@ -0,0 +1,9 @@
@0xbbfd48ae4b99d012;
using Cxx = import "/capnp/c++.capnp";
$Cxx.namespace("Foo::Bar::Baz");
struct Outer {
inner @0: import "UnitTest10b.capnp".Inner ;
}

View File

@ -0,0 +1,7 @@
@0xaf95c1c78b01be97;
using Cxx = import "/capnp/c++.capnp";
$Cxx.namespace("Foo::Garf::Snarf");
struct Inner {}

View File

@ -0,0 +1,11 @@
@0xf6041efc5e8b1e59;
interface Interface1(V1) {}
interface Interface {
method @0 () -> (arg : AnyPointer);
method1 @1 () -> (arg : Interface1(AnyPointer));
method2 @2 () -> (arg : Interface2(AnyPointer));
}
interface Interface2(V2) {}

View File

@ -0,0 +1,5 @@
@0xcebb7a61a86a7492;
annotation anAnnotation(file): Text;
const aConstant :UInt16 = 42;

View File

@ -0,0 +1,6 @@
@0xb7158f7fa52b8db6;
using Cxx = import "/capnp/c++.capnp";
$Cxx.namespace("Foo.Bar.Baz");

View File

@ -0,0 +1,129 @@
using capnpc_csharp.Tests.Properties;
using Capnp;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace CapnpC
{
[TestClass]
public class UnitTests
{
[TestMethod]
public void Test00Enumerant()
{
var model = Load(Resources.UnitTest1_capnp);
Assert.AreEqual("@byte", GetTypeDef(0xc8461867c409f5d4, model).Enumerants[0].Literal);
}
[TestMethod]
public void Test01NestedClash()
{
var model = Load(Resources.UnitTest1_capnp);
var structFoo = GetTypeDef(0x93db6ba5509bac24, model);
var codeGen = NewGeneratorFor(model);
codeGen.Transform(model.FilesToGenerate.First());
var names = codeGen.GetNames();
var fieldName = names.GetCodeIdentifier(structFoo.Fields[0]).ToString();
Assert.AreEqual("Foo", structFoo.Name);
Assert.AreNotEqual(structFoo.Name, fieldName);
}
[TestMethod]
public void Test02ForwardInheritance()
{
var model = Load(Resources.UnitTest2_capnp);
var codeGen = NewGeneratorFor(model);
codeGen.Transform(model.FilesToGenerate.First());
// Should not throw
}
[TestMethod]
public void Test03NonGeneratedNodeSkip()
{
var model = Load(Resources.UnitTest3_capnp);
// Should not throw
}
[TestMethod]
public void Test10ImportedNamespaces()
{
var model = Load(Resources.UnitTest10_capnp);
var codeGen = NewGeneratorFor(model);
var genFile = model.FilesToGenerate.First();
var outerTypeDef = genFile.NestedTypes.First();
var outerType = Model.Types.FromDefinition(outerTypeDef);
var innerType = outerTypeDef.Fields[0].Type;
var innerTypeDef = innerType.Definition;
var names = codeGen.GetNames();
var outerNameSyntax = names.GetQName(outerType, outerTypeDef);
var innerNameSyntax = names.GetQName(innerType, outerTypeDef);
string[] outerNamespace = { "Foo", "Bar", "Baz" };
string[] innerNamespace = { "Foo", "Garf", "Snarf" };
CollectionAssert.AreEqual(outerNamespace, (outerTypeDef.DeclaringElement as Model.GenFile).Namespace);
CollectionAssert.AreEqual(innerNamespace, (innerType.Definition.DeclaringElement as Model.GenFile).Namespace);
string outerNSStr = String.Join('.', outerNamespace);
string innerNSStr = String.Join('.', innerNamespace);
Assert.AreEqual($"{outerNSStr}.Outer", outerNameSyntax.ToString());
Assert.AreEqual($"{innerNSStr}.Inner", innerNameSyntax.ToString());
}
[TestMethod]
public void Test20AnnotationAndConst()
{
var model = Load(Resources.UnitTest20_capnp);
var codeGen = NewGeneratorFor(model);
codeGen.Transform(model.FilesToGenerate.First());
// Should not throw
}
[TestMethod]
public void Test30SchemaCapnp()
{
var model = Load(Resources.schema_with_offsets_capnp);
var codeGen = NewGeneratorFor(model);
codeGen.Transform(model.FilesToGenerate.First());
// Should not throw
}
static Generator.CodeGenerator NewGeneratorFor(Model.SchemaModel model)
=> new Generator.CodeGenerator(model, new Generator.GeneratorOptions());
static Model.TypeDefinition GetTypeDef(ulong id, Model.SchemaModel model)
{
foreach (var defs in model.FilesToGenerate.Select(f => f.NestedTypes))
{
if (GetTypeDef(id, defs) is Model.TypeDefinition def) return def;
}
return null;
}
static Model.TypeDefinition GetTypeDef(ulong id, IEnumerable<Model.TypeDefinition> defs)
{
foreach (var def in defs)
{
if (def.Id == id) return def;
var sub = GetTypeDef(id, def.NestedTypes);
if (sub != null) return sub;
}
return null;
}
static Model.SchemaModel Load(byte[] data)
{
WireFrame segments;
var input = new MemoryStream(data);
using (input)
{
segments = Framing.ReadSegments(input);
}
var dec = DeserializerState.CreateRoot(segments);
var reader = Schema.CodeGeneratorRequest.Reader.Create(dec);
var model = Model.SchemaModel.Create(reader);
return model;
}
}
}

View File

@ -0,0 +1,40 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<RootNamespace>capnpc_csharp.Tests</RootNamespace>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="MSTest.TestAdapter" Version="1.4.0" />
<PackageReference Include="MSTest.TestFramework" Version="1.4.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\capnpc-csharp\capnpc-csharp.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Remove="*.cs" />
<Compile Include="UnitTests.cs" />
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@ -33,6 +33,8 @@
_interfaceGen = new InterfaceSnippetGen(_names);
}
internal GenNames GetNames() => _names;
IEnumerable<MemberDeclarationSyntax> TransformEnum(TypeDefinition def)
{
yield return _commonGen.MakeEnum(def);
@ -102,7 +104,7 @@
yield return _interfaceGen.MakePipeliningSupport(def);
}
if (def.NestedTypes.Count > 0)
if (def.NestedTypes.Any())
{
var ns = ClassDeclaration(
_names.MakeTypeName(def, NameUsage.Namespace).ToString())
@ -143,20 +145,11 @@
}
}
string Transform(GenFile file)
internal string Transform(GenFile file)
{
if (file.Namespace != null)
{
_names.TopNamespace = IdentifierName(MakeCamel(file.Namespace[0]));
NameSyntax topNamespace = GenNames.NamespaceName(file.Namespace) ?? _names.TopNamespace;
foreach (string name in file.Namespace.Skip(1))
{
var temp = IdentifierName(MakeCamel(name));
_names.TopNamespace = QualifiedName(_names.TopNamespace, temp);
}
}
var ns = NamespaceDeclaration(_names.TopNamespace);
var ns = NamespaceDeclaration(topNamespace);
foreach (var def in file.NestedTypes)
{

View File

@ -213,29 +213,24 @@ namespace CapnpC.Generator
}
}
NameSyntax GetQName(TypeDefinition def)
public static NameSyntax NamespaceName(string[] @namespace)
{
var stack = new Stack<SimpleNameSyntax>();
stack.Push(MakeGenericTypeName(def, NameUsage.Default));
while (def.DeclaringElement is TypeDefinition pdef)
NameSyntax ident = null;
if (@namespace != null)
{
stack.Push(MakeGenericTypeName(pdef, NameUsage.Namespace));
def = pdef;
ident = IdentifierName(SyntaxHelpers.MakeCamel(@namespace[0]));
foreach (string name in @namespace.Skip(1))
{
var temp = IdentifierName(SyntaxHelpers.MakeCamel(name));
ident = QualifiedName(ident, temp);
}
}
return ident;
}
var qtype = TopNamespace;
NameSyntax GetNamespaceFor(TypeDefinition def) => NamespaceName(def?.File?.Namespace);
foreach (var name in stack)
{
qtype = QualifiedName(qtype, name);
}
return qtype;
}
NameSyntax GetQName(Model.Type type, TypeDefinition scope)
internal NameSyntax GetQName(Model.Type type, TypeDefinition scope)
{
// FIXME: With the help of the 'scope' parameter we will be able to generate abbreviated
// qualified names. Unfortunately the commented approach is too naive. It will fail if
@ -262,7 +257,10 @@ namespace CapnpC.Generator
def = pdef;
}
var qtype = TopNamespace;
var qtype =
GetNamespaceFor(type.Definition)
?? GetNamespaceFor(scope)
?? TopNamespace;
foreach (var name in stack)
{
@ -563,6 +561,7 @@ namespace CapnpC.Generator
}
var typeNames = new HashSet<Name>(def.NestedTypes.Select(t => MakeTypeName(t)));
typeNames.Add(MakeTypeName(def));
foreach (var member in def.Fields)
{

View File

@ -0,0 +1,19 @@

namespace CapnpC.Model
{
class Annotation : IDefinition
{
public ulong Id { get; }
public TypeTag Tag { get => TypeTag.Annotation; }
public IHasNestedDefinitions DeclaringElement { get; }
public Type Type { get; set; }
public Annotation(ulong id, IHasNestedDefinitions parent)
{
Id = id;
DeclaringElement = parent;
parent.NestedDefinitions.Add(this);
}
}
}

View File

@ -0,0 +1,19 @@

namespace CapnpC.Model
{
class Constant : IDefinition
{
public ulong Id { get; }
public TypeTag Tag { get => TypeTag.Const; }
public IHasNestedDefinitions DeclaringElement { get; }
public Value Value { get; set; }
public Constant(ulong id, IHasNestedDefinitions parent)
{
Id = id;
DeclaringElement = parent;
parent.NestedDefinitions.Add(this);
}
}
}

View File

@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace CapnpC.Model
{
class DefinitionManager
{
readonly Dictionary<ulong, IDefinition> _id2def = new Dictionary<ulong, IDefinition>();
public GenFile CreateFile(ulong id)
=> CreateId<GenFile>(id, () => new GenFile(id));
public GenFile GetExistingFile(ulong id)
=> GetId<GenFile>(id, TypeTag.File);
public TypeDefinition CreateTypeDef(ulong id, TypeTag tag, IHasNestedDefinitions decl)
=> CreateId<TypeDefinition>(id, () => new TypeDefinition(tag, id, decl));
public TypeDefinition GetExistingTypeDef(ulong id, TypeTag tag)
{
var def = GetId<TypeDefinition>(id, tag);
if (def.Tag == TypeTag.Unknown) def.Tag = tag;
return def;
}
public Annotation CreateAnnotation(ulong id, IHasNestedDefinitions decl)
=> CreateId<Annotation>(id, () => new Annotation(id, decl));
public Annotation GetExistingAnnotation(ulong id)
=> GetId<Annotation>(id, TypeTag.Annotation);
public Constant CreateConstant(ulong id, IHasNestedDefinitions decl)
=> CreateId<Constant>(id, () => new Constant(id, decl));
public Constant GetExistingConstant(ulong id)
=> GetId<Constant>(id, TypeTag.Const);
public IDefinition GetExistingDef(ulong id, TypeTag tag)
=> GetId<IDefinition>(id, tag);
public IEnumerable<GenFile> Files
{
get => _id2def.Values.Where(d => d.Tag == TypeTag.File).Select(f => f as GenFile);
}
T CreateId<T>(ulong id, Func<IDefinition> creator) where T : class, IDefinition
{
if (_id2def.TryGetValue(id, out var d))
{
throw new ArgumentException(nameof(id), $"Attempting to redefine {d.Tag.ToString()} {id.StrId()} (as {nameof(T)}).");
}
var def = creator();
_id2def.Add(id, def);
return def as T;
}
T GetId<T>(ulong id, TypeTag tag) where T : IDefinition
{
if (!_id2def.TryGetValue(id, out var anyDef))
{
throw new ArgumentOutOfRangeException($"Attempting to retrieve nonexistent node {id.StrId()}.");
}
if (!(anyDef is T def) || (tag != TypeTag.Unknown && def.Tag != tag))
{
throw new ArgumentOutOfRangeException($"Attempting to retrieve {tag.ToString()} {id.StrId()}, but found {anyDef.Tag.ToString()} instead.");
}
return def;
}
}
}

View File

@ -2,8 +2,12 @@
{
class Enumerant
{
string _literal;
public TypeDefinition TypeDefinition { get; set; }
public string Literal { get; set; }
public string Literal {
get => _literal;
set => _literal = IdentifierRenamer.ToNonKeyword(value);
}
public ushort? Ordinal { get; set; }
public int CodeOrder { get; set; }
}

View File

@ -1,15 +1,23 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Collections.Generic;
namespace CapnpC.Model
{
class GenFile: IHasNestedDefinitions
class GenFile: IDefinition, IHasNestedDefinitions
{
public ulong Id { get; }
public TypeTag Tag { get => TypeTag.File; }
public IHasNestedDefinitions DeclaringElement { get; }
public string Name { get; set; }
public string[] Namespace { get; set; }
public List<TypeDefinition> NestedTypes { get; } = new List<TypeDefinition>();
public List<Value> Constants { get; } = new List<Value>();
public IEnumerable<TypeDefinition> NestedTypes { get => this.GetNestedTypes(); }
public ICollection<IDefinition> NestedDefinitions { get; } = new List<IDefinition>();
public ICollection<Constant> Constants { get; } = new List<Constant>();
public GenFile(ulong id)
{
Id = id;
}
}
}

View File

@ -0,0 +1,10 @@

namespace CapnpC.Model
{
interface IDefinition
{
ulong Id { get; }
TypeTag Tag { get; }
IHasNestedDefinitions DeclaringElement { get; }
}
}

View File

@ -1,10 +1,18 @@
using System.Collections.Generic;
using System.Linq;
namespace CapnpC.Model
{
interface IHasNestedDefinitions
{
List<TypeDefinition> NestedTypes { get; }
List<Value> Constants { get; }
IEnumerable<TypeDefinition> NestedTypes { get; }
ICollection<IDefinition> NestedDefinitions { get; }
ICollection<Constant> Constants { get; }
}
static partial class Extensions
{
public static IEnumerable<TypeDefinition> GetNestedTypes(this IHasNestedDefinitions def)
=> def.NestedDefinitions.Select(d => d as TypeDefinition).Where(d => d != null);
}
}

View File

@ -0,0 +1,22 @@
using Microsoft.CodeAnalysis.CSharp;
using System;
using System.Collections.Generic;
using System.Text;
namespace CapnpC.Model
{
public class IdentifierRenamer
{
public static bool IsAnyKeyword(string str)
{
return SyntaxFacts.GetKeywordKind(str) != SyntaxKind.None
|| SyntaxFacts.GetContextualKeywordKind(str) != SyntaxKind.None;
}
public static string ToNonKeyword(string str)
{
// Capnp schema identifiers should be already valid, but could be a keyword
if (IsAnyKeyword(str)) return $"@{str}";
return str;
}
}
}

View File

@ -9,30 +9,30 @@ namespace CapnpC.Model
class SchemaModel
{
readonly Schema.CodeGeneratorRequest.Reader _request;
readonly List<GenFile> _files = new List<GenFile>();
readonly Stack<IHasNestedDefinitions> _typeNest = new Stack<IHasNestedDefinitions>();
readonly TypeDefinitionManager _typeDefMgr = new TypeDefinitionManager();
Method _currentMethod;
readonly List<GenFile> _generatedFiles = new List<GenFile>();
readonly DefinitionManager _typeDefMgr = new DefinitionManager();
Dictionary<ulong, Schema.Node.Reader> _id2node;
readonly Dictionary<ulong, Schema.Node.Reader> _id2node = new Dictionary<ulong, Schema.Node.Reader>();
public SchemaModel(Schema.CodeGeneratorRequest.Reader request)
{
_request = request;
}
public IReadOnlyList<GenFile> FilesToGenerate => _files;
public IReadOnlyList<GenFile> FilesToGenerate => _generatedFiles;
Schema.Node.Reader? IdToNode(ulong id, bool mustExist)
{
if (_id2node.TryGetValue(id, out var node))
return node;
if (mustExist)
throw new InvalidSchemaException($"Node with ID {id.StrId()} is required by the codegen backend but is missing.");
return null;
}
Schema.Node.Reader IdToNode(ulong id)
{
try
{
return _id2node[id];
}
catch (KeyNotFoundException)
{
throw new InvalidSchemaException($"Node with ID {id} is missing");
}
return (Schema.Node.Reader)IdToNode(id, true);
}
void Build()
@ -42,69 +42,178 @@ namespace CapnpC.Model
throw new InvalidSchemaException("No nodes, nothing to generate");
}
try
foreach (var node in _request.Nodes)
{
_id2node = _request.Nodes.ToDictionary(n => n.Id);
if (_id2node.TryGetValue(node.Id, out var existingNode))
{
throw new InvalidSchemaException($"Node {node.StrId()} \"{node.DisplayName}\" has a duplicate ID, prior node was \"{existingNode.DisplayName}\"");
}
catch (ArgumentException)
{
throw new InvalidSchemaException("Nodes with duplicate IDs detected");
_id2node[node.Id] = node;
}
foreach (var reqFile in _request.RequestedFiles)
var requestedFiles = _request.RequestedFiles.ToDictionary(req => req.Id);
BuildPass1(requestedFiles);
BuildPass2(requestedFiles);
}
// First pass: create type definitions for each node.
struct Pass1State
{
var file = new GenFile()
public HashSet<ulong> unprocessedNodes;
public bool isGenerated;
public IHasNestedDefinitions parent;
}
void BuildPass1(Dictionary<ulong, Schema.CodeGeneratorRequest.RequestedFile.Reader> requestedFiles)
{
Name = reqFile.Filename
Pass1State state = new Pass1State()
{
unprocessedNodes = new HashSet<ulong>(_id2node.Keys)
};
_files.Add(file);
_typeNest.Push(file);
var fileNode = IdToNode(reqFile.Id);
if (!fileNode.IsFile)
throw new InvalidSchemaException("Expected a file node");
ProcessFile(fileNode);
_typeNest.Pop();
}
}
void ProcessFile(Schema.Node.Reader fileReader)
foreach (var node in _id2node.Values.Where(n => n.IsFile))
{
foreach (var annotation in fileReader.Annotations)
GenFile file;
state.isGenerated = requestedFiles.TryGetValue(node.Id, out var req);
state.parent = null;
if (state.isGenerated)
{
file = (GenFile)ProcessNodePass1(node.Id, req.Filename, state);
_generatedFiles.Add(file);
}
else
{
file = (GenFile)ProcessNodePass1(node.Id, node.DisplayName, state);
}
}
if (state.unprocessedNodes.Count != 0)
{
throw new InvalidSchemaException("Unreferenced nodes were present in the schema.");
}
}
IDefinition ProcessNodePass1(ulong id, string name, Pass1State state)
{
if (!(IdToNode(id, state.isGenerated) is Schema.Node.Reader node))
return null;
if (!state.unprocessedNodes.Remove(id))
return null;
IDefinition def = null;
bool processNestedNodes = false;
bool processFields = false;
bool processInterfaceMethods = false;
switch (node.GetKind())
{
case NodeKind.Annotation:
return _typeDefMgr.CreateAnnotation(id, state.parent);
case NodeKind.Const:
return _typeDefMgr.CreateConstant(id, state.parent);
case NodeKind.File:
if (state.parent != null)
throw new InvalidSchemaException("Did not expect file nodes to appear as nested nodes");
var file = _typeDefMgr.CreateFile(id);
file.Namespace = GetNamespaceAnnotation(node);
file.Name = name;
def = file;
processNestedNodes = true;
break;
case NodeKind.Enum:
break;
case NodeKind.Interface:
processNestedNodes = true;
processFields = true;
processInterfaceMethods = true;
break;
case NodeKind.Struct:
case NodeKind.Group:
processNestedNodes = true;
processFields = true;
break;
default:
throw new InvalidSchemaException($"Don't know how to process node {node.StrId()} \"{node.DisplayName}\"");
}
if (def == null)
{
var typeDef = _typeDefMgr.CreateTypeDef(id, node.GetTypeTag(), state.parent);
typeDef.Name = name;
def = typeDef;
}
state.parent = def as IHasNestedDefinitions;
if (processNestedNodes && node.NestedNodes != null)
foreach (var nested in node.NestedNodes)
{
ProcessNodePass1(nested.Id, nested.Name, state);
}
if (processFields && node.Fields != null)
foreach (var field in node.Fields.Where(f => f.IsGroup))
{
var group = IdToNode(field.Group_TypeId);
if (!group.IsStruct || !group.Struct_IsGroup)
{
throw new InvalidSchemaException($"Expected node with id {group.StrId()} to be a struct definition");
}
ProcessNodePass1(field.Group_TypeId, field.Name, state);
}
if (processInterfaceMethods && node.Interface_Methods != null)
foreach (var method in node.Interface_Methods)
{
var pnode = IdToNode(method.ParamStructType);
if (pnode.ScopeId == 0) ProcessNodePass1(pnode.Id, null, state); // Anonymous generated type
pnode = IdToNode(method.ResultStructType);
if (pnode.ScopeId == 0) ProcessNodePass1(pnode.Id, null, state); // Anonymous generated type
}
return def;
}
string[] GetNamespaceAnnotation(Schema.Node.Reader fileNode)
{
foreach (var annotation in fileNode.Annotations)
{
if (annotation.Id == 0xb9c6f99ebf805f2c) // Cxx namespace
{
((GenFile)_typeNest.Peek()).Namespace = annotation.Value.Text.Split("::");
return annotation.Value.Text.Split(new string[1] { "::" }, default);
}
}
return null;
}
foreach (var nestedNode in fileReader.NestedNodes)
// 2nd pass: Generate types based on definitions
struct Pass2State
{
var node = IdToNode(nestedNode.Id);
ProcessNode(node, nestedNode.Name);
}
public Method currentMethod;
public bool isGenerated;
public HashSet<ulong> processedNodes;
}
TypeDefinition GetOrCreateTypeDef(ulong id, TypeTag tag) => _typeDefMgr.GetOrCreate(id, tag);
TypeDefinition GetGroupTypeDef(ulong id, string name)
void BuildPass2(Dictionary<ulong, Schema.CodeGeneratorRequest.RequestedFile.Reader> requestedFiles)
{
var nodeReader = _id2node[id];
if (!nodeReader.IsStruct)
throw new InvalidSchemaException($"Expected node with id {id} to be a struct definition");
return ProcessStruct(nodeReader, name);
var state = new Pass2State() { processedNodes = new HashSet<ulong>() };
foreach (var file in _typeDefMgr.Files)
{
var node = IdToNode(file.Id);
state.isGenerated = requestedFiles.ContainsKey(file.Id);
ProcessNestedNodes(node.NestedNodes, state);
}
void ProcessBrand(Schema.Brand.Reader brandReader, Type type)
}
void ProcessNestedNodes(IEnumerable<Schema.Node.NestedNode.Reader> nestedNodes, Pass2State state)
{
foreach (var nestedNode in nestedNodes)
{
ProcessNode(nestedNode.Id, state);
}
}
void ProcessBrand(Schema.Brand.Reader brandReader, Type type, Pass2State state)
{
foreach (var scopeReader in brandReader.Scopes)
{
var whatToBind = GetOrCreateTypeDef(scopeReader.ScopeId, TypeTag.Unknown);
var whatToBind = ProcessTypeDef(scopeReader.ScopeId, state);
int index = 0;
switch (0)
@ -121,7 +230,7 @@ namespace CapnpC.Model
switch (0)
{
case 0 when bindingReader.IsType:
type.BindGenericParameter(typeParameter, ProcessType(bindingReader.Type));
type.BindGenericParameter(typeParameter, ProcessType(bindingReader.Type, state));
break;
case 0 when bindingReader.IsUnbound:
@ -146,7 +255,8 @@ namespace CapnpC.Model
}
}
}
Type ProcessType(Schema.Type.Reader typeReader)
Type ProcessType(Schema.Type.Reader typeReader, Pass2State state)
{
Type result;
@ -159,7 +269,7 @@ namespace CapnpC.Model
return Types.FromParameter(
new GenericParameter()
{
DeclaringEntity = GetOrCreateTypeDef(typeReader.AnyPointer_Parameter_ScopeId, TypeTag.Unknown),
DeclaringEntity = ProcessTypeDef(typeReader.AnyPointer_Parameter_ScopeId, state),
Index = typeReader.AnyPointer_Parameter_ParameterIndex
});
@ -167,7 +277,7 @@ namespace CapnpC.Model
return Types.FromParameter(
new GenericParameter()
{
DeclaringEntity = _currentMethod ?? throw new InvalidOperationException("current method not set"),
DeclaringEntity = state.currentMethod ?? throw new InvalidOperationException("current method not set"),
Index = typeReader.AnyPointer_ImplicitMethodParameter_ParameterIndex
});
@ -205,7 +315,7 @@ namespace CapnpC.Model
return Types.F64;
case 0 when typeReader.IsEnum:
return Types.FromDefinition(GetOrCreateTypeDef(typeReader.Enum_TypeId, TypeTag.Enum));
return Types.FromDefinition(ProcessTypeDef(typeReader.Enum_TypeId, state, TypeTag.Enum));
case 0 when typeReader.IsFloat32:
return Types.F32;
@ -223,16 +333,16 @@ namespace CapnpC.Model
return Types.S8;
case 0 when typeReader.IsInterface:
result = Types.FromDefinition(GetOrCreateTypeDef(typeReader.Interface_TypeId, TypeTag.Interface));
ProcessBrand(typeReader.Interface_Brand, result);
result = Types.FromDefinition(ProcessTypeDef(typeReader.Interface_TypeId, state, TypeTag.Interface));
ProcessBrand(typeReader.Interface_Brand, result, state);
return result;
case 0 when typeReader.IsList:
return Types.List(ProcessType(typeReader.List_ElementType));
return Types.List(ProcessType(typeReader.List_ElementType, state));
case 0 when typeReader.IsStruct:
result = Types.FromDefinition(GetOrCreateTypeDef(typeReader.Struct_TypeId, TypeTag.Struct));
ProcessBrand(typeReader.Struct_Brand, result);
result = Types.FromDefinition(ProcessTypeDef(typeReader.Struct_TypeId, state, TypeTag.Struct));
ProcessBrand(typeReader.Struct_Brand, result, state);
return result;
case 0 when typeReader.IsText:
@ -365,7 +475,7 @@ namespace CapnpC.Model
return value;
}
void ProcessFields(Schema.Node.Reader reader, TypeDefinition declaringType, List<Field> fields)
void ProcessFields(Schema.Node.Reader reader, TypeDefinition declaringType, List<Field> fields, Pass2State state)
{
if (reader.Fields == null)
{
@ -389,15 +499,15 @@ namespace CapnpC.Model
switch (0)
{
case 0 when fieldReader.IsGroup:
field.Type = Types.FromDefinition(GetGroupTypeDef(
fieldReader.Group_TypeId, fieldReader.Name));
var def = ProcessTypeDef(fieldReader.Group_TypeId, state, TypeTag.Group);
field.Type = Types.FromDefinition(def);
break;
case 0 when fieldReader.IsSlot:
field.DefaultValue = ProcessValue(fieldReader.Slot_DefaultValue);
field.DefaultValueIsExplicit = fieldReader.Slot_HadExplicitDefault;
field.Offset = fieldReader.Slot_Offset;
field.Type = ProcessType(fieldReader.Slot_Type);
field.Type = ProcessType(fieldReader.Slot_Type, state);
field.DefaultValue.Type = field.Type;
break;
@ -411,7 +521,7 @@ namespace CapnpC.Model
}
}
void ProcessInterfaceOrStructTail(TypeDefinition def, Schema.Node.Reader reader)
TypeDefinition ProcessInterfaceOrStructTail(TypeDefinition def, Schema.Node.Reader reader, Pass2State state)
{
def.IsGeneric = reader.IsGeneric;
@ -423,18 +533,9 @@ namespace CapnpC.Model
}
}
_typeNest.Push(def);
ProcessNestedNodes(reader.NestedNodes, state);
if (reader.NestedNodes != null)
{
foreach (var nestedReader in reader.NestedNodes)
{
var node = IdToNode(nestedReader.Id);
ProcessNode(node, nestedReader.Name);
}
}
ProcessFields(reader, def, def.Fields);
ProcessFields(reader, def, def.Fields, state);
if (reader.IsInterface)
{
@ -450,12 +551,12 @@ namespace CapnpC.Model
{
method.GenericParameters.Add(implicitParameterReader.Name);
}
_currentMethod = method;
state.currentMethod = method;
def.Methods.Add(method);
var paramNode = IdToNode(methodReader.ParamStructType);
var paramType = ProcessParameterList(paramNode, methodReader.ParamBrand, method.Params);
var paramType = ProcessParameterList(paramNode, methodReader.ParamBrand, method.Params, state);
if (paramType != null)
{
paramType.SpecialName = SpecialName.MethodParamsStruct;
@ -470,7 +571,7 @@ namespace CapnpC.Model
method.ParamsStruct.InheritFreeParameters(method);
var resultNode = IdToNode(methodReader.ResultStructType);
var resultType = ProcessParameterList(resultNode, methodReader.ResultBrand, method.Results);
var resultType = ProcessParameterList(resultNode, methodReader.ResultBrand, method.Results, state);
if (resultType != null)
{
resultType.SpecialName = SpecialName.MethodResultStruct;
@ -485,24 +586,13 @@ namespace CapnpC.Model
method.ResultStruct.InheritFreeParameters(method);
}
_currentMethod = null;
state.currentMethod = null;
}
return def;
}
_typeNest.Pop();
}
TypeDefinition ProcessStruct(Schema.Node.Reader structReader, string name)
TypeDefinition ProcessStruct(Schema.Node.Reader structReader, TypeDefinition def, Pass2State state)
{
var def = GetOrCreateTypeDef(
structReader.Id,
structReader.Struct_IsGroup ? TypeTag.Group : TypeTag.Struct);
def.DeclaringElement = _typeNest.Peek();
if (structReader.Struct_IsGroup)
((TypeDefinition)def.DeclaringElement).NestedGroups.Add(def);
else
def.DeclaringElement.NestedTypes.Add(def);
def.Name = name;
def.StructDataWordCount = structReader.Struct_DataWordCount;
def.StructPointerCount = structReader.Struct_PointerCount;
@ -513,12 +603,10 @@ namespace CapnpC.Model
16u * structReader.Struct_DiscriminantOffset);
}
ProcessInterfaceOrStructTail(def, structReader);
return def;
return ProcessInterfaceOrStructTail(def, structReader, state);
}
TypeDefinition ProcessParameterList(Schema.Node.Reader reader, Schema.Brand.Reader brandReader, List<Field> list)
TypeDefinition ProcessParameterList(Schema.Node.Reader reader, Schema.Brand.Reader brandReader, List<Field> list, Pass2State state)
{
//# If a named parameter list was specified in the method
//# declaration (rather than a single struct parameter type) then a corresponding struct type is
@ -533,58 +621,38 @@ namespace CapnpC.Model
throw new InvalidSchemaException("Expected a struct");
}
var def = ProcessTypeDef(reader.Id, state, TypeTag.Struct);
if (reader.ScopeId == 0)
{
// Auto-generated => Named parameter list
ProcessFields(reader, null, list);
return ProcessStruct(reader, null);
foreach (var field in def.Fields) list.Add(field);
return def;
}
else
{
// Single, anonymous, struct-typed parameter
var def = GetOrCreateTypeDef(reader.Id, TypeTag.Struct);
var type = Types.FromDefinition(def);
ProcessBrand(brandReader, type);
ProcessBrand(brandReader, type, state);
var anon = new Field() { Type = type };
list.Add(anon);
return null;
}
}
TypeDefinition ProcessInterface(Schema.Node.Reader ifaceReader, string name)
TypeDefinition ProcessInterface(Schema.Node.Reader ifaceReader, TypeDefinition def, Pass2State state)
{
var def = GetOrCreateTypeDef(
ifaceReader.Id,
TypeTag.Interface);
def.DeclaringElement = _typeNest.Peek();
def.DeclaringElement.NestedTypes.Add(def);
def.Name = name;
foreach (var superClassReader in ifaceReader.Interface_Superclasses)
{
var superClass = GetOrCreateTypeDef(
superClassReader.Id,
TypeTag.Interface);
var superClass = ProcessTypeDef(superClassReader.Id, state, TypeTag.Interface);
def.Superclasses.Add(Types.FromDefinition(superClass));
}
ProcessInterfaceOrStructTail(def, ifaceReader);
return def;
return ProcessInterfaceOrStructTail(def, ifaceReader, state);
}
void ProcessEnum(Schema.Node.Reader enumReader, string name)
TypeDefinition ProcessEnum(Schema.Node.Reader enumReader, TypeDefinition def, Pass2State state)
{
var def = GetOrCreateTypeDef(enumReader.Id, TypeTag.Enum);
def.DeclaringElement = _typeNest.Peek();
def.DeclaringElement.NestedTypes.Add(def);
def.Name = name;
_typeNest.Push(def);
foreach (var fieldReader in enumReader.Enumerants)
{
var field = new Enumerant()
@ -601,46 +669,52 @@ namespace CapnpC.Model
def.Enumerants.Add(field);
}
_typeNest.Pop();
return def;
}
void ProcessConst(Schema.Node.Reader constReader, string name)
Constant ProcessConst(Schema.Node.Reader constReader, Constant @const, Pass2State state)
{
var value = ProcessValue(constReader.Const_Value);
value.Type = ProcessType(constReader.Const_Type);
_typeNest.Peek().Constants.Add(value);
value.Type = ProcessType(constReader.Const_Type, state);
@const.Value = value;
return @const;
}
void ProcessNode(Schema.Node.Reader node, string name)
TypeDefinition ProcessTypeDef(ulong id, Pass2State state, TypeTag tag = default)
{
switch (0)
var def = ProcessNode(id, state, tag);
var typeDef = def as TypeDefinition;
if (def == null)
throw new ArgumentException(
$"Expected node {id.StrId()} to be a TypeDefinition but got {def.GetType().Name} instead.",
nameof(id));
return typeDef;
}
IDefinition ProcessNode(ulong id, Pass2State state, TypeTag tag = default)
{
case 0 when node.IsAnnotation:
break;
case 0 when node.IsConst:
ProcessConst(node, name);
break;
case 0 when node.IsEnum:
ProcessEnum(node, name);
break;
case 0 when node.IsFile:
throw new InvalidSchemaException("Did not expect file nodes to appear as nested nodes");
case 0 when node.IsInterface:
ProcessInterface(node, name);
break;
case 0 when node.IsStruct:
ProcessStruct(node, name);
break;
if (!(IdToNode(id, state.isGenerated) is Schema.Node.Reader node)) return null;
var kind = node.GetKind();
if (tag == TypeTag.Unknown) tag = kind.GetTypeTag();
var def = _typeDefMgr.GetExistingDef(id, tag);
if (state.processedNodes.Contains(id)) return def;
state.processedNodes.Add(id);
switch (def)
{
case Annotation annotation:
return annotation;
case Constant constant:
def.DeclaringElement.Constants.Add(ProcessConst(node, constant, state));
return def;
case TypeDefinition typeDef when kind == NodeKind.Enum:
return ProcessEnum(node, typeDef, state);
case TypeDefinition typeDef when kind == NodeKind.Interface:
return ProcessInterface(node, typeDef, state);
case TypeDefinition typeDef when kind == NodeKind.Struct || kind == NodeKind.Group:
return ProcessStruct(node, typeDef, state);
default:
throw new InvalidSchemaException($"Don't know how to process node {node.DisplayName}");
throw new InvalidProgramException($"An unexpected node {node.StrId()} was found during the 2nd schema model building pass.");
}
}
@ -651,4 +725,55 @@ namespace CapnpC.Model
return model;
}
}
public enum NodeKind
{
Unknown,
Annotation,
Const,
Enum,
File,
Interface,
Struct,
Group
}
public static class SchemaExtensions
{
public static string GetName(this Schema.Node.Reader node)
=> node.DisplayName.Substring((int)node.DisplayNamePrefixLength);
public static string StrId(this Schema.Node.Reader node)
=> $"0x{node.Id.ToString("X")}";
public static string StrId(this ulong nodeId)
=> $"0x{nodeId.ToString("X")}";
public static NodeKind GetKind(this Schema.Node.Reader node)
{
if (node.IsStruct)
return node.Struct_IsGroup ? NodeKind.Group : NodeKind.Struct;
if (node.IsInterface) return NodeKind.Interface;
if (node.IsEnum) return NodeKind.Enum;
if (node.IsConst) return NodeKind.Const;
if (node.IsAnnotation) return NodeKind.Annotation;
if (node.IsFile) return NodeKind.File;
return NodeKind.Unknown;
}
internal static TypeTag GetTypeTag(this NodeKind kind)
{
switch (kind)
{
case NodeKind.Enum: return TypeTag.Enum;
case NodeKind.Interface: return TypeTag.Interface;
case NodeKind.Struct: return TypeTag.Struct;
case NodeKind.Group: return TypeTag.Group;
default: return TypeTag.Unknown;
}
}
internal static TypeTag GetTypeTag(this Schema.Node.Reader node)
=> node.GetKind().GetTypeTag();
}
}

View File

@ -9,9 +9,16 @@ namespace CapnpC.Model
{
class Type: AbstractType
{
// Representation of a type expression in the schema language
public TypeDefinition Definition { get; set; }
// The model for all nodes that are not file nodes - they define types
public GenericParameter Parameter { get; set; }
// A reference to type parameter in this scope
public Type ElementType { get; set; }
// The type of a list element, if this is a list.
readonly Dictionary<GenericParameter, Type> _parameterBindings =
new Dictionary<GenericParameter, Type>();

View File

@ -2,7 +2,7 @@
using System.Linq;
namespace CapnpC.Model
{
class TypeDefinition : AbstractType, IHasNestedDefinitions, IHasGenericParameters
class TypeDefinition : AbstractType, IDefinition, IHasNestedDefinitions, IHasGenericParameters
{
public class DiscriminationInfo
{
@ -16,23 +16,30 @@ namespace CapnpC.Model
public uint TagOffset { get; }
}
public TypeDefinition(TypeTag tag, ulong id)
public TypeDefinition(TypeTag tag, ulong id, IHasNestedDefinitions parent)
{
Tag = tag;
Id = id;
DeclaringElement = parent;
if (tag == TypeTag.Group)
((TypeDefinition)parent).NestedGroups.Add(this);
else
parent.NestedDefinitions.Add(this);
}
public ulong Id { get; }
public IHasNestedDefinitions DeclaringElement { get; set; }
public IHasNestedDefinitions DeclaringElement { get; }
public Method UsingMethod { get; set; }
public string Name { get; set; }
public SpecialName SpecialName { get; set; }
public DiscriminationInfo UnionInfo { get; set; }
public new List<Field> Fields => base.Fields;
public List<Enumerant> Enumerants { get; } = new List<Enumerant>();
public List<TypeDefinition> NestedTypes { get; } = new List<TypeDefinition>();
public ICollection<IDefinition> NestedDefinitions { get; } = new List<IDefinition>();
public IEnumerable<TypeDefinition> NestedTypes { get => this.GetNestedTypes(); }
public List<TypeDefinition> NestedGroups { get; } = new List<TypeDefinition>();
public List<Value> Constants { get; } = new List<Value>();
public ICollection<Constant> Constants { get; } = new List<Constant>();
public List<Method> Methods { get; } = new List<Method>();
public List<Type> Superclasses { get; } = new List<Type>();
public List<string> GenericParameters { get; } = new List<string>();
@ -54,6 +61,16 @@ namespace CapnpC.Model
}
}
public GenFile File
{
get
{
IHasNestedDefinitions cur = this;
while (cur is TypeDefinition def) cur = def.DeclaringElement;
return cur as GenFile;
}
}
public IEnumerable<GenericParameter> AllTypeParameters
{
get

View File

@ -1,38 +0,0 @@
using System;
using System.Collections.Generic;
namespace CapnpC.Model
{
class TypeDefinitionManager
{
readonly Dictionary<ulong, TypeDefinition> _id2def =
new Dictionary<ulong, TypeDefinition>();
public TypeDefinition GetOrCreate(ulong id, TypeTag tag)
{
if (_id2def.TryGetValue(id, out var def))
{
if (def.Tag == TypeTag.Unknown)
{
def.Tag = tag;
}
else if (tag != TypeTag.Unknown && def.Tag != tag)
{
throw new ArgumentOutOfRangeException(nameof(tag), "Type tag does not match existing type");
}
}
else
{
def = new TypeDefinition(tag, id);
_id2def.Add(id, def);
}
return def;
}
public TypeDefinition GetExisting(ulong id)
{
return _id2def[id];
}
}
}

View File

@ -28,6 +28,9 @@
Group,
Interface,
Enum,
AnyEnum
AnyEnum,
Const,
Annotation,
File
}
}

View File

@ -1,6 +1,9 @@
using Capnp;
using System;
using System.IO;
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleToAttribute("capnpc-csharp.tests")]
namespace CapnpC
{

View File

@ -1,6 +1,6 @@
# F:/Downloads/capnproto-c++-win32-0.7.0/capnproto-c++-0.7.0/src/capnp/schema-priv.capnp
# schema.capnp
@0xa93fc509624c72d9;
$import "/F:/Downloads/capnproto-c++-win32-0.7.0/capnproto-c++-0.7.0/src/capnp/c++.capnp".namespace("capnp::schema");
$import "/capnp/c++.capnp".namespace("capnp::schema");
struct Node @0xe682ab4cf923a417 { # 40 bytes, 6 ptrs
id @0 :UInt64; # bits[0, 64)
displayName @1 :Text; # ptr[0]