package parse import ( "math" "strconv" "git.makaay.nl/mauricem/go-parsekit/parse" "git.makaay.nl/mauricem/go-toml/ast" ) var ( // Integers are whole numbers. Positive numbers may be prefixed with a // plus sign. Negative numbers are prefixed with a minus sign. For large // 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())).Or(a.Signed(a.Zero)) underscoreDigits = m.Drop(a.Underscore).Then(a.Digits) integerSuffix = c.ZeroOrMore(underscoreDigits) integer = integerPrefix.Then(integerSuffix) // Integer values -0 and +0 are valid and identical to an unprefixed zero. plusZero = a.Plus.Then(a.Zero) minusZero = a.Minus.Then(a.Zero) // Non-negative integer values may also be expressed in hexadecimal, octal, // or binary. In these formats, leading + is not allowed and leading zeros // are allowed (after the prefix). Hex values are case insensitive. // Underscores are allowed between digits (but not between the prefix // and the value). // Hexadecimal with prefix `0x`. hexDigits = c.OneOrMore(a.HexDigit) underscoreHexDigits = m.Drop(a.Underscore).Then(hexDigits) hexadecimal = a.Rune('x').Then(tok.Str("x", hexDigits.Then(c.ZeroOrMore(underscoreHexDigits)))) // Octal with prefix `0o`. octalDigits = c.OneOrMore(a.RuneRange('0', '7')) underscoreOctalDigits = m.Drop(a.Underscore).Then(octalDigits) octal = a.Rune('o').Then(tok.Str("o", octalDigits.Then(c.ZeroOrMore(underscoreOctalDigits)))) // Binary with prefix `0b`. 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. inf = a.Signed(a.Str("inf")) nan = a.Signed(a.Str("nan")) ) func (t *parser) parseNumber(p *parse.API) (*ast.Value, bool) { switch { case p.Accept(tok.Float64(nil, float)): return ast.NewValue(ast.TypeFloat, p.Result().Value(0).(float64)), true case p.Accept(nan): return ast.NewValue(ast.TypeFloat, math.NaN()), true case p.Accept(inf): if p.Result().Rune(0) == '-' { return ast.NewValue(ast.TypeFloat, math.Inf(-1)), true } return ast.NewValue(ast.TypeFloat, math.Inf(+1)), true case p.Accept(a.Zero): return t.parseIntegerStartingWithZero(p) case p.Accept(tok.Int64(nil, integer)): return ast.NewValue(ast.TypeInteger, p.Result().Value(0).(int64)), true default: p.Expected("a number") return nil, false } } func (t *parser) parseIntegerStartingWithZero(p *parse.API) (*ast.Value, bool) { var value int64 var err error switch { case p.Accept(hexadecimal): value, err = strconv.ParseInt(p.Result().Value(0).(string), 16, 64) case p.Accept(octal): value, err = strconv.ParseInt(p.Result().Value(0).(string), 8, 64) case p.Accept(binary): value, err = strconv.ParseInt(p.Result().Value(0).(string), 2, 64) default: return ast.NewValue(ast.TypeInteger, int64(0)), true } if err == nil { return ast.NewValue(ast.TypeInteger, value), true } p.Error("invalid integer value 0%s: %s", p.Result().String(), err) return nil, false }