github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/talks/2010/io/eval1.go (about) 1 // Copyright 2010 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // +build OMIT 6 7 package main 8 9 import ( 10 "bufio" 11 "fmt" 12 "os" 13 "strconv" 14 "strings" 15 ) 16 17 // Generic expression parser/evaluator 18 19 type Value interface { 20 String() string 21 BinaryOp(op string, y Value) Value 22 } 23 24 type Parser struct { 25 precTab map[string]int 26 newVal func(string) Value 27 src string 28 pos int 29 tok string 30 } 31 32 const alphanum = "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" 33 34 func (p *Parser) stop(c uint8) bool { 35 switch { 36 case p.pos >= len(p.src): 37 return true 38 case c == '"': 39 if p.src[p.pos] == '"' { 40 p.pos++ 41 return true 42 } 43 return false 44 case strings.IndexRune(alphanum, int(c)) >= 0: 45 return strings.IndexRune(alphanum, int(p.src[p.pos])) < 0 46 } 47 return true 48 } 49 50 func (p *Parser) next() { 51 // skip blanks 52 for ; p.pos < len(p.src) && p.src[p.pos] <= ' '; p.pos++ { 53 } 54 if p.pos >= len(p.src) { 55 p.tok = "" 56 return 57 } 58 start := p.pos 59 c := p.src[p.pos] 60 for p.pos < len(p.src) { 61 p.pos++ 62 if p.stop(c) { 63 break 64 } 65 } 66 p.tok = p.src[start:p.pos] 67 } 68 69 func (p *Parser) binaryExpr(prec1 int) Value { 70 x := p.newVal(p.tok) 71 p.next() 72 for prec := p.precTab[p.tok]; prec >= prec1; prec-- { 73 for p.precTab[p.tok] == prec { 74 op := p.tok 75 p.next() 76 y := p.binaryExpr(prec + 1) 77 x = x.BinaryOp(op, y) 78 } 79 } 80 return x 81 } 82 83 func Eval(precTab map[string]int, newVal func(string) Value, src string) Value { 84 var p Parser 85 p.precTab = precTab 86 p.newVal = newVal 87 p.src = src 88 p.next() 89 return p.binaryExpr(1) 90 } 91 92 // Command-line expression evaluator 93 94 func main() { 95 r := bufio.NewReader(os.Stdin) 96 for { 97 fmt.Printf("> ") 98 line, err := r.ReadString('\n') 99 if err != nil { 100 break 101 } 102 fmt.Printf("%s\n", Eval(precTab, trace(newVal), line)) 103 } 104 } 105 106 // Custom grammar and values 107 108 var precTab = map[string]int{ 109 "&&": 1, 110 "||": 2, 111 "==": 3, 112 "!=": 3, 113 "<": 3, 114 "<=": 3, 115 ">": 3, 116 ">=": 3, 117 "+": 4, 118 "-": 4, 119 "*": 5, 120 "/": 5, 121 "%": 5, 122 } 123 124 func newVal(lit string) Value { 125 x, err := strconv.Atoi(lit) 126 if err == nil { 127 return Int(x) 128 } 129 b, err := strconv.ParseBool(lit) 130 if err == nil { 131 return Bool(b) 132 } 133 return Error(fmt.Sprintf("illegal literal '%s'", lit)) 134 } 135 136 type Error string 137 138 func (e Error) String() string { return string(e) } 139 func (e Error) BinaryOp(op string, y Value) Value { return e } 140 141 type Int int 142 143 func (x Int) String() string { return strconv.Itoa(int(x)) } 144 func (x Int) BinaryOp(op string, y Value) Value { 145 switch y := y.(type) { 146 case Error: 147 return y 148 case Int: 149 switch op { 150 case "+": 151 return x + y 152 case "-": 153 return x - y 154 case "*": 155 return x * y 156 case "/": 157 return x / y 158 case "%": 159 return x % y 160 case "==": 161 return Bool(x == y) 162 case "!=": 163 return Bool(x != y) 164 case "<": 165 return Bool(x < y) 166 case "<=": 167 return Bool(x <= y) 168 case ">": 169 return Bool(x > y) 170 case ">=": 171 return Bool(x >= y) 172 } 173 } 174 return Error(fmt.Sprintf("illegal operation: '%v %s %v'", x, op, y)) 175 } 176 177 type Bool bool 178 179 func (x Bool) String() string { return strconv.FormatBool(bool(x)) } 180 func (x Bool) BinaryOp(op string, y Value) Value { 181 switch y := y.(type) { 182 case Error: 183 return y 184 case Bool: 185 switch op { 186 case "&&": 187 return Bool(x && y) 188 case "||": 189 return Bool(x || y) 190 case "==": 191 return Bool(x == y) 192 case "!=": 193 return Bool(x != y) 194 } 195 } 196 return Error(fmt.Sprintf("illegal operation: '%v %s %v'", x, op, y)) 197 } 198 199 func trace(newVal func(string) Value) func(string) Value { 200 return func(s string) Value { 201 v := newVal(s) 202 fmt.Printf("\tnewVal(%q) = %s\n", s, fmtv(v)) 203 return &traceValue{v} 204 } 205 } 206 207 type traceValue struct { 208 Value 209 } 210 211 func (x *traceValue) BinaryOp(op string, y Value) Value { 212 z := x.Value.BinaryOp(op, y.(*traceValue).Value) 213 fmt.Printf("\t%s.BinaryOp(%q, %s) = %s\n", fmtv(x.Value), op, fmtv(y.(*traceValue).Value), fmtv(z)) 214 return &traceValue{z} 215 } 216 217 func (x *traceValue) String() string { 218 s := x.Value.String() 219 fmt.Printf("\t%s.String() = %#v\n", fmtv(x.Value), s) 220 return s 221 } 222 223 func fmtv(v Value) string { 224 t := fmt.Sprintf("%T", v) 225 if i := strings.LastIndex(t, "."); i >= 0 { // strip package 226 t = t[i+1:] 227 } 228 return fmt.Sprintf("%s(%#v)", t, v) 229 }