【Interpreter】构建简单的解释器(第2部分—Go语言实现)
一、描述
- 允许输入多位数整数,例如 “12+3”
- 添加忽略空白字符的方法,让计算器可以处理带有空白字符的输入,如 “12 + 3”
- 扩展代码,可以使用 ‘-’ 代替 ‘+’ 来处理像 “7-5” 这样的减法
二、代码
// 综合以上三个问题,使用 Go 简单实现
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
"unicode"
)
type TokenType int
const (
NONE TokenType = iota
INTEGER
PLUS
MINUS
EOF
)
type Token struct {
Type TokenType
Value interface{}
}
type Interpreter struct {
Text []rune
Pos int
CurrentToken Token
CurrentChar rune
}
func NewToken(t TokenType, v interface{}) Token {
return Token{
Type: t,
Value: v,
}
}
func StrToInt(s string) int {
if ret, err := strconv.Atoi(s); err == nil {
return ret
}
panic(fmt.Sprintf("Cannot convert %s to int!", s))
}
func (interpreter *Interpreter) RuneToDigit() int {
result := ""
for interpreter.CurrentChar != 0 && unicode.IsDigit(interpreter.CurrentChar) {
result += string(interpreter.CurrentChar)
interpreter.Advance()
}
return StrToInt(result)
}
func (interpreter *Interpreter) Advance() {
interpreter.Pos += 1
if interpreter.Pos > len(interpreter.Text)-1 {
interpreter.CurrentChar = 0
} else {
interpreter.CurrentChar = interpreter.Text[interpreter.Pos]
}
}
func (interpreter *Interpreter) SkipWhiteSpace() {
for interpreter.CurrentChar != 0 && unicode.IsSpace(interpreter.CurrentChar) {
interpreter.Advance()
}
}
func (interpreter *Interpreter) Eat(token_type TokenType) {
if interpreter.CurrentToken.Type == token_type {
interpreter.CurrentToken = interpreter.GetNextToken()
} else {
panic(fmt.Sprintf("invalid token type: %s", interpreter.Text))
}
}
func (interpreter *Interpreter) GetNextToken() Token {
for interpreter.CurrentChar != 0 {
if unicode.IsSpace(interpreter.CurrentChar) {
interpreter.SkipWhiteSpace()
continue
}
if unicode.IsDigit(interpreter.CurrentChar) {
return NewToken(INTEGER, interpreter.RuneToDigit())
}
if interpreter.CurrentChar == '+' {
interpreter.Advance()
return NewToken(PLUS, "+")
}
if interpreter.CurrentChar == '-' {
interpreter.Advance()
return NewToken(MINUS, "-")
}
panic(fmt.Sprintf("invalid input: %s", string(interpreter.Text)))
}
return NewToken(EOF, nil)
}
func (interpreter *Interpreter) Expr() {
defer func() {
if r := recover(); r != nil {
fmt.Println("[ERROR] ", r)
}
}()
interpreter.CurrentToken = interpreter.GetNextToken()
left := interpreter.CurrentToken
interpreter.Eat(INTEGER)
op_token := interpreter.CurrentToken
if op_token.Type == PLUS {
interpreter.Eat(PLUS)
} else {
interpreter.Eat(MINUS)
}
right := interpreter.CurrentToken
interpreter.Eat(INTEGER)
result := 0
if op_token.Type == PLUS {
result = left.Value.(int) + right.Value.(int)
} else {
result = left.Value.(int) - right.Value.(int)
}
fmt.Println(result)
}
func main() {
fmt.Println("------------ PART-2 --------------")
reader := bufio.NewReader(os.Stdin)
for {
fmt.Print("[CALC]-> ")
text, _ := reader.ReadString('\n')
// convert CRLF to LF
text = strings.ToLower(strings.TrimSpace(strings.TrimSuffix(text, "\n")))
// fmt.Println(text)
if 0 == len(text) {
continue
}
text = strings.ToLower(text)
if text == "exit" {
fmt.Println("退出成功!")
fmt.Println("------------------------------------")
os.Exit(0)
}
my_text := []rune(text)
my_interpreter := Interpreter{my_text, 0, Token{}, my_text[0]}
my_interpreter.Expr()
}
}
三、运行结果
------------ PART-2 --------------
[CALC]-> 12+3
15
[CALC]-> 12-3
9
[CALC]-> 123 + 100
223
[CALC]-> 123 + 100
223
[CALC]-> 123 - 100
23
[CALC]-> 123 - 100
23
[CALC]-> 12 3 - 100
[ERROR] invalid token type
[CALC]-> exit
退出成功!
------------------------------------
——2019-01-02——