2014-03-03 13:41:44 +08:00
|
|
|
package eval
|
|
|
|
|
2014-04-02 10:51:09 +08:00
|
|
|
// Builtin special forms.
|
2014-03-03 13:41:44 +08:00
|
|
|
|
2015-01-25 06:45:34 +08:00
|
|
|
import (
|
2015-01-25 07:31:52 +08:00
|
|
|
"os"
|
2015-01-25 06:45:34 +08:00
|
|
|
|
2016-01-25 01:10:54 +08:00
|
|
|
"github.com/elves/elvish/parse"
|
2015-01-25 06:45:34 +08:00
|
|
|
)
|
2014-03-03 13:41:44 +08:00
|
|
|
|
2016-02-08 06:23:16 +08:00
|
|
|
type compileBuiltin func(*compiler, *parse.Form) Op
|
2014-04-02 10:51:09 +08:00
|
|
|
|
2016-01-23 01:05:15 +08:00
|
|
|
var builtinSpecials map[string]compileBuiltin
|
2016-02-08 06:23:16 +08:00
|
|
|
|
|
|
|
// BuiltinSpecialNames contains all names of builtin special forms. It is
|
|
|
|
// useful for the syntax highlighter.
|
2015-07-06 23:58:51 +08:00
|
|
|
var BuiltinSpecialNames []string
|
2014-04-02 10:51:09 +08:00
|
|
|
|
|
|
|
func init() {
|
|
|
|
// Needed to avoid initialization loop
|
2016-01-23 01:05:15 +08:00
|
|
|
builtinSpecials = map[string]compileBuiltin{
|
|
|
|
"del": compileDel,
|
2016-01-23 08:24:47 +08:00
|
|
|
"fn": compileFn,
|
|
|
|
//"use": compileUse,
|
2014-04-02 10:51:09 +08:00
|
|
|
}
|
2016-02-08 06:23:16 +08:00
|
|
|
for k := range builtinSpecials {
|
2015-07-06 23:58:51 +08:00
|
|
|
BuiltinSpecialNames = append(BuiltinSpecialNames, k)
|
|
|
|
}
|
2014-04-02 10:51:09 +08:00
|
|
|
}
|
|
|
|
|
2016-02-01 18:42:28 +08:00
|
|
|
func doSet(ec *evalCtx, variables []Variable, values []Value) {
|
2014-03-20 13:50:13 +08:00
|
|
|
// TODO Support assignment of mismatched arity in some restricted way -
|
|
|
|
// "optional" and "rest" arguments and the like
|
2016-02-01 18:42:28 +08:00
|
|
|
if len(variables) != len(values) {
|
2016-02-08 06:23:16 +08:00
|
|
|
throw(ErrArityMismatch)
|
2014-03-20 13:50:13 +08:00
|
|
|
}
|
|
|
|
|
2016-02-01 18:42:28 +08:00
|
|
|
for i, variable := range variables {
|
2014-03-20 13:50:13 +08:00
|
|
|
// TODO Prevent overriding builtin variables e.g. $pid $env
|
2016-02-01 18:42:28 +08:00
|
|
|
variable.Set(values[i])
|
2014-03-20 13:50:13 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-22 00:22:55 +08:00
|
|
|
// DelForm = 'del' { VariablePrimary }
|
2016-02-08 06:23:16 +08:00
|
|
|
func compileDel(cp *compiler, fn *parse.Form) Op {
|
2014-09-16 23:57:33 +08:00
|
|
|
// Do conventional compiling of all compound expressions, including
|
|
|
|
// ensuring that variables can be resolved
|
2015-01-25 07:05:47 +08:00
|
|
|
var names, envNames []string
|
2016-01-23 07:56:09 +08:00
|
|
|
for _, cn := range fn.Args {
|
|
|
|
qname := mustString(cp, cn, "should be a literal variable name")
|
2016-02-07 10:12:54 +08:00
|
|
|
splice, ns, name := parseVariable(qname)
|
|
|
|
if splice {
|
|
|
|
cp.errorf(cn.Begin(), "removing spliced variable makes no sense")
|
|
|
|
}
|
2015-01-25 07:05:47 +08:00
|
|
|
switch ns {
|
|
|
|
case "", "local":
|
2016-01-23 07:56:09 +08:00
|
|
|
if !cp.thisScope()[name] {
|
2016-01-25 02:04:15 +08:00
|
|
|
cp.errorf(cn.Begin(), "variable $%s not found on current local scope", name)
|
2015-01-25 07:05:47 +08:00
|
|
|
}
|
2016-01-23 07:56:09 +08:00
|
|
|
delete(cp.thisScope(), name)
|
2015-01-25 07:05:47 +08:00
|
|
|
names = append(names, name)
|
|
|
|
case "env":
|
|
|
|
envNames = append(envNames, name)
|
|
|
|
default:
|
2016-01-25 02:04:15 +08:00
|
|
|
cp.errorf(cn.Begin(), "can only delete a variable in local: or env:")
|
2014-04-02 11:13:43 +08:00
|
|
|
}
|
2015-01-22 00:22:55 +08:00
|
|
|
|
2014-04-02 11:13:43 +08:00
|
|
|
}
|
2016-01-31 09:00:31 +08:00
|
|
|
return func(ec *evalCtx) {
|
2014-09-26 05:26:56 +08:00
|
|
|
for _, name := range names {
|
2015-02-25 19:21:06 +08:00
|
|
|
delete(ec.local, name)
|
2014-04-30 10:45:54 +08:00
|
|
|
}
|
2015-01-25 07:05:47 +08:00
|
|
|
for _, name := range envNames {
|
2015-02-26 22:17:05 +08:00
|
|
|
// BUG(xiaq): We rely on the fact that os.Unsetenv always returns
|
|
|
|
// nil.
|
2015-01-25 07:31:52 +08:00
|
|
|
os.Unsetenv(name)
|
2015-01-25 07:05:47 +08:00
|
|
|
}
|
2014-04-02 11:13:43 +08:00
|
|
|
}
|
|
|
|
}
|
2015-01-20 05:11:44 +08:00
|
|
|
|
2016-01-23 07:56:09 +08:00
|
|
|
/*
|
2015-02-10 21:50:58 +08:00
|
|
|
func stem(fname string) string {
|
|
|
|
base := path.Base(fname)
|
|
|
|
ext := path.Ext(base)
|
|
|
|
return base[0 : len(base)-len(ext)]
|
|
|
|
}
|
|
|
|
|
|
|
|
// UseForm = 'use' StringPrimary.modname Primary.fname
|
|
|
|
// = 'use' StringPrimary.fname
|
2016-01-31 09:00:31 +08:00
|
|
|
func compileUse(cp *compiler, fn *parse.Form) op {
|
2015-02-25 04:59:14 +08:00
|
|
|
var fnameNode *parse.Compound
|
2015-02-10 21:50:58 +08:00
|
|
|
var fname, modname string
|
|
|
|
|
|
|
|
switch len(fn.Args.Nodes) {
|
|
|
|
case 0:
|
2016-01-23 01:05:15 +08:00
|
|
|
cp.errorf(fn.Args.Pos, "expect module name or file name")
|
2015-02-10 21:50:58 +08:00
|
|
|
case 1, 2:
|
|
|
|
fnameNode = fn.Args.Nodes[0]
|
2016-01-23 01:05:15 +08:00
|
|
|
_, fname = ensureStringPrimary(cp, fnameNode, "expect string literal")
|
2015-02-10 21:50:58 +08:00
|
|
|
if len(fn.Args.Nodes) == 2 {
|
|
|
|
modnameNode := fn.Args.Nodes[1]
|
|
|
|
_, modname = ensureStringPrimary(
|
2016-01-23 01:05:15 +08:00
|
|
|
cp, modnameNode, "expect string literal")
|
2015-02-10 21:50:58 +08:00
|
|
|
if modname == "" {
|
2016-01-23 01:05:15 +08:00
|
|
|
cp.errorf(modnameNode.Pos, "module name is empty")
|
2015-02-10 21:50:58 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
modname = stem(fname)
|
|
|
|
if modname == "" {
|
2016-01-23 01:05:15 +08:00
|
|
|
cp.errorf(fnameNode.Pos, "stem of file name is empty")
|
2015-02-10 21:50:58 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
default:
|
2016-01-23 01:05:15 +08:00
|
|
|
cp.errorf(fn.Args.Nodes[2].Pos, "superfluous argument")
|
2015-02-10 21:50:58 +08:00
|
|
|
}
|
2015-02-24 01:36:46 +08:00
|
|
|
switch {
|
|
|
|
case strings.HasPrefix(fname, "/"):
|
|
|
|
// Absolute file name, do nothing
|
|
|
|
case strings.HasPrefix(fname, "./") || strings.HasPrefix(fname, "../"):
|
|
|
|
// File name relative to current source
|
2016-01-23 01:05:15 +08:00
|
|
|
fname = path.Clean(path.Join(cp.dir, fname))
|
2015-02-24 01:36:46 +08:00
|
|
|
default:
|
|
|
|
// File name relative to data dir
|
2016-01-23 01:05:15 +08:00
|
|
|
fname = path.Clean(path.Join(cp.dataDir, fname))
|
2015-02-24 01:36:46 +08:00
|
|
|
}
|
2015-02-10 21:50:58 +08:00
|
|
|
src, err := readFileUTF8(fname)
|
|
|
|
if err != nil {
|
2016-01-23 01:05:15 +08:00
|
|
|
cp.errorf(fnameNode.Pos, "cannot read module: %s", err.Error())
|
2015-02-10 21:50:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
cn, err := parse.Parse(fname, src)
|
|
|
|
if err != nil {
|
|
|
|
// TODO(xiaq): Pretty print
|
2016-01-23 01:05:15 +08:00
|
|
|
cp.errorf(fnameNode.Pos, "cannot parse module: %s", err.Error())
|
2015-02-10 21:50:58 +08:00
|
|
|
}
|
|
|
|
|
2016-01-23 01:05:15 +08:00
|
|
|
newCc := &compiler{
|
|
|
|
cp.Compiler,
|
2015-02-25 22:04:10 +08:00
|
|
|
fname, src, path.Dir(fname),
|
|
|
|
[]staticNS{staticNS{}}, staticNS{},
|
|
|
|
}
|
|
|
|
|
|
|
|
op, err := newCc.compile(cn)
|
2015-02-10 21:50:58 +08:00
|
|
|
if err != nil {
|
|
|
|
// TODO(xiaq): Pretty print
|
2016-01-23 01:05:15 +08:00
|
|
|
cp.errorf(fnameNode.Pos, "cannot compile module: %s", err.Error())
|
2015-02-10 21:50:58 +08:00
|
|
|
}
|
|
|
|
|
2016-01-23 01:05:15 +08:00
|
|
|
cp.mod[modname] = newCc.scopes[0]
|
2015-02-10 21:50:58 +08:00
|
|
|
|
2015-02-25 19:21:06 +08:00
|
|
|
return func(ec *evalCtx) exitus {
|
2015-07-08 06:12:24 +08:00
|
|
|
// TODO(xiaq): Should handle failures when evaluting the module
|
2015-02-25 19:21:06 +08:00
|
|
|
newEc := &evalCtx{
|
|
|
|
ec.Evaler,
|
|
|
|
fname, src, "module " + modname,
|
|
|
|
ns{}, ns{},
|
2015-07-08 06:12:24 +08:00
|
|
|
ec.ports,
|
2015-02-25 19:21:06 +08:00
|
|
|
}
|
2015-07-08 06:12:24 +08:00
|
|
|
op.f(newEc)
|
2015-02-25 19:21:06 +08:00
|
|
|
ec.mod[modname] = newEc.local
|
2015-08-23 22:46:33 +08:00
|
|
|
return ok
|
2015-02-10 21:50:58 +08:00
|
|
|
}
|
|
|
|
}
|
2016-01-23 08:24:47 +08:00
|
|
|
*/
|
2015-02-10 21:50:58 +08:00
|
|
|
|
2016-01-31 09:00:31 +08:00
|
|
|
// makeFnOp wraps an op such that a return is converted to an ok.
|
2016-02-08 06:23:16 +08:00
|
|
|
func makeFnOp(op Op) Op {
|
2016-01-31 09:00:31 +08:00
|
|
|
return func(ec *evalCtx) {
|
2016-02-09 03:27:05 +08:00
|
|
|
ex := ec.PEval(op)
|
2016-01-31 09:00:31 +08:00
|
|
|
if ex != Return {
|
2016-01-31 09:11:10 +08:00
|
|
|
// rethrow
|
|
|
|
throw(ex)
|
2015-07-08 18:31:40 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-27 00:42:37 +08:00
|
|
|
// FnForm = 'fn' StringPrimary LambdaPrimary
|
2015-01-22 01:06:14 +08:00
|
|
|
//
|
2016-01-28 05:24:17 +08:00
|
|
|
// fn f []{foobar} is a shorthand for set '&'f = []{foobar}.
|
2016-02-08 06:23:16 +08:00
|
|
|
func compileFn(cp *compiler, fn *parse.Form) Op {
|
2016-01-23 08:24:47 +08:00
|
|
|
if len(fn.Args) == 0 {
|
2016-01-25 02:04:15 +08:00
|
|
|
cp.errorf(fn.End(), "should be followed by function name")
|
2015-01-22 01:06:14 +08:00
|
|
|
}
|
2016-01-23 08:24:47 +08:00
|
|
|
fnName := mustString(cp, fn.Args[0], "must be a literal string")
|
2016-01-27 00:42:37 +08:00
|
|
|
varName := FnPrefix + fnName
|
2015-01-22 01:06:14 +08:00
|
|
|
|
2016-01-23 08:24:47 +08:00
|
|
|
if len(fn.Args) == 1 {
|
2016-01-25 02:04:15 +08:00
|
|
|
cp.errorf(fn.Args[0].End(), "should be followed by a lambda")
|
2015-01-22 01:06:14 +08:00
|
|
|
}
|
2016-01-23 08:24:47 +08:00
|
|
|
pn := mustPrimary(cp, fn.Args[1], "should be a lambda")
|
|
|
|
if pn.Type != parse.Lambda {
|
2016-01-25 02:04:15 +08:00
|
|
|
cp.errorf(pn.Begin(), "should be a lambda")
|
2016-01-23 08:24:47 +08:00
|
|
|
}
|
|
|
|
if len(fn.Args) > 2 {
|
2016-01-25 02:04:15 +08:00
|
|
|
cp.errorf(fn.Args[2].Begin(), "superfluous argument")
|
2015-01-22 01:06:14 +08:00
|
|
|
}
|
|
|
|
|
2016-01-26 06:45:51 +08:00
|
|
|
cp.registerVariableSet(":" + varName)
|
2016-01-23 08:24:47 +08:00
|
|
|
op := cp.lambda(pn)
|
2015-01-22 01:06:14 +08:00
|
|
|
|
2016-01-31 09:00:31 +08:00
|
|
|
return func(ec *evalCtx) {
|
2016-01-29 20:55:14 +08:00
|
|
|
closure := op(ec)[0].(*Closure)
|
2015-07-08 18:31:40 +08:00
|
|
|
closure.Op = makeFnOp(closure.Op)
|
2016-01-29 20:59:00 +08:00
|
|
|
ec.local[varName] = newPtrVariable(closure)
|
2015-01-22 01:06:14 +08:00
|
|
|
}
|
|
|
|
}
|