generated from TBD54566975/tbd-project-template
-
Notifications
You must be signed in to change notification settings - Fork 5
/
parser.go
198 lines (174 loc) 路 5.41 KB
/
parser.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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
package schema
import (
"fmt"
"io"
"reflect"
"strings"
"github.com/alecthomas/participle/v2"
"github.com/alecthomas/participle/v2/lexer"
"google.golang.org/protobuf/proto"
)
var (
declUnion = []Decl{&Data{}, &Verb{}, &Database{}, &Enum{}, &Config{}, &Secret{}}
nonOptionalTypeUnion = []Type{
&Int{}, &Float{}, &String{}, &Bytes{}, &Bool{}, &Time{}, &Array{},
&Map{}, &Any{}, &Unit{},
// Note: any types resolved by identifier (eg. "Any", "Unit", etc.) must
// be prior to Ref.
&Ref{},
}
typeUnion = append(nonOptionalTypeUnion, &Optional{})
metadataUnion = []Metadata{&MetadataCalls{}, &MetadataIngress{}, &MetadataCronJob{}, &MetadataDatabases{}, &MetadataAlias{}}
ingressUnion = []IngressPathComponent{&IngressPathLiteral{}, &IngressPathParameter{}}
valueUnion = []Value{&StringValue{}, &IntValue{}, &TypeValue{}}
// Used by protobuf generation.
unions = map[reflect.Type][]reflect.Type{
reflect.TypeOf((*Type)(nil)).Elem(): reflectUnion(typeUnion...),
reflect.TypeOf((*Metadata)(nil)).Elem(): reflectUnion(metadataUnion...),
reflect.TypeOf((*IngressPathComponent)(nil)).Elem(): reflectUnion(ingressUnion...),
reflect.TypeOf((*Decl)(nil)).Elem(): reflectUnion(declUnion...),
reflect.TypeOf((*Value)(nil)).Elem(): reflectUnion(valueUnion...),
}
Lexer = lexer.MustSimple([]lexer.SimpleRule{
{Name: "Whitespace", Pattern: `\s+`},
{Name: "Ident", Pattern: `\b[a-zA-Z_][a-zA-Z0-9_]*\b`},
{Name: "Comment", Pattern: `//.*`},
{Name: "String", Pattern: `"(?:\\.|[^"])*"`},
{Name: "Number", Pattern: `[0-9]+(?:\.[0-9]+)?`},
{Name: "Punct", Pattern: `[%/\-\_:[\]{}<>()*+?.,\\^$|#~!\'@=]`},
})
commonParserOptions = []participle.Option{
participle.Lexer(Lexer),
participle.Elide("Whitespace"),
participle.Unquote(),
participle.UseLookahead(2),
participle.Map(func(token lexer.Token) (lexer.Token, error) {
token.Value = strings.TrimSpace(strings.TrimPrefix(token.Value, "//"))
return token, nil
}, "Comment"),
participle.Union(metadataUnion...),
participle.Union(ingressUnion...),
participle.Union(declUnion...),
participle.Union(valueUnion...),
}
// Parser options for every parser _except_ the type parser.
parserOptions = append(commonParserOptions, participle.ParseTypeWith(parseType))
parser = participle.MustBuild[Schema](parserOptions...)
moduleParser = participle.MustBuild[Module](parserOptions...)
typeParser = participle.MustBuild[typeParserGrammar](append(commonParserOptions, participle.Union(nonOptionalTypeUnion...))...)
refParser = participle.MustBuild[Ref](parserOptions...)
)
type Position struct {
Filename string `protobuf:"1"`
Offset int `parser:"" protobuf:"-"`
Line int `protobuf:"2"`
Column int `protobuf:"3"`
}
func (p Position) String() string {
if p.Filename == "" {
return fmt.Sprintf("%d:%d", p.Line, p.Column)
}
return fmt.Sprintf("%s:%d:%d", p.Filename, p.Line, p.Column)
}
func (p Position) ToProto() proto.Message { return posToProto(p) }
// A Node in the schema grammar.
//
//sumtype:decl
type Node interface {
String() string
ToProto() proto.Message
Position() Position
// schemaChildren returns the children of this node.
schemaChildren() []Node
}
// Type represents a Type Node in the schema grammar.
//
//sumtype:decl
type Type interface {
Node
// schemaType is a marker to ensure that all sqltypes implement the Type interface.
schemaType()
}
// Metadata represents a metadata Node in the schema grammar.
//
//sumtype:decl
type Metadata interface {
Node
schemaMetadata()
}
// Value represents a value Node in the schema grammar.
//
//sumtype:decl
type Value interface {
Node
GetValue() any
schemaValueType() Type
}
// Symbol represents a symbol in the schema grammar.
//
// A Symbol is a named type that can be referenced by other types. This includes
// user defined data types such as data structures and enums, and builtin types.
//
//sumtype:decl
type Symbol interface {
Node
schemaSymbol()
}
// A Named symbol in the grammar.
type Named interface {
Symbol
GetName() string
}
// Decl represents user-defined data types in the schema grammar.
//
//sumtype:decl
type Decl interface {
Symbol
GetName() string
IsExported() bool
schemaDecl()
}
// We have a separate parser for types because Participle doesn't support left
// recursion and "Type = Type ? | Int | String ..." is left recursive.
type typeParserGrammar struct {
Type Type `parser:"@@"`
Optional bool `parser:"@'?'?"`
}
func parseType(pl *lexer.PeekingLexer) (Type, error) {
typ, err := typeParser.ParseFromLexer(pl, participle.AllowTrailing(true))
if err != nil {
return nil, err
}
if typ.Optional {
return &Optional{Type: typ.Type}, nil
}
return typ.Type, nil
}
func ParseString(filename, input string) (*Schema, error) {
mod, err := parser.ParseString(filename, input)
if err != nil {
return nil, err
}
return ValidateSchema(mod)
}
func ParseModuleString(filename, input string) (*Module, error) {
mod, err := moduleParser.ParseString(filename, input)
if err != nil {
return nil, err
}
return mod, ValidateModule(mod)
}
func Parse(filename string, r io.Reader) (*Schema, error) {
mod, err := parser.Parse(filename, r)
if err != nil {
return nil, err
}
return ValidateSchema(mod)
}
func ParseModule(filename string, r io.Reader) (*Module, error) {
mod, err := moduleParser.Parse(filename, r)
if err != nil {
return nil, err
}
return mod, ValidateModule(mod)
}