Implemented all number formats for TOML.
This commit is contained in:
parent
e8739d38ea
commit
278efd7dbe
14
toml.go
14
toml.go
|
@ -14,12 +14,14 @@ type cmdType string
|
||||||
|
|
||||||
// Command types that are emitted by the parser.
|
// Command types that are emitted by the parser.
|
||||||
const (
|
const (
|
||||||
cComment cmdType = "comment" // a # comment at the end of the line
|
cComment cmdType = "comment" // a # comment at the end of the line
|
||||||
cKey = "key" // set key name
|
cKey = "key" // set key name
|
||||||
cKeyDot = "keydot" // new key stack level
|
cKeyDot = "keydot" // new key stack level
|
||||||
cAssign = "assign" // assign a value
|
cAssign = "assign" // assign a value
|
||||||
csetStrVal = "string" // set a string value
|
csetStrVal = "string" // set a string value
|
||||||
csetIntVal = "integer" // set an integer value
|
csetIntVal = "integer" // set an integer value
|
||||||
|
csetFloatVal = "float" // set a float value
|
||||||
|
csetBoolVal = "boolean" // set a boolean value
|
||||||
)
|
)
|
||||||
|
|
||||||
type parser struct {
|
type parser struct {
|
||||||
|
|
|
@ -12,7 +12,7 @@ var (
|
||||||
// numbers, you may use underscores between digits to enhance readability.
|
// numbers, you may use underscores between digits to enhance readability.
|
||||||
// Each underscore must be surrounded by at least one digit on each side.
|
// Each underscore must be surrounded by at least one digit on each side.
|
||||||
// Leading zeros are not allowed.
|
// Leading zeros are not allowed.
|
||||||
integerPrefix = a.Signed(a.DigitNotZero.Then(a.Digits.Optional()))
|
integerPrefix = a.Signed(a.DigitNotZero.Then(a.Digits.Optional())).Or(a.Signed(zero))
|
||||||
underscoreDigits = m.Drop(a.Underscore).Then(a.Digits)
|
underscoreDigits = m.Drop(a.Underscore).Then(a.Digits)
|
||||||
integerSuffix = c.ZeroOrMore(underscoreDigits)
|
integerSuffix = c.ZeroOrMore(underscoreDigits)
|
||||||
integer = integerPrefix.Then(integerSuffix)
|
integer = integerPrefix.Then(integerSuffix)
|
||||||
|
@ -39,18 +39,50 @@ var (
|
||||||
binaryDigits = c.OneOrMore(a.RuneRange('0', '1'))
|
binaryDigits = c.OneOrMore(a.RuneRange('0', '1'))
|
||||||
underscoreBinaryDigits = m.Drop(a.Underscore).Then(binaryDigits)
|
underscoreBinaryDigits = m.Drop(a.Underscore).Then(binaryDigits)
|
||||||
binary = a.Rune('b').Then(tok.Str("b", binaryDigits.Then(c.ZeroOrMore(underscoreBinaryDigits))))
|
binary = a.Rune('b').Then(tok.Str("b", binaryDigits.Then(c.ZeroOrMore(underscoreBinaryDigits))))
|
||||||
|
|
||||||
|
// A fractional part is a decimal point followed by one or more digits.
|
||||||
|
// Similar to integers, you may use underscores to enhance readability.
|
||||||
|
// Each underscore must be surrounded by at least one digit.
|
||||||
|
fractionalPart = a.Dot.Then(a.Digits).Then(c.ZeroOrMore(underscoreDigits))
|
||||||
|
|
||||||
|
// An exponent part is an E (upper or lower case) followed by an integer
|
||||||
|
// part (which follows the same rules as decimal integer values).
|
||||||
|
exponentPart = a.Runes('e', 'E').Then(integer)
|
||||||
|
|
||||||
|
// Floats should be implemented as IEEE 754 binary64 values.
|
||||||
|
// A float consists of an integer part (which follows the same rules as
|
||||||
|
// decimal integer values) followed by a fractional part and/or an exponent
|
||||||
|
// part. If both a fractional part and exponent part are present, the
|
||||||
|
// fractional part must precede the exponent part.
|
||||||
|
fractAndOrExp = c.Any(fractionalPart.Then(c.Optional(exponentPart)), exponentPart)
|
||||||
|
float = integer.Then(fractAndOrExp)
|
||||||
|
|
||||||
|
// Float values -0.0 and +0.0 are valid and should map according to IEEE 754.
|
||||||
|
zeroFloat = a.Signed(a.Str("0.0"))
|
||||||
|
|
||||||
|
// Special float values can also be expressed. They are always lowercase.
|
||||||
|
infinity = a.Signed(a.Str("inf"))
|
||||||
|
nan = a.Signed(a.Str("nan"))
|
||||||
)
|
)
|
||||||
|
|
||||||
func (t *parser) startInteger(p *parse.API) {
|
func (t *parser) startNumber(p *parse.API) {
|
||||||
switch {
|
switch {
|
||||||
|
case p.Accept(float):
|
||||||
|
f, err := strconv.ParseFloat(p.Result().String(), 64)
|
||||||
|
if err != nil {
|
||||||
|
p.Error("Cannot parse value 0%s: %s", p.Result().String(), err)
|
||||||
|
} else {
|
||||||
|
t.emitCommand(csetFloatVal, f)
|
||||||
|
}
|
||||||
|
case p.Accept(zeroFloat):
|
||||||
|
f, _ := strconv.ParseFloat(p.Result().String(), 64)
|
||||||
|
t.emitCommand(csetFloatVal, f)
|
||||||
case p.Accept(zero):
|
case p.Accept(zero):
|
||||||
p.Handle(t.startIntegerStartingWithZero)
|
p.Handle(t.startIntegerStartingWithZero)
|
||||||
case p.Accept(plusZero.Or(minusZero)):
|
|
||||||
t.emitCommand(csetIntVal, int64(0))
|
|
||||||
case p.Accept(tok.Int64(nil, integer)):
|
case p.Accept(tok.Int64(nil, integer)):
|
||||||
t.emitCommand(csetIntVal, p.Result().Value(0).(int64))
|
t.emitCommand(csetIntVal, p.Result().Value(0).(int64))
|
||||||
default:
|
default:
|
||||||
p.Expected("an integer value")
|
p.Expected("a number")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
|
|
||||||
func TestInteger(t *testing.T) {
|
func TestInteger(t *testing.T) {
|
||||||
for _, test := range []parseTest{
|
for _, test := range []parseTest{
|
||||||
{``, []string{`Error: unexpected end of file (expected an integer value) at start of file`}},
|
{``, []string{`Error: unexpected end of file (expected a number) at start of file`}},
|
||||||
// Decimal
|
// Decimal
|
||||||
{`0`, []string{`integer(0)`}},
|
{`0`, []string{`integer(0)`}},
|
||||||
{`+0`, []string{`integer(0)`}},
|
{`+0`, []string{`integer(0)`}},
|
||||||
|
@ -16,7 +16,7 @@ func TestInteger(t *testing.T) {
|
||||||
{`+99`, []string{`integer(99)`}},
|
{`+99`, []string{`integer(99)`}},
|
||||||
{`-17`, []string{`integer(-17)`}},
|
{`-17`, []string{`integer(-17)`}},
|
||||||
{`1234`, []string{`integer(1234)`}},
|
{`1234`, []string{`integer(1234)`}},
|
||||||
{`_`, []string{`Error: unexpected input (expected an integer value) at start of file`}},
|
{`_`, []string{`Error: unexpected input (expected a number) at start of file`}},
|
||||||
{`1_`, []string{`integer(1)`, `Error: unexpected input (expected end of file) at line 1, column 2`}},
|
{`1_`, []string{`integer(1)`, `Error: unexpected input (expected end of file) at line 1, column 2`}},
|
||||||
{`1_000`, []string{`integer(1000)`}},
|
{`1_000`, []string{`integer(1000)`}},
|
||||||
{`5_349_221`, []string{`integer(5349221)`}},
|
{`5_349_221`, []string{`integer(5349221)`}},
|
||||||
|
@ -75,6 +75,27 @@ func TestInteger(t *testing.T) {
|
||||||
`value out of range at line 1, column 74`}},
|
`value out of range at line 1, column 74`}},
|
||||||
} {
|
} {
|
||||||
p := &parser{}
|
p := &parser{}
|
||||||
testParseHandler(t, p, p.startInteger, test)
|
testParseHandler(t, p, p.startNumber, test)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFloat(t *testing.T) {
|
||||||
|
for _, test := range []parseTest{
|
||||||
|
{``, []string{`Error: unexpected end of file (expected a number) at start of file`}},
|
||||||
|
{`0.0`, []string{`float(0)`}},
|
||||||
|
{`+0.0`, []string{`float(0)`}},
|
||||||
|
{`-0.0`, []string{`float(-0)`}},
|
||||||
|
{`+1.0`, []string{`float(1)`}},
|
||||||
|
{`3.1415`, []string{`float(3.1415)`}},
|
||||||
|
{`-0.01`, []string{`float(-0.01)`}},
|
||||||
|
{`5e+22`, []string{`float(5e+22)`}},
|
||||||
|
{`1E6`, []string{`float(1e+06)`}},
|
||||||
|
{`-2E-2`, []string{`float(-0.02)`}},
|
||||||
|
{`6.626e-34`, []string{`float(6.626e-34)`}},
|
||||||
|
{`224_617.445_991_228`, []string{`float(224617.445991228)`}},
|
||||||
|
{`12_345.111_222e+1_2_3`, []string{`float(1.2345111222e+127)`}},
|
||||||
|
} {
|
||||||
|
p := &parser{}
|
||||||
|
testParseHandler(t, p, p.startNumber, test)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue