diff --git a/eval/boilerplate.go b/eval/boilerplate.go index 11da9d74..465e70c5 100644 --- a/eval/boilerplate.go +++ b/eval/boilerplate.go @@ -77,14 +77,14 @@ func assignmentBegins(ns []*parse.Assignment) []int { return begins } -func (cp *compiler) indexingVars(ns []*parse.Indexing, msg string) []VariableOp { +func (cp *compiler) singleVariables(ns []*parse.Indexing, msg string) []VariableOp { ops := make([]VariableOp, len(ns)) for i, n := range ns { - ops[i] = cp.indexingVar(n, msg) + ops[i] = cp.singleVariable(n, msg) } return ops } -func indexingVarBegins(ns []*parse.Indexing) []int { +func singleVariableBegins(ns []*parse.Indexing) []int { begins := make([]int, len(ns)) for i, n := range ns { begins[i] = n.Begin() diff --git a/eval/compile.go b/eval/compile.go index f41fe6e3..603c5326 100644 --- a/eval/compile.go +++ b/eval/compile.go @@ -277,8 +277,17 @@ func (cp *compiler) control(n *parse.Control) Op { } } case parse.ForControl: - cp.errorf(n.Begin(), "not yet implemented") - panic("unreachable") + iteratorOp := cp.singleVariable(n.Iterator, "must be a single variable") + valuesOp := cp.array(n.Array) + bodyOp := cp.chunk(n.Body) + return func(ec *EvalCtx) { + iterator := iteratorOp(ec) + values := valuesOp(ec) + for _, v := range values { + doSet(ec, []Variable{iterator}, []Value{v}) + bodyOp(ec) + } + } case parse.BeginControl: return cp.chunk(n.Body) default: @@ -298,21 +307,7 @@ func (cp *compiler) literal(n *parse.Primary, msg string) string { } func (cp *compiler) assignment(n *parse.Assignment) Op { - var variableOps []VariableOp - if n.Dst.Head.Type == parse.Braced { - compounds := n.Dst.Head.Braced - indexings := make([]*parse.Indexing, len(compounds)) - for i, cn := range compounds { - if len(cn.Indexings) != 1 { - cp.errorf(cn.Begin(), "must be a variable spec") - } - indexings[i] = cn.Indexings[0] - } - variableOps = cp.indexingVars(indexings, "must be a variable spc") - } else { - variableOps = []VariableOp{cp.indexingVar(n.Dst, "must be a variable spec or a braced list of those")} - } - + variableOps := cp.multiVariable(n.Dst) valuesOp := cp.compound(n.Src) return func(ec *EvalCtx) { @@ -324,8 +319,27 @@ func (cp *compiler) assignment(n *parse.Assignment) Op { } } -func (cp *compiler) indexingVar(n *parse.Indexing, msg string) VariableOp { - // XXX will we be using indexingVar for purposes other than setting? +func (cp *compiler) multiVariable(n *parse.Indexing) []VariableOp { + var variableOps []VariableOp + if n.Head.Type == parse.Braced { + // XXX ignore n.Indicies. + compounds := n.Head.Braced + indexings := make([]*parse.Indexing, len(compounds)) + for i, cn := range compounds { + if len(cn.Indexings) != 1 { + cp.errorf(cn.Begin(), "must be a variable spec") + } + indexings[i] = cn.Indexings[0] + } + variableOps = cp.singleVariables(indexings, "must be a variable spc") + } else { + variableOps = []VariableOp{cp.singleVariable(n, "must be a variable spec or a braced list of those")} + } + return variableOps +} + +func (cp *compiler) singleVariable(n *parse.Indexing, msg string) VariableOp { + // XXX will we be using this for purposes other than setting? varname := cp.literal(n.Head, msg) p := n.Begin() diff --git a/parse/boilerplate.go b/parse/boilerplate.go index efb31764..9618c417 100644 --- a/parse/boilerplate.go +++ b/parse/boilerplate.go @@ -92,7 +92,7 @@ func (n *Control) setCondition(ch *Chunk) { addChild(n, ch) } -func (n *Control) setIterator(ch *Primary) { +func (n *Control) setIterator(ch *Indexing) { n.Iterator = ch addChild(n, ch) } diff --git a/parse/parse.go b/parse/parse.go index 3922d728..7f98ce4e 100644 --- a/parse/parse.go +++ b/parse/parse.go @@ -286,13 +286,13 @@ func (an *Assignment) parse(ps *parser) { type Control struct { node Kind ControlKind - Condition *Chunk // Valid for WhileControl. - Iterator *Primary // Valid for ForControl. - Array *Array // Valid for ForControl. - Body *Chunk // Valid for all except IfControl. - Conditions []*Chunk // Valid for IfControl. - Bodies []*Chunk // Valid for IfControl. - ElseBody *Chunk // Valid for IfControl, WhileControl and ForControl. + Condition *Chunk // Valid for WhileControl. + Iterator *Indexing // Valid for ForControl. + Array *Array // Valid for ForControl. + Body *Chunk // Valid for all except IfControl. + Conditions []*Chunk // Valid for IfControl. + Bodies []*Chunk // Valid for IfControl. + ElseBody *Chunk // Valid for IfControl, WhileControl and ForControl. } // ControlKind identifies which control structure a Control represents. @@ -374,7 +374,7 @@ func (ctrl *Control) parse(ps *parser, leader string) { case "for": ctrl.Kind = ForControl parseSpaces(ctrl, ps) - ctrl.setIterator(parsePrimary(ps)) + ctrl.setIterator(parseIndexing(ps)) parseSpaces(ctrl, ps) if ps.findPossibleLeader() == "in" { ps.advance(len("in"))