diff --git a/Makefile b/Makefile index ebf1cb59..95937728 100644 --- a/Makefile +++ b/Makefile @@ -75,7 +75,9 @@ install: ## Install all binaries .PHONY: lint lint: $(BIN)/golangci-lint ## Lint Go - $(GO) vet ./... ./internal/benchmarks/... + @# golanci-lint already catches unsafe.Pointer misuse, and can be silenced + @# when it has false positives, unlike go vet, which cannot. + $(GO) vet -unsafeptr=false ./... ./internal/benchmarks/... $(BIN)/golangci-lint run cd internal/benchmarks && $(BIN)/golangci-lint run diff --git a/experimental/expr/block.go b/experimental/expr/block.go new file mode 100644 index 00000000..88815323 --- /dev/null +++ b/experimental/expr/block.go @@ -0,0 +1,67 @@ +// Copyright 2020-2025 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 expr + +import ( + "github.com/bufbuild/protocompile/experimental/id" + "github.com/bufbuild/protocompile/experimental/seq" + "github.com/bufbuild/protocompile/experimental/source" + "github.com/bufbuild/protocompile/experimental/token" +) + +// Block := `{` (Expr (`;` | `\n`))* Expr? `}`. +type Block id.Node[Block, *Context, *rawBlock] + +// BlockArgs is arguments for [Nodes.NewBlock]. +type BlockArgs struct { + Braces token.Token +} + +type rawBlock struct { + braces token.ID + tags id.DynSeq[Expr, Kind, *Context] +} + +// AsAny type-erases this type value. +// +// See [Expr] for more information. +func (e Block) AsAny() Expr { + if e.IsZero() { + return Expr{} + } + return id.WrapDyn(e.Context(), id.NewDyn(KindBlock, id.ID[Expr](e.ID()))) +} + +// Braces returns the braces that surround this block. +func (e Block) Braces() token.Token { + if e.IsZero() { + return token.Zero + } + return id.Wrap(e.Context().Stream(), e.Raw().braces) +} + +// Exprs returns an inserter over the expressions in this block. +func (e Block) Exprs() seq.Inserter[Expr] { + var tags *id.DynSeq[Expr, Kind, *Context] + if !e.IsZero() { + tags = &e.Raw().tags + } + return tags.Inserter(e.Context()) +} + +// Span implements [source.Spanner]. +func (e Block) Span() source.Span { + return e.Braces().Span() +} diff --git a/experimental/expr/call.go b/experimental/expr/call.go new file mode 100644 index 00000000..aa21f638 --- /dev/null +++ b/experimental/expr/call.go @@ -0,0 +1,76 @@ +// Copyright 2020-2025 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 expr + +import ( + "github.com/bufbuild/protocompile/experimental/id" + "github.com/bufbuild/protocompile/experimental/source" + "github.com/bufbuild/protocompile/experimental/token" +) + +// Call is a function call/indexing expression, consisting of an expression +// followed by bracketed [Params]. +// +// # Grammar +// +// Call := Expr (`(` Params `)` | `[` Params `]` | `{` Params `}`) +type Call id.Node[Call, *Context, *rawCall] + +// CallArgs is arguments for [Nodes.NewCall]. +type CallArgs struct { + Callee Expr + Args Params +} + +type rawCall struct { + callee id.Dyn[Expr, Kind] + args id.ID[Params] +} + +// AsAny type-erases this type value. +// +// See [Expr] for more information. +func (e Call) AsAny() Expr { + if e.IsZero() { + return Expr{} + } + return id.WrapDyn(e.Context(), id.NewDyn(KindCall, id.ID[Expr](e.ID()))) +} + +// Callee returns the expression's callee. +func (e Call) Callee() Expr { + if e.IsZero() { + return Expr{} + } + return id.WrapDyn(e.Context(), e.Raw().callee) +} + +// Args returns the expression's arguments. +func (e Call) Args() Params { + if e.IsZero() { + return Params{} + } + return id.Wrap(e.Context(), e.Raw().args) +} + +// Brackets returns the brackets for this call. +func (e Call) Brackets() token.Token { + return e.Args().Brackets() +} + +// Span implements [source.Spanner]. +func (e Call) Span() source.Span { + return source.Join(e.Callee(), e.Args()) +} diff --git a/experimental/expr/context.go b/experimental/expr/context.go new file mode 100644 index 00000000..243d9eaf --- /dev/null +++ b/experimental/expr/context.go @@ -0,0 +1,241 @@ +// Copyright 2020-2025 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 expr + +import ( + "fmt" + + "github.com/bufbuild/protocompile/experimental/id" + "github.com/bufbuild/protocompile/experimental/token" + "github.com/bufbuild/protocompile/internal/arena" + "github.com/bufbuild/protocompile/internal/ext/unsafex" +) + +// Context is book-keeping for all of the expressions parsed from a particular +// [token.Stream]. +// +// This type does not represent an AST node; instead, it is intended to be +// embedded into the top-level struct for one of the other AST packages in +// the compiler. +type Context struct { + _ unsafex.NoCopy + stream *token.Stream + + arenas +} + +// Nodes provides storage for the various AST node types, and can be used +// to construct new ones. +type Nodes Context + +// New creates a fresh expression context for a stream. +func New(stream *token.Stream) *Context { + return &Context{stream: stream} +} + +// Stream returns the underlying token stream. +func (c *Context) Stream() *token.Stream { + if c == nil { + return nil + } + return c.stream +} + +// Nodes returns the node arena for this file, which can be used to allocate +// new AST nodes. +func (c *Context) Nodes() *Nodes { + return (*Nodes)(c) +} + +// File returns the [File] that this Nodes adds nodes to. +func (n *Nodes) Context() *Context { + return (*Context)(n) +} + +// NewBlock constructs a new [Block] in this context. +func (n *Nodes) NewBlock(args BlockArgs) Block { + n.panicIfNotOurs(args.Braces) + + return id.Wrap(n.Context(), id.ID[Block](n.Context().blocks.NewCompressed(rawBlock{ + braces: args.Braces.ID(), + }))) +} + +// NewCall constructs a new [Call] in this context. +func (n *Nodes) NewCall(args CallArgs) Call { + n.panicIfNotOurs(args.Callee, args.Args) + + return id.Wrap(n.Context(), id.ID[Call](n.Context().calls.NewCompressed(rawCall{ + callee: args.Callee.ID(), + args: args.Args.ID(), + }))) +} + +// NewCase constructs a new [Case] in this context. +func (n *Nodes) NewCase(args CaseArgs) Case { + n.panicIfNotOurs(args.Keyword, args.Alts, args.Colon, args.Block) + + return id.Wrap(n.Context(), id.ID[Case](n.Context().cases.NewCompressed(rawCase{ + kw: args.Keyword.ID(), + alts: args.Alts.ID(), + colon: args.Colon.ID(), + block: args.Block.ID(), + }))) +} + +// NewCase constructs a new [Case] in this context. +func (n *Nodes) NewControl(args ControlArgs) Control { + n.panicIfNotOurs(args.Keyword, args.Args, args.If, args.Condition) + + return id.Wrap(n.Context(), id.ID[Control](n.Context().controls.NewCompressed(rawControl{ + kw: args.Keyword.ID(), + args: args.Args.ID(), + ifT: args.If.ID(), + cond: args.Condition.ID(), + }))) +} + +// NewFor constructs a new [Case] in this context. +func (n *Nodes) NewFor(args ForArgs) For { + n.panicIfNotOurs(args.For, args.Vars, args.In, args.Iterator, args.Block) + + return id.Wrap(n.Context(), id.ID[For](n.Context().fors.NewCompressed(rawFor{ + forT: args.For.ID(), + vars: args.Vars.ID(), + inT: args.For.ID(), + iter: args.Iterator.ID(), + block: args.Block.ID(), + }))) +} + +// NewIf constructs a new [If] in this context. +func (n *Nodes) NewIf(args IfArgs) If { + n.panicIfNotOurs(args.Else, args.If, args.Cond, args.Block) + + return id.Wrap(n.Context(), id.ID[If](n.Context().ifs.NewCompressed(rawIf{ + elseT: args.Else.ID(), + ifT: args.If.ID(), + cond: args.Cond.ID(), + block: args.Block.ID(), + }))) +} + +// NewOp constructs a new [Op] in this context. +func (n *Nodes) NewOp(args OpArgs) Op { + n.panicIfNotOurs(args.Left, args.Right, args.Op) + + return id.Wrap(n.Context(), id.ID[Op](n.Context().ops.NewCompressed(rawOp{ + left: args.Left.ID(), + right: args.Right.ID(), + op: args.Op.ID(), + }))) +} + +// NewRecord constructs a new [Record] in this context. +func (n *Nodes) NewRecord(args RecordArgs) Record { + n.panicIfNotOurs(args.Entries) + + return id.WrapRaw(n.Context(), id.ID[Record](args.Entries.ID()), args.Entries.Raw()) +} + +// NewSwitch constructs a new [Switch] in this context. +func (n *Nodes) NewSwitch(args SwitchArgs) Switch { + n.panicIfNotOurs(args.Switch, args.Arg, args.Braces) + + return id.Wrap(n.Context(), id.ID[Switch](n.Context().switches.NewCompressed(rawSwitch{ + kw: args.Switch.ID(), + arg: args.Arg.ID(), + braces: args.Braces.ID(), + }))) +} + +type arenas struct { + blocks arena.Arena[rawBlock] + calls arena.Arena[rawCall] + cases arena.Arena[rawCase] + controls arena.Arena[rawControl] + fors arena.Arena[rawFor] + funcs arena.Arena[rawFunc] + ifs arena.Arena[rawIf] + ops arena.Arena[rawOp] + params arena.Arena[rawParams] + switches arena.Arena[rawSwitch] +} + +// FromID implements [id.Context]. +func (c *Context) FromID(id uint64, want any) any { + switch want.(type) { + case **rawBlock: + return c.blocks.Deref(arena.Pointer[rawBlock](id)) + case **rawCall: + return c.calls.Deref(arena.Pointer[rawCall](id)) + case **rawCase: + return c.cases.Deref(arena.Pointer[rawCase](id)) + case **rawControl: + return c.controls.Deref(arena.Pointer[rawControl](id)) + case **rawFor: + return c.fors.Deref(arena.Pointer[rawFor](id)) + case **rawFunc: + return c.funcs.Deref(arena.Pointer[rawFunc](id)) + case **rawIf: + return c.ifs.Deref(arena.Pointer[rawIf](id)) + case **rawOp: + return c.ops.Deref(arena.Pointer[rawOp](id)) + case **rawParams: + return c.params.Deref(arena.Pointer[rawParams](id)) + case **rawSwitch: + return c.switches.Deref(arena.Pointer[rawSwitch](id)) + + default: + return c.stream.FromID(id, want) + } +} + +// panicIfNotOurs checks that a contextual value is owned by this context, and panics if not. +// +// Does not panic if that is zero or has a zero context. Panics if n is zero. +func (n *Nodes) panicIfNotOurs(that ...any) { + for _, that := range that { + if that == nil { + continue + } + + var path string + switch that := that.(type) { + case interface{ Context() *token.Stream }: + ctx := that.Context() + if ctx == nil || ctx == n.Context().Stream() { + continue + } + path = ctx.Path() + + case interface{ Context() *Context }: + ctx := that.Context() + if ctx == nil || ctx == n.Context() { + continue + } + path = ctx.Stream().Path() + + default: + continue + } + + panic(fmt.Sprintf( + "protocompile/expr: attempt to mix different contexts: %q vs %q", + n.stream.Path(), + path, + )) + } +} diff --git a/experimental/expr/control.go b/experimental/expr/control.go new file mode 100644 index 00000000..fc7b4936 --- /dev/null +++ b/experimental/expr/control.go @@ -0,0 +1,100 @@ +// Copyright 2020-2025 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 expr + +import ( + "github.com/bufbuild/protocompile/experimental/id" + "github.com/bufbuild/protocompile/experimental/source" + "github.com/bufbuild/protocompile/experimental/token" + "github.com/bufbuild/protocompile/experimental/token/keyword" +) + +// Control is a control flow expression, such as a break or continue, with an +// optional condition at the end. +// +// # Grammar +// +// Switch := (`break` | `continue` | `return`) Params (`if` Expr)? +type Control id.Node[Control, *Context, *rawControl] + +// ControlArgs is arguments for [Nodes.NewControl]. +type ControlArgs struct { + Keyword token.Token + Args Params + + If token.Token + Condition Expr +} + +type rawControl struct { + kw, ifT token.ID + args id.ID[Params] + cond id.Dyn[Expr, Kind] +} + +// AsAny type-erases this type value. +// +// See [Expr] for more information. +func (e Control) AsAny() Expr { + if e.IsZero() { + return Expr{} + } + return id.WrapDyn(e.Context(), id.NewDyn(KindControl, id.ID[Expr](e.ID()))) +} + +// Kind returns what kind of control flow expression this is. +func (e Control) Kind() keyword.Keyword { + return e.KeywordToken().Keyword() +} + +// KeywordToken returns the token for the keyword for this expression. +func (e Control) KeywordToken() token.Token { + if e.IsZero() { + return token.Zero + } + return id.Wrap(e.Context().Stream(), e.Raw().kw) +} + +// IfToken returns the token for an `if` after this expression, if it has an +// associated condition. +func (e Control) IfToken() token.Token { + if e.IsZero() { + return token.Zero + } + return id.Wrap(e.Context().Stream(), e.Raw().ifT) +} + +// Args returns the arguments for this expression. +func (e Control) Args() Params { + if e.IsZero() { + return Params{} + } + return id.Wrap(e.Context(), e.Raw().args) +} + +// Condition returns the condition expression for this expression. +// +// This will be zero if the expression is not conditioned. +func (e Control) Condition() Expr { + if e.IsZero() { + return Expr{} + } + return id.WrapDyn(e.Context(), e.Raw().cond) +} + +// Span implements [source.Spanner]. +func (e Control) Span() source.Span { + return source.Join(e.KeywordToken(), e.Args(), e.IfToken(), e.Condition()) +} diff --git a/experimental/expr/doc.go b/experimental/expr/doc.go new file mode 100644 index 00000000..1ec1531a --- /dev/null +++ b/experimental/expr/doc.go @@ -0,0 +1,53 @@ +// Copyright 2020-2025 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 expr provides an abstract syntax tree implementation for an +// expression language shared by various parts of the compiler. +// +// # Node Types +// +// Most types in this package represent AST nodes of some kind. Each such type +// contains a grammar for the lenient extension of the Protobuf grammar that it +// represents. The grammar is notated using regular expression syntax, with the +// following modifications: +// 1. Whitespace is ignored. +// 2. Literal strings (except in character classes) are represented with Go +// strings. +// 3. Unquoted names refer to other productions. +// +// Productions that represent a type from this package, a [token.Kind], or +// a production used by a different type, are in PascalCase; all other +// productions are in camelCase and are scoped to that type. +// +// These grammars are provided for illustration only: the complete grammar is +// ambiguous in the absence of greediness decisions, which, except where +// otherwise noted, neither affect the parsing of correct Protobuf files, nor +// are they specified and are subject to change. +// +// # Pointer-like Types +// +// Virtually all AST nodes in this library are "pointer-like" types, in that +// although they are not Go pointers, they do refer to something stored in a +// [Context] somewhere. Internally, they contain a pointer to a [Context] and +// a pointer to the compressed node representation inside the [Context]. This is +// done so that we can avoid spending an extra eight or twelve bytes per +// node-at-rest, and to minimize GC churn by avoiding pointer cycles in the +// in-memory representation of the AST. +// +// All pointer-like types have a bool-returning IsZero method, which checks for +// the zero value. Pointer-like types should generally be passed by value, not +// by pointer; all of them have value receivers. +package expr + +//go:generate go run github.com/bufbuild/protocompile/internal/enum kind.yaml diff --git a/experimental/expr/expr.go b/experimental/expr/expr.go new file mode 100644 index 00000000..80c993e2 --- /dev/null +++ b/experimental/expr/expr.go @@ -0,0 +1,172 @@ +// Copyright 2020-2025 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 expr + +import ( + "github.com/bufbuild/protocompile/experimental/id" + "github.com/bufbuild/protocompile/experimental/source" + "github.com/bufbuild/protocompile/experimental/token" +) + +// Expr is any expression type in this package. +// +// Values of this type can be obtained by calling an AsAny method on a expression +// type, such as [Token.AsAny]. It can be type-asserted back to any of +// the concrete expression types using its own As* methods. +// +// This type is used in lieu of a putative Expr interface type to avoid heap +// allocations in functions that would return one of many different expression +// types. +// +// Note that the expression and type grammars for the language are the same, so +// Expr appears in places you might expect to see a "Type" node. +type Expr id.DynNode[Expr, Kind, *Context] + +// AsBlock converts an Expr into a [Block], if that is its concrete type. +// +// Otherwise, returns zero. +func (e Expr) AsBlock() Block { + if e.Kind() != KindBlock { + return Block{} + } + + return id.Wrap(e.Context(), id.ID[Block](e.ID().Value())) +} + +// AsFor converts an Expr into a [Call], if that is its concrete type. +// +// Otherwise, returns zero. +func (e Expr) AsCall() Call { + if e.Kind() != KindCall { + return Call{} + } + + return id.Wrap(e.Context(), id.ID[Call](e.ID().Value())) +} + +// AsControl converts an Expr into a [Control], if that is its concrete type. +// +// Otherwise, returns zero. +func (e Expr) AsControl() Control { + if e.Kind() != KindControl { + return Control{} + } + + return id.Wrap(e.Context(), id.ID[Control](e.ID().Value())) +} + +// AsFor converts an Expr into a [For], if that is its concrete type. +// +// Otherwise, returns zero. +func (e Expr) AsFor() For { + if e.Kind() != KindFor { + return For{} + } + + return id.Wrap(e.Context(), id.ID[For](e.ID().Value())) +} + +// AsFunc converts an Expr into a [Func], if that is its concrete type. +// +// Otherwise, returns zero. +func (e Expr) AsFunc() Func { + if e.Kind() != KindFunc { + return Func{} + } + + return id.Wrap(e.Context(), id.ID[Func](e.ID().Value())) +} + +// AsIf converts an Expr into a [If], if that is its concrete type. +// +// Otherwise, returns zero. +func (e Expr) AsIf() If { + if e.Kind() != KindIf { + return If{} + } + + return id.Wrap(e.Context(), id.ID[If](e.ID().Value())) +} + +// AsOp converts an Expr into a [Op], if that is its concrete type. +// +// Otherwise, returns zero. +func (e Expr) AsOp() Op { + if e.Kind() != KindOp { + return Op{} + } + + return id.Wrap(e.Context(), id.ID[Op](e.ID().Value())) +} + +// AsRecord converts an Expr into a [Record], if that is its concrete type. +// +// Otherwise, returns zero. +func (e Expr) AsRecord() Record { + if e.Kind() != KindRecord { + return Record{} + } + + return id.Wrap(e.Context(), id.ID[Record](e.ID().Value())) +} + +// AsSwitch converts an Expr into a [Switch], if that is its concrete type. +// +// Otherwise, returns zero. +func (e Expr) AsSwitch() Switch { + if e.Kind() != KindSwitch { + return Switch{} + } + + return id.Wrap(e.Context(), id.ID[Switch](e.ID().Value())) +} + +// AsToken converts a Any into a ExprLiteral, if that is the type +// it contains. +// +// Otherwise, returns zero. +func (e Expr) AsToken() Token { + if e.Kind() != KindToken { + return Token{} + } + return Token{ + ExprContext: e.Context(), + Token: id.Wrap(e.Context().Stream(), id.ID[token.Token](e.ID().Value())), + } +} + +// Span implements [source.Spanner]. +func (e Expr) Span() source.Span { + return source.Join( + e.AsBlock(), + e.AsCall(), + e.AsControl(), + e.AsFor(), + e.AsFunc(), + e.AsIf(), + e.AsOp(), + e.AsRecord(), + e.AsSwitch(), + e.AsToken(), + ) +} + +func (Kind) DecodeDynID(lo, _ int32) Kind { + return Kind(lo) +} + +func (k Kind) EncodeDynID(value int32) (int32, int32, bool) { + return int32(k), value, true +} diff --git a/experimental/expr/for.go b/experimental/expr/for.go new file mode 100644 index 00000000..6e9d74f7 --- /dev/null +++ b/experimental/expr/for.go @@ -0,0 +1,101 @@ +// Copyright 2020-2025 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 expr + +import ( + "github.com/bufbuild/protocompile/experimental/id" + "github.com/bufbuild/protocompile/experimental/source" + "github.com/bufbuild/protocompile/experimental/token" +) + +// For is a loop which assigns items out of an iterator to variables in +// a [Params]. +// +// # Grammar +// +// For := `for` Params `in` Expr Block +type For id.Node[For, *Context, *rawFor] + +// ForArgs is arguments for [Nodes.NewFor]. +type ForArgs struct { + For token.Token + Vars Params + In token.Token + Iterator Expr + Block Block +} + +type rawFor struct { + iter id.Dyn[Expr, Kind] + forT token.ID + inT token.ID + vars id.ID[Params] + block id.ID[Block] +} + +// AsAny type-erases this type value. +// +// See [Expr] for more information. +func (e For) AsAny() Expr { + if e.IsZero() { + return Expr{} + } + return id.WrapDyn(e.Context(), id.NewDyn(KindFor, id.ID[Expr](e.ID()))) +} + +// Keywords returns this expression's for token. +func (e For) ForToken() token.Token { + if e.IsZero() { + return token.Zero + } + return id.Wrap(e.Context().Stream(), e.Raw().forT) +} + +// Keywords returns this expression's in token. +func (e For) InToken() token.Token { + if e.IsZero() { + return token.Zero + } + return id.Wrap(e.Context().Stream(), e.Raw().inT) +} + +// Vars returns the variables this for assigns to. +func (e For) Vars() Params { + if e.IsZero() { + return Params{} + } + return id.Wrap(e.Context(), e.Raw().vars) +} + +// Iterator returns the expression this loop iterates over. +func (e For) Iterator() Expr { + if e.IsZero() { + return Expr{} + } + return id.WrapDyn(e.Context(), e.Raw().iter) +} + +// Block returns this for's block. +func (e For) Block() Block { + if e.IsZero() { + return Block{} + } + return id.Wrap(e.Context(), e.Raw().block) +} + +// Span implements [source.Spanner]. +func (e For) Span() source.Span { + return source.Join(e.ForToken(), e.Vars(), e.InToken(), e.Iterator(), e.Block()) +} diff --git a/experimental/expr/func.go b/experimental/expr/func.go new file mode 100644 index 00000000..fa60b4c0 --- /dev/null +++ b/experimental/expr/func.go @@ -0,0 +1,111 @@ +// Copyright 2020-2025 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 expr + +import ( + "github.com/bufbuild/protocompile/experimental/id" + "github.com/bufbuild/protocompile/experimental/source" + "github.com/bufbuild/protocompile/experimental/token" +) + +// Func is a function literal or definition. A function definition is simply +// a function that includes a name after the func keyword. +// +// # Grammar +// +// Func := `func` Ident? `(` Params `)` (`->` Expr Block? | Expr)? +type Func id.Node[Func, *Context, *rawFunc] + +// FuncArgs is arguments for [Nodes.NewFunc]. +type FuncArgs struct { + Func token.Token + Name token.Token + Params Params + Arrow token.Token + Return Expr + Body Expr +} + +type rawFunc struct { + funcT token.ID + name token.ID + params id.ID[Params] + arrow token.ID + ret id.Dyn[Expr, Kind] + body id.Dyn[Expr, Kind] +} + +// AsAny type-erases this type value. +// +// See [Expr] for more information. +func (e Func) AsAny() Expr { + if e.IsZero() { + return Expr{} + } + return id.WrapDyn(e.Context(), id.NewDyn(KindFunc, id.ID[Expr](e.ID()))) +} + +// Keywords returns this expression's func token. +func (e Func) FuncToken() token.Token { + if e.IsZero() { + return token.Zero + } + return id.Wrap(e.Context().Stream(), e.Raw().funcT) +} + +// Name returns the declared function's name (if it is a declaration). +func (e Func) Name() token.Token { + if e.IsZero() { + return token.Zero + } + return id.Wrap(e.Context().Stream(), e.Raw().name) +} + +// Params returns the function's input parameters. +func (e Func) Params() Params { + if e.IsZero() { + return Params{} + } + return id.Wrap(e.Context(), e.Raw().params) +} + +// Arrow returns the arrow that indicates a return type. +func (e Func) Arrow() token.Token { + if e.IsZero() { + return token.Zero + } + return id.Wrap(e.Context().Stream(), e.Raw().arrow) +} + +// Return returns the type expression for this function's return type. +func (e Func) Return() Expr { + if e.IsZero() { + return Expr{} + } + return id.WrapDyn(e.Context(), e.Raw().ret) +} + +// Body returns this function's body expression. +func (e Func) Body() Expr { + if e.IsZero() { + return Expr{} + } + return id.WrapDyn(e.Context(), e.Raw().body) +} + +// Span implements [source.Spanner]. +func (e Func) Span() source.Span { + return source.Join(e.FuncToken(), e.Name(), e.Params(), e.Return(), e.Body()) +} diff --git a/experimental/expr/if.go b/experimental/expr/if.go new file mode 100644 index 00000000..c7efc2a3 --- /dev/null +++ b/experimental/expr/if.go @@ -0,0 +1,110 @@ +// Copyright 2020-2025 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 expr + +import ( + "github.com/bufbuild/protocompile/experimental/id" + "github.com/bufbuild/protocompile/experimental/source" + "github.com/bufbuild/protocompile/experimental/token" +) + +// If is a chain of if conditions. +// +// Each If represents one of the blocks in the chain, which may be either an +// if, an else if, or an else. Only ifs and else ifs have conditions. +// +// # Grammar +// +// If := `if` Expr Block (`else` (If | Block))? +type If id.Node[If, *Context, *rawIf] + +// IfArgs is arguments for [Nodes.NewIf]. +type IfArgs struct { + Else, If token.Token + Cond Expr + Block Block +} + +type rawIf struct { + elseT, ifT token.ID + cond id.Dyn[Expr, Kind] + block id.ID[Block] + else_ id.ID[If] //nolint:revive +} + +// AsAny type-erases this type value. +// +// See [Expr] for more information. +func (e If) AsAny() Expr { + if e.IsZero() { + return Expr{} + } + return id.WrapDyn(e.Context(), id.NewDyn(KindIf, id.ID[Expr](e.ID()))) +} + +// IfToken returns this expression's if token, if it has one. +func (e If) IfToken() token.Token { + if e.IsZero() { + return token.Zero + } + return id.Wrap(e.Context().Stream(), e.Raw().ifT) +} + +// ElseToken returns this expression's else token, if it has one. +func (e If) ElseToken() token.Token { + if e.IsZero() { + return token.Zero + } + return id.Wrap(e.Context().Stream(), e.Raw().elseT) +} + +// Condition returns the condition expression for this if. +// +// This will be zero if it is a final else block. +func (e If) Condition() Expr { + if e.IsZero() { + return Expr{} + } + return id.WrapDyn(e.Context(), e.Raw().cond) +} + +// Block returns this if's block. +func (e If) Block() Block { + if e.IsZero() { + return Block{} + } + return id.Wrap(e.Context(), e.Raw().block) +} + +// Else returns this if's else block, if it has one. +func (e If) Else() If { + if e.IsZero() { + return If{} + } + return id.Wrap(e.Context(), e.Raw().else_) +} + +// SetElse sets this if's else block. +func (e If) SetElse(then If) { + if !e.IsZero() { + e.Context().Nodes().panicIfNotOurs(then) + e.Raw().else_ = e.ID() + } +} + +// Span implements [source.Spanner]. +func (e If) Span() source.Span { + return source.Join(e.ElseToken(), e.IfToken(), e.Condition(), e.Block(), e.Else()) +} diff --git a/experimental/expr/kind.go b/experimental/expr/kind.go new file mode 100644 index 00000000..105ae309 --- /dev/null +++ b/experimental/expr/kind.go @@ -0,0 +1,85 @@ +// Copyright 2020-2025 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 kind.yaml. DO NOT EDIT. + +package expr + +import ( + "fmt" + "iter" +) + +// Kind is a kind of expression. There is one value of Kind for each +// expression type in this package. +type Kind int8 + +const ( + KindInvalid Kind = iota + KindToken + KindRecord + KindOp + KindCall + KindBlock + KindIf + KindFor + KindSwitch + KindControl + KindFunc +) + +// 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[v] +} + +// GoString implements [fmt.GoStringer]. +func (v Kind) GoString() string { + if int(v) < 0 || int(v) > len(_table_Kind_GoString) { + return fmt.Sprintf("expr.Kind(%v)", int(v)) + } + return _table_Kind_GoString[v] +} + +var _table_Kind_String = [...]string{ + KindInvalid: "KindInvalid", + KindToken: "KindToken", + KindRecord: "KindRecord", + KindOp: "KindOp", + KindCall: "KindCall", + KindBlock: "KindBlock", + KindIf: "KindIf", + KindFor: "KindFor", + KindSwitch: "KindSwitch", + KindControl: "KindControl", + KindFunc: "KindFunc", +} + +var _table_Kind_GoString = [...]string{ + KindInvalid: "KindInvalid", + KindToken: "KindToken", + KindRecord: "KindRecord", + KindOp: "KindOp", + KindCall: "KindCall", + KindBlock: "KindBlock", + KindIf: "KindIf", + KindFor: "KindFor", + KindSwitch: "KindSwitch", + KindControl: "KindControl", + KindFunc: "KindFunc", +} +var _ iter.Seq[int] // Mark iter as used. diff --git a/experimental/expr/kind.yaml b/experimental/expr/kind.yaml new file mode 100644 index 00000000..f83180dc --- /dev/null +++ b/experimental/expr/kind.yaml @@ -0,0 +1,34 @@ +# 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: int8 + docs: | + Kind is a kind of expression. There is one value of Kind for each + expression type in this package. + methods: + - kind: string + - kind: go-string + values: + - name: KindInvalid + - name: KindToken + - name: KindRecord + - name: KindOp + - name: KindCall + - name: KindBlock + - name: KindIf + - name: KindFor + - name: KindSwitch + - name: KindControl + - name: KindFunc \ No newline at end of file diff --git a/experimental/expr/op.go b/experimental/expr/op.go new file mode 100644 index 00000000..6bfaf778 --- /dev/null +++ b/experimental/expr/op.go @@ -0,0 +1,111 @@ +// Copyright 2020-2025 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 expr + +import ( + "github.com/bufbuild/protocompile/experimental/id" + "github.com/bufbuild/protocompile/experimental/source" + "github.com/bufbuild/protocompile/experimental/token" + "github.com/bufbuild/protocompile/experimental/token/keyword" +) + +// Op is an operator expression, consisting of one or two expressions and +// an operator. This subsumes all of the operator expressions in the grammar, +// including property, assignment, and range expressions. +// +// Unary do not have a left-hand side. +// +// # Grammar +// +// ExprToken := Expr Op Expr +// +// Here, Op is any of the following tokens, ordered from least to greatest +// precedence (later tokens bind more tightly): +// +// = := += -= *= /= %= +// , +// or +// and +// == != < > <= >= +// .. ..= to +// + - +// * / % +// - not (unary) +// . +// +// Note that ExprCall has higher precedence than ., so -x.f() will group as +// -((x.f)()). +type Op id.Node[Op, *Context, *rawOp] + +// OpArgs is arguments for [Nodes.NewOp]. +type OpArgs struct { + Left, Right Expr + Op token.Token +} + +type rawOp struct { + left, right id.Dyn[Expr, Kind] + op token.ID +} + +// AsAny type-erases this type value. +// +// See [Expr] for more information. +func (e Op) AsAny() Expr { + if e.IsZero() { + return Expr{} + } + return id.WrapDyn(e.Context(), id.NewDyn(KindOp, id.ID[Expr](e.ID()))) +} + +// Left returns the left-hand side of this expression. Unary operators do +// not have a left-hand side. +func (e Op) Left() Expr { + if e.IsZero() { + return Expr{} + } + return id.WrapDyn(e.Context(), e.Raw().left) +} + +// Right returns the right-hand side of this expression. +func (e Op) Right() Expr { + if e.IsZero() { + return Expr{} + } + return id.WrapDyn(e.Context(), e.Raw().right) +} + +// IsUnary returns whether this is an unary operation. +func (e Op) IsUnary() bool { + return e.Left().IsZero() && !e.Right().IsZero() +} + +// Operator returns this expression's operator. +func (e Op) Operator() keyword.Keyword { + return e.OperatorToken().Keyword() +} + +// OperatorToken returns the token for this expression's operator. +func (e Op) OperatorToken() token.Token { + if e.IsZero() { + return token.Zero + } + return id.Wrap(e.Context().Stream(), e.Raw().op) +} + +// Span implements [source.Spanner]. +func (e Op) Span() source.Span { + return source.Join(e.Left(), e.OperatorToken(), e.Right()) +} diff --git a/experimental/expr/params.go b/experimental/expr/params.go new file mode 100644 index 00000000..ed4c461a --- /dev/null +++ b/experimental/expr/params.go @@ -0,0 +1,175 @@ +// Copyright 2020-2025 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 expr + +import ( + "slices" + + "github.com/bufbuild/protocompile/experimental/id" + "github.com/bufbuild/protocompile/experimental/source" + "github.com/bufbuild/protocompile/experimental/token" +) + +// Params is a parameter list, which is used for all expression lists in the +// grammar. +// +// # Grammar +// +// Params := (Param `,`)* Param? +// Param := Expr (`:` Expr)? (`if` Expr) +type Params id.Node[Params, *Context, *rawParams] + +// Param is a parameter in a [Params]. +type Param struct { + // The name for this parameter. In a map context, this would be the key; + // in a function parameter context, this would be the argument name. + Name Expr + + Colon token.Token + // The expression for this parameter. In a map context, this would be the value; + // in a function parameter context, this would be the argument type. + Expr Expr + + If token.Token + // A condition for the parameter; not allowed in all productions. + Cond Expr +} + +// ParamsArgs is arguments for [Nodes.NewParams]. +type ParamsArgs struct { + Brackets token.Token +} + +type rawParams struct { + params []rawParam + brackets token.ID +} + +type rawParam struct { + name, ty, cond id.ID[Expr] + nameKind, tyKind, condKind Kind + colon, if_, comma token.ID //nolint:revive +} + +// Brackets returns the token tree for the brackets wrapping the argument list. +// +// May be zero, if the user forgot to include brackets. +func (p Params) Brackets() token.Token { + if p.IsZero() { + return token.Zero + } + + return id.Wrap(p.Context().Stream(), p.Raw().brackets) +} + +// SetBrackets sets the token tree for the brackets wrapping the argument list. +func (p Params) SetBrackets(brackets token.Token) { + p.Context().Nodes().panicIfNotOurs(brackets) + p.Raw().brackets = brackets.ID() +} + +// Len implements [seq.Indexer]. +func (p Params) Len() int { + if p.IsZero() { + return 0 + } + + return len(p.Raw().params) +} + +// At implements [seq.Indexer]. +func (p Params) At(n int) Param { + v := p.Raw().params[n] + return Param{ + Name: id.WrapDyn(p.Context(), id.NewDyn(v.nameKind, v.name)), + Colon: id.Wrap(p.Context().Stream(), v.colon), + Expr: id.WrapDyn(p.Context(), id.NewDyn(v.tyKind, v.ty)), + If: id.Wrap(p.Context().Stream(), v.if_), + Cond: id.WrapDyn(p.Context(), id.NewDyn(v.condKind, v.cond)), + } +} + +// SetAt implements [seq.Setter]. +func (p Params) SetAt(n int, v Param) { + p.Context().Nodes().panicIfNotOurs(v.Name, v.Colon, v.Expr, v.If, v.Cond) + + r := &p.Raw().params[n] + + r.name = v.Name.ID().Value() + r.nameKind = v.Name.Kind() + r.ty = v.Expr.ID().Value() + r.tyKind = v.Expr.Kind() + r.cond = v.Cond.ID().Value() + r.condKind = v.Cond.Kind() + + r.colon = v.Colon.ID() + r.if_ = v.If.ID() +} + +// Insert implements [seq.Inserter]. +func (p Params) Insert(n int, v Param) { + p.InsertComma(n, v, token.Zero) +} + +// Delete implements [seq.Inserter]. +func (p Params) Delete(n int) { + p.Raw().params = slices.Delete(p.Raw().params, n, n+1) +} + +// Comma implements [Commas]. +func (p Params) Comma(n int) token.Token { + return id.Wrap(p.Context().Stream(), p.Raw().params[n].comma) +} + +// AppendComma implements [Commas]. +func (p Params) AppendComma(v Param, comma token.Token) { + p.InsertComma(p.Len(), v, comma) +} + +// InsertComma implements [Commas]. +func (p Params) InsertComma(n int, v Param, comma token.Token) { + p.Context().Nodes().panicIfNotOurs(v.Name, v.Colon, v.Expr, comma) + + p.Raw().params = slices.Insert(p.Raw().params, n, rawParam{ + name: v.Name.ID().Value(), + nameKind: v.Name.Kind(), + ty: v.Expr.ID().Value(), + tyKind: v.Expr.Kind(), + cond: v.Cond.ID().Value(), + condKind: v.Cond.Kind(), + colon: v.Colon.ID(), + if_: v.If.ID(), + comma: comma.ID(), + }) +} + +// Span implements [source.Spanner]. +func (p Param) Span() source.Span { + return source.Join(p.Name, p.Colon, p.Expr) +} + +// Span implements [source.Spanner]. +func (p Params) Span() source.Span { + switch { + case p.IsZero(): + return source.Span{} + case !p.Brackets().IsZero(): + return p.Brackets().Span() + case p.Len() == 0: + return source.Span{} + default: + return source.Join(p.At(0), p.At(p.Len()-1)) + } +} diff --git a/experimental/expr/record.go b/experimental/expr/record.go new file mode 100644 index 00000000..32de7fd2 --- /dev/null +++ b/experimental/expr/record.go @@ -0,0 +1,61 @@ +// Copyright 2020-2025 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 expr + +import ( + "github.com/bufbuild/protocompile/experimental/id" + "github.com/bufbuild/protocompile/experimental/source" + "github.com/bufbuild/protocompile/experimental/token" +) + +// Record is a bracketed sequence of expressions, represented as a [Params]. +// +// # Grammar +// +// Record := `(` Params `)` | `[` Params `]` | `{` Params `}` +type Record id.Node[Record, *Context, *rawParams] + +// RecordArgs is arguments for [Nodes.NewRecord]. +type RecordArgs struct { + Entries Params +} + +// AsAny type-erases this type value. +// +// See [Expr] for more information. +func (e Record) AsAny() Expr { + if e.IsZero() { + return Expr{} + } + return id.WrapDyn(e.Context(), id.NewDyn(KindRecord, id.ID[Expr](e.ID()))) +} + +// Entries returns the expression's entries. +func (e Record) Entries() Params { + if e.IsZero() { + return Params{} + } + return id.WrapRaw(e.Context(), id.ID[Params](e.ID()), e.Raw()) +} + +// Brackets returns the brackets for this expression. +func (e Record) Brackets() token.Token { + return e.Entries().Brackets() +} + +// Span implements [source.Spanner]. +func (e Record) Span() source.Span { + return e.Entries().Span() +} diff --git a/experimental/expr/switch.go b/experimental/expr/switch.go new file mode 100644 index 00000000..f066a0dd --- /dev/null +++ b/experimental/expr/switch.go @@ -0,0 +1,155 @@ +// Copyright 2020-2025 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 expr + +import ( + "github.com/bufbuild/protocompile/experimental/id" + "github.com/bufbuild/protocompile/experimental/seq" + "github.com/bufbuild/protocompile/experimental/source" + "github.com/bufbuild/protocompile/experimental/token" + "github.com/bufbuild/protocompile/experimental/token/keyword" +) + +// Switch selects one of several blocks to execute based on a number of cases. +// +// # Grammar +// +// Switch := `switch` Expr? `{` Case* `}` +type Switch id.Node[Switch, *Context, *rawSwitch] + +// Case is a case block within a [Switch]. +// +// # Grammar +// +// Case := (`case` (Expr `,`)* Expr? | `else`) `:` +type Case id.Node[Case, *Context, *rawCase] + +// SwitchArgs is arguments for [Nodes.NewSwitch]. +type SwitchArgs struct { + Switch token.Token + Arg Expr + Braces token.Token +} + +// CaseArgs is arguments for [Nodes.NewCase]. +type CaseArgs struct { + Keyword token.Token + Alts Params + Colon token.Token + Block Block +} + +type rawSwitch struct { + cases id.Seq[Case, *Context, *rawCase] + arg id.Dyn[Expr, Kind] + kw token.ID + braces token.ID +} + +type rawCase struct { + kw, colon token.ID + alts id.ID[Params] + block id.ID[Block] +} + +// AsAny type-erases this type value. +// +// See [Expr] for more information. +func (e Switch) AsAny() Expr { + if e.IsZero() { + return Expr{} + } + return id.WrapDyn(e.Context(), id.NewDyn(KindSwitch, id.ID[Expr](e.ID()))) +} + +// SwitchToken returns this expression's switch token. +func (e Switch) SwitchToken() token.Token { + if e.IsZero() { + return token.Zero + } + return id.Wrap(e.Context().Stream(), e.Raw().kw) +} + +// Arg returns the optional argument to the switch. +func (e Switch) Arg() Expr { + if e.IsZero() { + return Expr{} + } + return id.WrapDyn(e.Context(), e.Raw().arg) +} + +// Braces returns the braces for the block within the switch. +func (e Switch) Braces() token.Token { + if e.IsZero() { + return token.Zero + } + return id.Wrap(e.Context().Stream(), e.Raw().braces) +} + +// Cases returns the cases contained within the switch. +func (e Switch) Cases() seq.Inserter[Case] { + var cases *id.Seq[Case, *Context, *rawCase] + if !e.IsZero() { + cases = &e.Raw().cases + } + return cases.Inserter(e.Context()) +} + +// Span implements [source.Spanner]. +func (e Switch) Span() source.Span { + return source.Join(e.SwitchToken(), e.Arg(), e.Braces()) +} + +// Kind returns which kind of case this is. +func (e Case) Kind() keyword.Keyword { + return e.KeywordToken().Keyword() +} + +// KeywordToken returns this switch's "case" or "else" keyword. +func (e Case) KeywordToken() token.Token { + if e.IsZero() { + return token.Zero + } + return id.Wrap(e.Context().Stream(), e.Raw().kw) +} + +// Colon returns the colon at the end of the alternatives. +func (e Case) Colon() token.Token { + if e.IsZero() { + return token.Zero + } + return id.Wrap(e.Context().Stream(), e.Raw().colon) +} + +// Alts returns the potential alternatives for this case. +func (e Case) Alts() Params { + if e.IsZero() { + return Params{} + } + return id.Wrap(e.Context(), e.Raw().alts) +} + +// Block returns the block of expressions for this case. +func (e Case) Block() Block { + if e.IsZero() { + return Block{} + } + return id.Wrap(e.Context(), e.Raw().block) +} + +// Span implements [source.Spanner]. +func (e Case) Span() source.Span { + return source.Join(e.KeywordToken(), e.Alts(), e.Colon(), e.Block()) +} diff --git a/experimental/expr/token.go b/experimental/expr/token.go new file mode 100644 index 00000000..b875c985 --- /dev/null +++ b/experimental/expr/token.go @@ -0,0 +1,55 @@ +// Copyright 2020-2025 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 expr + +import ( + "github.com/bufbuild/protocompile/experimental/id" + "github.com/bufbuild/protocompile/experimental/token" +) + +// Token is an expression corresponding to a single token: a string, a +// number, or an identifier. +// +// # Grammar +// +// Token := token.Number | token.String | token.Ident +type Token struct { + ExprContext *Context + // The token backing this expression. Must be [token.String], [token.Number], + // or [token.Ident]. + token.Token +} + +// Context returns this token's context. +// +// This returns a [Context] rather than a [token.Stream], which would otherwise +// be returned because Token embeds [token.Token]. +func (e Token) Context() *Context { + return e.ExprContext +} + +// AsAny type-erases this type value. +// +// See [Expr] for more information. +func (e Token) AsAny() Expr { + if e.IsZero() { + return Expr{} + } + + return id.WrapDyn( + e.Context(), + id.NewDyn(KindToken, id.ID[Expr](e.ID())), + ) +} diff --git a/experimental/id/seq.go b/experimental/id/seq.go new file mode 100644 index 00000000..1951bc8d --- /dev/null +++ b/experimental/id/seq.go @@ -0,0 +1,74 @@ +// Copyright 2020-2025 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 id + +import ( + "github.com/bufbuild/protocompile/experimental/seq" +) + +// Seq is an array of nodes which uses a compressed representation. +// +// A zero value is empty and ready to use. +type Seq[T ~Node[T, C, Raw], C Constraint, Raw any] struct { + ids []ID[T] +} + +// Inserter returns a [seq.Inserter] wrapping this [Seq]. +func (s *Seq[T, C, Raw]) Inserter(context C) seq.Inserter[T] { + var ids *[]ID[T] + if s != nil { + ids = &s.ids + } + + return seq.NewSliceInserter( + ids, + func(_ int, p ID[T]) T { + return Wrap(context, p) + }, + func(_ int, t T) ID[T] { + return Node[T, C, Raw](t).ID() + }, + ) +} + +// DynSeq is an array of dynamic nodes which uses a compressed representation. +// +// A zero value is empty and ready to use. +type DynSeq[T ~DynNode[T, K, C], K Kind[K], C Constraint] struct { + kinds []K + ids []ID[T] +} + +// Inserter returns a [seq.Inserter] wrapping this [DynSeq]. +func (s *DynSeq[T, K, C]) Inserter(context C) seq.Inserter[T] { + var kinds *[]K + var ids *[]ID[T] + if s != nil { + kinds = &s.kinds + ids = &s.ids + } + + return seq.NewSliceInserter2( + kinds, + ids, + func(_ int, k K, p ID[T]) T { + return WrapDyn(context, NewDyn(k, p)) + }, + func(_ int, t T) (K, ID[T]) { + id := DynNode[T, K, C](t).ID() + return id.Kind(), id.Value() + }, + ) +} diff --git a/experimental/internal/exprx/encode.go b/experimental/internal/exprx/encode.go new file mode 100644 index 00000000..12169171 --- /dev/null +++ b/experimental/internal/exprx/encode.go @@ -0,0 +1,385 @@ +// Copyright 2020-2025 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. + +//nolint:revive +package exprx + +import ( + "fmt" + + "github.com/bufbuild/protocompile/experimental/expr" + "github.com/bufbuild/protocompile/experimental/seq" + "github.com/bufbuild/protocompile/experimental/source" + "github.com/bufbuild/protocompile/experimental/token" + "github.com/bufbuild/protocompile/experimental/token/keyword" + exprpb "github.com/bufbuild/protocompile/internal/gen/buf/compiler/expr/v1alpha1" +) + +// ToProtoOptions contains options for the [File.ToProto] function. +type ToProtoOptions struct { + // If set, no spans will be serialized. + // + // This operation only destroys non-semantic information. + OmitSpans bool + + // If set, the contents of the file the AST was parsed from will not + // be serialized. + OmitFile bool +} + +// ToProto converts this AST into a Protobuf representation, which may be +// serialized. +// +// Note that package ast does not support deserialization from this proto; +// instead, you will need to re-parse the text file included in the message. +// This is because the AST is much richer than what is stored in this message; +// the message only provides enough information for further semantic analysis +// and diagnostic generation, but not for pretty-printing. +// +// Panics if the AST contains a cycle (e.g. a message that contains itself as +// a nested message). Parsed ASTs will never contain cycles, but users may +// modify them into a cyclic state. +func ToProto(e expr.Expr, options ToProtoOptions) *exprpb.Expr { + return (&protoEncoder{ToProtoOptions: options}).expr(e) +} + +// protoEncoder is the state needed for converting an AST node into a Protobuf message. +type protoEncoder struct { + ToProtoOptions + + stackMap map[source.Spanner]struct{} +} + +// checkCycle panics if v is visited cyclically. +// +// Should be called like this, so that on function exit the entry is popped: +// +// defer c.checkCycle(v)() +func (c *protoEncoder) checkCycle(v source.Spanner) func() { + if c.stackMap == nil { + c.stackMap = make(map[source.Spanner]struct{}) + } + + _, cycle := c.stackMap[v] + c.stackMap[v] = struct{}{} + + if cycle { + panic(fmt.Sprintf("protocompile/ast: called File.ToProto on a cyclic AST %v", v.Span())) + } + + return func() { delete(c.stackMap, v) } +} + +func (c *protoEncoder) span(s source.Spanner) *exprpb.Span { + if c.OmitSpans || s == nil { + return nil + } + + span := s.Span() + if span.IsZero() { + return nil + } + + return &exprpb.Span{ + Start: uint32(span.Start), + End: uint32(span.End), + } +} + +func (c *protoEncoder) expr(e expr.Expr) *exprpb.Expr { + if e.IsZero() { + return nil + } + + pb := new(exprpb.Expr) + defer c.checkCycle(e)() + switch e.Kind() { + case expr.KindBlock: + pb.Expr = &exprpb.Expr_Block{Block: c.block(e.AsBlock())} + case expr.KindCall: + pb.Expr = &exprpb.Expr_Call{Call: c.call(e.AsCall())} + case expr.KindControl: + pb.Expr = &exprpb.Expr_Control{Control: c.control(e.AsControl())} + case expr.KindFor: + pb.Expr = &exprpb.Expr_For{For: c.for_(e.AsFor())} + case expr.KindFunc: + pb.Expr = &exprpb.Expr_Func{Func: c.func_(e.AsFunc())} + case expr.KindIf: + pb.Expr = &exprpb.Expr_If{If: c.if_(e.AsIf())} + case expr.KindOp: + pb.Expr = &exprpb.Expr_Op{Op: c.op(e.AsOp())} + case expr.KindRecord: + pb.Expr = &exprpb.Expr_Record{Record: c.params(e.AsRecord().Entries())} + case expr.KindSwitch: + pb.Expr = &exprpb.Expr_Switch{Switch: c.switch_(e.AsSwitch())} + case expr.KindToken: + pb.Expr = &exprpb.Expr_Token{Token: c.token(e.AsToken())} + default: + panic("unexpected expr.Kind") + } + + return pb +} + +func (c *protoEncoder) block(e expr.Block) *exprpb.Block { + if e.IsZero() { + return nil + } + + pb := &exprpb.Block{ + Span: c.span(e), + } + for expr := range seq.Values(e.Exprs()) { + pb.Exprs = append(pb.Exprs, c.expr(expr)) + } + return pb +} + +func (c *protoEncoder) call(e expr.Call) *exprpb.Call { + if e.IsZero() { + return nil + } + + return &exprpb.Call{ + Callee: c.expr(e.Callee()), + Params: c.params(e.Args()), + Span: c.span(e), + } +} + +func (c *protoEncoder) control(e expr.Control) *exprpb.Control { + if e.IsZero() { + return nil + } + + var kind exprpb.Control_Kind + switch e.Kind() { + case keyword.Return: + kind = exprpb.Control_RETURN + case keyword.Break: + kind = exprpb.Control_BREAK + case keyword.Continue: + kind = exprpb.Control_CONTINUE + } + + return &exprpb.Control{ + Kind: kind, + Args: c.params(e.Args()), + Cond: c.expr(e.Condition()), + Span: c.span(e), + KwSpan: c.span(e.KeywordToken()), + IfSpan: c.span(e.IfToken()), + } +} + +func (c *protoEncoder) for_(e expr.For) *exprpb.For { + if e.IsZero() { + return nil + } + + return &exprpb.For{ + Vars: c.params(e.Vars()), + Iter: c.expr(e.Iterator()), + Block: c.block(e.Block()), + Span: c.span(e), + ForSpan: c.span(e.ForToken()), + InSpan: c.span(e.InToken()), + } +} + +func (c *protoEncoder) func_(e expr.Func) *exprpb.Func { + if e.IsZero() { + return nil + } + + return &exprpb.Func{ + Name: e.Name().Name(), + Params: c.params(e.Params()), + Return: c.expr(e.Return()), + Body: c.expr(e.Body()), + Span: c.span(e), + FuncSpan: c.span(e.FuncToken()), + ArrowSpan: c.span(e.Arrow()), + } +} + +func (c *protoEncoder) if_(e expr.If) *exprpb.If { + if e.IsZero() { + return nil + } + defer c.checkCycle(e)() + + return &exprpb.If{ + Cond: c.expr(e.Condition()), + Block: c.block(e.Block()), + Else: c.if_(e.Else()), + Span: c.span(e), + ElseSpan: c.span(e.ElseToken()), + IfSpan: c.span(e.IfToken()), + } +} + +func (c *protoEncoder) op(e expr.Op) *exprpb.Op { + if e.IsZero() { + return nil + } + + var op exprpb.Op_Kind + switch e.Operator() { + case keyword.Eq: + op = exprpb.Op_ASSIGN + case keyword.ColonEq: + op = exprpb.Op_ASSIGN_NEW + case keyword.PlusEq: + op = exprpb.Op_ASSIGN_ADD + case keyword.MinusEq: + op = exprpb.Op_ASSIGN_SUB + case keyword.StarEq: + op = exprpb.Op_ASSIGN_MUL + case keyword.SlashEq: + op = exprpb.Op_ASSIGN_DIV + case keyword.PercentEq: + op = exprpb.Op_ASSIGN_REM + + case keyword.Comma: + op = exprpb.Op_COMMA + case keyword.Or: + op = exprpb.Op_OR + case keyword.And: + op = exprpb.Op_AND + case keyword.Not: + op = exprpb.Op_NOT + + case keyword.EqEq: + op = exprpb.Op_EQ + case keyword.BangEq: + op = exprpb.Op_NE + case keyword.Less: + op = exprpb.Op_LT + case keyword.Greater: + op = exprpb.Op_GT + case keyword.LessEq: + op = exprpb.Op_LE + case keyword.GreaterEq: + op = exprpb.Op_GE + + case keyword.Range: + op = exprpb.Op_RANGE + case keyword.RangeEq: + op = exprpb.Op_RANGE_EQ + + case keyword.Plus: + op = exprpb.Op_ADD + case keyword.Minus: + op = exprpb.Op_SUB + case keyword.Star: + op = exprpb.Op_MUL + case keyword.Slash: + op = exprpb.Op_DIV + case keyword.Percent: + op = exprpb.Op_REM + + case keyword.Dot: + op = exprpb.Op_PROPERTY + } + + return &exprpb.Op{ + Left: c.expr(e.Left()), + Right: c.expr(e.Right()), + Op: op, + Span: c.span(e), + OpSpan: c.span(e.OperatorToken()), + } +} + +func (c *protoEncoder) switch_(e expr.Switch) *exprpb.Switch { + if e.IsZero() { + return nil + } + + pb := &exprpb.Switch{ + Arg: c.expr(e.Arg()), + Span: c.span(e), + SwitchSpan: c.span(e.SwitchToken()), + BlockSpan: c.span(e.Braces()), + } + for p := range seq.Values(e.Cases()) { + pb.Cases = append(pb.Cases, &exprpb.Switch_Case{ + Else: p.Kind() == keyword.Else, + Alts: c.params(p.Alts()), + Block: c.block(p.Block()), + Span: c.span(p), + KwSpan: c.span(p.KeywordToken()), + ColonSpan: c.span(p.Colon()), + }) + } + return pb +} + +func (c *protoEncoder) token(e expr.Token) *exprpb.Token { + if e.IsZero() { + return nil + } + + pb := &exprpb.Token{ + Span: c.span(e), + } + switch e.Kind() { + case token.Number: + if n, ok := e.AsNumber().Int(); ok { + pb.Value = &exprpb.Token_Int{Int: n} + } else if n, ok := e.AsNumber().Float(); ok { + pb.Value = &exprpb.Token_Float{Float: n} + } + case token.String: + pb.Value = &exprpb.Token_String_{String_: e.AsString().Text()} + case token.Ident: + pb.Value = &exprpb.Token_Ident{Ident: e.Name()} + } + return pb +} + +func (c *protoEncoder) params(e expr.Params) *exprpb.Params { + if e.IsZero() { + return nil + } + + var brackets exprpb.Brackets + switch e.Brackets().Keyword() { + case keyword.Parens: + brackets = exprpb.Brackets_BRACKETS_PARENS + case keyword.Brackets: + brackets = exprpb.Brackets_BRACKETS_SQUARE + case keyword.Braces: + brackets = exprpb.Brackets_BRACKETS_CURLY + case keyword.Angles: + brackets = exprpb.Brackets_BRACKETS_ANGLE + } + + pb := &exprpb.Params{ + Brackets: brackets, + Span: c.span(e), + } + for p := range seq.Values(e) { + pb.Params = append(pb.Params, &exprpb.Params_Param{ + Name: c.expr(p.Name), + Expr: c.expr(p.Expr), + Cond: c.expr(p.Cond), + Span: c.span(p), + ColonSpan: c.span(p.Colon), + IfSpan: c.span(p.If), + }) + } + return pb +} diff --git a/experimental/internal/lexer/loop.go b/experimental/internal/lexer/loop.go index 017c8dec..a33b2d49 100644 --- a/experimental/internal/lexer/loop.go +++ b/experimental/internal/lexer/loop.go @@ -61,14 +61,21 @@ func loop(l *lexer) { } // Find the next valid keyword. - kw := keyword.Prefix(l.rest()) - switch what := l.OnKeyword(kw); what { - case DiscardKeyword: + var what OnKeyword + var kw keyword.Keyword + for k := range keyword.Prefixes(l.rest()) { + n := l.OnKeyword(k) + if n != DiscardKeyword { + kw = k + what = n + } + } + switch what { case KeepKeyword, BracketKeyword: word := kw.String() if l.NumberCanStartWithDot && kw == keyword.Dot { - next, _ := stringsx.Rune(l.rest()[len(word):], 0) + next, _ := stringsx.Rune(l.rest(), len(word)) if unicode.IsDigit(next) { break } @@ -79,7 +86,7 @@ func loop(l *lexer) { kind = token.Ident // If this is a reserved word, the rune after it must not be // an XID continue. - next, _ := stringsx.Rune(l.rest()[len(word):], 0) + next, _ := stringsx.Rune(l.rest(), len(word)) if unicodex.IsXIDContinue(next) { break } diff --git a/experimental/token/keyword/keyword.go b/experimental/token/keyword/keyword.go index 47d0dffc..c4b42472 100644 --- a/experimental/token/keyword/keyword.go +++ b/experimental/token/keyword/keyword.go @@ -47,7 +47,6 @@ const ( RPC Returns To - In Repeated Optional Required @@ -76,6 +75,19 @@ const ( Null Map Max + Return + Break + Continue + If + Else + For + In + Switch + Case + Func + And + Or + Not Default JsonName Semi @@ -90,6 +102,14 @@ const ( Percent Bang Ask + ColonEq + PlusEq + MinusEq + StarEq + SlashEq + PercentEq + Range + RangeEq LParen RParen LBracket @@ -140,7 +160,7 @@ func Lookup(s string) Keyword { // All returns an iterator over all distinct [Keyword] values. func All() iter.Seq[Keyword] { return func(yield func(Keyword) bool) { - for i := 0; i < 84; i++ { + for i := 0; i < 104; i++ { if !yield(Keyword(i)) { return } @@ -168,7 +188,6 @@ var _table_Keyword_String = [...]string{ RPC: "rpc", Returns: "returns", To: "to", - In: "in", Repeated: "repeated", Optional: "optional", Required: "required", @@ -197,6 +216,19 @@ var _table_Keyword_String = [...]string{ Null: "null", Map: "map", Max: "max", + Return: "return", + Break: "break", + Continue: "continue", + If: "if", + Else: "else", + For: "for", + In: "in", + Switch: "switch", + Case: "case", + Func: "func", + And: "and", + Or: "or", + Not: "not", Default: "default", JsonName: "json_name", Semi: ";", @@ -211,6 +243,14 @@ var _table_Keyword_String = [...]string{ Percent: "%", Bang: "!", Ask: "?", + ColonEq: ":=", + PlusEq: "+=", + MinusEq: "-=", + StarEq: "*=", + SlashEq: "/=", + PercentEq: "%=", + Range: "..", + RangeEq: "..=", LParen: "(", RParen: ")", LBracket: "[", @@ -255,7 +295,6 @@ var _table_Keyword_GoString = [...]string{ RPC: "RPC", Returns: "Returns", To: "To", - In: "In", Repeated: "Repeated", Optional: "Optional", Required: "Required", @@ -284,6 +323,19 @@ var _table_Keyword_GoString = [...]string{ Null: "Null", Map: "Map", Max: "Max", + Return: "Return", + Break: "Break", + Continue: "Continue", + If: "If", + Else: "Else", + For: "For", + In: "In", + Switch: "Switch", + Case: "Case", + Func: "Func", + And: "And", + Or: "Or", + Not: "Not", Default: "Default", JsonName: "JsonName", Semi: "Semi", @@ -298,6 +350,14 @@ var _table_Keyword_GoString = [...]string{ Percent: "Percent", Bang: "Bang", Ask: "Ask", + ColonEq: "ColonEq", + PlusEq: "PlusEq", + MinusEq: "MinusEq", + StarEq: "StarEq", + SlashEq: "SlashEq", + PercentEq: "PercentEq", + Range: "Range", + RangeEq: "RangeEq", LParen: "LParen", RParen: "RParen", LBracket: "LBracket", @@ -342,7 +402,6 @@ var _table_Keyword_Lookup = map[string]Keyword{ "rpc": RPC, "returns": Returns, "to": To, - "in": In, "repeated": Repeated, "optional": Optional, "required": Required, @@ -371,6 +430,19 @@ var _table_Keyword_Lookup = map[string]Keyword{ "null": Null, "map": Map, "max": Max, + "return": Return, + "break": Break, + "continue": Continue, + "if": If, + "else": Else, + "for": For, + "in": In, + "switch": Switch, + "case": Case, + "func": Func, + "and": And, + "or": Or, + "not": Not, "default": Default, "json_name": JsonName, ";": Semi, @@ -385,6 +457,14 @@ var _table_Keyword_Lookup = map[string]Keyword{ "%": Percent, "!": Bang, "?": Ask, + ":=": ColonEq, + "+=": PlusEq, + "-=": MinusEq, + "*=": StarEq, + "/=": SlashEq, + "%=": PercentEq, + "..": Range, + "..=": RangeEq, "(": LParen, ")": RParen, "[": LBracket, diff --git a/experimental/token/keyword/keyword.yaml b/experimental/token/keyword/keyword.yaml index a0e04e7f..c9b43416 100644 --- a/experimental/token/keyword/keyword.yaml +++ b/experimental/token/keyword/keyword.yaml @@ -59,7 +59,6 @@ - {name: Returns, string: returns} - {name: To, string: to} - - {name: In, string: in} - {name: Repeated, string: repeated} - {name: Optional, string: optional} @@ -92,6 +91,21 @@ - {name: Map, string: map} - {name: Max, string: max} + - {name: Return, string: return} + - {name: Break, string: break} + - {name: Continue, string: continue} + - {name: If, string: if} + - {name: Else, string: else} + - {name: For, string: for} + - {name: In, string: in} + - {name: Switch, string: switch} + - {name: Case, string: case} + - {name: Func, string: func} + + - {name: And, string: and} + - {name: Or, string: or} + - {name: Not, string: not} + # Pseudo-option names. - {name: Default, string: default} @@ -112,6 +126,16 @@ - {name: Bang, string: "!"} - {name: Ask, string: "?"} + - {name: ColonEq, string: ":="} + - {name: PlusEq, string: "+="} + - {name: MinusEq, string: "-="} + - {name: StarEq, string: "*="} + - {name: SlashEq, string: "/="} + - {name: PercentEq, string: "%="} + + - {name: Range, string: ".."} + - {name: RangeEq, string: "..="} + - {name: LParen, string: "("} - {name: RParen, string: ")"} - {name: LBracket, string: "["} diff --git a/experimental/token/keyword/methods.go b/experimental/token/keyword/methods.go index 1ac58630..b8f4bc8e 100644 --- a/experimental/token/keyword/methods.go +++ b/experimental/token/keyword/methods.go @@ -14,7 +14,12 @@ package keyword -import "github.com/bufbuild/protocompile/internal/trie" +import ( + "iter" + + "github.com/bufbuild/protocompile/internal/ext/iterx" + "github.com/bufbuild/protocompile/internal/trie" +) var kwTrie = func() *trie.Trie[Keyword] { trie := new(trie.Trie[Keyword]) @@ -33,6 +38,12 @@ func Prefix(text string) Keyword { return kw } +// Prefix returns an iterator over the keywords that can be returned by [Lookup] +// which are prefixes of text, in ascending order of length. +func Prefixes(text string) iter.Seq[Keyword] { + return iterx.Right(kwTrie.Prefixes(text)) +} + // Brackets returns the open and close brackets if k is a bracket keyword. func (k Keyword) Brackets() (left, right, joined Keyword) { if int(k) >= len(braces) { diff --git a/internal/ext/iterx/get.go b/internal/ext/iterx/get.go index f484abb5..895fc586 100644 --- a/internal/ext/iterx/get.go +++ b/internal/ext/iterx/get.go @@ -20,20 +20,38 @@ import ( // First retrieves the first element of an iterator. func First[T any](seq iter.Seq[T]) (v T, ok bool) { - for x := range seq { - return x, true + for v = range seq { + ok = true + break + } + return v, ok +} + +// First retrieves the first element a two-element iterator. +func First2[K, V any](seq iter.Seq2[K, V]) (k K, v V, ok bool) { + for k, v = range seq { + ok = true + break } - return v, false + return k, v, ok } // Last retrieves the last element of an iterator. func Last[T any](seq iter.Seq[T]) (v T, ok bool) { - for x := range seq { - v, ok = x, true + for v = range seq { + ok = true } return v, ok } +// Last retrieves the last element of a two-element iterator. +func Last2[K, V any](seq iter.Seq2[K, V]) (k K, v V, ok bool) { + for k, v = range seq { + ok = true + } + return k, v, ok +} + // OnlyOne retrieves the only element of an iterator. func OnlyOne[T any](seq iter.Seq[T]) (v T, ok bool) { for i, x := range Enumerate(seq) { diff --git a/internal/ext/unsafex/unsafex.go b/internal/ext/unsafex/unsafex.go index c0dc3063..c283221b 100644 --- a/internal/ext/unsafex/unsafex.go +++ b/internal/ext/unsafex/unsafex.go @@ -152,3 +152,22 @@ func BytesAlias[S ~[]B, B ~byte](data string) []B { len(data), ) } + +// NoEscape makes a copy of a pointer which is hidden from the compiler's +// escape analysis. If the return value of this function appears to escape, the +// compiler will *not* treat its input as escaping, potentially allowing stack +// promotion of values which escape. This function is essentially the opposite +// of runtime.KeepAlive. +// +// This is only safe when the return value does not actually escape to the heap, +// but only appears to, such as by being passed to a virtual call which does not +// actually result in a heap escape. +// +//go:nosplit +func NoEscape[P ~*E, E any](ptr P) P { + p := unsafe.Pointer(ptr) + // Xoring the address with zero is a reliable way to hide a pointer from + // the compiler. + p = unsafe.Pointer(uintptr(p) ^ 0) //nolint:staticcheck + return P(p) +} diff --git a/internal/gen/buf/compiler/expr/v1alpha1/expr.pb.go b/internal/gen/buf/compiler/expr/v1alpha1/expr.pb.go new file mode 100644 index 00000000..d61bab15 --- /dev/null +++ b/internal/gen/buf/compiler/expr/v1alpha1/expr.pb.go @@ -0,0 +1,1823 @@ +// Copyright 2020-2025 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 protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.10 +// protoc (unknown) +// source: buf/compiler/expr/v1alpha1/expr.proto + +package exprv1alpha1 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Brackets is one of the various known bracket types. +// buf:lint:ignore ENUM_ZERO_VALUE_SUFFIX +type Brackets int32 + +const ( + Brackets_BRACKETS_NONE Brackets = 0 + Brackets_BRACKETS_PARENS Brackets = 1 + Brackets_BRACKETS_SQUARE Brackets = 2 + Brackets_BRACKETS_CURLY Brackets = 3 + Brackets_BRACKETS_ANGLE Brackets = 4 +) + +// Enum value maps for Brackets. +var ( + Brackets_name = map[int32]string{ + 0: "BRACKETS_NONE", + 1: "BRACKETS_PARENS", + 2: "BRACKETS_SQUARE", + 3: "BRACKETS_CURLY", + 4: "BRACKETS_ANGLE", + } + Brackets_value = map[string]int32{ + "BRACKETS_NONE": 0, + "BRACKETS_PARENS": 1, + "BRACKETS_SQUARE": 2, + "BRACKETS_CURLY": 3, + "BRACKETS_ANGLE": 4, + } +) + +func (x Brackets) Enum() *Brackets { + p := new(Brackets) + *p = x + return p +} + +func (x Brackets) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Brackets) Descriptor() protoreflect.EnumDescriptor { + return file_buf_compiler_expr_v1alpha1_expr_proto_enumTypes[0].Descriptor() +} + +func (Brackets) Type() protoreflect.EnumType { + return &file_buf_compiler_expr_v1alpha1_expr_proto_enumTypes[0] +} + +func (x Brackets) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Brackets.Descriptor instead. +func (Brackets) EnumDescriptor() ([]byte, []int) { + return file_buf_compiler_expr_v1alpha1_expr_proto_rawDescGZIP(), []int{0} +} + +// buf:lint:ignore ENUM_VALUE_PREFIX +type Control_Kind int32 + +const ( + Control_KIND_UNSPECIFIED Control_Kind = 0 + Control_RETURN Control_Kind = 1 + Control_BREAK Control_Kind = 2 + Control_CONTINUE Control_Kind = 3 +) + +// Enum value maps for Control_Kind. +var ( + Control_Kind_name = map[int32]string{ + 0: "KIND_UNSPECIFIED", + 1: "RETURN", + 2: "BREAK", + 3: "CONTINUE", + } + Control_Kind_value = map[string]int32{ + "KIND_UNSPECIFIED": 0, + "RETURN": 1, + "BREAK": 2, + "CONTINUE": 3, + } +) + +func (x Control_Kind) Enum() *Control_Kind { + p := new(Control_Kind) + *p = x + return p +} + +func (x Control_Kind) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Control_Kind) Descriptor() protoreflect.EnumDescriptor { + return file_buf_compiler_expr_v1alpha1_expr_proto_enumTypes[1].Descriptor() +} + +func (Control_Kind) Type() protoreflect.EnumType { + return &file_buf_compiler_expr_v1alpha1_expr_proto_enumTypes[1] +} + +func (x Control_Kind) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Control_Kind.Descriptor instead. +func (Control_Kind) EnumDescriptor() ([]byte, []int) { + return file_buf_compiler_expr_v1alpha1_expr_proto_rawDescGZIP(), []int{3, 0} +} + +// buf:lint:ignore ENUM_VALUE_PREFIX +type Op_Kind int32 + +const ( + Op_KIND_UNSPECIFIED Op_Kind = 0 + Op_ASSIGN Op_Kind = 1 + Op_ASSIGN_NEW Op_Kind = 2 + Op_ASSIGN_ADD Op_Kind = 3 + Op_ASSIGN_SUB Op_Kind = 4 + Op_ASSIGN_MUL Op_Kind = 5 + Op_ASSIGN_DIV Op_Kind = 6 + Op_ASSIGN_REM Op_Kind = 7 + Op_COMMA Op_Kind = 8 + Op_OR Op_Kind = 9 + Op_AND Op_Kind = 10 + Op_NOT Op_Kind = 11 + Op_EQ Op_Kind = 12 + Op_NE Op_Kind = 13 + Op_LT Op_Kind = 14 + Op_GT Op_Kind = 15 + Op_LE Op_Kind = 16 + Op_GE Op_Kind = 17 + Op_RANGE Op_Kind = 18 + Op_RANGE_EQ Op_Kind = 19 + Op_TO Op_Kind = 20 + Op_ADD Op_Kind = 21 + Op_SUB Op_Kind = 22 + Op_MUL Op_Kind = 23 + Op_DIV Op_Kind = 24 + Op_REM Op_Kind = 25 + Op_PROPERTY Op_Kind = 26 +) + +// Enum value maps for Op_Kind. +var ( + Op_Kind_name = map[int32]string{ + 0: "KIND_UNSPECIFIED", + 1: "ASSIGN", + 2: "ASSIGN_NEW", + 3: "ASSIGN_ADD", + 4: "ASSIGN_SUB", + 5: "ASSIGN_MUL", + 6: "ASSIGN_DIV", + 7: "ASSIGN_REM", + 8: "COMMA", + 9: "OR", + 10: "AND", + 11: "NOT", + 12: "EQ", + 13: "NE", + 14: "LT", + 15: "GT", + 16: "LE", + 17: "GE", + 18: "RANGE", + 19: "RANGE_EQ", + 20: "TO", + 21: "ADD", + 22: "SUB", + 23: "MUL", + 24: "DIV", + 25: "REM", + 26: "PROPERTY", + } + Op_Kind_value = map[string]int32{ + "KIND_UNSPECIFIED": 0, + "ASSIGN": 1, + "ASSIGN_NEW": 2, + "ASSIGN_ADD": 3, + "ASSIGN_SUB": 4, + "ASSIGN_MUL": 5, + "ASSIGN_DIV": 6, + "ASSIGN_REM": 7, + "COMMA": 8, + "OR": 9, + "AND": 10, + "NOT": 11, + "EQ": 12, + "NE": 13, + "LT": 14, + "GT": 15, + "LE": 16, + "GE": 17, + "RANGE": 18, + "RANGE_EQ": 19, + "TO": 20, + "ADD": 21, + "SUB": 22, + "MUL": 23, + "DIV": 24, + "REM": 25, + "PROPERTY": 26, + } +) + +func (x Op_Kind) Enum() *Op_Kind { + p := new(Op_Kind) + *p = x + return p +} + +func (x Op_Kind) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Op_Kind) Descriptor() protoreflect.EnumDescriptor { + return file_buf_compiler_expr_v1alpha1_expr_proto_enumTypes[2].Descriptor() +} + +func (Op_Kind) Type() protoreflect.EnumType { + return &file_buf_compiler_expr_v1alpha1_expr_proto_enumTypes[2] +} + +func (x Op_Kind) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Op_Kind.Descriptor instead. +func (Op_Kind) EnumDescriptor() ([]byte, []int) { + return file_buf_compiler_expr_v1alpha1_expr_proto_rawDescGZIP(), []int{7, 0} +} + +// An expression parsed from some file. +type Expr struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Types that are valid to be assigned to Expr: + // + // *Expr_Block + // *Expr_Call + // *Expr_Control + // *Expr_For + // *Expr_Func + // *Expr_If + // *Expr_Op + // *Expr_Record + // *Expr_Switch + // *Expr_Token + Expr isExpr_Expr `protobuf_oneof:"expr"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Expr) Reset() { + *x = Expr{} + mi := &file_buf_compiler_expr_v1alpha1_expr_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Expr) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Expr) ProtoMessage() {} + +func (x *Expr) ProtoReflect() protoreflect.Message { + mi := &file_buf_compiler_expr_v1alpha1_expr_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Expr.ProtoReflect.Descriptor instead. +func (*Expr) Descriptor() ([]byte, []int) { + return file_buf_compiler_expr_v1alpha1_expr_proto_rawDescGZIP(), []int{0} +} + +func (x *Expr) GetExpr() isExpr_Expr { + if x != nil { + return x.Expr + } + return nil +} + +func (x *Expr) GetBlock() *Block { + if x != nil { + if x, ok := x.Expr.(*Expr_Block); ok { + return x.Block + } + } + return nil +} + +func (x *Expr) GetCall() *Call { + if x != nil { + if x, ok := x.Expr.(*Expr_Call); ok { + return x.Call + } + } + return nil +} + +func (x *Expr) GetControl() *Control { + if x != nil { + if x, ok := x.Expr.(*Expr_Control); ok { + return x.Control + } + } + return nil +} + +func (x *Expr) GetFor() *For { + if x != nil { + if x, ok := x.Expr.(*Expr_For); ok { + return x.For + } + } + return nil +} + +func (x *Expr) GetFunc() *Func { + if x != nil { + if x, ok := x.Expr.(*Expr_Func); ok { + return x.Func + } + } + return nil +} + +func (x *Expr) GetIf() *If { + if x != nil { + if x, ok := x.Expr.(*Expr_If); ok { + return x.If + } + } + return nil +} + +func (x *Expr) GetOp() *Op { + if x != nil { + if x, ok := x.Expr.(*Expr_Op); ok { + return x.Op + } + } + return nil +} + +func (x *Expr) GetRecord() *Params { + if x != nil { + if x, ok := x.Expr.(*Expr_Record); ok { + return x.Record + } + } + return nil +} + +func (x *Expr) GetSwitch() *Switch { + if x != nil { + if x, ok := x.Expr.(*Expr_Switch); ok { + return x.Switch + } + } + return nil +} + +func (x *Expr) GetToken() *Token { + if x != nil { + if x, ok := x.Expr.(*Expr_Token); ok { + return x.Token + } + } + return nil +} + +type isExpr_Expr interface { + isExpr_Expr() +} + +type Expr_Block struct { + Block *Block `protobuf:"bytes,1,opt,name=block,proto3,oneof"` +} + +type Expr_Call struct { + Call *Call `protobuf:"bytes,2,opt,name=call,proto3,oneof"` +} + +type Expr_Control struct { + Control *Control `protobuf:"bytes,3,opt,name=control,proto3,oneof"` +} + +type Expr_For struct { + For *For `protobuf:"bytes,4,opt,name=for,proto3,oneof"` +} + +type Expr_Func struct { + Func *Func `protobuf:"bytes,5,opt,name=func,proto3,oneof"` +} + +type Expr_If struct { + If *If `protobuf:"bytes,6,opt,name=if,proto3,oneof"` +} + +type Expr_Op struct { + Op *Op `protobuf:"bytes,7,opt,name=op,proto3,oneof"` +} + +type Expr_Record struct { + Record *Params `protobuf:"bytes,8,opt,name=record,proto3,oneof"` +} + +type Expr_Switch struct { + Switch *Switch `protobuf:"bytes,9,opt,name=switch,proto3,oneof"` +} + +type Expr_Token struct { + Token *Token `protobuf:"bytes,10,opt,name=token,proto3,oneof"` +} + +func (*Expr_Block) isExpr_Expr() {} + +func (*Expr_Call) isExpr_Expr() {} + +func (*Expr_Control) isExpr_Expr() {} + +func (*Expr_For) isExpr_Expr() {} + +func (*Expr_Func) isExpr_Expr() {} + +func (*Expr_If) isExpr_Expr() {} + +func (*Expr_Op) isExpr_Expr() {} + +func (*Expr_Record) isExpr_Expr() {} + +func (*Expr_Switch) isExpr_Expr() {} + +func (*Expr_Token) isExpr_Expr() {} + +// An expr.Block. +type Block struct { + state protoimpl.MessageState `protogen:"open.v1"` + Exprs []*Expr `protobuf:"bytes,1,rep,name=exprs,proto3" json:"exprs,omitempty"` + Span *Span `protobuf:"bytes,10,opt,name=span,proto3" json:"span,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Block) Reset() { + *x = Block{} + mi := &file_buf_compiler_expr_v1alpha1_expr_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Block) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Block) ProtoMessage() {} + +func (x *Block) ProtoReflect() protoreflect.Message { + mi := &file_buf_compiler_expr_v1alpha1_expr_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Block.ProtoReflect.Descriptor instead. +func (*Block) Descriptor() ([]byte, []int) { + return file_buf_compiler_expr_v1alpha1_expr_proto_rawDescGZIP(), []int{1} +} + +func (x *Block) GetExprs() []*Expr { + if x != nil { + return x.Exprs + } + return nil +} + +func (x *Block) GetSpan() *Span { + if x != nil { + return x.Span + } + return nil +} + +// An expr.Call. +type Call struct { + state protoimpl.MessageState `protogen:"open.v1"` + Callee *Expr `protobuf:"bytes,1,opt,name=callee,proto3" json:"callee,omitempty"` + Params *Params `protobuf:"bytes,2,opt,name=params,proto3" json:"params,omitempty"` + Span *Span `protobuf:"bytes,10,opt,name=span,proto3" json:"span,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Call) Reset() { + *x = Call{} + mi := &file_buf_compiler_expr_v1alpha1_expr_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Call) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Call) ProtoMessage() {} + +func (x *Call) ProtoReflect() protoreflect.Message { + mi := &file_buf_compiler_expr_v1alpha1_expr_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Call.ProtoReflect.Descriptor instead. +func (*Call) Descriptor() ([]byte, []int) { + return file_buf_compiler_expr_v1alpha1_expr_proto_rawDescGZIP(), []int{2} +} + +func (x *Call) GetCallee() *Expr { + if x != nil { + return x.Callee + } + return nil +} + +func (x *Call) GetParams() *Params { + if x != nil { + return x.Params + } + return nil +} + +func (x *Call) GetSpan() *Span { + if x != nil { + return x.Span + } + return nil +} + +// An expr.Control. +type Control struct { + state protoimpl.MessageState `protogen:"open.v1"` + Kind Control_Kind `protobuf:"varint,1,opt,name=kind,proto3,enum=buf.compiler.expr.v1alpha1.Control_Kind" json:"kind,omitempty"` + Args *Params `protobuf:"bytes,2,opt,name=args,proto3" json:"args,omitempty"` + Cond *Expr `protobuf:"bytes,3,opt,name=cond,proto3" json:"cond,omitempty"` + Span *Span `protobuf:"bytes,10,opt,name=span,proto3" json:"span,omitempty"` + KwSpan *Span `protobuf:"bytes,11,opt,name=kw_span,json=kwSpan,proto3" json:"kw_span,omitempty"` + IfSpan *Span `protobuf:"bytes,12,opt,name=if_span,json=ifSpan,proto3" json:"if_span,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Control) Reset() { + *x = Control{} + mi := &file_buf_compiler_expr_v1alpha1_expr_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Control) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Control) ProtoMessage() {} + +func (x *Control) ProtoReflect() protoreflect.Message { + mi := &file_buf_compiler_expr_v1alpha1_expr_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Control.ProtoReflect.Descriptor instead. +func (*Control) Descriptor() ([]byte, []int) { + return file_buf_compiler_expr_v1alpha1_expr_proto_rawDescGZIP(), []int{3} +} + +func (x *Control) GetKind() Control_Kind { + if x != nil { + return x.Kind + } + return Control_KIND_UNSPECIFIED +} + +func (x *Control) GetArgs() *Params { + if x != nil { + return x.Args + } + return nil +} + +func (x *Control) GetCond() *Expr { + if x != nil { + return x.Cond + } + return nil +} + +func (x *Control) GetSpan() *Span { + if x != nil { + return x.Span + } + return nil +} + +func (x *Control) GetKwSpan() *Span { + if x != nil { + return x.KwSpan + } + return nil +} + +func (x *Control) GetIfSpan() *Span { + if x != nil { + return x.IfSpan + } + return nil +} + +// An expr.For. +type For struct { + state protoimpl.MessageState `protogen:"open.v1"` + Vars *Params `protobuf:"bytes,1,opt,name=vars,proto3" json:"vars,omitempty"` + Iter *Expr `protobuf:"bytes,2,opt,name=iter,proto3" json:"iter,omitempty"` + Block *Block `protobuf:"bytes,3,opt,name=block,proto3" json:"block,omitempty"` + Span *Span `protobuf:"bytes,10,opt,name=span,proto3" json:"span,omitempty"` + ForSpan *Span `protobuf:"bytes,11,opt,name=for_span,json=forSpan,proto3" json:"for_span,omitempty"` + InSpan *Span `protobuf:"bytes,12,opt,name=in_span,json=inSpan,proto3" json:"in_span,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *For) Reset() { + *x = For{} + mi := &file_buf_compiler_expr_v1alpha1_expr_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *For) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*For) ProtoMessage() {} + +func (x *For) ProtoReflect() protoreflect.Message { + mi := &file_buf_compiler_expr_v1alpha1_expr_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use For.ProtoReflect.Descriptor instead. +func (*For) Descriptor() ([]byte, []int) { + return file_buf_compiler_expr_v1alpha1_expr_proto_rawDescGZIP(), []int{4} +} + +func (x *For) GetVars() *Params { + if x != nil { + return x.Vars + } + return nil +} + +func (x *For) GetIter() *Expr { + if x != nil { + return x.Iter + } + return nil +} + +func (x *For) GetBlock() *Block { + if x != nil { + return x.Block + } + return nil +} + +func (x *For) GetSpan() *Span { + if x != nil { + return x.Span + } + return nil +} + +func (x *For) GetForSpan() *Span { + if x != nil { + return x.ForSpan + } + return nil +} + +func (x *For) GetInSpan() *Span { + if x != nil { + return x.InSpan + } + return nil +} + +type Func struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Params *Params `protobuf:"bytes,2,opt,name=params,proto3" json:"params,omitempty"` + Return *Expr `protobuf:"bytes,3,opt,name=return,proto3" json:"return,omitempty"` + Body *Expr `protobuf:"bytes,4,opt,name=body,proto3" json:"body,omitempty"` + Span *Span `protobuf:"bytes,10,opt,name=span,proto3" json:"span,omitempty"` + FuncSpan *Span `protobuf:"bytes,11,opt,name=func_span,json=funcSpan,proto3" json:"func_span,omitempty"` + NameSpan *Span `protobuf:"bytes,12,opt,name=name_span,json=nameSpan,proto3" json:"name_span,omitempty"` + ArrowSpan *Span `protobuf:"bytes,13,opt,name=arrow_span,json=arrowSpan,proto3" json:"arrow_span,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Func) Reset() { + *x = Func{} + mi := &file_buf_compiler_expr_v1alpha1_expr_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Func) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Func) ProtoMessage() {} + +func (x *Func) ProtoReflect() protoreflect.Message { + mi := &file_buf_compiler_expr_v1alpha1_expr_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Func.ProtoReflect.Descriptor instead. +func (*Func) Descriptor() ([]byte, []int) { + return file_buf_compiler_expr_v1alpha1_expr_proto_rawDescGZIP(), []int{5} +} + +func (x *Func) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Func) GetParams() *Params { + if x != nil { + return x.Params + } + return nil +} + +func (x *Func) GetReturn() *Expr { + if x != nil { + return x.Return + } + return nil +} + +func (x *Func) GetBody() *Expr { + if x != nil { + return x.Body + } + return nil +} + +func (x *Func) GetSpan() *Span { + if x != nil { + return x.Span + } + return nil +} + +func (x *Func) GetFuncSpan() *Span { + if x != nil { + return x.FuncSpan + } + return nil +} + +func (x *Func) GetNameSpan() *Span { + if x != nil { + return x.NameSpan + } + return nil +} + +func (x *Func) GetArrowSpan() *Span { + if x != nil { + return x.ArrowSpan + } + return nil +} + +// An expr.If. +type If struct { + state protoimpl.MessageState `protogen:"open.v1"` + Cond *Expr `protobuf:"bytes,1,opt,name=cond,proto3" json:"cond,omitempty"` + Block *Block `protobuf:"bytes,2,opt,name=block,proto3" json:"block,omitempty"` + Else *If `protobuf:"bytes,3,opt,name=else,proto3" json:"else,omitempty"` + Span *Span `protobuf:"bytes,10,opt,name=span,proto3" json:"span,omitempty"` + ElseSpan *Span `protobuf:"bytes,11,opt,name=else_span,json=elseSpan,proto3" json:"else_span,omitempty"` + IfSpan *Span `protobuf:"bytes,12,opt,name=if_span,json=ifSpan,proto3" json:"if_span,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *If) Reset() { + *x = If{} + mi := &file_buf_compiler_expr_v1alpha1_expr_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *If) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*If) ProtoMessage() {} + +func (x *If) ProtoReflect() protoreflect.Message { + mi := &file_buf_compiler_expr_v1alpha1_expr_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use If.ProtoReflect.Descriptor instead. +func (*If) Descriptor() ([]byte, []int) { + return file_buf_compiler_expr_v1alpha1_expr_proto_rawDescGZIP(), []int{6} +} + +func (x *If) GetCond() *Expr { + if x != nil { + return x.Cond + } + return nil +} + +func (x *If) GetBlock() *Block { + if x != nil { + return x.Block + } + return nil +} + +func (x *If) GetElse() *If { + if x != nil { + return x.Else + } + return nil +} + +func (x *If) GetSpan() *Span { + if x != nil { + return x.Span + } + return nil +} + +func (x *If) GetElseSpan() *Span { + if x != nil { + return x.ElseSpan + } + return nil +} + +func (x *If) GetIfSpan() *Span { + if x != nil { + return x.IfSpan + } + return nil +} + +// An expr.Op. +type Op struct { + state protoimpl.MessageState `protogen:"open.v1"` + Left *Expr `protobuf:"bytes,1,opt,name=left,proto3" json:"left,omitempty"` + Right *Expr `protobuf:"bytes,2,opt,name=right,proto3" json:"right,omitempty"` + Op Op_Kind `protobuf:"varint,3,opt,name=op,proto3,enum=buf.compiler.expr.v1alpha1.Op_Kind" json:"op,omitempty"` + Span *Span `protobuf:"bytes,10,opt,name=span,proto3" json:"span,omitempty"` + OpSpan *Span `protobuf:"bytes,11,opt,name=op_span,json=opSpan,proto3" json:"op_span,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Op) Reset() { + *x = Op{} + mi := &file_buf_compiler_expr_v1alpha1_expr_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Op) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Op) ProtoMessage() {} + +func (x *Op) ProtoReflect() protoreflect.Message { + mi := &file_buf_compiler_expr_v1alpha1_expr_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Op.ProtoReflect.Descriptor instead. +func (*Op) Descriptor() ([]byte, []int) { + return file_buf_compiler_expr_v1alpha1_expr_proto_rawDescGZIP(), []int{7} +} + +func (x *Op) GetLeft() *Expr { + if x != nil { + return x.Left + } + return nil +} + +func (x *Op) GetRight() *Expr { + if x != nil { + return x.Right + } + return nil +} + +func (x *Op) GetOp() Op_Kind { + if x != nil { + return x.Op + } + return Op_KIND_UNSPECIFIED +} + +func (x *Op) GetSpan() *Span { + if x != nil { + return x.Span + } + return nil +} + +func (x *Op) GetOpSpan() *Span { + if x != nil { + return x.OpSpan + } + return nil +} + +// An expr.Switch. +type Switch struct { + state protoimpl.MessageState `protogen:"open.v1"` + Arg *Expr `protobuf:"bytes,1,opt,name=arg,proto3" json:"arg,omitempty"` + Cases []*Switch_Case `protobuf:"bytes,2,rep,name=cases,proto3" json:"cases,omitempty"` + Span *Span `protobuf:"bytes,10,opt,name=span,proto3" json:"span,omitempty"` + SwitchSpan *Span `protobuf:"bytes,11,opt,name=switch_span,json=switchSpan,proto3" json:"switch_span,omitempty"` + BlockSpan *Span `protobuf:"bytes,12,opt,name=block_span,json=blockSpan,proto3" json:"block_span,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Switch) Reset() { + *x = Switch{} + mi := &file_buf_compiler_expr_v1alpha1_expr_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Switch) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Switch) ProtoMessage() {} + +func (x *Switch) ProtoReflect() protoreflect.Message { + mi := &file_buf_compiler_expr_v1alpha1_expr_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Switch.ProtoReflect.Descriptor instead. +func (*Switch) Descriptor() ([]byte, []int) { + return file_buf_compiler_expr_v1alpha1_expr_proto_rawDescGZIP(), []int{8} +} + +func (x *Switch) GetArg() *Expr { + if x != nil { + return x.Arg + } + return nil +} + +func (x *Switch) GetCases() []*Switch_Case { + if x != nil { + return x.Cases + } + return nil +} + +func (x *Switch) GetSpan() *Span { + if x != nil { + return x.Span + } + return nil +} + +func (x *Switch) GetSwitchSpan() *Span { + if x != nil { + return x.SwitchSpan + } + return nil +} + +func (x *Switch) GetBlockSpan() *Span { + if x != nil { + return x.BlockSpan + } + return nil +} + +// An expr.Token. +type Token struct { + state protoimpl.MessageState `protogen:"open.v1"` + // None of these may be set, in the case of an invalid value. + // + // Types that are valid to be assigned to Value: + // + // *Token_Int + // *Token_Float + // *Token_String_ + // *Token_Ident + Value isToken_Value `protobuf_oneof:"value"` + Span *Span `protobuf:"bytes,10,opt,name=span,proto3" json:"span,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Token) Reset() { + *x = Token{} + mi := &file_buf_compiler_expr_v1alpha1_expr_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Token) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Token) ProtoMessage() {} + +func (x *Token) ProtoReflect() protoreflect.Message { + mi := &file_buf_compiler_expr_v1alpha1_expr_proto_msgTypes[9] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Token.ProtoReflect.Descriptor instead. +func (*Token) Descriptor() ([]byte, []int) { + return file_buf_compiler_expr_v1alpha1_expr_proto_rawDescGZIP(), []int{9} +} + +func (x *Token) GetValue() isToken_Value { + if x != nil { + return x.Value + } + return nil +} + +func (x *Token) GetInt() uint64 { + if x != nil { + if x, ok := x.Value.(*Token_Int); ok { + return x.Int + } + } + return 0 +} + +func (x *Token) GetFloat() float64 { + if x != nil { + if x, ok := x.Value.(*Token_Float); ok { + return x.Float + } + } + return 0 +} + +func (x *Token) GetString_() string { + if x != nil { + if x, ok := x.Value.(*Token_String_); ok { + return x.String_ + } + } + return "" +} + +func (x *Token) GetIdent() string { + if x != nil { + if x, ok := x.Value.(*Token_Ident); ok { + return x.Ident + } + } + return "" +} + +func (x *Token) GetSpan() *Span { + if x != nil { + return x.Span + } + return nil +} + +type isToken_Value interface { + isToken_Value() +} + +type Token_Int struct { + Int uint64 `protobuf:"varint,1,opt,name=int,proto3,oneof"` +} + +type Token_Float struct { + Float float64 `protobuf:"fixed64,2,opt,name=float,proto3,oneof"` +} + +type Token_String_ struct { + String_ string `protobuf:"bytes,3,opt,name=string,proto3,oneof"` +} + +type Token_Ident struct { + Ident string `protobuf:"bytes,4,opt,name=ident,proto3,oneof"` +} + +func (*Token_Int) isToken_Value() {} + +func (*Token_Float) isToken_Value() {} + +func (*Token_String_) isToken_Value() {} + +func (*Token_Ident) isToken_Value() {} + +// An expr.Params. +type Params struct { + state protoimpl.MessageState `protogen:"open.v1"` + Brackets Brackets `protobuf:"varint,1,opt,name=brackets,proto3,enum=buf.compiler.expr.v1alpha1.Brackets" json:"brackets,omitempty"` + Params []*Params_Param `protobuf:"bytes,2,rep,name=params,proto3" json:"params,omitempty"` + Span *Span `protobuf:"bytes,10,opt,name=span,proto3" json:"span,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Params) Reset() { + *x = Params{} + mi := &file_buf_compiler_expr_v1alpha1_expr_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Params) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Params) ProtoMessage() {} + +func (x *Params) ProtoReflect() protoreflect.Message { + mi := &file_buf_compiler_expr_v1alpha1_expr_proto_msgTypes[10] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Params.ProtoReflect.Descriptor instead. +func (*Params) Descriptor() ([]byte, []int) { + return file_buf_compiler_expr_v1alpha1_expr_proto_rawDescGZIP(), []int{10} +} + +func (x *Params) GetBrackets() Brackets { + if x != nil { + return x.Brackets + } + return Brackets_BRACKETS_NONE +} + +func (x *Params) GetParams() []*Params_Param { + if x != nil { + return x.Params + } + return nil +} + +func (x *Params) GetSpan() *Span { + if x != nil { + return x.Span + } + return nil +} + +// A source code span for a specific `File`. +// +// This only contains byte offsets for the span; all other information +// (such as the line number) should be re-computed as needed. +type Span struct { + state protoimpl.MessageState `protogen:"open.v1"` + Start uint32 `protobuf:"varint,1,opt,name=start,proto3" json:"start,omitempty"` + End uint32 `protobuf:"varint,2,opt,name=end,proto3" json:"end,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Span) Reset() { + *x = Span{} + mi := &file_buf_compiler_expr_v1alpha1_expr_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Span) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Span) ProtoMessage() {} + +func (x *Span) ProtoReflect() protoreflect.Message { + mi := &file_buf_compiler_expr_v1alpha1_expr_proto_msgTypes[11] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Span.ProtoReflect.Descriptor instead. +func (*Span) Descriptor() ([]byte, []int) { + return file_buf_compiler_expr_v1alpha1_expr_proto_rawDescGZIP(), []int{11} +} + +func (x *Span) GetStart() uint32 { + if x != nil { + return x.Start + } + return 0 +} + +func (x *Span) GetEnd() uint32 { + if x != nil { + return x.End + } + return 0 +} + +type Switch_Case struct { + state protoimpl.MessageState `protogen:"open.v1"` + Else bool `protobuf:"varint,1,opt,name=else,proto3" json:"else,omitempty"` + Alts *Params `protobuf:"bytes,2,opt,name=alts,proto3" json:"alts,omitempty"` + Block *Block `protobuf:"bytes,3,opt,name=block,proto3" json:"block,omitempty"` + Span *Span `protobuf:"bytes,10,opt,name=span,proto3" json:"span,omitempty"` + KwSpan *Span `protobuf:"bytes,11,opt,name=kw_span,json=kwSpan,proto3" json:"kw_span,omitempty"` + ColonSpan *Span `protobuf:"bytes,12,opt,name=colon_span,json=colonSpan,proto3" json:"colon_span,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Switch_Case) Reset() { + *x = Switch_Case{} + mi := &file_buf_compiler_expr_v1alpha1_expr_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Switch_Case) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Switch_Case) ProtoMessage() {} + +func (x *Switch_Case) ProtoReflect() protoreflect.Message { + mi := &file_buf_compiler_expr_v1alpha1_expr_proto_msgTypes[12] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Switch_Case.ProtoReflect.Descriptor instead. +func (*Switch_Case) Descriptor() ([]byte, []int) { + return file_buf_compiler_expr_v1alpha1_expr_proto_rawDescGZIP(), []int{8, 0} +} + +func (x *Switch_Case) GetElse() bool { + if x != nil { + return x.Else + } + return false +} + +func (x *Switch_Case) GetAlts() *Params { + if x != nil { + return x.Alts + } + return nil +} + +func (x *Switch_Case) GetBlock() *Block { + if x != nil { + return x.Block + } + return nil +} + +func (x *Switch_Case) GetSpan() *Span { + if x != nil { + return x.Span + } + return nil +} + +func (x *Switch_Case) GetKwSpan() *Span { + if x != nil { + return x.KwSpan + } + return nil +} + +func (x *Switch_Case) GetColonSpan() *Span { + if x != nil { + return x.ColonSpan + } + return nil +} + +type Params_Param struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name *Expr `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Expr *Expr `protobuf:"bytes,2,opt,name=expr,proto3" json:"expr,omitempty"` + Cond *Expr `protobuf:"bytes,3,opt,name=cond,proto3" json:"cond,omitempty"` + Span *Span `protobuf:"bytes,10,opt,name=span,proto3" json:"span,omitempty"` + ColonSpan *Span `protobuf:"bytes,11,opt,name=colon_span,json=colonSpan,proto3" json:"colon_span,omitempty"` + IfSpan *Span `protobuf:"bytes,12,opt,name=if_span,json=ifSpan,proto3" json:"if_span,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Params_Param) Reset() { + *x = Params_Param{} + mi := &file_buf_compiler_expr_v1alpha1_expr_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Params_Param) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Params_Param) ProtoMessage() {} + +func (x *Params_Param) ProtoReflect() protoreflect.Message { + mi := &file_buf_compiler_expr_v1alpha1_expr_proto_msgTypes[13] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Params_Param.ProtoReflect.Descriptor instead. +func (*Params_Param) Descriptor() ([]byte, []int) { + return file_buf_compiler_expr_v1alpha1_expr_proto_rawDescGZIP(), []int{10, 0} +} + +func (x *Params_Param) GetName() *Expr { + if x != nil { + return x.Name + } + return nil +} + +func (x *Params_Param) GetExpr() *Expr { + if x != nil { + return x.Expr + } + return nil +} + +func (x *Params_Param) GetCond() *Expr { + if x != nil { + return x.Cond + } + return nil +} + +func (x *Params_Param) GetSpan() *Span { + if x != nil { + return x.Span + } + return nil +} + +func (x *Params_Param) GetColonSpan() *Span { + if x != nil { + return x.ColonSpan + } + return nil +} + +func (x *Params_Param) GetIfSpan() *Span { + if x != nil { + return x.IfSpan + } + return nil +} + +var File_buf_compiler_expr_v1alpha1_expr_proto protoreflect.FileDescriptor + +const file_buf_compiler_expr_v1alpha1_expr_proto_rawDesc = "" + + "\n" + + "%buf/compiler/expr/v1alpha1/expr.proto\x12\x1abuf.compiler.expr.v1alpha1\"\xca\x04\n" + + "\x04Expr\x129\n" + + "\x05block\x18\x01 \x01(\v2!.buf.compiler.expr.v1alpha1.BlockH\x00R\x05block\x126\n" + + "\x04call\x18\x02 \x01(\v2 .buf.compiler.expr.v1alpha1.CallH\x00R\x04call\x12?\n" + + "\acontrol\x18\x03 \x01(\v2#.buf.compiler.expr.v1alpha1.ControlH\x00R\acontrol\x123\n" + + "\x03for\x18\x04 \x01(\v2\x1f.buf.compiler.expr.v1alpha1.ForH\x00R\x03for\x126\n" + + "\x04func\x18\x05 \x01(\v2 .buf.compiler.expr.v1alpha1.FuncH\x00R\x04func\x120\n" + + "\x02if\x18\x06 \x01(\v2\x1e.buf.compiler.expr.v1alpha1.IfH\x00R\x02if\x120\n" + + "\x02op\x18\a \x01(\v2\x1e.buf.compiler.expr.v1alpha1.OpH\x00R\x02op\x12<\n" + + "\x06record\x18\b \x01(\v2\".buf.compiler.expr.v1alpha1.ParamsH\x00R\x06record\x12<\n" + + "\x06switch\x18\t \x01(\v2\".buf.compiler.expr.v1alpha1.SwitchH\x00R\x06switch\x129\n" + + "\x05token\x18\n" + + " \x01(\v2!.buf.compiler.expr.v1alpha1.TokenH\x00R\x05tokenB\x06\n" + + "\x04expr\"u\n" + + "\x05Block\x126\n" + + "\x05exprs\x18\x01 \x03(\v2 .buf.compiler.expr.v1alpha1.ExprR\x05exprs\x124\n" + + "\x04span\x18\n" + + " \x01(\v2 .buf.compiler.expr.v1alpha1.SpanR\x04span\"\xb2\x01\n" + + "\x04Call\x128\n" + + "\x06callee\x18\x01 \x01(\v2 .buf.compiler.expr.v1alpha1.ExprR\x06callee\x12:\n" + + "\x06params\x18\x02 \x01(\v2\".buf.compiler.expr.v1alpha1.ParamsR\x06params\x124\n" + + "\x04span\x18\n" + + " \x01(\v2 .buf.compiler.expr.v1alpha1.SpanR\x04span\"\xa4\x03\n" + + "\aControl\x12<\n" + + "\x04kind\x18\x01 \x01(\x0e2(.buf.compiler.expr.v1alpha1.Control.KindR\x04kind\x126\n" + + "\x04args\x18\x02 \x01(\v2\".buf.compiler.expr.v1alpha1.ParamsR\x04args\x124\n" + + "\x04cond\x18\x03 \x01(\v2 .buf.compiler.expr.v1alpha1.ExprR\x04cond\x124\n" + + "\x04span\x18\n" + + " \x01(\v2 .buf.compiler.expr.v1alpha1.SpanR\x04span\x129\n" + + "\akw_span\x18\v \x01(\v2 .buf.compiler.expr.v1alpha1.SpanR\x06kwSpan\x129\n" + + "\aif_span\x18\f \x01(\v2 .buf.compiler.expr.v1alpha1.SpanR\x06ifSpan\"A\n" + + "\x04Kind\x12\x14\n" + + "\x10KIND_UNSPECIFIED\x10\x00\x12\n" + + "\n" + + "\x06RETURN\x10\x01\x12\t\n" + + "\x05BREAK\x10\x02\x12\f\n" + + "\bCONTINUE\x10\x03\"\xda\x02\n" + + "\x03For\x126\n" + + "\x04vars\x18\x01 \x01(\v2\".buf.compiler.expr.v1alpha1.ParamsR\x04vars\x124\n" + + "\x04iter\x18\x02 \x01(\v2 .buf.compiler.expr.v1alpha1.ExprR\x04iter\x127\n" + + "\x05block\x18\x03 \x01(\v2!.buf.compiler.expr.v1alpha1.BlockR\x05block\x124\n" + + "\x04span\x18\n" + + " \x01(\v2 .buf.compiler.expr.v1alpha1.SpanR\x04span\x12;\n" + + "\bfor_span\x18\v \x01(\v2 .buf.compiler.expr.v1alpha1.SpanR\aforSpan\x129\n" + + "\ain_span\x18\f \x01(\v2 .buf.compiler.expr.v1alpha1.SpanR\x06inSpan\"\xbb\x03\n" + + "\x04Func\x12\x12\n" + + "\x04name\x18\x01 \x01(\tR\x04name\x12:\n" + + "\x06params\x18\x02 \x01(\v2\".buf.compiler.expr.v1alpha1.ParamsR\x06params\x128\n" + + "\x06return\x18\x03 \x01(\v2 .buf.compiler.expr.v1alpha1.ExprR\x06return\x124\n" + + "\x04body\x18\x04 \x01(\v2 .buf.compiler.expr.v1alpha1.ExprR\x04body\x124\n" + + "\x04span\x18\n" + + " \x01(\v2 .buf.compiler.expr.v1alpha1.SpanR\x04span\x12=\n" + + "\tfunc_span\x18\v \x01(\v2 .buf.compiler.expr.v1alpha1.SpanR\bfuncSpan\x12=\n" + + "\tname_span\x18\f \x01(\v2 .buf.compiler.expr.v1alpha1.SpanR\bnameSpan\x12?\n" + + "\n" + + "arrow_span\x18\r \x01(\v2 .buf.compiler.expr.v1alpha1.SpanR\tarrowSpan\"\xd7\x02\n" + + "\x02If\x124\n" + + "\x04cond\x18\x01 \x01(\v2 .buf.compiler.expr.v1alpha1.ExprR\x04cond\x127\n" + + "\x05block\x18\x02 \x01(\v2!.buf.compiler.expr.v1alpha1.BlockR\x05block\x122\n" + + "\x04else\x18\x03 \x01(\v2\x1e.buf.compiler.expr.v1alpha1.IfR\x04else\x124\n" + + "\x04span\x18\n" + + " \x01(\v2 .buf.compiler.expr.v1alpha1.SpanR\x04span\x12=\n" + + "\telse_span\x18\v \x01(\v2 .buf.compiler.expr.v1alpha1.SpanR\belseSpan\x129\n" + + "\aif_span\x18\f \x01(\v2 .buf.compiler.expr.v1alpha1.SpanR\x06ifSpan\"\xd4\x04\n" + + "\x02Op\x124\n" + + "\x04left\x18\x01 \x01(\v2 .buf.compiler.expr.v1alpha1.ExprR\x04left\x126\n" + + "\x05right\x18\x02 \x01(\v2 .buf.compiler.expr.v1alpha1.ExprR\x05right\x123\n" + + "\x02op\x18\x03 \x01(\x0e2#.buf.compiler.expr.v1alpha1.Op.KindR\x02op\x124\n" + + "\x04span\x18\n" + + " \x01(\v2 .buf.compiler.expr.v1alpha1.SpanR\x04span\x129\n" + + "\aop_span\x18\v \x01(\v2 .buf.compiler.expr.v1alpha1.SpanR\x06opSpan\"\xb9\x02\n" + + "\x04Kind\x12\x14\n" + + "\x10KIND_UNSPECIFIED\x10\x00\x12\n" + + "\n" + + "\x06ASSIGN\x10\x01\x12\x0e\n" + + "\n" + + "ASSIGN_NEW\x10\x02\x12\x0e\n" + + "\n" + + "ASSIGN_ADD\x10\x03\x12\x0e\n" + + "\n" + + "ASSIGN_SUB\x10\x04\x12\x0e\n" + + "\n" + + "ASSIGN_MUL\x10\x05\x12\x0e\n" + + "\n" + + "ASSIGN_DIV\x10\x06\x12\x0e\n" + + "\n" + + "ASSIGN_REM\x10\a\x12\t\n" + + "\x05COMMA\x10\b\x12\x06\n" + + "\x02OR\x10\t\x12\a\n" + + "\x03AND\x10\n" + + "\x12\a\n" + + "\x03NOT\x10\v\x12\x06\n" + + "\x02EQ\x10\f\x12\x06\n" + + "\x02NE\x10\r\x12\x06\n" + + "\x02LT\x10\x0e\x12\x06\n" + + "\x02GT\x10\x0f\x12\x06\n" + + "\x02LE\x10\x10\x12\x06\n" + + "\x02GE\x10\x11\x12\t\n" + + "\x05RANGE\x10\x12\x12\f\n" + + "\bRANGE_EQ\x10\x13\x12\x06\n" + + "\x02TO\x10\x14\x12\a\n" + + "\x03ADD\x10\x15\x12\a\n" + + "\x03SUB\x10\x16\x12\a\n" + + "\x03MUL\x10\x17\x12\a\n" + + "\x03DIV\x10\x18\x12\a\n" + + "\x03REM\x10\x19\x12\f\n" + + "\bPROPERTY\x10\x1a\"\xf5\x04\n" + + "\x06Switch\x122\n" + + "\x03arg\x18\x01 \x01(\v2 .buf.compiler.expr.v1alpha1.ExprR\x03arg\x12=\n" + + "\x05cases\x18\x02 \x03(\v2'.buf.compiler.expr.v1alpha1.Switch.CaseR\x05cases\x124\n" + + "\x04span\x18\n" + + " \x01(\v2 .buf.compiler.expr.v1alpha1.SpanR\x04span\x12A\n" + + "\vswitch_span\x18\v \x01(\v2 .buf.compiler.expr.v1alpha1.SpanR\n" + + "switchSpan\x12?\n" + + "\n" + + "block_span\x18\f \x01(\v2 .buf.compiler.expr.v1alpha1.SpanR\tblockSpan\x1a\xbd\x02\n" + + "\x04Case\x12\x12\n" + + "\x04else\x18\x01 \x01(\bR\x04else\x126\n" + + "\x04alts\x18\x02 \x01(\v2\".buf.compiler.expr.v1alpha1.ParamsR\x04alts\x127\n" + + "\x05block\x18\x03 \x01(\v2!.buf.compiler.expr.v1alpha1.BlockR\x05block\x124\n" + + "\x04span\x18\n" + + " \x01(\v2 .buf.compiler.expr.v1alpha1.SpanR\x04span\x129\n" + + "\akw_span\x18\v \x01(\v2 .buf.compiler.expr.v1alpha1.SpanR\x06kwSpan\x12?\n" + + "\n" + + "colon_span\x18\f \x01(\v2 .buf.compiler.expr.v1alpha1.SpanR\tcolonSpan\"\xa4\x01\n" + + "\x05Token\x12\x12\n" + + "\x03int\x18\x01 \x01(\x04H\x00R\x03int\x12\x16\n" + + "\x05float\x18\x02 \x01(\x01H\x00R\x05float\x12\x18\n" + + "\x06string\x18\x03 \x01(\tH\x00R\x06string\x12\x16\n" + + "\x05ident\x18\x04 \x01(\tH\x00R\x05ident\x124\n" + + "\x04span\x18\n" + + " \x01(\v2 .buf.compiler.expr.v1alpha1.SpanR\x04spanB\a\n" + + "\x05value\"\xa0\x04\n" + + "\x06Params\x12@\n" + + "\bbrackets\x18\x01 \x01(\x0e2$.buf.compiler.expr.v1alpha1.BracketsR\bbrackets\x12@\n" + + "\x06params\x18\x02 \x03(\v2(.buf.compiler.expr.v1alpha1.Params.ParamR\x06params\x124\n" + + "\x04span\x18\n" + + " \x01(\v2 .buf.compiler.expr.v1alpha1.SpanR\x04span\x1a\xdb\x02\n" + + "\x05Param\x124\n" + + "\x04name\x18\x01 \x01(\v2 .buf.compiler.expr.v1alpha1.ExprR\x04name\x124\n" + + "\x04expr\x18\x02 \x01(\v2 .buf.compiler.expr.v1alpha1.ExprR\x04expr\x124\n" + + "\x04cond\x18\x03 \x01(\v2 .buf.compiler.expr.v1alpha1.ExprR\x04cond\x124\n" + + "\x04span\x18\n" + + " \x01(\v2 .buf.compiler.expr.v1alpha1.SpanR\x04span\x12?\n" + + "\n" + + "colon_span\x18\v \x01(\v2 .buf.compiler.expr.v1alpha1.SpanR\tcolonSpan\x129\n" + + "\aif_span\x18\f \x01(\v2 .buf.compiler.expr.v1alpha1.SpanR\x06ifSpan\".\n" + + "\x04Span\x12\x14\n" + + "\x05start\x18\x01 \x01(\rR\x05start\x12\x10\n" + + "\x03end\x18\x02 \x01(\rR\x03end*o\n" + + "\bBrackets\x12\x11\n" + + "\rBRACKETS_NONE\x10\x00\x12\x13\n" + + "\x0fBRACKETS_PARENS\x10\x01\x12\x13\n" + + "\x0fBRACKETS_SQUARE\x10\x02\x12\x12\n" + + "\x0eBRACKETS_CURLY\x10\x03\x12\x12\n" + + "\x0eBRACKETS_ANGLE\x10\x04B\x8d\x02\n" + + "\x1ecom.buf.compiler.expr.v1alpha1B\tExprProtoP\x01ZUgithub.com/bufbuild/protocompile/internal/gen/buf/compiler/expr/v1alpha1;exprv1alpha1\xa2\x02\x03BCE\xaa\x02\x1aBuf.Compiler.Expr.V1alpha1\xca\x02\x1aBuf\\Compiler\\Expr\\V1alpha1\xe2\x02&Buf\\Compiler\\Expr\\V1alpha1\\GPBMetadata\xea\x02\x1dBuf::Compiler::Expr::V1alpha1b\x06proto3" + +var ( + file_buf_compiler_expr_v1alpha1_expr_proto_rawDescOnce sync.Once + file_buf_compiler_expr_v1alpha1_expr_proto_rawDescData []byte +) + +func file_buf_compiler_expr_v1alpha1_expr_proto_rawDescGZIP() []byte { + file_buf_compiler_expr_v1alpha1_expr_proto_rawDescOnce.Do(func() { + file_buf_compiler_expr_v1alpha1_expr_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_buf_compiler_expr_v1alpha1_expr_proto_rawDesc), len(file_buf_compiler_expr_v1alpha1_expr_proto_rawDesc))) + }) + return file_buf_compiler_expr_v1alpha1_expr_proto_rawDescData +} + +var file_buf_compiler_expr_v1alpha1_expr_proto_enumTypes = make([]protoimpl.EnumInfo, 3) +var file_buf_compiler_expr_v1alpha1_expr_proto_msgTypes = make([]protoimpl.MessageInfo, 14) +var file_buf_compiler_expr_v1alpha1_expr_proto_goTypes = []any{ + (Brackets)(0), // 0: buf.compiler.expr.v1alpha1.Brackets + (Control_Kind)(0), // 1: buf.compiler.expr.v1alpha1.Control.Kind + (Op_Kind)(0), // 2: buf.compiler.expr.v1alpha1.Op.Kind + (*Expr)(nil), // 3: buf.compiler.expr.v1alpha1.Expr + (*Block)(nil), // 4: buf.compiler.expr.v1alpha1.Block + (*Call)(nil), // 5: buf.compiler.expr.v1alpha1.Call + (*Control)(nil), // 6: buf.compiler.expr.v1alpha1.Control + (*For)(nil), // 7: buf.compiler.expr.v1alpha1.For + (*Func)(nil), // 8: buf.compiler.expr.v1alpha1.Func + (*If)(nil), // 9: buf.compiler.expr.v1alpha1.If + (*Op)(nil), // 10: buf.compiler.expr.v1alpha1.Op + (*Switch)(nil), // 11: buf.compiler.expr.v1alpha1.Switch + (*Token)(nil), // 12: buf.compiler.expr.v1alpha1.Token + (*Params)(nil), // 13: buf.compiler.expr.v1alpha1.Params + (*Span)(nil), // 14: buf.compiler.expr.v1alpha1.Span + (*Switch_Case)(nil), // 15: buf.compiler.expr.v1alpha1.Switch.Case + (*Params_Param)(nil), // 16: buf.compiler.expr.v1alpha1.Params.Param +} +var file_buf_compiler_expr_v1alpha1_expr_proto_depIdxs = []int32{ + 4, // 0: buf.compiler.expr.v1alpha1.Expr.block:type_name -> buf.compiler.expr.v1alpha1.Block + 5, // 1: buf.compiler.expr.v1alpha1.Expr.call:type_name -> buf.compiler.expr.v1alpha1.Call + 6, // 2: buf.compiler.expr.v1alpha1.Expr.control:type_name -> buf.compiler.expr.v1alpha1.Control + 7, // 3: buf.compiler.expr.v1alpha1.Expr.for:type_name -> buf.compiler.expr.v1alpha1.For + 8, // 4: buf.compiler.expr.v1alpha1.Expr.func:type_name -> buf.compiler.expr.v1alpha1.Func + 9, // 5: buf.compiler.expr.v1alpha1.Expr.if:type_name -> buf.compiler.expr.v1alpha1.If + 10, // 6: buf.compiler.expr.v1alpha1.Expr.op:type_name -> buf.compiler.expr.v1alpha1.Op + 13, // 7: buf.compiler.expr.v1alpha1.Expr.record:type_name -> buf.compiler.expr.v1alpha1.Params + 11, // 8: buf.compiler.expr.v1alpha1.Expr.switch:type_name -> buf.compiler.expr.v1alpha1.Switch + 12, // 9: buf.compiler.expr.v1alpha1.Expr.token:type_name -> buf.compiler.expr.v1alpha1.Token + 3, // 10: buf.compiler.expr.v1alpha1.Block.exprs:type_name -> buf.compiler.expr.v1alpha1.Expr + 14, // 11: buf.compiler.expr.v1alpha1.Block.span:type_name -> buf.compiler.expr.v1alpha1.Span + 3, // 12: buf.compiler.expr.v1alpha1.Call.callee:type_name -> buf.compiler.expr.v1alpha1.Expr + 13, // 13: buf.compiler.expr.v1alpha1.Call.params:type_name -> buf.compiler.expr.v1alpha1.Params + 14, // 14: buf.compiler.expr.v1alpha1.Call.span:type_name -> buf.compiler.expr.v1alpha1.Span + 1, // 15: buf.compiler.expr.v1alpha1.Control.kind:type_name -> buf.compiler.expr.v1alpha1.Control.Kind + 13, // 16: buf.compiler.expr.v1alpha1.Control.args:type_name -> buf.compiler.expr.v1alpha1.Params + 3, // 17: buf.compiler.expr.v1alpha1.Control.cond:type_name -> buf.compiler.expr.v1alpha1.Expr + 14, // 18: buf.compiler.expr.v1alpha1.Control.span:type_name -> buf.compiler.expr.v1alpha1.Span + 14, // 19: buf.compiler.expr.v1alpha1.Control.kw_span:type_name -> buf.compiler.expr.v1alpha1.Span + 14, // 20: buf.compiler.expr.v1alpha1.Control.if_span:type_name -> buf.compiler.expr.v1alpha1.Span + 13, // 21: buf.compiler.expr.v1alpha1.For.vars:type_name -> buf.compiler.expr.v1alpha1.Params + 3, // 22: buf.compiler.expr.v1alpha1.For.iter:type_name -> buf.compiler.expr.v1alpha1.Expr + 4, // 23: buf.compiler.expr.v1alpha1.For.block:type_name -> buf.compiler.expr.v1alpha1.Block + 14, // 24: buf.compiler.expr.v1alpha1.For.span:type_name -> buf.compiler.expr.v1alpha1.Span + 14, // 25: buf.compiler.expr.v1alpha1.For.for_span:type_name -> buf.compiler.expr.v1alpha1.Span + 14, // 26: buf.compiler.expr.v1alpha1.For.in_span:type_name -> buf.compiler.expr.v1alpha1.Span + 13, // 27: buf.compiler.expr.v1alpha1.Func.params:type_name -> buf.compiler.expr.v1alpha1.Params + 3, // 28: buf.compiler.expr.v1alpha1.Func.return:type_name -> buf.compiler.expr.v1alpha1.Expr + 3, // 29: buf.compiler.expr.v1alpha1.Func.body:type_name -> buf.compiler.expr.v1alpha1.Expr + 14, // 30: buf.compiler.expr.v1alpha1.Func.span:type_name -> buf.compiler.expr.v1alpha1.Span + 14, // 31: buf.compiler.expr.v1alpha1.Func.func_span:type_name -> buf.compiler.expr.v1alpha1.Span + 14, // 32: buf.compiler.expr.v1alpha1.Func.name_span:type_name -> buf.compiler.expr.v1alpha1.Span + 14, // 33: buf.compiler.expr.v1alpha1.Func.arrow_span:type_name -> buf.compiler.expr.v1alpha1.Span + 3, // 34: buf.compiler.expr.v1alpha1.If.cond:type_name -> buf.compiler.expr.v1alpha1.Expr + 4, // 35: buf.compiler.expr.v1alpha1.If.block:type_name -> buf.compiler.expr.v1alpha1.Block + 9, // 36: buf.compiler.expr.v1alpha1.If.else:type_name -> buf.compiler.expr.v1alpha1.If + 14, // 37: buf.compiler.expr.v1alpha1.If.span:type_name -> buf.compiler.expr.v1alpha1.Span + 14, // 38: buf.compiler.expr.v1alpha1.If.else_span:type_name -> buf.compiler.expr.v1alpha1.Span + 14, // 39: buf.compiler.expr.v1alpha1.If.if_span:type_name -> buf.compiler.expr.v1alpha1.Span + 3, // 40: buf.compiler.expr.v1alpha1.Op.left:type_name -> buf.compiler.expr.v1alpha1.Expr + 3, // 41: buf.compiler.expr.v1alpha1.Op.right:type_name -> buf.compiler.expr.v1alpha1.Expr + 2, // 42: buf.compiler.expr.v1alpha1.Op.op:type_name -> buf.compiler.expr.v1alpha1.Op.Kind + 14, // 43: buf.compiler.expr.v1alpha1.Op.span:type_name -> buf.compiler.expr.v1alpha1.Span + 14, // 44: buf.compiler.expr.v1alpha1.Op.op_span:type_name -> buf.compiler.expr.v1alpha1.Span + 3, // 45: buf.compiler.expr.v1alpha1.Switch.arg:type_name -> buf.compiler.expr.v1alpha1.Expr + 15, // 46: buf.compiler.expr.v1alpha1.Switch.cases:type_name -> buf.compiler.expr.v1alpha1.Switch.Case + 14, // 47: buf.compiler.expr.v1alpha1.Switch.span:type_name -> buf.compiler.expr.v1alpha1.Span + 14, // 48: buf.compiler.expr.v1alpha1.Switch.switch_span:type_name -> buf.compiler.expr.v1alpha1.Span + 14, // 49: buf.compiler.expr.v1alpha1.Switch.block_span:type_name -> buf.compiler.expr.v1alpha1.Span + 14, // 50: buf.compiler.expr.v1alpha1.Token.span:type_name -> buf.compiler.expr.v1alpha1.Span + 0, // 51: buf.compiler.expr.v1alpha1.Params.brackets:type_name -> buf.compiler.expr.v1alpha1.Brackets + 16, // 52: buf.compiler.expr.v1alpha1.Params.params:type_name -> buf.compiler.expr.v1alpha1.Params.Param + 14, // 53: buf.compiler.expr.v1alpha1.Params.span:type_name -> buf.compiler.expr.v1alpha1.Span + 13, // 54: buf.compiler.expr.v1alpha1.Switch.Case.alts:type_name -> buf.compiler.expr.v1alpha1.Params + 4, // 55: buf.compiler.expr.v1alpha1.Switch.Case.block:type_name -> buf.compiler.expr.v1alpha1.Block + 14, // 56: buf.compiler.expr.v1alpha1.Switch.Case.span:type_name -> buf.compiler.expr.v1alpha1.Span + 14, // 57: buf.compiler.expr.v1alpha1.Switch.Case.kw_span:type_name -> buf.compiler.expr.v1alpha1.Span + 14, // 58: buf.compiler.expr.v1alpha1.Switch.Case.colon_span:type_name -> buf.compiler.expr.v1alpha1.Span + 3, // 59: buf.compiler.expr.v1alpha1.Params.Param.name:type_name -> buf.compiler.expr.v1alpha1.Expr + 3, // 60: buf.compiler.expr.v1alpha1.Params.Param.expr:type_name -> buf.compiler.expr.v1alpha1.Expr + 3, // 61: buf.compiler.expr.v1alpha1.Params.Param.cond:type_name -> buf.compiler.expr.v1alpha1.Expr + 14, // 62: buf.compiler.expr.v1alpha1.Params.Param.span:type_name -> buf.compiler.expr.v1alpha1.Span + 14, // 63: buf.compiler.expr.v1alpha1.Params.Param.colon_span:type_name -> buf.compiler.expr.v1alpha1.Span + 14, // 64: buf.compiler.expr.v1alpha1.Params.Param.if_span:type_name -> buf.compiler.expr.v1alpha1.Span + 65, // [65:65] is the sub-list for method output_type + 65, // [65:65] is the sub-list for method input_type + 65, // [65:65] is the sub-list for extension type_name + 65, // [65:65] is the sub-list for extension extendee + 0, // [0:65] is the sub-list for field type_name +} + +func init() { file_buf_compiler_expr_v1alpha1_expr_proto_init() } +func file_buf_compiler_expr_v1alpha1_expr_proto_init() { + if File_buf_compiler_expr_v1alpha1_expr_proto != nil { + return + } + file_buf_compiler_expr_v1alpha1_expr_proto_msgTypes[0].OneofWrappers = []any{ + (*Expr_Block)(nil), + (*Expr_Call)(nil), + (*Expr_Control)(nil), + (*Expr_For)(nil), + (*Expr_Func)(nil), + (*Expr_If)(nil), + (*Expr_Op)(nil), + (*Expr_Record)(nil), + (*Expr_Switch)(nil), + (*Expr_Token)(nil), + } + file_buf_compiler_expr_v1alpha1_expr_proto_msgTypes[9].OneofWrappers = []any{ + (*Token_Int)(nil), + (*Token_Float)(nil), + (*Token_String_)(nil), + (*Token_Ident)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_buf_compiler_expr_v1alpha1_expr_proto_rawDesc), len(file_buf_compiler_expr_v1alpha1_expr_proto_rawDesc)), + NumEnums: 3, + NumMessages: 14, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_buf_compiler_expr_v1alpha1_expr_proto_goTypes, + DependencyIndexes: file_buf_compiler_expr_v1alpha1_expr_proto_depIdxs, + EnumInfos: file_buf_compiler_expr_v1alpha1_expr_proto_enumTypes, + MessageInfos: file_buf_compiler_expr_v1alpha1_expr_proto_msgTypes, + }.Build() + File_buf_compiler_expr_v1alpha1_expr_proto = out.File + file_buf_compiler_expr_v1alpha1_expr_proto_goTypes = nil + file_buf_compiler_expr_v1alpha1_expr_proto_depIdxs = nil +} diff --git a/internal/proto/buf/compiler/expr/v1alpha1/expr.proto b/internal/proto/buf/compiler/expr/v1alpha1/expr.proto new file mode 100644 index 00000000..410c5db3 --- /dev/null +++ b/internal/proto/buf/compiler/expr/v1alpha1/expr.proto @@ -0,0 +1,218 @@ +// Copyright 2020-2025 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. + +syntax = "proto3"; + +package buf.compiler.expr.v1alpha1; + +// An expression parsed from some file. +message Expr { + oneof expr { + Block block = 1; + Call call = 2; + Control control = 3; + For for = 4; + Func func = 5; + If if = 6; + Op op = 7; + Params record = 8; + Switch switch = 9; + Token token = 10; + } +} + +// An expr.Block. +message Block { + repeated Expr exprs = 1; + Span span = 10; +} + +// An expr.Call. +message Call { + Expr callee = 1; + Params params = 2; + + Span span = 10; +} + +// An expr.Control. +message Control { + // buf:lint:ignore ENUM_VALUE_PREFIX + enum Kind { + KIND_UNSPECIFIED = 0; + + RETURN = 1; + BREAK = 2; + CONTINUE = 3; + } + + Kind kind = 1; + Params args = 2; + Expr cond = 3; + + Span span = 10; + Span kw_span = 11; + Span if_span = 12; +} + +// An expr.For. +message For { + Params vars = 1; + Expr iter = 2; + Block block = 3; + + Span span = 10; + Span for_span = 11; + Span in_span = 12; +} + +message Func { + string name = 1; + Params params = 2; + Expr return = 3; + Expr body = 4; + + Span span = 10; + Span func_span = 11; + Span name_span = 12; + Span arrow_span = 13; +} + +// An expr.If. +message If { + Expr cond = 1; + Block block = 2; + If else = 3; + + Span span = 10; + Span else_span = 11; + Span if_span = 12; +} + +// An expr.Op. +message Op { + // buf:lint:ignore ENUM_VALUE_PREFIX + enum Kind { + KIND_UNSPECIFIED = 0; + + ASSIGN = 1; + ASSIGN_NEW = 2; + ASSIGN_ADD = 3; + ASSIGN_SUB = 4; + ASSIGN_MUL = 5; + ASSIGN_DIV = 6; + ASSIGN_REM = 7; + + COMMA = 8; + + OR = 9; + AND = 10; + NOT = 11; + + EQ = 12; + NE = 13; + LT = 14; + GT = 15; + LE = 16; + GE = 17; + + RANGE = 18; + RANGE_EQ = 19; + TO = 20; + + ADD = 21; + SUB = 22; + MUL = 23; + DIV = 24; + REM = 25; + + PROPERTY = 26; + } + + Expr left = 1; + Expr right = 2; + Kind op = 3; + + Span span = 10; + Span op_span = 11; +} + +// An expr.Switch. +message Switch { + message Case { + bool else = 1; + Params alts = 2; + Block block = 3; + + Span span = 10; + Span kw_span = 11; + Span colon_span = 12; + } + + Expr arg = 1; + repeated Case cases = 2; + + Span span = 10; + Span switch_span = 11; + Span block_span = 12; +} + +// An expr.Token. +message Token { + // None of these may be set, in the case of an invalid value. + oneof value { + uint64 int = 1; + double float = 2; + string string = 3; + string ident = 4; + } + + Span span = 10; +} + +// An expr.Params. +message Params { + message Param { + Expr name = 1; + Expr expr = 2; + Expr cond = 3; + + Span span = 10; + Span colon_span = 11; + Span if_span = 12; + } + + Brackets brackets = 1; + repeated Param params = 2; + Span span = 10; +} + +// A source code span for a specific `File`. +// +// This only contains byte offsets for the span; all other information +// (such as the line number) should be re-computed as needed. +message Span { + uint32 start = 1; + uint32 end = 2; +} + +// Brackets is one of the various known bracket types. +// buf:lint:ignore ENUM_ZERO_VALUE_SUFFIX +enum Brackets { + BRACKETS_NONE = 0; + BRACKETS_PARENS = 1; + BRACKETS_SQUARE = 2; + BRACKETS_CURLY = 3; + BRACKETS_ANGLE = 4; +} \ No newline at end of file diff --git a/internal/trie/nybbles.go b/internal/trie/nybbles.go index 2a66033f..ab4b8613 100644 --- a/internal/trie/nybbles.go +++ b/internal/trie/nybbles.go @@ -45,13 +45,11 @@ type nybbles[N uint8 | uint16 | uint32 | uint64] struct { hasValue []uint } -// Get returns the index corresponding to the longest prefix of key present -// in the trie. The match is exact when len(key) == len(prefix). -// -// If no key in the trie is a prefix of key, returns "", -1. -func (t *nybbles[N]) get(key string) (prefix string, found int) { - if t.hi == nil { - return "", -1 +// search walks the trie along the path given by the +// given key, yielding prefixes and indices for each node visited. +func (t *nybbles[N]) search(key string, yield func(string, int) bool) { + if t.has(0) && !yield("", 0) { + return } var n int @@ -69,13 +67,10 @@ func (t *nybbles[N]) get(key string) (prefix string, found int) { } n = int(t.lo[m][lo]) - if t.has(n) { - prefix = key[:i+1] - found = n + if t.has(n) && !yield(key[:i+1], n) { + return } } - - return prefix, found } // insert adds a new key to the trie; returns the index to insert the diff --git a/internal/trie/trie.go b/internal/trie/trie.go index c33fe39b..4385f00f 100644 --- a/internal/trie/trie.go +++ b/internal/trie/trie.go @@ -14,7 +14,13 @@ package trie -import "strings" +import ( + "iter" + "strings" + + "github.com/bufbuild/protocompile/internal/ext/iterx" + "github.com/bufbuild/protocompile/internal/ext/unsafex" +) // Trie implements a map from strings to V, except lookups return the key // which is the longest prefix of a given query. @@ -22,7 +28,7 @@ import "strings" // The zero value is empty and ready to use. type Trie[V any] struct { impl interface { - get(key string) (prefix string, found int) + search(key string, yield func(string, int) bool) insert(key string) int dump(*strings.Builder) @@ -31,20 +37,33 @@ type Trie[V any] struct { } // Get returns the value corresponding to the longest prefix of key present -// in the trie. The match is exact when len(key) == len(prefix). +// in the trie, such that the prefix and its associated value satisfy the +// predicate ok (ok == nil implies that all values are ok). // // If no key in the trie is a prefix of key, returns "" and the zero value of v. +// The match is exact when len(key) == len(prefix). func (t *Trie[V]) Get(key string) (prefix string, value V) { - if t.impl == nil { - return "", value - } + prefix, value, _ = iterx.Last2(t.Prefixes(key)) + return prefix, value +} - prefix, n := t.impl.get(key) - if n == -1 { - return "", value - } +// Prefixes returns an iterator over prefixes of key within the trie, and their +// associated values. +func (t *Trie[V]) Prefixes(key string) iter.Seq2[string, V] { + return func(yield func(string, V) bool) { + if t.impl == nil { + return + } - return prefix, t.values[n] + adapt := func(prefix string, index int) bool { + return yield(prefix, t.values[index]) + } + + // No implementation of impl will ever cause adapt to escape. This + // avoids a heap allocation. + adapt = *unsafex.NoEscape(&adapt) + t.impl.search(key, adapt) + } } // Insert adds a new value to this trie. diff --git a/internal/trie/trie_test.go b/internal/trie/trie_test.go index 7fcd3b66..5b1cc789 100644 --- a/internal/trie/trie_test.go +++ b/internal/trie/trie_test.go @@ -15,11 +15,13 @@ package trie_test import ( + "slices" "strings" "testing" "github.com/stretchr/testify/assert" + "github.com/bufbuild/protocompile/internal/ext/iterx" "github.com/bufbuild/protocompile/internal/trie" ) @@ -29,17 +31,17 @@ func TestTrie(t *testing.T) { tests := []struct { data []string keys []string - want []int + want [][]int }{ { data: []string{"fo", "foo", "ba", "bar", "baz"}, keys: []string{"fo", "foo", "ba", "bar", "baz"}, - want: []int{1, 2, 3, 4, 5}, + want: [][]int{{0}, {0, 1}, {2}, {2, 3}, {2, 4}}, }, { data: []string{"fo", "foo", "ba", "bar", "baz"}, keys: []string{"f", "fooo", "barr", "bazr", "baar"}, - want: []int{0, 2, 4, 5, 3}, + want: [][]int{nil, {0, 1}, {2, 3}, {2, 4}, {2}}, }, } @@ -49,13 +51,12 @@ func TestTrie(t *testing.T) { trie := new(trie.Trie[int]) for i, s := range test.data { - trie.Insert(s, i+1) + trie.Insert(s, i) } t.Log(trie.Dump()) for i, key := range test.keys { - _, v := trie.Get(key) - assert.Equal(t, test.want[i], v, "#%d", i) + assert.Equal(t, test.want[i], slices.Collect(iterx.Right(trie.Prefixes(key))), "#%d", i) } }) } @@ -72,7 +73,8 @@ func TestHammerTrie(t *testing.T) { t.Log(trie.Dump()) for i := range 1000 { - _, v := trie.Get(strings.Repeat("a", i)) - assert.Equal(t, i+1, v) + k := strings.Repeat("a", i) + _, v := trie.Get(k) + assert.Equal(t, i+1, v, len(k)) } }