Login light
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/

package eval

import (
    "fmt"
    "math"
    "testing"
)

//!+Eval
func TestEval(t *testing.T) {
    tests := []struct {
        expr string
        env  Env
        want string
    }{
        {"sqrt(A / pi)", Env{"A": 87616, "pi": math.Pi}, "167"},
        {"pow(x, 3) + pow(y, 3)", Env{"x": 12, "y": 1}, "1729"},
        {"pow(x, 3) + pow(y, 3)", Env{"x": 9, "y": 10}, "1729"},
        {"5 / 9 * (F - 32)", Env{"F": -40}, "-40"},
        {"5 / 9 * (F - 32)", Env{"F": 32}, "0"},
        {"5 / 9 * (F - 32)", Env{"F": 212}, "100"},
        //!-Eval
        // additional tests that don't appear in the book
        {"-1 + -x", Env{"x": 1}, "-2"},
        {"-1 - x", Env{"x": 1}, "-2"},
        //!+Eval
    }
    var prevExpr string
    for _, test := range tests {
        // Print expr only when it changes.
        if test.expr != prevExpr {
            fmt.Printf("\n%s\n", test.expr)
            prevExpr = test.expr
        }
        expr, err := Parse(test.expr)
        if err != nil {
            t.Error(err) // parse error
            continue
        }
        got := fmt.Sprintf("%.6g", expr.Eval(test.env))
        fmt.Printf("\t%v => %s\n", test.env, got)
        if got != test.want {
            t.Errorf("%s.Eval() in %v = %q, want %q\n",
                test.expr, test.env, got, test.want)
        }
    }
}

//!-Eval

/*
//!+output
sqrt(A / pi)
    map[A:87616 pi:3.141592653589793] => 167

pow(x, 3) + pow(y, 3)
    map[x:12 y:1] => 1729
    map[x:9 y:10] => 1729

5 / 9 * (F - 32)
    map[F:-40] => -40
    map[F:32] => 0
    map[F:212] => 100
//!-output

// Additional outputs that don't appear in the book.

-1 - x
    map[x:1] => -2

-1 + -x
    map[x:1] => -2
*/

func TestErrors(t *testing.T) {
    for _, test := range []struct{ expr, wantErr string }{
        {"x % 2", "unexpected '%'"},
        {"math.Pi", "unexpected '.'"},
        {"!true", "unexpected '!'"},
        {`"hello"`, "unexpected '\"'"},
        {"log(10)", `unknown function "log"`},
        {"sqrt(1, 2)", "call to sqrt has 2 args, want 1"},
    } {
        expr, err := Parse(test.expr)
        if err == nil {
            vars := make(map[Var]bool)
            err = expr.Check(vars)
            if err == nil {
                t.Errorf("unexpected success: %s", test.expr)
                continue
            }
        }
        fmt.Printf("%-20s%v\n", test.expr, err) // (for book)
        if err.Error() != test.wantErr {
            t.Errorf("got error %s, want %s", err, test.wantErr)
        }
    }
}

/*
//!+errors
x % 2               unexpected '%'
math.Pi             unexpected '.'
!true               unexpected '!'
"hello"             unexpected '"'

log(10)             unknown function "log"
sqrt(1, 2)          call to sqrt has 2 args, want 1
//!-errors
*/