From 20bb55327910f4983a41249fe0ebec15b0fe334a Mon Sep 17 00:00:00 2001 From: Miguel Young de la Sota Date: Wed, 18 Dec 2024 14:57:04 -0800 Subject: [PATCH 1/6] add a code generator for enum boilerplate --- internal/enum/enum.go | 316 ++++++++++++++++++++++++++++++++ internal/enum/generated.go.tmpl | 95 ++++++++++ 2 files changed, 411 insertions(+) create mode 100644 internal/enum/enum.go create mode 100644 internal/enum/generated.go.tmpl diff --git a/internal/enum/enum.go b/internal/enum/enum.go new file mode 100644 index 00000000..f1b43f23 --- /dev/null +++ b/internal/enum/enum.go @@ -0,0 +1,316 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// enum is a helper for generating boilerplate related to Go enums. +// +// To generate boilerplate for a given file, use +// +// //go:generate go run github.com/bufbuild/protocompile/internal/enum +package main + +import ( + "debug/buildinfo" + _ "embed" + "fmt" + "go/ast" + "go/parser" + "go/token" + "os" + "slices" + "strconv" + "strings" + "text/scanner" + "text/template" + "unicode" + + "github.com/bufbuild/protocompile/internal/ext/slicesx" +) + +var ( + //go:embed generated.go.tmpl + tmplText string + tmpl = template.Must(template.New("generated.go.tmpl").Parse(tmplText)) +) + +type Directive struct { + Pos token.Pos + Name string + Args []string +} + +func HasDirectives(comments []*ast.CommentGroup) bool { + for _, g := range comments { + for _, c := range g.List { + rest, ok := strings.CutPrefix(c.Text, "//enum:") + if ok && rest != "" { + return true + } + } + } + return false +} + +func ParseDirectives(fs *token.FileSet, comments []*ast.CommentGroup) ([]Directive, error) { + var d []Directive + for _, g := range comments { + for _, c := range g.List { + rest, ok := strings.CutPrefix(c.Text, "//enum:") + rest = strings.TrimSpace(rest) + if !ok || rest == "" { + continue + } + + var err error + var args []string + sc := scanner.Scanner{ + Error: func(s *scanner.Scanner, msg string) { + err = fmt.Errorf("%s", msg) + }, + Mode: scanner.ScanIdents | scanner.ScanStrings | scanner.ScanRawStrings, + IsIdentRune: func(r rune, _ int) bool { + return !unicode.IsSpace(r) && r != '"' && r != '`' + }, + } + sc.Init(strings.NewReader(rest)) + scan: + for { + next := sc.Scan() + if err != nil { + return nil, fmt.Errorf("%v: invalid directive: %v", fs.Position(c.Pos()), err) + } + + switch next { + case scanner.EOF: + break scan + case scanner.Ident: + args = append(args, sc.TokenText()) + case scanner.String: + str, _ := strconv.Unquote(sc.TokenText()) + args = append(args, str) + } + } + + d = append(d, Directive{ + Pos: c.Pos(), + Name: args[0], + Args: args[1:], + }) + } + } + return d, nil +} + +type Enum struct { + Type *ast.TypeSpec + Methods struct { + String, GoString, FromString string + + StringFunc string + } + Docs struct { + String, GoString, FromString []string + } + + Values []Value +} + +type Value struct { + Value *ast.ValueSpec + String string + Skip bool +} + +func Main() error { + input := os.Getenv("GOFILE") + output := strings.TrimSuffix(input, ".go") + "_enum.go" + + fs := new(token.FileSet) + f, err := parser.ParseFile(fs, input, nil, parser.ParseComments) + if err != nil { + return err + } + + comments := ast.NewCommentMap(fs, f, f.Comments) + + constsByType := make(map[string][]*ast.ValueSpec) + var types []*ast.GenDecl + + for _, decl := range f.Decls { + decl, ok := decl.(*ast.GenDecl) + if !ok { + continue + } + + switch decl.Tok { + case token.TYPE: + if !HasDirectives(comments[decl]) { + continue + } + + if decl.Lparen.IsValid() { + return fmt.Errorf("%v: //enum: directive on type group", fs.Position(decl.TokPos)) + } + + types = append(types, decl) + case token.CONST: + var ty string + for _, spec := range decl.Specs { + v := spec.(*ast.ValueSpec) + if v.Type == nil { + constsByType[ty] = append(constsByType[ty], v) + } + if ident, ok := v.Type.(*ast.Ident); ok { + ty = ident.Name + constsByType[ty] = append(constsByType[ty], v) + } + } + } + } + + imports := map[string]struct{}{"fmt": {}} + var enums []Enum + for _, ty := range types { + enum := Enum{Type: ty.Specs[0].(*ast.TypeSpec)} + dirs, err := ParseDirectives(fs, comments[ty]) + if err != nil { + return err + } + for _, d := range dirs { + var ok bool + switch d.Name { + case "import": + path, ok := slicesx.Get(d.Args, 0) + if !ok { + return fmt.Errorf("%v: //enum:import requires an argument", fs.Position(d.Pos)) + } + imports[path] = struct{}{} + + case "string": + enum.Methods.String, ok = slicesx.Get(d.Args, 0) + if !ok { + enum.Methods.String = "String" + break + } + enum.Docs.String = d.Args[1:] + + case "gostring": + enum.Methods.GoString, ok = slicesx.Get(d.Args, 0) + if !ok { + enum.Methods.GoString = "GoString" + break + } + enum.Docs.GoString = d.Args[1:] + + case "fromstring": + enum.Methods.FromString, ok = slicesx.Get(d.Args, 0) + if !ok { + return fmt.Errorf("%v: //enum:fromstring requires an argument", fs.Position(d.Pos)) + } + enum.Docs.FromString = d.Args[1:] + + case "stringfunc": + enum.Methods.StringFunc, ok = slicesx.Get(d.Args, 0) + if !ok { + return fmt.Errorf("%v: //enum:stringfunc requires an argument", fs.Position(d.Pos)) + } + + case "doc": + arg, _ := slicesx.Get(d.Args, 0) + text, _ := slicesx.Get(d.Args, 1) + switch arg { + case "string": + enum.Docs.String = append(enum.Docs.String, text) + case "gostring": + enum.Docs.GoString = append(enum.Docs.GoString, text) + case "fromstring": + enum.Docs.FromString = append(enum.Docs.FromString, text) + default: + return fmt.Errorf("%v: invalid method for //enum:doc: %q", fs.Position(d.Pos), arg) + } + + default: + return fmt.Errorf("%v: unknown type directive %q", fs.Position(d.Pos), d.Name) + } + } + + if enum.Methods.String != "" && enum.Docs.String == nil { + enum.Docs.String = []string{"String implements [fmt.Stringer]."} + } + if enum.Methods.GoString != "" && enum.Docs.GoString == nil { + enum.Docs.GoString = []string{"GoString implements [fmt.GoStringer]."} + } + + for _, v := range constsByType[enum.Type.Name.Name] { + value := Value{Value: v} + dirs, err := ParseDirectives(fs, comments[v]) + if err != nil { + return err + } + for _, d := range dirs { + switch d.Name { + case "string": + name, ok := slicesx.Get(d.Args, 0) + if !ok { + return fmt.Errorf("%v: //enum:string requires an argument", fs.Position(d.Pos)) + } + value.String = strconv.Quote(name) + case "skip": + value.Skip = true + + default: + return fmt.Errorf("%v: unknown const directive %q", fs.Position(d.Pos), d.Name) + } + } + + enum.Values = append(enum.Values, value) + } + + enums = append(enums, enum) + } + + importList := make([]string, 0, len(imports)) + for imp := range imports { + importList = append(importList, strconv.Quote(imp)) + } + slices.Sort(importList) + + out, err := os.Create(output) + if err != nil { + return err + } + defer out.Close() + + info, err := buildinfo.ReadFile(os.Args[0]) + if err != nil { + return err + } + + return tmpl.ExecuteTemplate(out, "generated.go.tmpl", struct { + Binary, Package string + Imports []string + Enums []Enum + }{ + Binary: info.Path, + Package: f.Name.Name, + Imports: importList, + Enums: enums, + }) +} + +func main() { + if err := Main(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} diff --git a/internal/enum/generated.go.tmpl b/internal/enum/generated.go.tmpl new file mode 100644 index 00000000..a04b5d44 --- /dev/null +++ b/internal/enum/generated.go.tmpl @@ -0,0 +1,95 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by {{.Binary}}. DO NOT EDIT. + +package {{$pkg := .Package}}{{$pkg}} + +import ( +{{range .Imports}} {{.}} +{{end -}} +) +{{ range .Enums -}} +{{- $ty := .Type.Name.Name -}} +{{- $e := . -}} + +{{- $name := .Methods.String -}} +{{- if ne $name "" -}} +{{- range .Docs.String }} +// {{.}} +{{end -}} +func (v {{$ty}}) {{$name}}() string { + if int(v) < 0 || int(v) > len(_{{$ty}}Strings) { + return fmt.Sprintf("{{$ty}}(%v)", int(v)) + } + return _{{$ty}}Strings[int(v)] +} + +var _{{$ty}}Strings = [...]string { +{{range .Values}}{{if .Skip}}{{continue}}{{end}} {{(index .Value.Names 0).Name}}: {{"" -}} + {{- if ne $e.Methods.StringFunc "" -}} + {{$e.Methods.StringFunc}}("{{(index .Value.Names 0).Name}}") + {{- else if ne .String "" -}} + {{.String}} + {{- else -}} + "{{(index .Value.Names 0).Name}}" + {{- end }}, +{{end -}} +} + +{{end -}} + +{{- $name = .Methods.GoString -}} +{{- if ne $name "" -}} +{{- range .Docs.GoString -}} +{{with . -}} // {{.}} {{- else -}} // {{- end }} +{{end -}} +func (v {{$ty}}) {{$name}}() string { + if int(v) < 0 || int(v) > len(_{{$ty}}Strings) { + return fmt.Sprintf("{{$pkg}}.{{$ty}}(%v)", int(v)) + } + return _{{$ty}}Strings[int(v)] +} + +var _{{$ty}}GoStrings = [...]string { +{{range .Values}}{{if .Skip}}{{continue}}{{end}} {{(index .Value.Names 0).Name}}: "{{$pkg}}.{{(index .Value.Names 0).Name}}", +{{end -}} +} + +{{end -}} + +{{- $name = .Methods.FromString -}} +{{- if ne $name "" -}} +{{- range .Docs.FromString -}} +{{with . -}} // {{.}} {{- else -}} // {{- end }} +{{end -}} +func {{$name}}(s string) {{$ty}} { + return _{{$ty}}FromString[s] +} + +var _{{$ty}}FromString = map[string]{{$ty}} { +{{range .Values}}{{if .Skip}}{{continue}}{{end}} {{"" -}} + {{- if ne $e.Methods.StringFunc "" -}} + {{$e.Methods.StringFunc}}("{{(index .Value.Names 0).Name}}") + {{- else if ne .String "" -}} + {{.String}} + {{- else -}} + "{{(index .Value.Names 0).Name}}" + {{- end}}: {{(index .Value.Names 0).Name}}, +{{end -}} +} + +{{end -}} + +{{- end -}} \ No newline at end of file From 7c79525784a58077d3f0a73a8ada742af3a998ea Mon Sep 17 00:00:00 2001 From: Miguel Young de la Sota Date: Wed, 18 Dec 2024 14:57:15 -0800 Subject: [PATCH 2/6] use it in the new parser --- experimental/ast/decl.go | 13 +- experimental/ast/decl_enum.go | 60 +++++ experimental/ast/expr.go | 13 +- experimental/ast/expr_enum.go | 60 +++++ experimental/ast/expr_prefixed.go | 13 +- experimental/ast/expr_prefixed_enum.go | 48 ++++ experimental/ast/predeclared/predeclared.go | 108 ++------ .../ast/predeclared/predeclared_enum.go | 121 +++++++++ experimental/ast/type.go | 13 +- experimental/ast/type_enum.go | 52 ++++ experimental/ast/type_prefixed.go | 44 ++- experimental/ast/type_prefixed_enum.go | 54 ++++ experimental/internal/taxa/names.go | 240 ----------------- experimental/internal/taxa/taxa.go | 251 ++++++++---------- experimental/internal/taxa/taxa_enum.go | 222 ++++++++++++++++ experimental/internal/taxa/taxa_test.go | 21 -- experimental/token/kind.go | 34 +-- experimental/token/kind_enum.go | 58 ++++ 18 files changed, 865 insertions(+), 560 deletions(-) create mode 100644 experimental/ast/decl_enum.go create mode 100644 experimental/ast/expr_enum.go create mode 100644 experimental/ast/expr_prefixed_enum.go create mode 100644 experimental/ast/predeclared/predeclared_enum.go create mode 100644 experimental/ast/type_enum.go create mode 100644 experimental/ast/type_prefixed_enum.go delete mode 100644 experimental/internal/taxa/names.go create mode 100644 experimental/internal/taxa/taxa_enum.go create mode 100644 experimental/token/kind_enum.go diff --git a/experimental/ast/decl.go b/experimental/ast/decl.go index 42a23c4c..4b59ea20 100644 --- a/experimental/ast/decl.go +++ b/experimental/ast/decl.go @@ -22,6 +22,15 @@ import ( "github.com/bufbuild/protocompile/internal/arena" ) +//go:generate go run github.com/bufbuild/protocompile/internal/enum + +// DeclKind is a kind of declaration. There is one value of DeclKind for each +// Decl* type in this package. +// +//enum:string +//enum:gostring +type DeclKind int8 + const ( DeclKindNil DeclKind = iota DeclKindEmpty @@ -33,10 +42,6 @@ const ( DeclKindRange ) -// DeclKind is a kind of declaration. There is one value of DeclKind for each -// Decl* type in this package. -type DeclKind int8 - // DeclAny is any Decl* type in this package. // // Values of this type can be obtained by calling an AsAny method on a Decl* diff --git a/experimental/ast/decl_enum.go b/experimental/ast/decl_enum.go new file mode 100644 index 00000000..f44ac70b --- /dev/null +++ b/experimental/ast/decl_enum.go @@ -0,0 +1,60 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by github.com/bufbuild/protocompile/internal/enum. DO NOT EDIT. + +package ast + +import ( + "fmt" +) + +// String implements [fmt.Stringer]. +func (v DeclKind) String() string { + if int(v) < 0 || int(v) > len(_DeclKindStrings) { + return fmt.Sprintf("DeclKind(%v)", int(v)) + } + return _DeclKindStrings[int(v)] +} + +var _DeclKindStrings = [...]string { + DeclKindNil: "DeclKindNil", + DeclKindEmpty: "DeclKindEmpty", + DeclKindSyntax: "DeclKindSyntax", + DeclKindPackage: "DeclKindPackage", + DeclKindImport: "DeclKindImport", + DeclKindDef: "DeclKindDef", + DeclKindBody: "DeclKindBody", + DeclKindRange: "DeclKindRange", +} + +// GoString implements [fmt.GoStringer]. +func (v DeclKind) GoString() string { + if int(v) < 0 || int(v) > len(_DeclKindStrings) { + return fmt.Sprintf("ast.DeclKind(%v)", int(v)) + } + return _DeclKindStrings[int(v)] +} + +var _DeclKindGoStrings = [...]string { + DeclKindNil: "ast.DeclKindNil", + DeclKindEmpty: "ast.DeclKindEmpty", + DeclKindSyntax: "ast.DeclKindSyntax", + DeclKindPackage: "ast.DeclKindPackage", + DeclKindImport: "ast.DeclKindImport", + DeclKindDef: "ast.DeclKindDef", + DeclKindBody: "ast.DeclKindBody", + DeclKindRange: "ast.DeclKindRange", +} + diff --git a/experimental/ast/expr.go b/experimental/ast/expr.go index 99ae1400..dd17e5e5 100644 --- a/experimental/ast/expr.go +++ b/experimental/ast/expr.go @@ -23,6 +23,15 @@ import ( "github.com/bufbuild/protocompile/internal/arena" ) +//go:generate go run github.com/bufbuild/protocompile/internal/enum + +// ExprKind is a kind of expression. There is one value of ExprKind for each +// Expr* type in this package. +// +//enum:string +//enum:gostring +type ExprKind int8 + const ( ExprKindNil ExprKind = iota ExprKindLiteral @@ -34,10 +43,6 @@ const ( ExprKindField ) -// ExprKind is a kind of expression. There is one value of ExprKind for each -// Expr* type in this package. -type ExprKind int8 - // ExprAny is any ExprAny* type in this package. // // Values of this type can be obtained by calling an AsAny method on a ExprAny* diff --git a/experimental/ast/expr_enum.go b/experimental/ast/expr_enum.go new file mode 100644 index 00000000..79ee3835 --- /dev/null +++ b/experimental/ast/expr_enum.go @@ -0,0 +1,60 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by github.com/bufbuild/protocompile/internal/enum. DO NOT EDIT. + +package ast + +import ( + "fmt" +) + +// String implements [fmt.Stringer]. +func (v ExprKind) String() string { + if int(v) < 0 || int(v) > len(_ExprKindStrings) { + return fmt.Sprintf("ExprKind(%v)", int(v)) + } + return _ExprKindStrings[int(v)] +} + +var _ExprKindStrings = [...]string { + ExprKindNil: "ExprKindNil", + ExprKindLiteral: "ExprKindLiteral", + ExprKindPrefixed: "ExprKindPrefixed", + ExprKindPath: "ExprKindPath", + ExprKindRange: "ExprKindRange", + ExprKindArray: "ExprKindArray", + ExprKindDict: "ExprKindDict", + ExprKindField: "ExprKindField", +} + +// GoString implements [fmt.GoStringer]. +func (v ExprKind) GoString() string { + if int(v) < 0 || int(v) > len(_ExprKindStrings) { + return fmt.Sprintf("ast.ExprKind(%v)", int(v)) + } + return _ExprKindStrings[int(v)] +} + +var _ExprKindGoStrings = [...]string { + ExprKindNil: "ast.ExprKindNil", + ExprKindLiteral: "ast.ExprKindLiteral", + ExprKindPrefixed: "ast.ExprKindPrefixed", + ExprKindPath: "ast.ExprKindPath", + ExprKindRange: "ast.ExprKindRange", + ExprKindArray: "ast.ExprKindArray", + ExprKindDict: "ast.ExprKindDict", + ExprKindField: "ast.ExprKindField", +} + diff --git a/experimental/ast/expr_prefixed.go b/experimental/ast/expr_prefixed.go index 3416fcb3..55806824 100644 --- a/experimental/ast/expr_prefixed.go +++ b/experimental/ast/expr_prefixed.go @@ -19,14 +19,19 @@ import ( "github.com/bufbuild/protocompile/experimental/token" ) -const ( - ExprPrefixUnknown ExprPrefix = iota - ExprPrefixMinus -) +//go:generate go run github.com/bufbuild/protocompile/internal/enum // TypePrefix is a prefix for an expression, such as a minus sign. +// +//enum:string +//enum:gostring type ExprPrefix int8 +const ( + ExprPrefixUnknown ExprPrefix = iota //enum:string unknown + ExprPrefixMinus //enum:string "-" +) + // ExprPrefixByName looks up a prefix kind by name. // // If name is not a known prefix, returns [ExprPrefixUnknown]. diff --git a/experimental/ast/expr_prefixed_enum.go b/experimental/ast/expr_prefixed_enum.go new file mode 100644 index 00000000..6a5797f9 --- /dev/null +++ b/experimental/ast/expr_prefixed_enum.go @@ -0,0 +1,48 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by github.com/bufbuild/protocompile/internal/enum. DO NOT EDIT. + +package ast + +import ( + "fmt" +) + +// String implements [fmt.Stringer]. +func (v ExprPrefix) String() string { + if int(v) < 0 || int(v) > len(_ExprPrefixStrings) { + return fmt.Sprintf("ExprPrefix(%v)", int(v)) + } + return _ExprPrefixStrings[int(v)] +} + +var _ExprPrefixStrings = [...]string { + ExprPrefixUnknown: "unknown", + ExprPrefixMinus: "-", +} + +// GoString implements [fmt.GoStringer]. +func (v ExprPrefix) GoString() string { + if int(v) < 0 || int(v) > len(_ExprPrefixStrings) { + return fmt.Sprintf("ast.ExprPrefix(%v)", int(v)) + } + return _ExprPrefixStrings[int(v)] +} + +var _ExprPrefixGoStrings = [...]string { + ExprPrefixUnknown: "ast.ExprPrefixUnknown", + ExprPrefixMinus: "ast.ExprPrefixMinus", +} + diff --git a/experimental/ast/predeclared/predeclared.go b/experimental/ast/predeclared/predeclared.go index e45b2321..1b6e37af 100644 --- a/experimental/ast/predeclared/predeclared.go +++ b/experimental/ast/predeclared/predeclared.go @@ -22,7 +22,22 @@ // such counts as a predeclared identifier. package predeclared -import "fmt" +//go:generate go run github.com/bufbuild/protocompile/internal/enum + +//enum:import "strings" +//enum:stringfunc strings.ToLower +//enum:string +//enum:gostring + +//enum:fromstring Lookup +//enum:doc fromstring "Lookup looks up a builtin type by name." +//enum:doc fromstring +//enum:doc fromstring "If name does not name a builtin, returns [Unknown]." + +// Name is one of the built-in Protobuf names. These represent particular +// paths whose meaning the language overrides to mean something other than +// a relative path with that name. +type Name byte const ( Unknown Name = iota @@ -60,94 +75,7 @@ const ( Inf Nan - count // Total number of valid Name values, used in the names constant below. - // Aliases for the floating-point types with explicit bit-sizes. - Float32 = Float - Float64 = Double -) - -// Name is one of the built-in Protobuf names. These represent particular -// paths whose meaning the language overrides to mean something other than -// a relative path with that name. -type Name byte - -// Lookup looks up a builtin type by name. -// -// If name does not name a builtin, returns [Unknown]. -func Lookup(name string) Name { - // The zero value is Unknown, which map indexing will helpfully - // return for us here. - return byName[name] -} - -// String implements [strings.Stringer]. -func (n Name) String() string { - if int(n) < len(names) { - return names[int(n)] - } - return fmt.Sprintf("builtin%d", int(n)) -} - -// IsScalarType returns if this builtin name refers to one of the built-in -// scalar types (an integer or float, or one of bool, string, or bytes). -func (n Name) IsScalarType() bool { - switch n { - case - Int32, Int64, - UInt32, UInt64, - SInt32, SInt64, - - Fixed32, Fixed64, - SFixed32, SFixed64, - - Float, Double, - - Bool, String, Bytes: - return true - - default: - return false - } -} - -var ( - byName = map[string]Name{ - "int32": Int32, - "int64": Int64, - "uint32": UInt32, - "uint64": UInt64, - "sint32": SInt32, - "sint64": SInt64, - - "fixed32": Fixed32, - "fixed64": Fixed64, - "sfixed32": SFixed32, - "sfixed64": SFixed64, - - "float": Float, - "double": Double, - - "bool": Bool, - "string": String, - "bytes": Bytes, - - "map": Map, - "max": Max, - - "true": True, - "false": False, - "inf": Inf, - "nan": Nan, - } - - names = func() []string { - names := make([]string, count) - names[0] = "unknown" - - for name, idx := range byName { - names[idx] = name - } - return names - }() + Float32 = Float //enum:skip + Float64 = Double //enum:skip ) diff --git a/experimental/ast/predeclared/predeclared_enum.go b/experimental/ast/predeclared/predeclared_enum.go new file mode 100644 index 00000000..e0f79c85 --- /dev/null +++ b/experimental/ast/predeclared/predeclared_enum.go @@ -0,0 +1,121 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by github.com/bufbuild/protocompile/internal/enum. DO NOT EDIT. + +package predeclared + +import ( + "fmt" + "strings" +) + +// String implements [fmt.Stringer]. +func (v Name) String() string { + if int(v) < 0 || int(v) > len(_NameStrings) { + return fmt.Sprintf("Name(%v)", int(v)) + } + return _NameStrings[int(v)] +} + +var _NameStrings = [...]string { + Unknown: strings.ToLower("Unknown"), + Int32: strings.ToLower("Int32"), + Int64: strings.ToLower("Int64"), + UInt32: strings.ToLower("UInt32"), + UInt64: strings.ToLower("UInt64"), + SInt32: strings.ToLower("SInt32"), + SInt64: strings.ToLower("SInt64"), + Fixed32: strings.ToLower("Fixed32"), + Fixed64: strings.ToLower("Fixed64"), + SFixed32: strings.ToLower("SFixed32"), + SFixed64: strings.ToLower("SFixed64"), + Float: strings.ToLower("Float"), + Double: strings.ToLower("Double"), + Bool: strings.ToLower("Bool"), + String: strings.ToLower("String"), + Bytes: strings.ToLower("Bytes"), + Map: strings.ToLower("Map"), + Max: strings.ToLower("Max"), + True: strings.ToLower("True"), + False: strings.ToLower("False"), + Inf: strings.ToLower("Inf"), + Nan: strings.ToLower("Nan"), +} + +// GoString implements [fmt.GoStringer]. +func (v Name) GoString() string { + if int(v) < 0 || int(v) > len(_NameStrings) { + return fmt.Sprintf("predeclared.Name(%v)", int(v)) + } + return _NameStrings[int(v)] +} + +var _NameGoStrings = [...]string { + Unknown: "predeclared.Unknown", + Int32: "predeclared.Int32", + Int64: "predeclared.Int64", + UInt32: "predeclared.UInt32", + UInt64: "predeclared.UInt64", + SInt32: "predeclared.SInt32", + SInt64: "predeclared.SInt64", + Fixed32: "predeclared.Fixed32", + Fixed64: "predeclared.Fixed64", + SFixed32: "predeclared.SFixed32", + SFixed64: "predeclared.SFixed64", + Float: "predeclared.Float", + Double: "predeclared.Double", + Bool: "predeclared.Bool", + String: "predeclared.String", + Bytes: "predeclared.Bytes", + Map: "predeclared.Map", + Max: "predeclared.Max", + True: "predeclared.True", + False: "predeclared.False", + Inf: "predeclared.Inf", + Nan: "predeclared.Nan", +} + +// Lookup looks up a builtin type by name. +// +// If name does not name a builtin, returns [Unknown]. +func Lookup(s string) Name { + return _NameFromString[s] +} + +var _NameFromString = map[string]Name { + strings.ToLower("Unknown"): Unknown, + strings.ToLower("Int32"): Int32, + strings.ToLower("Int64"): Int64, + strings.ToLower("UInt32"): UInt32, + strings.ToLower("UInt64"): UInt64, + strings.ToLower("SInt32"): SInt32, + strings.ToLower("SInt64"): SInt64, + strings.ToLower("Fixed32"): Fixed32, + strings.ToLower("Fixed64"): Fixed64, + strings.ToLower("SFixed32"): SFixed32, + strings.ToLower("SFixed64"): SFixed64, + strings.ToLower("Float"): Float, + strings.ToLower("Double"): Double, + strings.ToLower("Bool"): Bool, + strings.ToLower("String"): String, + strings.ToLower("Bytes"): Bytes, + strings.ToLower("Map"): Map, + strings.ToLower("Max"): Max, + strings.ToLower("True"): True, + strings.ToLower("False"): False, + strings.ToLower("Inf"): Inf, + strings.ToLower("Nan"): Nan, +} + diff --git a/experimental/ast/type.go b/experimental/ast/type.go index d020d53a..d1efc286 100644 --- a/experimental/ast/type.go +++ b/experimental/ast/type.go @@ -22,6 +22,15 @@ import ( "github.com/bufbuild/protocompile/internal/arena" ) +//go:generate go run github.com/bufbuild/protocompile/internal/enum + +// TypeKind is a kind of type. There is one value of TypeKind for each +// Type* type in this package. +// +//enum:string +//enum:gostring +type TypeKind int8 + const ( TypeKindNil TypeKind = iota TypeKindPath @@ -29,10 +38,6 @@ const ( TypeKindGeneric ) -// TypeKind is a kind of type. There is one value of TypeKind for each -// Type* type in this package. -type TypeKind int8 - // TypeAny is any Type* type in this package. // // Values of this type can be obtained by calling an AsAny method on a Type* diff --git a/experimental/ast/type_enum.go b/experimental/ast/type_enum.go new file mode 100644 index 00000000..ba7d9a70 --- /dev/null +++ b/experimental/ast/type_enum.go @@ -0,0 +1,52 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by github.com/bufbuild/protocompile/internal/enum. DO NOT EDIT. + +package ast + +import ( + "fmt" +) + +// String implements [fmt.Stringer]. +func (v TypeKind) String() string { + if int(v) < 0 || int(v) > len(_TypeKindStrings) { + return fmt.Sprintf("TypeKind(%v)", int(v)) + } + return _TypeKindStrings[int(v)] +} + +var _TypeKindStrings = [...]string { + TypeKindNil: "TypeKindNil", + TypeKindPath: "TypeKindPath", + TypeKindPrefixed: "TypeKindPrefixed", + TypeKindGeneric: "TypeKindGeneric", +} + +// GoString implements [fmt.GoStringer]. +func (v TypeKind) GoString() string { + if int(v) < 0 || int(v) > len(_TypeKindStrings) { + return fmt.Sprintf("ast.TypeKind(%v)", int(v)) + } + return _TypeKindStrings[int(v)] +} + +var _TypeKindGoStrings = [...]string { + TypeKindNil: "ast.TypeKindNil", + TypeKindPath: "ast.TypeKindPath", + TypeKindPrefixed: "ast.TypeKindPrefixed", + TypeKindGeneric: "ast.TypeKindGeneric", +} + diff --git a/experimental/ast/type_prefixed.go b/experimental/ast/type_prefixed.go index 8abca2d8..2804c346 100644 --- a/experimental/ast/type_prefixed.go +++ b/experimental/ast/type_prefixed.go @@ -15,8 +15,6 @@ package ast import ( - "fmt" - "github.com/bufbuild/protocompile/experimental/report" "github.com/bufbuild/protocompile/experimental/token" ) @@ -72,20 +70,28 @@ func (t TypePrefixed) Span() report.Span { return report.Join(t.PrefixToken(), t.Type()) } +//go:generate go run github.com/bufbuild/protocompile/internal/enum + +// TypeKind is a kind of type. There is one value of TypeKind for each +// Type* type in this package. +// +// TypePrefix is a prefix for a type, such as required, optional, or repeated. +// +//enum:string +//enum:gostring +type TypePrefix int8 + const ( - TypePrefixUnknown TypePrefix = iota - TypePrefixOptional - TypePrefixRepeated - TypePrefixRequired + TypePrefixUnknown TypePrefix = iota //enum:string unknown + TypePrefixOptional //enum:string optional + TypePrefixRepeated //enum:string repeated + TypePrefixRequired //enum:string required // This is the "stream Foo.bar" syntax of RPC methods. It is also treated as // a prefix. - TypePrefixStream + TypePrefixStream //enum:string stream ) -// TypePrefix is a prefix for a type, such as required, optional, or repeated. -type TypePrefix int8 - // TypePrefixByName looks up a prefix kind by name. // // If name is not a known prefix, returns [TypePrefixUnknown]. @@ -103,21 +109,3 @@ func TypePrefixByName(name string) TypePrefix { return TypePrefixUnknown } } - -// String implements [strings.Stringer]. -func (m TypePrefix) String() string { - switch m { - case TypePrefixUnknown: - return "unknown" - case TypePrefixOptional: - return "optional" - case TypePrefixRepeated: - return "repeated" - case TypePrefixRequired: - return "required" - case TypePrefixStream: - return "stream" - default: - return fmt.Sprintf("modifier%d", int(m)) - } -} diff --git a/experimental/ast/type_prefixed_enum.go b/experimental/ast/type_prefixed_enum.go new file mode 100644 index 00000000..4406513f --- /dev/null +++ b/experimental/ast/type_prefixed_enum.go @@ -0,0 +1,54 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by github.com/bufbuild/protocompile/internal/enum. DO NOT EDIT. + +package ast + +import ( + "fmt" +) + +// String implements [fmt.Stringer]. +func (v TypePrefix) String() string { + if int(v) < 0 || int(v) > len(_TypePrefixStrings) { + return fmt.Sprintf("TypePrefix(%v)", int(v)) + } + return _TypePrefixStrings[int(v)] +} + +var _TypePrefixStrings = [...]string { + TypePrefixUnknown: "unknown", + TypePrefixOptional: "optional", + TypePrefixRepeated: "repeated", + TypePrefixRequired: "required", + TypePrefixStream: "stream", +} + +// GoString implements [fmt.GoStringer]. +func (v TypePrefix) GoString() string { + if int(v) < 0 || int(v) > len(_TypePrefixStrings) { + return fmt.Sprintf("ast.TypePrefix(%v)", int(v)) + } + return _TypePrefixStrings[int(v)] +} + +var _TypePrefixGoStrings = [...]string { + TypePrefixUnknown: "ast.TypePrefixUnknown", + TypePrefixOptional: "ast.TypePrefixOptional", + TypePrefixRepeated: "ast.TypePrefixRepeated", + TypePrefixRequired: "ast.TypePrefixRequired", + TypePrefixStream: "ast.TypePrefixStream", +} + diff --git a/experimental/internal/taxa/names.go b/experimental/internal/taxa/names.go deleted file mode 100644 index 2220bc35..00000000 --- a/experimental/internal/taxa/names.go +++ /dev/null @@ -1,240 +0,0 @@ -// Copyright 2020-2024 Buf Technologies, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package taxa - -var ( - // names is an array of user-visible names of all of the productions in this - // package. - names = [...]string{ - Unknown: "", - Unrecognized: "unrecognized token", - TopLevel: "file scope", - EOF: "end-of-file", - - Decl: "declaration", - Empty: "empty declaration", - Syntax: "`syntax` declaration", - Edition: "`edition` declaration", - Package: "`package` declaration", - Import: "import", - WeakImport: "weak import", - PublicImport: "public import", - Extensions: "extension range", - Reserved: "reserved range", - Body: "definition body", - - Def: "definition", - Message: "message definition", - Enum: "enum definition", - Service: "service definition", - Extend: "message extension block", - Oneof: "oneof definition", - - Option: "option setting", - CustomOption: "custom option setting", - - Field: "message field", - EnumValue: "enum value", - Method: "service method", - - CompactOptions: "compact options", - MethodIns: "method parameter list", - MethodOuts: "method return type", - - FieldTag: "message field tag", - OptionValue: "option setting value", - - QualifiedName: "qualified name", - FullyQualifiedName: "fully qualified name", - ExtensionName: "extension name", - - Expr: "expression", - Range: "range expression", - Array: "array expression", - Dict: "message expression", - DictField: "message field value", - - Type: "type", - TypePath: "type name", - TypeParams: "type parameters", - - Whitespace: "whitespace", - Comment: "comment", - Ident: "identifier", - String: "string literal", - Float: "floating-point literal", - Int: "integer literal", - - Semicolon: "`;`", - Comma: "`,`", - Slash: "`/`", - Colon: "`:`", - Equals: "`=`", - Minus: "`-`", - Period: "`.`", - - LParen: "`(`", - LBracket: "`[`", - LBrace: "`{`", - LAngle: "`<`", - - RParen: "`)`", - RBracket: "`]`", - RBrace: "`}`", - RAngle: "`>`", - - Parens: "`(...)`", - Brackets: "`[...]`", - Braces: "`{...}`", - Angles: "`<...>`", - - KeywordSyntax: "`syntax`", - KeywordEdition: "`edition`", - KeywordImport: "`import`", - KeywordWeak: "`weak`", - KeywordPublic: "`public`", - KeywordPackage: "`package`", - - KeywordOption: "`option`", - KeywordMessage: "`message`", - KeywordEnum: "`enum`", - KeywordService: "`service`", - KeywordExtend: "`extend`", - KeywordOneof: "`oneof`", - - KeywordExtensions: "`extensions`", - KeywordReserved: "`reserved`", - KeywordTo: "`to`", - KeywordRPC: "`rpc`", - KeywordReturns: "`returns`", - - KeywordOptional: "`optional`", - KeywordRepeated: "`repeated`", - KeywordRequired: "`required`", - KeywordGroup: "`group`", - KeywordStream: "`stream`", - } - - // constNames is an array of the names of the constants, for use in GoString(). - constNames = [...]string{ - Unknown: "Unknown", - Unrecognized: "Unrecognized", - TopLevel: "TopLevel", - EOF: "EOF", - - Decl: "Decl", - Empty: "Empty", - Syntax: "Syntax", - Edition: "Edition", - Package: "Package", - Import: "Import", - WeakImport: "WeakImport", - PublicImport: "PublicImport", - Extensions: "Extensions", - Reserved: "Reserved", - Body: "Body", - - Def: "Def", - Message: "Message", - Enum: "Enum", - Service: "Service", - Extend: "Extend", - Oneof: "Oneof", - - Option: "Option", - CustomOption: "CustomOption", - - Field: "Field", - EnumValue: "EnumValue", - Method: "Method", - - FieldTag: "FieldTag", - OptionValue: "OptionValue", - - CompactOptions: "CompactOptions", - MethodIns: "MethodIns", - MethodOuts: "MethodOuts", - - QualifiedName: "QualifiedName", - FullyQualifiedName: "FullyQualifiedName", - ExtensionName: "ExtensionName", - - Expr: "Expr", - Range: "Range", - Array: "Array", - Dict: "Dict", - DictField: "DictField", - - Type: "Type", - TypePath: "TypePath", - TypeParams: "TypeParams", - - Whitespace: "Whitespace", - Comment: "Comment", - Ident: "Ident", - String: "String", - Float: "Float", - Int: "Int", - - Semicolon: "Semicolon", - Comma: "Comma", - Slash: "Slash", - Colon: "Colon", - Equals: "Equals", - Minus: "Minus", - Period: "Period", - - LParen: "LParen", - LBracket: "LBracket", - LBrace: "LBrace", - LAngle: "LAngle", - - RParen: "RParen", - RBracket: "RBracket", - RBrace: "RBrace", - RAngle: "RAngle", - - Parens: "Parens", - Brackets: "Brackets", - Braces: "Braces", - Angles: "Angles", - - KeywordSyntax: "KeywordSyntax", - KeywordEdition: "KeywordEdition", - KeywordImport: "KeywordImport", - KeywordWeak: "KeywordWeak", - KeywordPublic: "KeywordPublic", - KeywordPackage: "KeywordPackage", - - KeywordOption: "KeywordOption", - KeywordMessage: "KeywordMessage", - KeywordEnum: "KeywordEnum", - KeywordService: "KeywordService", - KeywordExtend: "KeywordExtend", - KeywordOneof: "KeywordOneof", - - KeywordExtensions: "KeywordExtensions", - KeywordReserved: "KeywordReserved", - KeywordTo: "KeywordTo", - KeywordRPC: "KeywordRPC", - KeywordReturns: "KeywordReturns", - - KeywordOptional: "KeywordOptional", - KeywordRepeated: "KeywordRepeated", - KeywordRequired: "KeywordRequired", - KeywordGroup: "KeywordGroup", - KeywordStream: "KeywordStream", - } -) diff --git a/experimental/internal/taxa/taxa.go b/experimental/internal/taxa/taxa.go index 494bc399..51344933 100644 --- a/experimental/internal/taxa/taxa.go +++ b/experimental/internal/taxa/taxa.go @@ -23,123 +23,125 @@ package taxa import ( "fmt" - "strconv" - - "github.com/bufbuild/protocompile/internal/iter" ) +//go:generate go run github.com/bufbuild/protocompile/internal/enum + // Noun is a syntactic or semantic element within the grammar that can be // referred to within a diagnostic. +// +//enum:string +//enum:gostring type Noun int const ( - Unknown Noun = iota - Unrecognized - TopLevel - EOF - - Decl - Empty - Syntax - Edition - Package - Import - WeakImport - PublicImport - Extensions - Reserved - Body - - Def - Message - Enum - Service - Extend - Oneof - - Option - CustomOption - - Field - EnumValue - Method - - FieldTag - OptionValue - - CompactOptions - MethodIns - MethodOuts - - QualifiedName - FullyQualifiedName - ExtensionName - - Expr - Range - Array - Dict - DictField - - Type - TypePath - TypeParams - - Whitespace - Comment - Ident - String - Float - Int - - Semicolon - Comma - Slash - Colon - Equals - Minus - Period - - LParen - LBracket - LBrace - LAngle - - RParen - RBracket - RBrace - RAngle - - Parens - Brackets - Braces - Angles - - KeywordSyntax - KeywordEdition - KeywordImport - KeywordWeak - KeywordPublic - KeywordPackage - - KeywordOption - KeywordMessage - KeywordEnum - KeywordService - KeywordExtend - KeywordOneof - - KeywordExtensions - KeywordReserved - KeywordTo - KeywordRPC - KeywordReturns - - KeywordOptional - KeywordRepeated - KeywordRequired - KeywordGroup - KeywordStream + Unknown Noun = iota //enum:string "" + Unrecognized //enum:string "unrecognized token" + TopLevel //enum:string "file scope" + EOF //enum:string "end-of-file" + + Decl //enum:string "declaration" + Empty //enum:string "empty declaration" + Syntax //enum:string "`syntax` declaration" + Edition //enum:string "`edition` declaration" + Package //enum:string "`package` declaration" + Import //enum:string "import" + WeakImport //enum:string "weak import" + PublicImport //enum:string "public import" + Extensions //enum:string "extension range" + Reserved //enum:string "reserved range" + Body //enum:string "definition body" + + Def //enum:string "definition" + Message //enum:string "message definition" + Enum //enum:string "enum definition" + Service //enum:string "service definition" + Extend //enum:string "message extension block" + Oneof //enum:string "oneof definition" + + Option //enum:string "option setting" + CustomOption //enum:string "custom option setting" + + Field //enum:string "message field" + EnumValue //enum:string "enum value" + Method //enum:string "service method" + + CompactOptions //enum:string "compact options" + MethodIns //enum:string "method parameter list" + MethodOuts //enum:string "method return type" + + FieldTag //enum:string "message field tag" + OptionValue //enum:string "option setting value" + + QualifiedName //enum:string "qualified name" + FullyQualifiedName //enum:string "fully qualified name" + ExtensionName //enum:string "extension name" + + Expr //enum:string "expression" + Range //enum:string "range expression" + Array //enum:string "array expression" + Dict //enum:string "message expression" + DictField //enum:string "message field value" + + Type //enum:string "type" + TypePath //enum:string "type name" + TypeParams //enum:string "type parameters" + + Whitespace //enum:string "whitespace" + Comment //enum:string "comment" + Ident //enum:string "identifier" + String //enum:string "string literal" + Float //enum:string "floating-point literal" + Int //enum:string "integer literal" + + Semicolon //enum:string "`;`" + Comma //enum:string "`,`" + Slash //enum:string "`/`" + Colon //enum:string "`:`" + Equals //enum:string "`=`" + Minus //enum:string "`-`" + Period //enum:string "`.`" + + LParen //enum:string "`(`" + LBracket //enum:string "`[`" + LBrace //enum:string "`{`" + LAngle //enum:string "`<`" + + RParen //enum:string "`)`" + RBracket //enum:string "`]`" + RBrace //enum:string "`}`" + RAngle //enum:string "`>`" + + Parens //enum:string "`(...)`" + Brackets //enum:string "`[...]`" + Braces //enum:string "`{...}`" + Angles //enum:string "`<...>`" + + KeywordSyntax //enum:string "`syntax`" + KeywordEdition //enum:string "`edition`" + KeywordImport //enum:string "`import`" + KeywordWeak //enum:string "`weak`" + KeywordPublic //enum:string "`public`" + KeywordPackage //enum:string "`package`" + + KeywordOption //enum:string "`option`" + KeywordMessage //enum:string "`message`" + KeywordEnum //enum:string "`enum`" + KeywordService //enum:string "`service`" + KeywordExtend //enum:string "`extend`" + KeywordOneof //enum:string "`oneof`" + + KeywordExtensions //enum:string "`extensions`" + KeywordReserved //enum:string "`reserved`" + KeywordTo //enum:string "`to`" + KeywordRPC //enum:string "`rpc`" + KeywordReturns //enum:string "`returns`" + + KeywordOptional //enum:string "`optional`" + KeywordRepeated //enum:string "`repeated`" + KeywordRequired //enum:string "`required`" + KeywordGroup //enum:string "`group`" + KeywordStream //enum:string "`stream`" // total is the total number of known [What] values. total int = iota @@ -165,35 +167,6 @@ func (s Noun) AsSet() Set { return NewSet(s) } -// String implements [fmt.Stringer]. -func (s Noun) String() string { - if int(s) >= len(names) { - return names[0] - } - return names[s] -} - -// All returns an iterator over all subjects. -func All() iter.Seq[Noun] { - return func(yield func(Noun) bool) { - for i := 0; i < total; i++ { - if !yield(Noun(i)) { - break - } - } - } -} - -// GoString implements [fmt.GoStringer]. -// -// This exists to get pretty output out of the assert package. -func (s Noun) GoString() string { - if int(s) >= len(constNames) { - return strconv.Itoa(int(s)) - } - return "what." + constNames[s] -} - // Place is a location within the grammar that can be referred to within a // diagnostic. // diff --git a/experimental/internal/taxa/taxa_enum.go b/experimental/internal/taxa/taxa_enum.go new file mode 100644 index 00000000..4d853d06 --- /dev/null +++ b/experimental/internal/taxa/taxa_enum.go @@ -0,0 +1,222 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by github.com/bufbuild/protocompile/internal/enum. DO NOT EDIT. + +package taxa + +import ( + "fmt" +) + +// String implements [fmt.Stringer]. +func (v Noun) String() string { + if int(v) < 0 || int(v) > len(_NounStrings) { + return fmt.Sprintf("Noun(%v)", int(v)) + } + return _NounStrings[int(v)] +} + +var _NounStrings = [...]string { + Unknown: "", + Unrecognized: "unrecognized token", + TopLevel: "file scope", + EOF: "end-of-file", + Decl: "declaration", + Empty: "empty declaration", + Syntax: "`syntax` declaration", + Edition: "`edition` declaration", + Package: "`package` declaration", + Import: "import", + WeakImport: "weak import", + PublicImport: "public import", + Extensions: "extension range", + Reserved: "reserved range", + Body: "definition body", + Def: "definition", + Message: "message definition", + Enum: "enum definition", + Service: "service definition", + Extend: "message extension block", + Oneof: "oneof definition", + Option: "option setting", + CustomOption: "custom option setting", + Field: "message field", + EnumValue: "enum value", + Method: "service method", + CompactOptions: "compact options", + MethodIns: "method parameter list", + MethodOuts: "method return type", + FieldTag: "message field tag", + OptionValue: "option setting value", + QualifiedName: "qualified name", + FullyQualifiedName: "fully qualified name", + ExtensionName: "extension name", + Expr: "expression", + Range: "range expression", + Array: "array expression", + Dict: "message expression", + DictField: "message field value", + Type: "type", + TypePath: "type name", + TypeParams: "type parameters", + Whitespace: "whitespace", + Comment: "comment", + Ident: "identifier", + String: "string literal", + Float: "floating-point literal", + Int: "integer literal", + Semicolon: "`;`", + Comma: "`,`", + Slash: "`/`", + Colon: "`:`", + Equals: "`=`", + Minus: "`-`", + Period: "`.`", + LParen: "`(`", + LBracket: "`[`", + LBrace: "`{`", + LAngle: "`<`", + RParen: "`)`", + RBracket: "`]`", + RBrace: "`}`", + RAngle: "`>`", + Parens: "`(...)`", + Brackets: "`[...]`", + Braces: "`{...}`", + Angles: "`<...>`", + KeywordSyntax: "`syntax`", + KeywordEdition: "`edition`", + KeywordImport: "`import`", + KeywordWeak: "`weak`", + KeywordPublic: "`public`", + KeywordPackage: "`package`", + KeywordOption: "`option`", + KeywordMessage: "`message`", + KeywordEnum: "`enum`", + KeywordService: "`service`", + KeywordExtend: "`extend`", + KeywordOneof: "`oneof`", + KeywordExtensions: "`extensions`", + KeywordReserved: "`reserved`", + KeywordTo: "`to`", + KeywordRPC: "`rpc`", + KeywordReturns: "`returns`", + KeywordOptional: "`optional`", + KeywordRepeated: "`repeated`", + KeywordRequired: "`required`", + KeywordGroup: "`group`", + KeywordStream: "`stream`", +} + +// GoString implements [fmt.GoStringer]. +func (v Noun) GoString() string { + if int(v) < 0 || int(v) > len(_NounStrings) { + return fmt.Sprintf("taxa.Noun(%v)", int(v)) + } + return _NounStrings[int(v)] +} + +var _NounGoStrings = [...]string { + Unknown: "taxa.Unknown", + Unrecognized: "taxa.Unrecognized", + TopLevel: "taxa.TopLevel", + EOF: "taxa.EOF", + Decl: "taxa.Decl", + Empty: "taxa.Empty", + Syntax: "taxa.Syntax", + Edition: "taxa.Edition", + Package: "taxa.Package", + Import: "taxa.Import", + WeakImport: "taxa.WeakImport", + PublicImport: "taxa.PublicImport", + Extensions: "taxa.Extensions", + Reserved: "taxa.Reserved", + Body: "taxa.Body", + Def: "taxa.Def", + Message: "taxa.Message", + Enum: "taxa.Enum", + Service: "taxa.Service", + Extend: "taxa.Extend", + Oneof: "taxa.Oneof", + Option: "taxa.Option", + CustomOption: "taxa.CustomOption", + Field: "taxa.Field", + EnumValue: "taxa.EnumValue", + Method: "taxa.Method", + CompactOptions: "taxa.CompactOptions", + MethodIns: "taxa.MethodIns", + MethodOuts: "taxa.MethodOuts", + FieldTag: "taxa.FieldTag", + OptionValue: "taxa.OptionValue", + QualifiedName: "taxa.QualifiedName", + FullyQualifiedName: "taxa.FullyQualifiedName", + ExtensionName: "taxa.ExtensionName", + Expr: "taxa.Expr", + Range: "taxa.Range", + Array: "taxa.Array", + Dict: "taxa.Dict", + DictField: "taxa.DictField", + Type: "taxa.Type", + TypePath: "taxa.TypePath", + TypeParams: "taxa.TypeParams", + Whitespace: "taxa.Whitespace", + Comment: "taxa.Comment", + Ident: "taxa.Ident", + String: "taxa.String", + Float: "taxa.Float", + Int: "taxa.Int", + Semicolon: "taxa.Semicolon", + Comma: "taxa.Comma", + Slash: "taxa.Slash", + Colon: "taxa.Colon", + Equals: "taxa.Equals", + Minus: "taxa.Minus", + Period: "taxa.Period", + LParen: "taxa.LParen", + LBracket: "taxa.LBracket", + LBrace: "taxa.LBrace", + LAngle: "taxa.LAngle", + RParen: "taxa.RParen", + RBracket: "taxa.RBracket", + RBrace: "taxa.RBrace", + RAngle: "taxa.RAngle", + Parens: "taxa.Parens", + Brackets: "taxa.Brackets", + Braces: "taxa.Braces", + Angles: "taxa.Angles", + KeywordSyntax: "taxa.KeywordSyntax", + KeywordEdition: "taxa.KeywordEdition", + KeywordImport: "taxa.KeywordImport", + KeywordWeak: "taxa.KeywordWeak", + KeywordPublic: "taxa.KeywordPublic", + KeywordPackage: "taxa.KeywordPackage", + KeywordOption: "taxa.KeywordOption", + KeywordMessage: "taxa.KeywordMessage", + KeywordEnum: "taxa.KeywordEnum", + KeywordService: "taxa.KeywordService", + KeywordExtend: "taxa.KeywordExtend", + KeywordOneof: "taxa.KeywordOneof", + KeywordExtensions: "taxa.KeywordExtensions", + KeywordReserved: "taxa.KeywordReserved", + KeywordTo: "taxa.KeywordTo", + KeywordRPC: "taxa.KeywordRPC", + KeywordReturns: "taxa.KeywordReturns", + KeywordOptional: "taxa.KeywordOptional", + KeywordRepeated: "taxa.KeywordRepeated", + KeywordRequired: "taxa.KeywordRequired", + KeywordGroup: "taxa.KeywordGroup", + KeywordStream: "taxa.KeywordStream", +} + diff --git a/experimental/internal/taxa/taxa_test.go b/experimental/internal/taxa/taxa_test.go index 53c75560..ad16a1f9 100644 --- a/experimental/internal/taxa/taxa_test.go +++ b/experimental/internal/taxa/taxa_test.go @@ -23,27 +23,6 @@ import ( "github.com/bufbuild/protocompile/internal/ext/slicesx" ) -func TestAllStringify(t *testing.T) { - t.Parallel() - - // We use only one map to test for duplicates, because no noun should have - // the same user-visible string as its Go constant name. - strings := make(map[string]struct{}) - taxa.All()(func(s taxa.Noun) bool { - name := s.String() - assert.NotEqual(t, "", name) - assert.NotContains(t, strings, name) - strings[name] = struct{}{} - - name = s.GoString() - assert.NotEqual(t, "", name) - assert.NotContains(t, strings, name) - strings[name] = struct{}{} - - return true - }) -} - func TestSet(t *testing.T) { t.Parallel() diff --git a/experimental/token/kind.go b/experimental/token/kind.go index 0718d181..18a99d77 100644 --- a/experimental/token/kind.go +++ b/experimental/token/kind.go @@ -14,7 +14,13 @@ package token -import "fmt" +//go:generate go run github.com/bufbuild/protocompile/internal/enum + +// Kind identifies what kind of token a particular [Token] is. +// +//enum:string +//enum:gostring +type Kind byte const ( Unrecognized Kind = iota // Unrecognized garbage in the input file. @@ -26,38 +32,14 @@ const ( Number // A run of digits that is some kind of number. Punct // Some punctuation. May be a non-leaf for delimiters like {}. _KindUnused // Reserved for future use. + //enum:skip // DO NOT ADD MORE TOKEN KINDS: ONLY THREE BITS ARE AVAILABLE // TO STORE THEM. ) -// Kind identifies what kind of token a particular [Token] is. -type Kind byte - // IsSkippable returns whether this is a token that should be examined during // syntactic analysis. func (t Kind) IsSkippable() bool { return t == Space || t == Comment || t == Unrecognized } - -// String implements [strings.Stringer]. -func (t Kind) String() string { - switch t { - case Unrecognized: - return "Unrecognized" - case Space: - return "Space" - case Comment: - return "Comment" - case Ident: - return "Ident" - case String: - return "String" - case Number: - return "Number" - case Punct: - return "Punct" - default: - return fmt.Sprintf("token.Kind(%d)", int(t)) - } -} diff --git a/experimental/token/kind_enum.go b/experimental/token/kind_enum.go new file mode 100644 index 00000000..00f4846d --- /dev/null +++ b/experimental/token/kind_enum.go @@ -0,0 +1,58 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by github.com/bufbuild/protocompile/internal/enum. DO NOT EDIT. + +package token + +import ( + "fmt" +) + +// String implements [fmt.Stringer]. +func (v Kind) String() string { + if int(v) < 0 || int(v) > len(_KindStrings) { + return fmt.Sprintf("Kind(%v)", int(v)) + } + return _KindStrings[int(v)] +} + +var _KindStrings = [...]string { + Unrecognized: "Unrecognized", + Space: "Space", + Comment: "Comment", + Ident: "Ident", + String: "String", + Number: "Number", + Punct: "Punct", +} + +// GoString implements [fmt.GoStringer]. +func (v Kind) GoString() string { + if int(v) < 0 || int(v) > len(_KindStrings) { + return fmt.Sprintf("token.Kind(%v)", int(v)) + } + return _KindStrings[int(v)] +} + +var _KindGoStrings = [...]string { + Unrecognized: "token.Unrecognized", + Space: "token.Space", + Comment: "token.Comment", + Ident: "token.Ident", + String: "token.String", + Number: "token.Number", + Punct: "token.Punct", +} + From 5ee265fe5cf3983fd41babbaf23897e9da5d6e46 Mon Sep 17 00:00:00 2001 From: Miguel Young de la Sota Date: Wed, 18 Dec 2024 14:58:33 -0800 Subject: [PATCH 3/6] lint --- internal/enum/enum.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/enum/enum.go b/internal/enum/enum.go index f1b43f23..19b7d401 100644 --- a/internal/enum/enum.go +++ b/internal/enum/enum.go @@ -74,7 +74,7 @@ func ParseDirectives(fs *token.FileSet, comments []*ast.CommentGroup) ([]Directi var err error var args []string sc := scanner.Scanner{ - Error: func(s *scanner.Scanner, msg string) { + Error: func(_ *scanner.Scanner, msg string) { err = fmt.Errorf("%s", msg) }, Mode: scanner.ScanIdents | scanner.ScanStrings | scanner.ScanRawStrings, @@ -166,7 +166,7 @@ func Main() error { case token.CONST: var ty string for _, spec := range decl.Specs { - v := spec.(*ast.ValueSpec) + v := spec.(*ast.ValueSpec) //nolint:errcheck if v.Type == nil { constsByType[ty] = append(constsByType[ty], v) } @@ -179,9 +179,9 @@ func Main() error { } imports := map[string]struct{}{"fmt": {}} - var enums []Enum + enums := make([]Enum, 0, len(types)) for _, ty := range types { - enum := Enum{Type: ty.Specs[0].(*ast.TypeSpec)} + enum := Enum{Type: ty.Specs[0].(*ast.TypeSpec)} //nolint:errcheck dirs, err := ParseDirectives(fs, comments[ty]) if err != nil { return err From 3aa75469f30b8be20e29d9e43233ac2b20491799 Mon Sep 17 00:00:00 2001 From: Miguel Young de la Sota Date: Wed, 18 Dec 2024 15:09:23 -0800 Subject: [PATCH 4/6] document the enum generator --- internal/enum/enum.go | 37 +++++++++++++++++++++++++++++++++ internal/enum/generated.go.tmpl | 12 +++++------ 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/internal/enum/enum.go b/internal/enum/enum.go index 19b7d401..f7bdc359 100644 --- a/internal/enum/enum.go +++ b/internal/enum/enum.go @@ -17,6 +17,43 @@ // To generate boilerplate for a given file, use // // //go:generate go run github.com/bufbuild/protocompile/internal/enum +// +// To generate code for an enum type, annotate it with the appropriate +// directives. +// +// - //enum:string Name will generate a stringifying methods named Name, +// which default to "String". +// +// - //enum:gostring Name is similar, but the default name is "GoString" +// and each constant stringifies to "package.Constant". +// +// - //enum:fromstring Name generates a function that takes a string and +// returns the constant that stringifies to that value, or 0 if there is +// no such value. +// +// - //enum:doc directive "..." adds a line of doc comment to the method +// generated by directive with the given text. +// +// - //enum:import "..." will include a package to import in the generated +// file. +// +// - //enum:skip, attached to an enum constant, will cause that constant to +// be ignored by the generator. +// +// - //enum:stringfunc Name provides a function to use for stringifying +// based on the name of a constant (see below). +// +// The string representation of an enum constant is computed by the following +// process: +// +// 1. If the constant is annotated with //enum:string "...", it will use that +// value. +// +// 2. If the enum type is annotated with //enum:stringfunc Name, the string +// will be Name("Constant"), where Constant is the Go-declared name of the +// constant. +// +// 3. The string is "Constant". package main import ( diff --git a/internal/enum/generated.go.tmpl b/internal/enum/generated.go.tmpl index a04b5d44..cd80af15 100644 --- a/internal/enum/generated.go.tmpl +++ b/internal/enum/generated.go.tmpl @@ -38,10 +38,10 @@ func (v {{$ty}}) {{$name}}() string { var _{{$ty}}Strings = [...]string { {{range .Values}}{{if .Skip}}{{continue}}{{end}} {{(index .Value.Names 0).Name}}: {{"" -}} - {{- if ne $e.Methods.StringFunc "" -}} - {{$e.Methods.StringFunc}}("{{(index .Value.Names 0).Name}}") - {{- else if ne .String "" -}} + {{- if ne .String "" -}} {{.String}} + {{- else if ne $e.Methods.StringFunc "" -}} + {{$e.Methods.StringFunc}}("{{(index .Value.Names 0).Name}}") {{- else -}} "{{(index .Value.Names 0).Name}}" {{- end }}, @@ -80,10 +80,10 @@ func {{$name}}(s string) {{$ty}} { var _{{$ty}}FromString = map[string]{{$ty}} { {{range .Values}}{{if .Skip}}{{continue}}{{end}} {{"" -}} - {{- if ne $e.Methods.StringFunc "" -}} - {{$e.Methods.StringFunc}}("{{(index .Value.Names 0).Name}}") - {{- else if ne .String "" -}} + {{- if ne .String "" -}} {{.String}} + {{- else if ne $e.Methods.StringFunc "" -}} + {{$e.Methods.StringFunc}}("{{(index .Value.Names 0).Name}}") {{- else -}} "{{(index .Value.Names 0).Name}}" {{- end}}: {{(index .Value.Names 0).Name}}, From 4d3cd0681a9d3581b3204fe49710d5597c01582e Mon Sep 17 00:00:00 2001 From: Miguel Young de la Sota Date: Fri, 20 Dec 2024 11:41:12 -0800 Subject: [PATCH 5/6] wip --- experimental/ast/predeclared/doc.go | 23 ++ experimental/ast/predeclared/predeclared.go | 145 ++++++-- .../ast/predeclared/predeclared.go.yaml | 109 ++++++ .../ast/predeclared/predeclared_enum.go | 121 ------- internal/enum/enum.go | 335 +++++------------- internal/enum/generated.go.tmpl | 122 +++---- internal/ext/slicesx/collect.go | 9 + 7 files changed, 409 insertions(+), 455 deletions(-) create mode 100644 experimental/ast/predeclared/doc.go create mode 100644 experimental/ast/predeclared/predeclared.go.yaml delete mode 100644 experimental/ast/predeclared/predeclared_enum.go diff --git a/experimental/ast/predeclared/doc.go b/experimental/ast/predeclared/doc.go new file mode 100644 index 00000000..3216cd29 --- /dev/null +++ b/experimental/ast/predeclared/doc.go @@ -0,0 +1,23 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// package predeclared provides all of the identifiers with a special meaning +// in Protobuf. +// +// These are not keywords, but are rather special names injected into scope in +// places where any user-defined path is allowed. For example, the identifier +// string overrides the meaning of a path with a single identifier called string, +// (such as a reference to a message named string in the current package) and as +// such counts as a predeclared identifier. +package predeclared diff --git a/experimental/ast/predeclared/predeclared.go b/experimental/ast/predeclared/predeclared.go index 1b6e37af..4d5e7253 100644 --- a/experimental/ast/predeclared/predeclared.go +++ b/experimental/ast/predeclared/predeclared.go @@ -12,27 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -// package predeclared provides all of the identifiers with a special meaning -// in Protobuf. -// -// These are not keywords, but are rather special names injected into scope in -// places where any user-defined path is allowed. For example, the identifier -// string overrides the meaning of a path with a single identifier called string, -// (such as a reference to a message named string in the current package) and as -// such counts as a predeclared identifier. -package predeclared +// Code generated by github.com/bufbuild/protocompile/internal/enum. DO NOT EDIT. -//go:generate go run github.com/bufbuild/protocompile/internal/enum +package predeclared -//enum:import "strings" -//enum:stringfunc strings.ToLower -//enum:string -//enum:gostring +import "fmt" -//enum:fromstring Lookup -//enum:doc fromstring "Lookup looks up a builtin type by name." -//enum:doc fromstring -//enum:doc fromstring "If name does not name a builtin, returns [Unknown]." +//go:generate go run github.com/bufbuild/protocompile/internal/enum +// See predeclared.go.yaml. // Name is one of the built-in Protobuf names. These represent particular // paths whose meaning the language overrides to mean something other than @@ -60,12 +47,20 @@ const ( Float Double - Bool // Booleans. - String // Textual strings (ostensibly UTF-8). - Bytes // Arbitrary byte blobs. + // Booleans. + Bool + + // Textual strings (ostensibly UTF-8). + String + + // Arbitrary byte blobs. + Bytes + + // The special "type" map, the only generic type in Protobuf. + Map - Map // The special "type" map, the only generic type in Protobuf. - Max // The special "constant" max, used in range expressions. + // The special "constant" max, used in range expressions. + Max // True and false constants for bool. True @@ -73,9 +68,107 @@ const ( // Special floating-point constants for infinity and NaN. Inf - Nan + NAN // Aliases for the floating-point types with explicit bit-sizes. - Float32 = Float //enum:skip - Float64 = Double //enum:skip + Float32 = Float + Float64 = Double ) + +// String implements [fmt.Stringer]. +func (v Name) String() string { + if int(v) < 0 || int(v) > len(_table_Name_String) { + return fmt.Sprintf("Name(%v)", int(v)) + } + return _table_Name_String[int(v)] +} + +// GoString implements [fmt.GoStringer]. +func (v Name) GoString() string { + if int(v) < 0 || int(v) > len(_table_Name_GoString) { + return fmt.Sprintf("predeclaredName(%v)", int(v)) + } + return _table_Name_GoString[int(v)] +} + +// Lookup looks up a predefined identifier by name. +// +// If name does not name a predefined identifier, returns [Unknown]. +func Lookup(s string) Name { + return _table_Name_Lookup[s] +} + +var _table_Name_String = [...]string { + Unknown: "unknown", + Int32: "int32", + Int64: "int64", + UInt32: "uint32", + UInt64: "uint64", + SInt32: "sint32", + SInt64: "sint64", + Fixed32: "fixed32", + Fixed64: "fixed64", + SFixed32: "sfixed32", + SFixed64: "sfixed64", + Float: "float", + Double: "double", + Bool: "bool", + String: "string", + Bytes: "bytes", + Map: "map", + Max: "max", + True: "true", + False: "false", + Inf: "inf", + NAN: "nan", +} + +var _table_Name_GoString = [...]string { + Unknown: "Unknown", + Int32: "Int32", + Int64: "Int64", + UInt32: "UInt32", + UInt64: "UInt64", + SInt32: "SInt32", + SInt64: "SInt64", + Fixed32: "Fixed32", + Fixed64: "Fixed64", + SFixed32: "SFixed32", + SFixed64: "SFixed64", + Float: "Float", + Double: "Double", + Bool: "Bool", + String: "String", + Bytes: "Bytes", + Map: "Map", + Max: "Max", + True: "True", + False: "False", + Inf: "Inf", + NAN: "NAN", +} + +var _table_Name_Lookup = map[string]Name { + "unknown": Unknown, + "int32": Int32, + "int64": Int64, + "uint32": UInt32, + "uint64": UInt64, + "sint32": SInt32, + "sint64": SInt64, + "fixed32": Fixed32, + "fixed64": Fixed64, + "sfixed32": SFixed32, + "sfixed64": SFixed64, + "float": Float, + "double": Double, + "bool": Bool, + "string": String, + "bytes": Bytes, + "map": Map, + "max": Max, + "true": True, + "false": False, + "inf": Inf, + "nan": NAN, +} diff --git a/experimental/ast/predeclared/predeclared.go.yaml b/experimental/ast/predeclared/predeclared.go.yaml new file mode 100644 index 00000000..2887318d --- /dev/null +++ b/experimental/ast/predeclared/predeclared.go.yaml @@ -0,0 +1,109 @@ +# Copyright 2020-2024 Buf Technologies, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http:#www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +- name: Name + type: byte + docs: | + Name is one of the built-in Protobuf names. These represent particular + paths whose meaning the language overrides to mean something other than + a relative path with that name. + methods: + - kind: string + - kind: go-string + - kind: from-string + name: Lookup + docs: | + Lookup looks up a predefined identifier by name. + + If name does not name a predefined identifier, returns [Unknown]. + values: + - name: Unknown + string: unknown + + - docs: | + Varint types: 32/64-bit signed, unsigned, and Zig-Zag. + name: Int32 + string: int32 + - name: Int64 + string: int64 + - name: UInt32 + string: uint32 + - name: UInt64 + string: uint64 + - name: SInt32 + string: sint32 + - name: SInt64 + string: sint64 + + - docs: | + Fixed integer types: 32/64-bit unsigned and signed. + name: Fixed32 + string: fixed32 + - name: Fixed64 + string: fixed64 + - name: SFixed32 + string: sfixed32 + - name: SFixed64 + string: sfixed64 + + - docs: | + Floating-point types: 32/64-bit, using C-style names. + name: Float + string: float + - name: Double + string: double + + - docs: | + Booleans. + name: Bool + string: bool + - docs: | + Textual strings (ostensibly UTF-8). + name: String + string: string + - docs: | + Arbitrary byte blobs. + name: Bytes + string: bytes + + - docs: | + The special "type" map, the only generic type in Protobuf. + name: Map + string: map + + - docs: | + The special "constant" max, used in range expressions. + name: Max + string: max + + - docs: | + True and false constants for bool. + name: True + string: true + - name: False + string: false + + - docs: | + Special floating-point constants for infinity and NaN. + name: Inf + string: inf + - name: NAN + string: nan + + - docs: | + Aliases for the floating-point types with explicit bit-sizes. + name: Float32 + alias: Float + - name: Float64 + alias: Double \ No newline at end of file diff --git a/experimental/ast/predeclared/predeclared_enum.go b/experimental/ast/predeclared/predeclared_enum.go deleted file mode 100644 index e0f79c85..00000000 --- a/experimental/ast/predeclared/predeclared_enum.go +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2020-2024 Buf Technologies, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Code generated by github.com/bufbuild/protocompile/internal/enum. DO NOT EDIT. - -package predeclared - -import ( - "fmt" - "strings" -) - -// String implements [fmt.Stringer]. -func (v Name) String() string { - if int(v) < 0 || int(v) > len(_NameStrings) { - return fmt.Sprintf("Name(%v)", int(v)) - } - return _NameStrings[int(v)] -} - -var _NameStrings = [...]string { - Unknown: strings.ToLower("Unknown"), - Int32: strings.ToLower("Int32"), - Int64: strings.ToLower("Int64"), - UInt32: strings.ToLower("UInt32"), - UInt64: strings.ToLower("UInt64"), - SInt32: strings.ToLower("SInt32"), - SInt64: strings.ToLower("SInt64"), - Fixed32: strings.ToLower("Fixed32"), - Fixed64: strings.ToLower("Fixed64"), - SFixed32: strings.ToLower("SFixed32"), - SFixed64: strings.ToLower("SFixed64"), - Float: strings.ToLower("Float"), - Double: strings.ToLower("Double"), - Bool: strings.ToLower("Bool"), - String: strings.ToLower("String"), - Bytes: strings.ToLower("Bytes"), - Map: strings.ToLower("Map"), - Max: strings.ToLower("Max"), - True: strings.ToLower("True"), - False: strings.ToLower("False"), - Inf: strings.ToLower("Inf"), - Nan: strings.ToLower("Nan"), -} - -// GoString implements [fmt.GoStringer]. -func (v Name) GoString() string { - if int(v) < 0 || int(v) > len(_NameStrings) { - return fmt.Sprintf("predeclared.Name(%v)", int(v)) - } - return _NameStrings[int(v)] -} - -var _NameGoStrings = [...]string { - Unknown: "predeclared.Unknown", - Int32: "predeclared.Int32", - Int64: "predeclared.Int64", - UInt32: "predeclared.UInt32", - UInt64: "predeclared.UInt64", - SInt32: "predeclared.SInt32", - SInt64: "predeclared.SInt64", - Fixed32: "predeclared.Fixed32", - Fixed64: "predeclared.Fixed64", - SFixed32: "predeclared.SFixed32", - SFixed64: "predeclared.SFixed64", - Float: "predeclared.Float", - Double: "predeclared.Double", - Bool: "predeclared.Bool", - String: "predeclared.String", - Bytes: "predeclared.Bytes", - Map: "predeclared.Map", - Max: "predeclared.Max", - True: "predeclared.True", - False: "predeclared.False", - Inf: "predeclared.Inf", - Nan: "predeclared.Nan", -} - -// Lookup looks up a builtin type by name. -// -// If name does not name a builtin, returns [Unknown]. -func Lookup(s string) Name { - return _NameFromString[s] -} - -var _NameFromString = map[string]Name { - strings.ToLower("Unknown"): Unknown, - strings.ToLower("Int32"): Int32, - strings.ToLower("Int64"): Int64, - strings.ToLower("UInt32"): UInt32, - strings.ToLower("UInt64"): UInt64, - strings.ToLower("SInt32"): SInt32, - strings.ToLower("SInt64"): SInt64, - strings.ToLower("Fixed32"): Fixed32, - strings.ToLower("Fixed64"): Fixed64, - strings.ToLower("SFixed32"): SFixed32, - strings.ToLower("SFixed64"): SFixed64, - strings.ToLower("Float"): Float, - strings.ToLower("Double"): Double, - strings.ToLower("Bool"): Bool, - strings.ToLower("String"): String, - strings.ToLower("Bytes"): Bytes, - strings.ToLower("Map"): Map, - strings.ToLower("Max"): Max, - strings.ToLower("True"): True, - strings.ToLower("False"): False, - strings.ToLower("Inf"): Inf, - strings.ToLower("Nan"): Nan, -} - diff --git a/internal/enum/enum.go b/internal/enum/enum.go index f7bdc359..e3ea9e35 100644 --- a/internal/enum/enum.go +++ b/internal/enum/enum.go @@ -60,289 +60,142 @@ import ( "debug/buildinfo" _ "embed" "fmt" - "go/ast" - "go/parser" - "go/token" "os" "slices" - "strconv" "strings" - "text/scanner" "text/template" - "unicode" - "github.com/bufbuild/protocompile/internal/ext/slicesx" + "gopkg.in/yaml.v3" ) -var ( - //go:embed generated.go.tmpl - tmplText string - tmpl = template.Must(template.New("generated.go.tmpl").Parse(tmplText)) -) +type Enum struct { + Name string `yaml:"name"` + Type string `yaml:"type"` + Docs string `yaml:"docs"` + Methods []Method `yaml:"methods"` + Values []Value `yaml:"values"` +} -type Directive struct { - Pos token.Pos - Name string - Args []string +type Value struct { + Name string `yaml:"name"` + Alias string `yaml:"alias"` + String_ string `yaml:"string"` + Docs string `yaml:"docs"` } -func HasDirectives(comments []*ast.CommentGroup) bool { - for _, g := range comments { - for _, c := range g.List { - rest, ok := strings.CutPrefix(c.Text, "//enum:") - if ok && rest != "" { - return true - } - } +func (v Value) String() string { + if v.String_ == "" { + return v.Name } - return false + return v.String_ } -func ParseDirectives(fs *token.FileSet, comments []*ast.CommentGroup) ([]Directive, error) { - var d []Directive - for _, g := range comments { - for _, c := range g.List { - rest, ok := strings.CutPrefix(c.Text, "//enum:") - rest = strings.TrimSpace(rest) - if !ok || rest == "" { - continue - } - - var err error - var args []string - sc := scanner.Scanner{ - Error: func(_ *scanner.Scanner, msg string) { - err = fmt.Errorf("%s", msg) - }, - Mode: scanner.ScanIdents | scanner.ScanStrings | scanner.ScanRawStrings, - IsIdentRune: func(r rune, _ int) bool { - return !unicode.IsSpace(r) && r != '"' && r != '`' - }, - } - sc.Init(strings.NewReader(rest)) - scan: - for { - next := sc.Scan() - if err != nil { - return nil, fmt.Errorf("%v: invalid directive: %v", fs.Position(c.Pos()), err) - } +type Method struct { + Kind MethodKind `yaml:"kind"` + Name_ string `yaml:"name"` + Docs_ string `yaml:"docs"` + Skip []string `yaml:"skip"` +} - switch next { - case scanner.EOF: - break scan - case scanner.Ident: - args = append(args, sc.TokenText()) - case scanner.String: - str, _ := strconv.Unquote(sc.TokenText()) - args = append(args, str) - } - } +func (m Method) Name() (string, error) { + if m.Name_ != "" { + return m.Name_, nil + } - d = append(d, Directive{ - Pos: c.Pos(), - Name: args[0], - Args: args[1:], - }) - } + switch m.Kind { + case MethodFromString: + return "", fmt.Errorf("missing name for kind: %#v", MethodFromString) + case MethodGoString: + return "GoString", nil + case MethodString: + return "String", nil + default: + return "", fmt.Errorf("unexpected kind: %#v", m.Kind) } - return d, nil } -type Enum struct { - Type *ast.TypeSpec - Methods struct { - String, GoString, FromString string - - StringFunc string +func (m Method) Docs() string { + if m.Docs_ != "" { + return m.Docs_ } - Docs struct { - String, GoString, FromString []string - } - - Values []Value -} -type Value struct { - Value *ast.ValueSpec - String string - Skip bool + switch m.Kind { + case MethodGoString: + return "GoString implements [fmt.GoStringer]." + case MethodString: + return "String implements [fmt.Stringer]." + default: + return "" + } } -func Main() error { - input := os.Getenv("GOFILE") - output := strings.TrimSuffix(input, ".go") + "_enum.go" +type MethodKind string - fs := new(token.FileSet) - f, err := parser.ParseFile(fs, input, nil, parser.ParseComments) - if err != nil { - return err - } +const ( + MethodString MethodKind = "string" + MethodGoString MethodKind = "go-string" + MethodFromString MethodKind = "from-string" +) - comments := ast.NewCommentMap(fs, f, f.Comments) +//go:embed generated.go.tmpl +var tmplText string - constsByType := make(map[string][]*ast.ValueSpec) - var types []*ast.GenDecl +// makeDocs converts a data into doc comments. +func makeDocs(data, indent string) string { + if data == "" { + return "" + } - for _, decl := range f.Decls { - decl, ok := decl.(*ast.GenDecl) - if !ok { + var out strings.Builder + for _, line := range strings.Split(strings.TrimSpace(data), "\n") { + out.WriteString(indent) + if line == "" { + out.WriteString("//\n") continue } - - switch decl.Tok { - case token.TYPE: - if !HasDirectives(comments[decl]) { - continue - } - - if decl.Lparen.IsValid() { - return fmt.Errorf("%v: //enum: directive on type group", fs.Position(decl.TokPos)) - } - - types = append(types, decl) - case token.CONST: - var ty string - for _, spec := range decl.Specs { - v := spec.(*ast.ValueSpec) //nolint:errcheck - if v.Type == nil { - constsByType[ty] = append(constsByType[ty], v) - } - if ident, ok := v.Type.(*ast.Ident); ok { - ty = ident.Name - constsByType[ty] = append(constsByType[ty], v) - } - } - } + out.WriteString("// ") + out.WriteString(line) + out.WriteString("\n") } + return out.String() +} - imports := map[string]struct{}{"fmt": {}} - enums := make([]Enum, 0, len(types)) - for _, ty := range types { - enum := Enum{Type: ty.Specs[0].(*ast.TypeSpec)} //nolint:errcheck - dirs, err := ParseDirectives(fs, comments[ty]) - if err != nil { - return err - } - for _, d := range dirs { - var ok bool - switch d.Name { - case "import": - path, ok := slicesx.Get(d.Args, 0) - if !ok { - return fmt.Errorf("%v: //enum:import requires an argument", fs.Position(d.Pos)) - } - imports[path] = struct{}{} - - case "string": - enum.Methods.String, ok = slicesx.Get(d.Args, 0) - if !ok { - enum.Methods.String = "String" - break - } - enum.Docs.String = d.Args[1:] - - case "gostring": - enum.Methods.GoString, ok = slicesx.Get(d.Args, 0) - if !ok { - enum.Methods.GoString = "GoString" - break - } - enum.Docs.GoString = d.Args[1:] - - case "fromstring": - enum.Methods.FromString, ok = slicesx.Get(d.Args, 0) - if !ok { - return fmt.Errorf("%v: //enum:fromstring requires an argument", fs.Position(d.Pos)) - } - enum.Docs.FromString = d.Args[1:] - - case "stringfunc": - enum.Methods.StringFunc, ok = slicesx.Get(d.Args, 0) - if !ok { - return fmt.Errorf("%v: //enum:stringfunc requires an argument", fs.Position(d.Pos)) - } - - case "doc": - arg, _ := slicesx.Get(d.Args, 0) - text, _ := slicesx.Get(d.Args, 1) - switch arg { - case "string": - enum.Docs.String = append(enum.Docs.String, text) - case "gostring": - enum.Docs.GoString = append(enum.Docs.GoString, text) - case "fromstring": - enum.Docs.FromString = append(enum.Docs.FromString, text) - default: - return fmt.Errorf("%v: invalid method for //enum:doc: %q", fs.Position(d.Pos), arg) - } - - default: - return fmt.Errorf("%v: unknown type directive %q", fs.Position(d.Pos), d.Name) - } - } - - if enum.Methods.String != "" && enum.Docs.String == nil { - enum.Docs.String = []string{"String implements [fmt.Stringer]."} - } - if enum.Methods.GoString != "" && enum.Docs.GoString == nil { - enum.Docs.GoString = []string{"GoString implements [fmt.GoStringer]."} - } - - for _, v := range constsByType[enum.Type.Name.Name] { - value := Value{Value: v} - dirs, err := ParseDirectives(fs, comments[v]) - if err != nil { - return err - } - for _, d := range dirs { - switch d.Name { - case "string": - name, ok := slicesx.Get(d.Args, 0) - if !ok { - return fmt.Errorf("%v: //enum:string requires an argument", fs.Position(d.Pos)) - } - value.String = strconv.Quote(name) - case "skip": - value.Skip = true - - default: - return fmt.Errorf("%v: unknown const directive %q", fs.Position(d.Pos), d.Name) - } - } - - enum.Values = append(enum.Values, value) - } - - enums = append(enums, enum) +func Main() error { + var input struct { + Binary, Package, Path string + YAML []Enum } + input.Package = os.Getenv("GOPACKAGE") + input.Path = os.Getenv("GOFILE") - importList := make([]string, 0, len(imports)) - for imp := range imports { - importList = append(importList, strconv.Quote(imp)) + buildinfo, err := buildinfo.ReadFile(os.Args[0]) + if err != nil { + return err } - slices.Sort(importList) + input.Binary = buildinfo.Path - out, err := os.Create(output) + text, err := os.ReadFile(input.Path + ".yaml") if err != nil { return err } - defer out.Close() + if err := yaml.Unmarshal(text, &input.YAML); err != nil { + return err + } - info, err := buildinfo.ReadFile(os.Args[0]) + tmpl, err := template.New("generated.go.tmpl").Funcs(template.FuncMap{ + "makeDocs": makeDocs, + "contains": slices.Contains[[]string], + }).Parse(tmplText) if err != nil { return err } - return tmpl.ExecuteTemplate(out, "generated.go.tmpl", struct { - Binary, Package string - Imports []string - Enums []Enum - }{ - Binary: info.Path, - Package: f.Name.Name, - Imports: importList, - Enums: enums, - }) + out, err := os.Create(input.Path) + if err != nil { + return err + } + defer out.Close() + return tmpl.ExecuteTemplate(out, "generated.go.tmpl", input) } func main() { diff --git a/internal/enum/generated.go.tmpl b/internal/enum/generated.go.tmpl index cd80af15..422d5499 100644 --- a/internal/enum/generated.go.tmpl +++ b/internal/enum/generated.go.tmpl @@ -12,84 +12,72 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Code generated by {{.Binary}}. DO NOT EDIT. +// Code generated by {{.Binary}}. DO NOT EDIT.{{$input := .}} -package {{$pkg := .Package}}{{$pkg}} +package {{.Package}} -import ( -{{range .Imports}} {{.}} -{{end -}} -) -{{ range .Enums -}} -{{- $ty := .Type.Name.Name -}} -{{- $e := . -}} +{{with .YAML -}} +import "fmt" -{{- $name := .Methods.String -}} -{{- if ne $name "" -}} -{{- range .Docs.String }} -// {{.}} -{{end -}} -func (v {{$ty}}) {{$name}}() string { - if int(v) < 0 || int(v) > len(_{{$ty}}Strings) { - return fmt.Sprintf("{{$ty}}(%v)", int(v)) - } - return _{{$ty}}Strings[int(v)] -} +//go:generate go run github.com/bufbuild/protocompile/internal/enum +// See {{$input.Path}}.yaml. +{{range $_, $e := .}} +{{makeDocs $e.Docs "" -}} +type {{$e.Name}} {{$e.Type}} -var _{{$ty}}Strings = [...]string { -{{range .Values}}{{if .Skip}}{{continue}}{{end}} {{(index .Value.Names 0).Name}}: {{"" -}} - {{- if ne .String "" -}} - {{.String}} - {{- else if ne $e.Methods.StringFunc "" -}} - {{$e.Methods.StringFunc}}("{{(index .Value.Names 0).Name}}") - {{- else -}} - "{{(index .Value.Names 0).Name}}" - {{- end }}, +const ( +{{range $i, $v := $e.Values -}}{{if ne $v.Docs ""}} +{{end}}{{makeDocs $v.Docs "\t" -}} +{{if eq $i 0}} {{$v.Name}} {{$e.Name}} = iota +{{else if ne $v.Alias ""}} {{$v.Name}} = {{$v.Alias}} +{{else}} {{$v.Name}} {{end -}} -} - -{{end -}} - -{{- $name = .Methods.GoString -}} -{{- if ne $name "" -}} -{{- range .Docs.GoString -}} -{{with . -}} // {{.}} {{- else -}} // {{- end }} {{end -}} -func (v {{$ty}}) {{$name}}() string { - if int(v) < 0 || int(v) > len(_{{$ty}}Strings) { - return fmt.Sprintf("{{$pkg}}.{{$ty}}(%v)", int(v)) +) +{{range $_, $m := $e.Methods -}} +{{- if eq $m.Kind "string"}} +{{makeDocs $m.Docs "" -}} +func (v {{$e.Name}}) {{$m.Name}}() string { + if int(v) < 0 || int(v) > len(_table_{{$e.Name}}_{{$m.Name}}) { + return fmt.Sprintf("{{$e.Name}}(%v)", int(v)) } - return _{{$ty}}Strings[int(v)] + return _table_{{$e.Name}}_{{$m.Name}}[int(v)] } - -var _{{$ty}}GoStrings = [...]string { -{{range .Values}}{{if .Skip}}{{continue}}{{end}} {{(index .Value.Names 0).Name}}: "{{$pkg}}.{{(index .Value.Names 0).Name}}", -{{end -}} +{{else if eq $m.Kind "go-string"}} +{{makeDocs $m.Docs "" -}} +func (v {{$e.Name}}) {{$m.Name}}() string { + if int(v) < 0 || int(v) > len(_table_{{$e.Name}}_{{$m.Name}}) { + return fmt.Sprintf("{{$input.Package}}{{$e.Name}}(%v)", int(v)) + } + return _table_{{$e.Name}}_{{$m.Name}}[int(v)] } - -{{end -}} - -{{- $name = .Methods.FromString -}} -{{- if ne $name "" -}} -{{- range .Docs.FromString -}} -{{with . -}} // {{.}} {{- else -}} // {{- end }} +{{else if eq $m.Kind "from-string"}} +{{makeDocs $m.Docs "" -}} +func {{$m.Name}}(s string) {{$e.Name}} { + return _table_{{$e.Name}}_{{$m.Name}}[s] +} +{{end -}}{{- end -}} +{{range $_, $m := $e.Methods -}} +{{- if eq $m.Kind "string"}} +var _table_{{$e.Name}}_{{$m.Name}} = [...]string { +{{range $e.Values -}} +{{- with .Alias}}{{continue}}{{end -}} +{{- if contains $m.Skip .Name}}{{continue}}{{end}} {{.Name}}: {{printf "%q" .String}}, {{end -}} -func {{$name}}(s string) {{$ty}} { - return _{{$ty}}FromString[s] } - -var _{{$ty}}FromString = map[string]{{$ty}} { -{{range .Values}}{{if .Skip}}{{continue}}{{end}} {{"" -}} - {{- if ne .String "" -}} - {{.String}} - {{- else if ne $e.Methods.StringFunc "" -}} - {{$e.Methods.StringFunc}}("{{(index .Value.Names 0).Name}}") - {{- else -}} - "{{(index .Value.Names 0).Name}}" - {{- end}}: {{(index .Value.Names 0).Name}}, +{{else if eq $m.Kind "go-string"}} +var _table_{{$e.Name}}_{{$m.Name}} = [...]string { +{{range $e.Values -}} +{{- with .Alias}}{{continue}}{{end -}} +{{- if contains $m.Skip .Name}}{{continue}}{{end}} {{.Name}}: {{printf "%q" .Name}}, {{end -}} } - +{{else if eq $m.Kind "from-string"}} +var _table_{{$e.Name}}_{{$m.Name}} = map[string]{{$e.Name}} { +{{range $e.Values -}} +{{- with .Alias}}{{continue}}{{end -}} +{{- if contains $m.Skip .Name}}{{continue}}{{end}} {{printf "%q" .String}}: {{.Name}}, {{end -}} - -{{- end -}} \ No newline at end of file +} +{{end -}}{{- end -}} +{{- end -}}{{- end -}} diff --git a/internal/ext/slicesx/collect.go b/internal/ext/slicesx/collect.go index 12ce0756..5dcadf70 100644 --- a/internal/ext/slicesx/collect.go +++ b/internal/ext/slicesx/collect.go @@ -30,3 +30,12 @@ func AppendSeq[S ~[]E, E any](s S, seq iter.Seq[E]) []E { }) return s } + +// Map constructs a new slice by applying f to each element. +func Map[S ~[]E, E, R any](s S, f func(E) R) []R { + out := make([]R, len(s)) + for i, e := range s { + out[i] = f(e) + } + return out +} From 34c5f6060919c2a5032b0b6c92d44e1207d370f5 Mon Sep 17 00:00:00 2001 From: Miguel Young de la Sota Date: Fri, 20 Dec 2024 12:37:21 -0800 Subject: [PATCH 6/6] switch using yaml files --- experimental/ast/decl.go | 22 +- experimental/ast/decl_def.go | 18 -- experimental/ast/decl_enum.go | 60 ---- experimental/ast/enums.go | 346 ++++++++++++++++++++++++ experimental/ast/enums.go.yaml | 133 +++++++++ experimental/ast/expr.go | 22 +- experimental/ast/expr_enum.go | 60 ---- experimental/ast/expr_prefixed.go | 25 -- experimental/ast/expr_prefixed_enum.go | 48 ---- experimental/ast/type.go | 16 -- experimental/ast/type_enum.go | 52 ---- experimental/ast/type_prefixed.go | 40 --- experimental/ast/type_prefixed_enum.go | 54 ---- experimental/internal/taxa/noun.go | 318 ++++++++++++++++++++++ experimental/internal/taxa/noun.go.yaml | 131 +++++++++ experimental/internal/taxa/taxa.go | 126 +-------- experimental/internal/taxa/taxa_enum.go | 222 --------------- experimental/token/kind.go | 64 +++-- experimental/token/kind.go.yaml | 39 +++ experimental/token/kind_enum.go | 58 ---- experimental/token/token.go | 7 + internal/enum/enum.go | 83 +++--- internal/enum/generated.go.tmpl | 11 +- 23 files changed, 1064 insertions(+), 891 deletions(-) delete mode 100644 experimental/ast/decl_enum.go create mode 100644 experimental/ast/enums.go create mode 100644 experimental/ast/enums.go.yaml delete mode 100644 experimental/ast/expr_enum.go delete mode 100644 experimental/ast/expr_prefixed_enum.go delete mode 100644 experimental/ast/type_enum.go delete mode 100644 experimental/ast/type_prefixed_enum.go create mode 100644 experimental/internal/taxa/noun.go create mode 100644 experimental/internal/taxa/noun.go.yaml delete mode 100644 experimental/internal/taxa/taxa_enum.go create mode 100644 experimental/token/kind.go.yaml delete mode 100644 experimental/token/kind_enum.go diff --git a/experimental/ast/decl.go b/experimental/ast/decl.go index 4b59ea20..b43374d1 100644 --- a/experimental/ast/decl.go +++ b/experimental/ast/decl.go @@ -22,26 +22,6 @@ import ( "github.com/bufbuild/protocompile/internal/arena" ) -//go:generate go run github.com/bufbuild/protocompile/internal/enum - -// DeclKind is a kind of declaration. There is one value of DeclKind for each -// Decl* type in this package. -// -//enum:string -//enum:gostring -type DeclKind int8 - -const ( - DeclKindNil DeclKind = iota - DeclKindEmpty - DeclKindSyntax - DeclKindPackage - DeclKindImport - DeclKindDef - DeclKindBody - DeclKindRange -) - // DeclAny is any Decl* type in this package. // // Values of this type can be obtained by calling an AsAny method on a Decl* @@ -171,7 +151,7 @@ type rawDecl struct { } func (d rawDecl) With(c Context) DeclAny { - if c == nil || d.ptr.Nil() || d.kind == DeclKindNil { + if c == nil || d.ptr.Nil() || d.kind == DeclKindInvalid { return DeclAny{} } diff --git a/experimental/ast/decl_def.go b/experimental/ast/decl_def.go index 07b23ed3..c9447e05 100644 --- a/experimental/ast/decl_def.go +++ b/experimental/ast/decl_def.go @@ -20,24 +20,6 @@ import ( "github.com/bufbuild/protocompile/internal/arena" ) -const ( - DefKindMessage DefKind = iota + 1 - DefKindEnum - DefKindService - DefKindExtend - DefKindField - DefKindOneof - DefKindGroup - DefKindEnumValue - DefKindMethod - DefKindOption -) - -// DefKind is the kind of definition a [DeclDef] contains. -// -// See [DeclDef.Classify]. -type DefKind int8 - // DeclDef is a general Protobuf definition. // // This [Decl] represents the union of several similar AST nodes, to aid in permissive diff --git a/experimental/ast/decl_enum.go b/experimental/ast/decl_enum.go deleted file mode 100644 index f44ac70b..00000000 --- a/experimental/ast/decl_enum.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2020-2024 Buf Technologies, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Code generated by github.com/bufbuild/protocompile/internal/enum. DO NOT EDIT. - -package ast - -import ( - "fmt" -) - -// String implements [fmt.Stringer]. -func (v DeclKind) String() string { - if int(v) < 0 || int(v) > len(_DeclKindStrings) { - return fmt.Sprintf("DeclKind(%v)", int(v)) - } - return _DeclKindStrings[int(v)] -} - -var _DeclKindStrings = [...]string { - DeclKindNil: "DeclKindNil", - DeclKindEmpty: "DeclKindEmpty", - DeclKindSyntax: "DeclKindSyntax", - DeclKindPackage: "DeclKindPackage", - DeclKindImport: "DeclKindImport", - DeclKindDef: "DeclKindDef", - DeclKindBody: "DeclKindBody", - DeclKindRange: "DeclKindRange", -} - -// GoString implements [fmt.GoStringer]. -func (v DeclKind) GoString() string { - if int(v) < 0 || int(v) > len(_DeclKindStrings) { - return fmt.Sprintf("ast.DeclKind(%v)", int(v)) - } - return _DeclKindStrings[int(v)] -} - -var _DeclKindGoStrings = [...]string { - DeclKindNil: "ast.DeclKindNil", - DeclKindEmpty: "ast.DeclKindEmpty", - DeclKindSyntax: "ast.DeclKindSyntax", - DeclKindPackage: "ast.DeclKindPackage", - DeclKindImport: "ast.DeclKindImport", - DeclKindDef: "ast.DeclKindDef", - DeclKindBody: "ast.DeclKindBody", - DeclKindRange: "ast.DeclKindRange", -} - diff --git a/experimental/ast/enums.go b/experimental/ast/enums.go new file mode 100644 index 00000000..03cf7dfe --- /dev/null +++ b/experimental/ast/enums.go @@ -0,0 +1,346 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by github.com/bufbuild/protocompile/internal/enum. DO NOT EDIT. + +package ast + +import "fmt" + +//go:generate go run github.com/bufbuild/protocompile/internal/enum +// See enums.go.yaml. + +// DeclKind is a kind of declaration. There is one value of DeclKind for each +// Decl* type in this package. +type DeclKind int8 + +const ( + DeclKindInvalid DeclKind = iota + DeclKindEmpty + DeclKindSyntax + DeclKindPackage + DeclKindImport + DeclKindDef + DeclKindBody + DeclKindRange +) + +// String implements [fmt.Stringer]. +func (v DeclKind) String() string { + if int(v) < 0 || int(v) > len(_table_DeclKind_String) { + return fmt.Sprintf("DeclKind(%v)", int(v)) + } + return _table_DeclKind_String[int(v)] +} + +// GoString implements [fmt.GoStringer]. +func (v DeclKind) GoString() string { + if int(v) < 0 || int(v) > len(_table_DeclKind_GoString) { + return fmt.Sprintf("astDeclKind(%v)", int(v)) + } + return _table_DeclKind_GoString[int(v)] +} + +var _table_DeclKind_String = [...]string { + DeclKindInvalid: "DeclKindInvalid", + DeclKindEmpty: "DeclKindEmpty", + DeclKindSyntax: "DeclKindSyntax", + DeclKindPackage: "DeclKindPackage", + DeclKindImport: "DeclKindImport", + DeclKindDef: "DeclKindDef", + DeclKindBody: "DeclKindBody", + DeclKindRange: "DeclKindRange", +} + +var _table_DeclKind_GoString = [...]string { + DeclKindInvalid: "DeclKindInvalid", + DeclKindEmpty: "DeclKindEmpty", + DeclKindSyntax: "DeclKindSyntax", + DeclKindPackage: "DeclKindPackage", + DeclKindImport: "DeclKindImport", + DeclKindDef: "DeclKindDef", + DeclKindBody: "DeclKindBody", + DeclKindRange: "DeclKindRange", +} + +// DefKind is the kind of definition a [DeclDef] contains. +// +// See [DeclDef.Classify]. +type DefKind int8 + +const ( + DefKindInvalid DefKind = iota + DefKindMessage + DefKindEnum + DefKindService + DefKindExtend + DefKindField + DefKindOneof + DefKindGroup + DefKindEnumValue + DefKindMethod + DefKindOption +) + +// String implements [fmt.Stringer]. +func (v DefKind) String() string { + if int(v) < 0 || int(v) > len(_table_DefKind_String) { + return fmt.Sprintf("DefKind(%v)", int(v)) + } + return _table_DefKind_String[int(v)] +} + +// GoString implements [fmt.GoStringer]. +func (v DefKind) GoString() string { + if int(v) < 0 || int(v) > len(_table_DefKind_GoString) { + return fmt.Sprintf("astDefKind(%v)", int(v)) + } + return _table_DefKind_GoString[int(v)] +} + +var _table_DefKind_String = [...]string { + DefKindInvalid: "DefKindInvalid", + DefKindMessage: "DefKindMessage", + DefKindEnum: "DefKindEnum", + DefKindService: "DefKindService", + DefKindExtend: "DefKindExtend", + DefKindField: "DefKindField", + DefKindOneof: "DefKindOneof", + DefKindGroup: "DefKindGroup", + DefKindEnumValue: "DefKindEnumValue", + DefKindMethod: "DefKindMethod", + DefKindOption: "DefKindOption", +} + +var _table_DefKind_GoString = [...]string { + DefKindInvalid: "DefKindInvalid", + DefKindMessage: "DefKindMessage", + DefKindEnum: "DefKindEnum", + DefKindService: "DefKindService", + DefKindExtend: "DefKindExtend", + DefKindField: "DefKindField", + DefKindOneof: "DefKindOneof", + DefKindGroup: "DefKindGroup", + DefKindEnumValue: "DefKindEnumValue", + DefKindMethod: "DefKindMethod", + DefKindOption: "DefKindOption", +} + +// ExprKind is a kind of expression. There is one value of ExprKind for each +// Expr* type in this package. +type ExprKind int8 + +const ( + ExprKindInvalid ExprKind = iota + ExprKindError + ExprKindLiteral + ExprKindPrefixed + ExprKindPath + ExprKindRange + ExprKindArray + ExprKindDict + ExprKindField +) + +// String implements [fmt.Stringer]. +func (v ExprKind) String() string { + if int(v) < 0 || int(v) > len(_table_ExprKind_String) { + return fmt.Sprintf("ExprKind(%v)", int(v)) + } + return _table_ExprKind_String[int(v)] +} + +// GoString implements [fmt.GoStringer]. +func (v ExprKind) GoString() string { + if int(v) < 0 || int(v) > len(_table_ExprKind_GoString) { + return fmt.Sprintf("astExprKind(%v)", int(v)) + } + return _table_ExprKind_GoString[int(v)] +} + +var _table_ExprKind_String = [...]string { + ExprKindInvalid: "ExprKindInvalid", + ExprKindError: "ExprKindError", + ExprKindLiteral: "ExprKindLiteral", + ExprKindPrefixed: "ExprKindPrefixed", + ExprKindPath: "ExprKindPath", + ExprKindRange: "ExprKindRange", + ExprKindArray: "ExprKindArray", + ExprKindDict: "ExprKindDict", + ExprKindField: "ExprKindField", +} + +var _table_ExprKind_GoString = [...]string { + ExprKindInvalid: "ExprKindInvalid", + ExprKindError: "ExprKindError", + ExprKindLiteral: "ExprKindLiteral", + ExprKindPrefixed: "ExprKindPrefixed", + ExprKindPath: "ExprKindPath", + ExprKindRange: "ExprKindRange", + ExprKindArray: "ExprKindArray", + ExprKindDict: "ExprKindDict", + ExprKindField: "ExprKindField", +} + +// TypeKind is a kind of type. There is one value of TypeKind for each +// Type* type in this package. +type TypeKind int8 + +const ( + TypeKindNil TypeKind = iota + TypeKindError + TypeKindPath + TypeKindPrefixed + TypeKindGeneric +) + +// String implements [fmt.Stringer]. +func (v TypeKind) String() string { + if int(v) < 0 || int(v) > len(_table_TypeKind_String) { + return fmt.Sprintf("TypeKind(%v)", int(v)) + } + return _table_TypeKind_String[int(v)] +} + +// GoString implements [fmt.GoStringer]. +func (v TypeKind) GoString() string { + if int(v) < 0 || int(v) > len(_table_TypeKind_GoString) { + return fmt.Sprintf("astTypeKind(%v)", int(v)) + } + return _table_TypeKind_GoString[int(v)] +} + +var _table_TypeKind_String = [...]string { + TypeKindNil: "TypeKindNil", + TypeKindError: "TypeKindError", + TypeKindPath: "TypeKindPath", + TypeKindPrefixed: "TypeKindPrefixed", + TypeKindGeneric: "TypeKindGeneric", +} + +var _table_TypeKind_GoString = [...]string { + TypeKindNil: "TypeKindNil", + TypeKindError: "TypeKindError", + TypeKindPath: "TypeKindPath", + TypeKindPrefixed: "TypeKindPrefixed", + TypeKindGeneric: "TypeKindGeneric", +} + +// TypePrefix is a prefix for an expression, such as a minus sign. +type ExprPrefix int8 + +const ( + ExprPrefixUnknown ExprPrefix = iota + ExprPrefixMinus +) + +// String implements [fmt.Stringer]. +func (v ExprPrefix) String() string { + if int(v) < 0 || int(v) > len(_table_ExprPrefix_String) { + return fmt.Sprintf("ExprPrefix(%v)", int(v)) + } + return _table_ExprPrefix_String[int(v)] +} + +// GoString implements [fmt.GoStringer]. +func (v ExprPrefix) GoString() string { + if int(v) < 0 || int(v) > len(_table_ExprPrefix_GoString) { + return fmt.Sprintf("astExprPrefix(%v)", int(v)) + } + return _table_ExprPrefix_GoString[int(v)] +} + +// ExprPrefixByName looks up a prefix kind by name. +// +// If name is not a known prefix, returns [ExprPrefixUnknown]. +func ExprPrefixByName(s string) ExprPrefix { + return _table_ExprPrefix_ExprPrefixByName[s] +} + +var _table_ExprPrefix_String = [...]string { + ExprPrefixUnknown: "unknown", + ExprPrefixMinus: "-", +} + +var _table_ExprPrefix_GoString = [...]string { + ExprPrefixUnknown: "ExprPrefixUnknown", + ExprPrefixMinus: "ExprPrefixMinus", +} + +var _table_ExprPrefix_ExprPrefixByName = map[string]ExprPrefix { + "-": ExprPrefixMinus, +} + +// TypeKind is a kind of type. There is one value of TypeKind for each +// Type* type in this package. +// +// TypePrefix is a prefix for a type, such as required, optional, or repeated. +type TypePrefix int8 + +const ( + TypePrefixUnknown TypePrefix = iota + TypePrefixOptional + TypePrefixRepeated + TypePrefixRequired + + // This is the "stream Foo.bar" syntax of RPC methods. It is also treated as + // a prefix. + TypePrefixStream +) + +// String implements [fmt.Stringer]. +func (v TypePrefix) String() string { + if int(v) < 0 || int(v) > len(_table_TypePrefix_String) { + return fmt.Sprintf("TypePrefix(%v)", int(v)) + } + return _table_TypePrefix_String[int(v)] +} + +// GoString implements [fmt.GoStringer]. +func (v TypePrefix) GoString() string { + if int(v) < 0 || int(v) > len(_table_TypePrefix_GoString) { + return fmt.Sprintf("astTypePrefix(%v)", int(v)) + } + return _table_TypePrefix_GoString[int(v)] +} + +// TypePrefixByName looks up a prefix kind by name. +// +// If name is not a known prefix, returns [TypePrefixUnknown]. +func TypePrefixByName(s string) TypePrefix { + return _table_TypePrefix_TypePrefixByName[s] +} + +var _table_TypePrefix_String = [...]string { + TypePrefixUnknown: "unknown", + TypePrefixOptional: "optional", + TypePrefixRepeated: "required", + TypePrefixRequired: "repeated", + TypePrefixStream: "stream", +} + +var _table_TypePrefix_GoString = [...]string { + TypePrefixUnknown: "TypePrefixUnknown", + TypePrefixOptional: "TypePrefixOptional", + TypePrefixRepeated: "TypePrefixRepeated", + TypePrefixRequired: "TypePrefixRequired", + TypePrefixStream: "TypePrefixStream", +} + +var _table_TypePrefix_TypePrefixByName = map[string]TypePrefix { + "optional": TypePrefixOptional, + "required": TypePrefixRepeated, + "repeated": TypePrefixRequired, + "stream": TypePrefixStream, +} diff --git a/experimental/ast/enums.go.yaml b/experimental/ast/enums.go.yaml new file mode 100644 index 00000000..79082d90 --- /dev/null +++ b/experimental/ast/enums.go.yaml @@ -0,0 +1,133 @@ +# Copyright 2020-2024 Buf Technologies, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http:#www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +- name: DeclKind + type: int8 + docs: | + DeclKind is a kind of declaration. There is one value of DeclKind for each + Decl* type in this package. + methods: + - kind: string + - kind: go-string + values: + - name: DeclKindInvalid + - name: DeclKindEmpty + - name: DeclKindSyntax + - name: DeclKindPackage + - name: DeclKindImport + - name: DeclKindDef + - name: DeclKindBody + - name: DeclKindRange + +- name: DefKind + type: int8 + docs: | + DefKind is the kind of definition a [DeclDef] contains. + + See [DeclDef.Classify]. + methods: + - kind: string + - kind: go-string + values: + - name: DefKindInvalid + - name: DefKindMessage + - name: DefKindEnum + - name: DefKindService + - name: DefKindExtend + - name: DefKindField + - name: DefKindOneof + - name: DefKindGroup + - name: DefKindEnumValue + - name: DefKindMethod + - name: DefKindOption + +- name: ExprKind + type: int8 + docs: | + ExprKind is a kind of expression. There is one value of ExprKind for each + Expr* type in this package. + methods: + - kind: string + - kind: go-string + values: + - name: ExprKindInvalid + - name: ExprKindError + - name: ExprKindLiteral + - name: ExprKindPrefixed + - name: ExprKindPath + - name: ExprKindRange + - name: ExprKindArray + - name: ExprKindDict + - name: ExprKindField + +- name: TypeKind + type: int8 + docs: | + TypeKind is a kind of type. There is one value of TypeKind for each + Type* type in this package. + methods: + - kind: string + - kind: go-string + values: + - name: TypeKindNil + - name: TypeKindError + - name: TypeKindPath + - name: TypeKindPrefixed + - name: TypeKindGeneric + +- name: ExprPrefix + type: int8 + docs: | + TypePrefix is a prefix for an expression, such as a minus sign. + methods: + - kind: string + - kind: go-string + - kind: from-string + name: ExprPrefixByName + docs: | + ExprPrefixByName looks up a prefix kind by name. + + If name is not a known prefix, returns [ExprPrefixUnknown]. + skip: [ExprPrefixUnknown] + values: + - {name: ExprPrefixUnknown, string: unknown} + - {name: ExprPrefixMinus, string: "-"} + +- name: TypePrefix + type: int8 + docs: | + TypeKind is a kind of type. There is one value of TypeKind for each + Type* type in this package. + + TypePrefix is a prefix for a type, such as required, optional, or repeated. + methods: + - kind: string + - kind: go-string + - kind: from-string + name: TypePrefixByName + docs: | + TypePrefixByName looks up a prefix kind by name. + + If name is not a known prefix, returns [TypePrefixUnknown]. + skip: [TypePrefixUnknown] + values: + - {name: TypePrefixUnknown, string: unknown} + - {name: TypePrefixOptional, string: optional} + - {name: TypePrefixRepeated, string: required} + - {name: TypePrefixRequired, string: repeated} + - name: TypePrefixStream + string: stream + docs: | + This is the "stream Foo.bar" syntax of RPC methods. It is also treated as + a prefix. \ No newline at end of file diff --git a/experimental/ast/expr.go b/experimental/ast/expr.go index dd17e5e5..11dd4d51 100644 --- a/experimental/ast/expr.go +++ b/experimental/ast/expr.go @@ -23,26 +23,6 @@ import ( "github.com/bufbuild/protocompile/internal/arena" ) -//go:generate go run github.com/bufbuild/protocompile/internal/enum - -// ExprKind is a kind of expression. There is one value of ExprKind for each -// Expr* type in this package. -// -//enum:string -//enum:gostring -type ExprKind int8 - -const ( - ExprKindNil ExprKind = iota - ExprKindLiteral - ExprKindPrefixed - ExprKindPath - ExprKindRange - ExprKindArray - ExprKindDict - ExprKindField -) - // ExprAny is any ExprAny* type in this package. // // Values of this type can be obtained by calling an AsAny method on a ExprAny* @@ -76,7 +56,7 @@ func newExprAny(c Context, e rawExpr) ExprAny { // in a switch statement. func (e ExprAny) Kind() ExprKind { if e.Nil() { - return ExprKindNil + return ExprKindInvalid } if kind, ok := e.raw.kind(); ok { diff --git a/experimental/ast/expr_enum.go b/experimental/ast/expr_enum.go deleted file mode 100644 index 79ee3835..00000000 --- a/experimental/ast/expr_enum.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2020-2024 Buf Technologies, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Code generated by github.com/bufbuild/protocompile/internal/enum. DO NOT EDIT. - -package ast - -import ( - "fmt" -) - -// String implements [fmt.Stringer]. -func (v ExprKind) String() string { - if int(v) < 0 || int(v) > len(_ExprKindStrings) { - return fmt.Sprintf("ExprKind(%v)", int(v)) - } - return _ExprKindStrings[int(v)] -} - -var _ExprKindStrings = [...]string { - ExprKindNil: "ExprKindNil", - ExprKindLiteral: "ExprKindLiteral", - ExprKindPrefixed: "ExprKindPrefixed", - ExprKindPath: "ExprKindPath", - ExprKindRange: "ExprKindRange", - ExprKindArray: "ExprKindArray", - ExprKindDict: "ExprKindDict", - ExprKindField: "ExprKindField", -} - -// GoString implements [fmt.GoStringer]. -func (v ExprKind) GoString() string { - if int(v) < 0 || int(v) > len(_ExprKindStrings) { - return fmt.Sprintf("ast.ExprKind(%v)", int(v)) - } - return _ExprKindStrings[int(v)] -} - -var _ExprKindGoStrings = [...]string { - ExprKindNil: "ast.ExprKindNil", - ExprKindLiteral: "ast.ExprKindLiteral", - ExprKindPrefixed: "ast.ExprKindPrefixed", - ExprKindPath: "ast.ExprKindPath", - ExprKindRange: "ast.ExprKindRange", - ExprKindArray: "ast.ExprKindArray", - ExprKindDict: "ast.ExprKindDict", - ExprKindField: "ast.ExprKindField", -} - diff --git a/experimental/ast/expr_prefixed.go b/experimental/ast/expr_prefixed.go index 55806824..524e4936 100644 --- a/experimental/ast/expr_prefixed.go +++ b/experimental/ast/expr_prefixed.go @@ -19,31 +19,6 @@ import ( "github.com/bufbuild/protocompile/experimental/token" ) -//go:generate go run github.com/bufbuild/protocompile/internal/enum - -// TypePrefix is a prefix for an expression, such as a minus sign. -// -//enum:string -//enum:gostring -type ExprPrefix int8 - -const ( - ExprPrefixUnknown ExprPrefix = iota //enum:string unknown - ExprPrefixMinus //enum:string "-" -) - -// ExprPrefixByName looks up a prefix kind by name. -// -// If name is not a known prefix, returns [ExprPrefixUnknown]. -func ExprPrefixByName(name string) ExprPrefix { - switch name { - case "-": - return ExprPrefixMinus - default: - return ExprPrefixUnknown - } -} - // ExprPrefixed is an expression prefixed with an operator. type ExprPrefixed struct{ exprImpl[rawExprPrefixed] } diff --git a/experimental/ast/expr_prefixed_enum.go b/experimental/ast/expr_prefixed_enum.go deleted file mode 100644 index 6a5797f9..00000000 --- a/experimental/ast/expr_prefixed_enum.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2020-2024 Buf Technologies, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Code generated by github.com/bufbuild/protocompile/internal/enum. DO NOT EDIT. - -package ast - -import ( - "fmt" -) - -// String implements [fmt.Stringer]. -func (v ExprPrefix) String() string { - if int(v) < 0 || int(v) > len(_ExprPrefixStrings) { - return fmt.Sprintf("ExprPrefix(%v)", int(v)) - } - return _ExprPrefixStrings[int(v)] -} - -var _ExprPrefixStrings = [...]string { - ExprPrefixUnknown: "unknown", - ExprPrefixMinus: "-", -} - -// GoString implements [fmt.GoStringer]. -func (v ExprPrefix) GoString() string { - if int(v) < 0 || int(v) > len(_ExprPrefixStrings) { - return fmt.Sprintf("ast.ExprPrefix(%v)", int(v)) - } - return _ExprPrefixStrings[int(v)] -} - -var _ExprPrefixGoStrings = [...]string { - ExprPrefixUnknown: "ast.ExprPrefixUnknown", - ExprPrefixMinus: "ast.ExprPrefixMinus", -} - diff --git a/experimental/ast/type.go b/experimental/ast/type.go index d1efc286..a56ff1ff 100644 --- a/experimental/ast/type.go +++ b/experimental/ast/type.go @@ -22,22 +22,6 @@ import ( "github.com/bufbuild/protocompile/internal/arena" ) -//go:generate go run github.com/bufbuild/protocompile/internal/enum - -// TypeKind is a kind of type. There is one value of TypeKind for each -// Type* type in this package. -// -//enum:string -//enum:gostring -type TypeKind int8 - -const ( - TypeKindNil TypeKind = iota - TypeKindPath - TypeKindPrefixed - TypeKindGeneric -) - // TypeAny is any Type* type in this package. // // Values of this type can be obtained by calling an AsAny method on a Type* diff --git a/experimental/ast/type_enum.go b/experimental/ast/type_enum.go deleted file mode 100644 index ba7d9a70..00000000 --- a/experimental/ast/type_enum.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2020-2024 Buf Technologies, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Code generated by github.com/bufbuild/protocompile/internal/enum. DO NOT EDIT. - -package ast - -import ( - "fmt" -) - -// String implements [fmt.Stringer]. -func (v TypeKind) String() string { - if int(v) < 0 || int(v) > len(_TypeKindStrings) { - return fmt.Sprintf("TypeKind(%v)", int(v)) - } - return _TypeKindStrings[int(v)] -} - -var _TypeKindStrings = [...]string { - TypeKindNil: "TypeKindNil", - TypeKindPath: "TypeKindPath", - TypeKindPrefixed: "TypeKindPrefixed", - TypeKindGeneric: "TypeKindGeneric", -} - -// GoString implements [fmt.GoStringer]. -func (v TypeKind) GoString() string { - if int(v) < 0 || int(v) > len(_TypeKindStrings) { - return fmt.Sprintf("ast.TypeKind(%v)", int(v)) - } - return _TypeKindStrings[int(v)] -} - -var _TypeKindGoStrings = [...]string { - TypeKindNil: "ast.TypeKindNil", - TypeKindPath: "ast.TypeKindPath", - TypeKindPrefixed: "ast.TypeKindPrefixed", - TypeKindGeneric: "ast.TypeKindGeneric", -} - diff --git a/experimental/ast/type_prefixed.go b/experimental/ast/type_prefixed.go index 2804c346..9e7c5bdc 100644 --- a/experimental/ast/type_prefixed.go +++ b/experimental/ast/type_prefixed.go @@ -69,43 +69,3 @@ func (t TypePrefixed) Span() report.Span { return report.Join(t.PrefixToken(), t.Type()) } - -//go:generate go run github.com/bufbuild/protocompile/internal/enum - -// TypeKind is a kind of type. There is one value of TypeKind for each -// Type* type in this package. -// -// TypePrefix is a prefix for a type, such as required, optional, or repeated. -// -//enum:string -//enum:gostring -type TypePrefix int8 - -const ( - TypePrefixUnknown TypePrefix = iota //enum:string unknown - TypePrefixOptional //enum:string optional - TypePrefixRepeated //enum:string repeated - TypePrefixRequired //enum:string required - - // This is the "stream Foo.bar" syntax of RPC methods. It is also treated as - // a prefix. - TypePrefixStream //enum:string stream -) - -// TypePrefixByName looks up a prefix kind by name. -// -// If name is not a known prefix, returns [TypePrefixUnknown]. -func TypePrefixByName(name string) TypePrefix { - switch name { - case "optional": - return TypePrefixOptional - case "repeated": - return TypePrefixRepeated - case "required": - return TypePrefixRequired - case "stream": - return TypePrefixStream - default: - return TypePrefixUnknown - } -} diff --git a/experimental/ast/type_prefixed_enum.go b/experimental/ast/type_prefixed_enum.go deleted file mode 100644 index 4406513f..00000000 --- a/experimental/ast/type_prefixed_enum.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2020-2024 Buf Technologies, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Code generated by github.com/bufbuild/protocompile/internal/enum. DO NOT EDIT. - -package ast - -import ( - "fmt" -) - -// String implements [fmt.Stringer]. -func (v TypePrefix) String() string { - if int(v) < 0 || int(v) > len(_TypePrefixStrings) { - return fmt.Sprintf("TypePrefix(%v)", int(v)) - } - return _TypePrefixStrings[int(v)] -} - -var _TypePrefixStrings = [...]string { - TypePrefixUnknown: "unknown", - TypePrefixOptional: "optional", - TypePrefixRepeated: "repeated", - TypePrefixRequired: "required", - TypePrefixStream: "stream", -} - -// GoString implements [fmt.GoStringer]. -func (v TypePrefix) GoString() string { - if int(v) < 0 || int(v) > len(_TypePrefixStrings) { - return fmt.Sprintf("ast.TypePrefix(%v)", int(v)) - } - return _TypePrefixStrings[int(v)] -} - -var _TypePrefixGoStrings = [...]string { - TypePrefixUnknown: "ast.TypePrefixUnknown", - TypePrefixOptional: "ast.TypePrefixOptional", - TypePrefixRepeated: "ast.TypePrefixRepeated", - TypePrefixRequired: "ast.TypePrefixRequired", - TypePrefixStream: "ast.TypePrefixStream", -} - diff --git a/experimental/internal/taxa/noun.go b/experimental/internal/taxa/noun.go new file mode 100644 index 00000000..004e1fe9 --- /dev/null +++ b/experimental/internal/taxa/noun.go @@ -0,0 +1,318 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by github.com/bufbuild/protocompile/internal/enum. DO NOT EDIT. + +package taxa + +import "fmt" + +//go:generate go run github.com/bufbuild/protocompile/internal/enum +// See noun.go.yaml. + +// Noun is a syntactic or semantic element within the grammar that can be +// referred to within a diagnostic. +type Noun int + +const ( + Unknown Noun = iota + Unrecognized + TopLevel + EOF + Decl + Empty + Syntax + Edition + Package + Import + WeakImport + PublicImport + Extensions + Reserved + Body + Def + Message + Enum + Service + Extend + Oneof + Option + CustomOption + Field + EnumValue + Method + CompactOptions + MethodIns + MethodOuts + FieldTag + OptionValue + QualifiedName + FullyQualifiedName + ExtensionName + Expr + Range + Array + Dict + DictField + Type + TypePath + TypeParams + Whitespace + Comment + Ident + String + Float + Int + Semicolon + Comma + Slash + Colon + Equals + Minus + Period + LParen + LBracket + LBrace + LAngle + RParen + RBracket + RBrace + RAngle + Parens + Brackets + Braces + Angles + KeywordSyntax + KeywordEdition + KeywordImport + KeywordWeak + KeywordPublic + KeywordPackage + KeywordOption + KeywordMessage + KeywordEnum + KeywordService + KeywordExtend + KeywordOneof + KeywordExtensions + KeywordReserved + KeywordTo + KeywordRPC + KeywordReturns + KeywordOptional + KeywordRepeated + KeywordRequired + KeywordGroup + KeywordStream + total int = iota) + +// String implements [fmt.Stringer]. +func (v Noun) String() string { + if int(v) < 0 || int(v) > len(_table_Noun_String) { + return fmt.Sprintf("Noun(%v)", int(v)) + } + return _table_Noun_String[int(v)] +} + +// GoString implements [fmt.GoStringer]. +func (v Noun) GoString() string { + if int(v) < 0 || int(v) > len(_table_Noun_GoString) { + return fmt.Sprintf("taxaNoun(%v)", int(v)) + } + return _table_Noun_GoString[int(v)] +} + +var _table_Noun_String = [...]string { + Unknown: "", + Unrecognized: "unrecognized token", + TopLevel: "file scope", + EOF: "end-of-file", + Decl: "declaration", + Empty: "empty declaration", + Syntax: "`syntax` declaration", + Edition: "`edition` declaration", + Package: "`package` declaration", + Import: "import", + WeakImport: "weak import", + PublicImport: "public import", + Extensions: "extension range", + Reserved: "reserved range", + Body: "definition body", + Def: "definition", + Message: "message definition", + Enum: "enum definition", + Service: "service definition", + Extend: "message extension block", + Oneof: "oneof definition", + Option: "option setting", + CustomOption: "custom option setting", + Field: "message field", + EnumValue: "enum value", + Method: "service method", + CompactOptions: "compact options", + MethodIns: "method parameter list", + MethodOuts: "method return type", + FieldTag: "message field tag", + OptionValue: "option setting value", + QualifiedName: "qualified name", + FullyQualifiedName: "fully qualified name", + ExtensionName: "extension name", + Expr: "expression", + Range: "range expression", + Array: "array expression", + Dict: "message expression", + DictField: "message field value", + Type: "type", + TypePath: "type name", + TypeParams: "type parameters", + Whitespace: "whitespace", + Comment: "comment", + Ident: "identifier", + String: "string literal", + Float: "floating-point literal", + Int: "integer literal", + Semicolon: "`;`", + Comma: "`,`", + Slash: "`/`", + Colon: "`:`", + Equals: "`=`", + Minus: "`-`", + Period: "`.`", + LParen: "`(`", + LBracket: "`[`", + LBrace: "`{`", + LAngle: "`<`", + RParen: "`)`", + RBracket: "`]`", + RBrace: "`}`", + RAngle: "`>`", + Parens: "`(...)`", + Brackets: "`[...]`", + Braces: "`{...}`", + Angles: "`<...>`", + KeywordSyntax: "`syntax`", + KeywordEdition: "`edition`", + KeywordImport: "`import`", + KeywordWeak: "`weak`", + KeywordPublic: "`public`", + KeywordPackage: "`package`", + KeywordOption: "`option`", + KeywordMessage: "`message`", + KeywordEnum: "`enum`", + KeywordService: "`service`", + KeywordExtend: "`extend`", + KeywordOneof: "`oneof`", + KeywordExtensions: "`extensions`", + KeywordReserved: "`reserved`", + KeywordTo: "`to`", + KeywordRPC: "`rpc`", + KeywordReturns: "`returns`", + KeywordOptional: "`optional`", + KeywordRepeated: "`repeated`", + KeywordRequired: "`required`", + KeywordGroup: "`group`", + KeywordStream: "`stream`", +} + +var _table_Noun_GoString = [...]string { + Unknown: "Unknown", + Unrecognized: "Unrecognized", + TopLevel: "TopLevel", + EOF: "EOF", + Decl: "Decl", + Empty: "Empty", + Syntax: "Syntax", + Edition: "Edition", + Package: "Package", + Import: "Import", + WeakImport: "WeakImport", + PublicImport: "PublicImport", + Extensions: "Extensions", + Reserved: "Reserved", + Body: "Body", + Def: "Def", + Message: "Message", + Enum: "Enum", + Service: "Service", + Extend: "Extend", + Oneof: "Oneof", + Option: "Option", + CustomOption: "CustomOption", + Field: "Field", + EnumValue: "EnumValue", + Method: "Method", + CompactOptions: "CompactOptions", + MethodIns: "MethodIns", + MethodOuts: "MethodOuts", + FieldTag: "FieldTag", + OptionValue: "OptionValue", + QualifiedName: "QualifiedName", + FullyQualifiedName: "FullyQualifiedName", + ExtensionName: "ExtensionName", + Expr: "Expr", + Range: "Range", + Array: "Array", + Dict: "Dict", + DictField: "DictField", + Type: "Type", + TypePath: "TypePath", + TypeParams: "TypeParams", + Whitespace: "Whitespace", + Comment: "Comment", + Ident: "Ident", + String: "String", + Float: "Float", + Int: "Int", + Semicolon: "Semicolon", + Comma: "Comma", + Slash: "Slash", + Colon: "Colon", + Equals: "Equals", + Minus: "Minus", + Period: "Period", + LParen: "LParen", + LBracket: "LBracket", + LBrace: "LBrace", + LAngle: "LAngle", + RParen: "RParen", + RBracket: "RBracket", + RBrace: "RBrace", + RAngle: "RAngle", + Parens: "Parens", + Brackets: "Brackets", + Braces: "Braces", + Angles: "Angles", + KeywordSyntax: "KeywordSyntax", + KeywordEdition: "KeywordEdition", + KeywordImport: "KeywordImport", + KeywordWeak: "KeywordWeak", + KeywordPublic: "KeywordPublic", + KeywordPackage: "KeywordPackage", + KeywordOption: "KeywordOption", + KeywordMessage: "KeywordMessage", + KeywordEnum: "KeywordEnum", + KeywordService: "KeywordService", + KeywordExtend: "KeywordExtend", + KeywordOneof: "KeywordOneof", + KeywordExtensions: "KeywordExtensions", + KeywordReserved: "KeywordReserved", + KeywordTo: "KeywordTo", + KeywordRPC: "KeywordRPC", + KeywordReturns: "KeywordReturns", + KeywordOptional: "KeywordOptional", + KeywordRepeated: "KeywordRepeated", + KeywordRequired: "KeywordRequired", + KeywordGroup: "KeywordGroup", + KeywordStream: "KeywordStream", +} diff --git a/experimental/internal/taxa/noun.go.yaml b/experimental/internal/taxa/noun.go.yaml new file mode 100644 index 00000000..d9042290 --- /dev/null +++ b/experimental/internal/taxa/noun.go.yaml @@ -0,0 +1,131 @@ +# Copyright 2020-2024 Buf Technologies, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http:#www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +- name: Noun + type: int + total: total + docs: | + Noun is a syntactic or semantic element within the grammar that can be + referred to within a diagnostic. + methods: + - kind: string + - kind: go-string + values: + - {name: Unknown, string: ""} + - {name: Unrecognized, string: "unrecognized token"} + - {name: TopLevel, string: "file scope"} + - {name: EOF, string: "end-of-file"} + + - {name: Decl, string: "declaration"} + - {name: Empty, string: "empty declaration"} + - {name: Syntax, string: "`syntax` declaration"} + - {name: Edition, string: "`edition` declaration"} + - {name: Package, string: "`package` declaration"} + - {name: Import, string: "import"} + - {name: WeakImport, string: "weak import"} + - {name: PublicImport, string: "public import"} + - {name: Extensions, string: "extension range"} + - {name: Reserved, string: "reserved range"} + - {name: Body, string: "definition body"} + + - {name: Def, string: "definition"} + - {name: Message, string: "message definition"} + - {name: Enum, string: "enum definition"} + - {name: Service, string: "service definition"} + - {name: Extend, string: "message extension block"} + - {name: Oneof, string: "oneof definition"} + + - {name: Option, string: "option setting"} + - {name: CustomOption, string: "custom option setting"} + + - {name: Field, string: "message field"} + - {name: EnumValue, string: "enum value"} + - {name: Method, string: "service method"} + + - {name: CompactOptions, string: "compact options"} + - {name: MethodIns, string: "method parameter list"} + - {name: MethodOuts, string: "method return type"} + + - {name: FieldTag, string: "message field tag"} + - {name: OptionValue, string: "option setting value"} + + - {name: QualifiedName, string: "qualified name"} + - {name: FullyQualifiedName, string: "fully qualified name"} + - {name: ExtensionName, string: "extension name"} + + - {name: Expr, string: "expression"} + - {name: Range, string: "range expression"} + - {name: Array, string: "array expression"} + - {name: Dict, string: "message expression"} + - {name: DictField, string: "message field value"} + + - {name: Type, string: "type"} + - {name: TypePath, string: "type name"} + - {name: TypeParams, string: "type parameters"} + + - {name: Whitespace, string: "whitespace"} + - {name: Comment, string: "comment"} + - {name: Ident, string: "identifier"} + - {name: String, string: "string literal"} + - {name: Float, string: "floating-point literal"} + - {name: Int, string: "integer literal"} + + - {name: Semicolon, string: "`;`"} + - {name: Comma, string: "`,`"} + - {name: Slash, string: "`/`"} + - {name: Colon, string: "`:`"} + - {name: Equals, string: "`=`"} + - {name: Minus, string: "`-`"} + - {name: Period, string: "`.`"} + + - {name: LParen, string: "`(`"} + - {name: LBracket, string: "`[`"} + - {name: LBrace, string: "`{`"} + - {name: LAngle, string: "`<`"} + + - {name: RParen, string: "`)`"} + - {name: RBracket, string: "`]`"} + - {name: RBrace, string: "`}`"} + - {name: RAngle, string: "`>`"} + + - {name: Parens, string: "`(...)`"} + - {name: Brackets, string: "`[...]`"} + - {name: Braces, string: "`{...}`"} + - {name: Angles, string: "`<...>`"} + + - {name: KeywordSyntax, string: "`syntax`"} + - {name: KeywordEdition, string: "`edition`"} + - {name: KeywordImport, string: "`import`"} + - {name: KeywordWeak, string: "`weak`"} + - {name: KeywordPublic, string: "`public`"} + - {name: KeywordPackage, string: "`package`"} + + - {name: KeywordOption, string: "`option`"} + - {name: KeywordMessage, string: "`message`"} + - {name: KeywordEnum, string: "`enum`"} + - {name: KeywordService, string: "`service`"} + - {name: KeywordExtend, string: "`extend`"} + - {name: KeywordOneof, string: "`oneof`"} + + - {name: KeywordExtensions, string: "`extensions`"} + - {name: KeywordReserved, string: "`reserved`"} + - {name: KeywordTo, string: "`to`"} + - {name: KeywordRPC, string: "`rpc`"} + - {name: KeywordReturns, string: "`returns`"} + + - {name: KeywordOptional, string: "`optional`"} + - {name: KeywordRepeated, string: "`repeated`"} + - {name: KeywordRequired, string: "`required`"} + - {name: KeywordGroup, string: "`group`"} + - {name: KeywordStream, string: "`stream`"} \ No newline at end of file diff --git a/experimental/internal/taxa/taxa.go b/experimental/internal/taxa/taxa.go index 51344933..b7be0176 100644 --- a/experimental/internal/taxa/taxa.go +++ b/experimental/internal/taxa/taxa.go @@ -21,131 +21,7 @@ // represents "everything" the parser stack pushes around. package taxa -import ( - "fmt" -) - -//go:generate go run github.com/bufbuild/protocompile/internal/enum - -// Noun is a syntactic or semantic element within the grammar that can be -// referred to within a diagnostic. -// -//enum:string -//enum:gostring -type Noun int - -const ( - Unknown Noun = iota //enum:string "" - Unrecognized //enum:string "unrecognized token" - TopLevel //enum:string "file scope" - EOF //enum:string "end-of-file" - - Decl //enum:string "declaration" - Empty //enum:string "empty declaration" - Syntax //enum:string "`syntax` declaration" - Edition //enum:string "`edition` declaration" - Package //enum:string "`package` declaration" - Import //enum:string "import" - WeakImport //enum:string "weak import" - PublicImport //enum:string "public import" - Extensions //enum:string "extension range" - Reserved //enum:string "reserved range" - Body //enum:string "definition body" - - Def //enum:string "definition" - Message //enum:string "message definition" - Enum //enum:string "enum definition" - Service //enum:string "service definition" - Extend //enum:string "message extension block" - Oneof //enum:string "oneof definition" - - Option //enum:string "option setting" - CustomOption //enum:string "custom option setting" - - Field //enum:string "message field" - EnumValue //enum:string "enum value" - Method //enum:string "service method" - - CompactOptions //enum:string "compact options" - MethodIns //enum:string "method parameter list" - MethodOuts //enum:string "method return type" - - FieldTag //enum:string "message field tag" - OptionValue //enum:string "option setting value" - - QualifiedName //enum:string "qualified name" - FullyQualifiedName //enum:string "fully qualified name" - ExtensionName //enum:string "extension name" - - Expr //enum:string "expression" - Range //enum:string "range expression" - Array //enum:string "array expression" - Dict //enum:string "message expression" - DictField //enum:string "message field value" - - Type //enum:string "type" - TypePath //enum:string "type name" - TypeParams //enum:string "type parameters" - - Whitespace //enum:string "whitespace" - Comment //enum:string "comment" - Ident //enum:string "identifier" - String //enum:string "string literal" - Float //enum:string "floating-point literal" - Int //enum:string "integer literal" - - Semicolon //enum:string "`;`" - Comma //enum:string "`,`" - Slash //enum:string "`/`" - Colon //enum:string "`:`" - Equals //enum:string "`=`" - Minus //enum:string "`-`" - Period //enum:string "`.`" - - LParen //enum:string "`(`" - LBracket //enum:string "`[`" - LBrace //enum:string "`{`" - LAngle //enum:string "`<`" - - RParen //enum:string "`)`" - RBracket //enum:string "`]`" - RBrace //enum:string "`}`" - RAngle //enum:string "`>`" - - Parens //enum:string "`(...)`" - Brackets //enum:string "`[...]`" - Braces //enum:string "`{...}`" - Angles //enum:string "`<...>`" - - KeywordSyntax //enum:string "`syntax`" - KeywordEdition //enum:string "`edition`" - KeywordImport //enum:string "`import`" - KeywordWeak //enum:string "`weak`" - KeywordPublic //enum:string "`public`" - KeywordPackage //enum:string "`package`" - - KeywordOption //enum:string "`option`" - KeywordMessage //enum:string "`message`" - KeywordEnum //enum:string "`enum`" - KeywordService //enum:string "`service`" - KeywordExtend //enum:string "`extend`" - KeywordOneof //enum:string "`oneof`" - - KeywordExtensions //enum:string "`extensions`" - KeywordReserved //enum:string "`reserved`" - KeywordTo //enum:string "`to`" - KeywordRPC //enum:string "`rpc`" - KeywordReturns //enum:string "`returns`" - - KeywordOptional //enum:string "`optional`" - KeywordRepeated //enum:string "`repeated`" - KeywordRequired //enum:string "`required`" - KeywordGroup //enum:string "`group`" - KeywordStream //enum:string "`stream`" - - // total is the total number of known [What] values. - total int = iota -) +import "fmt" // In is a shorthand for the "in" preposition. func (s Noun) In() Place { diff --git a/experimental/internal/taxa/taxa_enum.go b/experimental/internal/taxa/taxa_enum.go deleted file mode 100644 index 4d853d06..00000000 --- a/experimental/internal/taxa/taxa_enum.go +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright 2020-2024 Buf Technologies, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Code generated by github.com/bufbuild/protocompile/internal/enum. DO NOT EDIT. - -package taxa - -import ( - "fmt" -) - -// String implements [fmt.Stringer]. -func (v Noun) String() string { - if int(v) < 0 || int(v) > len(_NounStrings) { - return fmt.Sprintf("Noun(%v)", int(v)) - } - return _NounStrings[int(v)] -} - -var _NounStrings = [...]string { - Unknown: "", - Unrecognized: "unrecognized token", - TopLevel: "file scope", - EOF: "end-of-file", - Decl: "declaration", - Empty: "empty declaration", - Syntax: "`syntax` declaration", - Edition: "`edition` declaration", - Package: "`package` declaration", - Import: "import", - WeakImport: "weak import", - PublicImport: "public import", - Extensions: "extension range", - Reserved: "reserved range", - Body: "definition body", - Def: "definition", - Message: "message definition", - Enum: "enum definition", - Service: "service definition", - Extend: "message extension block", - Oneof: "oneof definition", - Option: "option setting", - CustomOption: "custom option setting", - Field: "message field", - EnumValue: "enum value", - Method: "service method", - CompactOptions: "compact options", - MethodIns: "method parameter list", - MethodOuts: "method return type", - FieldTag: "message field tag", - OptionValue: "option setting value", - QualifiedName: "qualified name", - FullyQualifiedName: "fully qualified name", - ExtensionName: "extension name", - Expr: "expression", - Range: "range expression", - Array: "array expression", - Dict: "message expression", - DictField: "message field value", - Type: "type", - TypePath: "type name", - TypeParams: "type parameters", - Whitespace: "whitespace", - Comment: "comment", - Ident: "identifier", - String: "string literal", - Float: "floating-point literal", - Int: "integer literal", - Semicolon: "`;`", - Comma: "`,`", - Slash: "`/`", - Colon: "`:`", - Equals: "`=`", - Minus: "`-`", - Period: "`.`", - LParen: "`(`", - LBracket: "`[`", - LBrace: "`{`", - LAngle: "`<`", - RParen: "`)`", - RBracket: "`]`", - RBrace: "`}`", - RAngle: "`>`", - Parens: "`(...)`", - Brackets: "`[...]`", - Braces: "`{...}`", - Angles: "`<...>`", - KeywordSyntax: "`syntax`", - KeywordEdition: "`edition`", - KeywordImport: "`import`", - KeywordWeak: "`weak`", - KeywordPublic: "`public`", - KeywordPackage: "`package`", - KeywordOption: "`option`", - KeywordMessage: "`message`", - KeywordEnum: "`enum`", - KeywordService: "`service`", - KeywordExtend: "`extend`", - KeywordOneof: "`oneof`", - KeywordExtensions: "`extensions`", - KeywordReserved: "`reserved`", - KeywordTo: "`to`", - KeywordRPC: "`rpc`", - KeywordReturns: "`returns`", - KeywordOptional: "`optional`", - KeywordRepeated: "`repeated`", - KeywordRequired: "`required`", - KeywordGroup: "`group`", - KeywordStream: "`stream`", -} - -// GoString implements [fmt.GoStringer]. -func (v Noun) GoString() string { - if int(v) < 0 || int(v) > len(_NounStrings) { - return fmt.Sprintf("taxa.Noun(%v)", int(v)) - } - return _NounStrings[int(v)] -} - -var _NounGoStrings = [...]string { - Unknown: "taxa.Unknown", - Unrecognized: "taxa.Unrecognized", - TopLevel: "taxa.TopLevel", - EOF: "taxa.EOF", - Decl: "taxa.Decl", - Empty: "taxa.Empty", - Syntax: "taxa.Syntax", - Edition: "taxa.Edition", - Package: "taxa.Package", - Import: "taxa.Import", - WeakImport: "taxa.WeakImport", - PublicImport: "taxa.PublicImport", - Extensions: "taxa.Extensions", - Reserved: "taxa.Reserved", - Body: "taxa.Body", - Def: "taxa.Def", - Message: "taxa.Message", - Enum: "taxa.Enum", - Service: "taxa.Service", - Extend: "taxa.Extend", - Oneof: "taxa.Oneof", - Option: "taxa.Option", - CustomOption: "taxa.CustomOption", - Field: "taxa.Field", - EnumValue: "taxa.EnumValue", - Method: "taxa.Method", - CompactOptions: "taxa.CompactOptions", - MethodIns: "taxa.MethodIns", - MethodOuts: "taxa.MethodOuts", - FieldTag: "taxa.FieldTag", - OptionValue: "taxa.OptionValue", - QualifiedName: "taxa.QualifiedName", - FullyQualifiedName: "taxa.FullyQualifiedName", - ExtensionName: "taxa.ExtensionName", - Expr: "taxa.Expr", - Range: "taxa.Range", - Array: "taxa.Array", - Dict: "taxa.Dict", - DictField: "taxa.DictField", - Type: "taxa.Type", - TypePath: "taxa.TypePath", - TypeParams: "taxa.TypeParams", - Whitespace: "taxa.Whitespace", - Comment: "taxa.Comment", - Ident: "taxa.Ident", - String: "taxa.String", - Float: "taxa.Float", - Int: "taxa.Int", - Semicolon: "taxa.Semicolon", - Comma: "taxa.Comma", - Slash: "taxa.Slash", - Colon: "taxa.Colon", - Equals: "taxa.Equals", - Minus: "taxa.Minus", - Period: "taxa.Period", - LParen: "taxa.LParen", - LBracket: "taxa.LBracket", - LBrace: "taxa.LBrace", - LAngle: "taxa.LAngle", - RParen: "taxa.RParen", - RBracket: "taxa.RBracket", - RBrace: "taxa.RBrace", - RAngle: "taxa.RAngle", - Parens: "taxa.Parens", - Brackets: "taxa.Brackets", - Braces: "taxa.Braces", - Angles: "taxa.Angles", - KeywordSyntax: "taxa.KeywordSyntax", - KeywordEdition: "taxa.KeywordEdition", - KeywordImport: "taxa.KeywordImport", - KeywordWeak: "taxa.KeywordWeak", - KeywordPublic: "taxa.KeywordPublic", - KeywordPackage: "taxa.KeywordPackage", - KeywordOption: "taxa.KeywordOption", - KeywordMessage: "taxa.KeywordMessage", - KeywordEnum: "taxa.KeywordEnum", - KeywordService: "taxa.KeywordService", - KeywordExtend: "taxa.KeywordExtend", - KeywordOneof: "taxa.KeywordOneof", - KeywordExtensions: "taxa.KeywordExtensions", - KeywordReserved: "taxa.KeywordReserved", - KeywordTo: "taxa.KeywordTo", - KeywordRPC: "taxa.KeywordRPC", - KeywordReturns: "taxa.KeywordReturns", - KeywordOptional: "taxa.KeywordOptional", - KeywordRepeated: "taxa.KeywordRepeated", - KeywordRequired: "taxa.KeywordRequired", - KeywordGroup: "taxa.KeywordGroup", - KeywordStream: "taxa.KeywordStream", -} - diff --git a/experimental/token/kind.go b/experimental/token/kind.go index 18a99d77..9b7367bc 100644 --- a/experimental/token/kind.go +++ b/experimental/token/kind.go @@ -12,34 +12,60 @@ // See the License for the specific language governing permissions and // limitations under the License. +// Code generated by github.com/bufbuild/protocompile/internal/enum. DO NOT EDIT. + package token +import "fmt" + //go:generate go run github.com/bufbuild/protocompile/internal/enum +// See kind.go.yaml. // Kind identifies what kind of token a particular [Token] is. -// -//enum:string -//enum:gostring type Kind byte const ( Unrecognized Kind = iota // Unrecognized garbage in the input file. - - Space // Non-comment contiguous whitespace. - Comment // A single comment. - Ident // An identifier. - String // A string token. May be a non-leaf for non-contiguous quoted strings. - Number // A run of digits that is some kind of number. - Punct // Some punctuation. May be a non-leaf for delimiters like {}. - _KindUnused // Reserved for future use. - //enum:skip - - // DO NOT ADD MORE TOKEN KINDS: ONLY THREE BITS ARE AVAILABLE - // TO STORE THEM. + Space // Non-comment contiguous whitespace. + Comment // A single comment. + Ident // An identifier. + String // A string token. May be a non-leaf for non-contiguous quoted strings. + Number // A run of digits that is some kind of number. + Punct // Some punctuation. May be a non-leaf for delimiters like {}. ) -// IsSkippable returns whether this is a token that should be examined during -// syntactic analysis. -func (t Kind) IsSkippable() bool { - return t == Space || t == Comment || t == Unrecognized +// String implements [fmt.Stringer]. +func (v Kind) String() string { + if int(v) < 0 || int(v) > len(_table_Kind_String) { + return fmt.Sprintf("Kind(%v)", int(v)) + } + return _table_Kind_String[int(v)] +} + +// GoString implements [fmt.GoStringer]. +func (v Kind) GoString() string { + if int(v) < 0 || int(v) > len(_table_Kind_GoString) { + return fmt.Sprintf("tokenKind(%v)", int(v)) + } + return _table_Kind_GoString[int(v)] +} + +var _table_Kind_String = [...]string { + Unrecognized: "Unrecognized", + Space: "Space", + Comment: "Comment", + Ident: "Ident", + String: "String", + Number: "Number", + Punct: "Punct", +} + +var _table_Kind_GoString = [...]string { + Unrecognized: "Unrecognized", + Space: "Space", + Comment: "Comment", + Ident: "Ident", + String: "String", + Number: "Number", + Punct: "Punct", } diff --git a/experimental/token/kind.go.yaml b/experimental/token/kind.go.yaml new file mode 100644 index 00000000..138de6af --- /dev/null +++ b/experimental/token/kind.go.yaml @@ -0,0 +1,39 @@ +# Copyright 2020-2024 Buf Technologies, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http:#www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +- name: Kind + type: byte + docs: | + Kind identifies what kind of token a particular [Token] is. + methods: + - kind: string + - kind: go-string + values: + - name: Unrecognized + docs: "Unrecognized garbage in the input file." + - name: Space + docs: "Non-comment contiguous whitespace." + - name: Comment + docs: "A single comment." + - name: Ident + docs: "An identifier." + - name: String + docs: "A string token. May be a non-leaf for non-contiguous quoted strings." + - name: Number + docs: "A run of digits that is some kind of number." + - name: Punct + docs: "Some punctuation. May be a non-leaf for delimiters like {}." + # - name: Reserved + # DO NOT ADD MORE TOKEN KINDS: ONLY THREE BITS ARE AVAILABLE + # TO STORE THEM. \ No newline at end of file diff --git a/experimental/token/kind_enum.go b/experimental/token/kind_enum.go deleted file mode 100644 index 00f4846d..00000000 --- a/experimental/token/kind_enum.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2020-2024 Buf Technologies, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Code generated by github.com/bufbuild/protocompile/internal/enum. DO NOT EDIT. - -package token - -import ( - "fmt" -) - -// String implements [fmt.Stringer]. -func (v Kind) String() string { - if int(v) < 0 || int(v) > len(_KindStrings) { - return fmt.Sprintf("Kind(%v)", int(v)) - } - return _KindStrings[int(v)] -} - -var _KindStrings = [...]string { - Unrecognized: "Unrecognized", - Space: "Space", - Comment: "Comment", - Ident: "Ident", - String: "String", - Number: "Number", - Punct: "Punct", -} - -// GoString implements [fmt.GoStringer]. -func (v Kind) GoString() string { - if int(v) < 0 || int(v) > len(_KindStrings) { - return fmt.Sprintf("token.Kind(%v)", int(v)) - } - return _KindStrings[int(v)] -} - -var _KindGoStrings = [...]string { - Unrecognized: "token.Unrecognized", - Space: "token.Space", - Comment: "token.Comment", - Ident: "token.Ident", - String: "token.String", - Number: "token.Number", - Punct: "token.Punct", -} - diff --git a/experimental/token/token.go b/experimental/token/token.go index f090534d..0df6d5a3 100644 --- a/experimental/token/token.go +++ b/experimental/token/token.go @@ -24,6 +24,13 @@ import ( "github.com/bufbuild/protocompile/experimental/report" ) +// IsSkippable returns whether this is a token that should be examined during +// syntactic analysis. +func (t Kind) IsSkippable() bool { + // Note: kind.go is a generated file. + return t == Space || t == Comment || t == Unrecognized +} + // Nil is the nil [Token], i.e., the zero value. var Nil Token diff --git a/internal/enum/enum.go b/internal/enum/enum.go index e3ea9e35..1c85925c 100644 --- a/internal/enum/enum.go +++ b/internal/enum/enum.go @@ -18,42 +18,12 @@ // // //go:generate go run github.com/bufbuild/protocompile/internal/enum // -// To generate code for an enum type, annotate it with the appropriate -// directives. +// There should be a file with the same name as the file to generate with a +// .yaml prefix. E.g., if the generate directive appears in foo.go, it should +// there should be a foo.go.yaml file, which must contain an array of the +// Enum type defined in this package. // -// - //enum:string Name will generate a stringifying methods named Name, -// which default to "String". -// -// - //enum:gostring Name is similar, but the default name is "GoString" -// and each constant stringifies to "package.Constant". -// -// - //enum:fromstring Name generates a function that takes a string and -// returns the constant that stringifies to that value, or 0 if there is -// no such value. -// -// - //enum:doc directive "..." adds a line of doc comment to the method -// generated by directive with the given text. -// -// - //enum:import "..." will include a package to import in the generated -// file. -// -// - //enum:skip, attached to an enum constant, will cause that constant to -// be ignored by the generator. -// -// - //enum:stringfunc Name provides a function to use for stringifying -// based on the name of a constant (see below). -// -// The string representation of an enum constant is computed by the following -// process: -// -// 1. If the constant is annotated with //enum:string "...", it will use that -// value. -// -// 2. If the enum type is annotated with //enum:stringfunc Name, the string -// will be Name("Constant"), where Constant is the Go-declared name of the -// constant. -// -// 3. The string is "Constant". +//nolint:revive // We use _ in field names to disambiguate them from methods, while still exporting them. package main import ( @@ -66,21 +36,40 @@ import ( "text/template" "gopkg.in/yaml.v3" + + "github.com/bufbuild/protocompile/internal/ext/slicesx" ) type Enum struct { - Name string `yaml:"name"` - Type string `yaml:"type"` - Docs string `yaml:"docs"` + Name string `yaml:"name"` // The name of the new type. + Type string `yaml:"type"` // The underlying type. + Docs string `yaml:"docs"` // Documentation for the type. + Total string `yaml:"total"` // The name of a "total values" constant. Methods []Method `yaml:"methods"` - Values []Value `yaml:"values"` + Values_ []Value `yaml:"values"` +} + +func (e *Enum) Values() []Value { + for i := range e.Values_ { + e.Values_[i].Parent = e + e.Values_[i].Idx = i + } + return e.Values_ } type Value struct { - Name string `yaml:"name"` - Alias string `yaml:"alias"` - String_ string `yaml:"string"` - Docs string `yaml:"docs"` + Name string `yaml:"name"` // The name of the value. + Alias string `yaml:"alias"` // Another value this value aliases, if any. + String_ string `yaml:"string"` // The string representation of this value. + Docs string `yaml:"docs"` // Documentation for the value. + + Parent *Enum `yaml:"-"` + Idx int `yaml:"-"` +} + +func (v Value) HasSuffixDocs() bool { + next, ok := slicesx.Get(v.Parent.Values_, v.Idx+1) + return v.Docs != "" && !strings.Contains(v.Docs, "\n") && (!ok || next.Docs != "") } func (v Value) String() string { @@ -91,10 +80,10 @@ func (v Value) String() string { } type Method struct { - Kind MethodKind `yaml:"kind"` - Name_ string `yaml:"name"` - Docs_ string `yaml:"docs"` - Skip []string `yaml:"skip"` + Kind MethodKind `yaml:"kind"` // The kind of method to generate. + Name_ string `yaml:"name"` // The method's name; optional for some methods. + Docs_ string `yaml:"docs"` // Documentation for the method. + Skip []string `yaml:"skip"` // Enum values to ignore in this method. } func (m Method) Name() (string, error) { diff --git a/internal/enum/generated.go.tmpl b/internal/enum/generated.go.tmpl index 422d5499..b54839d9 100644 --- a/internal/enum/generated.go.tmpl +++ b/internal/enum/generated.go.tmpl @@ -26,13 +26,14 @@ import "fmt" type {{$e.Name}} {{$e.Type}} const ( -{{range $i, $v := $e.Values -}}{{if ne $v.Docs ""}} -{{end}}{{makeDocs $v.Docs "\t" -}} +{{range $i, $v := $e.Values -}}{{if and (not $v.HasSuffixDocs) (ne $v.Docs "")}} +{{makeDocs $v.Docs "\t"}}{{end -}} {{if eq $i 0}} {{$v.Name}} {{$e.Name}} = iota -{{else if ne $v.Alias ""}} {{$v.Name}} = {{$v.Alias}} -{{else}} {{$v.Name}} -{{end -}} +{{- else if ne $v.Alias ""}} {{$v.Name}} = {{$v.Alias}} +{{- else}} {{$v.Name}} +{{- end}}{{if $v.HasSuffixDocs}} // {{$v.Docs}}{{end}} {{end -}} +{{with $e.Total}} {{.}} int = iota{{end -}} ) {{range $_, $m := $e.Methods -}} {{- if eq $m.Kind "string"}}