Marcel van Lohuizen | 1f42c81 | 2020-05-01 16:00:06 +0200 | [diff] [blame] | 1 | // Copyright 2020 CUE Authors |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | package compile |
| 16 | |
| 17 | import ( |
| 18 | "fmt" |
| 19 | "strings" |
| 20 | |
| 21 | "cuelang.org/go/cue/ast" |
| 22 | "cuelang.org/go/cue/errors" |
| 23 | "cuelang.org/go/cue/literal" |
| 24 | "cuelang.org/go/cue/token" |
| 25 | "cuelang.org/go/internal" |
| 26 | "cuelang.org/go/internal/core/adt" |
Marcel van Lohuizen | 1f42c81 | 2020-05-01 16:00:06 +0200 | [diff] [blame] | 27 | "golang.org/x/xerrors" |
| 28 | ) |
| 29 | |
| 30 | // Config configures a compilation. |
| 31 | type Config struct { |
Marcel van Lohuizen | 08a9fdb | 2020-06-10 11:07:13 +0200 | [diff] [blame] | 32 | // Scope specifies a node in which to look up unresolved references. This |
| 33 | // is useful for evaluating expressions within an already evaluated |
| 34 | // configuration. |
| 35 | // |
| 36 | // TODO |
| 37 | Scope *adt.Vertex |
Marcel van Lohuizen | cf94469 | 2020-07-15 15:28:47 +0200 | [diff] [blame] | 38 | |
| 39 | // Imports allows unresolved identifiers to resolve to imports. |
| 40 | // |
| 41 | // Under normal circumstances, identifiers bind to import specifications, |
| 42 | // which get resolved to an ImportReference. Use this option to automaically |
| 43 | // resolve identifiers to imports. |
| 44 | Imports func(x *ast.Ident) (pkgPath string) |
Marcel van Lohuizen | 1f42c81 | 2020-05-01 16:00:06 +0200 | [diff] [blame] | 45 | } |
| 46 | |
| 47 | // Files compiles the given files as a single instance. It disregards |
| 48 | // the package names and it is the responsibility of the user to verify that |
| 49 | // the packages names are consistent. |
| 50 | // |
| 51 | // Files may return a completed parse even if it has errors. |
Marcel van Lohuizen | 0f935f8 | 2020-06-09 09:13:01 +0200 | [diff] [blame] | 52 | func Files(cfg *Config, r adt.Runtime, files ...*ast.File) (*adt.Vertex, errors.Error) { |
Marcel van Lohuizen | 0cfe411 | 2020-06-27 15:01:34 +0200 | [diff] [blame] | 53 | c := newCompiler(cfg, r) |
Marcel van Lohuizen | 1f42c81 | 2020-05-01 16:00:06 +0200 | [diff] [blame] | 54 | |
| 55 | v := c.compileFiles(files) |
| 56 | |
| 57 | if c.errs != nil { |
| 58 | return v, c.errs |
| 59 | } |
| 60 | return v, nil |
| 61 | } |
| 62 | |
Marcel van Lohuizen | 08a9fdb | 2020-06-10 11:07:13 +0200 | [diff] [blame] | 63 | func Expr(cfg *Config, r adt.Runtime, x ast.Expr) (adt.Conjunct, errors.Error) { |
| 64 | if cfg == nil { |
| 65 | cfg = &Config{} |
| 66 | } |
| 67 | c := &compiler{ |
| 68 | Config: *cfg, |
| 69 | index: r, |
| 70 | } |
| 71 | |
| 72 | v := c.compileExpr(x) |
| 73 | |
| 74 | if c.errs != nil { |
| 75 | return v, c.errs |
| 76 | } |
| 77 | return v, nil |
| 78 | } |
| 79 | |
Marcel van Lohuizen | 0cfe411 | 2020-06-27 15:01:34 +0200 | [diff] [blame] | 80 | func newCompiler(cfg *Config, r adt.Runtime) *compiler { |
| 81 | c := &compiler{ |
| 82 | index: r, |
| 83 | } |
| 84 | if cfg != nil { |
| 85 | c.Config = *cfg |
| 86 | } |
| 87 | return c |
| 88 | } |
| 89 | |
Marcel van Lohuizen | 1f42c81 | 2020-05-01 16:00:06 +0200 | [diff] [blame] | 90 | type compiler struct { |
Marcel van Lohuizen | 0cfe411 | 2020-06-27 15:01:34 +0200 | [diff] [blame] | 91 | Config |
| 92 | |
Marcel van Lohuizen | 1f42c81 | 2020-05-01 16:00:06 +0200 | [diff] [blame] | 93 | index adt.StringIndexer |
| 94 | |
| 95 | stack []frame |
| 96 | inSelector int |
| 97 | |
| 98 | fileScope map[adt.Feature]bool |
| 99 | |
| 100 | num literal.NumInfo |
| 101 | |
| 102 | errs errors.Error |
| 103 | } |
| 104 | |
| 105 | func (c *compiler) reset() { |
| 106 | c.fileScope = nil |
| 107 | c.stack = c.stack[:0] |
| 108 | c.errs = nil |
| 109 | } |
| 110 | |
| 111 | func (c *compiler) errf(n ast.Node, format string, args ...interface{}) *adt.Bottom { |
| 112 | err := &compilerError{ |
| 113 | n: n, |
| 114 | path: c.path(), |
| 115 | Message: errors.NewMessage(format, args), |
| 116 | } |
| 117 | c.errs = errors.Append(c.errs, err) |
Marcel van Lohuizen | 0f935f8 | 2020-06-09 09:13:01 +0200 | [diff] [blame] | 118 | return &adt.Bottom{Err: err} |
Marcel van Lohuizen | 1f42c81 | 2020-05-01 16:00:06 +0200 | [diff] [blame] | 119 | } |
| 120 | |
| 121 | func (c *compiler) path() []string { |
| 122 | a := []string{} |
| 123 | for _, f := range c.stack { |
| 124 | if f.label != nil { |
| 125 | a = append(a, f.label.labelString()) |
| 126 | } |
| 127 | } |
| 128 | return a |
| 129 | } |
| 130 | |
| 131 | type frame struct { |
| 132 | label labeler // path name leading to this frame. |
| 133 | scope ast.Node // *ast.File or *ast.Struct |
| 134 | field *ast.Field |
| 135 | // scope map[ast.Node]bool |
| 136 | upCount int32 // 1 for field, 0 for embedding. |
| 137 | |
| 138 | aliases map[string]aliasEntry |
| 139 | } |
| 140 | |
| 141 | type aliasEntry struct { |
| 142 | expr adt.Expr |
| 143 | source ast.Node |
| 144 | used bool |
| 145 | } |
| 146 | |
| 147 | func (c *compiler) insertAlias(id *ast.Ident, a aliasEntry) *adt.Bottom { |
| 148 | k := len(c.stack) - 1 |
| 149 | m := c.stack[k].aliases |
| 150 | if m == nil { |
| 151 | m = map[string]aliasEntry{} |
| 152 | c.stack[k].aliases = m |
| 153 | } |
| 154 | |
| 155 | if id == nil || !ast.IsValidIdent(id.Name) { |
| 156 | return c.errf(a.source, "invalid identifier name") |
| 157 | } |
| 158 | |
| 159 | if e, ok := m[id.Name]; ok { |
| 160 | return c.errf(a.source, |
| 161 | "alias %q already declared; previous declaration at %s", |
| 162 | id.Name, e.source.Pos()) |
| 163 | } |
| 164 | |
| 165 | m[id.Name] = a |
| 166 | return nil |
| 167 | } |
| 168 | |
| 169 | // lookupAlias looks up an alias with the given name at the k'th stack position. |
| 170 | func (c compiler) lookupAlias(k int, id *ast.Ident) aliasEntry { |
| 171 | m := c.stack[k].aliases |
| 172 | name := id.Name |
| 173 | entry, ok := m[name] |
| 174 | |
| 175 | if !ok { |
| 176 | err := c.errf(id, "could not find LetClause associated with identifier %q", name) |
| 177 | return aliasEntry{expr: err} |
| 178 | } |
| 179 | |
| 180 | entry.used = true |
| 181 | m[name] = entry |
| 182 | return entry |
| 183 | } |
| 184 | |
| 185 | func (c *compiler) pushScope(n labeler, upCount int32, id ast.Node) *frame { |
| 186 | c.stack = append(c.stack, frame{ |
| 187 | label: n, |
| 188 | scope: id, |
| 189 | upCount: upCount, |
| 190 | }) |
| 191 | return &c.stack[len(c.stack)-1] |
| 192 | } |
| 193 | |
| 194 | func (c *compiler) popScope() { |
| 195 | k := len(c.stack) - 1 |
| 196 | f := c.stack[k] |
| 197 | for k, v := range f.aliases { |
| 198 | if !v.used { |
| 199 | c.errf(v.source, "unreferenced alias or let clause %s", k) |
| 200 | } |
| 201 | } |
| 202 | c.stack = c.stack[:k] |
| 203 | } |
| 204 | |
Marcel van Lohuizen | 1f42c81 | 2020-05-01 16:00:06 +0200 | [diff] [blame] | 205 | func (c *compiler) compileFiles(a []*ast.File) *adt.Vertex { // Or value? |
| 206 | c.fileScope = map[adt.Feature]bool{} |
| 207 | |
| 208 | // Populate file scope to handle unresolved references. Note that we do |
| 209 | // not allow aliases to be resolved across file boundaries. |
| 210 | for _, f := range a { |
| 211 | for _, d := range f.Decls { |
| 212 | if f, ok := d.(*ast.Field); ok { |
| 213 | if id, ok := f.Label.(*ast.Ident); ok { |
| 214 | c.fileScope[c.label(id)] = true |
| 215 | } |
| 216 | } |
| 217 | } |
| 218 | } |
| 219 | |
Marcel van Lohuizen | cf94469 | 2020-07-15 15:28:47 +0200 | [diff] [blame] | 220 | // TODO: Assume that the other context is unified with the newly compiled |
| 221 | // files. This is not the same behavior as the old functionality, but we |
| 222 | // wanted to nix this anyway. For instance by allowing pkg_tool to be |
| 223 | // treated differently. |
| 224 | if v := c.Config.Scope; v != nil { |
| 225 | for _, arc := range v.Arcs { |
| 226 | if _, ok := c.fileScope[arc.Label]; !ok { |
| 227 | c.fileScope[arc.Label] = true |
| 228 | } |
| 229 | } |
| 230 | |
| 231 | c.pushScope(nil, 0, v.Source()) // File scope |
| 232 | defer c.popScope() |
| 233 | } |
| 234 | |
Marcel van Lohuizen | 1f42c81 | 2020-05-01 16:00:06 +0200 | [diff] [blame] | 235 | // TODO: set doc. |
| 236 | res := &adt.Vertex{} |
| 237 | |
| 238 | // env := &adt.Environment{Vertex: nil} // runtime: c.runtime |
| 239 | |
| 240 | for _, file := range a { |
| 241 | c.pushScope(nil, 0, file) // File scope |
| 242 | v := &adt.StructLit{Src: file} |
| 243 | c.addDecls(v, file.Decls) |
Marcel van Lohuizen | 3d863fc | 2020-09-03 19:58:22 +0200 | [diff] [blame] | 244 | res.Conjuncts = append(res.Conjuncts, adt.MakeRootConjunct(nil, v)) |
Marcel van Lohuizen | 1f42c81 | 2020-05-01 16:00:06 +0200 | [diff] [blame] | 245 | c.popScope() |
| 246 | } |
| 247 | |
| 248 | return res |
| 249 | } |
| 250 | |
Marcel van Lohuizen | 08a9fdb | 2020-06-10 11:07:13 +0200 | [diff] [blame] | 251 | func (c *compiler) compileExpr(x ast.Expr) adt.Conjunct { |
Marcel van Lohuizen | cf94469 | 2020-07-15 15:28:47 +0200 | [diff] [blame] | 252 | c.fileScope = map[adt.Feature]bool{} |
| 253 | |
| 254 | if v := c.Config.Scope; v != nil { |
| 255 | for _, arc := range v.Arcs { |
| 256 | c.fileScope[arc.Label] = true |
| 257 | } |
| 258 | |
| 259 | c.pushScope(nil, 0, v.Source()) // File scope |
| 260 | defer c.popScope() |
| 261 | } |
| 262 | |
Marcel van Lohuizen | 08a9fdb | 2020-06-10 11:07:13 +0200 | [diff] [blame] | 263 | expr := c.expr(x) |
| 264 | |
| 265 | env := &adt.Environment{} |
Marcel van Lohuizen | cf94469 | 2020-07-15 15:28:47 +0200 | [diff] [blame] | 266 | top := env |
| 267 | |
| 268 | for p := c.Config.Scope; p != nil; p = p.Parent { |
| 269 | top.Vertex = p |
| 270 | top.Up = &adt.Environment{} |
| 271 | top = top.Up |
| 272 | |
| 273 | // TODO: do something like this to allow multi-layered scopes. |
| 274 | // e := &adt.Environment{Vertex: p} |
| 275 | // if env != nil { |
| 276 | // env.Up = e |
| 277 | // } |
| 278 | // env = e |
| 279 | } |
| 280 | |
Marcel van Lohuizen | 3d863fc | 2020-09-03 19:58:22 +0200 | [diff] [blame] | 281 | return adt.MakeRootConjunct(env, expr) |
Marcel van Lohuizen | 0cfe411 | 2020-06-27 15:01:34 +0200 | [diff] [blame] | 282 | } |
| 283 | |
Marcel van Lohuizen | 1f42c81 | 2020-05-01 16:00:06 +0200 | [diff] [blame] | 284 | // resolve assumes that all existing resolutions are legal. Validation should |
| 285 | // be done in a separate step if required. |
| 286 | // |
| 287 | // TODO: collect validation pass to verify that all resolutions are |
| 288 | // legal? |
| 289 | func (c *compiler) resolve(n *ast.Ident) adt.Expr { |
| 290 | // X in import "path/X" |
| 291 | // X in import X "path" |
| 292 | if imp, ok := n.Node.(*ast.ImportSpec); ok { |
| 293 | return &adt.ImportReference{ |
| 294 | Src: n, |
| 295 | ImportPath: c.label(imp.Path), |
| 296 | Label: c.label(n), |
| 297 | } |
| 298 | } |
| 299 | |
| 300 | label := c.label(n) |
| 301 | |
| 302 | // Unresolved field. |
| 303 | if n.Node == nil { |
| 304 | upCount := int32(0) |
| 305 | for _, c := range c.stack { |
| 306 | upCount += c.upCount |
| 307 | } |
| 308 | if c.fileScope[label] { |
| 309 | return &adt.FieldReference{ |
| 310 | Src: n, |
| 311 | UpCount: upCount, |
| 312 | Label: label, |
| 313 | } |
| 314 | } |
| 315 | |
Marcel van Lohuizen | cf94469 | 2020-07-15 15:28:47 +0200 | [diff] [blame] | 316 | if c.Config.Imports != nil { |
| 317 | if pkgPath := c.Config.Imports(n); pkgPath != "" { |
| 318 | return &adt.ImportReference{ |
| 319 | Src: n, |
| 320 | ImportPath: adt.MakeStringLabel(c.index, pkgPath), |
| 321 | Label: c.label(n), |
| 322 | } |
| 323 | } |
| 324 | } |
| 325 | |
Marcel van Lohuizen | 1f42c81 | 2020-05-01 16:00:06 +0200 | [diff] [blame] | 326 | if p := predeclared(n); p != nil { |
| 327 | return p |
| 328 | } |
| 329 | |
| 330 | return c.errf(n, "reference %q not found", n.Name) |
| 331 | } |
| 332 | |
| 333 | // X in [X=x]: y Scope: Field Node: Expr (x) |
| 334 | // X in X=[x]: y Scope: Field Node: Field |
| 335 | if f, ok := n.Scope.(*ast.Field); ok { |
| 336 | upCount := int32(0) |
| 337 | |
| 338 | k := len(c.stack) - 1 |
| 339 | for ; k >= 0; k-- { |
| 340 | if c.stack[k].field == f { |
| 341 | break |
| 342 | } |
| 343 | upCount += c.stack[k].upCount |
| 344 | } |
| 345 | |
| 346 | label := &adt.LabelReference{ |
| 347 | Src: n, |
| 348 | UpCount: upCount, |
| 349 | } |
| 350 | |
| 351 | if f, ok := n.Node.(*ast.Field); ok { |
| 352 | _ = c.lookupAlias(k, f.Label.(*ast.Alias).Ident) // mark as used |
| 353 | return &adt.DynamicReference{ |
| 354 | Src: n, |
| 355 | UpCount: upCount, |
| 356 | Label: label, |
| 357 | } |
| 358 | } |
| 359 | return label |
| 360 | } |
| 361 | |
| 362 | upCount := int32(0) |
| 363 | |
| 364 | k := len(c.stack) - 1 |
| 365 | for ; k >= 0; k-- { |
| 366 | if c.stack[k].scope == n.Scope { |
| 367 | break |
| 368 | } |
| 369 | upCount += c.stack[k].upCount |
| 370 | } |
| 371 | if k < 0 { |
| 372 | // This is a programmatic error and should never happen if the users |
| 373 | // just builds with the cue command or if astutil.Resolve is used |
| 374 | // correctly. |
| 375 | c.errf(n, "reference %q set to unknown node in AST; "+ |
| 376 | "this can result from incorrect API usage or a compiler bug", |
| 377 | n.Name) |
| 378 | } |
| 379 | |
| 380 | switch n.Node.(type) { |
| 381 | // Local expressions |
| 382 | case *ast.LetClause, *ast.Alias: |
| 383 | entry := c.lookupAlias(k, n) |
| 384 | |
| 385 | return &adt.LetReference{ |
| 386 | Src: n, |
| 387 | UpCount: upCount, |
| 388 | Label: label, |
| 389 | X: entry.expr, |
| 390 | } |
| 391 | } |
| 392 | |
| 393 | if n.Scope == nil { |
| 394 | // Package. |
| 395 | // Should have been handled above. |
| 396 | panic("unreachable") // Or direct ancestor node? |
| 397 | } |
| 398 | |
| 399 | // X=x: y |
| 400 | // X=(x): y |
| 401 | // X="\(x)": y |
| 402 | if f, ok := n.Node.(*ast.Field); ok { |
| 403 | a, ok := f.Label.(*ast.Alias) |
| 404 | if !ok { |
| 405 | return c.errf(n, "illegal reference %s", n.Name) |
| 406 | } |
| 407 | aliasInfo := c.lookupAlias(k, a.Ident) // marks alias as used. |
| 408 | lab, ok := a.Expr.(ast.Label) |
| 409 | if !ok { |
| 410 | return c.errf(a.Expr, "invalid label expression") |
| 411 | } |
| 412 | name, _, err := ast.LabelName(lab) |
| 413 | switch { |
| 414 | case xerrors.Is(err, ast.ErrIsExpression): |
| 415 | if aliasInfo.expr == nil { |
| 416 | panic("unreachable") |
| 417 | } |
| 418 | return &adt.DynamicReference{ |
| 419 | Src: n, |
| 420 | UpCount: upCount, |
| 421 | Label: aliasInfo.expr, |
| 422 | } |
| 423 | |
| 424 | case err != nil: |
| 425 | return c.errf(n, "invalid label: %v", err) |
| 426 | |
| 427 | case name != "": |
| 428 | label = c.label(lab) |
| 429 | |
| 430 | default: |
| 431 | return c.errf(n, "unsupported field alias %q", name) |
| 432 | } |
| 433 | } |
| 434 | |
| 435 | return &adt.FieldReference{ |
| 436 | Src: n, |
| 437 | UpCount: upCount, |
| 438 | Label: label, |
| 439 | } |
| 440 | } |
| 441 | |
| 442 | func (c *compiler) addDecls(st *adt.StructLit, a []ast.Decl) { |
| 443 | for _, d := range a { |
Marcel van Lohuizen | e986a8f | 2020-07-17 08:50:49 +0200 | [diff] [blame] | 444 | c.addLetDecl(d) |
| 445 | } |
| 446 | for _, d := range a { |
Marcel van Lohuizen | 1f42c81 | 2020-05-01 16:00:06 +0200 | [diff] [blame] | 447 | if x := c.decl(d); x != nil { |
| 448 | st.Decls = append(st.Decls, x) |
| 449 | } |
| 450 | } |
| 451 | } |
| 452 | |
| 453 | func (c *compiler) decl(d ast.Decl) adt.Decl { |
| 454 | switch x := d.(type) { |
| 455 | case *ast.BadDecl: |
| 456 | return c.errf(d, "") |
| 457 | |
| 458 | case *ast.Field: |
| 459 | lab := x.Label |
| 460 | if a, ok := lab.(*ast.Alias); ok { |
| 461 | if lab, ok = a.Expr.(ast.Label); !ok { |
| 462 | return c.errf(a, "alias expression is not a valid label") |
| 463 | } |
| 464 | |
| 465 | e := aliasEntry{source: a} |
| 466 | |
| 467 | switch lab.(type) { |
Marcel van Lohuizen | 08a9fdb | 2020-06-10 11:07:13 +0200 | [diff] [blame] | 468 | case *ast.Ident, *ast.BasicLit, *ast.ListLit: |
Marcel van Lohuizen | 1f42c81 | 2020-05-01 16:00:06 +0200 | [diff] [blame] | 469 | // Even though we won't need the alias, we still register it |
| 470 | // for duplicate and failed reference detection. |
| 471 | default: |
| 472 | e.expr = c.expr(a.Expr) |
| 473 | } |
| 474 | |
| 475 | if err := c.insertAlias(a.Ident, e); err != nil { |
| 476 | return err |
| 477 | } |
| 478 | } |
| 479 | |
| 480 | value := c.labeledExpr(x, (*fieldLabel)(x), x.Value) |
| 481 | |
| 482 | switch l := lab.(type) { |
| 483 | case *ast.Ident, *ast.BasicLit: |
| 484 | label := c.label(lab) |
| 485 | |
| 486 | // TODO(legacy): remove: old-school definitions |
| 487 | if x.Token == token.ISA && !label.IsDef() { |
| 488 | name, isIdent, err := ast.LabelName(lab) |
| 489 | if err == nil && isIdent { |
| 490 | idx := c.index.StringToIndex(name) |
Marcel van Lohuizen | 0f935f8 | 2020-06-09 09:13:01 +0200 | [diff] [blame] | 491 | label, _ = adt.MakeLabel(x, idx, adt.DefinitionLabel) |
Marcel van Lohuizen | 1f42c81 | 2020-05-01 16:00:06 +0200 | [diff] [blame] | 492 | } |
| 493 | } |
| 494 | |
| 495 | if x.Optional == token.NoPos { |
| 496 | return &adt.Field{ |
| 497 | Src: x, |
| 498 | Label: label, |
| 499 | Value: value, |
| 500 | } |
| 501 | } else { |
| 502 | return &adt.OptionalField{ |
| 503 | Src: x, |
| 504 | Label: label, |
| 505 | Value: value, |
| 506 | } |
| 507 | } |
| 508 | |
| 509 | case *ast.ListLit: |
| 510 | if len(l.Elts) != 1 { |
| 511 | // error |
| 512 | return c.errf(x, "list label must have one element") |
| 513 | } |
Marcel van Lohuizen | 08a9fdb | 2020-06-10 11:07:13 +0200 | [diff] [blame] | 514 | var label adt.Feature |
Marcel van Lohuizen | 1f42c81 | 2020-05-01 16:00:06 +0200 | [diff] [blame] | 515 | elem := l.Elts[0] |
| 516 | // TODO: record alias for error handling? In principle it is okay |
| 517 | // to have duplicates, but we do want it to be used. |
| 518 | if a, ok := elem.(*ast.Alias); ok { |
Marcel van Lohuizen | 08a9fdb | 2020-06-10 11:07:13 +0200 | [diff] [blame] | 519 | label = c.label(a.Ident) |
Marcel van Lohuizen | 1f42c81 | 2020-05-01 16:00:06 +0200 | [diff] [blame] | 520 | elem = a.Expr |
| 521 | } |
| 522 | |
| 523 | return &adt.BulkOptionalField{ |
| 524 | Src: x, |
| 525 | Filter: c.expr(elem), |
| 526 | Value: value, |
Marcel van Lohuizen | 08a9fdb | 2020-06-10 11:07:13 +0200 | [diff] [blame] | 527 | Label: label, |
Marcel van Lohuizen | 1f42c81 | 2020-05-01 16:00:06 +0200 | [diff] [blame] | 528 | } |
| 529 | |
| 530 | case *ast.Interpolation: // *ast.ParenExpr, |
| 531 | if x.Token == token.ISA { |
| 532 | c.errf(x, "definitions not supported for interpolations") |
| 533 | } |
| 534 | return &adt.DynamicField{ |
| 535 | Src: x, |
| 536 | Key: c.expr(l), |
| 537 | Value: value, |
| 538 | } |
| 539 | } |
| 540 | |
Marcel van Lohuizen | e986a8f | 2020-07-17 08:50:49 +0200 | [diff] [blame] | 541 | // Handled in addLetDecl. |
| 542 | case *ast.LetClause, *ast.Alias: |
Marcel van Lohuizen | 1f42c81 | 2020-05-01 16:00:06 +0200 | [diff] [blame] | 543 | |
| 544 | case *ast.CommentGroup: |
| 545 | // Nothing to do for a free-floating comment group. |
| 546 | |
| 547 | case *ast.Attribute: |
| 548 | // Nothing to do for now for an attribute declaration. |
| 549 | |
| 550 | case *ast.Ellipsis: |
| 551 | return &adt.Ellipsis{ |
| 552 | Src: x, |
| 553 | Value: c.expr(x.Type), |
| 554 | } |
| 555 | |
| 556 | case *ast.Comprehension: |
| 557 | return c.comprehension(x) |
| 558 | |
| 559 | case *ast.EmbedDecl: // Deprecated |
| 560 | return c.embed(x.Expr) |
| 561 | |
| 562 | case ast.Expr: |
| 563 | return c.embed(x) |
| 564 | } |
| 565 | return nil |
| 566 | } |
| 567 | |
Marcel van Lohuizen | e986a8f | 2020-07-17 08:50:49 +0200 | [diff] [blame] | 568 | func (c *compiler) addLetDecl(d ast.Decl) { |
| 569 | switch x := d.(type) { |
| 570 | // An alias reference will have an expression that is looked up in the |
| 571 | // environment cash. |
| 572 | case *ast.LetClause: |
| 573 | // Cache the parsed expression. Creating a unique expression for each |
| 574 | // reference allows the computation to be shared given that we don't |
| 575 | // have fields for expressions. This, in turn, prevents exponential |
| 576 | // blowup in x2: x1+x1, x3: x2+x2, ... patterns. |
| 577 | |
| 578 | expr := c.labeledExpr(nil, (*letScope)(x), x.Expr) |
| 579 | |
| 580 | a := aliasEntry{source: x, expr: expr} |
| 581 | |
| 582 | c.insertAlias(x.Ident, a) |
| 583 | |
| 584 | case *ast.Alias: |
| 585 | |
| 586 | expr := c.labeledExpr(nil, (*deprecatedAliasScope)(x), x.Expr) |
| 587 | |
| 588 | // TODO(legacy): deprecated, remove this use of Alias |
| 589 | a := aliasEntry{source: x, expr: expr} |
| 590 | |
| 591 | c.insertAlias(x.Ident, a) |
| 592 | } |
| 593 | } |
| 594 | |
Marcel van Lohuizen | 1f42c81 | 2020-05-01 16:00:06 +0200 | [diff] [blame] | 595 | func (c *compiler) elem(n ast.Expr) adt.Elem { |
| 596 | switch x := n.(type) { |
| 597 | case *ast.Ellipsis: |
| 598 | return &adt.Ellipsis{ |
| 599 | Src: x, |
| 600 | Value: c.expr(x.Type), |
| 601 | } |
| 602 | |
| 603 | case *ast.Comprehension: |
| 604 | return c.comprehension(x) |
| 605 | |
| 606 | case ast.Expr: |
| 607 | return c.expr(x) |
| 608 | } |
| 609 | return nil |
| 610 | } |
| 611 | |
| 612 | func (c *compiler) comprehension(x *ast.Comprehension) adt.Elem { |
| 613 | var cur adt.Yielder |
| 614 | var first adt.Elem |
| 615 | var prev, next *adt.Yielder |
| 616 | for _, v := range x.Clauses { |
| 617 | switch x := v.(type) { |
| 618 | case *ast.ForClause: |
| 619 | var key adt.Feature |
| 620 | if x.Key != nil { |
| 621 | key = c.label(x.Key) |
| 622 | } |
| 623 | y := &adt.ForClause{ |
| 624 | Syntax: x, |
| 625 | Key: key, |
| 626 | Value: c.label(x.Value), |
| 627 | Src: c.expr(x.Source), |
| 628 | } |
| 629 | cur = y |
| 630 | c.pushScope((*forScope)(x), 1, v) |
| 631 | defer c.popScope() |
| 632 | next = &y.Dst |
| 633 | |
| 634 | case *ast.IfClause: |
| 635 | y := &adt.IfClause{ |
| 636 | Src: x, |
| 637 | Condition: c.expr(x.Condition), |
| 638 | } |
| 639 | cur = y |
| 640 | next = &y.Dst |
| 641 | |
| 642 | case *ast.LetClause: |
| 643 | y := &adt.LetClause{ |
| 644 | Src: x, |
| 645 | Label: c.label(x.Ident), |
| 646 | Expr: c.expr(x.Expr), |
| 647 | } |
| 648 | cur = y |
| 649 | c.pushScope((*letScope)(x), 1, v) |
| 650 | defer c.popScope() |
| 651 | next = &y.Dst |
| 652 | } |
| 653 | |
| 654 | if prev != nil { |
| 655 | *prev = cur |
| 656 | } else { |
| 657 | var ok bool |
| 658 | if first, ok = cur.(adt.Elem); !ok { |
| 659 | return c.errf(x, |
| 660 | "first comprehension clause must be 'if' or 'for'") |
| 661 | } |
| 662 | } |
| 663 | prev = next |
| 664 | } |
| 665 | |
Marcel van Lohuizen | c6ade67 | 2020-08-21 17:39:06 +0200 | [diff] [blame] | 666 | // TODO: make x.Value an *ast.StructLit and this is redundant. |
Marcel van Lohuizen | 1f42c81 | 2020-05-01 16:00:06 +0200 | [diff] [blame] | 667 | if y, ok := x.Value.(*ast.StructLit); !ok { |
| 668 | return c.errf(x.Value, |
| 669 | "comprehension value must be struct, found %T", y) |
| 670 | } |
| 671 | |
| 672 | y := c.expr(x.Value) |
| 673 | |
| 674 | st, ok := y.(*adt.StructLit) |
| 675 | if !ok { |
| 676 | // Error must have been generated. |
| 677 | return y |
| 678 | } |
| 679 | |
| 680 | if prev != nil { |
| 681 | *prev = &adt.ValueClause{StructLit: st} |
| 682 | } else { |
| 683 | return c.errf(x, "comprehension value without clauses") |
| 684 | } |
| 685 | |
| 686 | return first |
| 687 | } |
| 688 | |
| 689 | func (c *compiler) embed(expr ast.Expr) adt.Expr { |
| 690 | switch n := expr.(type) { |
| 691 | case *ast.StructLit: |
| 692 | c.pushScope(nil, 1, n) |
| 693 | v := &adt.StructLit{Src: n} |
| 694 | c.addDecls(v, n.Elts) |
| 695 | c.popScope() |
| 696 | return v |
| 697 | } |
| 698 | return c.expr(expr) |
| 699 | } |
| 700 | |
| 701 | func (c *compiler) labeledExpr(f *ast.Field, lab labeler, expr ast.Expr) adt.Expr { |
| 702 | k := len(c.stack) - 1 |
| 703 | if c.stack[k].field != nil { |
| 704 | panic("expected nil field") |
| 705 | } |
| 706 | c.stack[k].label = lab |
| 707 | c.stack[k].field = f |
| 708 | value := c.expr(expr) |
| 709 | c.stack[k].label = nil |
| 710 | c.stack[k].field = nil |
| 711 | return value |
| 712 | } |
| 713 | |
| 714 | func (c *compiler) expr(expr ast.Expr) adt.Expr { |
| 715 | switch n := expr.(type) { |
| 716 | case nil: |
| 717 | return nil |
| 718 | case *ast.Ident: |
| 719 | return c.resolve(n) |
| 720 | |
| 721 | case *ast.StructLit: |
| 722 | c.pushScope(nil, 1, n) |
| 723 | v := &adt.StructLit{Src: n} |
| 724 | c.addDecls(v, n.Elts) |
| 725 | c.popScope() |
| 726 | return v |
| 727 | |
| 728 | case *ast.ListLit: |
| 729 | v := &adt.ListLit{Src: n} |
| 730 | elts, ellipsis := internal.ListEllipsis(n) |
| 731 | for _, d := range elts { |
| 732 | elem := c.elem(d) |
| 733 | |
| 734 | switch x := elem.(type) { |
| 735 | case nil: |
| 736 | case adt.Elem: |
| 737 | v.Elems = append(v.Elems, x) |
| 738 | default: |
| 739 | c.errf(d, "type %T not allowed in ListLit", d) |
| 740 | } |
| 741 | } |
| 742 | if ellipsis != nil { |
| 743 | d := &adt.Ellipsis{ |
| 744 | Src: ellipsis, |
| 745 | Value: c.expr(ellipsis.Type), |
| 746 | } |
| 747 | v.Elems = append(v.Elems, d) |
| 748 | } |
| 749 | return v |
| 750 | |
| 751 | case *ast.SelectorExpr: |
| 752 | c.inSelector++ |
| 753 | ret := &adt.SelectorExpr{ |
| 754 | Src: n, |
| 755 | X: c.expr(n.X), |
| 756 | Sel: c.label(n.Sel)} |
| 757 | c.inSelector-- |
| 758 | return ret |
| 759 | |
| 760 | case *ast.IndexExpr: |
| 761 | return &adt.IndexExpr{ |
| 762 | Src: n, |
| 763 | X: c.expr(n.X), |
| 764 | Index: c.expr(n.Index), |
| 765 | } |
| 766 | |
| 767 | case *ast.SliceExpr: |
| 768 | slice := &adt.SliceExpr{Src: n, X: c.expr(n.X)} |
| 769 | if n.Low != nil { |
| 770 | slice.Lo = c.expr(n.Low) |
| 771 | } |
| 772 | if n.High != nil { |
| 773 | slice.Hi = c.expr(n.High) |
| 774 | } |
| 775 | return slice |
| 776 | |
| 777 | case *ast.BottomLit: |
Marcel van Lohuizen | 0f935f8 | 2020-06-09 09:13:01 +0200 | [diff] [blame] | 778 | return &adt.Bottom{ |
| 779 | Src: n, |
| 780 | Code: adt.UserError, |
Marcel van Lohuizen | df01042 | 2020-09-12 19:56:21 +0200 | [diff] [blame] | 781 | Err: errors.Newf(n.Pos(), "explicit error (_|_ literal) in source"), |
Marcel van Lohuizen | 0f935f8 | 2020-06-09 09:13:01 +0200 | [diff] [blame] | 782 | } |
Marcel van Lohuizen | 1f42c81 | 2020-05-01 16:00:06 +0200 | [diff] [blame] | 783 | |
| 784 | case *ast.BadExpr: |
| 785 | return c.errf(n, "invalid expression") |
| 786 | |
| 787 | case *ast.BasicLit: |
| 788 | return c.parse(n) |
| 789 | |
| 790 | case *ast.Interpolation: |
| 791 | if len(n.Elts) == 0 { |
| 792 | return c.errf(n, "invalid interpolation") |
| 793 | } |
| 794 | first, ok1 := n.Elts[0].(*ast.BasicLit) |
| 795 | last, ok2 := n.Elts[len(n.Elts)-1].(*ast.BasicLit) |
| 796 | if !ok1 || !ok2 { |
| 797 | return c.errf(n, "invalid interpolation") |
| 798 | } |
| 799 | if len(n.Elts) == 1 { |
| 800 | return c.expr(n.Elts[0]) |
| 801 | } |
Marcel van Lohuizen | 30ca062 | 2020-08-22 14:07:59 +0200 | [diff] [blame] | 802 | lit := &adt.Interpolation{Src: n} |
Marcel van Lohuizen | 1f42c81 | 2020-05-01 16:00:06 +0200 | [diff] [blame] | 803 | info, prefixLen, _, err := literal.ParseQuotes(first.Value, last.Value) |
| 804 | if err != nil { |
| 805 | return c.errf(n, "invalid interpolation: %v", err) |
| 806 | } |
Marcel van Lohuizen | 30ca062 | 2020-08-22 14:07:59 +0200 | [diff] [blame] | 807 | if info.IsDouble() { |
| 808 | lit.K = adt.StringKind |
| 809 | } else { |
| 810 | lit.K = adt.BytesKind |
| 811 | } |
Marcel van Lohuizen | 1f42c81 | 2020-05-01 16:00:06 +0200 | [diff] [blame] | 812 | prefix := "" |
| 813 | for i := 0; i < len(n.Elts); i += 2 { |
| 814 | l, ok := n.Elts[i].(*ast.BasicLit) |
| 815 | if !ok { |
| 816 | return c.errf(n, "invalid interpolation") |
| 817 | } |
| 818 | s := l.Value |
| 819 | if !strings.HasPrefix(s, prefix) { |
| 820 | return c.errf(l, "invalid interpolation: unmatched ')'") |
| 821 | } |
| 822 | s = l.Value[prefixLen:] |
| 823 | x := parseString(c, l, info, s) |
| 824 | lit.Parts = append(lit.Parts, x) |
| 825 | if i+1 < len(n.Elts) { |
| 826 | lit.Parts = append(lit.Parts, c.expr(n.Elts[i+1])) |
| 827 | } |
| 828 | prefix = ")" |
| 829 | prefixLen = 1 |
| 830 | } |
| 831 | return lit |
| 832 | |
| 833 | case *ast.ParenExpr: |
| 834 | return c.expr(n.X) |
| 835 | |
| 836 | case *ast.CallExpr: |
| 837 | call := &adt.CallExpr{Src: n, Fun: c.expr(n.Fun)} |
| 838 | for _, a := range n.Args { |
| 839 | call.Args = append(call.Args, c.expr(a)) |
| 840 | } |
| 841 | return call |
| 842 | |
| 843 | case *ast.UnaryExpr: |
| 844 | switch n.Op { |
| 845 | case token.NOT, token.ADD, token.SUB: |
| 846 | return &adt.UnaryExpr{ |
| 847 | Src: n, |
| 848 | Op: adt.OpFromToken(n.Op), |
| 849 | X: c.expr(n.X), |
| 850 | } |
| 851 | case token.GEQ, token.GTR, token.LSS, token.LEQ, |
| 852 | token.NEQ, token.MAT, token.NMAT: |
| 853 | return &adt.BoundExpr{ |
| 854 | Src: n, |
| 855 | Op: adt.OpFromToken(n.Op), |
| 856 | Expr: c.expr(n.X), |
| 857 | } |
| 858 | |
| 859 | case token.MUL: |
| 860 | return c.errf(n, "preference mark not allowed at this position") |
| 861 | default: |
| 862 | return c.errf(n, "unsupported unary operator %q", n.Op) |
| 863 | } |
| 864 | |
| 865 | case *ast.BinaryExpr: |
| 866 | switch n.Op { |
| 867 | case token.OR: |
| 868 | d := &adt.DisjunctionExpr{Src: n} |
| 869 | c.addDisjunctionElem(d, n.X, false) |
| 870 | c.addDisjunctionElem(d, n.Y, false) |
| 871 | return d |
| 872 | |
| 873 | default: |
| 874 | // return updateBin(c, |
| 875 | return &adt.BinaryExpr{ |
| 876 | Src: n, |
| 877 | Op: adt.OpFromToken(n.Op), // op |
| 878 | X: c.expr(n.X), // left |
| 879 | Y: c.expr(n.Y), // right |
| 880 | } // ) |
| 881 | } |
| 882 | |
| 883 | default: |
| 884 | panic(fmt.Sprintf("unknown expression type %T", n)) |
| 885 | // return c.errf(n, "unknown expression type %T", n) |
| 886 | } |
| 887 | } |
| 888 | |
| 889 | func (c *compiler) addDisjunctionElem(d *adt.DisjunctionExpr, n ast.Expr, mark bool) { |
| 890 | switch x := n.(type) { |
| 891 | case *ast.BinaryExpr: |
| 892 | if x.Op == token.OR { |
| 893 | c.addDisjunctionElem(d, x.X, mark) |
| 894 | c.addDisjunctionElem(d, x.Y, mark) |
| 895 | return |
| 896 | } |
| 897 | case *ast.UnaryExpr: |
| 898 | if x.Op == token.MUL { |
| 899 | d.HasDefaults = true |
| 900 | c.addDisjunctionElem(d, x.X, true) |
| 901 | return |
| 902 | } |
| 903 | } |
| 904 | d.Values = append(d.Values, adt.Disjunct{Val: c.expr(n), Default: mark}) |
| 905 | } |
| 906 | |
Marcel van Lohuizen | 0f935f8 | 2020-06-09 09:13:01 +0200 | [diff] [blame] | 907 | // TODO(perf): validate that regexps are cached at the right time. |
| 908 | |
Marcel van Lohuizen | 1f42c81 | 2020-05-01 16:00:06 +0200 | [diff] [blame] | 909 | func (c *compiler) parse(l *ast.BasicLit) (n adt.Expr) { |
| 910 | s := l.Value |
| 911 | if s == "" { |
| 912 | return c.errf(l, "invalid literal %q", s) |
| 913 | } |
| 914 | switch l.Kind { |
| 915 | case token.STRING: |
| 916 | info, nStart, _, err := literal.ParseQuotes(s, s) |
| 917 | if err != nil { |
| 918 | return c.errf(l, err.Error()) |
| 919 | } |
| 920 | s := s[nStart:] |
| 921 | return parseString(c, l, info, s) |
| 922 | |
| 923 | case token.FLOAT, token.INT: |
| 924 | err := literal.ParseNum(s, &c.num) |
| 925 | if err != nil { |
| 926 | return c.errf(l, "parse error: %v", err) |
| 927 | } |
| 928 | kind := adt.FloatKind |
| 929 | if c.num.IsInt() { |
| 930 | kind = adt.IntKind |
| 931 | } |
| 932 | n := &adt.Num{Src: l, K: kind} |
| 933 | if err = c.num.Decimal(&n.X); err != nil { |
| 934 | return c.errf(l, "error converting number to decimal: %v", err) |
| 935 | } |
| 936 | return n |
| 937 | |
| 938 | case token.TRUE: |
| 939 | return &adt.Bool{Src: l, B: true} |
| 940 | |
| 941 | case token.FALSE: |
| 942 | return &adt.Bool{Src: l, B: false} |
| 943 | |
| 944 | case token.NULL: |
| 945 | return &adt.Null{Src: l} |
| 946 | |
| 947 | default: |
| 948 | return c.errf(l, "unknown literal type") |
| 949 | } |
| 950 | } |
| 951 | |
| 952 | // parseString decodes a string without the starting and ending quotes. |
| 953 | func parseString(c *compiler, node ast.Expr, q literal.QuoteInfo, s string) (n adt.Expr) { |
| 954 | str, err := q.Unquote(s) |
| 955 | if err != nil { |
| 956 | return c.errf(node, "invalid string: %v", err) |
| 957 | } |
| 958 | if q.IsDouble() { |
| 959 | return &adt.String{Src: node, Str: str, RE: nil} |
| 960 | } |
| 961 | return &adt.Bytes{Src: node, B: []byte(str), RE: nil} |
| 962 | } |