mirror of
https://github.com/go-sylixos/elvish.git
synced 2024-12-05 03:17:50 +08:00
123 lines
3.1 KiB
Go
123 lines
3.1 KiB
Go
package eval
|
|
|
|
//go:generate ./boilerplate.py
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/elves/elvish/pkg/diag"
|
|
"github.com/elves/elvish/pkg/parse"
|
|
"github.com/elves/elvish/pkg/util"
|
|
)
|
|
|
|
// compiler maintains the set of states needed when compiling a single source
|
|
// file.
|
|
type compiler struct {
|
|
// Builtin namespace.
|
|
builtin staticNs
|
|
// Lexical namespaces.
|
|
scopes []staticNs
|
|
// Variables captured from outer scopes.
|
|
capture staticNs
|
|
// Position of what is being compiled.
|
|
begin, end int
|
|
// Information about the source.
|
|
srcMeta *Source
|
|
}
|
|
|
|
func compile(b, g staticNs, n *parse.Chunk, src *Source) (op Op, err error) {
|
|
cp := &compiler{b, []staticNs{g}, make(staticNs), 0, 0, src}
|
|
defer util.Catch(&err)
|
|
return Op{cp.chunkOp(n), src}, nil
|
|
}
|
|
|
|
func (cp *compiler) compiling(n parse.Node) {
|
|
cp.begin, cp.end = n.Range().From, n.Range().To
|
|
}
|
|
|
|
func (cp *compiler) errorpf(begin, end int, format string, args ...interface{}) {
|
|
util.Throw(NewCompilationError(fmt.Sprintf(format, args...),
|
|
*diag.NewContext(cp.srcMeta.Name, cp.srcMeta.Code, begin, end)))
|
|
}
|
|
|
|
func (cp *compiler) errorf(format string, args ...interface{}) {
|
|
cp.errorpf(cp.begin, cp.end, format, args...)
|
|
}
|
|
|
|
func (cp *compiler) thisScope() staticNs {
|
|
return cp.scopes[len(cp.scopes)-1]
|
|
}
|
|
|
|
func (cp *compiler) pushScope() staticNs {
|
|
sc := make(staticNs)
|
|
cp.scopes = append(cp.scopes, sc)
|
|
return sc
|
|
}
|
|
|
|
func (cp *compiler) popScope() {
|
|
cp.scopes[len(cp.scopes)-1] = make(staticNs)
|
|
cp.scopes = cp.scopes[:len(cp.scopes)-1]
|
|
}
|
|
|
|
func (cp *compiler) registerVariableSet(qname string) bool {
|
|
return cp.registerVariableAccess(qname, true)
|
|
}
|
|
|
|
func (cp *compiler) registerVariableGet(qname string) bool {
|
|
return cp.registerVariableAccess(qname, false)
|
|
}
|
|
|
|
func (cp *compiler) registerVariableAccess(qname string, set bool) bool {
|
|
readLocal := func(name string) bool { return cp.thisScope().has(name) }
|
|
|
|
readUpvalue := func(name string) bool {
|
|
for i := len(cp.scopes) - 2; i >= 0; i-- {
|
|
if cp.scopes[i].has(name) {
|
|
// Existing name: record capture and return.
|
|
cp.capture.set(name)
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
readBuiltin := func(name string) bool { return cp.builtin.has(name) }
|
|
|
|
readNonPseudo := func(name string) bool {
|
|
return readLocal(name) || readUpvalue(name) || readBuiltin(name)
|
|
}
|
|
|
|
createLocal := func(name string) bool {
|
|
if set && name != "" && !strings.ContainsRune(name[:len(name)-1], ':') {
|
|
cp.thisScope().set(name)
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
ns, name := SplitQNameNsFirst(qname) // ns = "", name = "ns:"
|
|
name1 := name // name1 = "ns:"
|
|
if name != "" && strings.ContainsRune(name[:len(name)-1], ':') {
|
|
name1, _ = SplitQNameNsFirst(name)
|
|
}
|
|
|
|
// This switch mirrors the structure of that from (*Frame).ResoleVar.
|
|
switch ns {
|
|
case "E:":
|
|
return true
|
|
case "e:":
|
|
return !set && strings.HasSuffix(name, FnSuffix)
|
|
case "local:":
|
|
return readLocal(name1) || createLocal(name)
|
|
case "up:":
|
|
return readUpvalue(name1)
|
|
case "builtin:":
|
|
return readBuiltin(name1)
|
|
case "", ":":
|
|
return readNonPseudo(name1) || createLocal(name)
|
|
default:
|
|
return readNonPseudo(ns)
|
|
}
|
|
}
|