mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-09-07 09:05:55 +00:00
WIP: use pql.Decimal instead of float64
This commit introduces a new type: pql.Decimal We use that instead of float64 in order to ensure that the string representation is consistent. One unfortunate discovery during implementation is that the RowAttrs and ColAttrs support floats, and the PEG file was treating them as such. So I had to split the PEG definitions into float-specific items and decimal-specific items.
This commit is contained in:
parent
334eb3cd08
commit
963affcc30
7 changed files with 2182 additions and 1414 deletions
|
|
@ -2926,7 +2926,7 @@ func (e *executor) executeSet(ctx context.Context, index string, c *pql.Call, op
|
|||
// Read row value.
|
||||
rowVal, err := getScaledInt(f, v)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("reading Set() row: %v", err)
|
||||
return false, fmt.Errorf("reading Set() row (int/decimal): %v", err)
|
||||
}
|
||||
return e.executeSetValueField(ctx, index, c, f, colID, rowVal, opt)
|
||||
|
||||
|
|
@ -4477,6 +4477,8 @@ func getScaledInt(f *Field, v interface{}) (int64, error) {
|
|||
value = int64(float64(tv) * math.Pow10(int(scale)))
|
||||
case uint64:
|
||||
value = int64(float64(tv) * math.Pow10(int(scale)))
|
||||
case pql.Decimal:
|
||||
value = tv.ToInt64(scale)
|
||||
case float64:
|
||||
value = int64(tv * math.Pow10(int(scale)))
|
||||
default:
|
||||
|
|
|
|||
18
pql/ast.go
18
pql/ast.go
|
|
@ -62,7 +62,7 @@ func (q *Query) lastCallStackElem() *callStackElem {
|
|||
|
||||
func (q *Query) addPosNum(key, value string) {
|
||||
q.addField(key)
|
||||
q.addNumVal(value)
|
||||
q.addNumVal(value, false)
|
||||
}
|
||||
|
||||
func (q *Query) addPosStr(key, value string) {
|
||||
|
|
@ -87,9 +87,9 @@ func (q *Query) endConditional() {
|
|||
if len(q.conditional) != 5 {
|
||||
panic(fmt.Sprintf("conditional of wrong length: %#v", q.conditional))
|
||||
}
|
||||
low := parseNum(q.conditional[0])
|
||||
low := parseNum(q.conditional[0], false)
|
||||
field := q.conditional[2]
|
||||
high := parseNum(q.conditional[4])
|
||||
high := parseNum(q.conditional[4], false)
|
||||
|
||||
var op Token
|
||||
switch q.conditional[1] + q.conditional[3] {
|
||||
|
|
@ -157,12 +157,12 @@ func (q *Query) addVal(val interface{}) {
|
|||
elem.lastCond = ILLEGAL
|
||||
}
|
||||
|
||||
func (q *Query) addNumVal(val string) {
|
||||
func (q *Query) addNumVal(val string, asFloat bool) {
|
||||
elem := q.lastCallStackElem()
|
||||
if elem == nil || elem.lastField == "" {
|
||||
panic(fmt.Sprintf("addIntVal called with '%s' when lastField is empty", val))
|
||||
}
|
||||
ival := parseNum(val)
|
||||
ival := parseNum(val, asFloat)
|
||||
if elem.inList {
|
||||
if elem.lastCond != ILLEGAL {
|
||||
list := elem.call.Args[elem.lastField].(*Condition).Value.([]interface{})
|
||||
|
|
@ -968,11 +968,15 @@ func joinUint64Slice(a []uint64) string {
|
|||
return "[" + strings.Join(other, ",") + "]"
|
||||
}
|
||||
|
||||
func parseNum(val string) interface{} {
|
||||
func parseNum(val string, asFloat bool) interface{} {
|
||||
var ival interface{}
|
||||
var err error
|
||||
if strings.Contains(val, ".") {
|
||||
ival, err = strconv.ParseFloat(val, 64)
|
||||
if asFloat {
|
||||
ival, err = strconv.ParseFloat(val, 64)
|
||||
} else {
|
||||
ival, err = ParseDecimal(val)
|
||||
}
|
||||
} else {
|
||||
ival, err = strconv.ParseInt(val, 10, 64)
|
||||
}
|
||||
|
|
|
|||
204
pql/decimal.go
Normal file
204
pql/decimal.go
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
// Copyright 2017 Pilosa Corp.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package pql
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Decimal represents a decimal value; the intention
|
||||
// is to avoid relying on float64, and they primary
|
||||
// purpose is to have a predictable way to encode such
|
||||
// values used in query strings.
|
||||
// Sign = true represents a negative value.
|
||||
// Scale is the number of digits to the right of the
|
||||
// decimal point.
|
||||
// Precision is currently not considered; precision, for
|
||||
// our purposes is implied to be the complete, known value.
|
||||
type Decimal struct {
|
||||
Sign bool
|
||||
Value uint32
|
||||
Scale int64
|
||||
}
|
||||
|
||||
// ToInt64 returns d as an int64 adjusted to the
|
||||
// provided scale.
|
||||
func (d Decimal) ToInt64(scale int64) int64 {
|
||||
var ret int64
|
||||
scaleDiff := scale - d.Scale
|
||||
if scaleDiff == 0 {
|
||||
ret = int64(d.Value)
|
||||
} else {
|
||||
ret = int64(float64(d.Value) * math.Pow10(int(scaleDiff)))
|
||||
}
|
||||
if d.Sign {
|
||||
ret *= -1
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// String returns the string representation of the decimal.
|
||||
func (d Decimal) String() string {
|
||||
var s string
|
||||
|
||||
sval := fmt.Sprintf("%d", d.Value)
|
||||
if d.Scale == 0 {
|
||||
s = sval
|
||||
} else if d.Scale < 0 {
|
||||
s = sval + strings.Repeat("0", int(-1*d.Scale))
|
||||
} else {
|
||||
var bufLen int
|
||||
if int(d.Scale) < len(sval) {
|
||||
bufLen = len(sval) + 1
|
||||
} else {
|
||||
bufLen = int(d.Scale) + 2
|
||||
}
|
||||
|
||||
buf := make([]byte, bufLen)
|
||||
j := 0
|
||||
for i := range buf {
|
||||
z := len(buf) - 1 - i // index into buf from the end
|
||||
if i == int(d.Scale) {
|
||||
buf[z] = '.'
|
||||
continue
|
||||
}
|
||||
if len(sval) > j {
|
||||
buf[z] = sval[len(sval)-1-j]
|
||||
j++
|
||||
} else {
|
||||
buf[z] = '0'
|
||||
}
|
||||
}
|
||||
s = string(buf)
|
||||
}
|
||||
|
||||
if d.Sign {
|
||||
return "-" + s
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
const (
|
||||
stateSign = "sign"
|
||||
stateLeadingZeros = "zeros"
|
||||
stateMantissa = "mantissa"
|
||||
)
|
||||
|
||||
// ParseDecimal parses a string into a Decimal.
|
||||
func ParseDecimal(s string) (Decimal, error) {
|
||||
if s == "" {
|
||||
return Decimal{}, nil
|
||||
}
|
||||
var sign bool
|
||||
var value uint64
|
||||
var scale int64
|
||||
var err error
|
||||
|
||||
// General steps:
|
||||
// - Trim leading whitespace/zeros
|
||||
// - Get the sign value
|
||||
// - Trim leading zeros
|
||||
// - Push characters into a buffer
|
||||
// - Track position of decimal point
|
||||
// - Trim trailing zeros of buffer
|
||||
// - value = buffer -> int
|
||||
// - scale = len(buffer) - tracked position
|
||||
|
||||
var decimalPos int = -1
|
||||
var pos int
|
||||
mantissa := make([]byte, len(s))
|
||||
|
||||
state := stateSign
|
||||
for i := 0; i < len(s); i++ {
|
||||
switch state {
|
||||
case stateSign:
|
||||
switch s[i] {
|
||||
case ' ':
|
||||
continue
|
||||
case '-':
|
||||
sign = true
|
||||
fallthrough
|
||||
case '+':
|
||||
state = stateLeadingZeros
|
||||
default:
|
||||
state = stateLeadingZeros
|
||||
i--
|
||||
}
|
||||
case stateLeadingZeros:
|
||||
switch s[i] {
|
||||
case '0':
|
||||
continue
|
||||
default:
|
||||
state = stateMantissa
|
||||
i--
|
||||
}
|
||||
case stateMantissa:
|
||||
switch s[i] {
|
||||
case '.':
|
||||
if decimalPos == -1 {
|
||||
decimalPos = pos
|
||||
} else {
|
||||
return Decimal{}, errors.Errorf("invalid decimal string: %s", s)
|
||||
}
|
||||
continue
|
||||
default:
|
||||
mantissa[pos] = s[i]
|
||||
pos++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Trim trailing zeros/spaces of mantissa
|
||||
// for any portion that would have been to the
|
||||
// right of the decimal.
|
||||
trimZeroCnt := 0
|
||||
trimSpaceCnt := 0
|
||||
for i := len(mantissa) - 1; i >= 0; i-- {
|
||||
switch mantissa[i] {
|
||||
case uint8(0), uint8(32): // nil/space
|
||||
trimSpaceCnt++
|
||||
continue
|
||||
case uint8(48): // zero
|
||||
trimZeroCnt++
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
mantissa = mantissa[:len(mantissa)-trimSpaceCnt-trimZeroCnt]
|
||||
|
||||
// Based on where (or if) the decimal was found,
|
||||
// calculate scale.
|
||||
if decimalPos == -1 {
|
||||
scale = -1 * int64(trimZeroCnt)
|
||||
} else {
|
||||
scale = int64(len(mantissa) - decimalPos)
|
||||
}
|
||||
|
||||
value, err = strconv.ParseUint(string(mantissa), 10, 32)
|
||||
if err != nil {
|
||||
return Decimal{}, errors.Wrap(err, "converting mantissa to uint32")
|
||||
}
|
||||
|
||||
return Decimal{
|
||||
Sign: sign,
|
||||
Value: uint32(value),
|
||||
Scale: scale,
|
||||
}, nil
|
||||
}
|
||||
134
pql/decimal_test.go
Normal file
134
pql/decimal_test.go
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
// Copyright 2017 Pilosa Corp.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package pql_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/pilosa/pilosa/v2/pql"
|
||||
)
|
||||
|
||||
// Ensure call can be converted into a string.
|
||||
func TestDecimal(t *testing.T) {
|
||||
t.Run("Parse", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
s string
|
||||
exp pql.Decimal
|
||||
expErr error // TODO: add tests for errors
|
||||
}{
|
||||
{"123.4567", pql.Decimal{false, 1234567, 4}, nil},
|
||||
{" 123.4567", pql.Decimal{false, 1234567, 4}, nil},
|
||||
{" 123.4567 ", pql.Decimal{false, 1234567, 4}, nil},
|
||||
{"123.456700", pql.Decimal{false, 1234567, 4}, nil},
|
||||
{"00123.4567", pql.Decimal{false, 1234567, 4}, nil},
|
||||
{"+123.4567", pql.Decimal{false, 1234567, 4}, nil},
|
||||
{"-123.4567", pql.Decimal{true, 1234567, 4}, nil},
|
||||
{"-00123.4567", pql.Decimal{true, 1234567, 4}, nil},
|
||||
{"-12.25", pql.Decimal{true, 1225, 2}, nil},
|
||||
|
||||
{"123", pql.Decimal{false, 123, 0}, nil},
|
||||
{"-12300", pql.Decimal{true, 123, -2}, nil},
|
||||
{"+012300", pql.Decimal{false, 123, -2}, nil},
|
||||
{"12300", pql.Decimal{false, 123, -2}, nil},
|
||||
{"12300.", pql.Decimal{false, 123, -2}, nil},
|
||||
{"12300.0", pql.Decimal{false, 123, -2}, nil},
|
||||
{"123.0", pql.Decimal{false, 123, 0}, nil},
|
||||
|
||||
{"0.123", pql.Decimal{false, 123, 3}, nil},
|
||||
{"0.001230", pql.Decimal{false, 123, 5}, nil},
|
||||
{" 0.001230 ", pql.Decimal{false, 123, 5}, nil},
|
||||
{"-0.001230 ", pql.Decimal{true, 123, 5}, nil},
|
||||
}
|
||||
for i, test := range tests {
|
||||
dec, err := pql.ParseDecimal(test.s)
|
||||
if err != nil {
|
||||
t.Fatalf("parsing string `%s`: %s", test.s, err)
|
||||
} else if dec != test.exp {
|
||||
t.Fatalf("test %d expected: %v, but got: %v", i, test.exp, dec)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("ToInt64", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
dec pql.Decimal
|
||||
scale int64
|
||||
exp int64
|
||||
}{
|
||||
{pql.Decimal{false, 0, 0}, 0, 0}, // 0 : 0
|
||||
{pql.Decimal{false, 0, 0}, 1, 0}, // 0 : 0.0
|
||||
{pql.Decimal{false, 0, 0}, -1, 0}, // 0 : 0
|
||||
|
||||
{pql.Decimal{false, 1234567, 4}, 5, 12345670}, // 123.4567 : 123.45670
|
||||
{pql.Decimal{false, 1234567, 4}, 4, 1234567}, // 123.4567 : 123.4567
|
||||
{pql.Decimal{false, 1234567, 4}, 3, 123456}, // 123.4567 : 123.456
|
||||
|
||||
{pql.Decimal{true, 1234567, 4}, 5, -12345670}, // -123.4567 : -123.45670
|
||||
{pql.Decimal{true, 1234567, 4}, 4, -1234567}, // -123.4567 : -123.4567
|
||||
{pql.Decimal{true, 1234567, 4}, 3, -123456}, // -123.4567 : -123.456
|
||||
|
||||
{pql.Decimal{false, 123, -2}, 5, 1230000000}, // 12300 : 12300.00000
|
||||
{pql.Decimal{false, 123, -2}, -1, 1230}, // 12300 : 1230
|
||||
{pql.Decimal{false, 123, 1}, -1, 1}, // 12.3 : 1
|
||||
{pql.Decimal{false, 123, 1}, -2, 0}, // 12.3 : 0
|
||||
}
|
||||
for i, test := range tests {
|
||||
v := test.dec.ToInt64(test.scale)
|
||||
if v != test.exp {
|
||||
t.Fatalf("test %d expected: %d, but got: %d", i, test.exp, v)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("String", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
s string
|
||||
exp string
|
||||
}{
|
||||
{"123.4567", "123.4567"},
|
||||
{" 123.4567", "123.4567"},
|
||||
{" 123.4567 ", "123.4567"},
|
||||
{"123.456700", "123.4567"},
|
||||
{"00123.4567", "123.4567"},
|
||||
{"+123.4567", "123.4567"},
|
||||
{"-123.4567", "-123.4567"},
|
||||
{"-00123.4567", "-123.4567"},
|
||||
{"-12.25", "-12.25"},
|
||||
|
||||
{"123", "123"},
|
||||
{"-12300", "-12300"},
|
||||
{"+012300", "12300"},
|
||||
{"12300", "12300"},
|
||||
|
||||
{"12300.", "12300"},
|
||||
{"12300.0", "12300"},
|
||||
{"123.0", "123"},
|
||||
|
||||
{"0.123", "0.123"},
|
||||
{"0.001230", "0.00123"},
|
||||
{" 0.001230 ", "0.00123"},
|
||||
{"-0.001230 ", "-0.00123"},
|
||||
}
|
||||
for i, test := range tests {
|
||||
dec, err := pql.ParseDecimal(test.s)
|
||||
if err != nil {
|
||||
t.Fatalf("parsing string `%s`: %s", test.s, err)
|
||||
}
|
||||
if str := dec.String(); str != test.exp {
|
||||
t.Fatalf("test %d expected: %s, but got: %s", i, test.exp, str)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -96,8 +96,8 @@ func TestParser_Parse(t *testing.T) {
|
|||
}
|
||||
})
|
||||
|
||||
// Parse with float arguments.
|
||||
t.Run("WithFloatArgs", func(t *testing.T) {
|
||||
// Parse with decimal arguments.
|
||||
t.Run("WithDecimalArgs", func(t *testing.T) {
|
||||
q, err := pql.ParseString(`Row( key=12.25, foo= 13.167, bar=2., baz=0.9)`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
@ -105,10 +105,10 @@ func TestParser_Parse(t *testing.T) {
|
|||
&pql.Call{
|
||||
Name: "Row",
|
||||
Args: map[string]interface{}{
|
||||
"key": 12.25,
|
||||
"foo": 13.167,
|
||||
"bar": 2.,
|
||||
"baz": 0.9,
|
||||
"key": pql.Decimal{false, 1225, 2},
|
||||
"foo": pql.Decimal{false, 13167, 3},
|
||||
"bar": pql.Decimal{false, 2, 0},
|
||||
"baz": pql.Decimal{false, 9, 1},
|
||||
},
|
||||
},
|
||||
) {
|
||||
|
|
@ -125,7 +125,7 @@ func TestParser_Parse(t *testing.T) {
|
|||
&pql.Call{
|
||||
Name: "Row",
|
||||
Args: map[string]interface{}{
|
||||
"key": -12.25,
|
||||
"key": pql.Decimal{true, 1225, 2},
|
||||
"foo": int64(-13),
|
||||
},
|
||||
},
|
||||
|
|
@ -181,7 +181,7 @@ func TestParser_Parse(t *testing.T) {
|
|||
Name: "Row",
|
||||
Args: map[string]interface{}{
|
||||
"key": "foo",
|
||||
"x": &pql.Condition{Op: pql.EQ, Value: 12.25},
|
||||
"x": &pql.Condition{Op: pql.EQ, Value: pql.Decimal{false, 1225, 2}},
|
||||
"y": &pql.Condition{Op: pql.GTE, Value: int64(100)},
|
||||
"z": &pql.Condition{Op: pql.BETWEEN, Value: []interface{}{int64(4), int64(8)}},
|
||||
"m": &pql.Condition{Op: pql.NEQ, Value: nil},
|
||||
|
|
|
|||
62
pql/pql.peg
62
pql/pql.peg
|
|
@ -6,22 +6,27 @@ type PQL Peg {
|
|||
|
||||
|
||||
Calls <- sp (Call sp)* !.
|
||||
Call <- 'Set' {p.startCall("Set")} open col comma args (comma timestamp)? close {p.endCall()}
|
||||
/ 'SetRowAttrs' {p.startCall("SetRowAttrs")} open posfield comma row comma args close {p.endCall()}
|
||||
/ 'SetColumnAttrs' {p.startCall("SetColumnAttrs")} open col comma args close {p.endCall()}
|
||||
/ 'Clear' {p.startCall("Clear")} open col comma args close {p.endCall()}
|
||||
/ 'ClearRow' {p.startCall("ClearRow")} open arg close {p.endCall()}
|
||||
/ 'Store' {p.startCall("Store")} open Call comma arg close {p.endCall()}
|
||||
Call <- 'Set' {p.startCall("Set")} open col comma dargs (comma timestamp)? close {p.endCall()}
|
||||
/ 'SetRowAttrs' {p.startCall("SetRowAttrs")} open posfield comma row comma fargs close {p.endCall()}
|
||||
/ 'SetColumnAttrs' {p.startCall("SetColumnAttrs")} open col comma fargs close {p.endCall()}
|
||||
/ 'Clear' {p.startCall("Clear")} open col comma dargs close {p.endCall()}
|
||||
/ 'ClearRow' {p.startCall("ClearRow")} open darg close {p.endCall()}
|
||||
/ 'Store' {p.startCall("Store")} open Call comma darg close {p.endCall()}
|
||||
/ 'TopN' {p.startCall("TopN")} open posfield (comma allargs)? close {p.endCall()}
|
||||
/ 'Rows' {p.startCall("Rows")} open posfield (comma allargs)? close {p.endCall()}
|
||||
/ 'Range' {p.startCall("Range")} open field sp '=' sp value comma 'from='? {p.addField("from")} timestampfmt {p.addVal(buffer[begin:end])} comma 'to='? sp {p.addField("to")} timestampfmt {p.addVal(buffer[begin:end])} close {p.endCall()}
|
||||
/ 'Range' {p.startCall("Range")} open field sp '=' sp fvalue comma 'from='? {p.addField("from")} timestampfmt {p.addVal(buffer[begin:end])} comma 'to='? sp {p.addField("to")} timestampfmt {p.addVal(buffer[begin:end])} close {p.endCall()}
|
||||
/ < IDENT > { p.startCall(buffer[begin:end] ) } open allargs comma? close { p.endCall() }
|
||||
allargs <- Call (comma Call)* (comma args)? / args / sp
|
||||
args <- arg (comma args)? sp
|
||||
arg <- ( field sp '=' sp value
|
||||
/ field sp COND sp value
|
||||
/ conditional
|
||||
)
|
||||
allargs <- Call (comma Call)* (comma dargs)? / dargs / sp
|
||||
fargs <- farg (comma fargs)? sp
|
||||
farg <- ( field sp '=' sp fvalue
|
||||
/ field sp COND sp fvalue
|
||||
/ conditional
|
||||
)
|
||||
dargs <- darg (comma dargs)? sp
|
||||
darg <- ( field sp '=' sp dvalue
|
||||
/ field sp COND sp dvalue
|
||||
/ conditional
|
||||
)
|
||||
COND <- ( '><' { p.addBTWN() }
|
||||
/ '<=' { p.addLTE() }
|
||||
/ '>=' { p.addGTE() }
|
||||
|
|
@ -36,21 +41,38 @@ condint <- < '-'? [0-9]* '.' [0-9]+ / '0' / '-'? [1-9] [0-9]* > sp {p.condAdd(bu
|
|||
condLT <- <('<=' / '<')> sp {p.condAdd(buffer[begin:end])}
|
||||
condfield <- <fieldExpr> sp {p.condAdd(buffer[begin:end])}
|
||||
|
||||
value <- ( item
|
||||
/ lbrack { p.startList() } list rbrack { p.endList() }
|
||||
dvalue <- ( ditem
|
||||
/ lbrack { p.startList() } dlist rbrack { p.endList() }
|
||||
)
|
||||
list <- item (comma list)?
|
||||
item <- ( 'null' &(comma / sp close) { p.addVal(nil) }
|
||||
fvalue <- ( fitem
|
||||
/ lbrack { p.startList() } flist rbrack { p.endList() }
|
||||
)
|
||||
dlist <- ditem (comma dlist)?
|
||||
flist <- fitem (comma flist)?
|
||||
ditem <- ( itema
|
||||
/ decimal
|
||||
/ itemb
|
||||
)
|
||||
fitem <- ( itema
|
||||
/ float
|
||||
/ itemb
|
||||
)
|
||||
itema <- ( 'null' &(comma / sp close) { p.addVal(nil) }
|
||||
/ 'true' &(comma / sp close) { p.addVal(true) }
|
||||
/ 'false' &(comma / sp close) { p.addVal(false) }
|
||||
/ timestampfmt { p.addVal(buffer[begin:end]) }
|
||||
/ < '-'? [0-9]+ ('.'[0-9]*)? > { p.addNumVal(buffer[begin:end]) }
|
||||
/ < '-'? '.'[0-9]+ > { p.addNumVal(buffer[begin:end]) }
|
||||
/ < IDENT > { p.startCall(buffer[begin:end]) } open allargs comma? close { p.addVal(p.endCall()) }
|
||||
)
|
||||
itemb <- ( < IDENT > { p.startCall(buffer[begin:end]) } open allargs comma? close { p.addVal(p.endCall()) }
|
||||
/ < ([[A-Z]] / [0-9] / '-' / '_' / ':')+ > { p.addVal(buffer[begin:end]) }
|
||||
/ < '"' doublequotedstring '"' > { s, _ := strconv.Unquote(buffer[begin:end]); p.addVal(s) }
|
||||
/ '\'' < singlequotedstring > '\'' { p.addVal(buffer[begin:end]) }
|
||||
)
|
||||
float <- ( < '-'? [0-9]+ ('.'[0-9]*)? > { p.addNumVal(buffer[begin:end], true) }
|
||||
/ < '-'? '.'[0-9]+ > { p.addNumVal(buffer[begin:end], true) }
|
||||
)
|
||||
decimal <- ( < '-'? [0-9]+ ('.'[0-9]*)? > { p.addNumVal(buffer[begin:end], false) }
|
||||
/ < '-'? '.'[0-9]+ > { p.addNumVal(buffer[begin:end], false) }
|
||||
)
|
||||
|
||||
doublequotedstring <- ( '\\"' / '\\\\' / [^"] )*
|
||||
singlequotedstring <- ( '\\\'' / '\\\\' / [^'] )*
|
||||
|
|
|
|||
3158
pql/pql.peg.go
3158
pql/pql.peg.go
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue