main.go
package main
import (
"fmt"
"os"
)
var data []byte
func main() {
var err error
data, err = os.ReadFile(os.Args[1])
if err != nil {
panic(err)
}
//在调用parseA之前调用
getNextToken()
parseA()
//判断语法是否符合规则,需要判断是不是把所有的token都解析完了,
//如果没有解析完成就说明是不符合语法规则的,需要报错
if currentToken != TokenEnd {
var errDesc string = "期望"
for _, value := range expectTokens {
errDesc += tokenDes[value] + ","
}
errDesc += " 实际是" + tokenDes[currentToken]
errDesc += fmt.Sprintf("行号:%d,列号:%d", currenLine, currenColumn)
panic(errDesc)
} else {
fmt.Println("解析成功")
}
}
parse.go
package main
import "fmt"
//A->MG
//G->+MG|-MG
//M->QN
//N->*QN|/QN
//Q->数字|(A)
// A->MG
func parseA() {
parseM()
parseG()
}
// M->QN
func parseM() {
parseQ()
parseN()
}
// G->+MG|-MG|空
func parseG() {
if currentToken == TokenPlus || currentToken == TokenSub {
expectTokens = nil
getNextToken()
parseM()
parseG()
} else {
expectTokens = append(expectTokens, TokenPlus, TokenSub)
}
}
// Q->数字|(A)
// 这里开头必须是数字或者左括号
func parseQ() {
switch currentToken {
case TokenNumber:
getNextToken()
case TokenOpenPa:
getNextToken()
parseA()
expectToken(TokenClosePa)
getNextToken()
default:
if currentToken != TokenEnd {
panic(fmt.Sprintf("期望数字或者是左括号,实际为%s,行号为%d,列号为%d", currentTokenStr, currenLine, currenColumn))
} else {
panic(fmt.Sprintf("期望下一个token是数字或者是左括号,但是没有token了,当前token是%s", currentTokenStr))
}
}
expectTokens = nil
}
// N->*QN|/QN|空
func parseN() {
if currentToken == TokenMul || currentToken == TokenDiv {
expectTokens = nil
getNextToken()
parseQ()
parseN()
} else {
expectTokens = append(expectTokens, TokenMul, TokenDiv)
}
}
token.go
package main
import "fmt"
// 词法
// token : +,-,*,/,数字
// \n换行,空格 忽略
//(左括号 ) 右括号
const (
TokenNumber = 0
TokenPlus = 1
TokenSub = 2
TokenMul = 3
TokenDiv = 4
TokenEnd = 5
TokenOpenPa = 6
TokenClosePa = 7
)
var tokenDes = map[int]string{
TokenNumber: "数字",
TokenPlus: "加号",
TokenSub: "减号",
TokenMul: "乘号",
TokenDiv: "除号",
TokenOpenPa: "左括号",
TokenClosePa: "右括号",
TokenEnd: "结束符号",
}
// 48, 49, 50, 51, 52, 53, 54, 55, 56, 57 对应数字0,1,2,3,4,5,6,7,8,9
// 空格
// + ,-,*,/
// 换行符号
// (左括号
// )右括号
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 //换行\n
charEOF = 111 //没有字符了
charOpenPa = 40 // (
charClosePa = 41 //)
)
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 expectTokens []int
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 //结束
currentToken = TokenEnd //结束
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"
currentToken = TokenNumber
return
case charOne, charTwo, charThree, charFour, charFive, charSix, charSeven, charEight, charNine: // 1-9
currentTokenStr = ""
currentTokenStr += string(currentChar)
for {
getNextChar()
switch currentChar {
case charZero, charOne, charTwo, charThree, charFour, charFive, charSix, charSeven, charEight, charNine: //数字
currentTokenStr += string(currentChar)
case charWrap:
default:
backChar()
currentToken = TokenNumber
return
}
}
case charPlus: // +
currentToken = TokenPlus
currentTokenStr = "+"
return
case charSub: // -
currentToken = TokenSub
currentTokenStr = "-"
return
case charMul: // *
currentToken = TokenMul
currentTokenStr = "*"
return
case charDiv: // /
currentToken = TokenDiv
currentTokenStr = "/"
return
case charOpenPa: // (
currentToken = TokenOpenPa
currentTokenStr = "("
return
case charClosePa: // (
currentToken = TokenClosePa
currentTokenStr = ")"
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)
}
}
在前面一篇的基础上对报错进行了优化,假如语法解析错误,会指出具体报错的位置,还有报错的位置应该是什么token或者下一个token应该是什么
如有错误,敬请指正。