mirror of https://github.com/1Panel-dev/1Panel
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
173 lines
4.3 KiB
173 lines
4.3 KiB
2 years ago
|
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)
|
||
|
}
|