compiler.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. package eval
  2. import (
  3. "fmt"
  4. "io"
  5. "src.elv.sh/pkg/diag"
  6. "src.elv.sh/pkg/eval/vars"
  7. "src.elv.sh/pkg/parse"
  8. "src.elv.sh/pkg/prog"
  9. )
  10. // compiler maintains the set of states needed when compiling a single source
  11. // file.
  12. type compiler struct {
  13. // Builtin namespace.
  14. builtin *staticNs
  15. // Lexical namespaces.
  16. scopes []*staticNs
  17. // Sources of captured variables.
  18. captures []*staticUpNs
  19. // Pragmas tied to scopes.
  20. pragmas []*scopePragma
  21. // Destination of warning messages. This is currently only used for
  22. // deprecation messages.
  23. warn io.Writer
  24. // Deprecation registry.
  25. deprecations deprecationRegistry
  26. // Information about the source.
  27. srcMeta parse.Source
  28. }
  29. type scopePragma struct {
  30. unknownCommandIsExternal bool
  31. }
  32. func compile(b, g *staticNs, tree parse.Tree, w io.Writer) (op nsOp, err error) {
  33. g = g.clone()
  34. cp := &compiler{
  35. b, []*staticNs{g}, []*staticUpNs{new(staticUpNs)},
  36. []*scopePragma{{unknownCommandIsExternal: true}},
  37. w, newDeprecationRegistry(), tree.Source}
  38. defer func() {
  39. r := recover()
  40. if r == nil {
  41. return
  42. } else if e := GetCompilationError(r); e != nil {
  43. // Save the compilation error and stop the panic.
  44. err = e
  45. } else {
  46. // Resume the panic; it is not supposed to be handled here.
  47. panic(r)
  48. }
  49. }()
  50. chunkOp := cp.chunkOp(tree.Root)
  51. return nsOp{chunkOp, g}, nil
  52. }
  53. type nsOp struct {
  54. inner effectOp
  55. template *staticNs
  56. }
  57. // Prepares the local namespace, and returns the namespace and a function for
  58. // executing the inner effectOp. Mutates fm.local.
  59. func (op nsOp) prepare(fm *Frame) (*Ns, func() Exception) {
  60. if len(op.template.infos) > len(fm.local.infos) {
  61. n := len(op.template.infos)
  62. newLocal := &Ns{make([]vars.Var, n), op.template.infos}
  63. copy(newLocal.slots, fm.local.slots)
  64. for i := len(fm.local.infos); i < n; i++ {
  65. // TODO: Take readOnly into account too
  66. newLocal.slots[i] = MakeVarFromName(newLocal.infos[i].name)
  67. }
  68. fm.local = newLocal
  69. } else {
  70. // If no new variable has been created, there might still be some
  71. // existing variables deleted.
  72. fm.local = &Ns{fm.local.slots, op.template.infos}
  73. }
  74. return fm.local, func() Exception { return op.inner.exec(fm) }
  75. }
  76. const compilationErrorType = "compilation error"
  77. func (cp *compiler) errorpf(r diag.Ranger, format string, args ...any) {
  78. // The panic is caught by the recover in compile above.
  79. panic(&diag.Error{
  80. Type: compilationErrorType,
  81. Message: fmt.Sprintf(format, args...),
  82. Context: *diag.NewContext(cp.srcMeta.Name, cp.srcMeta.Code, r)})
  83. }
  84. // GetCompilationError returns a *diag.Error if the given value is a compilation
  85. // error. Otherwise it returns nil.
  86. func GetCompilationError(e any) *diag.Error {
  87. if e, ok := e.(*diag.Error); ok && e.Type == compilationErrorType {
  88. return e
  89. }
  90. return nil
  91. }
  92. func (cp *compiler) thisScope() *staticNs {
  93. return cp.scopes[len(cp.scopes)-1]
  94. }
  95. func (cp *compiler) currentPragma() *scopePragma {
  96. return cp.pragmas[len(cp.pragmas)-1]
  97. }
  98. func (cp *compiler) pushScope() (*staticNs, *staticUpNs) {
  99. sc := new(staticNs)
  100. up := new(staticUpNs)
  101. cp.scopes = append(cp.scopes, sc)
  102. cp.captures = append(cp.captures, up)
  103. currentPragmaCopy := *cp.currentPragma()
  104. cp.pragmas = append(cp.pragmas, &currentPragmaCopy)
  105. return sc, up
  106. }
  107. func (cp *compiler) popScope() {
  108. cp.scopes[len(cp.scopes)-1] = nil
  109. cp.scopes = cp.scopes[:len(cp.scopes)-1]
  110. cp.captures[len(cp.captures)-1] = nil
  111. cp.captures = cp.captures[:len(cp.captures)-1]
  112. cp.pragmas[len(cp.pragmas)-1] = nil
  113. cp.pragmas = cp.pragmas[:len(cp.pragmas)-1]
  114. }
  115. func (cp *compiler) checkDeprecatedBuiltin(name string, r diag.Ranger) {
  116. msg := ""
  117. minLevel := 18
  118. // There is no builtin deprecated for 0.18.x yet. But this function is
  119. // only called for builtins that actually exist, so no harm checking for a
  120. // non-existent command here.
  121. switch name {
  122. case "deprecated-builtin~":
  123. msg = `the "deprecated-builtin" command is deprecated; use "new-builtin" instead`
  124. default:
  125. return
  126. }
  127. cp.deprecate(r, msg, minLevel)
  128. }
  129. func (cp *compiler) deprecate(r diag.Ranger, msg string, minLevel int) {
  130. if cp.warn == nil || r == nil {
  131. return
  132. }
  133. dep := deprecation{cp.srcMeta.Name, r.Range(), msg}
  134. if prog.DeprecationLevel >= minLevel && cp.deprecations.register(dep) {
  135. err := diag.Error{
  136. Type: "deprecation", Message: msg,
  137. Context: diag.Context{
  138. Name: cp.srcMeta.Name, Source: cp.srcMeta.Code, Ranging: r.Range()}}
  139. fmt.Fprintln(cp.warn, err.Show(""))
  140. }
  141. }