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), Line: p.currentToken.Line, } 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, Line: p.currentToken.Line, }) } p.nextToken() } return context } func (p *Parser) parseStatement() components.IDirective { 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) } 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) }