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}}, } } 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 } 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 { if len(cn.Nodes) != 1 || cn.Nodes[0].Right != nil { cp.errorf(cn.Pos, msg) } return cn.Nodes[0].Left } // 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, "" } } // 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) } ensureVariablePrimary(cp, fn.Args.Nodes[0], "expect variable") } // VarForm = 'var' { VarGroup } [ '=' Compound ] // VarGroup = { VariablePrimary } [ StringPrimary ] // // Variables in the same VarGroup has the type specified by the StringPrimary. // 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. func compileVar(cp *Compiler, fn *parse.FormNode) strOp { var ( names []string types []Type values []*parse.CompoundNode ) ensureStartWithVariable(cp, fn, "var") 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) } else { if len(names) == len(types) { cp.errorf(pn.Pos, "duplicate type") } for i := len(types); i < len(names); i++ { types = append(types, t) } } } } } for i := len(types); i < len(names); i++ { types = append(types, AnyType{}) } for i, name := range names { cp.pushVar(name, types[i]) } 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()) } if vop.f != nil { return doSet(ev, names, vop.f(ev)) } return "" } } // SetForm = 'set' { VariablePrimary } '=' { Compound } func compileSet(cp *Compiler, fn *parse.FormNode) strOp { var ( names []string values []*parse.CompoundNode ) ensureStartWithVariable(cp, fn, "set") 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 } } var vop valuesOp vop = cp.compileCompounds(values) checkSetType(cp, names, values, vop, fn.Pos) return func(ev *Evaluator) string { return doSet(ev, names, vop.f(ev)) } } 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 "" } // 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 var names []string 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") } cp.popVar(name) names = append(names, name) } return func(ev *Evaluator) string { 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 "" } }