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) } }