Files
streamsql/rsql/parser.go
rulego-team 443dd9de28 重构
2025-03-04 19:02:25 +08:00

159 lines
3.1 KiB
Go

package rsql
import (
"errors"
"fmt"
"strconv"
"strings"
)
type Parser struct {
lexer *Lexer
}
func NewParser(input string) *Parser {
return &Parser{
lexer: NewLexer(input),
}
}
func (p *Parser) Parse() (*SelectStatement, error) {
stmt := &SelectStatement{}
// 解析SELECT子句
if err := p.parseSelect(stmt); err != nil {
return nil, err
}
// 解析FROM子句
if err := p.parseFrom(stmt); err != nil {
return nil, err
}
// 解析WHERE子句
if err := p.parseWhere(stmt); err != nil {
return nil, err
}
// 解析GROUP BY子句
if err := p.parseGroupBy(stmt); err != nil {
return nil, err
}
return stmt, nil
}
func (p *Parser) parseSelect(stmt *SelectStatement) error {
p.lexer.NextToken() // 跳过SELECT
for {
tok := p.lexer.NextToken()
if tok.Type == TokenFROM {
break
}
field := Field{Expression: tok.Value}
if p.lexer.peekChar() == ' ' {
if aliasTok := p.lexer.NextToken(); aliasTok.Type == TokenAS {
field.Alias = p.lexer.NextToken().Value
}
}
stmt.Fields = append(stmt.Fields, field)
if p.lexer.NextToken().Type != TokenComma {
break
}
}
return nil
}
func (p *Parser) parseFrom(stmt *SelectStatement) error {
tok := p.lexer.NextToken()
if tok.Type != TokenIdent {
return errors.New("expected source identifier after FROM")
}
stmt.Source = tok.Value
return nil
}
func (p *Parser) parseWhere(stmt *SelectStatement) error {
var conditions []string
p.lexer.NextToken() // 跳过WHERE
for {
tok := p.lexer.NextToken()
switch tok.Type {
case TokenIdent, TokenNumber, TokenString:
conditions = append(conditions, tok.Value)
case TokenEQ:
conditions = append(conditions, "==")
case TokenAND:
conditions = append(conditions, "&&")
case TokenOR:
conditions = append(conditions, "||")
default:
stmt.Condition = strings.Join(conditions, " ")
return nil
}
}
}
func (p *Parser) parseGroupBy(stmt *SelectStatement) error {
p.lexer.NextToken() // 跳过GROUP
p.lexer.NextToken() // 跳过BY
for {
tok := p.lexer.NextToken()
if tok.Type == TokenTumbling || tok.Type == TokenSliding {
return p.parseWindowFunction(stmt, tok.Value)
}
stmt.GroupBy = append(stmt.GroupBy, tok.Value)
if p.lexer.NextToken().Type != TokenComma {
break
}
}
return nil
}
func (p *Parser) parseWindowFunction(stmt *SelectStatement, winType string) error {
p.lexer.NextToken() // 跳过函数名
params := make(map[string]interface{})
for p.lexer.peekChar() != ')' {
keyTok := p.lexer.NextToken()
if keyTok.Type != TokenIdent {
return fmt.Errorf("expected parameter key, got %v", keyTok)
}
valTok := p.lexer.NextToken()
params[keyTok.Value] = convertValue(valTok.Value)
}
stmt.Window = WindowDefinition{
Type: winType,
Params: params,
}
return nil
}
func convertValue(s string) interface{} {
if s == "true" {
return true
}
if s == "false" {
return false
}
if i, err := strconv.Atoi(s); err == nil {
return i
}
if f, err := strconv.ParseFloat(s, 64); err == nil {
return f
}
if strings.HasPrefix(s, "'") && strings.HasSuffix(s, "'") {
return strings.Trim(s, "'")
}
return s
}