Numbers typed in skip the formula path, operators count their own nesting without an extra call, and an abandoned evaluation finds its visiting cells by scanning rather than keeping a stack: RecalcAll and Edit on the chain, volatile and dense shapes are back to their previous times.
- commit
- 7ea2199c090dbda7f2c6ad485096a67869e94cdb
- parent
- 7140572955a6c0bba351e82b6daf3fe310049d77
- author
- Dami <dami.etoile@gmail.com>
| M | internal/ | 22 | ++---- |
| M | internal/ | 103 | +++++++++++++++++++----------- |
@@ -118,19 +118,6 @@ func eval(n Node, get lookup) Value {
118118 return get.cell(n.Sheet, n.Rect.From)119119 }120120 return ErrValue121- case formula.Unary, decUnary, formula.Binary, decBinary, formula.Call:122- get.w.depth++123- v := evalNested(n, get)124- get.w.depth--125- return v126- }127- return ErrValue128-}129-130-// evalNested computes an operator or a call, which evaluate their131-// operands: eval counts the nesting around it (see evaluate.go).132-func evalNested(n Node, get lookup) Value {133- switch n := n.(type) {134121 case formula.Unary:135122 return evalUnary(n, get, false)136123 case decUnary:@@ -140,7 +127,10 @@ func evalNested(n Node, get lookup) Value {
140127 case decBinary:141128 return evalBinary(formula.Binary(n), get, true)142129 case formula.Call:143- return funcOf(n).call(n.Args, get)130+ get.w.depth++ // see evaluate.go; operators count in theirs131+ v := funcOf(n).call(n.Args, get)132+ get.w.depth--133+ return v144134 }145135 return ErrValue146136 }@@ -148,7 +138,9 @@ func evalNested(n Node, get lookup) Value {
148138 // evalUnary computes a prefix operator or the postfix %; with dec, % is149139 // decimal (decimal.go).150140 func evalUnary(n formula.Unary, get lookup, dec bool) Value {141+ get.w.depth++151142 x := eval(n.X, get)143+ get.w.depth--152144 if x.Kind == Error {153145 return x154146 }@@ -183,7 +175,9 @@ func evalUnary(n formula.Unary, get lookup, dec bool) Value {
183175 // evalBinary computes a binary operator; with dec, arithmetic is decimal184176 // (decimal.go).185177 func evalBinary(n formula.Binary, get lookup, dec bool) Value {178+ get.w.depth++186179 l, r := eval(n.L, get), eval(n.R, get)180+ get.w.depth--187181 if l.Kind == Error {188182 return l189183 }@@ -32,7 +32,6 @@ type tooDeep struct{}
3232 // evaluator is the state of one evaluate.3333 type evaluator struct {3434 w *Workbook35- stack []loc // cells being evaluated, outermost first3635 pending []loc // cells put off, each needed by the one before3736 }3837 @@ -52,10 +51,8 @@ func (w *Workbook) evaluate() {
5251 s.calcFmt = w.formatFrom(s)5352 }5453 for _, s := range w.sheets {55- for a, st := range s.calc {56- if st == dirty {57- e.settle(loc{s, a})58- }54+ for !e.sweep(s) {55+ e.settle()5956 }6057 }6158 for _, s := range w.sheets {@@ -80,14 +77,11 @@ func (e *evaluator) compute(s *Sheet, a Addr) Value {
8077 case c.derived: // a pivot's result, set when the pivot was computed8178 s.calc[a] = done8279 return c.Value83- case w.depth >= maxEvalDepth:80+ case c.expr != nil && w.depth >= maxEvalDepth:8481 s.calc[a] = deferred8582 e.pending = append(e.pending, loc{s, a})8683 panic(tooDeep{})8784 }88- s.calc[a] = visiting89- e.stack = append(e.stack, loc{s, a})90- w.depth++9185 c.auto = Format{}9286 switch {9387 case c.Input == "":@@ -97,26 +91,47 @@ func (e *evaluator) compute(s *Sheet, a Addr) Value {
9791 case c.expr == nil:9892 c.Value = Value{Kind: Text, Str: strings.TrimPrefix(c.Input, "'")}9993 default:100- expr := s.bound(c)101- outer := w.evaluating102- w.evaluating = loc{s, a}103- c.Value = eval(w.arith(expr), s.calcGet)104- w.evaluating = outer105- if _, lit := expr.(formula.Num); !lit {106- c.auto = inferFormat(expr, s.calcFmt)94+ if n, lit := c.expr.(formula.Num); lit { // a number typed in95+ c.Value = num(n.V)96+ } else {97+ e.formula(s, a, c)10798 }10899 }109- w.depth--110- e.stack = e.stack[:len(e.stack)-1]111100 s.calc[a] = done112101 s.cells.changed(a)113102 return c.Value114103 }115104 116-// settle evaluates the dirty cell at l, and every cell put off on the117-// way, deepest first.118-func (e *evaluator) settle(l loc) {119- e.pending = append(e.pending[:0], l)105+// formula evaluates the formula in c, at a, which may read other cells:106+// it's visiting meanwhile.107+func (e *evaluator) formula(s *Sheet, a Addr, c *Cell) {108+ w := e.w109+ s.calc[a] = visiting110+ w.depth++111+ expr := s.bound(c)112+ outer := w.evaluating113+ w.evaluating = loc{s, a}114+ c.Value = eval(w.arith(expr), s.calcGet)115+ w.evaluating = outer116+ c.auto = inferFormat(expr, s.calcFmt)117+ w.depth--118+}119+120+// sweep evaluates s's dirty cells, reporting false if it went too deep121+// and put off a cell.122+func (e *evaluator) sweep(s *Sheet) (finished bool) {123+ defer e.abandon(&finished)124+ for a, st := range s.calc {125+ if st == dirty {126+ e.compute(s, a)127+ }128+ }129+ return true130+}131+132+// settle evaluates the cells put off, deepest first, putting off more on133+// the way if need be.134+func (e *evaluator) settle() {120135 for len(e.pending) > 0 {121136 top := e.pending[len(e.pending)-1]122137 if top.s.calc[top.a] == deferred {@@ -131,23 +146,33 @@ func (e *evaluator) settle(l loc) {
131146 // try evaluates the cell at l, reporting false if it went too deep and132147 // put off a cell, having undone what it had started.133148 func (e *evaluator) try(l loc) (finished bool) {134- defer func() {135- if r := recover(); r != nil {136- if _, deep := r.(tooDeep); !deep {137- panic(r)138- }139- for _, v := range e.stack {140- v.s.calc[v.a] = dirty141- }142- for _, p := range e.pending {143- p.s.calc[p.a] = deferred144- }145- e.stack = e.stack[:0]146- e.w.depth = 0147- e.w.evaluating = loc{}148- finished = false149- }150- }()149+ defer e.abandon(&finished)151150 e.compute(l.s, l.a)152151 return true153152 }153+154+// abandon, deferred, recovers from an evaluation that went too deep:155+// the cells it was evaluating go back to dirty, those put off stay156+// deferred, and finished is set false.157+func (e *evaluator) abandon(finished *bool) {158+ r := recover()159+ if r == nil {160+ return161+ }162+ if _, deep := r.(tooDeep); !deep {163+ panic(r)164+ }165+ for _, s := range e.w.sheets {166+ for a, st := range s.calc {167+ if st == visiting {168+ s.calc[a] = dirty169+ }170+ }171+ }172+ for _, p := range e.pending {173+ p.s.calc[p.a] = deferred174+ }175+ e.w.depth = 0176+ e.w.evaluating = loc{}177+ *finished = false178+}