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.
|
||||
const (
|
||||
cComment cmdType = "comment" // a # comment at the end of the line
|
||||
cKey = "key" // set key name
|
||||
cKeyDot = "keydot" // new key stack level
|
||||
cAssign = "assign" // assign a value
|
||||
csetStrVal = "string" // set a string value
|
||||
csetIntVal = "integer" // set an integer value
|
||||
cComment cmdType = "comment" // a # comment at the end of the line
|
||||
cKey = "key" // set key name
|
||||
cKeyDot = "keydot" // new key stack level
|
||||
cAssign = "assign" // assign a value
|
||||
csetStrVal = "string" // set a string value
|
||||
csetIntVal = "integer" // set an integer value
|
||||
csetFloatVal = "float" // set a float value
|
||||
csetBoolVal = "boolean" // set a boolean value
|
||||
)
|
||||
|
||||
type parser struct {
|
||||
|
|
|
@ -12,7 +12,7 @@ var (
|
|||
// numbers, you may use underscores between digits to enhance readability.
|
||||
// Each underscore must be surrounded by at least one digit on each side.
|
||||
// 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)
|
||||
integerSuffix = c.ZeroOrMore(underscoreDigits)
|
||||
integer = integerPrefix.Then(integerSuffix)
|
||||
|
@ -39,18 +39,50 @@ var (
|
|||
binaryDigits = c.OneOrMore(a.RuneRange('0', '1'))
|
||||
underscoreBinaryDigits = m.Drop(a.Underscore).Then(binaryDigits)
|
||||
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 {
|
||||
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):
|
||||
p.Handle(t.startIntegerStartingWithZero)
|
||||
case p.Accept(plusZero.Or(minusZero)):
|
||||
t.emitCommand(csetIntVal, int64(0))
|
||||
case p.Accept(tok.Int64(nil, integer)):
|
||||
t.emitCommand(csetIntVal, p.Result().Value(0).(int64))
|
||||
default:
|
||||
p.Expected("an integer value")
|
||||
p.Expected("a number")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
|
||||
func TestInteger(t *testing.T) {
|
||||
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
|
||||
{`0`, []string{`integer(0)`}},
|
||||
{`+0`, []string{`integer(0)`}},
|
||||
|
@ -16,7 +16,7 @@ func TestInteger(t *testing.T) {
|
|||
{`+99`, []string{`integer(99)`}},
|
||||
{`-17`, []string{`integer(-17)`}},
|
||||
{`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_000`, []string{`integer(1000)`}},
|
||||
{`5_349_221`, []string{`integer(5349221)`}},
|
||||
|
@ -75,6 +75,27 @@ func TestInteger(t *testing.T) {
|
|||
`value out of range at line 1, column 74`}},
|
||||
} {
|
||||
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