/
tag.go
117 lines (102 loc) · 2.28 KB
/
tag.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
package binio
import (
"errors"
"fmt"
"strings"
"github.com/KlemensWinter/go-binio/expr"
"golang.org/x/exp/maps"
)
var (
ErrUnknownTagOption = errors.New("unknown tag option")
ErrInvalidTagOption = errors.New("invalid tag option")
)
var (
validKeywords = []string{
"type",
"size",
"if",
"ptrs",
}
// these must be lowercase
strDynArray = "dynarray"
strHoleyArray = "holeyarray"
strDynString = "dynstring"
)
type (
Tag struct {
Size expr.Expr
If expr.Expr
Ptrs expr.Expr
Vars map[string]expr.Expr
typ string
}
Type byte
)
func (t *Tag) IsDynArray() bool { return t.typ == strDynArray }
func (t *Tag) IsHoleyArray() bool { return t.typ == strHoleyArray }
func (t *Tag) IsDynString() bool { return t.typ == strDynString }
func (t *Tag) AddVar(name string, value expr.Expr) {
if t.Vars == nil {
t.Vars = make(map[string]expr.Expr)
}
t.Vars[name] = value
}
func (t *Tag) HasVar(name string) bool {
if t.Vars == nil {
return false
}
_, ok := t.Vars[name]
return ok
}
func (t *Tag) VarNames() []string {
if t.Vars == nil {
return nil
}
return maps.Keys(t.Vars)
}
func ParseTag(str string) (*Tag, error) {
var tg Tag
for _, str := range strings.Split(str, ",") {
s := strings.SplitN(str, "=", 2)
key := strings.TrimSpace(s[0])
value := strings.TrimSpace(s[1])
switch key {
case "type":
switch strings.ToLower(value) {
case strDynArray, strHoleyArray, strDynString:
tg.typ = strings.ToLower(value)
default:
panic(fmt.Errorf("invalid type %q", value))
}
case "size":
exp, err := expr.Parse(value)
if err != nil {
return nil, fmt.Errorf("failed to parse size: %w", err)
}
tg.Size = exp
case "if":
cond, err := expr.Parse(value)
if err != nil {
panic(fmt.Errorf("failed to parse condition %q: %w", value, err))
}
tg.If = cond // TODO: syntax check?
case "ptrs":
e, err := expr.Parse(value)
if err != nil {
return nil, fmt.Errorf("failed to parse ptrs: %w", err)
}
tg.Ptrs = e
default:
if strings.HasPrefix(key, "$") {
e, err := expr.Parse(value)
if err != nil {
return nil, fmt.Errorf("failed to parse variable %q: %w", key, err)
}
tg.AddVar(key[1:], e)
} else {
return nil, fmt.Errorf("%w: %q", ErrInvalidTagOption, key)
}
}
}
return &tg, nil
}