2017-12-28 03:59:42 +08:00
|
|
|
package eval
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
2018-09-28 01:50:53 +08:00
|
|
|
"fmt"
|
2017-12-28 03:59:42 +08:00
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"sync"
|
|
|
|
|
2021-01-27 09:28:38 +08:00
|
|
|
"src.elv.sh/pkg/diag"
|
2021-03-24 12:15:27 +08:00
|
|
|
"src.elv.sh/pkg/eval/errs"
|
2021-01-27 09:28:38 +08:00
|
|
|
"src.elv.sh/pkg/parse"
|
|
|
|
"src.elv.sh/pkg/prog"
|
|
|
|
"src.elv.sh/pkg/strutil"
|
2017-12-28 03:59:42 +08:00
|
|
|
)
|
|
|
|
|
2020-09-11 10:07:19 +08:00
|
|
|
// Frame contains information of the current running function, akin to a call
|
2017-12-28 03:59:42 +08:00
|
|
|
// frame in native CPU execution. A Frame is only modified during and very
|
|
|
|
// shortly after creation; new Frame's are "forked" when needed.
|
|
|
|
type Frame struct {
|
2021-01-05 07:54:13 +08:00
|
|
|
Evaler *Evaler
|
2020-03-29 07:56:47 +08:00
|
|
|
|
2020-04-26 02:22:38 +08:00
|
|
|
srcMeta parse.Source
|
2017-12-28 03:59:42 +08:00
|
|
|
|
2020-12-25 01:39:51 +08:00
|
|
|
local, up *Ns
|
2021-12-10 06:10:31 +08:00
|
|
|
defers *[]func(*Frame) Exception
|
2020-03-29 07:56:47 +08:00
|
|
|
|
2020-04-16 06:03:58 +08:00
|
|
|
intCh <-chan struct{}
|
2020-03-29 07:56:47 +08:00
|
|
|
ports []*Port
|
2017-12-28 03:59:42 +08:00
|
|
|
|
2020-09-03 12:54:32 +08:00
|
|
|
traceback *StackTrace
|
2017-12-28 03:59:42 +08:00
|
|
|
|
|
|
|
background bool
|
|
|
|
}
|
|
|
|
|
2021-01-24 22:10:45 +08:00
|
|
|
// PrepareEval prepares a piece of code for evaluation in a copy of the current
|
|
|
|
// Frame. If r is not nil, it is added to the traceback of the evaluation
|
|
|
|
// context. If ns is not nil, it is used in place of the current local namespace
|
|
|
|
// as the namespace to evaluate the code in.
|
|
|
|
//
|
|
|
|
// If there is any parse error or compilation error, it returns a nil *Ns, nil
|
|
|
|
// function and the error. If there is no parse error or compilation error, it
|
|
|
|
// returns the altered local namespace, function that can be called to actuate
|
|
|
|
// the evaluation, and a nil error.
|
|
|
|
func (fm *Frame) PrepareEval(src parse.Source, r diag.Ranger, ns *Ns) (*Ns, func() Exception, error) {
|
2021-02-01 22:17:57 +08:00
|
|
|
tree, err := parse.Parse(src, parse.Config{WarningWriter: fm.ErrorFile()})
|
2021-01-09 22:59:00 +08:00
|
|
|
if err != nil {
|
2021-01-24 22:10:45 +08:00
|
|
|
return nil, nil, err
|
2021-01-09 22:59:00 +08:00
|
|
|
}
|
|
|
|
local := fm.local
|
|
|
|
if ns != nil {
|
|
|
|
local = ns
|
|
|
|
}
|
|
|
|
traceback := fm.traceback
|
|
|
|
if r != nil {
|
|
|
|
traceback = fm.addTraceback(r)
|
|
|
|
}
|
|
|
|
newFm := &Frame{
|
2021-12-10 06:10:31 +08:00
|
|
|
fm.Evaler, src, local, new(Ns), nil, fm.intCh, fm.ports, traceback, fm.background}
|
2021-01-09 22:59:00 +08:00
|
|
|
op, err := compile(newFm.Evaler.Builtin().static(), local.static(), tree, fm.ErrorFile())
|
2021-01-24 22:10:45 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
newLocal, exec := op.prepare(newFm)
|
|
|
|
return newLocal, exec, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Eval evaluates a piece of code in a copy of the current Frame. It returns the
|
|
|
|
// altered local namespace, and any parse error, compilation error or exception.
|
|
|
|
//
|
|
|
|
// See PrepareEval for a description of the arguments.
|
|
|
|
func (fm *Frame) Eval(src parse.Source, r diag.Ranger, ns *Ns) (*Ns, error) {
|
|
|
|
newLocal, exec, err := fm.PrepareEval(src, r, ns)
|
2021-01-09 22:59:00 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-01-24 22:10:45 +08:00
|
|
|
return newLocal, exec()
|
2021-01-09 22:59:00 +08:00
|
|
|
}
|
|
|
|
|
2018-03-01 10:03:04 +08:00
|
|
|
// Close releases resources allocated for this frame. It always returns a nil
|
|
|
|
// error. It may be called only once.
|
|
|
|
func (fm *Frame) Close() error {
|
|
|
|
for _, port := range fm.ports {
|
2021-01-05 12:07:35 +08:00
|
|
|
port.close()
|
2018-03-01 10:03:04 +08:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-12-28 03:59:42 +08:00
|
|
|
// InputChan returns a channel from which input can be read.
|
2018-03-01 10:17:56 +08:00
|
|
|
func (fm *Frame) InputChan() chan interface{} {
|
|
|
|
return fm.ports[0].Chan
|
2017-12-28 03:59:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// InputFile returns a file from which input can be read.
|
2018-03-01 10:17:56 +08:00
|
|
|
func (fm *Frame) InputFile() *os.File {
|
|
|
|
return fm.ports[0].File
|
2017-12-28 03:59:42 +08:00
|
|
|
}
|
|
|
|
|
2021-06-18 07:11:51 +08:00
|
|
|
// ValueOutput returns a handle for writing value outputs.
|
|
|
|
func (fm *Frame) ValueOutput() ValueOutput {
|
2021-06-22 07:33:41 +08:00
|
|
|
p := fm.ports[1]
|
|
|
|
return valueOutput{p.Chan, p.sendStop, p.sendError}
|
2017-12-28 03:59:42 +08:00
|
|
|
}
|
|
|
|
|
2021-06-12 05:20:27 +08:00
|
|
|
// ByteOutput returns a handle for writing byte outputs.
|
|
|
|
func (fm *Frame) ByteOutput() ByteOutput {
|
|
|
|
return byteOutput{fm.ports[1].File}
|
2017-12-28 03:59:42 +08:00
|
|
|
}
|
|
|
|
|
2021-06-18 07:11:51 +08:00
|
|
|
// ErrorFile returns a file onto which error messages can be written.
|
2020-09-05 06:07:04 +08:00
|
|
|
func (fm *Frame) ErrorFile() *os.File {
|
|
|
|
return fm.ports[2].File
|
|
|
|
}
|
|
|
|
|
2017-12-28 03:59:42 +08:00
|
|
|
// IterateInputs calls the passed function for each input element.
|
2018-03-01 10:17:56 +08:00
|
|
|
func (fm *Frame) IterateInputs(f func(interface{})) {
|
2021-05-18 10:42:08 +08:00
|
|
|
var wg sync.WaitGroup
|
2018-01-30 01:39:41 +08:00
|
|
|
inputs := make(chan interface{})
|
2017-12-28 03:59:42 +08:00
|
|
|
|
2021-05-18 10:42:08 +08:00
|
|
|
wg.Add(2)
|
2017-12-28 03:59:42 +08:00
|
|
|
go func() {
|
2020-09-05 06:07:04 +08:00
|
|
|
linesToChan(fm.InputFile(), inputs)
|
2021-05-18 10:42:08 +08:00
|
|
|
wg.Done()
|
2017-12-28 03:59:42 +08:00
|
|
|
}()
|
|
|
|
go func() {
|
2018-03-01 10:17:56 +08:00
|
|
|
for v := range fm.ports[0].Chan {
|
2017-12-28 03:59:42 +08:00
|
|
|
inputs <- v
|
|
|
|
}
|
2021-05-18 10:42:08 +08:00
|
|
|
wg.Done()
|
2017-12-28 03:59:42 +08:00
|
|
|
}()
|
|
|
|
go func() {
|
2021-05-18 10:42:08 +08:00
|
|
|
wg.Wait()
|
2017-12-28 03:59:42 +08:00
|
|
|
close(inputs)
|
|
|
|
}()
|
|
|
|
|
|
|
|
for v := range inputs {
|
|
|
|
f(v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-30 01:39:41 +08:00
|
|
|
func linesToChan(r io.Reader, ch chan<- interface{}) {
|
2017-12-28 03:59:42 +08:00
|
|
|
filein := bufio.NewReader(r)
|
|
|
|
for {
|
|
|
|
line, err := filein.ReadString('\n')
|
|
|
|
if line != "" {
|
2020-09-05 04:13:59 +08:00
|
|
|
ch <- strutil.ChopLineEnding(line)
|
2017-12-28 03:59:42 +08:00
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
if err != io.EOF {
|
|
|
|
logger.Println("error on reading:", err)
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-30 23:01:43 +08:00
|
|
|
// Fork returns a modified copy of fm. The ports are forked, and the name is
|
2017-12-28 03:59:42 +08:00
|
|
|
// changed to the given value. Other fields are copied shallowly.
|
2021-12-30 23:01:43 +08:00
|
|
|
func (fm *Frame) Fork(name string) *Frame {
|
2018-03-01 10:17:56 +08:00
|
|
|
newPorts := make([]*Port, len(fm.ports))
|
|
|
|
for i, p := range fm.ports {
|
2020-01-12 08:42:18 +08:00
|
|
|
if p != nil {
|
2021-01-05 12:07:35 +08:00
|
|
|
newPorts[i] = p.fork()
|
2020-01-12 08:42:18 +08:00
|
|
|
}
|
2017-12-28 03:59:42 +08:00
|
|
|
}
|
|
|
|
return &Frame{
|
2020-04-09 06:33:22 +08:00
|
|
|
fm.Evaler, fm.srcMeta,
|
2021-12-10 06:10:31 +08:00
|
|
|
fm.local, fm.up, fm.defers,
|
2020-03-29 07:56:47 +08:00
|
|
|
fm.intCh, newPorts,
|
2020-03-29 06:03:42 +08:00
|
|
|
fm.traceback, fm.background,
|
2017-12-28 03:59:42 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-02 09:40:04 +08:00
|
|
|
// A shorthand for forking a frame and setting the output port.
|
|
|
|
func (fm *Frame) forkWithOutput(name string, p *Port) *Frame {
|
2021-12-30 23:01:43 +08:00
|
|
|
newFm := fm.Fork(name)
|
2021-01-02 09:40:04 +08:00
|
|
|
newFm.ports[1] = p
|
|
|
|
return newFm
|
|
|
|
}
|
|
|
|
|
2020-04-10 08:12:25 +08:00
|
|
|
// CaptureOutput captures the output of a given callback that operates on a Frame.
|
|
|
|
func (fm *Frame) CaptureOutput(f func(*Frame) error) ([]interface{}, error) {
|
2021-01-02 09:40:04 +08:00
|
|
|
outPort, collect, err := CapturePort()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
err = f(fm.forkWithOutput("[output capture]", outPort))
|
|
|
|
return collect(), err
|
2017-12-28 03:59:42 +08:00
|
|
|
}
|
|
|
|
|
2020-04-10 08:12:25 +08:00
|
|
|
// PipeOutput calls a callback with output piped to the given output handlers.
|
2021-01-02 09:40:04 +08:00
|
|
|
func (fm *Frame) PipeOutput(f func(*Frame) error, vCb func(<-chan interface{}), bCb func(*os.File)) error {
|
|
|
|
outPort, done, err := PipePort(vCb, bCb)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = f(fm.forkWithOutput("[output pipe]", outPort))
|
|
|
|
done()
|
|
|
|
return err
|
2019-12-08 08:29:19 +08:00
|
|
|
}
|
|
|
|
|
2020-09-03 12:54:32 +08:00
|
|
|
func (fm *Frame) addTraceback(r diag.Ranger) *StackTrace {
|
|
|
|
return &StackTrace{
|
|
|
|
Head: diag.NewContext(fm.srcMeta.Name, fm.srcMeta.Code, r.Range()),
|
|
|
|
Next: fm.traceback,
|
2020-04-09 06:33:22 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns an Exception with specified range and cause.
|
2021-01-09 08:24:19 +08:00
|
|
|
func (fm *Frame) errorp(r diag.Ranger, e error) Exception {
|
2020-01-13 07:00:45 +08:00
|
|
|
switch e := e.(type) {
|
|
|
|
case nil:
|
|
|
|
return nil
|
2021-01-09 08:24:19 +08:00
|
|
|
case Exception:
|
2020-01-13 07:00:45 +08:00
|
|
|
return e
|
|
|
|
default:
|
2021-03-24 12:15:27 +08:00
|
|
|
ctx := diag.NewContext(fm.srcMeta.Name, fm.srcMeta.Code, r)
|
2021-05-02 07:13:22 +08:00
|
|
|
if _, ok := e.(errs.SetReadOnlyVar); ok {
|
|
|
|
e = errs.SetReadOnlyVar{VarName: ctx.RelevantString()}
|
|
|
|
}
|
2021-03-24 12:15:27 +08:00
|
|
|
return &exception{e, &StackTrace{Head: ctx, Next: fm.traceback}}
|
2017-12-28 03:59:42 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-09 06:33:22 +08:00
|
|
|
// Returns an Exception with specified range and error text.
|
2021-01-09 08:24:19 +08:00
|
|
|
func (fm *Frame) errorpf(r diag.Ranger, format string, args ...interface{}) Exception {
|
2020-04-09 06:33:22 +08:00
|
|
|
return fm.errorp(r, fmt.Errorf(format, args...))
|
2017-12-28 03:59:42 +08:00
|
|
|
}
|
2020-04-26 08:13:05 +08:00
|
|
|
|
|
|
|
// Deprecate shows a deprecation message. The message is not shown if the same
|
|
|
|
// deprecation message has been shown for the same location before.
|
2021-01-24 23:32:24 +08:00
|
|
|
func (fm *Frame) Deprecate(msg string, ctx *diag.Context, minLevel int) {
|
|
|
|
if prog.DeprecationLevel < minLevel {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if ctx == nil {
|
|
|
|
fmt.Fprintf(fm.ErrorFile(), "deprecation: \033[31;1m%s\033[m\n", msg)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if fm.Evaler.registerDeprecation(deprecation{ctx.Name, ctx.Ranging, msg}) {
|
|
|
|
err := diag.Error{Type: "deprecation", Message: msg, Context: *ctx}
|
2020-11-17 11:34:35 +08:00
|
|
|
fm.ErrorFile().WriteString(err.Show("") + "\n")
|
2020-04-26 08:13:05 +08:00
|
|
|
}
|
|
|
|
}
|
2021-12-10 06:10:31 +08:00
|
|
|
|
|
|
|
func (fm *Frame) addDefer(f func(*Frame) Exception) {
|
|
|
|
*fm.defers = append(*fm.defers, f)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (fm *Frame) runDefers() Exception {
|
|
|
|
var exc Exception
|
|
|
|
defers := *fm.defers
|
|
|
|
for i := len(defers) - 1; i >= 0; i-- {
|
|
|
|
exc2 := defers[i](fm)
|
|
|
|
// TODO: Combine exc and exc2 if both are not nil
|
|
|
|
if exc2 != nil && exc == nil {
|
|
|
|
exc = exc2
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return exc
|
|
|
|
}
|