Home | History | Annotate | Download | only in expr
      1 // Copyright 2013 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 // This is an example of a goyacc program.
      6 // To build it:
      7 // go tool yacc -p "expr" expr.y (produces y.go)
      8 // go build -o expr y.go
      9 // expr
     10 // > <type an expression>
     11 
     12 %{
     13 
     14 package main
     15 
     16 import (
     17 	"bufio"
     18 	"bytes"
     19 	"fmt"
     20 	"io"
     21 	"log"
     22 	"math/big"
     23 	"os"
     24 	"unicode/utf8"
     25 )
     26 
     27 %}
     28 
     29 %union {
     30 	num *big.Rat
     31 }
     32 
     33 %type	<num>	expr expr1 expr2 expr3
     34 
     35 %token '+' '-' '*' '/' '(' ')'
     36 
     37 %token	<num>	NUM
     38 
     39 %%
     40 
     41 top:
     42 	expr
     43 	{
     44 		if $1.IsInt() {
     45 			fmt.Println($1.Num().String())
     46 		} else {
     47 			fmt.Println($1.String())
     48 		}
     49 	}
     50 
     51 expr:
     52 	expr1
     53 |	'+' expr
     54 	{
     55 		$$ = $2
     56 	}
     57 |	'-' expr
     58 	{
     59 		$$ = $2.Neg($2)
     60 	}
     61 
     62 expr1:
     63 	expr2
     64 |	expr1 '+' expr2
     65 	{
     66 		$$ = $1.Add($1, $3)
     67 	}
     68 |	expr1 '-' expr2
     69 	{
     70 		$$ = $1.Sub($1, $3)
     71 	}
     72 
     73 expr2:
     74 	expr3
     75 |	expr2 '*' expr3
     76 	{
     77 		$$ = $1.Mul($1, $3)
     78 	}
     79 |	expr2 '/' expr3
     80 	{
     81 		$$ = $1.Quo($1, $3)
     82 	}
     83 
     84 expr3:
     85 	NUM
     86 |	'(' expr ')'
     87 	{
     88 		$$ = $2
     89 	}
     90 
     91 
     92 %%
     93 
     94 // The parser expects the lexer to return 0 on EOF.  Give it a name
     95 // for clarity.
     96 const eof = 0
     97 
     98 // The parser uses the type <prefix>Lex as a lexer.  It must provide
     99 // the methods Lex(*<prefix>SymType) int and Error(string).
    100 type exprLex struct {
    101 	line []byte
    102 	peek rune
    103 }
    104 
    105 // The parser calls this method to get each new token.  This
    106 // implementation returns operators and NUM.
    107 func (x *exprLex) Lex(yylval *exprSymType) int {
    108 	for {
    109 		c := x.next()
    110 		switch c {
    111 		case eof:
    112 			return eof
    113 		case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
    114 			return x.num(c, yylval)
    115 		case '+', '-', '*', '/', '(', ')':
    116 			return int(c)
    117 
    118 		// Recognize Unicode multiplication and division
    119 		// symbols, returning what the parser expects.
    120 		case '':
    121 			return '*'
    122 		case '':
    123 			return '/'
    124 
    125 		case ' ', '\t', '\n', '\r':
    126 		default:
    127 			log.Printf("unrecognized character %q", c)
    128 		}
    129 	}
    130 }
    131 
    132 // Lex a number.
    133 func (x *exprLex) num(c rune, yylval *exprSymType) int {
    134 	add := func(b *bytes.Buffer, c rune) {
    135 		if _, err := b.WriteRune(c); err != nil {
    136 			log.Fatalf("WriteRune: %s", err)
    137 		}
    138 	}
    139 	var b bytes.Buffer
    140 	add(&b, c)
    141 	L: for {
    142 		c = x.next()
    143 		switch c {
    144 		case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', 'e', 'E':
    145 			add(&b, c)
    146 		default:
    147 			break L
    148 		}
    149 	}
    150 	if c != eof {
    151 		x.peek = c
    152 	}
    153 	yylval.num = &big.Rat{}
    154 	_, ok := yylval.num.SetString(b.String())
    155 	if !ok {
    156 		log.Printf("bad number %q", b.String())
    157 		return eof
    158 	}
    159 	return NUM
    160 }
    161 
    162 // Return the next rune for the lexer.
    163 func (x *exprLex) next() rune {
    164 	if x.peek != eof {
    165 		r := x.peek
    166 		x.peek = eof
    167 		return r
    168 	}
    169 	if len(x.line) == 0 {
    170 		return eof
    171 	}
    172 	c, size := utf8.DecodeRune(x.line)
    173 	x.line = x.line[size:]
    174 	if c == utf8.RuneError && size == 1 {
    175 		log.Print("invalid utf8")
    176 		return x.next()
    177 	}
    178 	return c
    179 }
    180 
    181 // The parser calls this method on a parse error.
    182 func (x *exprLex) Error(s string) {
    183 	log.Printf("parse error: %s", s)
    184 }
    185 
    186 func main() {
    187 	in := bufio.NewReader(os.Stdin)
    188 	for {
    189 		if _, err := os.Stdout.WriteString("> "); err != nil {
    190 			log.Fatalf("WriteString: %s", err)
    191 		}
    192 		line, err := in.ReadBytes('\n')
    193 		if err == io.EOF {
    194 			return
    195 		}
    196 		if err != nil {
    197 			log.Fatalf("ReadBytes: %s", err)
    198 		}
    199 
    200 		exprParse(&exprLex{line: line})
    201 	}
    202 }
    203