// FnForm = 'fn' StringPrimary LambdaPrimary // // fn f []{foobar} is a shorthand for set '&'f = []{foobar}. func compileFn(cp *compiler, fn *parse.Form) op { if len(fn.Args) == 0 { cp.errorf(fn.End(), "should be followed by function name") } fnName := mustString(cp, fn.Args[0], "must be a literal string") varName := FnPrefix + fnName if len(fn.Args) == 1 { cp.errorf(fn.Args[0].End(), "should be followed by a lambda") } pn := mustPrimary(cp, fn.Args[1], "should be a lambda") if pn.Type != parse.Lambda { cp.errorf(pn.Begin(), "should be a lambda") } if len(fn.Args) > 2 { cp.errorf(fn.Args[2].Begin(), "superfluous argument") } cp.registerVariableSet(":" + varName) op := cp.lambda(pn) return func(ec *evalCtx) { closure := op(ec)[0].(*Closure) closure.Op = makeFnOp(closure.Op) ec.local[varName] = newPtrVariable(closure) } }
// FnForm = 'fn' StringPrimary LambdaPrimary // // fn f []{foobar} is a shorthand for set '&'f = []{foobar}. func compileFn(cp *compiler, fn *parse.Form) OpFunc { if len(fn.Args) == 0 { end := fn.End() cp.errorpf(end, end, "should be followed by function name") } fnName := mustString(cp, fn.Args[0], "must be a literal string") varName := FnPrefix + fnName if len(fn.Args) == 1 { end := fn.Args[0].End() cp.errorpf(end, end, "should be followed by a lambda") } pn := mustPrimary(cp, fn.Args[1], "should be a lambda") if pn.Type != parse.Lambda { cp.compiling(pn) cp.errorf("should be a lambda") } if len(fn.Args) > 2 { cp.errorpf(fn.Args[2].Begin(), fn.Args[len(fn.Args)-1].End(), "superfluous argument(s)") } cp.registerVariableSet(":" + varName) op := cp.lambda(pn) return func(ec *EvalCtx) { // Initialize the function variable with the builtin nop // function. This step allows the definition of recursive // functions; the actual function will never be called. ec.local[varName] = NewPtrVariable(&BuiltinFn{"<shouldn't be called>", nop}) closure := op(ec)[0].(*Closure) closure.Op = makeFnOp(closure.Op) ec.local[varName].Set(closure) } }
func (cp *compiler) form(n *parse.Form) Op { if len(n.Assignments) > 0 { if n.Head != nil { cp.errorf(n.Begin(), "temporary assignments not yet supported") } ops := cp.assignments(n.Assignments) return func(ec *EvalCtx) { for _, op := range ops { op(ec) } } } headStr, ok := oneString(n.Head) if ok { compileForm, ok := builtinSpecials[headStr] if ok { // special form return compileForm(cp, n) } // Ignore the output. If a matching function exists it will be // captured and eventually the Evaler executes it. If not, nothing // happens here and the Evaler executes an external command. cp.registerVariableGet(FnPrefix + headStr) // XXX Dynamic head names should always refer to external commands } headOp := cp.compound(n.Head) argOps := cp.compounds(n.Args) // TODO: n.NamedArgs redirOps := cp.redirs(n.Redirs) // TODO: n.ErrorRedir p := n.Begin() // ec here is always a subevaler created in compiler.pipeline, so it can // be safely modified. return func(ec *EvalCtx) { // head headValues := headOp(ec) headMust := ec.must(headValues, "the head of command", p) headMust.mustLen(1) switch headValues[0].(type) { case String, Caller, Indexer: default: headMust.error("a string or callable", headValues[0].Kind()) } // args var args []Value for _, argOp := range argOps { args = append(args, argOp(ec)...) } // redirs for _, redirOp := range redirOps { redirOp(ec) } ec.resolveCaller(headValues[0]).Call(ec, args) } }
func (cp *compiler) formOp(n *parse.Form) Op { return Op{cp.form(n), n.Begin(), n.End()} }
func (cp *compiler) form(n *parse.Form) OpFunc { var saveVarsOps []LValuesOp var assignmentOps []Op if len(n.Assignments) > 0 { assignmentOps = cp.assignmentOps(n.Assignments) if n.Head == nil { // Permanent assignment. return func(ec *EvalCtx) { for _, op := range assignmentOps { op.Exec(ec) } } } else { for _, a := range n.Assignments { v, r := cp.lvaluesOp(a.Dst) saveVarsOps = append(saveVarsOps, v, r) } } } if n.Control != nil { if len(n.Args) > 0 { cp.errorpf(n.Args[0].Begin(), n.Args[len(n.Args)-1].End(), "control structure takes no arguments") } redirOps := cp.redirOps(n.Redirs) controlOp := cp.controlOp(n.Control) return func(ec *EvalCtx) { for _, redirOp := range redirOps { redirOp.Exec(ec) } controlOp.Exec(ec) } } headStr, ok := oneString(n.Head) if ok { compileForm, ok := builtinSpecials[headStr] if ok { // special form return compileForm(cp, n) } // Ignore the output. If a matching function exists it will be // captured and eventually the Evaler executes it. If not, nothing // happens here and the Evaler executes an external command. cp.registerVariableGet(FnPrefix + headStr) // XXX Dynamic head names should always refer to external commands } headOp := cp.compoundOp(n.Head) argOps := cp.compoundOps(n.Args) optsOp := cp.mapPairs(n.Opts) redirOps := cp.redirOps(n.Redirs) // TODO: n.ErrorRedir begin, end := n.Begin(), n.End() // ec here is always a subevaler created in compiler.pipeline, so it can // be safely modified. return func(ec *EvalCtx) { // Temporary assignment. if len(saveVarsOps) > 0 { // There is a temporary assignment. // Save variables. var saveVars []Variable var saveVals []Value for _, op := range saveVarsOps { saveVars = append(saveVars, op.Exec(ec)...) } for _, v := range saveVars { val := v.Get() saveVals = append(saveVals, val) Logger.Printf("saved %s = %s", v, val) } // Do assignment. for _, op := range assignmentOps { op.Exec(ec) } // Defer variable restoration. Will be executed even if an error // occurs when evaling other part of the form. defer func() { for i, v := range saveVars { val := saveVals[i] if val == nil { // XXX Old value is nonexistent. We should delete the // variable. However, since the compiler now doesn't delete // it, we don't delete it in the evaler either. val = String("") } v.Set(val) Logger.Printf("restored %s = %s", v, val) } }() } // head headValues := headOp.Exec(ec) ec.must(headValues, "head of command", headOp.Begin, headOp.End).mustLen(1) headFn := mustFn(headValues[0]) // args var args []Value for _, argOp := range argOps { args = append(args, argOp.Exec(ec)...) } // opts // XXX This conversion should be avoided. opts := optsOp(ec)[0].(Map) convertedOpts := make(map[string]Value) for k, v := range *opts.inner { if ks, ok := k.(String); ok { convertedOpts[string(ks)] = v } else { throwf("Option key must be string, got %s", k.Kind()) } } // redirs for _, redirOp := range redirOps { redirOp.Exec(ec) } // In case some arguments returned false. ec.verdict = true ec.begin, ec.end = begin, end headFn.Call(ec, args, convertedOpts) } }