Fix attempted use of incomplete dependent types in schema model.

Resolve node hierarchy and names in a new 1st pass before generating types in pass2. Consider imported files.
This commit is contained in:
Kuba Ober 2019-08-29 09:45:48 -04:00
parent a0fcb93676
commit 92a7c03cb2
3 changed files with 308 additions and 168 deletions

View File

@ -9,19 +9,18 @@ namespace CapnpC.Model
class SchemaModel class SchemaModel
{ {
readonly Schema.CodeGeneratorRequest.Reader _request; readonly Schema.CodeGeneratorRequest.Reader _request;
readonly List<GenFile> _files = new List<GenFile>(); readonly List<GenFile> _generatedFiles = new List<GenFile>();
readonly Stack<IHasNestedDefinitions> _typeNest = new Stack<IHasNestedDefinitions>(); Dictionary<ulong, IHasNestedDefinitions> _allDefinitions = new Dictionary<ulong, IHasNestedDefinitions>();
readonly TypeDefinitionManager _typeDefMgr = new TypeDefinitionManager(); readonly TypeDefinitionManager _typeDefMgr = new TypeDefinitionManager();
Method _currentMethod;
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) public SchemaModel(Schema.CodeGeneratorRequest.Reader request)
{ {
_request = request; _request = request;
} }
public IReadOnlyList<GenFile> FilesToGenerate => _files; public IReadOnlyList<GenFile> FilesToGenerate => _generatedFiles;
Schema.Node.Reader IdToNode(ulong id) Schema.Node.Reader IdToNode(ulong id)
{ {
@ -42,69 +41,196 @@ namespace CapnpC.Model
throw new InvalidSchemaException("No nodes, nothing to generate"); 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))
}
catch (ArgumentException)
{
throw new InvalidSchemaException("Nodes with duplicate IDs detected");
}
foreach (var reqFile in _request.RequestedFiles)
{
var file = new GenFile()
{ {
Name = reqFile.Filename throw new InvalidSchemaException($"Node {node.StrId()} \"{node.DisplayName}\" has a duplicate ID, prior node was \"{existingNode.DisplayName}\"");
}; }
_id2node[node.Id] = node;
}
_files.Add(file); BuildPass1();
_typeNest.Push(file); BuildPass2();
}
var fileNode = IdToNode(reqFile.Id); // First pass: create type definitions for each node.
if (!fileNode.IsFile) struct Pass1State
throw new InvalidSchemaException("Expected a file node"); {
public HashSet<ulong> unprocessedNodes;
public bool isGenerated;
public IHasNestedDefinitions parent;
}
ProcessFile(fileNode); void BuildPass1()
{
_typeNest.Pop(); Pass1State state = new Pass1State()
{
unprocessedNodes = new HashSet<ulong>(_id2node.Keys)
};
var requestedFiles = _request.RequestedFiles.ToDictionary(req => req.Id);
foreach (var node in _id2node.Values.Where(n => n.IsFile))
{
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);
}
_allDefinitions.Add(node.Id, file);
}
if (state.unprocessedNodes.Count != 0)
{
throw new InvalidSchemaException("Unreferenced nodes were present in the schema.");
} }
} }
void ProcessFile(Schema.Node.Reader fileReader) TypeDefinition CreateTypeDef(Schema.Node.Reader node, IHasNestedDefinitions parent)
{ {
foreach (var annotation in fileReader.Annotations) var kind = node.GetKind();
var def = _typeDefMgr.Create(node.Id, kind.GetTypeTag());
def.DeclaringElement = parent;
if (kind == NodeKind.Group)
((TypeDefinition)parent).NestedGroups.Add(def);
else
parent.NestedTypes.Add(def);
return def;
}
IHasNestedDefinitions ProcessNodePass1(ulong id, string name, Pass1State state)
{
if (!_id2node.TryGetValue(id, out var node))
{
if (!state.isGenerated) return null;
throw new InvalidSchemaException($"The node {id.StrId()} was necessary for backend codegen but is missing.");
}
if (!state.unprocessedNodes.Remove(id))
throw new InvalidSchemaException($"The node \"{node.DisplayName}\" {node.StrId()} has been declared recursively.");
GenFile file = null;
bool processNestedNodes = false;
bool processFields = false;
bool processInterfaceMethods = false;
switch (node.GetKind())
{
case NodeKind.Annotation:
case NodeKind.Const:
// A dummy TypeDefinition is created to node hierarchy
break;
case NodeKind.File:
if (state.parent != null)
throw new InvalidSchemaException("Did not expect file nodes to appear as nested nodes");
file = new GenFile();
file.Namespace = null; // TODO
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}\"");
}
TypeDefinition def = null;
if (file != null)
{
state.parent = file;
file.Name = name;
}
else
{
state.parent = def = CreateTypeDef(node, state.parent);
def.Name = name;
}
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 state.parent;
}
// 2nd pass: Generate types based on definitions
struct Pass2State
{
public Method currentMethod;
public HashSet<ulong> processedNodes;
}
void BuildPass2()
{
var files = _allDefinitions.Select(d => (Id: d.Key, File: d.Value as GenFile)).Where(d => d.File != null);
var state = new Pass2State() { processedNodes = new HashSet<ulong>() };
foreach (var file in files)
{
var node = IdToNode(file.Id);
ProcessFileAnnotations(node, file.File);
ProcessNestedNodes(node.NestedNodes, state);
}
}
void ProcessNestedNodes(IEnumerable<Schema.Node.NestedNode.Reader> nestedNodes, Pass2State state)
{
foreach (var nestedNode in nestedNodes)
{
ProcessNode(nestedNode.Id, state);
}
}
void ProcessFileAnnotations(Schema.Node.Reader fileNode, GenFile fileElement)
{
foreach (var annotation in fileNode.Annotations)
{ {
if (annotation.Id == 0xb9c6f99ebf805f2c) // Cxx namespace if (annotation.Id == 0xb9c6f99ebf805f2c) // Cxx namespace
{ {
((GenFile)_typeNest.Peek()).Namespace = annotation.Value.Text.Split("::"); fileElement.Namespace = annotation.Value.Text.Split(new string[1] { "::" }, default);
} }
} }
foreach (var nestedNode in fileReader.NestedNodes)
{
var node = IdToNode(nestedNode.Id);
ProcessNode(node, nestedNode.Name);
}
} }
TypeDefinition GetOrCreateTypeDef(ulong id, TypeTag tag) => _typeDefMgr.GetOrCreate(id, tag); void ProcessBrand(Schema.Brand.Reader brandReader, Type type, Pass2State state)
TypeDefinition GetGroupTypeDef(ulong id, string name)
{
var nodeReader = _id2node[id];
if (!nodeReader.IsStruct)
throw new InvalidSchemaException($"Expected node with id {id} to be a struct definition");
return ProcessStruct(nodeReader, name);
}
void ProcessBrand(Schema.Brand.Reader brandReader, Type type)
{ {
foreach (var scopeReader in brandReader.Scopes) foreach (var scopeReader in brandReader.Scopes)
{ {
var whatToBind = GetOrCreateTypeDef(scopeReader.ScopeId, TypeTag.Unknown); var whatToBind = ProcessNode(scopeReader.ScopeId, state);
int index = 0; int index = 0;
switch (0) switch (0)
@ -121,7 +247,7 @@ namespace CapnpC.Model
switch (0) switch (0)
{ {
case 0 when bindingReader.IsType: case 0 when bindingReader.IsType:
type.BindGenericParameter(typeParameter, ProcessType(bindingReader.Type)); type.BindGenericParameter(typeParameter, ProcessType(bindingReader.Type, state));
break; break;
case 0 when bindingReader.IsUnbound: case 0 when bindingReader.IsUnbound:
@ -146,7 +272,8 @@ namespace CapnpC.Model
} }
} }
} }
Type ProcessType(Schema.Type.Reader typeReader)
Type ProcessType(Schema.Type.Reader typeReader, Pass2State state)
{ {
Type result; Type result;
@ -159,7 +286,7 @@ namespace CapnpC.Model
return Types.FromParameter( return Types.FromParameter(
new GenericParameter() new GenericParameter()
{ {
DeclaringEntity = GetOrCreateTypeDef(typeReader.AnyPointer_Parameter_ScopeId, TypeTag.Unknown), DeclaringEntity = ProcessNode(typeReader.AnyPointer_Parameter_ScopeId, state),
Index = typeReader.AnyPointer_Parameter_ParameterIndex Index = typeReader.AnyPointer_Parameter_ParameterIndex
}); });
@ -167,7 +294,7 @@ namespace CapnpC.Model
return Types.FromParameter( return Types.FromParameter(
new GenericParameter() 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 Index = typeReader.AnyPointer_ImplicitMethodParameter_ParameterIndex
}); });
@ -205,7 +332,7 @@ namespace CapnpC.Model
return Types.F64; return Types.F64;
case 0 when typeReader.IsEnum: case 0 when typeReader.IsEnum:
return Types.FromDefinition(GetOrCreateTypeDef(typeReader.Enum_TypeId, TypeTag.Enum)); return Types.FromDefinition(ProcessNode(typeReader.Enum_TypeId, state, TypeTag.Enum));
case 0 when typeReader.IsFloat32: case 0 when typeReader.IsFloat32:
return Types.F32; return Types.F32;
@ -223,16 +350,16 @@ namespace CapnpC.Model
return Types.S8; return Types.S8;
case 0 when typeReader.IsInterface: case 0 when typeReader.IsInterface:
result = Types.FromDefinition(GetOrCreateTypeDef(typeReader.Interface_TypeId, TypeTag.Interface)); result = Types.FromDefinition(ProcessNode(typeReader.Interface_TypeId, state, TypeTag.Interface));
ProcessBrand(typeReader.Interface_Brand, result); ProcessBrand(typeReader.Interface_Brand, result, state);
return result; return result;
case 0 when typeReader.IsList: 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: case 0 when typeReader.IsStruct:
result = Types.FromDefinition(GetOrCreateTypeDef(typeReader.Struct_TypeId, TypeTag.Struct)); result = Types.FromDefinition(ProcessNode(typeReader.Struct_TypeId, state, TypeTag.Struct));
ProcessBrand(typeReader.Struct_Brand, result); ProcessBrand(typeReader.Struct_Brand, result, state);
return result; return result;
case 0 when typeReader.IsText: case 0 when typeReader.IsText:
@ -365,7 +492,7 @@ namespace CapnpC.Model
return value; 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) if (reader.Fields == null)
{ {
@ -389,15 +516,15 @@ namespace CapnpC.Model
switch (0) switch (0)
{ {
case 0 when fieldReader.IsGroup: case 0 when fieldReader.IsGroup:
field.Type = Types.FromDefinition(GetGroupTypeDef( var def = ProcessNode(fieldReader.Group_TypeId, state, TypeTag.Group);
fieldReader.Group_TypeId, fieldReader.Name)); field.Type = Types.FromDefinition(def);
break; break;
case 0 when fieldReader.IsSlot: case 0 when fieldReader.IsSlot:
field.DefaultValue = ProcessValue(fieldReader.Slot_DefaultValue); field.DefaultValue = ProcessValue(fieldReader.Slot_DefaultValue);
field.DefaultValueIsExplicit = fieldReader.Slot_HadExplicitDefault; field.DefaultValueIsExplicit = fieldReader.Slot_HadExplicitDefault;
field.Offset = fieldReader.Slot_Offset; field.Offset = fieldReader.Slot_Offset;
field.Type = ProcessType(fieldReader.Slot_Type); field.Type = ProcessType(fieldReader.Slot_Type, state);
field.DefaultValue.Type = field.Type; field.DefaultValue.Type = field.Type;
break; break;
@ -411,7 +538,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; def.IsGeneric = reader.IsGeneric;
@ -423,18 +550,9 @@ namespace CapnpC.Model
} }
} }
_typeNest.Push(def); ProcessNestedNodes(reader.NestedNodes, state);
if (reader.NestedNodes != null) ProcessFields(reader, def, def.Fields, state);
{
foreach (var nestedReader in reader.NestedNodes)
{
var node = IdToNode(nestedReader.Id);
ProcessNode(node, nestedReader.Name);
}
}
ProcessFields(reader, def, def.Fields);
if (reader.IsInterface) if (reader.IsInterface)
{ {
@ -450,12 +568,12 @@ namespace CapnpC.Model
{ {
method.GenericParameters.Add(implicitParameterReader.Name); method.GenericParameters.Add(implicitParameterReader.Name);
} }
_currentMethod = method; state.currentMethod = method;
def.Methods.Add(method); def.Methods.Add(method);
var paramNode = IdToNode(methodReader.ParamStructType); 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) if (paramType != null)
{ {
paramType.SpecialName = SpecialName.MethodParamsStruct; paramType.SpecialName = SpecialName.MethodParamsStruct;
@ -470,7 +588,7 @@ namespace CapnpC.Model
method.ParamsStruct.InheritFreeParameters(method); method.ParamsStruct.InheritFreeParameters(method);
var resultNode = IdToNode(methodReader.ResultStructType); 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) if (resultType != null)
{ {
resultType.SpecialName = SpecialName.MethodResultStruct; resultType.SpecialName = SpecialName.MethodResultStruct;
@ -485,24 +603,13 @@ namespace CapnpC.Model
method.ResultStruct.InheritFreeParameters(method); 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.StructDataWordCount = structReader.Struct_DataWordCount;
def.StructPointerCount = structReader.Struct_PointerCount; def.StructPointerCount = structReader.Struct_PointerCount;
@ -513,12 +620,10 @@ namespace CapnpC.Model
16u * structReader.Struct_DiscriminantOffset); 16u * structReader.Struct_DiscriminantOffset);
} }
ProcessInterfaceOrStructTail(def, structReader); return ProcessInterfaceOrStructTail(def, structReader, state);
return def;
} }
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 //# If a named parameter list was specified in the method
//# declaration (rather than a single struct parameter type) then a corresponding struct type is //# declaration (rather than a single struct parameter type) then a corresponding struct type is
@ -533,58 +638,38 @@ namespace CapnpC.Model
throw new InvalidSchemaException("Expected a struct"); throw new InvalidSchemaException("Expected a struct");
} }
var def = ProcessNode(reader.Id, state, TypeTag.Struct);
if (reader.ScopeId == 0) if (reader.ScopeId == 0)
{ {
// Auto-generated => Named parameter list // Auto-generated => Named parameter list
ProcessFields(reader, null, list); foreach (var field in def.Fields) list.Add(field);
return ProcessStruct(reader, null); return def;
} }
else else
{ {
// Single, anonymous, struct-typed parameter // Single, anonymous, struct-typed parameter
var def = GetOrCreateTypeDef(reader.Id, TypeTag.Struct);
var type = Types.FromDefinition(def); var type = Types.FromDefinition(def);
ProcessBrand(brandReader, type); ProcessBrand(brandReader, type, state);
var anon = new Field() { Type = type }; var anon = new Field() { Type = type };
list.Add(anon); list.Add(anon);
return null; 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) foreach (var superClassReader in ifaceReader.Interface_Superclasses)
{ {
var superClass = GetOrCreateTypeDef( var superClass = ProcessNode(superClassReader.Id, state, TypeTag.Interface);
superClassReader.Id,
TypeTag.Interface);
def.Superclasses.Add(Types.FromDefinition(superClass)); def.Superclasses.Add(Types.FromDefinition(superClass));
} }
ProcessInterfaceOrStructTail(def, ifaceReader); return ProcessInterfaceOrStructTail(def, ifaceReader, state);
return def;
} }
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) foreach (var fieldReader in enumReader.Enumerants)
{ {
var field = new Enumerant() var field = new Enumerant()
@ -601,46 +686,41 @@ namespace CapnpC.Model
def.Enumerants.Add(field); def.Enumerants.Add(field);
} }
return def;
_typeNest.Pop();
} }
void ProcessConst(Schema.Node.Reader constReader, string name) Value ProcessConst(Schema.Node.Reader constReader, Pass2State state)
{ {
var value = ProcessValue(constReader.Const_Value); var value = ProcessValue(constReader.Const_Value);
value.Type = ProcessType(constReader.Const_Type); value.Type = ProcessType(constReader.Const_Type, state);
return value;
_typeNest.Peek().Constants.Add(value);
} }
void ProcessNode(Schema.Node.Reader node, string name) TypeDefinition ProcessNode(ulong id, Pass2State state, TypeTag tag = default)
{ {
switch (0) var node = IdToNode(id);
var kind = node.GetKind();
if (tag == TypeTag.Unknown) tag = kind.GetTypeTag();
var def = _typeDefMgr.GetExisting(id, tag);
if (state.processedNodes.Contains(id)) return def;
state.processedNodes.Add(id);
switch (kind)
{ {
case 0 when node.IsAnnotation: case NodeKind.Annotation:
break; return def;
case NodeKind.Const:
case 0 when node.IsConst: def.DeclaringElement.Constants.Add(ProcessConst(node, state));
ProcessConst(node, name); return def;
break; case NodeKind.Enum:
return ProcessEnum(node, def, state);
case 0 when node.IsEnum: case NodeKind.Interface:
ProcessEnum(node, name); return ProcessInterface(node, def, state);
break; case NodeKind.Struct:
case NodeKind.Group:
case 0 when node.IsFile: return ProcessStruct(node, def, state);
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;
default: 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 +731,55 @@ namespace CapnpC.Model
return 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 class Type: AbstractType
{ {
// Representation of a type expression in the schema language
public TypeDefinition Definition { get; set; } public TypeDefinition Definition { get; set; }
// The model for all nodes that are not file nodes - they define types
public GenericParameter Parameter { get; set; } public GenericParameter Parameter { get; set; }
// A reference to type parameter in this scope
public Type ElementType { get; set; } public Type ElementType { get; set; }
// The type of a list element, if this is a list.
readonly Dictionary<GenericParameter, Type> _parameterBindings = readonly Dictionary<GenericParameter, Type> _parameterBindings =
new Dictionary<GenericParameter, Type>(); new Dictionary<GenericParameter, Type>();

View File

@ -8,7 +8,19 @@ namespace CapnpC.Model
readonly Dictionary<ulong, TypeDefinition> _id2def = readonly Dictionary<ulong, TypeDefinition> _id2def =
new Dictionary<ulong, TypeDefinition>(); new Dictionary<ulong, TypeDefinition>();
public TypeDefinition GetOrCreate(ulong id, TypeTag tag) public TypeDefinition Create(ulong id, TypeTag tag)
{
if (_id2def.ContainsKey(id))
{
throw new ArgumentException(nameof(id), $"Attempting to redefine {tag.ToString()} {id.StrId()}.");
}
var def = new TypeDefinition(tag, id);
_id2def.Add(id, def);
return def;
}
public TypeDefinition GetExisting(ulong id, TypeTag tag)
{ {
if (_id2def.TryGetValue(id, out var def)) if (_id2def.TryGetValue(id, out var def))
{ {
@ -20,19 +32,9 @@ namespace CapnpC.Model
{ {
throw new ArgumentOutOfRangeException(nameof(tag), "Type tag does not match existing type"); throw new ArgumentOutOfRangeException(nameof(tag), "Type tag does not match existing type");
} }
return def;
} }
else throw new ArgumentOutOfRangeException($"Attempting to retrieve nonexistend node {id.StrId()}.");
{
def = new TypeDefinition(tag, id);
_id2def.Add(id, def);
}
return def;
}
public TypeDefinition GetExisting(ulong id)
{
return _id2def[id];
} }
} }
} }