diff --git a/internal/filetypes/filetypes.go b/internal/filetypes/filetypes.go index 47237904b..ba40c88d4 100644 --- a/internal/filetypes/filetypes.go +++ b/internal/filetypes/filetypes.go @@ -23,6 +23,7 @@ import ( "cuelang.org/go/cue/build" "cuelang.org/go/cue/errors" "cuelang.org/go/cue/token" + "cuelang.org/go/internal/filetypes/internal" ) // Mode indicate the base mode of operation and indicates a different set of @@ -50,26 +51,7 @@ func (m Mode) String() string { } } -// FileInfo defines the parsing plan for a file. -type FileInfo struct { - Filename string `json:"filename"` - Encoding build.Encoding `json:"encoding,omitempty"` - Interpretation build.Interpretation `json:"interpretation,omitempty"` - Form build.Form `json:"form,omitempty"` - - Definitions bool `json:"definitions"` // include/allow definition fields - Data bool `json:"data"` // include/allow regular fields - Optional bool `json:"optional"` // include/allow definition fields - Constraints bool `json:"constraints"` // include/allow constraints - References bool `json:"references"` // don't resolve/allow references - Cycles bool `json:"cycles"` // cycles are permitted - KeepDefaults bool `json:"keepDefaults"` // select/allow default values - Incomplete bool `json:"incomplete"` // permit incomplete values - Imports bool `json:"imports"` // don't expand/allow imports - Stream bool `json:"stream"` // permit streaming - Docs bool `json:"docs"` // show/allow docs - Attributes bool `json:"attributes"` // include/allow attributes -} +type FileInfo = internal.FileInfo // ParseArgs converts a sequence of command line arguments representing // files into a sequence of build file specifications. diff --git a/internal/filetypes/fromfile.dat b/internal/filetypes/fromfile.dat new file mode 100644 index 0000000000000000000000000000000000000000..87868b118851b043574a2de5f044dc5ab8d24248 GIT binary patch literal 11502 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 64 { - // As we use uint64 to represent the tag space, if there - // are more than 64 of them, our logic fails. In the future - // we could use a more sophisticated bitset representation. - // As of the time of writing, there are only 20, so there's reasonable - // headroom for now. - panic(fmt.Errorf("too many tags (%d, max %d)", len(topLevelTags), 64)) + panic("too many tags") } type bitsValue struct { bits uint64 @@ -442,6 +615,48 @@ func structFields(v cue.Value, opts ...cue.Option) iter.Seq2[string, cue.Value] } } +type genFromFileParams struct { + genstruct.Struct + Mode genstruct.Accessor[filetypes.Mode] + Encoding genstruct.Accessor[build.Encoding] + Interpretation genstruct.Accessor[build.Interpretation] + Form genstruct.Accessor[build.Form] +} + +type genFromFileResult struct { + genstruct.Struct + Encoding genstruct.Accessor[build.Encoding] + Interpretation genstruct.Accessor[build.Interpretation] + Form genstruct.Accessor[build.Form] + Aspects genstruct.Accessor[internal.Aspects] +} + +func newFromFileParamsStruct( + encodings []build.Encoding, + interpretations []build.Interpretation, + forms []build.Form, +) *genFromFileParams { + r := &genFromFileParams{} + r.Mode = genstruct.AddInt(&r.Struct, filetypes.NumModes, "Mode") + r.Encoding = genstruct.AddEnum(&r.Struct, encodings, "", "allEncodings", "build.Encoding", nil) + r.Interpretation = genstruct.AddEnum(&r.Struct, interpretations, "", "allInterpretations", "build.Interpretation", nil) + r.Form = genstruct.AddEnum(&r.Struct, forms, "", "allForms", "build.Form", nil) + return r +} + +func newFromFileResult( + encodings []build.Encoding, + interpretations []build.Interpretation, + forms []build.Form, +) *genFromFileResult { + r := &genFromFileResult{} + r.Encoding = genstruct.AddEnum(&r.Struct, encodings, "", "allEncodings", "build.Encoding", nil) + r.Interpretation = genstruct.AddEnum(&r.Struct, interpretations, "", "allInterpretations", "build.Interpretation", nil) + r.Form = genstruct.AddEnum(&r.Struct, forms, "", "allForms", "build.Form", nil) + r.Aspects = genstruct.AddInt(&r.Struct, internal.AllAspects, "internal.Aspects") + return r +} + func newToFileParamsStruct(topLevelTags, fileExts []string) *genToFileParams { r := &genToFileParams{} r.Mode = genstruct.AddInt(&r.Struct, filetypes.NumModes, "Mode") @@ -499,7 +714,7 @@ type dimspace[V any] struct { } // walkSpace explores the values that are possible to reach from the given initial -// value within the given number of dimensions (numDimentions), where each point in space +// value within the given number of dimensions (numDimensions), where each point in space // can have the given number of possible item values (numItems). // It calls evaluate to derive further values as it walks the space, and // truncates the tree whereever evaluate returns false. @@ -558,9 +773,7 @@ func lookup(v cue.Value, elems ...string) cue.Value { func allKeys[T ~string](v cue.Value, elems ...string) []T { return slices.Sorted( - seqMap(keys(structFields(lookup(v, elems...))), func(s string) T { - return T(s) - }), + seqMap(keys(structFields(lookup(v, elems...))), fromString[T]), ) } @@ -584,3 +797,7 @@ func seqMap[T1, T2 any](it iter.Seq[T1], f func(T1) T2) iter.Seq[T2] { } } } + +func fromString[T ~string](s string) T { + return T(s) +} diff --git a/internal/filetypes/internal/genstruct/genstruct_test.go b/internal/filetypes/internal/genstruct/genstruct_test.go index a6fcf2e9b..c5c36123d 100644 --- a/internal/filetypes/internal/genstruct/genstruct_test.go +++ b/internal/filetypes/internal/genstruct/genstruct_test.go @@ -39,7 +39,8 @@ func TestEnum(t *testing.T) { "bar": 1, "baz": 2, })) - qt.Assert(t, qt.Equals(s.GenInit(), `var ( + generated := make(map[string]bool) + qt.Assert(t, qt.Equals(s.GenInit(generated), `var ( values = []string { "foo", "bar", @@ -50,6 +51,10 @@ func TestEnum(t *testing.T) { `)) qt.Assert(t, qt.Equals(a.GenGet("data"), `genstruct.GetEnum(data, 0, 1, values)`)) qt.Assert(t, qt.Equals(GetEnum(data, 0, 1, values), "bar")) + qt.Assert(t, qt.DeepEquals(generated, map[string]bool{"values": true})) + + // Try GenInit again to make sure it doesn't regenerate the same variable. + qt.Assert(t, qt.Equals(s.GenInit(generated), "")) a.Put(data, "baz") qt.Assert(t, qt.Equals(GetEnum(data, 0, 1, values), "baz")) @@ -84,7 +89,7 @@ func TestSet(t *testing.T) { qt.Assert(t, qt.Equals(data[0], 0b101)) a.Put(data, slices.Values([]string{"bar"})) qt.Assert(t, qt.Equals(data[0], 0b010)) - qt.Assert(t, qt.Equals(s.GenInit(), `var ( + qt.Assert(t, qt.Equals(s.GenInit(make(map[string]bool)), `var ( values = []string { "foo", "bar", diff --git a/internal/filetypes/internal/genstruct/struct.go b/internal/filetypes/internal/genstruct/struct.go index 0df30c27d..4f2762a76 100644 --- a/internal/filetypes/internal/genstruct/struct.go +++ b/internal/filetypes/internal/genstruct/struct.go @@ -25,17 +25,25 @@ import ( "strings" ) +type initInfo struct { + genInit func() string + ident string +} + // Struct represents a binary-marshalable struct. type Struct struct { fields []field size int - genInits []func() string + genInits []initInfo } -func (s *Struct) GenInit() string { +func (s *Struct) GenInit(generated map[string]bool) string { var buf strings.Builder - for _, genInit := range s.genInits { - buf.WriteString(genInit()) + for _, info := range s.genInits { + if !generated[info.ident] { + generated[info.ident] = true + buf.WriteString(info.genInit()) + } } return buf.String() } @@ -125,7 +133,10 @@ func AddEnum[T comparable](s *Struct, values []T, defaultValue T, goValuesIdent, a.defaultValueIndex = i } - s.genInits = append(s.genInits, a.genInit) + s.genInits = append(s.genInits, initInfo{ + genInit: a.genInit, + ident: a.goValuesIdent, + }) return a } @@ -205,7 +216,10 @@ func AddSet(s *Struct, values []string, goValuesIdent string) Accessor[iter.Seq[ valueToBit: valueToBit, goValuesIdent: goValuesIdent, } - s.genInits = append(s.genInits, a.genInit) + s.genInits = append(s.genInits, initInfo{ + genInit: a.genInit, + ident: a.goValuesIdent, + }) return a } @@ -284,7 +298,10 @@ func AddEnumMap[T comparable](s *Struct, keys []string, values []T, defaultValue values: values, goIdent: goIdent, } - s.genInits = append(s.genInits, a.genInit) + s.genInits = append(s.genInits, initInfo{ + genInit: a.genInit, + ident: a.goIdent, + }) return a } diff --git a/internal/filetypes/internal/internal.go b/internal/filetypes/internal/internal.go index d2200971f..69bc4329c 100644 --- a/internal/filetypes/internal/internal.go +++ b/internal/filetypes/internal/internal.go @@ -16,6 +16,8 @@ // that need to be shared between the code generator and the package proper. package internal +import "cuelang.org/go/cue/build" + type ErrorKind int const ( @@ -25,3 +27,81 @@ const ( ErrNoEncodingSpecified NumErrorKinds ) + +type Aspects uint64 + +const ( + Definitions Aspects = 1 << iota + Data + Optional + Constraints + References + Cycles + KeepDefaults + Incomplete + Imports + Stream + Docs + Attributes + + AllAspects Aspects = (1 << iota) - 1 +) + +func (f FileInfo) Aspects() Aspects { + return 0 | + when(f.Definitions, Definitions) | + when(f.Data, Data) | + when(f.Optional, Optional) | + when(f.Constraints, Constraints) | + when(f.References, References) | + when(f.Cycles, Cycles) | + when(f.KeepDefaults, KeepDefaults) | + when(f.Incomplete, Incomplete) | + when(f.Imports, Imports) | + when(f.Stream, Stream) | + when(f.Docs, Docs) | + when(f.Attributes, Attributes) +} + +func (f *FileInfo) SetAspects(a Aspects) { + f.Definitions = (a & Definitions) != 0 + f.Data = (a & Data) != 0 + f.Optional = (a & Optional) != 0 + f.Constraints = (a & Constraints) != 0 + f.References = (a & References) != 0 + f.Cycles = (a & Cycles) != 0 + f.KeepDefaults = (a & KeepDefaults) != 0 + f.Incomplete = (a & Incomplete) != 0 + f.Imports = (a & Imports) != 0 + f.Stream = (a & Stream) != 0 + f.Docs = (a & Docs) != 0 + f.Attributes = (a & Attributes) != 0 +} + +func when[T ~uint64](b bool, mask T) T { + if b { + return mask + } + return 0 +} + +// FileInfo defines the parsing plan for a file. +type FileInfo struct { + Filename string `json:"filename"` + Encoding build.Encoding `json:"encoding,omitempty"` + Interpretation build.Interpretation `json:"interpretation,omitempty"` + Form build.Form `json:"form,omitempty"` + + Definitions bool `json:"definitions"` // include/allow definition fields + Data bool `json:"data"` // include/allow regular fields + Optional bool `json:"optional"` // include/allow definition fields + Constraints bool `json:"constraints"` // include/allow constraints + References bool `json:"references"` // don't resolve/allow references + Cycles bool `json:"cycles"` // cycles are permitted + KeepDefaults bool `json:"keepDefaults"` // select/allow default values + Incomplete bool `json:"incomplete"` // permit incomplete values + Imports bool `json:"imports"` // don't expand/allow imports + Stream bool `json:"stream"` // permit streaming + Docs bool `json:"docs"` // show/allow docs + Attributes bool `json:"attributes"` // include/allow attributes +} diff --git a/internal/filetypes/tofile.go b/internal/filetypes/tofile.go index ad6e56b89..8a3183bd4 100644 --- a/internal/filetypes/tofile.go +++ b/internal/filetypes/tofile.go @@ -33,15 +33,15 @@ var evalMu sync.Mutex //go:generate go run -tags bootstrap ./generate.go func toFile(mode Mode, sc *scope, filename string) (*build.File, error) { - f0, err0 := toFileOrig(mode, sc, filename) - f1, err1 := toFileGenerated(mode, sc, filename) + f0, err0 := toFileGenerated(mode, sc, filename) + f1, err1 := toFileOrig(mode, sc, filename) if (err0 != nil) != (err1 != nil) { - panic(fmt.Errorf("toFile discrepancy on error return; mode %v; scope %v; filename %v:\nold: %v\nnew: %v", mode, sc, filename, err0, err1)) + panic(fmt.Errorf("toFile discrepancy on error return; mode %v; scope %v; filename %v:\nold: %v\nnew: %v", mode, sc, filename, err1, err0)) } else if diff := cmp.Diff(f0, f1); diff != "" { panic(fmt.Errorf("toFile result discrepancy; mode %v; scope %v; filename %v:\n%s", mode, sc, filename, diff)) } - return f1, err1 + return f0, err0 } func toFileOrig(mode Mode, sc *scope, filename string) (*build.File, error) { @@ -101,6 +101,18 @@ func toFile1(modeVal, fileVal cue.Value, filename string, sc *scope) (*build.Fil // by [ParseArgs] or similar. // The b.Encoding field must be non-empty. func FromFile(b *build.File, mode Mode) (*FileInfo, error) { + fi0, err0 := fromFileGenerated(b, mode) + fi1, err1 := fromFileOrig(b, mode) + if (err0 != nil) != (err1 != nil) { + panic(fmt.Errorf("toFile discrepancy on error return; mode %v; file %#v:\nold: %v\nnew: %v", mode, b, err1, err0)) + } else if diff := cmp.Diff(fi1, fi0); diff != "" { + panic(fmt.Errorf("toFile result discrepancy; mode %v; file %#v\n%s", mode, b, diff)) + } + + return fi0, err0 +} + +func fromFileOrig(b *build.File, mode Mode) (*FileInfo, error) { evalMu.Lock() defer evalMu.Unlock() typesInit() diff --git a/internal/filetypes/tofile_bootstrap.go b/internal/filetypes/tofile_bootstrap.go index 799faabbe..b127edf51 100644 --- a/internal/filetypes/tofile_bootstrap.go +++ b/internal/filetypes/tofile_bootstrap.go @@ -21,3 +21,7 @@ import "cuelang.org/go/cue/build" func toFileGenerated(mode Mode, sc *scope, filename string) (*build.File, error) { panic("never called") } + +func fromFileGenerated(b *build.File, mode Mode) (*FileInfo, error) { + panic("never called") +} diff --git a/internal/filetypes/types_gen.go b/internal/filetypes/types_gen.go index 90ace7ef5..816a14306 100644 --- a/internal/filetypes/types_gen.go +++ b/internal/filetypes/types_gen.go @@ -19,11 +19,16 @@ import ( "cuelang.org/go/internal/filetypes/internal/opt" ) -// Define the opt type and its associated [some] function for use by the generated code. +// TODO use string instead of []byte so that we can +// use the init data directly without copying into +// read-write memory. //go:embed fileinfo.dat var fileInfoDataBytes []byte +//go:embed fromfile.dat +var fromFileDataBytes []byte + func init() { tagTypes = map[string]TagType{ "auto": TagTopLevel, @@ -205,6 +210,27 @@ func toFileGenerated(mode Mode, sc *scope, filename string) (*build.File, errors return &f, nil } +func fromFileGenerated(b *build.File, mode Mode) (*FileInfo, error) { + key := make([]byte, 4) + genstruct.PutUint64(key, 0, 1, uint64(mode)) + genstruct.PutEnum(key, 1, 1, allEncodings_rev, 12, b.Encoding) + genstruct.PutEnum(key, 2, 1, allInterpretations_rev, 4, b.Interpretation) + genstruct.PutEnum(key, 3, 1, allForms_rev, 5, b.Form) + + data, ok := genstruct.FindRecord(fromFileDataBytes, 4+5, key) + if !ok { + return nil, errors.Newf(token.NoPos, "no encoding specified") + } + fi := &FileInfo{ + Filename: b.Filename, + Encoding: genstruct.GetEnum(data, 0, 1, allEncodings), + Interpretation: genstruct.GetEnum(data, 1, 1, allInterpretations), + Form: genstruct.GetEnum(data, 2, 1, allForms), + } + fi.SetAspects(internal.Aspects(genstruct.GetUint64(data, 3, 2))) + return fi, nil +} + func someKey[K cmp.Ordered, V any](m map[K]V) K { return slices.Sorted(maps.Keys(m))[0] } diff --git a/internal/filetypes/types_gen.go.tmpl b/internal/filetypes/types_gen.go.tmpl index 33e4ee75c..49d45951f 100644 --- a/internal/filetypes/types_gen.go.tmpl +++ b/internal/filetypes/types_gen.go.tmpl @@ -19,21 +19,29 @@ import ( "cuelang.org/go/internal/filetypes/internal/genstruct" ) -// Define the opt type and its associated [some] function for use by the generated code. +// TODO use string instead of []byte so that we can +// use the init data directly without copying into +// read-write memory. //go:embed fileinfo.dat var fileInfoDataBytes []byte +//go:embed fromfile.dat +var fromFileDataBytes []byte + func init() { tagTypes = map[string]TagType{ {{range $k, $v := .TagTypes}}{{printf "\t%q: %v,\n" $k $v}}{{end}} } } +{{.ToFileParams.GenInit .Generated}} + +{{.ToFileResult.GenInit .Generated}} -{{.ToFileParams.GenInit}} +{{.FromFileParams.GenInit .Generated}} -{{.ToFileResult.GenInit}} +{{.FromFileResult.GenInit .Generated}} func toFileGenerated(mode Mode, sc *scope, filename string) (*build.File, errors.Error) { key := make([]byte, {{.ToFileParams.Size}}) @@ -97,6 +105,27 @@ func toFileGenerated(mode Mode, sc *scope, filename string) (*build.File, errors return &f, nil } +func fromFileGenerated(b *build.File, mode Mode) (*FileInfo, error) { + key := make([]byte, {{.FromFileParams.Size}}) + {{.FromFileParams.Mode.GenPut "key" "mode"}} + {{.FromFileParams.Encoding.GenPut "key" "b.Encoding"}} + {{.FromFileParams.Interpretation.GenPut "key" "b.Interpretation"}} + {{.FromFileParams.Form.GenPut "key" "b.Form"}} + + data, ok := genstruct.FindRecord(fromFileDataBytes, {{.FromFileParams.Size}}+{{.FromFileResult.Size}}, key) + if !ok { + return nil, errors.Newf(token.NoPos, "no encoding specified") + } + fi := &FileInfo{ + Filename: b.Filename, + Encoding: {{.FromFileResult.Encoding.GenGet "data"}}, + Interpretation: {{.FromFileResult.Interpretation.GenGet "data"}}, + Form: {{.FromFileResult.Form.GenGet "data"}}, + } + fi.SetAspects({{.FromFileResult.Aspects.GenGet "data"}}) + return fi, nil +} + func someKey[K cmp.Ordered, V any](m map[K] V) K { return slices.Sorted(maps.Keys(m))[0] }