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-20 05:26:45 +08:00
|
|
|
import "github.com/elves/elvish/parse"
|
2014-03-03 13:41:44 +08:00
|
|
|
|
2015-01-22 06:43:30 +08:00
|
|
|
type exitusOp func(*Evaluator) Exitus
|
|
|
|
type builtinSpecialCompile func(*Compiler, *parse.FormNode) exitusOp
|
2014-04-02 10:51:09 +08:00
|
|
|
|
|
|
|
type builtinSpecial struct {
|
2014-04-30 10:45:54 +08:00
|
|
|
compile builtinSpecialCompile
|
2014-04-02 10:51:09 +08:00
|
|
|
streamTypes [2]StreamType
|
|
|
|
}
|
|
|
|
|
|
|
|
var builtinSpecials map[string]builtinSpecial
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
// Needed to avoid initialization loop
|
|
|
|
builtinSpecials = map[string]builtinSpecial{
|
2014-04-30 10:45:54 +08:00
|
|
|
"var": builtinSpecial{compileVar, [2]StreamType{}},
|
|
|
|
"set": builtinSpecial{compileSet, [2]StreamType{}},
|
|
|
|
"del": builtinSpecial{compileDel, [2]StreamType{}},
|
2015-01-22 03:36:41 +08:00
|
|
|
|
|
|
|
"fn": builtinSpecial{compileFn, [2]StreamType{}},
|
|
|
|
|
2015-01-20 05:11:44 +08:00
|
|
|
"static-typeof": builtinSpecial{
|
|
|
|
compileStaticTypeof, [2]StreamType{0, fdStream}},
|
2014-04-02 10:51:09 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-23 02:22:03 +08:00
|
|
|
func mayAssign(tvar, tval Type) bool {
|
|
|
|
if isAny(tval) || isAny(tvar) {
|
|
|
|
return true
|
|
|
|
}
|
2015-01-23 07:38:44 +08:00
|
|
|
// XXX(xiaq) This is not how you check the equality of two interfaces. But
|
|
|
|
// it happens to work when all the Type instances we have are empty
|
|
|
|
// structs.
|
2015-01-23 02:22:03 +08:00
|
|
|
return tval == tvar
|
|
|
|
}
|
|
|
|
|
2014-09-26 05:26:56 +08:00
|
|
|
func checkSetType(cp *Compiler, names []string, values []*parse.CompoundNode, vop valuesOp, p parse.Pos) {
|
|
|
|
if !vop.tr.mayCountTo(len(names)) {
|
|
|
|
cp.errorf(p, "number of variables doesn't match that of values")
|
2014-07-19 16:53:31 +08:00
|
|
|
}
|
2014-09-17 23:45:32 +08:00
|
|
|
_, more := vop.tr.count()
|
|
|
|
if more {
|
|
|
|
// TODO Try to check soundness to some extent
|
|
|
|
return
|
|
|
|
}
|
2014-09-26 05:26:56 +08:00
|
|
|
for i, name := range names {
|
2015-01-20 05:00:14 +08:00
|
|
|
tval := vop.tr[i].t
|
2015-01-24 07:42:57 +08:00
|
|
|
tvar := cp.ResolveVar(splitQualifiedName(name))
|
2015-01-23 02:22:03 +08:00
|
|
|
if !mayAssign(tvar, tval) {
|
2015-01-20 05:00:14 +08:00
|
|
|
cp.errorf(values[i].Pos, "type mismatch: assigning %#v value to %#v variable", tval, tvar)
|
2014-07-19 16:53:31 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-19 23:46:40 +08:00
|
|
|
// ensure that a CompoundNode contains exactly one PrimaryNode.
|
|
|
|
func ensurePrimary(cp *Compiler, cn *parse.CompoundNode, msg string) *parse.PrimaryNode {
|
2014-09-26 05:26:56 +08:00
|
|
|
if len(cn.Nodes) != 1 || cn.Nodes[0].Right != nil {
|
|
|
|
cp.errorf(cn.Pos, msg)
|
|
|
|
}
|
|
|
|
return cn.Nodes[0].Left
|
|
|
|
}
|
|
|
|
|
2015-01-22 00:22:55 +08:00
|
|
|
// ensureVariableOrStringPrimary ensures that a CompoundNode contains exactly
|
|
|
|
// one PrimaryNode of type VariablePrimary or StringPrimary.
|
2015-01-19 23:46:40 +08:00
|
|
|
func ensureVariableOrStringPrimary(cp *Compiler, cn *parse.CompoundNode, msg string) (*parse.PrimaryNode, string) {
|
|
|
|
pn := ensurePrimary(cp, cn, msg)
|
|
|
|
switch pn.Typ {
|
|
|
|
case parse.VariablePrimary, parse.StringPrimary:
|
|
|
|
return pn, pn.Node.(*parse.StringNode).Text
|
|
|
|
default:
|
|
|
|
cp.errorf(cn.Pos, msg)
|
|
|
|
return nil, ""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-22 00:22:55 +08:00
|
|
|
// ensureVariablePrimary ensures that a CompoundNode contains exactly one
|
|
|
|
// PrimaryNode of type VariablePrimary.
|
|
|
|
func ensureVariablePrimary(cp *Compiler, cn *parse.CompoundNode, msg string) (*parse.PrimaryNode, string) {
|
|
|
|
pn, text := ensureVariableOrStringPrimary(cp, cn, msg)
|
|
|
|
if pn.Typ != parse.VariablePrimary {
|
|
|
|
cp.errorf(pn.Pos, msg)
|
|
|
|
}
|
|
|
|
return pn, text
|
|
|
|
}
|
|
|
|
|
|
|
|
// ensureStartWithVariabl ensures the first compound of the form is a
|
|
|
|
// VariablePrimary. This is merely for better error messages; No actual
|
|
|
|
// processing is done.
|
2015-01-19 23:46:40 +08:00
|
|
|
func ensureStartWithVariable(cp *Compiler, fn *parse.FormNode, form string) {
|
|
|
|
if len(fn.Args.Nodes) == 0 {
|
|
|
|
cp.errorf(fn.Pos, "expect variable after %s", form)
|
|
|
|
}
|
2015-01-22 00:22:55 +08:00
|
|
|
ensureVariablePrimary(cp, fn.Args.Nodes[0], "expect variable")
|
2015-01-19 23:46:40 +08:00
|
|
|
}
|
|
|
|
|
2015-01-22 00:22:55 +08:00
|
|
|
// VarForm = 'var' { VarGroup } [ '=' Compound ]
|
|
|
|
// VarGroup = { VariablePrimary } [ StringPrimary ]
|
2015-01-19 23:46:40 +08:00
|
|
|
//
|
|
|
|
// Variables in the same VarGroup has the type specified by the StringPrimary.
|
2015-01-22 00:22:55 +08:00
|
|
|
// Only in the last VarGroup the StringPrimary may be omitted, in which case it
|
|
|
|
// defaults to "any". For instance,
|
2015-01-19 23:46:40 +08:00
|
|
|
//
|
|
|
|
// var $u $v Type1 $x $y Type2 $z = a b c d e
|
|
|
|
//
|
|
|
|
// gives $u and $v type Type1, $x $y type Type2 and $z type Any and
|
|
|
|
// assigns them the values a, b, c, d, e respectively.
|
2015-01-22 06:43:30 +08:00
|
|
|
func compileVar(cp *Compiler, fn *parse.FormNode) exitusOp {
|
2014-09-26 05:26:56 +08:00
|
|
|
var (
|
|
|
|
names []string
|
|
|
|
types []Type
|
|
|
|
values []*parse.CompoundNode
|
|
|
|
)
|
|
|
|
|
2015-01-19 23:46:40 +08:00
|
|
|
ensureStartWithVariable(cp, fn, "var")
|
2014-09-26 05:26:56 +08:00
|
|
|
|
2015-01-19 23:46:40 +08:00
|
|
|
for i, cn := range fn.Args.Nodes {
|
|
|
|
expect := "expect variable, type or equal sign"
|
|
|
|
pn, text := ensureVariableOrStringPrimary(cp, cn, expect)
|
|
|
|
if pn.Typ == parse.VariablePrimary {
|
|
|
|
names = append(names, text)
|
2014-03-03 13:41:44 +08:00
|
|
|
} else {
|
2015-01-19 23:46:40 +08:00
|
|
|
if text == "=" {
|
|
|
|
values = fn.Args.Nodes[i+1:]
|
|
|
|
break
|
|
|
|
} else {
|
|
|
|
if t, ok := typenames[text]; !ok {
|
|
|
|
cp.errorf(pn.Pos, "%v is not a valid type name", text)
|
2014-09-26 05:26:56 +08:00
|
|
|
} else {
|
2015-01-19 23:46:40 +08:00
|
|
|
if len(names) == len(types) {
|
|
|
|
cp.errorf(pn.Pos, "duplicate type")
|
2014-09-26 05:26:56 +08:00
|
|
|
}
|
|
|
|
for i := len(types); i < len(names); i++ {
|
|
|
|
types = append(types, t)
|
|
|
|
}
|
2014-03-30 16:02:56 +08:00
|
|
|
}
|
|
|
|
}
|
2014-03-03 13:41:44 +08:00
|
|
|
}
|
2015-01-19 23:46:40 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
for i := len(types); i < len(names); i++ {
|
|
|
|
types = append(types, AnyType{})
|
2014-09-26 05:26:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
for i, name := range names {
|
|
|
|
cp.pushVar(name, types[i])
|
2014-03-03 13:41:44 +08:00
|
|
|
}
|
2014-09-26 05:26:56 +08:00
|
|
|
|
|
|
|
var vop valuesOp
|
|
|
|
if values != nil {
|
|
|
|
vop = cp.compileCompounds(values)
|
|
|
|
checkSetType(cp, names, values, vop, fn.Pos)
|
|
|
|
}
|
2015-01-22 06:43:30 +08:00
|
|
|
return func(ev *Evaluator) Exitus {
|
2014-09-26 05:26:56 +08:00
|
|
|
for i, name := range names {
|
2015-01-23 08:27:51 +08:00
|
|
|
ev.local[name] = newVariable(types[i].Default(), types[i])
|
2014-04-30 10:45:54 +08:00
|
|
|
}
|
2014-09-26 05:26:56 +08:00
|
|
|
if vop.f != nil {
|
|
|
|
return doSet(ev, names, vop.f(ev))
|
2014-04-30 10:45:54 +08:00
|
|
|
}
|
2015-01-22 06:43:30 +08:00
|
|
|
return success
|
2014-09-26 05:26:56 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-19 23:46:40 +08:00
|
|
|
// SetForm = 'set' { VariablePrimary } '=' { Compound }
|
2015-01-22 06:43:30 +08:00
|
|
|
func compileSet(cp *Compiler, fn *parse.FormNode) exitusOp {
|
2014-09-26 05:26:56 +08:00
|
|
|
var (
|
|
|
|
names []string
|
|
|
|
values []*parse.CompoundNode
|
|
|
|
)
|
|
|
|
|
2015-01-19 23:46:40 +08:00
|
|
|
ensureStartWithVariable(cp, fn, "set")
|
2014-09-26 05:26:56 +08:00
|
|
|
|
2015-01-19 23:46:40 +08:00
|
|
|
for i, cn := range fn.Args.Nodes {
|
|
|
|
expect := "expect variable or equal sign"
|
|
|
|
pn, text := ensureVariableOrStringPrimary(cp, cn, expect)
|
|
|
|
if pn.Typ == parse.VariablePrimary {
|
|
|
|
names = append(names, text)
|
|
|
|
} else {
|
|
|
|
if text != "=" {
|
|
|
|
cp.errorf(pn.Pos, expect)
|
2014-04-30 10:45:54 +08:00
|
|
|
}
|
2015-01-19 23:46:40 +08:00
|
|
|
values = fn.Args.Nodes[i+1:]
|
|
|
|
break
|
2014-03-30 16:02:56 +08:00
|
|
|
}
|
2014-03-30 10:10:06 +08:00
|
|
|
}
|
2014-03-03 13:41:44 +08:00
|
|
|
|
2014-09-26 05:26:56 +08:00
|
|
|
var vop valuesOp
|
|
|
|
vop = cp.compileCompounds(values)
|
|
|
|
checkSetType(cp, names, values, vop, fn.Pos)
|
2014-03-20 13:19:30 +08:00
|
|
|
|
2015-01-22 06:43:30 +08:00
|
|
|
return func(ev *Evaluator) Exitus {
|
2014-09-26 05:26:56 +08:00
|
|
|
return doSet(ev, names, vop.f(ev))
|
|
|
|
}
|
2014-03-20 13:19:30 +08:00
|
|
|
}
|
|
|
|
|
2015-01-22 06:43:30 +08:00
|
|
|
var (
|
|
|
|
arityMismatch Exitus = newFailure("arity mismatch")
|
2015-01-23 02:22:03 +08:00
|
|
|
typeMismatch Exitus = newFailure("type mismatch")
|
2015-01-22 06:43:30 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
func doSet(ev *Evaluator, names []string, values []Value) Exitus {
|
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
|
|
|
|
if len(names) != len(values) {
|
2015-01-22 06:43:30 +08:00
|
|
|
return arityMismatch
|
2014-03-20 13:50:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
for i, name := range names {
|
|
|
|
// TODO Prevent overriding builtin variables e.g. $pid $env
|
2015-01-24 07:42:57 +08:00
|
|
|
variable := ev.ResolveVar(splitQualifiedName(name))
|
2015-01-23 02:22:03 +08:00
|
|
|
tvar := variable.staticType
|
|
|
|
tval := values[i].Type()
|
|
|
|
if !mayAssign(tvar, tval) {
|
|
|
|
return typeMismatch
|
|
|
|
}
|
2015-01-23 08:27:51 +08:00
|
|
|
*variable.valuePtr = values[i]
|
2014-03-20 13:50:13 +08:00
|
|
|
}
|
|
|
|
|
2015-01-22 06:43:30 +08:00
|
|
|
return success
|
2014-03-20 13:50:13 +08:00
|
|
|
}
|
|
|
|
|
2015-01-22 00:22:55 +08:00
|
|
|
// DelForm = 'del' { VariablePrimary }
|
2015-01-22 06:43:30 +08:00
|
|
|
func compileDel(cp *Compiler, fn *parse.FormNode) exitusOp {
|
2014-09-16 23:57:33 +08:00
|
|
|
// Do conventional compiling of all compound expressions, including
|
|
|
|
// ensuring that variables can be resolved
|
2014-09-26 05:26:56 +08:00
|
|
|
var names []string
|
2015-01-22 00:22:55 +08:00
|
|
|
for _, cn := range fn.Args.Nodes {
|
2015-01-24 07:42:57 +08:00
|
|
|
_, qname := ensureVariablePrimary(cp, cn, "expect variable")
|
|
|
|
ns, name := splitQualifiedName(qname)
|
|
|
|
if ns != "" && ns != "local" {
|
|
|
|
cp.errorf(cn.Pos, "can only delete a variable on local scope")
|
|
|
|
}
|
2015-01-22 00:22:55 +08:00
|
|
|
if cp.resolveVarOnThisScope(name) == nil {
|
2015-01-24 07:42:57 +08:00
|
|
|
cp.errorf(cn.Pos, "variable $%s not found on current local scope", name)
|
2014-04-02 11:13:43 +08:00
|
|
|
}
|
2015-01-22 00:22:55 +08:00
|
|
|
|
2014-04-30 10:45:54 +08:00
|
|
|
cp.popVar(name)
|
2014-09-26 05:26:56 +08:00
|
|
|
names = append(names, name)
|
2014-04-02 11:13:43 +08:00
|
|
|
}
|
2015-01-22 06:43:30 +08:00
|
|
|
return func(ev *Evaluator) Exitus {
|
2014-09-26 05:26:56 +08:00
|
|
|
for _, name := range names {
|
2015-01-23 08:27:51 +08:00
|
|
|
delete(ev.local, name)
|
2014-04-30 10:45:54 +08:00
|
|
|
}
|
2015-01-22 06:43:30 +08:00
|
|
|
return success
|
2014-04-02 11:13:43 +08:00
|
|
|
}
|
|
|
|
}
|
2015-01-20 05:11:44 +08:00
|
|
|
|
2015-01-22 01:06:14 +08:00
|
|
|
// FnForm = 'fn' StringPrimary { VariablePrimary } ClosurePrimary
|
|
|
|
//
|
|
|
|
// fn defines a function. This isn't strictly needed, since user-defined
|
|
|
|
// functions are just variables. The following two lines should be exactly
|
|
|
|
// equivalent:
|
|
|
|
//
|
|
|
|
// fn f $a $b { put (* $a $b) (/ $a *b) }
|
|
|
|
// var $fn-f = { |$a $b| put (* $a $b) (/ $a $b) }
|
2015-01-22 06:43:30 +08:00
|
|
|
func compileFn(cp *Compiler, fn *parse.FormNode) exitusOp {
|
2015-01-22 01:06:14 +08:00
|
|
|
if len(fn.Args.Nodes) == 0 {
|
|
|
|
cp.errorf(fn.Pos, "expect function name after fn")
|
|
|
|
}
|
|
|
|
pn, fnName := ensureVariableOrStringPrimary(cp, fn.Args.Nodes[0], "expect string literal")
|
|
|
|
varName := "fn-" + fnName
|
|
|
|
if cp.resolveVarOnThisScope(varName) != nil {
|
|
|
|
cp.errorf(pn.Pos, "redefinition of function %s", fnName)
|
|
|
|
}
|
|
|
|
|
|
|
|
var closureNode *parse.ClosureNode
|
|
|
|
var argNames []*parse.CompoundNode
|
|
|
|
|
|
|
|
for i, cn := range fn.Args.Nodes[1:] {
|
|
|
|
expect := "expect variable or closure"
|
|
|
|
pn := ensurePrimary(cp, cn, expect)
|
|
|
|
switch pn.Typ {
|
|
|
|
case parse.ClosurePrimary:
|
|
|
|
if i+2 != len(fn.Args.Nodes) {
|
|
|
|
cp.errorf(fn.Args.Nodes[i+2].Pos, "garbage after closure literal")
|
|
|
|
}
|
|
|
|
closureNode = pn.Node.(*parse.ClosureNode)
|
|
|
|
break
|
|
|
|
case parse.VariablePrimary:
|
|
|
|
argNames = append(argNames, cn)
|
|
|
|
default:
|
|
|
|
cp.errorf(pn.Pos, expect)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(argNames) > 0 {
|
|
|
|
closureNode = &parse.ClosureNode{
|
|
|
|
closureNode.Pos,
|
|
|
|
&parse.SpacedNode{argNames[0].Pos, argNames},
|
|
|
|
closureNode.Chunk,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
op := cp.compileClosure(closureNode)
|
|
|
|
|
|
|
|
cp.pushVar(varName, ClosureType{})
|
|
|
|
|
2015-01-22 06:43:30 +08:00
|
|
|
return func(ev *Evaluator) Exitus {
|
2015-01-23 08:27:51 +08:00
|
|
|
ev.local[varName] = newVariable(op.f(ev)[0], ClosureType{})
|
2015-01-22 06:43:30 +08:00
|
|
|
return success
|
2015-01-22 01:06:14 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-22 06:43:30 +08:00
|
|
|
func compileStaticTypeof(cp *Compiler, fn *parse.FormNode) exitusOp {
|
2015-01-20 05:11:44 +08:00
|
|
|
// Do conventional compiling of all compounds, only keeping the static type
|
|
|
|
// information
|
|
|
|
var trs []typeRun
|
|
|
|
for _, cn := range fn.Args.Nodes {
|
|
|
|
trs = append(trs, cp.compileCompound(cn).tr)
|
|
|
|
}
|
2015-01-22 06:43:30 +08:00
|
|
|
return func(ev *Evaluator) Exitus {
|
2015-01-20 05:11:44 +08:00
|
|
|
out := ev.ports[1].ch
|
|
|
|
for _, tr := range trs {
|
2015-01-20 05:26:45 +08:00
|
|
|
out <- NewString(tr.String())
|
2015-01-20 05:11:44 +08:00
|
|
|
}
|
2015-01-22 06:43:30 +08:00
|
|
|
return success
|
2015-01-20 05:11:44 +08:00
|
|
|
}
|
|
|
|
}
|