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  }