【Interpreter】构建简单的解释器(第6部分—Go语言实现)

【Interpreter】构建简单的解释器(第6部分—Go语言实现)

一、描述

  1. 在之前文章的基础上拓展代码,使解释器可以解析包含括号以及任意多的 +-*/ 运算符的算数表达式;

二、代码

// Go 实现
package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
	"unicode"
)

type TOKEN_TYPE int

const (
	TYPE_NONE TOKEN_TYPE = iota
	TYPE_INTEGER
	TYPE_PLUS
	TYPE_MINUS
	TYPE_MUL
	TYPE_DIV
	TYPE_LPAREN
	TYPE_RPAREN
	TYPE_EOF
)

type Token struct {
	TokenType  TOKEN_TYPE
	TokenValue interface{}
}

type Lexer struct {
	Text        []rune
	Pos         int
	CurrentChar rune
}

type Interpreter struct {
	InterLexer   *Lexer
	CurrentToken Token
}

func (lexer *Lexer) Advance() {
	lexer.Pos += 1

	if lexer.Pos > len(lexer.Text)-1 {
		lexer.CurrentChar = 0
	} else {
		lexer.CurrentChar = lexer.Text[lexer.Pos]
	}
}

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 (lexer *Lexer) Integer() int {
	result := ""

	for lexer.CurrentChar != 0 && unicode.IsDigit(lexer.CurrentChar) {
		result += string(lexer.CurrentChar)
		lexer.Advance()
	}

	return StrToInt(result)
}

func (lexer *Lexer) SkipSpace() {
	for lexer.CurrentChar != 0 && unicode.IsSpace(lexer.CurrentChar) {
		lexer.Advance()
	}
}

func (lexer *Lexer) GetNextToken() Token {

	for lexer.CurrentChar != 0 {
		if unicode.IsSpace(lexer.CurrentChar) {
			lexer.SkipSpace()
		}

		if unicode.IsDigit(lexer.CurrentChar) {
			return Token{TYPE_INTEGER, lexer.Integer()}
		}

		if lexer.CurrentChar == '+' {
			lexer.Advance()
			return Token{TYPE_PLUS, '+'}
		}

		if lexer.CurrentChar == '-' {
			lexer.Advance()
			return Token{TYPE_MINUS, '-'}
		}

		if lexer.CurrentChar == '*' {
			lexer.Advance()
			return Token{TYPE_MUL, '*'}
		}

		if lexer.CurrentChar == '/' {
			lexer.Advance()
			return Token{TYPE_DIV, '/'}
		}

		if lexer.CurrentChar == '(' {
			lexer.Advance()
			return Token{TYPE_LPAREN, '('}
		}

		if lexer.CurrentChar == ')' {
			lexer.Advance()
			return Token{TYPE_RPAREN, ')'}
		}

		panic("invalid text.")
	}

	return Token{TYPE_EOF, nil}
}

func (interpreter *Interpreter) Eat(token_type TOKEN_TYPE) {
	if interpreter.CurrentToken.TokenType == token_type {
		interpreter.CurrentToken = interpreter.InterLexer.GetNextToken()
	} else {
		panic("invalid token type.")
	}
}

func (interpreter *Interpreter) Factor() int {
	token := interpreter.CurrentToken

	if token.TokenType == TYPE_INTEGER {
		interpreter.Eat(TYPE_INTEGER)
		return token.TokenValue.(int)
	} else if token.TokenType == TYPE_LPAREN {
		interpreter.Eat(TYPE_LPAREN)
		result := interpreter.Expr()
		interpreter.Eat(TYPE_RPAREN)
		return result
	}

	panic("error token type.")
}

func (interpreter *Interpreter) Term() int {

	result := interpreter.Factor()

	for {
		if interpreter.CurrentToken.TokenType == TYPE_MUL {
			interpreter.Eat(TYPE_MUL)
			result *= interpreter.Factor()
		} else if interpreter.CurrentToken.TokenType == TYPE_DIV {
			interpreter.Eat(TYPE_DIV)
			result /= interpreter.Factor()
		} else {
			break
		}
	}

	return result
}

func (interpreter *Interpreter) Expr() int {

	defer func() {
		if r := recover(); r != nil {
			fmt.Println("[ERROR] ", r)
		}
	}()

	result := interpreter.Term()

	for {
		if interpreter.CurrentToken.TokenType == TYPE_PLUS {
			interpreter.Eat(TYPE_PLUS)
			result += interpreter.Term()
		} else if interpreter.CurrentToken.TokenType == TYPE_MINUS {
			interpreter.Eat(TYPE_MINUS)
			result -= interpreter.Term()
		} else {
			break
		}
	}

	return result
}

func main() {
	fmt.Println("------------------ <PART-6> ------------------")

	reader := bufio.NewReader(os.Stdin)

	for {
		fmt.Print("[CALC]-> ")

		text, _ := reader.ReadString('\n')
		text = strings.ToLower(strings.TrimSpace(strings.TrimSuffix(text, "\n")))
		if len(text) == 0 {
			continue
		}

		if text == "exit" {
			fmt.Println("-------------------程序已退出------------------")
			fmt.Println("------------------- <END> --------------------")
			os.Exit(0)
		}

		my_text := []rune(text)

		my_lexer := Lexer{my_text, 0, my_text[0]}

		my_interpreter := Interpreter{&my_lexer, Token{}}

		my_interpreter.CurrentToken = my_interpreter.InterLexer.GetNextToken()

		result := my_interpreter.Expr()
		fmt.Println("> ", result)
	}
}

三、运行结果

------------------ <PART-6> ------------------
[CALC]-> 1 + 2
>  3
[CALC]-> 1+  3  * 4
>  13
[CALC]-> 1+2*3 + 4 * 5 / 6
>  10
[CALC]-> 1 + ( 2 * (3 +5) - 4) +6-(7-8)
>  20
[CALC]-> exit
-------------------程序已退出------------------
------------------- <END> --------------------

——2019-01-24——

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值