json_filter/lexical.go

275 lines
7.6 KiB
Go
Raw Normal View History

// Package filter ...
//
// Description : JSON 词法分析
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2022-07-04 17:52
package filter
2022-07-04 18:08:10 +08:00
import (
2022-07-04 18:39:29 +08:00
"git.zhangdeman.cn/zhangdeman/util"
"github.com/pkg/errors"
)
2022-07-04 20:59:57 +08:00
// NewLexical 获取实例
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 20:52 2022/7/4
func NewLexical(jsonData string) *lexical {
2022-07-04 23:17:02 +08:00
return &lexical{
jsonData: jsonData,
lexicalResult: make([]*lexicalNode, 0),
}
2022-07-04 20:59:57 +08:00
}
// lexical 词法解析
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 20:42 2022/7/4
type lexical struct {
jsonData string
keyLeftRightTokenCnt int
2022-07-04 23:17:02 +08:00
lexicalResult []*lexicalNode
2022-07-04 20:59:57 +08:00
}
// Parse 解析词法
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 18:11 2022/7/4
2022-07-04 23:28:12 +08:00
func (l *lexical) Parse(jsonData string) ([]*lexicalNode, error) {
// mt.Println(jsonData)
if len(jsonData) < 2 {
return nil, errors.New("input data is not json")
}
2022-07-04 18:35:32 +08:00
tmpStr := ""
for _, itemChar := range jsonData {
2022-07-04 18:35:32 +08:00
currentChar := string(itemChar)
tmpRealVal, tmpStrType := l.getTmpStrType(tmpStr)
2022-07-05 14:05:57 +08:00
if l.inputCharIsToken(currentChar, tmpStr, tmpStrType) {
2022-07-04 20:59:57 +08:00
if currentChar == keyLeftRightToken {
// 双引号计数
l.keyLeftRightTokenCnt++
}
2022-07-04 18:35:32 +08:00
// 是关键词
if len(tmpStr) > 0 {
2022-07-04 23:17:02 +08:00
l.lexicalResult = append(l.lexicalResult, &lexicalNode{
Val: tmpRealVal,
2022-07-04 18:35:32 +08:00
IsToken: false,
Type: tmpStrType,
2022-07-04 18:35:32 +08:00
})
}
2022-07-04 23:17:02 +08:00
l.lexicalResult = append(l.lexicalResult, &lexicalNode{
2022-07-04 18:35:32 +08:00
Val: currentChar,
IsToken: true,
Type: "string",
2022-07-04 18:35:32 +08:00
})
2022-07-04 18:47:50 +08:00
tmpStr = ""
2022-07-04 18:35:32 +08:00
} else {
// 不是关键词, 继续向后走
2022-07-05 11:18:53 +08:00
if currentChar == " " && l.keyLeftRightTokenCnt%2 == 0 {
// 当前字符是空格, 只有在 "" 之间方才有效 , 关键字之间的空格, 忽略即可
continue
2022-07-04 23:17:02 +08:00
}
2022-07-05 14:05:57 +08:00
if (currentChar == "\n" || currentChar == "\t") && (l.keyLeftRightTokenCnt%2 == 0 || tmpStrType == "number" || tmpStrType == "bool") {
// 数字或者 bool 之后的 \n \t 无意义 , 不在 "" 之间也无意义
continue
2022-07-05 11:18:53 +08:00
}
2022-07-05 14:05:57 +08:00
tmpStr = tmpStr + currentChar
2022-07-04 18:35:32 +08:00
}
}
2022-07-05 11:18:53 +08:00
2022-07-04 23:28:12 +08:00
return l.lexicalResult, nil
}
// inputCharIsToken 输入字符是否为关键字
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 18:15 2022/7/4
2022-07-05 14:05:57 +08:00
func (l *lexical) inputCharIsToken(inputChar string, tmpStr string, tmpStrType string) bool {
2022-07-05 11:34:14 +08:00
tokenList := []string{
// list 类型起始
listLeftToken,
// listRight list 类型结束
listRightToken,
// 对象起始
objectLeftToken,
// 对象结束
objectRightToken,
// key 值的起始
keyLeftRightToken,
// 转义符
// escapeCharacterToken,
// 冒号
colonToken,
// 逗号
commaToken,
}
preCheck := false
for _, item := range tokenList {
if item == inputChar {
preCheck = true
break
}
}
if !preCheck {
// 输入一定不是关键字
return false
}
2022-07-04 23:17:02 +08:00
if len(tmpStr) == 0 && inputChar == keyLeftRightToken {
// 双引号关键字
return true
}
tmpStrByte := []byte(tmpStr)
if len(tmpStrByte) > 0 {
// 字符串拼接过程中, 只有 " 可能是关键字
// 判断转义符个数
escapeCharacterTokenCnt := 0
for i := len(tmpStrByte) - 1; i >= 0; i-- {
if string(tmpStrByte[i]) == escapeCharacterToken {
// 前一个是转义符
escapeCharacterTokenCnt++
continue
}
// 非连续的转义符不计数
break
}
// 0 个 或者 偶数个转义符, " 是关键字
2022-07-05 11:18:53 +08:00
if inputChar == keyLeftRightToken && escapeCharacterTokenCnt%2 == 0 {
return true
}
}
2022-07-04 23:17:02 +08:00
var preNode *lexicalNode
if len(l.lexicalResult) > 0 {
preNode = l.lexicalResult[len(l.lexicalResult)-1]
}
if inputChar == colonToken && nil != preNode && preNode.ValStr() == keyLeftRightToken && l.keyLeftRightTokenCnt > 0 && l.keyLeftRightTokenCnt%2 == 0 {
2022-07-04 23:17:02 +08:00
// : 必须出现在偶数 " 之后才是关键字
return true
}
// , 是关键字的场景
// {"name":"zhangsan", "age":"18"}
// [{"name":"zhangsan", "age":"18"}, {"name":"zhangsan", "age":"18"}]
// [[],[]]
// [1,2,3]
// [true,false,true]
if inputChar == commaToken && (
2022-07-05 11:18:53 +08:00
// 对应 {"name":"zhangsan", "age":"18"}
(nil != preNode && preNode.ValStr() == keyLeftRightToken) ||
2022-07-04 23:17:02 +08:00
// 对应[{"name":"zhangsan", "age":"18"}, {"name":"zhangsan", "age":"18"}]
(nil != preNode && preNode.ValStr() == objectRightToken) ||
2022-07-04 23:17:02 +08:00
// 对应[[],[]]
(nil != preNode && preNode.ValStr() == listRightToken) ||
2022-07-04 23:17:02 +08:00
// 对应 [true,false,true] / [1,2,3] / [1,true,2,false]
(nil != preNode && (preNode.ValStr() == colonToken || preNode.ValStr() == listLeftToken || preNode.ValStr() == commaToken) && (tmpStrType == "number" || tmpStrType == "bool"))) { // 对应
2022-07-04 23:17:02 +08:00
return true
}
// { 可能出现的情况
// {}
// [{}] [{}, {}]
// {"person": {}}
if inputChar == objectLeftToken && len(tmpStr) == 0 &&
(nil == preNode || // 对应 {}
(nil != preNode && preNode.ValStr() == listLeftToken) || // 对应 [{}]
(nil != preNode && preNode.ValStr() == colonToken) || // 对应 {"person": {}}
(nil != preNode && preNode.ValStr() == commaToken)) { // 对应 [{}, {}]
2022-07-04 23:17:02 +08:00
// { 是关键字
return true
}
// } 可能出出现的情况
// {}
// [{}]
// {"name":"zhangsan"}
// {"person": {"name":"zhangsan"}}
// {"person": {"name":"zhangsan", "age": 18}}
// {"person": {"work":true}}
// {"person": {"like":1}}
2022-07-04 23:28:12 +08:00
// {"person_list": [{"like":1}]}
2022-07-04 23:17:02 +08:00
if inputChar == objectRightToken && len(tmpStr) == 0 && (
// 对应 {}, [{}]
(nil != preNode && preNode.ValStr() == objectLeftToken) ||
2022-07-04 23:17:02 +08:00
// 对应 {"name":"zhangsan"}
(nil != preNode && preNode.ValStr() == keyLeftRightToken) ||
2022-07-04 23:17:02 +08:00
// 对应 {"person": {"name":"zhangsan"}}
(nil != preNode && preNode.ValStr() == objectRightToken)) ||
2022-07-04 23:17:02 +08:00
// 对应 {"person": {"work":true}} / {"person": {"like":1}}
(nil != preNode && preNode.ValStr() == colonToken && (tmpStr == "number" || tmpStr == "bool")) ||
2022-07-04 23:28:12 +08:00
// 对应 {"person_list": [{"like":1}]}
(nil != preNode && preNode.ValStr() == listRightToken) {
2022-07-04 23:17:02 +08:00
// } 是关键字
return true
}
// [ 可能出现的场景
// []
// [[],[]]
// "a": []
2022-07-05 11:18:53 +08:00
if inputChar == listLeftToken && len(tmpStr) == 0 && (nil == preNode || // 对应 []
(nil != preNode && preNode.ValStr() == listLeftToken) || // 对应 [[],[]]
(nil != preNode && preNode.ValStr() == colonToken)) { // 对应 "a": []
2022-07-04 23:17:02 +08:00
// [ 是关键字
return true
}
// ] 可能出现的场景
// []
// [[],[]]
// [{}, {}]
// [1,2,3]
// [true, false]
// ["", "" ]
2022-07-05 11:18:53 +08:00
if inputChar == listRightToken && len(tmpStr) == 0 && (
2022-07-04 23:17:02 +08:00
//对应 []
(nil != preNode && preNode.ValStr() == listLeftToken) ||
2022-07-04 23:17:02 +08:00
// 对应 [[],[]]
(nil != preNode && preNode.ValStr() == listRightToken) ||
2022-07-04 23:17:02 +08:00
// [true, false] /
(nil != preNode && preNode.ValStr() == objectRightToken) ||
2022-07-04 23:17:02 +08:00
// 对应 [{}, {}] / [1,2,3]
(nil != preNode && (tmpStrType == "number" || tmpStrType == "bool")) ||
// 对应 ["", "" ]
(nil != preNode && preNode.ValStr() == keyLeftRightToken)) {
2022-07-05 11:18:53 +08:00
return true
2022-07-04 23:17:02 +08:00
}
return false
}
2022-07-04 23:17:02 +08:00
func (l *lexical) getTmpStrType(tmpStr string) (interface{}, string) {
2022-07-04 23:17:02 +08:00
var preNode *lexicalNode
if len(l.lexicalResult) > 0 {
preNode = l.lexicalResult[len(l.lexicalResult)-1]
}
// 数字只有一个场景 {"age": 18},多以若是数字, 则 前一次解析必为关键字 :
if nil != preNode && preNode.ValStr() == colonToken {
2022-07-04 23:17:02 +08:00
// 判断是否可转数字
var floatVal float64
if err := util.ConvertAssign(&floatVal, tmpStr); nil == err {
return floatVal, "number"
2022-07-04 23:17:02 +08:00
}
}
// 判断是否为 bool
// bool 只有一个场景 {"work": true/false},多以若是数字, 则 前一次解析必为关键字 :
if nil != preNode && preNode.ValStr() == colonToken {
2022-07-04 23:17:02 +08:00
// 判断是否可转数字
var boolVal bool
if err := util.ConvertAssign(&boolVal, tmpStr); nil == err {
return boolVal, "bool"
2022-07-04 23:17:02 +08:00
}
}
return tmpStr, "string"
2022-07-04 23:17:02 +08:00
}