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

本文介绍了一个使用Go语言构建的简单解释器,能够解析并计算包含括号、加减乘除运算的数学表达式。通过逐步解析输入,解释器能正确处理优先级和括号内的表达式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【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——

In this book we will create a programming language together. We'll start with 0 lines of code and end up with a fully working interpreter for the Monkey* programming language. Step by step. From tokens to output. All code shown and included. Fully tested. Buy this book to learn - How to build an interpreter for a C-like programming language from scratch - What a lexer, a parser and an Abstract Syntax Tree (AST) are and how to build your own - What closures are and how and why they work - What the Pratt parsing technique and a recursive descent parser is - What others talk about when they talk about built-in data structures - What REPL stands for and how to build one Why this book This is the book I wanted to have a year ago. This is the book I couldn't find. I wrote this book for you and me. So why should you buy it? What's different about it, compared to other interpreter or compiler literature? - Working code is the focus. Code is not just found in the appendix. Code is the main focus of this book. - It's small! It has around 200 pages of which a great deal are readable, syntax-highlighted, working code. - The code presented in the book is easy to understand, easy to extend, easy to maintain. - No 3rd party libraries! You're not left wondering: "But how does tool X do that?" We won't use a tool X. We only use the Go standard library and write everything ourselves. - Tests! The interpreter we build in the book is fully tested! Sometimes in TDD style, sometimes with the tests written after. You can easily run the tests to experiment with the interpreter and make changes. This book is for you if you... - learn by building, love to look under the hood - love programming and to program for the sake of learning and joy! - are interested in how your favorite, interpreted programming language works - never took a compiler course in college - want to get started with interpreters or compilers… - ... but don't want to work through a theory-heavy, 800 pages, 4 pounds co
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值