This is a simple Reverse Polish Notation calculator, accepting expressions in the form of "2 3 + 1 -" and evaluating a result depending on the branching logic supplied.
We start with a very similar type declaration as in the previous post, a action function and a dispatch table. The Stack is used to record the state of the evaluation.
type ( ActionFunc func(string, *Stack) ActionTable map[string]ActionFunc )
The evaluate function accepts the expression, the branching logic and a stack for the state. It loops through the expression and evaluates which action to take depending on the token. If the token is a number the NUMBER action is selected. Otherwise an action is selected depending on the token or a default one if that fails. The action is then executed.
The function ends with popping the top value of the stack and returning it to the caller.
func evaluate(expression []string, actions ActionTable, stack *Stack) interface{} { for _, t := range expression { var action ActionFunc if _, err := strconv.ParseFloat(t, 64); err == nil { action = actions["NUMBER"] } else { var ok bool if action, ok = actions[t]; !ok { action = actions["__DEFAULT__"] } } action(t, stack) } return stack.Pop() }
The main function starts with declaring a branching logic that supports calculating an expression in RPN format using the +, -, *, / and sqrt operators. Then the evaluate algorithm is called and the result is printed.
func main() {
calcActions := ActionTable{ "+": func(token string, stack *Stack) { stack.Push(stack.PopFloat() + stack.PopFloat()) }, "-": func(token string, stack *Stack) { v := stack.PopFloat() stack.Push(stack.PopFloat() - v) }, "*": func(token string, stack *Stack) { stack.Push(stack.PopFloat() * stack.PopFloat()) }, "/": func(token string, stack *Stack) { v := stack.PopFloat() stack.Push(stack.PopFloat() / v) }, "sqrt": func(token string, stack *Stack) { stack.Push(math.Sqrt(stack.PopFloat())) }, "NUMBER": func(token string, stack *Stack) { v, _ := strconv.ParseFloat(token, 64) stack.Push(v) }, "__DEFAULT__": func(token string, stack *Stack) { panic(fmt.Sprintf("Unkown token %q", token)) }, } v := evaluate(os.Args[1:], calcActions, new(Stack)) fmt.Printf("Result: %f\n", toFloat(v))
astActions := ActionTable{ "NUMBER": func(token string, stack *Stack) { stack.Push(token) }, "__DEFAULT__": func(token string, stack *Stack) { t := stack.Pop() if stack.Len() > 0 { stack.Push([]interface{}{token, stack.Pop(), t}) } else { stack.Push([]interface{}{token, t}) } }, } v = evaluate(os.Args[1:], astActions, new(Stack)) fmt.Printf("AST tree: %v\n", v) fmt.Printf("AST to string: %q\n", astToString(toInterfaces(v))) } func astToString(tree []interface{}) string { if len(tree) == 1 { return toString(tree[0]) } if len(tree) == 2 { op, val := toString(tree[0]), toInterfaces(tree[1]) s := astToString(val) return "( " + op + " " + s + " )" } op, l, r := toString(tree[0]), toInterfaces(tree[1]), toInterfaces(tree[2]) s1 := astToString(l) s2 := astToString(r) return "( " + s1 + " " + op + " " + s2 + " )" }
At the end of the file, not shown here, are some auxiliary functions. A stack data structure is defined along with three typecasting functions.
Get the full code at GitHub.
No comments:
Post a Comment