mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2025-09-13 07:02:16 +00:00
good good study, day day up!
This commit is contained in:
160
vendor/gopl.io/ch7/eval/parse.go
generated
vendored
Normal file
160
vendor/gopl.io/ch7/eval/parse.go
generated
vendored
Normal file
@@ -0,0 +1,160 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
package eval
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/scanner"
|
||||
)
|
||||
|
||||
// ---- lexer ----
|
||||
|
||||
// This lexer is similar to the one described in Chapter 13.
|
||||
type lexer struct {
|
||||
scan scanner.Scanner
|
||||
token rune // current lookahead token
|
||||
}
|
||||
|
||||
func (lex *lexer) next() { lex.token = lex.scan.Scan() }
|
||||
func (lex *lexer) text() string { return lex.scan.TokenText() }
|
||||
|
||||
type lexPanic string
|
||||
|
||||
// describe returns a string describing the current token, for use in errors.
|
||||
func (lex *lexer) describe() string {
|
||||
switch lex.token {
|
||||
case scanner.EOF:
|
||||
return "end of file"
|
||||
case scanner.Ident:
|
||||
return fmt.Sprintf("identifier %s", lex.text())
|
||||
case scanner.Int, scanner.Float:
|
||||
return fmt.Sprintf("number %s", lex.text())
|
||||
}
|
||||
return fmt.Sprintf("%q", rune(lex.token)) // any other rune
|
||||
}
|
||||
|
||||
func precedence(op rune) int {
|
||||
switch op {
|
||||
case '*', '/':
|
||||
return 2
|
||||
case '+', '-':
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// ---- parser ----
|
||||
|
||||
// Parse parses the input string as an arithmetic expression.
|
||||
//
|
||||
// expr = num a literal number, e.g., 3.14159
|
||||
// | id a variable name, e.g., x
|
||||
// | id '(' expr ',' ... ')' a function call
|
||||
// | '-' expr a unary operator (+-)
|
||||
// | expr '+' expr a binary operator (+-*/)
|
||||
//
|
||||
func Parse(input string) (_ Expr, err error) {
|
||||
defer func() {
|
||||
switch x := recover().(type) {
|
||||
case nil:
|
||||
// no panic
|
||||
case lexPanic:
|
||||
err = fmt.Errorf("%s", x)
|
||||
default:
|
||||
// unexpected panic: resume state of panic.
|
||||
panic(x)
|
||||
}
|
||||
}()
|
||||
lex := new(lexer)
|
||||
lex.scan.Init(strings.NewReader(input))
|
||||
lex.scan.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanFloats
|
||||
lex.next() // initial lookahead
|
||||
e := parseExpr(lex)
|
||||
if lex.token != scanner.EOF {
|
||||
return nil, fmt.Errorf("unexpected %s", lex.describe())
|
||||
}
|
||||
return e, nil
|
||||
}
|
||||
|
||||
func parseExpr(lex *lexer) Expr { return parseBinary(lex, 1) }
|
||||
|
||||
// binary = unary ('+' binary)*
|
||||
// parseBinary stops when it encounters an
|
||||
// operator of lower precedence than prec1.
|
||||
func parseBinary(lex *lexer, prec1 int) Expr {
|
||||
lhs := parseUnary(lex)
|
||||
for prec := precedence(lex.token); prec >= prec1; prec-- {
|
||||
for precedence(lex.token) == prec {
|
||||
op := lex.token
|
||||
lex.next() // consume operator
|
||||
rhs := parseBinary(lex, prec+1)
|
||||
lhs = binary{op, lhs, rhs}
|
||||
}
|
||||
}
|
||||
return lhs
|
||||
}
|
||||
|
||||
// unary = '+' expr | primary
|
||||
func parseUnary(lex *lexer) Expr {
|
||||
if lex.token == '+' || lex.token == '-' {
|
||||
op := lex.token
|
||||
lex.next() // consume '+' or '-'
|
||||
return unary{op, parseUnary(lex)}
|
||||
}
|
||||
return parsePrimary(lex)
|
||||
}
|
||||
|
||||
// primary = id
|
||||
// | id '(' expr ',' ... ',' expr ')'
|
||||
// | num
|
||||
// | '(' expr ')'
|
||||
func parsePrimary(lex *lexer) Expr {
|
||||
switch lex.token {
|
||||
case scanner.Ident:
|
||||
id := lex.text()
|
||||
lex.next() // consume Ident
|
||||
if lex.token != '(' {
|
||||
return Var(id)
|
||||
}
|
||||
lex.next() // consume '('
|
||||
var args []Expr
|
||||
if lex.token != ')' {
|
||||
for {
|
||||
args = append(args, parseExpr(lex))
|
||||
if lex.token != ',' {
|
||||
break
|
||||
}
|
||||
lex.next() // consume ','
|
||||
}
|
||||
if lex.token != ')' {
|
||||
msg := fmt.Sprintf("got %q, want ')'", lex.token)
|
||||
panic(lexPanic(msg))
|
||||
}
|
||||
}
|
||||
lex.next() // consume ')'
|
||||
return call{id, args}
|
||||
|
||||
case scanner.Int, scanner.Float:
|
||||
f, err := strconv.ParseFloat(lex.text(), 64)
|
||||
if err != nil {
|
||||
panic(lexPanic(err.Error()))
|
||||
}
|
||||
lex.next() // consume number
|
||||
return literal(f)
|
||||
|
||||
case '(':
|
||||
lex.next() // consume ')'
|
||||
e := parseExpr(lex)
|
||||
if lex.token != ')' {
|
||||
msg := fmt.Sprintf("got %s, want ')'", lex.describe())
|
||||
panic(lexPanic(msg))
|
||||
}
|
||||
lex.next() // consume ')'
|
||||
return e
|
||||
}
|
||||
msg := fmt.Sprintf("unexpected %s", lex.describe())
|
||||
panic(lexPanic(msg))
|
||||
}
|
Reference in New Issue
Block a user