package engine import ( "fmt" "io" "strings" "strconv" stmtd "github.com/tgckpg/mmqlengine/mmql/statements" ) type LexerExpect struct { Statements bool Keywords bool Brackets bool Quotes bool Key string } type Lexer struct { SupportedStmts *map[string] func( *Lexer ) ( stmtd.IStatement, error ) reader *strings.Reader readToken bool } func ( this *Lexer ) Parse( line string ) ( stmtd.IStatement, error ) { this.reader = strings.NewReader( line ) ex := LexerExpect{ Statements: true, Keywords: false, Brackets: true, Quotes: true, } return this.ReadStatement( &ex ) } func ( this *Lexer ) SetReader( reader *strings.Reader ) { this.reader = reader } func ( this *Lexer ) GetPos() int { return this.reader.Len() } /** * Reads * 123,456,789.00001 * 1234.567 * 1234 * 10.5k */ func ( this *Lexer ) ReadDecimal() ( f float64, err error ) { reader := this.reader defer this.Rollback( reader.Len(), &err ) var b strings.Builder dot := false comma := false k := false end := false for { r, _, _err := reader.ReadRune() err = _err if err != nil { if err == io.EOF { if comma { err = fmt.Errorf( "Unexpected ',' char" ) return } if b.Len() == 0 { err = fmt.Errorf( "Nothing left to read" ) return } } break } if end && !strings.ContainsRune( K_SPACES, r ) { if k { b.WriteRune( 'k' ) } err = fmt.Errorf( "Cannot parse '%s' in ReadDecimal", this.ReadUntilNot( K_STATEMENT_CHARS, true, r ), ) return } if r == ',' { if comma || dot { err = fmt.Errorf( "Unexpected ',' char in ReadDecimal" ) return } comma = true continue } if strings.ContainsRune( K_SPACES, r ) { if comma { err = fmt.Errorf( "Unexpected ',' char in ReadDecimal" ) return } if 0 < b.Len() { break } continue } if r == '.' { if !dot { b.WriteRune( r ) dot = true continue } err = fmt.Errorf( "Unexpected '.' char in ReadDecimal" ) return } if r == 'k' { k = true end = true continue } if '0' <= r && r <= '9' { b.WriteRune( r ) comma = false } else { err = fmt.Errorf( "Cannot parse '%s' in ReadDecimal", this.ReadUntilNot( K_STATEMENT_CHARS, true, r ), ) return } } f, err = strconv.ParseFloat( b.String(), 64 ) if k { f *= 1000 } return } func ( this *Lexer ) Rollback( oPos int, err *error ) { if *err != nil { this.reader.Seek( int64( this.reader.Len() - oPos ), io.SeekCurrent ) } } func ( this *Lexer ) ReadKeyword( Keyword string ) ( err error ) { reader := this.reader kReader := strings.NewReader( Keyword ) defer this.Rollback( reader.Len(), &err ) var b strings.Builder for { r, _, _err := reader.ReadRune() err = _err if err != nil { if err == io.EOF { if 0 < b.Len() { err = nil } else { err = fmt.Errorf( "Nothing left to read" ) return } } break } if strings.ContainsRune( K_SPACES, r ) && b.Len() == 0 { continue } k, _, _err := kReader.ReadRune() if _err != nil { if _err != io.EOF { err = _err } else { err = nil } return } if k != r { err = fmt.Errorf( "Expected \"%s\", got \"%s\"", Keyword, this.ReadUntilNot( K_STATEMENT_CHARS, true, r ), ) return } b.WriteRune( r ) } return } func ( this *Lexer ) ReadAlpha() ( s string, err error ) { reader := this.reader defer this.Rollback( reader.Len(), &err ) var b strings.Builder for { r, _, _err := reader.ReadRune() err = _err if err != nil { if err == io.EOF { if 0 < b.Len() { err = nil } else { err = fmt.Errorf( "Nothing left to read" ) return } } break } if strings.ContainsRune( K_SPACES, r ) { if 0 < b.Len() { break } continue } if ( 'A' <= r && r <= 'Z' ) || ( 'a' <= r && r <= 'z' ) { b.WriteRune( r ) } } s = b.String() return } func ( this *Lexer ) ReadStatement( expecting *LexerExpect ) ( stmtd.IStatement, error ) { reader := this.reader var stmt stmtd.IStatement for { r, _, err := reader.ReadRune() if err != nil { return nil, err } if expecting.Quotes && strings.ContainsRune( K_QUOTES, r ) { s, err := this.ReadUntilClose( r, true, '\\' ) if err != nil { return nil, err } stmt = stmtd.QuotedValue{ RawStatement: &stmtd.RawStatement{ Value: s }, Quote: string( r ), } return stmt, nil } bracketIndex := strings.IndexRune( K_BRACKETS, r ) if expecting.Brackets && bracketIndex % 2 == 0 { cl := rune( K_BRACKETS[ bracketIndex + 1 ] ) s, err := this.ReadUntilClose( cl, false, 'A' ) if err != nil { return nil, err } var b strings.Builder b.WriteRune( r ) b.WriteRune( cl ) stmt = stmtd.BracketedValue{ RawStatement: &stmtd.RawStatement{ Value: s }, Brackets: b.String(), } return stmt, nil } s := expecting.Key if s == "" && strings.ContainsRune( K_STATEMENT_CHARS, r ) { s = this.ReadUntilNot( K_STATEMENT_CHARS, true, r ) } if expecting.Statements && s != "" { if this.SupportedStmts == nil { return nil, fmt.Errorf( "Statement not supported: %s", s ) } if f, ok := (*this.SupportedStmts)[ strings.ToUpper( s ) ]; ok { // If we are using key, we need to move 1 rune back if expecting.Key != "" { err = reader.UnreadRune() if err != nil { return nil, err } } return f( this ) } else { var supported strings.Builder comma := false for k := range *this.SupportedStmts { if comma { supported.WriteString( ", " ) } else { comma = true } supported.WriteString( k ) } return nil, fmt.Errorf( "Statement not supported: %s, supported statements are: %s", s, supported.String() ) } } } return nil, fmt.Errorf( "Nothing to read" ) } func ( this *Lexer ) ReadUntilNot( charRange string, writeHead bool, head rune ) string { var b strings.Builder if writeHead { b.WriteRune( head ) } reader := this.reader for { r, _, err := reader.ReadRune() if err == nil && strings.ContainsRune( charRange, r ) { b.WriteRune( r ) } else { break } } return b.String() } func ( this *Lexer ) ReadUntilClose( closing rune, canEsc bool, escRune rune ) ( string, error ) { var b strings.Builder reader := this.reader for { r, _, err := reader.ReadRune() if err != nil { if err == io.EOF { return "", fmt.Errorf( "Unexpected end of data after opening %s", string( closing ) ) } return "", err } if r == closing { break } else if canEsc && r == escRune { r2, _, err := reader.ReadRune() if err != nil { return "", err } if r2 != closing { b.WriteRune( r ) b.WriteRune( r2 ) continue } } b.WriteRune( r ) } return b.String(), nil }