package eval
import (
)
var logger = logutil.GetLogger("[eval] ")
const (
FnSuffix = "~"
NsSuffix = ":"
)
const (
defaultValuePrefix = "▶ "
defaultNotifyBgJobSuccess = true
initIndent = vals.NoPretty
)
type Evaler struct {
mu sync.RWMutex
global, builtin *Ns
deprecations deprecationRegistry
libDir string
modules map[string]*Ns
valuePrefix string
notifyBgJobSuccess bool
numBgJobs int
args vals.List
beforeChdir, afterChdir []func(string)
daemonClient daemon.Client
}
type Editor interface {
RunAfterCommandHooks(src parse.Source, duration float64, err error)
}
func () *Evaler {
:= builtinNs.Ns()
, := vector.Empty, vector.Empty
:= &Evaler{
global: new(Ns),
builtin: ,
deprecations: newDeprecationRegistry(),
modules: map[string]*Ns{"builtin": },
valuePrefix: defaultValuePrefix,
notifyBgJobSuccess: defaultNotifyBgJobSuccess,
numBgJobs: 0,
args: vals.EmptyList,
}
.beforeChdir = []func(string){
adaptChdirHook("before-chdir", , &)}
.afterChdir = []func(string){
adaptChdirHook("after-chdir", , &)}
:= NsBuilder{}.
Add("pwd", NewPwdVar()).
Add("before-chdir", vars.FromPtr(&)).
Add("after-chdir", vars.FromPtr(&)).
Add("value-out-indicator", vars.FromPtrWithMutex(
&.valuePrefix, &.mu)).
Add("notify-bg-job-success", vars.FromPtrWithMutex(
&.notifyBgJobSuccess, &.mu)).
Add("num-bg-jobs", vars.FromGet(func() interface{} {
return strconv.Itoa(.getNumBgJobs())
})).
Add("args", vars.FromGet(func() interface{} {
return .getArgs()
})).
Ns()
.slots = append(.slots, .slots...)
.names = append(.names, .names...)
.deleted = append(.deleted, make([]bool, len(.names))...)
return
}
func ( string, *Evaler, *vector.Vector) func(string) {
return func( string) {
, := PortsFromStdFiles(.ValuePrefix())
defer ()
:= CallCfg{Args: []interface{}{}, From: "[hook " + + "]"}
:= EvalCfg{Ports: [:]}
for := (*).Iterator(); .HasElem(); .Next() {
, := .Elem().(Callable)
if ! {
fmt.Fprintln(os.Stderr, , "hook must be callable")
continue
}
:= .Call(, , )
if != nil {
fmt.Fprintln(os.Stderr, )
}
}
}
}
func ( *Evaler) () *Ns {
.mu.RLock()
defer .mu.RUnlock()
return .global
}
func ( *Evaler) ( *Ns) {
.mu.Lock()
defer .mu.Unlock()
.global = CombineNs(.global, )
}
func ( *Evaler) () *Ns {
.mu.RLock()
defer .mu.RUnlock()
return .builtin
}
func ( *Evaler) ( *Ns) {
.mu.Lock()
defer .mu.Unlock()
.builtin = CombineNs(.builtin, )
}
func ( *Evaler) ( deprecation) bool {
.mu.Lock()
defer .mu.Unlock()
return .deprecations.register()
}
func ( *Evaler) () string {
.mu.RLock()
defer .mu.RUnlock()
return .libDir
}
func ( *Evaler) ( string) {
.mu.Lock()
defer .mu.Unlock()
.libDir =
}
func ( *Evaler) ( string, *Ns) {
.mu.Lock()
defer .mu.Unlock()
.modules[] =
}
func ( *Evaler) () string {
.mu.RLock()
defer .mu.RUnlock()
return .valuePrefix
}
func ( *Evaler) () bool {
.mu.RLock()
defer .mu.RUnlock()
return .notifyBgJobSuccess
}
func ( *Evaler) () int {
.mu.RLock()
defer .mu.RUnlock()
return .numBgJobs
}
func ( *Evaler) ( int) {
.mu.Lock()
defer .mu.Unlock()
.numBgJobs +=
}
func ( *Evaler) () vals.List {
.mu.RLock()
defer .mu.RUnlock()
return .args
}
func ( *Evaler) ( []string) {
:= listOfStrings()
.mu.Lock()
defer .mu.Unlock()
.args =
}
func ( *Evaler) () ([]func(string), []func(string)) {
.mu.RLock()
defer .mu.RUnlock()
return append(([]func(string))(nil), .beforeChdir...),
append(([]func(string))(nil), .afterChdir...)
}
func ( *Evaler) ( func(string)) {
.mu.Lock()
defer .mu.Unlock()
.beforeChdir = append(.beforeChdir, )
}
func ( *Evaler) ( func(string)) {
.mu.Lock()
defer .mu.Unlock()
.afterChdir = append(.afterChdir, )
}
func ( *Evaler) ( daemon.Client) {
.mu.Lock()
defer .mu.Unlock()
.daemonClient =
}
func ( *Evaler) () daemon.Client {
.mu.RLock()
defer .mu.RUnlock()
return .daemonClient
}
func ( *Evaler) ( string) error {
, := .chdirHooks()
for , := range {
()
}
:= os.Chdir()
if != nil {
return
}
for , := range {
()
}
, := os.Getwd()
if != nil {
logger.Println("getwd after cd:", )
return nil
}
os.Setenv(env.PWD, )
return nil
}
type EvalCfg struct {
Ports []*Port
Interrupt func() (<-chan struct{}, func())
PutInFg bool
Global *Ns
}
func ( *EvalCfg) () {
if len(.Ports) < 3 {
.Ports = append(.Ports, make([]*Port, 3-len(.Ports))...)
}
if .Ports[0] == nil {
.Ports[0] = DummyInputPort
}
if .Ports[1] == nil {
.Ports[1] = DummyOutputPort
}
if .Ports[2] == nil {
.Ports[2] = DummyOutputPort
}
}
func ( *Evaler) ( parse.Source, EvalCfg) error {
.fillDefaults()
:= .Ports[2].File
, := parse.Parse(, parse.Config{WarningWriter: })
if != nil {
return
}
.mu.Lock()
:= .builtin
:= .Global == nil
if {
.Global = .global
} else {
.mu.Unlock()
}
, := compile(.static(), .Global.static(), , )
if != nil {
if {
.mu.Unlock()
}
return
}
, := .prepareFrame(, )
defer ()
, := .prepare()
if {
.global =
.mu.Unlock()
}
return ()
}
type CallCfg struct {
Args []interface{}
Opts map[string]interface{}
From string
}
func ( *CallCfg) () {
if .Opts == nil {
.Opts = NoOpts
}
if .From == "" {
.From = "[internal]"
}
}
func ( *Evaler) ( Callable, CallCfg, EvalCfg) error {
.fillDefaults()
.fillDefaults()
if .Global == nil {
.Global = .Global()
}
, := .prepareFrame(parse.Source{Name: .From}, )
defer ()
return .Call(, .Args, .Opts)
}
func ( *Evaler) ( parse.Source, EvalCfg) (*Frame, func()) {
var <-chan struct{}
var func()
if .Interrupt != nil {
, = .Interrupt()
}
:= &Frame{, , .Global, new(Ns), , .Ports, nil, false}
return , func() {
if != nil {
()
}
if .PutInFg {
:= putSelfInFg()
if != nil {
fmt.Fprintln(.Ports[2].File,
"failed to put myself in foreground:", )
}
}
}
}
func ( *Evaler) ( parse.Source, io.Writer) (*parse.Error, *diag.Error) {
, := parse.Parse(, parse.Config{WarningWriter: })
return parse.GetError(), .CheckTree(, )
}
func ( *Evaler) ( parse.Tree, io.Writer) *diag.Error {
, := .compile(, .Global(), )
return GetCompilationError()
}
func ( *Evaler) ( parse.Tree, *Ns, io.Writer) (nsOp, error) {
return compile(.Builtin().static(), .static(), , )
}