elvish/pkg/eval/compiler.go
Supreet 8f2accb0b9 File module with pipe function. Pipe command marked deprecated in compiler.go and builtin_fn_io.go
Prclose and pwclose with Deprecation marked in specific file. Give
feedback regarding the test cases.

File module now has prclose and pwclose

Wrote test cases for prclose and pwclose, need better test cases

Deprecation for prclose and pwclose marked in respective files.
2021-04-19 18:48:22 -04:00

157 lines
4.4 KiB
Go

package eval
import (
"fmt"
"io"
"src.elv.sh/pkg/diag"
"src.elv.sh/pkg/eval/vars"
"src.elv.sh/pkg/parse"
"src.elv.sh/pkg/prog"
)
// compiler maintains the set of states needed when compiling a single source
// file.
type compiler struct {
// Builtin namespace.
builtin *staticNs
// Lexical namespaces.
scopes []*staticNs
// Sources of captured variables.
captures []*staticUpNs
// Destination of warning messages. This is currently only used for
// deprecation messages.
warn io.Writer
// Deprecation registry.
deprecations deprecationRegistry
// Information about the source.
srcMeta parse.Source
}
type capture struct {
name string
// If true, the captured variable is from the immediate outer level scope,
// i.e. the local scope the lambda is evaluated in. Otherwise the captured
// variable is from a more outer level, i.e. the upvalue scope the lambda is
// evaluated in.
local bool
// Index to the captured variable.
index int
}
func compile(b, g *staticNs, tree parse.Tree, w io.Writer) (op nsOp, err error) {
g = g.clone()
cp := &compiler{
b, []*staticNs{g}, []*staticUpNs{new(staticUpNs)},
w, newDeprecationRegistry(), tree.Source}
defer func() {
r := recover()
if r == nil {
return
} else if e := GetCompilationError(r); e != nil {
// Save the compilation error and stop the panic.
err = e
} else {
// Resume the panic; it is not supposed to be handled here.
panic(r)
}
}()
chunkOp := cp.chunkOp(tree.Root)
return nsOp{chunkOp, g}, nil
}
type nsOp struct {
inner effectOp
template *staticNs
}
// Prepares the local namespace, and returns the namespace and a function for
// executing the inner effectOp. Mutates fm.local.
func (op nsOp) prepare(fm *Frame) (*Ns, func() Exception) {
if len(op.template.names) > len(fm.local.names) {
n := len(op.template.names)
newLocal := &Ns{make([]vars.Var, n), op.template.names, op.template.deleted}
copy(newLocal.slots, fm.local.slots)
for i := len(fm.local.names); i < n; i++ {
newLocal.slots[i] = MakeVarFromName(newLocal.names[i])
}
fm.local = newLocal
} else {
// If no new has been created, there might still be some existing
// variables deleted.
fm.local = &Ns{fm.local.slots, fm.local.names, op.template.deleted}
}
return fm.local, func() Exception { return op.inner.exec(fm) }
}
const compilationErrorType = "compilation error"
func (cp *compiler) errorpf(r diag.Ranger, format string, args ...interface{}) {
// The panic is caught by the recover in compile above.
panic(&diag.Error{
Type: compilationErrorType,
Message: fmt.Sprintf(format, args...),
Context: *diag.NewContext(cp.srcMeta.Name, cp.srcMeta.Code, r)})
}
// GetCompilationError returns a *diag.Error if the given value is a compilation
// error. Otherwise it returns nil.
func GetCompilationError(e interface{}) *diag.Error {
if e, ok := e.(*diag.Error); ok && e.Type == compilationErrorType {
return e
}
return nil
}
func (cp *compiler) thisScope() *staticNs {
return cp.scopes[len(cp.scopes)-1]
}
func (cp *compiler) pushScope() (*staticNs, *staticUpNs) {
sc := new(staticNs)
up := new(staticUpNs)
cp.scopes = append(cp.scopes, sc)
cp.captures = append(cp.captures, up)
return sc, up
}
func (cp *compiler) popScope() {
cp.scopes[len(cp.scopes)-1] = nil
cp.scopes = cp.scopes[:len(cp.scopes)-1]
cp.captures[len(cp.captures)-1] = nil
cp.captures = cp.captures[:len(cp.captures)-1]
}
func (cp *compiler) checkDeprecatedBuiltin(name string, r diag.Ranger) {
msg := ""
minLevel := 16
switch name {
case "fopen~":
msg = `the "fopen" command is deprecated; use "file:open" instead`
case "fclose~":
msg = `the "fclose" command is deprecated; use "file:close" instead`
case "pipe~":
msg = `the "pipe" command is deprecated; use "file:pipe" instead`
case "prclose~":
msg = `the "prclose" command is deprecated; use "file:prclose" instead`
case "pwclose":
msg = `the "pwclose" command is deprecated; use "file:pwclose" instead`
default:
return
}
cp.deprecate(r, msg, minLevel)
}
func (cp *compiler) deprecate(r diag.Ranger, msg string, minLevel int) {
if cp.warn == nil || r == nil {
return
}
dep := deprecation{cp.srcMeta.Name, r.Range(), msg}
if prog.DeprecationLevel >= minLevel && cp.deprecations.register(dep) {
err := diag.Error{
Type: "deprecation", Message: msg,
Context: diag.Context{
Name: cp.srcMeta.Name, Source: cp.srcMeta.Code, Ranging: r.Range()}}
fmt.Fprintln(cp.warn, err.Show(""))
}
}