1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package runtime
16
17 import (
18 "cuelang.org/go/cue/ast"
19 "cuelang.org/go/cue/build"
20 "cuelang.org/go/cue/errors"
21 "cuelang.org/go/cue/format"
22 "cuelang.org/go/cue/token"
23 "cuelang.org/go/internal"
24 "cuelang.org/go/internal/core/adt"
25 "cuelang.org/go/internal/core/walk"
26 )
27
28
29
30 func (r *Runtime) SetInterpreter(i Interpreter) {
31 if r.interpreters == nil {
32 r.interpreters = map[string]Interpreter{}
33 }
34 r.interpreters[i.Kind()] = i
35 }
36
37
38
39
40
41 type Interpreter interface {
42
43 NewCompiler(b *build.Instance, r *Runtime) (Compiler, errors.Error)
44
45
46 Kind() string
47 }
48
49
50 type Compiler interface {
51
52
53
54
55
56
57
58 Compile(name string, scope adt.Value, a *internal.Attr) (adt.Expr, errors.Error)
59 }
60
61
62
63 func (r *Runtime) injectImplementations(b *build.Instance, v *adt.Vertex) (errs errors.Error) {
64 if r.interpreters == nil {
65 return nil
66 }
67
68 d := &externDecorator{
69 runtime: r,
70 pkg: b,
71 }
72
73 for _, f := range b.Files {
74 d.errs = errors.Append(d.errs, d.addFile(f))
75 }
76
77 for _, c := range v.Conjuncts {
78 d.decorateConjunct(c.Elem(), v)
79 }
80
81 return d.errs
82 }
83
84
85
86
87
88
89
90
91 type externDecorator struct {
92 runtime *Runtime
93 pkg *build.Instance
94
95 compilers map[string]Compiler
96 fields map[*ast.Field]fieldInfo
97
98 errs errors.Error
99 }
100
101 type fieldInfo struct {
102 file *ast.File
103 extern string
104 funcName string
105 attrBody string
106 attr *ast.Attribute
107 }
108
109
110
111 func (d *externDecorator) addFile(f *ast.File) (errs errors.Error) {
112 kind, pos, decls, err := findExternFileAttr(f)
113 if len(decls) == 0 {
114 return err
115 }
116
117 ok, err := d.initCompiler(kind, pos)
118 if !ok {
119 return err
120 }
121
122 return d.markExternFieldAttr(kind, decls)
123 }
124
125
126
127
128
129
130 func findExternFileAttr(f *ast.File) (kind string, pos token.Pos, decls []ast.Decl, err errors.Error) {
131 var (
132 hasPkg bool
133 p int
134 fileAttr *ast.Attribute
135 )
136
137 loop:
138 for ; p < len(f.Decls); p++ {
139 switch a := f.Decls[p].(type) {
140 case *ast.Package:
141 hasPkg = true
142 break loop
143
144 case *ast.Attribute:
145 pos = a.Pos()
146 key, body := a.Split()
147 if key != "extern" {
148 continue
149 }
150 fileAttr = a
151
152 attr := internal.ParseAttrBody(a.Pos(), body)
153 if attr.Err != nil {
154 return "", pos, nil, attr.Err
155 }
156 k, err := attr.String(0)
157 if err != nil {
158
159 return "", pos, nil, errors.Newf(a.Pos(), "%s", err)
160 }
161
162 if k == "" {
163 return "", pos, nil, errors.Newf(a.Pos(),
164 "interpreter name must be non-empty")
165 }
166
167 if kind != "" {
168 return "", pos, nil, errors.Newf(a.Pos(),
169 "only one file-level extern attribute allowed per file")
170
171 }
172 kind = k
173 }
174 }
175
176 switch {
177 case fileAttr == nil && !hasPkg:
178
179 return "", pos, nil, nil
180
181 case fileAttr != nil && !hasPkg:
182 return "", pos, nil, errors.Newf(fileAttr.Pos(),
183 "extern attribute without package clause")
184
185 case fileAttr == nil && hasPkg:
186
187 for p++; p < len(f.Decls); p++ {
188 x, ok := f.Decls[p].(*ast.Attribute)
189 if !ok {
190 continue
191 }
192 if key, _ := x.Split(); key == "extern" {
193 err = errors.Append(err, errors.Newf(x.Pos(),
194 "extern attribute must appear before package clause"))
195 }
196 }
197 return "", pos, nil, err
198 }
199
200 return kind, pos, f.Decls[p:], nil
201 }
202
203
204
205 func (d *externDecorator) initCompiler(kind string, pos token.Pos) (ok bool, err errors.Error) {
206 if c, ok := d.compilers[kind]; ok {
207 return c != nil, nil
208 }
209
210
211 if d.compilers == nil {
212 d.compilers = map[string]Compiler{}
213 d.fields = map[*ast.Field]fieldInfo{}
214 }
215
216 x := d.runtime.interpreters[kind]
217 if x == nil {
218 return false, errors.Newf(pos, "no interpreter defined for %q", kind)
219 }
220
221 c, err := x.NewCompiler(d.pkg, d.runtime)
222 if err != nil {
223 return false, err
224 }
225
226 d.compilers[kind] = c
227
228 return c != nil, nil
229 }
230
231
232
233
234
235
236
237
238 func (d *externDecorator) markExternFieldAttr(kind string, decls []ast.Decl) (errs errors.Error) {
239 var fieldStack []*ast.Field
240
241 ast.Walk(&ast.File{Decls: decls}, func(n ast.Node) bool {
242 switch x := n.(type) {
243 case *ast.Field:
244 fieldStack = append(fieldStack, x)
245
246 case *ast.Attribute:
247 key, body := x.Split()
248 if key != "extern" {
249 break
250 }
251
252 lastField := len(fieldStack) - 1
253 if lastField < 0 {
254 errs = errors.Append(errs, errors.Newf(x.Pos(),
255 "extern attribute not associated with field"))
256 return true
257 }
258
259 f := fieldStack[lastField]
260
261 if _, ok := d.fields[f]; ok {
262 errs = errors.Append(errs, errors.Newf(x.Pos(),
263 "duplicate extern attributes"))
264 return true
265 }
266
267 name, isIdent, err := ast.LabelName(f.Label)
268 if err != nil || !isIdent {
269 b, _ := format.Node(f.Label)
270 errs = errors.Append(errs, errors.Newf(x.Pos(),
271 "can only define functions for fields with identifier names, found %v", string(b)))
272 return true
273 }
274
275 d.fields[f] = fieldInfo{
276 extern: kind,
277 funcName: name,
278 attrBody: body,
279 attr: x,
280 }
281 }
282
283 return true
284
285 }, func(n ast.Node) {
286 switch n.(type) {
287 case *ast.Field:
288 fieldStack = fieldStack[:len(fieldStack)-1]
289 }
290 })
291
292 return errs
293 }
294
295 func (d *externDecorator) decorateConjunct(e adt.Elem, scope *adt.Vertex) {
296 w := walk.Visitor{Before: func(n adt.Node) bool {
297 return d.processADTNode(n, scope)
298 }}
299 w.Elem(e)
300 }
301
302
303
304 func (d *externDecorator) processADTNode(n adt.Node, scope *adt.Vertex) bool {
305 f, ok := n.(*adt.Field)
306 if !ok {
307 return true
308 }
309
310 info, ok := d.fields[f.Src]
311 if !ok {
312 return true
313 }
314
315 c, ok := d.compilers[info.extern]
316 if !ok {
317
318
319 return true
320 }
321
322 attr := internal.ParseAttrBody(info.attr.Pos(), info.attrBody)
323 if attr.Err != nil {
324 d.errs = errors.Append(d.errs, attr.Err)
325 return true
326 }
327 name := info.funcName
328 if str, ok, _ := attr.Lookup(1, "name"); ok {
329 name = str
330 }
331
332 b, err := c.Compile(name, scope, &attr)
333 if err != nil {
334 err = errors.Newf(info.attr.Pos(), "can't load from external module: %v", err)
335 d.errs = errors.Append(d.errs, err)
336 return true
337 }
338
339 f.Value = &adt.BinaryExpr{
340 Op: adt.AndOp,
341 X: f.Value,
342 Y: b,
343 }
344
345 return true
346 }
347
View as plain text