1Panel/agent/utils/nginx/parser/parser.go
2024-07-23 14:48:37 +08:00

211 lines
5.4 KiB
Go

package parser
import (
"bufio"
"errors"
"fmt"
"os"
"strings"
components "github.com/1Panel-dev/1Panel/agent/utils/nginx/components"
"github.com/1Panel-dev/1Panel/agent/utils/nginx/parser/flag"
)
type Parser struct {
lexer *lexer
currentToken flag.Flag
followingToken flag.Flag
blockWrappers map[string]func(*components.Directive) (components.IDirective, error)
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
}
defer f.Close()
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, error){
"http": func(directive *components.Directive) (components.IDirective, error) {
return parser.wrapHttp(directive), nil
},
"server": func(directive *components.Directive) (components.IDirective, error) {
return parser.wrapServer(directive), nil
},
"location": func(directive *components.Directive) (components.IDirective, error) {
return parser.wrapLocation(directive), nil
},
"upstream": func(directive *components.Directive) (components.IDirective, error) {
return parser.wrapUpstream(directive), nil
},
"_by_lua_block": func(directive *components.Directive) (components.IDirective, error) {
return parser.wrapLuaBlock(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, error) {
parsedBlock, err := p.parseBlock(false)
if err != nil {
return nil, err
}
c := &components.Config{
FilePath: p.lexer.file,
Block: parsedBlock,
}
return c, err
}
func (p *Parser) parseBlock(inBlock bool) (*components.Block, error) {
context := &components.Block{
Comment: "",
Directives: make([]components.IDirective, 0),
Line: p.currentToken.Line,
}
parsingloop:
for {
switch {
case p.curTokenIs(flag.EOF):
if inBlock {
return nil, errors.New("unexpected eof in block")
}
break parsingloop
case p.curTokenIs(flag.BlockEnd):
break parsingloop
case p.curTokenIs(flag.LuaCode):
context.IsLuaBlock = true
context.LiteralCode = p.currentToken.Literal
case p.curTokenIs(flag.Keyword) || p.curTokenIs(flag.QuotedString):
s, err := p.parseStatement()
if err != nil {
return nil, err
}
context.Directives = append(context.Directives, s)
case p.curTokenIs(flag.Comment):
context.Directives = append(context.Directives, &components.Comment{
Detail: p.currentToken.Literal,
Line: p.currentToken.Line,
})
}
p.nextToken()
}
return context, nil
}
func (p *Parser) parseStatement() (components.IDirective, error) {
d := &components.Directive{
Name: p.currentToken.Literal,
Line: p.currentToken.Line,
}
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), nil
}
if p.followingTokenIs(flag.Comment) && p.currentToken.Line == p.followingToken.Line {
d.Comment = p.followingToken.Literal
p.nextToken()
}
return d, nil
}
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, err := p.parseBlock(false)
if err != nil {
return nil, err
}
block.Comment = inLineComment
d.Block = block
if strings.HasSuffix(d.Name, "_by_lua_block") {
return p.blockWrappers["_by_lua_block"](d)
}
if bw, ok := p.blockWrappers[d.Name]; ok {
return bw(d)
}
return d, nil
}
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) wrapLuaBlock(directive *components.Directive) (*components.LuaBlock, error) {
return components.NewLuaBlock(directive)
}
func (p *Parser) parseUpstreamServer(directive *components.Directive) *components.UpstreamServer {
return components.NewUpstreamServer(directive)
}