package main
import (
"fmt"
"os"
)
// token:数字,+,-,*,/
// 四则运算
//
// E->数字M
// M->+数字M|-数字M|*数字M|/数字M
// 数字:以1-9头,后面跟着多个0-9
// 数字 [1-9][0-9]*
// 运算符 + |-|*|/
const (
TokenNumber = 0
TokenPlus = 1
TokenSub = 2
TokenMul = 3
TokenDiv = 4
TokenEnd = 5
)
var tokenDes = map[int]string{
TokenNumber: "数字",
TokenPlus: "加号",
TokenSub: "减号",
TokenMul: "乘号",
TokenDiv: "除号",
}
type dataStack struct {
data int
next *dataStack
prev *dataStack
tokenType uint8
}
func pushData(v int, tokenType uint8) {
if dataStackBase == nil {
dataStackBase = &dataStack{
data: v,
tokenType: tokenType,
}
dataStackTop = dataStackBase
} else {
dataStackTop.next = &dataStack{
data: v,
prev: dataStackTop,
tokenType: tokenType,
}
dataStackTop = dataStackTop.next
}
}
func calDataStack() int {
calMulDiv()
calPlusSub()
result := (dataStackBase.data)
dataStackBase = nil
dataStackTop = nil
return result
}
func charConvertToNum(data []byte) int {
var num int
for i := 0; i < len(data); i++ {
charToNum := data[i] - charZero
num = num*10 + int(charToNum)
}
return num
}
func cal(tokenType1, tokenType2 uint8) {
for i := dataStackBase; i != nil; i = i.next {
if i.tokenType == tokenType1 || i.tokenType == tokenType2 {
prevNum := i.prev.data
nextNum := i.next.data
var calResult int
if i.tokenType == TokenMul {
calResult = prevNum * nextNum
}
if i.tokenType == TokenDiv {
calResult = prevNum / nextNum
}
if i.tokenType == TokenPlus {
calResult = prevNum + nextNum
}
if i.tokenType == TokenSub {
calResult = prevNum - nextNum
}
newDataStack := &dataStack{
tokenType: TokenNumber,
data: calResult,
prev: i.prev.prev,
next: i.next.next,
}
if i.prev.prev == nil {
dataStackBase = newDataStack
} else {
i.prev.prev.next = newDataStack
}
if i.next.next == nil {
dataStackTop = newDataStack
} else {
i.next.next.prev = newDataStack
}
i = newDataStack
}
}
}
func calMulDiv() {
cal(TokenMul, TokenDiv)
}
func calPlusSub() {
cal(TokenPlus, TokenSub)
}
var dataStackBase *dataStack
var dataStackTop *dataStack
// 数字 48, 49, 50, 51, 52, 53, 54, 55, 56, 57
// 空格
// + ,-,*,/
// 换行符号
const (
charZero = 48
charOne = 49
charTwo = 50
charThree = 51
charFour = 52
charFive = 53
charSix = 54
charSeven = 55
charEight = 56
charNine = 57
charEmpty = 32 //空格
charPlus = 43
charSub = 45
charMul = 42
charDiv = 47
charWrap = 10 //换行
charEOF = 111 //没有字符了
)
var currentToken int
var currentTokenStr string
var currenLine int = 1
var currenColumn int = 0
var preColumn int = 0
var prevLine int = 1
var currentChar byte
// var currenChar int
var currentDataPoint = -1
var data []byte
func backChar() {
currenLine = prevLine
currenColumn = preColumn
currentDataPoint--
if currentDataPoint >= 0 && currentDataPoint <= len(data) {
currentChar = data[currentDataPoint]
}
}
// 获取下一个字符
func getNextChar() {
preColumn = currenColumn
prevLine = currenLine
currentDataPoint++
if currentDataPoint >= len(data) {
currentChar = charEOF
return
}
currentChar = data[currentDataPoint]
switch currentChar {
case charWrap:
currenLine++
currenColumn = 0
default:
currenColumn++
}
}
func getNextToken() {
for {
getNextChar()
switch currentChar {
case charEOF:
currentToken = TokenEnd
return
case charEmpty: // 空字符
currentTokenStr = ""
case charWrap: //\n换行
currentTokenStr = "\n"
case charZero:
currentTokenStr = "0"
pushData(0, TokenNumber)
currentToken = TokenNumber
return
case charOne, charTwo, charThree, charFour, charFive, charSix, charSeven, charEight, charNine: // 1-9
currentTokenStr = ""
currentTokenStr += string(currentChar)
var charArr []byte
charArr = append(charArr, currentChar)
for {
getNextChar()
switch currentChar {
case charZero, charOne, charTwo, charThree, charFour, charFive, charSix, charSeven, charEight, charNine: //数字
currentTokenStr += string(currentChar)
charArr = append(charArr, currentChar)
case charWrap:
default:
backChar()
pushData(charConvertToNum(charArr), TokenNumber)
currentToken = TokenNumber
return
}
}
case charPlus: // +
currentToken = TokenPlus
currentTokenStr = "+"
pushData(0, TokenPlus)
return
case charSub: // -
currentToken = TokenSub
currentTokenStr = "-"
pushData(0, TokenSub)
return
case charMul: // *
currentToken = TokenMul
currentTokenStr = "*"
pushData(0, TokenMul)
return
case charDiv: // /
currentToken = TokenDiv
currentTokenStr = "/"
pushData(0, TokenDiv)
return
default:
errMsg, _ := fmt.Printf("非法的字符%c,在第%d行,%d列", data[currentDataPoint], currenLine, currenColumn)
panic(errMsg)
}
}
}
func expectToken(token int) {
if currentToken != token {
errmsg, _ := fmt.Printf("期望%s,实际%s,错误的字符串%s,所在行%d,所在列%d", tokenDes[token], tokenDes[currentToken], currentTokenStr, currenLine, currenColumn)
panic(errmsg)
}
}
func parseE() {
getNextToken()
expectToken(TokenNumber)
parseM()
}
func parseM() {
getNextToken()
switch currentToken {
case TokenPlus, TokenSub, TokenMul, TokenDiv:
case TokenEnd:
fmt.Println("parse m 完成")
return
case TokenNumber:
panic(fmt.Errorf("期望+,-,*,/,或者空,而不是数字,非法字符%s,所在行%d,所在列%d", currentTokenStr, currenLine, currenColumn))
}
getNextToken()
expectToken(TokenNumber)
parseM()
}
func main() {
var err error
data, err = os.ReadFile(os.Args[1])
if err != nil {
panic(err)
}
parseE()
fmt.Println("解析成功")
result := calDataStack()
fmt.Println("结果是:", result)
}
这是一个简单的四则运算,包括词法分析,语法分析,运算通过栈实现先乘除后加减。在报错方面有一些bug,代码也写的很粗糙。如果发现bug可以提出来