You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
go-zero/tools/goctl/api/parser/g4/gen/api/baseparser.go

226 lines
5.2 KiB
Go

package api
import (
"fmt"
"net/http"
"regexp"
"strings"
"unicode"
"github.com/antlr/antlr4/runtime/Go/antlr"
)
const (
versionRegex = `(?m)"v[1-9][0-9]*"`
importValueRegex = `(?m)"(/?[a-zA-Z0-9_#-])+\.api"`
tagRegex = `(?m)\x60[a-z]+:".+"\x60`
)
var holder = struct{}{}
var kind = map[string]struct{}{
"bool": holder,
"int": holder,
"int8": holder,
"int16": holder,
"int32": holder,
"int64": holder,
"uint": holder,
"uint8": holder,
"uint16": holder,
"uint32": holder,
"uint64": holder,
"uintptr": holder,
"float32": holder,
"float64": holder,
"complex64": holder,
"complex128": holder,
"string": holder,
"byte": holder,
"rune": holder,
}
func match(p *ApiParserParser, text string) {
v := getCurrentTokenText(p)
if v != text {
notifyErrorListeners(p, expecting(text, v))
}
}
func checkVersion(p *ApiParserParser) {
v := getCurrentTokenText(p)
if !matchRegex(v, versionRegex) {
notifyErrorListeners(p, mismatched("version", v))
}
}
func checkImportValue(p *ApiParserParser) {
v := getCurrentTokenText(p)
if !matchRegex(v, importValueRegex) {
notifyErrorListeners(p, mismatched("import value", v))
}
}
func checkKeyValue(p *ApiParserParser) {
v := getCurrentTokenText(p)
if !strings.HasPrefix(v, ":") {
notifyErrorListeners(p, mismatched(":", v))
}
v = strings.TrimPrefix(v, ":")
v = strings.TrimFunc(v, func(r rune) bool {
return unicode.IsSpace(r)
})
setCurrentTokenText(p, v)
}
func checkHTTPMethod(p *ApiParserParser) {
method := getCurrentTokenText(p)
uppler := strings.ToUpper(method)
switch uppler {
case http.MethodPost, http.MethodGet, http.MethodHead,
http.MethodPut, http.MethodPatch, http.MethodDelete,
http.MethodConnect, http.MethodOptions, http.MethodTrace:
if method != strings.ToLower(method) {
notifyErrorListeners(p, expecting("http method lower case", method))
}
default:
notifyErrorListeners(p, expecting("http method", method))
}
}
func checkKeyword(p *ApiParserParser) {
v := getCurrentTokenText(p)
if IsGolangKeyWord(v) {
notifyErrorListeners(p, fmt.Sprintf("expecting ID, found golang keyword: '%s'", v))
}
}
func checkKey(p *ApiParserParser) {
v := getCurrentTokenText(p)
if IsGolangKeyWord(v) {
notifyErrorListeners(p, fmt.Sprintf("expecting ID, found golang keyword: '%s'", v))
}
if _, ok := kind[v]; !ok {
notifyErrorListeners(p, fmt.Sprintf("expecting golang basic type, found : '%s'", v))
}
}
// IsBasicType returns true if the input argument is basic golang type
func IsBasicType(text string) bool {
_, ok := kind[text]
return ok
}
// IsGolangKeyWord returns true if input argument is golang keyword, but it will be ignored which in excepts
func IsGolangKeyWord(text string, excepts ...string) bool {
for _, each := range excepts {
if text == each {
return false
}
}
switch text {
case "var", "const", "package", "import", "func", "return",
"defer", "go", "select", "interface", "struct", "break", "case",
"continue", "for", "fallthrough", "else", "if", "switch", "goto",
"default", "chan", "type", "map", "range":
return true
default:
return false
}
}
func isNormal(p *ApiParserParser) bool {
ct := p.GetTokenStream().(*antlr.CommonTokenStream)
line := p.GetCurrentToken().GetLine()
tokens := ct.GetAllTokens()
var list []string
for _, token := range tokens {
if token.GetLine() == line {
text := token.GetText()
if strings.HasPrefix(text, "//") {
continue
}
if strings.HasPrefix(text, "/*") {
continue
}
if text == "<EOF>" {
continue
}
if strings.TrimSpace(text) == "" {
continue
}
list = append(list, text)
}
}
if len(list) == 1 {
t := strings.TrimPrefix(list[0], "*")
if IsGolangKeyWord(t) {
notifyErrorListeners(p, fmt.Sprintf("expecting ID, found golang keyword: '%s'", t))
}
}
if len(list) > 1 {
if list[0] == "*" {
t := strings.TrimPrefix(list[1], "*")
if IsGolangKeyWord(t) {
notifyErrorListeners(p, fmt.Sprintf("expecting ID, found golang keyword: '%s'", t))
}
return false
}
}
return len(list) > 1
}
// MatchTag returns a Boolean value, which returns true if it does matched, otherwise returns fase
func MatchTag(v string) bool {
return matchRegex(v, tagRegex)
}
func isInterface(p *ApiParserParser) {
v := getCurrentTokenText(p)
if IsGolangKeyWord(v) {
notifyErrorListeners(p, fmt.Sprintf("expecting ID, found golang keyword: '%s'", v))
}
}
func getCurrentTokenText(p *ApiParserParser) string {
token := p.GetCurrentToken()
if token == nil {
return ""
}
return token.GetText()
}
func setCurrentTokenText(p *ApiParserParser, text string) {
token := p.GetCurrentToken()
if token == nil {
return
}
token.SetText(text)
}
func notifyErrorListeners(p *ApiParserParser, msg string) {
p.NotifyErrorListeners(msg, nil, nil)
}
func matchRegex(text, str string) bool {
re := regexp.MustCompile(str)
v := re.FindString(text)
text = strings.TrimFunc(text, func(r rune) bool {
return unicode.IsSpace(r)
})
return v == text
}
func expecting(expecting, found string) string {
return fmt.Sprintf(`expecting '%s', found input '%s'`, expecting, found)
}
func mismatched(expecting, found string) string {
return fmt.Sprintf(`mismatched '%s', found input '%s'`, expecting, found)
}