diff --git a/compute/compute.go b/compute/compute.go index 15fd8d3..337fd62 100644 --- a/compute/compute.go +++ b/compute/compute.go @@ -16,6 +16,11 @@ import ( var resHistory = []float64{} +// ClearHistory removes prior evaluation history used for @ references. +func ClearHistory() { + resHistory = []float64{} +} + func Evaluate(in string) (float64, error) { floats := NewFloatStack() ops := NewStringStack() @@ -29,7 +34,11 @@ ScanLoop: _, tok, lit := s.Scan() if lit != "@" && back > -1 && len(resHistory) > 0 { - floats.Push(getHistory(back)) + if hval, err := getHistory(back); err != nil { + return 0, err + } else { + floats.Push(hval) + } if prev == token.RPAREN || constants.IsConstant(prev.String()) { evalUnprecedenced("*", ops, floats) } @@ -184,8 +193,11 @@ func parseOperator(lit string) *operators.Operator { return operators.FindOperatorFromString(lit) } -func getHistory(back int) float64 { - return resHistory[back] +func getHistory(back int) (float64, error) { + if back >= len(resHistory) { + return 0, errors.New("History underflow") + } + return resHistory[back], nil } func pushHistory(res float64) { diff --git a/compute/compute_test.go b/compute/compute_test.go index bed24cd..cf7c91c 100644 --- a/compute/compute_test.go +++ b/compute/compute_test.go @@ -50,6 +50,34 @@ func TestEvaluate(t *testing.T) { } } +func TestEvaluateInvalid(t *testing.T) { + tests := [][]string{ + {"/"}, + {"1/"}, + {"1("}, + {")("}, + {"(()"}, + {"@"}, + {"@@"}, + {"0", "@@"}, + {"0", "@@@"}, + {"@@\xa6"}, + } + for i, series := range tests { + ClearHistory() + var fail error + for _, expr := range series { + if _, err := Evaluate(expr); err != nil { + fail = err + break + } + } + if fail == nil { + t.Errorf("case %d: expected error, finished successfully", i) + } + } +} + func BenchmarkEvaluate(b *testing.B) { tests := []string{ "π",