Fold by-category benchmark tests into parser_test.go
diff --git a/parser/BUILD.bazel b/parser/BUILD.bazel
index 97bc9bd..4a10284 100644
--- a/parser/BUILD.bazel
+++ b/parser/BUILD.bazel
@@ -48,6 +48,7 @@
     deps = [
         "//common/ast:go_default_library",
         "//common/debug:go_default_library",
+        "//common/operators:go_default_library",
         "//common/types:go_default_library",
         "//parser/gen:go_default_library",
         "//test:go_default_library",
diff --git a/parser/bench/BUILD.bazel b/parser/bench/BUILD.bazel
deleted file mode 100644
index e97556f..0000000
--- a/parser/bench/BUILD.bazel
+++ /dev/null
@@ -1,41 +0,0 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
-
-package(
-    licenses = ["notice"],  # Apache 2.0
-)
-
-go_library(
-    name = "go_default_library",
-    srcs = [
-        "bench.go",
-    ],
-    importpath = "github.com/google/cel-go/parser/bench",
-    visibility = ["//visibility:public"],
-    deps = [
-        "//common:go_default_library",
-        "//common/ast:go_default_library",
-        "//common/operators:go_default_library",
-        "//common/types:go_default_library",
-        "//parser:go_default_library",
-    ],
-)
-
-go_test(
-    name = "bench_test",
-    size = "small",
-    srcs = [
-        "bench_test.go",
-    ],
-    embed = [
-        ":go_default_library",
-    ],
-    deps = [
-        "//common:go_default_library",
-        "//parser:go_default_library",
-    ],
-)
-
-alias(
-    name = "go_default_test",
-    actual = ":bench_test",
-)
diff --git a/parser/bench/bench.go b/parser/bench/bench.go
deleted file mode 100644
index 1d03fc9..0000000
--- a/parser/bench/bench.go
+++ /dev/null
@@ -1,173 +0,0 @@
-// Copyright 2026 Google LLC
-//
-// 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 bench defines benchmark test cases and utilities for CEL parsers.
-package bench
-
-import (
-	"strings"
-
-	"github.com/google/cel-go/common"
-	"github.com/google/cel-go/common/ast"
-	"github.com/google/cel-go/common/operators"
-	"github.com/google/cel-go/common/types"
-	"github.com/google/cel-go/parser"
-)
-
-// ParseResult indicates whether a parse is expected to succeed or fail.
-type ParseResult int
-
-const (
-	// ParseResultSuccess indicates an expression that is expected to parse without error.
-	ParseResultSuccess ParseResult = iota
-	// ParseResultError indicates an expression that is expected to produce parse error(s).
-	ParseResultError
-)
-
-// TestCase represents an expression to parse and the expected parse result.
-type TestCase struct {
-	Expr   string
-	Result ParseResult
-}
-
-// ErrorCase returns a TestCase expecting a parse error.
-func ErrorCase(expr string) TestCase {
-	return TestCase{
-		Expr:   expr,
-		Result: ParseResultError,
-	}
-}
-
-// SuccessCase returns a TestCase expecting successful parsing.
-func SuccessCase(expr string) TestCase {
-	return TestCase{
-		Expr:   expr,
-		Result: ParseResultSuccess,
-	}
-}
-
-// Category represents a named group of test cases for benchmarking and verification.
-type Category struct {
-	Name  string
-	Cases []TestCase
-}
-
-// OptMapMacro expands `m.optMap(v, f)` into a conditional comprehension.
-var OptMapMacro = parser.NewReceiverMacro("optMap", 2, optMapExpander)
-
-func optMapExpander(meh parser.ExprHelper, target ast.Expr, args []ast.Expr) (ast.Expr, *common.Error) {
-	varIdent := args[0]
-	varName := ""
-	switch varIdent.Kind() {
-	case ast.IdentKind:
-		varName = varIdent.AsIdent()
-	default:
-		return nil, meh.NewError(varIdent.ID(), "optMap() variable name must be a simple identifier")
-	}
-	mapExpr := args[1]
-	return meh.NewCall(
-		operators.Conditional,
-		meh.NewMemberCall("hasValue", target),
-		meh.NewCall("optional.of",
-			meh.NewComprehension(
-				meh.NewList(),
-				"#unused",
-				varName,
-				meh.NewMemberCall("value", meh.Copy(target)),
-				meh.NewLiteral(types.False),
-				meh.NewIdent(varName),
-				mapExpr,
-			),
-		),
-		meh.NewCall("optional.none"),
-	), nil
-}
-
-// GetCategories returns benchmark and correctness test cases organized by category.
-func GetCategories() []Category {
-	return categories
-}
-
-// GetTestCases returns benchmark and correctness test cases flattened across all categories.
-func GetTestCases() []TestCase {
-	var allCases []TestCase
-	for _, cat := range categories {
-		allCases = append(allCases, cat.Cases...)
-	}
-	return allCases
-}
-
-var categories = []Category{
-	// Simple: common, representative CEL expressions covering basic syntax, operators, calls, and literals
-	{
-		Name: "Simple",
-		Cases: []TestCase{
-			SuccessCase("x * 2 + y / 3"),
-			SuccessCase(`foo.bar.baz(1, 2, "abc")`),
-			SuccessCase(`a > 5 && b < 10 || c == "xyz"`),
-			SuccessCase("x ? y : z"),
-			SuccessCase(`{"foo": 1, "bar": [2, 3]}`),
-			SuccessCase("a[b]"),
-			SuccessCase("a.b.c"),
-			SuccessCase("a.`b-c`"),
-			SuccessCase("\"\\a\\b\\f\\n\\r\\t\\v'\\\"\\\\ Legal escapes \\u2764\""),
-		},
-	},
-
-	// Complex: expressions with deep chaining, nesting, precedence, and complex structures
-	{
-		Name: "Complex",
-		Cases: []TestCase{
-			SuccessCase("a" + strings.Repeat(" + a", 49)),
-			SuccessCase("a" + strings.Repeat(" || a", 49)),
-			SuccessCase("a" + strings.Repeat(".f", 49)),
-			SuccessCase(strings.Repeat("(", 20) + "a" + strings.Repeat(")", 20)),
-			SuccessCase(`SomeMessage{foo: 5, bar: "xyz"}`),
-			SuccessCase("1 + 2 * 3 - 1 / 2 == 6 % 1"),
-			SuccessCase("[] + [1, 2, 3] + [4]"),
-		},
-	},
-
-	// Macros: standard and receiver comprehension macros, optional syntax traversal
-	{
-		Name: "Macros",
-		Cases: []TestCase{
-			SuccessCase("has(m.f)"),
-			SuccessCase("[1, 2, 3].all(x, x > 0)"),
-			SuccessCase("m.map(v, v * 2)"),
-			SuccessCase("m.filter(v, v > 0)"),
-			SuccessCase("m.exists_one(v, v == 1)"),
-			SuccessCase("x.filter(y, y.exists(z, has(z.a)))"),
-			SuccessCase("a.?b[?0] && a[?c]"),
-			SuccessCase("m.optMap(v, v + 1)"),
-		},
-	},
-
-	// Errors: representative syntax errors, invalid tokens, keywords, and unclosed delimiters
-	{
-		Name: "Errors",
-		Cases: []TestCase{
-			ErrorCase("x * 2 + y /"),
-			ErrorCase(`foo.bar.baz(1, 2, "abc"`),
-			ErrorCase("a > 5 && && b < 10"),
-			ErrorCase(`{"foo": 1, "bar": [2, 3`),
-			ErrorCase("1 + $"),
-			ErrorCase("break"),
-			ErrorCase(`"\xFh"`),
-			ErrorCase("a" + strings.Repeat(" + a", 49) + " +"),
-			ErrorCase(strings.Repeat("(", 20) + "a"),
-			ErrorCase("f(*" + strings.Repeat(", *", 9) + ")"),
-		},
-	},
-}
diff --git a/parser/bench/bench_test.go b/parser/bench/bench_test.go
deleted file mode 100644
index d016d09..0000000
--- a/parser/bench/bench_test.go
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright 2026 Google LLC
-//
-// 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 bench
-
-import (
-	"fmt"
-	"testing"
-
-	"github.com/google/cel-go/common"
-	"github.com/google/cel-go/parser"
-)
-
-func newBenchmarkParser(tb testing.TB) *parser.Parser {
-	tb.Helper()
-	p, err := parser.NewParser(
-		parser.Macros(append(parser.AllMacros, OptMapMacro)...),
-		parser.EnableOptionalSyntax(true),
-		parser.EnableIdentEscapeSyntax(true),
-		parser.MaxRecursionDepth(512),
-	)
-	if err != nil {
-		tb.Fatalf("parser.NewParser() failed: %v", err)
-	}
-	return p
-}
-
-func TestExpectedResult(t *testing.T) {
-	p := newBenchmarkParser(t)
-	for _, cat := range GetCategories() {
-		t.Run(cat.Name, func(t *testing.T) {
-			for i, tc := range cat.Cases {
-				t.Run(fmt.Sprintf("%d_%s", i, tc.Expr), func(t *testing.T) {
-					src := common.NewTextSource(tc.Expr)
-					_, errs := p.Parse(src)
-					hasErr := len(errs.GetErrors()) > 0
-					switch tc.Result {
-					case ParseResultSuccess:
-						if hasErr {
-							t.Errorf("p.Parse(%q) failed unexpectedly: %v", tc.Expr, errs.ToDisplayString())
-						}
-					case ParseResultError:
-						if !hasErr {
-							t.Errorf("p.Parse(%q) succeeded unexpectedly, wanted error", tc.Expr)
-						}
-					}
-				})
-			}
-		})
-	}
-}
-
-// BenchmarkParse benchmarks parsing organized by workload categories.
-func BenchmarkParse(b *testing.B) {
-	p := newBenchmarkParser(b)
-	for _, cat := range GetCategories() {
-		b.Run(cat.Name, func(b *testing.B) {
-			b.ResetTimer()
-			for i := 0; i < b.N; i++ {
-				for _, tc := range cat.Cases {
-					src := common.NewTextSource(tc.Expr)
-					_, errs := p.Parse(src)
-					hasErr := len(errs.GetErrors()) > 0
-					expectedErr := tc.Result == ParseResultError
-					if hasErr != expectedErr {
-						b.Fatalf("p.Parse(%q) got error: %v, expected error: %v", tc.Expr, hasErr, expectedErr)
-					}
-				}
-			}
-		})
-	}
-}
-
-// BenchmarkParseParallel benchmarks parsing concurrently across goroutines by category.
-func BenchmarkParseParallel(b *testing.B) {
-	p := newBenchmarkParser(b)
-	for _, cat := range GetCategories() {
-		b.Run(cat.Name, func(b *testing.B) {
-			b.ResetTimer()
-			b.RunParallel(func(pb *testing.PB) {
-				for pb.Next() {
-					for _, tc := range cat.Cases {
-						src := common.NewTextSource(tc.Expr)
-						_, errs := p.Parse(src)
-						hasErr := len(errs.GetErrors()) > 0
-						expectedErr := tc.Result == ParseResultError
-						if hasErr != expectedErr {
-							b.Fatalf("p.Parse(%q) got error: %v, expected error: %v", tc.Expr, hasErr, expectedErr)
-						}
-					}
-				}
-			})
-		})
-	}
-}
diff --git a/parser/parser_test.go b/parser/parser_test.go
index 88527d8..4a453a1 100644
--- a/parser/parser_test.go
+++ b/parser/parser_test.go
@@ -24,6 +24,7 @@
 	"github.com/google/cel-go/common"
 	"github.com/google/cel-go/common/ast"
 	"github.com/google/cel-go/common/debug"
+	"github.com/google/cel-go/common/operators"
 	"github.com/google/cel-go/common/types"
 	"github.com/google/cel-go/test"
 )
