forked from golang/perf
-
Notifications
You must be signed in to change notification settings - Fork 0
/
projection.go
134 lines (121 loc) · 3.15 KB
/
projection.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package parse
import (
"fmt"
"strings"
)
// A Field is one element in a projection expression. It represents
// extracting a single dimension of a benchmark result and applying an
// order to it.
type Field struct {
Key string
// Order is the sort order for this field. This can be
// "first", meaning to sort by order of first appearance;
// "fixed", meaning to use the explicit value order in Fixed;
// or a named sort order.
Order string
// Fixed gives the explicit value order for "fixed" ordering.
// If a record's value is not in this list, the record should
// be filtered out. Otherwise, values should be sorted
// according to their order in this list.
Fixed []string
// KeyOff and OrderOff give the byte offsets of the key and
// order, for error reporting.
KeyOff, OrderOff int
}
// String returns Projection as a valid projection expression.
func (p Field) String() string {
switch p.Order {
case "first":
return quoteWord(p.Key)
case "fixed":
words := make([]string, 0, len(p.Fixed))
for _, word := range p.Fixed {
words = append(words, quoteWord(word))
}
return fmt.Sprintf("%s@(%s)", quoteWord(p.Key), strings.Join(words, " "))
}
return fmt.Sprintf("%s@%s", quoteWord(p.Key), quoteWord(p.Order))
}
// ParseProjection parses a projection expression into a tuple of
// Fields.
func ParseProjection(q string) ([]Field, error) {
// Parse each projection field.
var fields []Field
toks := newTokenizer(q)
for {
// Peek at the next token.
tok, toks2 := toks.keyOrOp()
if tok.Kind == 0 {
// No more fields.
break
} else if tok.Kind == ',' && len(fields) > 0 {
// Consume optional separating comma.
toks = toks2
}
var f Field
f, toks = parseField(toks)
fields = append(fields, f)
}
toks.end()
if toks.errt.err != nil {
return nil, toks.errt.err
}
return fields, nil
}
func parseField(toks tokenizer) (Field, tokenizer) {
var f Field
// Consume key.
key, toks2 := toks.keyOrOp()
if !(key.Kind == 'w' || key.Kind == 'q') {
_, toks = toks.error("expected key")
return f, toks
}
toks = toks2
f.Key = key.Tok
f.KeyOff = key.Off
// Consume optional sort order.
f.Order = "first"
f.OrderOff = key.Off + len(key.Tok)
sep, toks2 := toks.keyOrOp()
if sep.Kind != '@' {
// No sort order.
return f, toks
}
toks = toks2
// Is it a named sort order?
order, toks2 := toks.keyOrOp()
f.OrderOff = order.Off
if order.Kind == 'w' || order.Kind == 'q' {
f.Order = order.Tok
return f, toks2
}
// Or a fixed sort order?
if order.Kind == '(' {
f.Order = "fixed"
toks = toks2
for {
t, toks2 := toks.keyOrOp()
if t.Kind == 'w' || t.Kind == 'q' {
toks = toks2
f.Fixed = append(f.Fixed, t.Tok)
} else if t.Kind == ')' {
if len(f.Fixed) == 0 {
_, toks = toks.error("nothing to match")
} else {
toks = toks2
}
break
} else {
_, toks = toks.error("missing )")
break
}
}
return f, toks
}
// Bad sort order syntax.
_, toks = toks.error("expected named sort order or parenthesized list")
return f, toks
}