Implemented all number formats for TOML.

This commit is contained in:
Maurice Makaay 2019-06-18 22:52:45 +00:00
parent e8739d38ea
commit 278efd7dbe
3 changed files with 69 additions and 14 deletions

View File

@ -20,6 +20,8 @@ const (
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 {

View File

@ -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")
}
}

View File

@ -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)
}
}