diff options
Diffstat (limited to 'doc/talks/io2010/eval1.go')
-rw-r--r-- | doc/talks/io2010/eval1.go | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/doc/talks/io2010/eval1.go b/doc/talks/io2010/eval1.go new file mode 100644 index 000000000..2d7fc3be6 --- /dev/null +++ b/doc/talks/io2010/eval1.go @@ -0,0 +1,229 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "bufio" + "fmt" + "os" + "strconv" + "strings" +) + +// Generic expression parser/evaluator + +type Value interface { + String() string + BinaryOp(op string, y Value) Value +} + +type Parser struct { + precTab map[string]int + newVal func(string) Value + src string + pos int + tok string +} + +const alphanum = "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + +func (p *Parser) stop(c uint8) bool { + switch { + case p.pos >= len(p.src): + return true + case c == '"': + if p.src[p.pos] == '"' { + p.pos++ + return true + } + return false + case strings.IndexRune(alphanum, int(c)) >= 0: + return strings.IndexRune(alphanum, int(p.src[p.pos])) < 0 + } + return true +} + +func (p *Parser) next() { + // skip blanks + for ; p.pos < len(p.src) && p.src[p.pos] <= ' '; p.pos++ { + } + if p.pos >= len(p.src) { + p.tok = "" + return + } + start := p.pos + c := p.src[p.pos] + for p.pos < len(p.src) { + p.pos++ + if p.stop(c) { + break + } + } + p.tok = p.src[start:p.pos] +} + +func (p *Parser) binaryExpr(prec1 int) Value { + x := p.newVal(p.tok) + p.next() + for prec := p.precTab[p.tok]; prec >= prec1; prec-- { + for p.precTab[p.tok] == prec { + op := p.tok + p.next() + y := p.binaryExpr(prec + 1) + x = x.BinaryOp(op, y) + } + } + return x +} + +func Eval(precTab map[string]int, newVal func(string) Value, src string) Value { + var p Parser + p.precTab = precTab + p.newVal = newVal + p.src = src + p.next() + return p.binaryExpr(1) +} + +// Command-line expression evaluator + +func main() { + r := bufio.NewReader(os.Stdin) + for { + fmt.Printf("> ") + line, err := r.ReadString('\n') + if err != nil { + break + } + fmt.Printf("%s\n", Eval(precTab, trace(newVal), line)) + } +} + + +// Custom grammar and values + +var precTab = map[string]int{ + "&&": 1, + "||": 2, + "==": 3, + "!=": 3, + "<": 3, + "<=": 3, + ">": 3, + ">=": 3, + "+": 4, + "-": 4, + "*": 5, + "/": 5, + "%": 5, +} + +func newVal(lit string) Value { + x, err := strconv.Atoi(lit) + if err == nil { + return Int(x) + } + b, err := strconv.Atob(lit) + if err == nil { + return Bool(b) + } + return Error(fmt.Sprintf("illegal literal '%s'", lit)) +} + +type Error string + +func (e Error) String() string { return string(e) } +func (e Error) BinaryOp(op string, y Value) Value { return e } + +type Int int + +func (x Int) String() string { return strconv.Itoa(int(x)) } +func (x Int) BinaryOp(op string, y Value) Value { + switch y := y.(type) { + case Error: + return y + case Int: + switch op { + case "+": + return x + y + case "-": + return x - y + case "*": + return x * y + case "/": + return x / y + case "%": + return x % y + case "==": + return Bool(x == y) + case "!=": + return Bool(x != y) + case "<": + return Bool(x < y) + case "<=": + return Bool(x <= y) + case ">": + return Bool(x > y) + case ">=": + return Bool(x >= y) + } + } + return Error(fmt.Sprintf("illegal operation: '%v %s %v'", x, op, y)) +} + +type Bool bool + +func (x Bool) String() string { return strconv.Btoa(bool(x)) } +func (x Bool) BinaryOp(op string, y Value) Value { + switch y := y.(type) { + case Error: + return y + case Bool: + switch op { + case "&&": + return Bool(x && y) + case "||": + return Bool(x || y) + case "==": + return Bool(x == y) + case "!=": + return Bool(x != y) + } + } + return Error(fmt.Sprintf("illegal operation: '%v %s %v'", x, op, y)) +} + + +func trace(newVal func(string) Value) func(string) Value { + return func(s string) Value { + v := newVal(s) + fmt.Printf("\tnewVal(%q) = %s\n", s, fmtv(v)) + return &traceValue{v} + } +} + +type traceValue struct { + Value +} + +func (x *traceValue) BinaryOp(op string, y Value) Value { + z := x.Value.BinaryOp(op, y.(*traceValue).Value) + fmt.Printf("\t%s.BinaryOp(%q, %s) = %s\n", fmtv(x.Value), op, fmtv(y.(*traceValue).Value), fmtv(z)) + return &traceValue{z} +} + +func (x *traceValue) String() string { + s := x.Value.String() + fmt.Printf("\t%s.String() = %#v\n", fmtv(x.Value), s) + return s +} + +func fmtv(v Value) string { + t := fmt.Sprintf("%T", v) + if i := strings.LastIndex(t, "."); i >= 0 { // strip package + t = t[i+1:] + } + return fmt.Sprintf("%s(%#v)", t, v) +} |