Skip to content
This repository has been archived by the owner on Sep 2, 2022. It is now read-only.

implement literal handling in compiler and engine #69

Merged
merged 6 commits into from
Oct 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion cmd/xdb/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"io"
"os"
"os/signal"
"path/filepath"
"syscall"

"github.com/rs/zerolog"
Expand Down Expand Up @@ -167,7 +168,7 @@ func startNode(cmd *cobra.Command, args []string) {

func createLogger(stdin io.Reader, stdout, stderr io.Writer) zerolog.Logger {
// open the log file
file, err := os.OpenFile(logfile, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0600)
file, err := os.OpenFile(filepath.Clean(logfile), os.O_CREATE|os.O_APPEND|os.O_RDWR, 0600)
if err != nil {
_, _ = fmt.Fprintln(stderr, fmt.Errorf("open logfile: %w", err).Error())
os.Exit(ExitAbnormal)
Expand Down
8 changes: 4 additions & 4 deletions internal/compiler/command/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,8 +237,8 @@ type (
// this is a representation derived from the AST. If this is empty, the
// executor has to interpolate the table from the execution context.
Table string
// Name is the name of the column.
Name Expr
// Expr is the name of the column.
Expr Expr
// Alias is the alias name for this table. May be empty.
Alias string
}
Expand Down Expand Up @@ -422,9 +422,9 @@ func (u UpdateSetter) String() string {

func (c Column) String() string {
if c.Alias == "" {
return c.Name.String()
return c.Expr.String()
}
return fmt.Sprintf("%v AS %v", c.Name, c.Alias)
return fmt.Sprintf("%v AS %v", c.Expr, c.Alias)
}

func (j Join) String() string {
Expand Down
60 changes: 53 additions & 7 deletions internal/compiler/command/expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,26 @@ type (
_expr()
}

// LiteralExpr is a simple literal expression that has a single string
// value.
LiteralExpr struct {
// Value is the simple string value of this expression.
Value string
// ConstantLiteral is a constant literal expression. The Numeric flag determines whether
// or not this is a numeric literal.
ConstantLiteral struct {
Value string
Numeric bool
}

// ConstantLiteralOrColumnReference is a string literal that represents either a constant
// string value or a column reference. If this literal is resolved, the column reference
// takes precedence over the string literal, meaning that if a column exists, whose name is
// equal to the value of this expression, the value in that column must be used. Only if there
// is no such column present, the string literal is to be used.
ConstantLiteralOrColumnReference struct {
ValueOrName string
}

// ColumnReference is a string literal that represents a reference to a column. During resolving,
// if no column with such a name is present, an error must be risen.
ColumnReference struct {
Name string
}

// ConstantBooleanExpr is a simple expression that represents a boolean
Expand Down Expand Up @@ -57,15 +72,46 @@ type (
}
)

func (LiteralExpr) _expr() {}
func (ConstantBooleanExpr) _expr() {}
func (RangeExpr) _expr() {}
func (FunctionExpr) _expr() {}

func (l LiteralExpr) String() string {
func (ConstantLiteral) _expr() {}
func (ConstantLiteralOrColumnReference) _expr() {}
func (ColumnReference) _expr() {}

// ConstantValue returns the constant value of this literal.
func (l ConstantLiteral) ConstantValue() string {
return l.Value
}

func (l ConstantLiteral) String() string {
return l.Value
}

// ConstantValue returns the constant value of this literal.
func (l ConstantLiteralOrColumnReference) ConstantValue() string {
return l.ValueOrName
}

// ReferencedColName returns the constant name of the referenced column name.
func (l ConstantLiteralOrColumnReference) ReferencedColName() string {
return l.ValueOrName
}

func (l ConstantLiteralOrColumnReference) String() string {
return l.ValueOrName
}

// ReferencedColName returns the constant name of the referenced column name.
func (l ColumnReference) ReferencedColName() string {
return l.Name
}

func (l ColumnReference) String() string {
return l.Name
}

func (b ConstantBooleanExpr) String() string {
return strconv.FormatBool(b.Value)
}
Expand Down
156 changes: 156 additions & 0 deletions internal/compiler/numeric_parser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package compiler

import (
"bytes"
"strings"
)

type numericParserState func(*numericParser) numericParserState

type numericParser struct {
candidate string
index int

isReal bool
isHexadecimal bool
isErronous bool
tsatke marked this conversation as resolved.
Show resolved Hide resolved
hasDigitsBeforeExponent bool

value *bytes.Buffer

current numericParserState
}

// isNumeric is an adapted copy of (engine.Engine).ToNumericValue.
func isNumeric(s string) bool {
if s == "" || s == "0x" || s == "." {
return false
}
p := numericParser{
candidate: s,
index: 0,

value: &bytes.Buffer{},

current: stateInitial,
}
p.parse()
return !p.isErronous
}

func (p numericParser) done() bool {
return p.index >= len(p.candidate)
}

func (p *numericParser) parse() {
for p.current != nil && !p.done() {
p.current = p.current(p)
}
}

func (p *numericParser) get() byte {
return p.candidate[p.index]
}

func (p *numericParser) step() {
_ = p.value.WriteByte(p.get())
p.index++
}

func stateInitial(p *numericParser) numericParserState {
switch {
case strings.HasPrefix(p.candidate, "0x"):
p.index += 2
p.isHexadecimal = true
return stateHex
case isDigit(p.get()):
return stateFirstDigits
case p.get() == '.':
return stateDecimalPoint
}
p.isErronous = true
return nil
}

func stateHex(p *numericParser) numericParserState {
if isHexDigit(p.get()) {
p.step()
return stateHex
}
p.isErronous = true
return nil
}

func stateFirstDigits(p *numericParser) numericParserState {
if isDigit(p.get()) {
p.hasDigitsBeforeExponent = true
p.step()
return stateFirstDigits
} else if p.get() == '.' {
return stateDecimalPoint
}
p.isErronous = true
return nil
}

func stateDecimalPoint(p *numericParser) numericParserState {
if p.get() == '.' {
p.step()
p.isReal = true
return stateSecondDigits
}
p.isErronous = true
return nil
}

func stateSecondDigits(p *numericParser) numericParserState {
if isDigit(p.get()) {
p.hasDigitsBeforeExponent = true
p.step()
return stateSecondDigits
} else if p.get() == 'E' {
if p.hasDigitsBeforeExponent {
return stateExponent
}
p.isErronous = true // if there were no first digits,
}
p.isErronous = true
return nil
}

func stateExponent(p *numericParser) numericParserState {
if p.get() == 'E' {
p.step()
return stateOptionalSign
}
p.isErronous = true
return nil
}

func stateOptionalSign(p *numericParser) numericParserState {
if p.get() == '+' || p.get() == '-' {
p.step()
return stateThirdDigits
} else if isDigit(p.get()) {
return stateThirdDigits
}
p.isErronous = true
return nil
}

func stateThirdDigits(p *numericParser) numericParserState {
if isDigit(p.get()) {
p.step()
return stateThirdDigits
}
p.isErronous = true
return nil
}

func isDigit(b byte) bool {
return b-'0' <= 9
}

func isHexDigit(b byte) bool {
return isDigit(b) || (b-'A' <= 15) || (b-'a' <= 15)
}
17 changes: 17 additions & 0 deletions internal/compiler/numeric_parser_bench_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package compiler

import (
"testing"
)

func BenchmarkToNumericValue(b *testing.B) {
str := "75610342.92389E-21423"

b.ResetTimer()

for i := 0; i < b.N; i++ {
if !isNumeric(str) {
b.FailNow()
}
}
}
Loading