1Panel/backend/utils/nginx/parser/parser.go
2022-11-17 21:20:33 +08:00

173 lines
4.3 KiB
Go

package parser
import (
"bufio"
"fmt"
components "github.com/1Panel-dev/1Panel/backend/utils/nginx/components"
"github.com/1Panel-dev/1Panel/backend/utils/nginx/parser/flag"
"os"
)
type Parser struct {
lexer *lexer
currentToken flag.Flag
followingToken flag.Flag
blockWrappers map[string]func(*components.Directive) components.IDirective
directiveWrappers map[string]func(*components.Directive) components.IDirective
}
func NewStringParser(str string) *Parser {
return NewParserFromLexer(lex(str))
}
func NewParser(filePath string) (*Parser, error) {
f, err := os.Open(filePath)
if err != nil {
return nil, err
}
l := newLexer(bufio.NewReader(f))
l.file = filePath
p := NewParserFromLexer(l)
return p, nil
}
func NewParserFromLexer(lexer *lexer) *Parser {
parser := &Parser{
lexer: lexer,
}
parser.nextToken()
parser.nextToken()
parser.blockWrappers = map[string]func(*components.Directive) components.IDirective{
"http": func(directive *components.Directive) components.IDirective {
return parser.wrapHttp(directive)
},
"server": func(directive *components.Directive) components.IDirective {
return parser.wrapServer(directive)
},
"location": func(directive *components.Directive) components.IDirective {
return parser.wrapLocation(directive)
},
"upstream": func(directive *components.Directive) components.IDirective {
return parser.wrapUpstream(directive)
},
}
parser.directiveWrappers = map[string]func(*components.Directive) components.IDirective{
"server": func(directive *components.Directive) components.IDirective {
return parser.parseUpstreamServer(directive)
},
}
return parser
}
func (p *Parser) nextToken() {
p.currentToken = p.followingToken
p.followingToken = p.lexer.scan()
}
func (p *Parser) curTokenIs(t flag.Type) bool {
return p.currentToken.Type == t
}
func (p *Parser) followingTokenIs(t flag.Type) bool {
return p.followingToken.Type == t
}
func (p *Parser) Parse() *components.Config {
return &components.Config{
FilePath: p.lexer.file,
Block: p.parseBlock(),
}
}
func (p *Parser) parseBlock() *components.Block {
context := &components.Block{
Comment: "",
Directives: make([]components.IDirective, 0),
}
parsingloop:
for {
switch {
case p.curTokenIs(flag.EOF) || p.curTokenIs(flag.BlockEnd):
break parsingloop
case p.curTokenIs(flag.Keyword):
context.Directives = append(context.Directives, p.parseStatement())
case p.curTokenIs(flag.Comment):
context.Directives = append(context.Directives, &components.Comment{
Detail: p.currentToken.Literal,
})
}
p.nextToken()
}
return context
}
func (p *Parser) parseStatement() components.IDirective {
d := &components.Directive{
Name: p.currentToken.Literal,
}
for p.nextToken(); p.currentToken.IsParameterEligible(); p.nextToken() {
d.Parameters = append(d.Parameters, p.currentToken.Literal)
}
if p.curTokenIs(flag.Semicolon) {
if dw, ok := p.directiveWrappers[d.Name]; ok {
return dw(d)
}
if p.followingTokenIs(flag.Comment) && p.currentToken.Line == p.followingToken.Line {
d.Comment = p.followingToken.Literal
p.nextToken()
}
return d
}
if p.curTokenIs(flag.BlockStart) {
inLineComment := ""
if p.followingTokenIs(flag.Comment) && p.currentToken.Line == p.followingToken.Line {
inLineComment = p.followingToken.Literal
p.nextToken()
p.nextToken()
}
block := p.parseBlock()
block.Comment = inLineComment
d.Block = block
if bw, ok := p.blockWrappers[d.Name]; ok {
return bw(d)
}
return d
}
panic(fmt.Errorf("unexpected token %s (%s) on line %d, column %d", p.currentToken.Type.String(), p.currentToken.Literal, p.currentToken.Line, p.currentToken.Column))
}
func (p *Parser) wrapLocation(directive *components.Directive) *components.Location {
return components.NewLocation(directive)
}
func (p *Parser) wrapServer(directive *components.Directive) *components.Server {
s, _ := components.NewServer(directive)
return s
}
func (p *Parser) wrapUpstream(directive *components.Directive) *components.Upstream {
s, _ := components.NewUpstream(directive)
return s
}
func (p *Parser) wrapHttp(directive *components.Directive) *components.Http {
h, _ := components.NewHttp(directive)
return h
}
func (p *Parser) parseUpstreamServer(directive *components.Directive) *components.UpstreamServer {
return components.NewUpstreamServer(directive)
}