mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2024-12-15 09:18:59 +08:00
209 lines
5.4 KiB
Go
209 lines
5.4 KiB
Go
package parser
|
|
|
|
import (
|
|
"bufio"
|
|
"errors"
|
|
"fmt"
|
|
components "github.com/1Panel-dev/1Panel/backend/utils/nginx/components"
|
|
"github.com/1Panel-dev/1Panel/backend/utils/nginx/parser/flag"
|
|
"os"
|
|
"strings"
|
|
)
|
|
|
|
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
|
|
}
|
|
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):
|
|
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)
|
|
}
|