package pkg import ( "io" "os" "path/filepath" "github.com/attic-labs/noms/chunks" "github.com/attic-labs/noms/d" "github.com/attic-labs/noms/ref" "github.com/attic-labs/noms/types" ) // ParseNomDL reads a Noms package specification from r and returns a Package. Errors will be annotated with packageName and thrown. func ParseNomDL(packageName string, r io.Reader, includePath string, cs chunks.ChunkStore) Parsed { i := runParser(packageName, r) i.Name = packageName imports := resolveImports(i.Aliases, includePath, cs) depsSet := make(map[ref.Ref]bool, len(imports)) deps := make([]ref.Ref, 0, len(imports)) for _, target := range imports { if !depsSet[target] { deps = append(deps, target) } depsSet[target] = true } resolveLocalOrdinals(&i) resolveNamespaces(&i, imports, GetDeps(deps, cs)) return Parsed{ types.NewPackage(i.Types, deps), i.Name, i.UsingDeclarations, } } // GetDeps reads the types.Package objects referred to by depRefs out of cs and returns a map of ref: PackageDef. func GetDeps(deps []ref.Ref, cs chunks.ChunkStore) map[ref.Ref]types.Package { depsMap := map[ref.Ref]types.Package{} for _, depRef := range deps { v := types.ReadValue(depRef, cs) d.Chk.NotNil(v, "Importing package by ref %s failed.", depRef.String()) depsMap[depRef] = v.(types.Package) } return depsMap } // Parsed represents a parsed Noms type package, which has some additional metadata beyond that which is present in a types.Package. // UsingDeclarations is kind of a hack to indicate specializations of Noms containers that need to be generated. These should all be one of ListKind, SetKind, MapKind or RefKind, and Desc should be a CompoundDesc instance. type Parsed struct { types.Package Name string UsingDeclarations []types.TypeRef } type intermediate struct { Name string Aliases map[string]string UsingDeclarations []types.TypeRef Types []types.TypeRef } func indexOf(t types.TypeRef, ts []types.TypeRef) int16 { for i, tt := range ts { if tt.Name() == t.Name() && tt.Namespace() == "" { return int16(i) } } return -1 } func (i *intermediate) checkLocal(t types.TypeRef) bool { if t.Namespace() == "" { d.Chk.True(t.HasOrdinal(), "Invalid local reference") return true } return false } func runParser(logname string, r io.Reader) intermediate { got, err := ParseReader(logname, r) d.Exp.NoError(err) return got.(intermediate) } func resolveImports(aliases map[string]string, includePath string, cs chunks.ChunkStore) map[string]ref.Ref { canonicalize := func(path string) string { if filepath.IsAbs(path) { return path } return filepath.Join(includePath, path) } imports := map[string]ref.Ref{} for alias, target := range aliases { var r ref.Ref if d.Try(func() { r = ref.Parse(target) }) != nil { canonical := canonicalize(target) inFile, err := os.Open(canonical) d.Chk.NoError(err) defer inFile.Close() parsedDep := ParseNomDL(alias, inFile, filepath.Dir(canonical), cs) imports[alias] = types.WriteValue(parsedDep.Package, cs) } else { imports[alias] = r } } return imports } func resolveLocalOrdinals(p *intermediate) { var rec func(t types.TypeRef) types.TypeRef resolveFields := func(fields []types.Field) { for idx, f := range fields { f.T = rec(f.T) fields[idx] = f } } rec = func(t types.TypeRef) types.TypeRef { if t.IsUnresolved() { if t.Namespace() == "" && !t.HasOrdinal() { ordinal := indexOf(t, p.Types) d.Chk.True(ordinal >= 0 && int(ordinal) < len(p.Types), "Invalid reference: %s", t.Name()) return types.MakeTypeRef(t.PackageRef(), int16(ordinal)) } return t } switch t.Kind() { case types.ListKind, types.SetKind, types.RefKind: return types.MakeCompoundTypeRef(t.Kind(), rec(t.Desc.(types.CompoundDesc).ElemTypes[0])) case types.MapKind: elemTypes := t.Desc.(types.CompoundDesc).ElemTypes return types.MakeCompoundTypeRef(t.Kind(), rec(elemTypes[0]), rec(elemTypes[1])) case types.StructKind: resolveFields(t.Desc.(types.StructDesc).Fields) resolveFields(t.Desc.(types.StructDesc).Union) } return t } for i, t := range p.Types { p.Types[i] = rec(t) } for i, t := range p.UsingDeclarations { p.UsingDeclarations[i] = rec(t) } } func resolveNamespaces(p *intermediate, aliases map[string]ref.Ref, deps map[ref.Ref]types.Package) { var rec func(t types.TypeRef) types.TypeRef resolveFields := func(fields []types.Field) { for idx, f := range fields { if f.T.IsUnresolved() { if p.checkLocal(f.T) { continue } f.T = resolveNamespace(f.T, aliases, deps) } else { f.T = rec(f.T) } d.Chk.True(!f.T.IsUnresolved() || f.T.HasOrdinal()) fields[idx] = f } } rec = func(t types.TypeRef) types.TypeRef { if t.IsUnresolved() { if p.checkLocal(t) { return t } t = resolveNamespace(t, aliases, deps) } switch t.Kind() { case types.ListKind, types.SetKind, types.RefKind: return types.MakeCompoundTypeRef(t.Kind(), rec(t.Desc.(types.CompoundDesc).ElemTypes[0])) case types.MapKind: elemTypes := t.Desc.(types.CompoundDesc).ElemTypes return types.MakeCompoundTypeRef(t.Kind(), rec(elemTypes[0]), rec(elemTypes[1])) case types.StructKind: resolveFields(t.Desc.(types.StructDesc).Fields) resolveFields(t.Desc.(types.StructDesc).Union) } d.Chk.True(!t.IsUnresolved() || t.HasOrdinal()) return t } for i, t := range p.Types { p.Types[i] = rec(t) } for i, t := range p.UsingDeclarations { p.UsingDeclarations[i] = rec(t) } } func resolveNamespace(t types.TypeRef, aliases map[string]ref.Ref, deps map[ref.Ref]types.Package) types.TypeRef { pkgRef, ok := aliases[t.Namespace()] d.Exp.True(ok, "Could not find import aliased to %s", t.Namespace()) d.Chk.NotEqual("", t.Name()) ordinal := deps[pkgRef].GetOrdinal(t.Name()) d.Exp.NotEqual(int64(-1), ordinal, "Could not find type %s in package %s (aliased to %s).", t.Name(), pkgRef.String(), t.Namespace()) return types.MakeTypeRef(pkgRef, int16(ordinal)) } // expandStruct takes a struct definition and expands the internal structs created for unions. func expandStruct(t types.TypeRef, ordinal int) []types.TypeRef { d.Chk.Equal(types.StructKind, t.Kind()) ts := []types.TypeRef{t} ordinal++ doFields := func(fields []types.Field) []types.Field { rv := make([]types.Field, len(fields)) for i, f := range fields { if f.T.Kind() == types.StructKind { newTypeRefs := expandStruct(f.T, ordinal) ts = append(ts, newTypeRefs...) rv[i] = types.Field{f.Name, types.MakeTypeRef(ref.Ref{}, int16(ordinal)), f.Optional} ordinal += len(newTypeRefs) } else { rv[i] = f } } return rv } desc := t.Desc.(types.StructDesc) fields := doFields(desc.Fields) var choices types.Choices if desc.Union != nil { choices = doFields(desc.Union) } if len(ts) != 1 { ts[0] = types.MakeStructTypeRef(t.Name(), fields, choices) } return ts }