Extend the current environment with additional options to produce a new Env. Note, the extended Env value should not share memory with the original. It is possible, however, that a CustomTypeAdapter or CustomTypeProvider options could provide values which are mutable. To ensure separation of state
(opts ...EnvOption)
| 489 | // TypeProvider are immutable, or that their underlying implementations are based on the |
| 490 | // ref.TypeRegistry which provides a Copy method which will be invoked by this method. |
| 491 | func (e *Env) Extend(opts ...EnvOption) (*Env, error) { |
| 492 | chk, chkErr := e.getCheckerOrError() |
| 493 | if chkErr != nil { |
| 494 | return nil, chkErr |
| 495 | } |
| 496 | |
| 497 | prsrOptsCopy := make([]parser.Option, len(e.prsrOpts)) |
| 498 | copy(prsrOptsCopy, e.prsrOpts) |
| 499 | |
| 500 | // The type-checker is configured with Declarations. The declarations may either be provided |
| 501 | // as options which have not yet been validated, or may come from a previous checker instance |
| 502 | // whose types have already been validated. |
| 503 | chkOptsCopy := make([]checker.Option, len(e.chkOpts)) |
| 504 | copy(chkOptsCopy, e.chkOpts) |
| 505 | |
| 506 | // Copy the declarations if needed. |
| 507 | if chk != nil { |
| 508 | // If the type-checker has already been instantiated, then the e.declarations have been |
| 509 | // validated within the chk instance. |
| 510 | chkOptsCopy = append(chkOptsCopy, checker.ValidatedDeclarations(chk)) |
| 511 | } |
| 512 | varsCopy := make([]*decls.VariableDecl, len(e.variables)) |
| 513 | copy(varsCopy, e.variables) |
| 514 | |
| 515 | // Copy macros and program options |
| 516 | macsCopy := make([]parser.Macro, len(e.macros)) |
| 517 | progOptsCopy := make([]ProgramOption, len(e.progOpts)) |
| 518 | copy(macsCopy, e.macros) |
| 519 | copy(progOptsCopy, e.progOpts) |
| 520 | |
| 521 | // Copy the adapter / provider if they appear to be mutable. |
| 522 | adapter := e.adapter |
| 523 | provider := e.provider |
| 524 | adapterReg, isAdapterReg := e.adapter.(*types.Registry) |
| 525 | providerReg, isProviderReg := e.provider.(*types.Registry) |
| 526 | // In most cases the provider and adapter will be a ref.TypeRegistry; |
| 527 | // however, in the rare cases where they are not, they are assumed to |
| 528 | // be immutable. Since it is possible to set the TypeProvider separately |
| 529 | // from the TypeAdapter, the possible configurations which could use a |
| 530 | // TypeRegistry as the base implementation are captured below. |
| 531 | if isAdapterReg && isProviderReg { |
| 532 | reg := providerReg.Copy() |
| 533 | provider = reg |
| 534 | // If the adapter and provider are the same object, set the adapter |
| 535 | // to the same ref.TypeRegistry as the provider. |
| 536 | if adapterReg == providerReg { |
| 537 | adapter = reg |
| 538 | } else { |
| 539 | // Otherwise, make a copy of the adapter. |
| 540 | adapter = adapterReg.Copy() |
| 541 | } |
| 542 | } else if isProviderReg { |
| 543 | provider = providerReg.Copy() |
| 544 | } else if isAdapterReg { |
| 545 | adapter = adapterReg.Copy() |
| 546 | } |
| 547 | |
| 548 | featuresCopy := make(map[int]bool, len(e.features)) |