@@ -2393,6 +2394,248 @@
 	})
 }
 
+type benchTestInfo struct {
+	// I contains the input expression to be parsed.
+	I string
+
+	// E indicates whether an error is expected.
+	E bool
+}
+
+type benchCategory struct {
+	name  string
+	cases []benchTestInfo
+}
+
+var benchCategories = []benchCategory{
+	// Simple: common, representative CEL expressions covering basic syntax, operators, calls, and literals
+	{
+		name: "Simple",
+		cases: []benchTestInfo{
+			{
+				I: "x * 2 + y / 3",
+			},
+			{
+				I: `foo.bar.baz(1, 2, "abc")`,
+			},
+			{
+				I: `a > 5 && b < 10 || c == "xyz"`,
+			},
+			{
+				I: "x ? y : z",
+			},
+			{
+				I: `{"foo": 1, "bar": [2, 3]}`,
+			},
+			{
+				I: "a[b]",
+			},
+			{
+				I: "a.b.c",
+			},
+			{
+				I: "a.`b-c`",
+			},
+			{
+				I: "\"\\a\\b\\f\\n\\r\\t\\v'\\\"\\\\ Legal escapes \\u2764\"",
+			},
+		},
+	},
+
+	// Complex: expressions with deep chaining, nesting, precedence, and complex structures
+	{
+		name: "Complex",
+		cases: []benchTestInfo{
+			{
+				I: "a" + strings.Repeat(" + a", 49),
+			},
+			{
+				I: "a" + strings.Repeat(" || a", 49),
+			},
+			{
+				I: "a" + strings.Repeat(".f", 49),
+			},
+			{
+				I: strings.Repeat("(", 20) + "a" + strings.Repeat(")", 20),
+			},
+			{
+				I: `SomeMessage{foo: 5, bar: "xyz"}`,
+			},
+			{
+				I: "1 + 2 * 3 - 1 / 2 == 6 % 1",
+			},
+			{
+				I: "[] + [1, 2, 3] + [4]",
+			},
+		},
+	},
+
+	// Macros: standard and receiver comprehension macros, optional syntax traversal
+	{
+		name: "Macros",
+		cases: []benchTestInfo{
+			{
+				I: "has(m.f)",
+			},
+			{
+				I: "[1, 2, 3].all(x, x > 0)",
+			},
+			{
+				I: "m.map(v, v * 2)",
+			},
+			{
+				I: "m.filter(v, v > 0)",
+			},
+			{
+				I: "m.exists_one(v, v == 1)",
+			},
+			{
+				I: "x.filter(y, y.exists(z, has(z.a)))",
+			},
+			{
+				I: "a.?b[?0] && a[?c]",
+			},
+			{
+				I: "m.optMap(v, v + 1)",
+			},
+		},
+	},
+
+	// Errors: representative syntax errors, invalid tokens, keywords, and unclosed delimiters
+	{
+		name: "Errors",
+		cases: []benchTestInfo{
+			{
+				I: "x * 2 + y /",
+				E: true,
+			},
+			{
+				I: `foo.bar.baz(1, 2, "abc"`,
+				E: true,
+			},
+			{
+				I: "a > 5 && && b < 10",
+				E: true,
+			},
+			{
+				I: `{"foo": 1, "bar": [2, 3`,
+				E: true,
+			},
+			{
+				I: "1 + $",
+				E: true,
+			},
+			{
+				I: "break",
+				E: true,
+			},
+			{
+				I: `"\xFh"`,
+				E: true,
+			},
+			{
+				I: "a" + strings.Repeat(" + a", 49) + " +",
+				E: true,
+			},
+			{
+				I: strings.Repeat("(", 20) + "a",
+				E: true,
+			},
+			{
+				I: "f(*" + strings.Repeat(", *", 9) + ")",
+				E: true,
+			},
+		},
+	},
+}
+
+// BenchmarkByCategory benchmarks parsing organized by workload categories.
+func BenchmarkByCategory(b *testing.B) {
+	p := newBenchmarkCategoryParser(b)
+	for _, cat := range benchCategories {
+		b.Run(cat.name, func(b *testing.B) {
+			b.ResetTimer()
+			for i := 0; i < b.N; i++ {
+				for _, tc := range cat.cases {
+					src := common.NewTextSource(tc.I)
+					_, errs := p.Parse(src)
+					hasErr := len(errs.GetErrors()) > 0
+					if hasErr != tc.E {
+						b.Fatalf("p.Parse(%q) got error: %v, expected error: %v", tc.I, hasErr, tc.E)
+					}
+				}
+			}
+		})
+	}
+}
+
+// BenchmarkParallelByCategory benchmarks parsing concurrently across goroutines by category.
+func BenchmarkParallelByCategory(b *testing.B) {
+	p := newBenchmarkCategoryParser(b)
+	for _, cat := range benchCategories {
+		b.Run(cat.name, func(b *testing.B) {
+			b.ResetTimer()
+			b.RunParallel(func(pb *testing.PB) {
+				for pb.Next() {
+					for _, tc := range cat.cases {
+						src := common.NewTextSource(tc.I)
+						_, errs := p.Parse(src)
+						hasErr := len(errs.GetErrors()) > 0
+						if hasErr != tc.E {
+							b.Fatalf("p.Parse(%q) got error: %v, expected error: %v", tc.I, hasErr, tc.E)
+						}
+					}
+				}
+			})
+		})
+	}
+}
+
+// optMapMacro expands `m.optMap(v, f)` into a conditional comprehension.
+var optMapMacro = NewReceiverMacro("optMap", 2, optMapExpander)
+
+func optMapExpander(meh ExprHelper, target ast.Expr, args []ast.Expr) (ast.Expr, *common.Error) {
+	varIdent := args[0]
+	varName := ""
+	switch varIdent.Kind() {
+	case ast.IdentKind:
+		varName = varIdent.AsIdent()
+	default:
+		return nil, meh.NewError(varIdent.ID(), "optMap() variable name must be a simple identifier")
+	}
+	mapExpr := args[1]
+	return meh.NewCall(
+		operators.Conditional,
+		meh.NewMemberCall("hasValue", target),
+		meh.NewCall("optional.of",
+			meh.NewComprehension(
+				meh.NewList(),
+				"#unused",
+				varName,
+				meh.NewMemberCall("value", meh.Copy(target)),
+				meh.NewLiteral(types.False),
+				meh.NewIdent(varName),
+				mapExpr,
+			),
+		),
+		meh.NewCall("optional.none"),
+	), nil
+}
+
+func newBenchmarkCategoryParser(tb testing.TB) *Parser {
+	tb.Helper()
+	p, err := NewParser(
+		Macros(append(AllMacros, optMapMacro)...),
+		EnableOptionalSyntax(true),
+		EnableIdentEscapeSyntax(true),
+		MaxRecursionDepth(512),
+	)
+	if err != nil {
+		tb.Fatalf("NewParser() failed: %v", err)
+	}
+	return p
+}
+
 func TestParseErrorData(t *testing.T) {
 	p := newTestParser(t)
 	src := common.NewTextSource(`a.?b`)