elvish/eval/builtin-special.go

302 lines
8.1 KiB
Go
Raw Normal View History

package eval
// Builtin special forms.
import "github.com/elves/elvish/parse"
type strOp func(*Evaluator) string
type builtinSpecialCompile func(*Compiler, *parse.FormNode) strOp
type builtinSpecial struct {
compile builtinSpecialCompile
streamTypes [2]StreamType
}
var builtinSpecials map[string]builtinSpecial
func init() {
// Needed to avoid initialization loop
builtinSpecials = map[string]builtinSpecial{
"var": builtinSpecial{compileVar, [2]StreamType{}},
"set": builtinSpecial{compileSet, [2]StreamType{}},
"del": builtinSpecial{compileDel, [2]StreamType{}},
"fn": builtinSpecial{compileFn, [2]StreamType{}},
"static-typeof": builtinSpecial{
compileStaticTypeof, [2]StreamType{0, fdStream}},
}
}
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")
}
_, 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 {
tval := vop.tr[i].t
tvar := cp.ResolveVar(name)
if isAny(tval) || isAny(tvar) {
// TODO Check type soundness at runtime
continue
}
if tvar != tval {
cp.errorf(values[i].Pos, "type mismatch: assigning %#v value to %#v variable", tval, tvar)
}
}
}
// 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.
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.
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-22 00:22:55 +08:00
// VarForm = 'var' { VarGroup } [ '=' Compound ]
// VarGroup = { VariablePrimary } [ StringPrimary ]
//
// 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,
//
// 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.
2014-09-26 05:26:56 +08:00
func compileVar(cp *Compiler, fn *parse.FormNode) strOp {
var (
names []string
types []Type
values []*parse.CompoundNode
)
ensureStartWithVariable(cp, fn, "var")
2014-09-26 05:26:56 +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)
} else {
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 {
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
}
}
}
}
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-09-26 05:26:56 +08:00
var vop valuesOp
if values != nil {
vop = cp.compileCompounds(values)
checkSetType(cp, names, values, vop, fn.Pos)
}
return func(ev *Evaluator) string {
for i, name := range names {
ev.scope[name] = valuePtr(types[i].Default())
}
2014-09-26 05:26:56 +08:00
if vop.f != nil {
return doSet(ev, names, vop.f(ev))
}
2014-09-26 05:26:56 +08:00
return ""
}
}
// SetForm = 'set' { VariablePrimary } '=' { Compound }
2014-09-26 05:26:56 +08:00
func compileSet(cp *Compiler, fn *parse.FormNode) strOp {
var (
names []string
values []*parse.CompoundNode
)
ensureStartWithVariable(cp, fn, "set")
2014-09-26 05:26:56 +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)
}
values = fn.Args.Nodes[i+1:]
break
2014-03-30 16:02:56 +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
2014-09-26 05:26:56 +08:00
return func(ev *Evaluator) string {
return doSet(ev, names, vop.f(ev))
}
2014-03-20 13:19:30 +08:00
}
func doSet(ev *Evaluator, names []string, values []Value) string {
// TODO Support assignment of mismatched arity in some restricted way -
// "optional" and "rest" arguments and the like
if len(names) != len(values) {
return "arity mismatch"
}
for i, name := range names {
// TODO Prevent overriding builtin variables e.g. $pid $env
*ev.scope[name] = values[i]
}
return ""
}
2015-01-22 00:22:55 +08:00
// DelForm = 'del' { VariablePrimary }
func compileDel(cp *Compiler, fn *parse.FormNode) strOp {
// 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 {
pn, name := ensureVariablePrimary(cp, cn, "expect variable")
cp.mustResolveVar(name, pn.Pos)
if cp.resolveVarOnThisScope(name) == nil {
cp.errorf(cn.Pos, "can only delete variable on current scope")
}
2015-01-22 00:22:55 +08:00
cp.popVar(name)
2014-09-26 05:26:56 +08:00
names = append(names, name)
}
return func(ev *Evaluator) string {
2014-09-26 05:26:56 +08:00
for _, name := range names {
delete(ev.scope, name)
}
return ""
}
}
// 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) }
func compileFn(cp *Compiler, fn *parse.FormNode) strOp {
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{})
return func(ev *Evaluator) string {
ev.scope[varName] = &op.f(ev)[0]
return ""
}
}
func compileStaticTypeof(cp *Compiler, fn *parse.FormNode) strOp {
// 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)
}
return func(ev *Evaluator) string {
out := ev.ports[1].ch
for _, tr := range trs {
out <- NewString(tr.String())
}
return ""
}
}