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.
1627 lines
30 KiB
Go
1627 lines
30 KiB
Go
package parser
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"path/filepath"
|
|
"reflect"
|
|
"strings"
|
|
|
|
"github.com/zeromicro/go-zero/tools/goctl/pkg/parser/api/ast"
|
|
"github.com/zeromicro/go-zero/tools/goctl/pkg/parser/api/scanner"
|
|
"github.com/zeromicro/go-zero/tools/goctl/pkg/parser/api/token"
|
|
)
|
|
|
|
const idAPI = "api"
|
|
|
|
// Parser is the parser for api file.
|
|
type Parser struct {
|
|
s *scanner.Scanner
|
|
errors []error
|
|
|
|
curTok token.Token
|
|
peekTok token.Token
|
|
|
|
headCommentGroup ast.CommentGroup
|
|
api *ast.AST
|
|
node map[token.Token]*ast.TokenNode
|
|
}
|
|
|
|
// New creates a new parser.
|
|
func New(filename string, src interface{}) *Parser {
|
|
abs, err := filepath.Abs(filename)
|
|
if err != nil {
|
|
log.Fatalln(err)
|
|
}
|
|
|
|
p := &Parser{
|
|
s: scanner.MustNewScanner(abs, src),
|
|
api: &ast.AST{Filename: abs},
|
|
node: make(map[token.Token]*ast.TokenNode),
|
|
}
|
|
|
|
return p
|
|
}
|
|
|
|
// Parse parses the api file.
|
|
func (p *Parser) Parse() *ast.AST {
|
|
if !p.init() {
|
|
return nil
|
|
}
|
|
|
|
for p.curTokenIsNotEof() {
|
|
stmt := p.parseStmt()
|
|
if isNil(stmt) {
|
|
return nil
|
|
}
|
|
|
|
p.appendStmt(stmt)
|
|
if !p.nextToken() {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return p.api
|
|
}
|
|
|
|
func (p *Parser) parseStmt() ast.Stmt {
|
|
switch p.curTok.Type {
|
|
case token.IDENT:
|
|
switch {
|
|
case p.curTok.Is(token.Syntax):
|
|
return p.parseSyntaxStmt()
|
|
case p.curTok.Is(token.Info):
|
|
return p.parseInfoStmt()
|
|
case p.curTok.Is(token.Service):
|
|
return p.parseService()
|
|
case p.curTok.Is(token.TypeKeyword):
|
|
return p.parseTypeStmt()
|
|
case p.curTok.Is(token.ImportKeyword):
|
|
return p.parseImportStmt()
|
|
default:
|
|
p.expectIdentError(p.curTok, token.Syntax, token.Info, token.Service, token.TYPE)
|
|
return nil
|
|
}
|
|
case token.AT_SERVER:
|
|
return p.parseService()
|
|
default:
|
|
p.errors = append(p.errors, fmt.Errorf("%s unexpected token '%s'", p.curTok.Position.String(), p.peekTok.Text))
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func (p *Parser) parseService() *ast.ServiceStmt {
|
|
var stmt = &ast.ServiceStmt{}
|
|
if p.curTokenIs(token.AT_SERVER) {
|
|
atServerStmt := p.parseAtServerStmt()
|
|
if atServerStmt == nil {
|
|
return nil
|
|
}
|
|
|
|
stmt.AtServerStmt = atServerStmt
|
|
if !p.advanceIfPeekTokenIs(token.Service) {
|
|
return nil
|
|
}
|
|
}
|
|
stmt.Service = p.curTokenNode()
|
|
|
|
if !p.advanceIfPeekTokenIs(token.IDENT) {
|
|
return nil
|
|
}
|
|
|
|
// service name expr
|
|
nameExpr := p.parseServiceNameExpr()
|
|
if nameExpr == nil {
|
|
return nil
|
|
}
|
|
|
|
stmt.Name = nameExpr
|
|
|
|
// token '{'
|
|
if !p.advanceIfPeekTokenIs(token.LBRACE) {
|
|
return nil
|
|
}
|
|
|
|
stmt.LBrace = p.curTokenNode()
|
|
|
|
// service item statements
|
|
routes := p.parseServiceItemsStmt()
|
|
if routes == nil {
|
|
return nil
|
|
}
|
|
|
|
stmt.Routes = routes
|
|
|
|
// token '}'
|
|
if !p.advanceIfPeekTokenIs(token.RBRACE) {
|
|
return nil
|
|
}
|
|
|
|
stmt.RBrace = p.curTokenNode()
|
|
|
|
return stmt
|
|
}
|
|
|
|
func (p *Parser) parseServiceItemsStmt() []*ast.ServiceItemStmt {
|
|
var stmt = make([]*ast.ServiceItemStmt, 0)
|
|
for p.curTokenIsNotEof() && p.peekTokenIsNot(token.RBRACE) {
|
|
item := p.parseServiceItemStmt()
|
|
if item == nil {
|
|
return nil
|
|
}
|
|
|
|
stmt = append(stmt, item)
|
|
if p.peekTokenIs(token.RBRACE) {
|
|
break
|
|
}
|
|
|
|
if p.notExpectPeekToken(token.AT_DOC, token.AT_HANDLER, token.RBRACE) {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return stmt
|
|
}
|
|
|
|
func (p *Parser) parseServiceItemStmt() *ast.ServiceItemStmt {
|
|
var stmt = &ast.ServiceItemStmt{}
|
|
// statement @doc
|
|
if p.peekTokenIs(token.AT_DOC) {
|
|
if !p.nextToken() {
|
|
return nil
|
|
}
|
|
|
|
atDocStmt := p.parseAtDocStmt()
|
|
if atDocStmt == nil {
|
|
return nil
|
|
}
|
|
|
|
stmt.AtDoc = atDocStmt
|
|
}
|
|
|
|
// statement @handler
|
|
if !p.advanceIfPeekTokenIs(token.AT_HANDLER, token.RBRACE) {
|
|
return nil
|
|
}
|
|
|
|
if p.peekTokenIs(token.RBRACE) {
|
|
return stmt
|
|
}
|
|
|
|
atHandlerStmt := p.parseAtHandlerStmt()
|
|
if atHandlerStmt == nil {
|
|
return nil
|
|
}
|
|
|
|
stmt.AtHandler = atHandlerStmt
|
|
|
|
// statement route
|
|
route := p.parseRouteStmt()
|
|
if route == nil {
|
|
return nil
|
|
}
|
|
|
|
stmt.Route = route
|
|
|
|
return stmt
|
|
}
|
|
|
|
func (p *Parser) parseRouteStmt() *ast.RouteStmt {
|
|
var stmt = &ast.RouteStmt{}
|
|
// token http method
|
|
if !p.advanceIfPeekTokenIs(token.HttpMethods...) {
|
|
return nil
|
|
}
|
|
|
|
stmt.Method = p.curTokenNode()
|
|
|
|
// path expression
|
|
pathExpr := p.parsePathExpr()
|
|
if pathExpr == nil {
|
|
return nil
|
|
}
|
|
|
|
stmt.Path = pathExpr
|
|
|
|
if p.peekTokenIs(token.AT_DOC, token.AT_HANDLER, token.RBRACE) {
|
|
return stmt
|
|
}
|
|
|
|
if p.peekTokenIs(token.SEMICOLON) {
|
|
p.nextToken()
|
|
return stmt
|
|
}
|
|
|
|
if p.notExpectPeekToken(token.Returns, token.LPAREN) {
|
|
return nil
|
|
}
|
|
|
|
if p.peekTokenIs(token.LPAREN) {
|
|
// request expression
|
|
requestBodyStmt := p.parseBodyStmt()
|
|
if requestBodyStmt == nil {
|
|
return nil
|
|
}
|
|
|
|
stmt.Request = requestBodyStmt
|
|
}
|
|
|
|
if p.notExpectPeekToken(token.Returns, token.AT_DOC, token.AT_HANDLER, token.RBRACE, token.SEMICOLON) {
|
|
return nil
|
|
}
|
|
|
|
// token 'returns'
|
|
if p.peekTokenIs(token.Returns) {
|
|
if !p.nextToken() {
|
|
return nil
|
|
}
|
|
|
|
stmt.Returns = p.curTokenNode()
|
|
responseBodyStmt := p.parseBodyStmt()
|
|
if responseBodyStmt == nil {
|
|
return nil
|
|
}
|
|
|
|
stmt.Response = responseBodyStmt
|
|
}
|
|
|
|
if p.peekTokenIs(token.SEMICOLON) {
|
|
p.nextToken()
|
|
}
|
|
|
|
return stmt
|
|
}
|
|
|
|
func (p *Parser) parseBodyStmt() *ast.BodyStmt {
|
|
var stmt = &ast.BodyStmt{}
|
|
// token '('
|
|
if !p.advanceIfPeekTokenIs(token.LPAREN) {
|
|
return nil
|
|
}
|
|
|
|
stmt.LParen = p.curTokenNode()
|
|
// token ')'
|
|
if p.peekTokenIs(token.RPAREN) {
|
|
if !p.nextToken() {
|
|
return nil
|
|
}
|
|
|
|
stmt.RParen = p.curTokenNode()
|
|
return stmt
|
|
}
|
|
|
|
expr := p.parseBodyExpr()
|
|
if expr == nil {
|
|
return nil
|
|
}
|
|
|
|
stmt.Body = expr
|
|
|
|
// token ')'
|
|
if !p.advanceIfPeekTokenIs(token.RPAREN) {
|
|
return nil
|
|
}
|
|
|
|
stmt.RParen = p.curTokenNode()
|
|
|
|
return stmt
|
|
}
|
|
|
|
func (p *Parser) parseBodyExpr() *ast.BodyExpr {
|
|
var expr = &ast.BodyExpr{}
|
|
switch {
|
|
case p.peekTokenIs(token.LBRACK): // token '['
|
|
if !p.nextToken() {
|
|
return nil
|
|
}
|
|
|
|
expr.LBrack = p.curTokenNode()
|
|
|
|
// token ']'
|
|
if !p.advanceIfPeekTokenIs(token.RBRACK) {
|
|
return nil
|
|
}
|
|
|
|
expr.RBrack = p.curTokenNode()
|
|
|
|
switch {
|
|
case p.peekTokenIs(token.MUL):
|
|
if !p.nextToken() {
|
|
return nil
|
|
}
|
|
|
|
expr.Star = p.curTokenNode()
|
|
if !p.advanceIfPeekTokenIs(token.IDENT) {
|
|
return nil
|
|
}
|
|
|
|
expr.Value = p.curTokenNode()
|
|
return expr
|
|
case p.peekTokenIs(token.IDENT):
|
|
if !p.nextToken() {
|
|
return nil
|
|
}
|
|
|
|
expr.Value = p.curTokenNode()
|
|
return expr
|
|
default:
|
|
p.expectPeekToken(token.MUL, token.IDENT)
|
|
return nil
|
|
}
|
|
case p.peekTokenIs(token.MUL):
|
|
if !p.nextToken() {
|
|
return nil
|
|
}
|
|
|
|
expr.Star = p.curTokenNode()
|
|
if !p.advanceIfPeekTokenIs(token.IDENT) {
|
|
return nil
|
|
}
|
|
|
|
expr.Value = p.curTokenNode()
|
|
return expr
|
|
case p.peekTokenIs(token.IDENT):
|
|
if !p.nextToken() {
|
|
return nil
|
|
}
|
|
|
|
expr.Value = p.curTokenNode()
|
|
return expr
|
|
default:
|
|
p.expectPeekToken(token.LBRACK, token.MUL, token.IDENT)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func (p *Parser) parsePathExpr() *ast.PathExpr {
|
|
var expr = &ast.PathExpr{}
|
|
|
|
var values []token.Token
|
|
for p.curTokenIsNotEof() &&
|
|
p.peekTokenIsNot(token.LPAREN, token.Returns, token.AT_DOC, token.AT_HANDLER, token.SEMICOLON, token.RBRACE) {
|
|
// token '/'
|
|
if !p.advanceIfPeekTokenIs(token.QUO) {
|
|
return nil
|
|
}
|
|
|
|
values = append(values, p.curTok)
|
|
if p.peekTokenIs(token.LPAREN, token.Returns, token.AT_DOC, token.AT_HANDLER, token.SEMICOLON, token.RBRACE) {
|
|
break
|
|
}
|
|
if p.notExpectPeekTokenGotComment(p.curTokenNode().PeekFirstLeadingComment(), token.COLON, token.IDENT, token.INT) {
|
|
return nil
|
|
}
|
|
|
|
// token ':' or IDENT
|
|
if p.notExpectPeekToken(token.COLON, token.IDENT, token.INT) {
|
|
return nil
|
|
}
|
|
|
|
if p.notExpectPeekTokenGotComment(p.curTokenNode().PeekFirstLeadingComment(), token.COLON) {
|
|
return nil
|
|
}
|
|
|
|
// token ':'
|
|
if p.peekTokenIs(token.COLON) {
|
|
if !p.nextToken() {
|
|
return nil
|
|
}
|
|
|
|
values = append(values, p.curTok)
|
|
}
|
|
|
|
// path id tokens
|
|
pathTokens := p.parsePathItem()
|
|
if pathTokens == nil {
|
|
return nil
|
|
}
|
|
|
|
values = append(values, pathTokens...)
|
|
if p.notExpectPeekToken(token.QUO, token.LPAREN, token.Returns, token.AT_DOC, token.AT_HANDLER, token.SEMICOLON, token.RBRACE) {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
var textList []string
|
|
for _, v := range values {
|
|
textList = append(textList, v.Text)
|
|
}
|
|
|
|
node := ast.NewTokenNode(token.Token{
|
|
Type: token.PATH,
|
|
Text: strings.Join(textList, ""),
|
|
Position: values[0].Position,
|
|
})
|
|
node.SetLeadingCommentGroup(p.curTokenNode().LeadingCommentGroup)
|
|
expr.Value = node
|
|
|
|
return expr
|
|
}
|
|
|
|
func (p *Parser) parsePathItem() []token.Token {
|
|
var list []token.Token
|
|
if !p.advanceIfPeekTokenIs(token.IDENT, token.INT) {
|
|
return nil
|
|
}
|
|
list = append(list, p.curTok)
|
|
|
|
for p.curTokenIsNotEof() &&
|
|
p.peekTokenIsNot(token.QUO, token.LPAREN, token.Returns, token.AT_DOC, token.AT_HANDLER, token.RBRACE, token.SEMICOLON, token.EOF) {
|
|
if p.peekTokenIs(token.SUB) {
|
|
if !p.nextToken() {
|
|
return nil
|
|
}
|
|
|
|
list = append(list, p.curTok)
|
|
|
|
if !p.advanceIfPeekTokenIs(token.IDENT) {
|
|
return nil
|
|
}
|
|
list = append(list, p.curTok)
|
|
} else {
|
|
if p.peekTokenIs(token.LPAREN, token.Returns, token.AT_DOC, token.AT_HANDLER, token.SEMICOLON, token.RBRACE) {
|
|
return list
|
|
}
|
|
|
|
if !p.advanceIfPeekTokenIs(token.IDENT) {
|
|
return nil
|
|
}
|
|
|
|
list = append(list, p.curTok)
|
|
}
|
|
}
|
|
|
|
return list
|
|
}
|
|
|
|
func (p *Parser) parseServiceNameExpr() *ast.ServiceNameExpr {
|
|
var expr = &ast.ServiceNameExpr{}
|
|
var text = p.curTok.Text
|
|
|
|
pos := p.curTok.Position
|
|
if p.peekTokenIs(token.SUB) {
|
|
if !p.nextToken() {
|
|
return nil
|
|
}
|
|
|
|
text += p.curTok.Text
|
|
if !p.expectPeekToken(idAPI) {
|
|
return nil
|
|
}
|
|
|
|
if !p.nextToken() {
|
|
return nil
|
|
}
|
|
|
|
text += p.curTok.Text
|
|
}
|
|
|
|
node := ast.NewTokenNode(token.Token{
|
|
Type: token.IDENT,
|
|
Text: text,
|
|
Position: pos,
|
|
})
|
|
node.SetLeadingCommentGroup(p.curTokenNode().LeadingCommentGroup)
|
|
expr.Name = node
|
|
|
|
return expr
|
|
}
|
|
|
|
func (p *Parser) parseAtDocStmt() ast.AtDocStmt {
|
|
if p.notExpectPeekToken(token.LPAREN, token.STRING) {
|
|
return nil
|
|
}
|
|
|
|
if p.peekTokenIs(token.LPAREN) {
|
|
return p.parseAtDocGroupStmt()
|
|
}
|
|
|
|
return p.parseAtDocLiteralStmt()
|
|
}
|
|
|
|
func (p *Parser) parseAtDocGroupStmt() ast.AtDocStmt {
|
|
var stmt = &ast.AtDocGroupStmt{}
|
|
stmt.AtDoc = p.curTokenNode()
|
|
|
|
// token '('
|
|
if !p.advanceIfPeekTokenIs(token.LPAREN) {
|
|
return nil
|
|
}
|
|
|
|
stmt.LParen = p.curTokenNode()
|
|
|
|
for p.curTokenIsNotEof() && p.peekTokenIsNot(token.RPAREN) {
|
|
expr := p.parseKVExpression()
|
|
if expr == nil {
|
|
return nil
|
|
}
|
|
|
|
stmt.Values = append(stmt.Values, expr)
|
|
if p.notExpectPeekToken(token.RPAREN, token.KEY) {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// token ')'
|
|
if !p.advanceIfPeekTokenIs(token.RPAREN) {
|
|
return nil
|
|
}
|
|
|
|
stmt.RParen = p.curTokenNode()
|
|
|
|
return stmt
|
|
}
|
|
|
|
func (p *Parser) parseAtDocLiteralStmt() ast.AtDocStmt {
|
|
var stmt = &ast.AtDocLiteralStmt{}
|
|
stmt.AtDoc = p.curTokenNode()
|
|
|
|
if !p.advanceIfPeekTokenIs(token.STRING) {
|
|
return nil
|
|
}
|
|
|
|
stmt.Value = p.curTokenNode()
|
|
|
|
return stmt
|
|
}
|
|
|
|
func (p *Parser) parseAtHandlerStmt() *ast.AtHandlerStmt {
|
|
var stmt = &ast.AtHandlerStmt{}
|
|
stmt.AtHandler = p.curTokenNode()
|
|
|
|
// token IDENT
|
|
if !p.advanceIfPeekTokenIs(token.IDENT) {
|
|
return nil
|
|
}
|
|
|
|
stmt.Name = p.curTokenNode()
|
|
|
|
return stmt
|
|
}
|
|
|
|
func (p *Parser) parseAtServerStmt() *ast.AtServerStmt {
|
|
var stmt = &ast.AtServerStmt{}
|
|
stmt.AtServer = p.curTokenNode()
|
|
|
|
// token '('
|
|
if !p.advanceIfPeekTokenIs(token.LPAREN) {
|
|
return nil
|
|
}
|
|
|
|
stmt.LParen = p.curTokenNode()
|
|
|
|
for p.curTokenIsNotEof() && p.peekTokenIsNot(token.RPAREN) {
|
|
expr := p.parseAtServerKVExpression()
|
|
if expr == nil {
|
|
return nil
|
|
}
|
|
|
|
stmt.Values = append(stmt.Values, expr)
|
|
if p.notExpectPeekToken(token.RPAREN, token.KEY) {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// token ')'
|
|
if !p.advanceIfPeekTokenIs(token.RPAREN) {
|
|
return nil
|
|
}
|
|
|
|
stmt.RParen = p.curTokenNode()
|
|
|
|
return stmt
|
|
}
|
|
|
|
func (p *Parser) parseTypeStmt() ast.TypeStmt {
|
|
switch {
|
|
case p.peekTokenIs(token.LPAREN):
|
|
return p.parseTypeGroupStmt()
|
|
case p.peekTokenIs(token.IDENT):
|
|
return p.parseTypeLiteralStmt()
|
|
default:
|
|
p.expectPeekToken(token.LPAREN, token.IDENT)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func (p *Parser) parseTypeLiteralStmt() ast.TypeStmt {
|
|
var stmt = &ast.TypeLiteralStmt{}
|
|
stmt.Type = p.curTokenNode()
|
|
|
|
expr := p.parseTypeExpr()
|
|
if expr == nil {
|
|
return nil
|
|
}
|
|
|
|
stmt.Expr = expr
|
|
|
|
return stmt
|
|
}
|
|
|
|
func (p *Parser) parseTypeGroupStmt() ast.TypeStmt {
|
|
var stmt = &ast.TypeGroupStmt{}
|
|
stmt.Type = p.curTokenNode()
|
|
|
|
// token '('
|
|
if !p.nextToken() {
|
|
return nil
|
|
}
|
|
|
|
stmt.LParen = p.curTokenNode()
|
|
|
|
exprList := p.parseTypeExprList()
|
|
if exprList == nil {
|
|
return nil
|
|
}
|
|
|
|
stmt.ExprList = exprList
|
|
|
|
// token ')'
|
|
if !p.advanceIfPeekTokenIs(token.RPAREN) {
|
|
return nil
|
|
}
|
|
|
|
stmt.RParen = p.curTokenNode()
|
|
|
|
return stmt
|
|
}
|
|
|
|
func (p *Parser) parseTypeExprList() []*ast.TypeExpr {
|
|
if !p.expectPeekToken(token.IDENT, token.RPAREN) {
|
|
return nil
|
|
}
|
|
|
|
var exprList = make([]*ast.TypeExpr, 0)
|
|
for p.curTokenIsNotEof() && p.peekTokenIsNot(token.RPAREN, token.EOF) {
|
|
expr := p.parseTypeExpr()
|
|
if expr == nil {
|
|
return nil
|
|
}
|
|
|
|
exprList = append(exprList, expr)
|
|
if !p.expectPeekToken(token.IDENT, token.RPAREN) {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return exprList
|
|
}
|
|
|
|
func (p *Parser) parseTypeExpr() *ast.TypeExpr {
|
|
var expr = &ast.TypeExpr{}
|
|
// token IDENT
|
|
if !p.advanceIfPeekTokenIs(token.IDENT) {
|
|
return nil
|
|
}
|
|
|
|
if p.curTokenIsKeyword() {
|
|
return nil
|
|
}
|
|
|
|
expr.Name = p.curTokenNode()
|
|
|
|
// token '='
|
|
if p.peekTokenIs(token.ASSIGN) {
|
|
if !p.nextToken() {
|
|
return nil
|
|
}
|
|
|
|
expr.Assign = p.curTokenNode()
|
|
}
|
|
|
|
dt := p.parseDataType()
|
|
if isNil(dt) {
|
|
return nil
|
|
}
|
|
|
|
expr.DataType = dt
|
|
|
|
return expr
|
|
}
|
|
|
|
func (p *Parser) parseDataType() ast.DataType {
|
|
switch {
|
|
case p.peekTokenIs(token.Any):
|
|
return p.parseAnyDataType()
|
|
case p.peekTokenIs(token.LBRACE):
|
|
return p.parseStructDataType()
|
|
case p.peekTokenIs(token.IDENT):
|
|
if p.peekTokenIs(token.MapKeyword) {
|
|
return p.parseMapDataType()
|
|
}
|
|
if !p.nextToken() {
|
|
return nil
|
|
}
|
|
|
|
if p.curTokenIsKeyword() {
|
|
return nil
|
|
}
|
|
|
|
node := p.curTokenNode()
|
|
baseDT := &ast.BaseDataType{Base: node}
|
|
|
|
return baseDT
|
|
case p.peekTokenIs(token.LBRACK):
|
|
if !p.nextToken() {
|
|
return nil
|
|
}
|
|
|
|
switch {
|
|
case p.peekTokenIs(token.RBRACK):
|
|
return p.parseSliceDataType()
|
|
case p.peekTokenIs(token.INT, token.ELLIPSIS):
|
|
return p.parseArrayDataType()
|
|
default:
|
|
p.expectPeekToken(token.RBRACK, token.INT, token.ELLIPSIS)
|
|
return nil
|
|
}
|
|
case p.peekTokenIs(token.ANY):
|
|
return p.parseInterfaceDataType()
|
|
case p.peekTokenIs(token.MUL):
|
|
return p.parsePointerDataType()
|
|
default:
|
|
p.expectPeekToken(token.IDENT, token.LBRACK, token.ANY, token.MUL, token.LBRACE)
|
|
return nil
|
|
}
|
|
}
|
|
func (p *Parser) parseStructDataType() *ast.StructDataType {
|
|
var tp = &ast.StructDataType{}
|
|
if !p.nextToken() {
|
|
return nil
|
|
}
|
|
|
|
tp.LBrace = p.curTokenNode()
|
|
|
|
if p.notExpectPeekToken(token.IDENT, token.MUL, token.RBRACE) {
|
|
return nil
|
|
}
|
|
|
|
// ElemExprList
|
|
elems := p.parseElemExprList()
|
|
if elems == nil {
|
|
return nil
|
|
}
|
|
|
|
tp.Elements = elems
|
|
|
|
if !p.advanceIfPeekTokenIs(token.RBRACE) {
|
|
return nil
|
|
}
|
|
|
|
tp.RBrace = p.curTokenNode()
|
|
|
|
return tp
|
|
}
|
|
|
|
func (p *Parser) parseElemExprList() ast.ElemExprList {
|
|
var list = make(ast.ElemExprList, 0)
|
|
for p.curTokenIsNotEof() && p.peekTokenIsNot(token.RBRACE, token.EOF) {
|
|
if p.notExpectPeekToken(token.IDENT, token.MUL, token.RBRACE) {
|
|
return nil
|
|
}
|
|
|
|
expr := p.parseElemExpr()
|
|
if expr == nil {
|
|
return nil
|
|
}
|
|
|
|
list = append(list, expr)
|
|
if p.notExpectPeekToken(token.IDENT, token.MUL, token.RBRACE) {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return list
|
|
}
|
|
|
|
func (p *Parser) parseElemExpr() *ast.ElemExpr {
|
|
var expr = &ast.ElemExpr{}
|
|
if !p.advanceIfPeekTokenIs(token.IDENT, token.MUL) {
|
|
return nil
|
|
}
|
|
|
|
if p.curTokenIsKeyword() {
|
|
return nil
|
|
}
|
|
|
|
identNode := p.curTokenNode()
|
|
if p.curTokenIs(token.MUL) {
|
|
star := p.curTokenNode()
|
|
if !p.advanceIfPeekTokenIs(token.IDENT) {
|
|
return nil
|
|
}
|
|
|
|
var dt ast.DataType
|
|
if p.curTokenIs(token.Any) {
|
|
dt = &ast.AnyDataType{Any: p.curTokenNode()}
|
|
} else {
|
|
dt = &ast.BaseDataType{Base: p.curTokenNode()}
|
|
}
|
|
expr.DataType = &ast.PointerDataType{
|
|
Star: star,
|
|
DataType: dt,
|
|
}
|
|
} else if p.peekTok.Line() > identNode.Token.Line() || p.peekTokenIs(token.RAW_STRING) {
|
|
if p.curTokenIs(token.Any) {
|
|
expr.DataType = &ast.AnyDataType{Any: identNode}
|
|
} else {
|
|
expr.DataType = &ast.BaseDataType{Base: identNode}
|
|
}
|
|
} else {
|
|
expr.Name = append(expr.Name, identNode)
|
|
if p.notExpectPeekToken(token.COMMA, token.IDENT, token.LBRACK, token.ANY, token.MUL, token.LBRACE) {
|
|
return nil
|
|
}
|
|
|
|
for p.peekTokenIs(token.COMMA) {
|
|
if !p.nextToken() {
|
|
return nil
|
|
}
|
|
|
|
if !p.advanceIfPeekTokenIs(token.IDENT) {
|
|
return nil
|
|
}
|
|
|
|
if p.curTokenIsKeyword() {
|
|
return nil
|
|
}
|
|
|
|
expr.Name = append(expr.Name, p.curTokenNode())
|
|
}
|
|
|
|
dt := p.parseDataType()
|
|
if isNil(dt) {
|
|
return nil
|
|
}
|
|
|
|
expr.DataType = dt
|
|
}
|
|
|
|
if p.notExpectPeekToken(token.RAW_STRING, token.MUL, token.IDENT, token.RBRACE) {
|
|
return nil
|
|
}
|
|
|
|
if p.peekTokenIs(token.RAW_STRING) {
|
|
if !p.nextToken() {
|
|
return nil
|
|
}
|
|
|
|
expr.Tag = p.curTokenNode()
|
|
}
|
|
|
|
return expr
|
|
}
|
|
|
|
func (p *Parser) parseAnyDataType() *ast.AnyDataType {
|
|
var tp = &ast.AnyDataType{}
|
|
if !p.nextToken() {
|
|
return nil
|
|
}
|
|
|
|
tp.Any = p.curTokenNode()
|
|
|
|
return tp
|
|
}
|
|
|
|
func (p *Parser) parsePointerDataType() *ast.PointerDataType {
|
|
var tp = &ast.PointerDataType{}
|
|
if !p.nextToken() {
|
|
return nil
|
|
}
|
|
|
|
tp.Star = p.curTokenNode()
|
|
|
|
if p.notExpectPeekToken(token.IDENT, token.LBRACK, token.ANY, token.MUL) {
|
|
return nil
|
|
}
|
|
|
|
// DataType
|
|
dt := p.parseDataType()
|
|
if isNil(dt) {
|
|
return nil
|
|
}
|
|
|
|
tp.DataType = dt
|
|
|
|
return tp
|
|
}
|
|
|
|
func (p *Parser) parseInterfaceDataType() *ast.InterfaceDataType {
|
|
var tp = &ast.InterfaceDataType{}
|
|
if !p.nextToken() {
|
|
return nil
|
|
}
|
|
|
|
tp.Interface = p.curTokenNode()
|
|
|
|
return tp
|
|
}
|
|
|
|
func (p *Parser) parseMapDataType() *ast.MapDataType {
|
|
var tp = &ast.MapDataType{}
|
|
if !p.nextToken() {
|
|
return nil
|
|
}
|
|
|
|
tp.Map = p.curTokenNode()
|
|
|
|
// token '['
|
|
if !p.advanceIfPeekTokenIs(token.LBRACK) {
|
|
return nil
|
|
}
|
|
|
|
tp.LBrack = p.curTokenNode()
|
|
|
|
// DataType
|
|
dt := p.parseDataType()
|
|
if isNil(dt) {
|
|
return nil
|
|
}
|
|
|
|
tp.Key = dt
|
|
|
|
// token ']'
|
|
if !p.advanceIfPeekTokenIs(token.RBRACK) {
|
|
return nil
|
|
}
|
|
|
|
tp.RBrack = p.curTokenNode()
|
|
|
|
// DataType
|
|
dt = p.parseDataType()
|
|
if isNil(dt) {
|
|
return nil
|
|
}
|
|
|
|
tp.Value = dt
|
|
|
|
return tp
|
|
}
|
|
|
|
func (p *Parser) parseArrayDataType() *ast.ArrayDataType {
|
|
var tp = &ast.ArrayDataType{}
|
|
tp.LBrack = p.curTokenNode()
|
|
|
|
// token INT | ELLIPSIS
|
|
if !p.nextToken() {
|
|
return nil
|
|
}
|
|
|
|
tp.Length = p.curTokenNode()
|
|
|
|
// token ']'
|
|
if !p.advanceIfPeekTokenIs(token.RBRACK) {
|
|
return nil
|
|
}
|
|
|
|
tp.RBrack = p.curTokenNode()
|
|
|
|
// DataType
|
|
dt := p.parseDataType()
|
|
if isNil(dt) {
|
|
return nil
|
|
}
|
|
|
|
tp.DataType = dt
|
|
|
|
return tp
|
|
}
|
|
|
|
func (p *Parser) parseSliceDataType() *ast.SliceDataType {
|
|
var tp = &ast.SliceDataType{}
|
|
tp.LBrack = p.curTokenNode()
|
|
|
|
// token ']'
|
|
if !p.advanceIfPeekTokenIs(token.RBRACK) {
|
|
return nil
|
|
}
|
|
|
|
tp.RBrack = p.curTokenNode()
|
|
|
|
// DataType
|
|
dt := p.parseDataType()
|
|
if isNil(dt) {
|
|
return nil
|
|
}
|
|
|
|
tp.DataType = dt
|
|
|
|
return tp
|
|
}
|
|
|
|
func (p *Parser) parseImportStmt() ast.ImportStmt {
|
|
if p.notExpectPeekToken(token.LPAREN, token.STRING) {
|
|
return nil
|
|
}
|
|
|
|
if p.peekTokenIs(token.LPAREN) {
|
|
return p.parseImportGroupStmt()
|
|
}
|
|
|
|
return p.parseImportLiteralStmt()
|
|
}
|
|
|
|
func (p *Parser) parseImportLiteralStmt() ast.ImportStmt {
|
|
var stmt = &ast.ImportLiteralStmt{}
|
|
stmt.Import = p.curTokenNode()
|
|
|
|
// token STRING
|
|
if !p.advanceIfPeekTokenIs(token.STRING) {
|
|
return nil
|
|
}
|
|
|
|
stmt.Value = p.curTokenNode()
|
|
|
|
return stmt
|
|
}
|
|
|
|
func (p *Parser) parseImportGroupStmt() ast.ImportStmt {
|
|
var stmt = &ast.ImportGroupStmt{}
|
|
stmt.Import = p.curTokenNode()
|
|
|
|
// token '('
|
|
if !p.advanceIfPeekTokenIs(token.LPAREN) { // assert: dead code
|
|
return nil
|
|
}
|
|
|
|
stmt.LParen = p.curTokenNode()
|
|
|
|
// token STRING
|
|
for p.curTokenIsNotEof() && p.peekTokenIsNot(token.RPAREN) {
|
|
if !p.advanceIfPeekTokenIs(token.STRING) {
|
|
return nil
|
|
}
|
|
|
|
stmt.Values = append(stmt.Values, p.curTokenNode())
|
|
|
|
if p.notExpectPeekToken(token.RPAREN, token.STRING) {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// token ')'
|
|
if !p.advanceIfPeekTokenIs(token.RPAREN) {
|
|
return nil
|
|
}
|
|
|
|
stmt.RParen = p.curTokenNode()
|
|
|
|
return stmt
|
|
}
|
|
|
|
func (p *Parser) parseInfoStmt() *ast.InfoStmt {
|
|
var stmt = &ast.InfoStmt{}
|
|
stmt.Info = p.curTokenNode()
|
|
|
|
// token '('
|
|
if !p.advanceIfPeekTokenIs(token.LPAREN) {
|
|
return nil
|
|
}
|
|
|
|
stmt.LParen = p.curTokenNode()
|
|
|
|
for p.curTokenIsNotEof() && p.peekTokenIsNot(token.RPAREN) {
|
|
expr := p.parseKVExpression()
|
|
if expr == nil {
|
|
return nil
|
|
}
|
|
|
|
stmt.Values = append(stmt.Values, expr)
|
|
if p.notExpectPeekToken(token.RPAREN, token.KEY) {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// token ')'
|
|
if !p.advanceIfPeekTokenIs(token.RPAREN) {
|
|
return nil
|
|
}
|
|
|
|
stmt.RParen = p.curTokenNode()
|
|
|
|
return stmt
|
|
}
|
|
|
|
func (p *Parser) parseAtServerKVExpression() *ast.KVExpr {
|
|
var expr = &ast.KVExpr{}
|
|
|
|
// token IDENT
|
|
if !p.advanceIfPeekTokenIs(token.KEY, token.RPAREN) {
|
|
return nil
|
|
}
|
|
|
|
expr.Key = p.curTokenNode()
|
|
|
|
var valueTok token.Token
|
|
var leadingCommentGroup ast.CommentGroup
|
|
if p.notExpectPeekToken(token.QUO, token.DURATION, token.IDENT, token.INT) {
|
|
return nil
|
|
}
|
|
|
|
if p.peekTokenIs(token.QUO) {
|
|
if !p.nextToken() {
|
|
return nil
|
|
}
|
|
|
|
slashTok := p.curTok
|
|
if !p.advanceIfPeekTokenIs(token.IDENT) {
|
|
return nil
|
|
}
|
|
|
|
idTok := p.curTok
|
|
valueTok = token.Token{
|
|
Text: slashTok.Text + idTok.Text,
|
|
Position: slashTok.Position,
|
|
}
|
|
leadingCommentGroup = p.curTokenNode().LeadingCommentGroup
|
|
} else if p.peekTokenIs(token.DURATION) {
|
|
if !p.nextToken() {
|
|
return nil
|
|
}
|
|
|
|
valueTok = p.curTok
|
|
leadingCommentGroup = p.curTokenNode().LeadingCommentGroup
|
|
node := ast.NewTokenNode(valueTok)
|
|
node.SetLeadingCommentGroup(leadingCommentGroup)
|
|
expr.Value = node
|
|
return expr
|
|
} else if p.peekTokenIs(token.INT) {
|
|
if !p.nextToken() {
|
|
return nil
|
|
}
|
|
|
|
valueTok = p.curTok
|
|
leadingCommentGroup = p.curTokenNode().LeadingCommentGroup
|
|
node := ast.NewTokenNode(valueTok)
|
|
node.SetLeadingCommentGroup(leadingCommentGroup)
|
|
expr.Value = node
|
|
return expr
|
|
} else {
|
|
if !p.advanceIfPeekTokenIs(token.IDENT) {
|
|
return nil
|
|
}
|
|
|
|
valueTok = p.curTok
|
|
leadingCommentGroup = p.curTokenNode().LeadingCommentGroup
|
|
if p.peekTokenIs(token.COMMA) {
|
|
for {
|
|
if p.peekTokenIs(token.COMMA) {
|
|
if !p.nextToken() {
|
|
return nil
|
|
}
|
|
|
|
slashTok := p.curTok
|
|
if !p.advanceIfPeekTokenIs(token.IDENT) {
|
|
return nil
|
|
}
|
|
|
|
idTok := p.curTok
|
|
valueTok = token.Token{
|
|
Text: valueTok.Text + slashTok.Text + idTok.Text,
|
|
Position: valueTok.Position,
|
|
}
|
|
leadingCommentGroup = p.curTokenNode().LeadingCommentGroup
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
|
|
valueTok.Type = token.PATH
|
|
node := ast.NewTokenNode(valueTok)
|
|
node.SetLeadingCommentGroup(leadingCommentGroup)
|
|
expr.Value = node
|
|
return expr
|
|
}
|
|
}
|
|
|
|
for {
|
|
if p.peekTokenIs(token.QUO) {
|
|
if !p.nextToken() {
|
|
return nil
|
|
}
|
|
|
|
slashTok := p.curTok
|
|
if !p.advanceIfPeekTokenIs(token.IDENT) {
|
|
return nil
|
|
}
|
|
|
|
idTok := p.curTok
|
|
valueTok = token.Token{
|
|
Text: valueTok.Text + slashTok.Text + idTok.Text,
|
|
Position: valueTok.Position,
|
|
}
|
|
leadingCommentGroup = p.curTokenNode().LeadingCommentGroup
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
|
|
valueTok.Type = token.PATH
|
|
node := ast.NewTokenNode(valueTok)
|
|
node.SetLeadingCommentGroup(leadingCommentGroup)
|
|
expr.Value = node
|
|
|
|
return expr
|
|
}
|
|
|
|
func (p *Parser) parseKVExpression() *ast.KVExpr {
|
|
var expr = &ast.KVExpr{}
|
|
|
|
// token IDENT
|
|
if !p.advanceIfPeekTokenIs(token.KEY) {
|
|
return nil
|
|
}
|
|
|
|
expr.Key = p.curTokenNode()
|
|
|
|
// token STRING
|
|
if !p.advanceIfPeekTokenIs(token.STRING) {
|
|
return nil
|
|
}
|
|
|
|
expr.Value = p.curTokenNode()
|
|
|
|
return expr
|
|
}
|
|
|
|
// syntax = "v1"
|
|
func (p *Parser) parseSyntaxStmt() *ast.SyntaxStmt {
|
|
var stmt = &ast.SyntaxStmt{}
|
|
stmt.Syntax = p.curTokenNode()
|
|
|
|
// token '='
|
|
if !p.advanceIfPeekTokenIs(token.ASSIGN) {
|
|
return nil
|
|
}
|
|
|
|
stmt.Assign = p.curTokenNode()
|
|
|
|
// token STRING
|
|
if !p.advanceIfPeekTokenIs(token.STRING) {
|
|
return nil
|
|
}
|
|
|
|
stmt.Value = p.curTokenNode()
|
|
|
|
return stmt
|
|
}
|
|
|
|
func (p *Parser) curTokenIsNotEof() bool {
|
|
return p.curTokenIsNot(token.EOF)
|
|
}
|
|
|
|
func (p *Parser) curTokenIsNot(expected token.Type) bool {
|
|
return p.curTok.Type != expected
|
|
}
|
|
|
|
func (p *Parser) curTokenIsKeyword() bool {
|
|
tp, ok := token.LookupKeyword(p.curTok.Text)
|
|
if ok {
|
|
p.curTokenIs()
|
|
p.expectIdentError(p.curTok.Fork(tp), token.IDENT)
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func (p *Parser) curTokenIs(expected ...interface{}) bool {
|
|
for _, v := range expected {
|
|
switch val := v.(type) {
|
|
case token.Type:
|
|
if p.curTok.Type == val {
|
|
return true
|
|
}
|
|
case string:
|
|
if p.curTok.Text == val {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func (p *Parser) advanceIfPeekTokenIs(expected ...interface{}) bool {
|
|
if p.expectPeekToken(expected...) {
|
|
if !p.nextToken() {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func (p *Parser) peekTokenIs(expected ...interface{}) bool {
|
|
for _, v := range expected {
|
|
switch val := v.(type) {
|
|
case token.Type:
|
|
if p.peekTok.Type == val {
|
|
return true
|
|
}
|
|
case string:
|
|
if p.peekTok.Text == val {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func (p *Parser) peekTokenIsNot(expected ...interface{}) bool {
|
|
for _, v := range expected {
|
|
switch val := v.(type) {
|
|
case token.Type:
|
|
if p.peekTok.Type == val {
|
|
return false
|
|
}
|
|
case string:
|
|
if p.peekTok.Text == val {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func (p *Parser) notExpectPeekToken(expected ...interface{}) bool {
|
|
if !p.peekTokenIsNot(expected...) {
|
|
return false
|
|
}
|
|
|
|
var expectedString []string
|
|
for _, v := range expected {
|
|
expectedString = append(expectedString, fmt.Sprintf("'%s'", v))
|
|
}
|
|
|
|
var got string
|
|
if p.peekTok.Type == token.ILLEGAL {
|
|
got = p.peekTok.Text
|
|
} else {
|
|
got = p.peekTok.Type.String()
|
|
}
|
|
|
|
var err error
|
|
if p.peekTok.Type == token.EOF {
|
|
position := p.curTok.Position
|
|
position.Column = position.Column + len(p.curTok.Text)
|
|
err = fmt.Errorf(
|
|
"%s syntax error: expected %s, got '%s'",
|
|
position,
|
|
strings.Join(expectedString, " | "),
|
|
got)
|
|
} else {
|
|
err = fmt.Errorf(
|
|
"%s syntax error: expected %s, got '%s'",
|
|
p.peekTok.Position,
|
|
strings.Join(expectedString, " | "),
|
|
got)
|
|
}
|
|
p.errors = append(p.errors, err)
|
|
|
|
return true
|
|
}
|
|
|
|
func (p *Parser) notExpectPeekTokenGotComment(actual *ast.CommentStmt, expected ...interface{}) bool {
|
|
if actual == nil {
|
|
return false
|
|
}
|
|
|
|
var expectedString []string
|
|
for _, v := range expected {
|
|
switch val := v.(type) {
|
|
case token.Token:
|
|
expectedString = append(expectedString, fmt.Sprintf("'%s'", val.Text))
|
|
default:
|
|
expectedString = append(expectedString, fmt.Sprintf("'%s'", v))
|
|
}
|
|
}
|
|
|
|
got := actual.Comment.Type.String()
|
|
p.errors = append(p.errors, fmt.Errorf(
|
|
"%s syntax error: expected %s, got '%s'",
|
|
p.peekTok.Position,
|
|
strings.Join(expectedString, " | "),
|
|
got))
|
|
|
|
return true
|
|
}
|
|
|
|
func (p *Parser) expectPeekToken(expected ...interface{}) bool {
|
|
if p.peekTokenIs(expected...) {
|
|
return true
|
|
}
|
|
|
|
var expectedString []string
|
|
for _, v := range expected {
|
|
expectedString = append(expectedString, fmt.Sprintf("'%s'", v))
|
|
}
|
|
|
|
var got string
|
|
if p.peekTok.Type == token.ILLEGAL {
|
|
got = p.peekTok.Text
|
|
} else {
|
|
got = p.peekTok.Type.String()
|
|
}
|
|
|
|
var err error
|
|
if p.peekTok.Type == token.EOF {
|
|
position := p.curTok.Position
|
|
position.Column = position.Column + len(p.curTok.Text)
|
|
err = fmt.Errorf(
|
|
"%s syntax error: expected %s, got '%s'",
|
|
position,
|
|
strings.Join(expectedString, " | "),
|
|
got)
|
|
} else {
|
|
err = fmt.Errorf(
|
|
"%s syntax error: expected %s, got '%s'",
|
|
p.peekTok.Position,
|
|
strings.Join(expectedString, " | "),
|
|
got)
|
|
}
|
|
p.errors = append(p.errors, err)
|
|
|
|
return false
|
|
}
|
|
|
|
func (p *Parser) expectIdentError(tok token.Token, expected ...interface{}) {
|
|
var expectedString []string
|
|
for _, v := range expected {
|
|
expectedString = append(expectedString, fmt.Sprintf("'%s'", v))
|
|
}
|
|
|
|
p.errors = append(p.errors, fmt.Errorf(
|
|
"%s syntax error: expected %s, got '%s'",
|
|
tok.Position,
|
|
strings.Join(expectedString, " | "),
|
|
tok.Type.String()))
|
|
}
|
|
|
|
func (p *Parser) init() bool {
|
|
if !p.nextToken() {
|
|
return false
|
|
}
|
|
|
|
return p.nextToken()
|
|
}
|
|
|
|
func (p *Parser) nextToken() bool {
|
|
var err error
|
|
p.curTok = p.peekTok
|
|
var line = -1
|
|
if p.curTok.Valid() {
|
|
if p.curTokenIs(token.EOF) {
|
|
for _, v := range p.headCommentGroup {
|
|
p.appendStmt(v)
|
|
}
|
|
|
|
p.headCommentGroup = ast.CommentGroup{}
|
|
return true
|
|
}
|
|
|
|
node := ast.NewTokenNode(p.curTok)
|
|
if p.headCommentGroup.Valid() {
|
|
node.HeadCommentGroup = append(node.HeadCommentGroup, p.headCommentGroup...)
|
|
p.headCommentGroup = ast.CommentGroup{}
|
|
}
|
|
|
|
p.node[p.curTok] = node
|
|
line = p.curTok.Line()
|
|
}
|
|
|
|
p.peekTok, err = p.s.NextToken()
|
|
if err != nil {
|
|
p.errors = append(p.errors, err)
|
|
return false
|
|
}
|
|
|
|
var leadingCommentGroup ast.CommentGroup
|
|
for p.peekTok.Type == token.COMMENT || p.peekTok.Type == token.DOCUMENT {
|
|
commentStmt := &ast.CommentStmt{Comment: p.peekTok}
|
|
if p.peekTok.Line() == line && line > -1 {
|
|
leadingCommentGroup = append(leadingCommentGroup, commentStmt)
|
|
} else {
|
|
p.headCommentGroup = append(p.headCommentGroup, commentStmt)
|
|
}
|
|
|
|
p.peekTok, err = p.s.NextToken()
|
|
if err != nil {
|
|
p.errors = append(p.errors, err)
|
|
return false
|
|
}
|
|
}
|
|
|
|
if len(leadingCommentGroup) > 0 {
|
|
p.curTokenNode().SetLeadingCommentGroup(leadingCommentGroup)
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func (p *Parser) curTokenNode() *ast.TokenNode {
|
|
return p.getNode(p.curTok)
|
|
}
|
|
|
|
func (p *Parser) getNode(tok token.Token) *ast.TokenNode {
|
|
return p.node[tok]
|
|
}
|
|
|
|
func isNil(v interface{}) bool {
|
|
if v == nil {
|
|
return true
|
|
}
|
|
|
|
vo := reflect.ValueOf(v)
|
|
if vo.Kind() == reflect.Ptr {
|
|
return vo.IsNil()
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// CheckErrors check parser errors.
|
|
func (p *Parser) CheckErrors() error {
|
|
if len(p.errors) == 0 {
|
|
return nil
|
|
}
|
|
|
|
var errors []string
|
|
for _, e := range p.errors {
|
|
errors = append(errors, e.Error())
|
|
}
|
|
|
|
return fmt.Errorf(strings.Join(errors, "\n"))
|
|
}
|
|
|
|
func (p *Parser) appendStmt(stmt ...ast.Stmt) {
|
|
p.api.Stmts = append(p.api.Stmts, stmt...)
|
|
}
|
|
|
|
func (p *Parser) hasNoErrors() bool {
|
|
return len(p.errors) == 0
|
|
}
|
|
|
|
/************************EXPERIMENTAL CODE BG************************/
|
|
// The following code block are experimental, do not call it out of unit test.
|
|
|
|
// ParseForUintTest parse the source code for unit test.
|
|
func (p *Parser) ParseForUintTest() *ast.AST {
|
|
api := &ast.AST{}
|
|
if !p.init() {
|
|
return nil
|
|
}
|
|
|
|
for p.curTokenIsNotEof() {
|
|
stmt := p.parseStmtForUniTest()
|
|
if isNil(stmt) {
|
|
return nil
|
|
}
|
|
|
|
api.Stmts = append(api.Stmts, stmt)
|
|
if !p.nextToken() {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return api
|
|
}
|
|
|
|
func (p *Parser) parseStmtForUniTest() ast.Stmt {
|
|
switch p.curTok.Type {
|
|
case token.AT_SERVER:
|
|
return p.parseAtServerStmt()
|
|
case token.AT_HANDLER:
|
|
return p.parseAtHandlerStmt()
|
|
case token.AT_DOC:
|
|
return p.parseAtDocStmt()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
/************************EXPERIMENTAL CODE END************************/
|