mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2024-12-15 09:18:59 +08:00
262 lines
4.8 KiB
Go
262 lines
4.8 KiB
Go
package parser
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"github.com/1Panel-dev/1Panel/backend/utils/nginx/parser/flag"
|
|
"io"
|
|
"strings"
|
|
)
|
|
|
|
type lexer struct {
|
|
reader *bufio.Reader
|
|
file string
|
|
line int
|
|
column int
|
|
inLuaBlock bool
|
|
Latest flag.Flag
|
|
}
|
|
|
|
func lex(content string) *lexer {
|
|
return newLexer(bytes.NewBuffer([]byte(content)))
|
|
}
|
|
|
|
func newLexer(r io.Reader) *lexer {
|
|
return &lexer{
|
|
line: 1,
|
|
reader: bufio.NewReader(r),
|
|
}
|
|
}
|
|
|
|
func (s *lexer) scan() flag.Flag {
|
|
s.Latest = s.getNextFlag()
|
|
return s.Latest
|
|
}
|
|
|
|
//func (s *lexer) all() flag.Flags {
|
|
// tokens := make([]flag.Flag, 0)
|
|
// for {
|
|
// v := s.scan()
|
|
// if v.Type == flag.EOF || v.Type == -1 {
|
|
// break
|
|
// }
|
|
// tokens = append(tokens, v)
|
|
// }
|
|
// return tokens
|
|
//}
|
|
|
|
func (s *lexer) getNextFlag() flag.Flag {
|
|
if s.inLuaBlock {
|
|
s.inLuaBlock = false
|
|
flag := s.scanLuaCode()
|
|
return flag
|
|
}
|
|
retoFlag:
|
|
ch := s.peek()
|
|
switch {
|
|
case isSpace(ch):
|
|
s.skipWhitespace()
|
|
goto retoFlag
|
|
case isEOF(ch):
|
|
return s.NewToken(flag.EOF).Lit(string(s.read()))
|
|
case ch == ';':
|
|
return s.NewToken(flag.Semicolon).Lit(string(s.read()))
|
|
case ch == '{':
|
|
if isLuaBlock(s.Latest) {
|
|
s.inLuaBlock = true
|
|
}
|
|
return s.NewToken(flag.BlockStart).Lit(string(s.read()))
|
|
case ch == '}':
|
|
return s.NewToken(flag.BlockEnd).Lit(string(s.read()))
|
|
case ch == '#':
|
|
return s.scanComment()
|
|
case ch == '$':
|
|
return s.scanVariable()
|
|
case isQuote(ch):
|
|
return s.scanQuotedString(ch)
|
|
default:
|
|
return s.scanKeyword()
|
|
}
|
|
}
|
|
|
|
func (s *lexer) scanLuaCode() flag.Flag {
|
|
ret := s.NewToken(flag.LuaCode)
|
|
stack := make([]rune, 0, 50)
|
|
code := strings.Builder{}
|
|
|
|
for {
|
|
ch := s.read()
|
|
if ch == rune(flag.EOF) {
|
|
panic("unexpected end of file while scanning a string, maybe an unclosed lua code?")
|
|
}
|
|
if ch == '#' {
|
|
code.WriteRune(ch)
|
|
code.WriteString(s.readUntil(isEndOfLine))
|
|
continue
|
|
} else if ch == '}' {
|
|
if len(stack) == 0 {
|
|
_ = s.reader.UnreadRune()
|
|
return ret.Lit(strings.TrimRight(strings.Trim(code.String(), "\n"), "\n "))
|
|
}
|
|
if stack[len(stack)-1] == '{' {
|
|
stack = stack[0 : len(stack)-1]
|
|
}
|
|
} else if ch == '{' {
|
|
stack = append(stack, ch)
|
|
}
|
|
code.WriteRune(ch)
|
|
}
|
|
}
|
|
|
|
func (s *lexer) peek() rune {
|
|
r, _, _ := s.reader.ReadRune()
|
|
_ = s.reader.UnreadRune()
|
|
return r
|
|
}
|
|
|
|
type runeCheck func(rune) bool
|
|
|
|
func (s *lexer) readUntil(until runeCheck) string {
|
|
var buf bytes.Buffer
|
|
buf.WriteRune(s.read())
|
|
|
|
for {
|
|
if ch := s.peek(); isEOF(ch) {
|
|
break
|
|
} else if until(ch) {
|
|
break
|
|
} else {
|
|
buf.WriteRune(s.read())
|
|
}
|
|
}
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
func (s *lexer) NewToken(tokenType flag.Type) flag.Flag {
|
|
return flag.Flag{
|
|
Type: tokenType,
|
|
Line: s.line,
|
|
Column: s.column,
|
|
}
|
|
}
|
|
|
|
func (s *lexer) readWhile(while runeCheck) string {
|
|
var buf bytes.Buffer
|
|
buf.WriteRune(s.read())
|
|
|
|
for {
|
|
if ch := s.peek(); while(ch) {
|
|
buf.WriteRune(s.read())
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
return buf.String()
|
|
}
|
|
|
|
func (s *lexer) skipWhitespace() {
|
|
s.readWhile(isSpace)
|
|
}
|
|
|
|
func (s *lexer) scanComment() flag.Flag {
|
|
return s.NewToken(flag.Comment).Lit(s.readUntil(isEndOfLine))
|
|
}
|
|
|
|
func (s *lexer) scanQuotedString(delimiter rune) flag.Flag {
|
|
var buf bytes.Buffer
|
|
tok := s.NewToken(flag.QuotedString)
|
|
_, _ = buf.WriteRune(s.read())
|
|
for {
|
|
ch := s.read()
|
|
|
|
if ch == rune(flag.EOF) {
|
|
panic("unexpected end of file while scanning a string, maybe an unclosed quote?")
|
|
}
|
|
|
|
if ch == '\\' && (s.peek() == delimiter) {
|
|
buf.WriteRune(ch)
|
|
buf.WriteRune(s.read())
|
|
continue
|
|
}
|
|
|
|
_, _ = buf.WriteRune(ch)
|
|
if ch == delimiter {
|
|
break
|
|
}
|
|
}
|
|
|
|
return tok.Lit(buf.String())
|
|
}
|
|
|
|
func (s *lexer) scanKeyword() flag.Flag {
|
|
var buf bytes.Buffer
|
|
tok := s.NewToken(flag.Keyword)
|
|
prev := s.read()
|
|
buf.WriteRune(prev)
|
|
for {
|
|
ch := s.peek()
|
|
|
|
if isSpace(ch) || isEOF(ch) || ch == ';' {
|
|
break
|
|
}
|
|
|
|
if ch == '{' {
|
|
if prev == '$' {
|
|
buf.WriteString(s.readUntil(func(r rune) bool {
|
|
return r == '}'
|
|
}))
|
|
buf.WriteRune(s.read()) //consume latest '}'
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
buf.WriteRune(s.read())
|
|
}
|
|
|
|
return tok.Lit(buf.String())
|
|
}
|
|
|
|
func (s *lexer) scanVariable() flag.Flag {
|
|
return s.NewToken(flag.Variable).Lit(s.readUntil(isKeywordTerminator))
|
|
}
|
|
|
|
func (s *lexer) read() rune {
|
|
ch, _, err := s.reader.ReadRune()
|
|
if err != nil {
|
|
return rune(flag.EOF)
|
|
}
|
|
|
|
if ch == '\n' {
|
|
s.column = 1
|
|
s.line++
|
|
} else {
|
|
s.column++
|
|
}
|
|
return ch
|
|
}
|
|
|
|
func isQuote(ch rune) bool {
|
|
return ch == '"' || ch == '\'' || ch == '`'
|
|
}
|
|
|
|
func isKeywordTerminator(ch rune) bool {
|
|
return isSpace(ch) || isEndOfLine(ch) || ch == '{' || ch == ';'
|
|
}
|
|
|
|
func isSpace(ch rune) bool {
|
|
return ch == ' ' || ch == '\t' || isEndOfLine(ch)
|
|
}
|
|
|
|
func isEOF(ch rune) bool {
|
|
return ch == rune(flag.EOF)
|
|
}
|
|
|
|
func isEndOfLine(ch rune) bool {
|
|
return ch == '\r' || ch == '\n'
|
|
}
|
|
|
|
func isLuaBlock(t flag.Flag) bool {
|
|
return t.Type == flag.Keyword && strings.HasSuffix(t.Literal, "_by_lua_block")
|
|
}
|