Replace parser's int or float code with scanner
This commit is contained in:
+179
-110
@@ -231,8 +231,8 @@ func (p *parser) parseVal(b []byte) (ast.Node, []byte, error) {
|
|||||||
b, err := p.parseInlineTable(&node, b)
|
b, err := p.parseInlineTable(&node, b)
|
||||||
return node, b, err
|
return node, b, err
|
||||||
default:
|
default:
|
||||||
// TODO
|
b, err = p.parseIntOrFloatOrDateTime(&node, b)
|
||||||
//return p.parseIntOrFloatOrDateTime(b)
|
return node, b, err
|
||||||
}
|
}
|
||||||
panic("parseVal not finished yet")
|
panic("parseVal not finished yet")
|
||||||
return ast.Node{}, nil, nil
|
return ast.Node{}, nil, nil
|
||||||
@@ -614,28 +614,28 @@ func (p *parser) parseWhitespace(b []byte) []byte {
|
|||||||
return rest
|
return rest
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) parseIntOrFloatOrDateTime(b []byte) ([]byte, error) {
|
func (p *parser) parseIntOrFloatOrDateTime(node *ast.Node, b []byte) ([]byte, error) {
|
||||||
switch b[0] {
|
switch b[0] {
|
||||||
case 'i':
|
case 'i':
|
||||||
if !scanFollowsInf(b) {
|
if !scanFollowsInf(b) {
|
||||||
return nil, fmt.Errorf("expected 'inf'")
|
return nil, fmt.Errorf("expected 'inf'")
|
||||||
}
|
}
|
||||||
//p.builder.FloatValue(math.Inf(1))
|
node.Kind = ast.Float
|
||||||
// TODO
|
node.Data = b[:3]
|
||||||
return b[3:], nil
|
return b[3:], nil
|
||||||
case 'n':
|
case 'n':
|
||||||
if !scanFollowsNan(b) {
|
if !scanFollowsNan(b) {
|
||||||
return nil, fmt.Errorf("expected 'nan'")
|
return nil, fmt.Errorf("expected 'nan'")
|
||||||
}
|
}
|
||||||
//p.builder.FloatValue(math.NaN())
|
node.Kind = ast.Float
|
||||||
// TODO
|
node.Data = b[:3]
|
||||||
return b[3:], nil
|
return b[3:], nil
|
||||||
case '+', '-':
|
case '+', '-':
|
||||||
return p.parseIntOrFloat(b)
|
return p.scanIntOrFloat(node, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(b) < 3 {
|
if len(b) < 3 {
|
||||||
return p.parseIntOrFloat(b)
|
return p.scanIntOrFloat(node, b)
|
||||||
}
|
}
|
||||||
s := 5
|
s := 5
|
||||||
if len(b) < s {
|
if len(b) < s {
|
||||||
@@ -652,7 +652,7 @@ func (p *parser) parseIntOrFloatOrDateTime(b []byte) ([]byte, error) {
|
|||||||
return p.parseDateTime(b)
|
return p.parseDateTime(b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return p.parseIntOrFloat(b)
|
return p.scanIntOrFloat(node, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func digitsToInt(b []byte) int {
|
func digitsToInt(b []byte) int {
|
||||||
@@ -925,135 +925,204 @@ func (p *parser) parseTime(b []byte) ([]byte, error) {
|
|||||||
return b[idx:], nil
|
return b[idx:], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) parseIntOrFloat(b []byte) ([]byte, error) {
|
func (p *parser) scanIntOrFloat(node *ast.Node, b []byte) ([]byte, error) {
|
||||||
i := 0
|
i := 0
|
||||||
r := b[0]
|
|
||||||
if r == '0' {
|
if len(b) > 2 && b[0] == '0' {
|
||||||
if len(b) >= 2 {
|
|
||||||
var isValidRune validRuneFn
|
var isValidRune validRuneFn
|
||||||
var parseFn func([]byte) (int64, error)
|
|
||||||
switch b[1] {
|
switch b[1] {
|
||||||
case 'x':
|
case 'x':
|
||||||
isValidRune = isValidHexRune
|
isValidRune = isValidHexRune
|
||||||
parseFn = parseIntHex
|
|
||||||
case 'o':
|
case 'o':
|
||||||
isValidRune = isValidOctalRune
|
isValidRune = isValidOctalRune
|
||||||
parseFn = parseIntOct
|
|
||||||
case 'b':
|
case 'b':
|
||||||
isValidRune = isValidBinaryRune
|
isValidRune = isValidBinaryRune
|
||||||
parseFn = parseIntBin
|
|
||||||
default:
|
default:
|
||||||
if b[1] >= 'a' && b[1] <= 'z' || b[1] >= 'A' && b[1] <= 'Z' {
|
return b, fmt.Errorf("unknown number base: %c. possible options are x (hex) o (octal) b (binary)", b[1])
|
||||||
return nil, fmt.Errorf("unknown number base: %s. possible options are x (hex) o (octal) b (binary)", string(b[1]))
|
|
||||||
}
|
|
||||||
parseFn = parseIntDec
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if isValidRune != nil {
|
i += 2
|
||||||
i = 2
|
for ; i < len(b); i++ {
|
||||||
digitSeen := false
|
|
||||||
for {
|
|
||||||
if !isValidRune(b[i]) {
|
if !isValidRune(b[i]) {
|
||||||
break
|
node.Kind = ast.Integer
|
||||||
}
|
node.Data = b[:i]
|
||||||
digitSeen = true
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
if !digitSeen {
|
|
||||||
return nil, fmt.Errorf("number needs at least one digit")
|
|
||||||
}
|
|
||||||
|
|
||||||
v, err := parseFn(b[:i])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
//p.builder.IntValue(v)
|
|
||||||
// TODO
|
|
||||||
v = v
|
|
||||||
return b[i:], nil
|
return b[i:], nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if r == '+' || r == '-' {
|
isFloat := false
|
||||||
b = b[1:]
|
|
||||||
if scanFollowsInf(b) {
|
for ; i < len(b); i++ {
|
||||||
if r == '+' {
|
c := b[i]
|
||||||
//p.builder.FloatValue(plusInf)
|
|
||||||
// TODO
|
if c >= '0' && c <= '9' || c == '+' || c == '-' || c == '_' {
|
||||||
} else {
|
continue
|
||||||
//p.builder.FloatValue(minusInf)
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
return b, nil
|
|
||||||
}
|
|
||||||
if scanFollowsNan(b) {
|
|
||||||
//p.builder.FloatValue(nan)
|
|
||||||
// TODO
|
|
||||||
return b, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pointSeen := false
|
if c == '.' || c == 'e' || c == 'E' {
|
||||||
expSeen := false
|
isFloat = true
|
||||||
digitSeen := false
|
continue
|
||||||
for i < len(b) {
|
|
||||||
next := b[i]
|
|
||||||
if next == '.' {
|
|
||||||
if pointSeen {
|
|
||||||
return nil, fmt.Errorf("cannot have two dots in one float")
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
if i < len(b) && !isDigit(b[i]) {
|
|
||||||
return nil, fmt.Errorf("float cannot end with a dot")
|
|
||||||
}
|
|
||||||
pointSeen = true
|
|
||||||
} else if next == 'e' || next == 'E' {
|
|
||||||
expSeen = true
|
|
||||||
i++
|
|
||||||
if i >= len(b) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if b[i] == '+' || b[i] == '-' {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
} else if isDigit(next) {
|
|
||||||
digitSeen = true
|
|
||||||
i++
|
|
||||||
} else if next == '_' {
|
|
||||||
i++
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if pointSeen && !digitSeen {
|
|
||||||
return nil, fmt.Errorf("cannot start float with a dot")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !digitSeen {
|
if c == 'i' {
|
||||||
return nil, fmt.Errorf("no digit in that number")
|
if scanFollowsInf(b[i:]) {
|
||||||
|
node.Kind = ast.Float
|
||||||
|
node.Data = b[:i+3]
|
||||||
|
return b[i+3:], nil
|
||||||
}
|
}
|
||||||
if pointSeen || expSeen {
|
return nil, fmt.Errorf("unexpected character i while scanning for a number")
|
||||||
f, err := parseFloat(b[:i])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
//p.builder.FloatValue(f)
|
if c == 'n' {
|
||||||
// TODO
|
if scanFollowsNan(b[i:]) {
|
||||||
f = f
|
node.Kind = ast.Float
|
||||||
|
node.Data = b[:i+3]
|
||||||
|
return b[i+3:], nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("unexpected character n while scanning for a number")
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if isFloat {
|
||||||
|
node.Kind = ast.Float
|
||||||
} else {
|
} else {
|
||||||
v, err := parseIntDec(b[:i])
|
node.Kind = ast.Integer
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
//p.builder.IntValue(v)
|
|
||||||
// TODO
|
|
||||||
v = v
|
|
||||||
}
|
}
|
||||||
|
node.Data = b[:i]
|
||||||
return b[i:], nil
|
return b[i:], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//func (p *parser) parseIntOrFloat(node *ast.Node, b []byte) ([]byte, error) {
|
||||||
|
// i := 0
|
||||||
|
// r := b[0]
|
||||||
|
// if r == '0' {
|
||||||
|
// if len(b) >= 2 {
|
||||||
|
// var isValidRune validRuneFn
|
||||||
|
// var parseFn func([]byte) (int64, error)
|
||||||
|
// switch b[1] {
|
||||||
|
// case 'x':
|
||||||
|
// isValidRune = isValidHexRune
|
||||||
|
// parseFn = parseIntHex
|
||||||
|
// case 'o':
|
||||||
|
// isValidRune = isValidOctalRune
|
||||||
|
// parseFn = parseIntOct
|
||||||
|
// case 'b':
|
||||||
|
// isValidRune = isValidBinaryRune
|
||||||
|
// parseFn = parseIntBin
|
||||||
|
// default:
|
||||||
|
// if b[1] >= 'a' && b[1] <= 'z' || b[1] >= 'A' && b[1] <= 'Z' {
|
||||||
|
// return nil, fmt.Errorf("unknown number base: %s. possible options are x (hex) o (octal) b (binary)", string(b[1]))
|
||||||
|
// }
|
||||||
|
// parseFn = parseIntDec
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if isValidRune != nil {
|
||||||
|
// i = 2
|
||||||
|
// digitSeen := false
|
||||||
|
// for {
|
||||||
|
// if !isValidRune(b[i]) {
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// digitSeen = true
|
||||||
|
// i++
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if !digitSeen {
|
||||||
|
// return nil, fmt.Errorf("number needs at least one digit")
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// v, err := parseFn(b[:i])
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
// //p.builder.IntValue(v)
|
||||||
|
// // TODO
|
||||||
|
// v = v
|
||||||
|
// return b[i:], nil
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if r == '+' || r == '-' {
|
||||||
|
// b = b[1:]
|
||||||
|
// if scanFollowsInf(b) {
|
||||||
|
// if r == '+' {
|
||||||
|
// //p.builder.FloatValue(plusInf)
|
||||||
|
// // TODO
|
||||||
|
// } else {
|
||||||
|
// //p.builder.FloatValue(minusInf)
|
||||||
|
// // TODO
|
||||||
|
// }
|
||||||
|
// return b, nil
|
||||||
|
// }
|
||||||
|
// if scanFollowsNan(b) {
|
||||||
|
// //p.builder.FloatValue(nan)
|
||||||
|
// // TODO
|
||||||
|
// return b, nil
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// pointSeen := false
|
||||||
|
// expSeen := false
|
||||||
|
// digitSeen := false
|
||||||
|
// for i < len(b) {
|
||||||
|
// next := b[i]
|
||||||
|
// if next == '.' {
|
||||||
|
// if pointSeen {
|
||||||
|
// return nil, fmt.Errorf("cannot have two dots in one float")
|
||||||
|
// }
|
||||||
|
// i++
|
||||||
|
// if i < len(b) && !isDigit(b[i]) {
|
||||||
|
// return nil, fmt.Errorf("float cannot end with a dot")
|
||||||
|
// }
|
||||||
|
// pointSeen = true
|
||||||
|
// } else if next == 'e' || next == 'E' {
|
||||||
|
// expSeen = true
|
||||||
|
// i++
|
||||||
|
// if i >= len(b) {
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// if b[i] == '+' || b[i] == '-' {
|
||||||
|
// i++
|
||||||
|
// }
|
||||||
|
// } else if isDigit(next) {
|
||||||
|
// digitSeen = true
|
||||||
|
// i++
|
||||||
|
// } else if next == '_' {
|
||||||
|
// i++
|
||||||
|
// } else {
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// if pointSeen && !digitSeen {
|
||||||
|
// return nil, fmt.Errorf("cannot start float with a dot")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if !digitSeen {
|
||||||
|
// return nil, fmt.Errorf("no digit in that number")
|
||||||
|
// }
|
||||||
|
// if pointSeen || expSeen {
|
||||||
|
// f, err := parseFloat(b[:i])
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
// //p.builder.FloatValue(f)
|
||||||
|
// // TODO
|
||||||
|
// f = f
|
||||||
|
// } else {
|
||||||
|
// v, err := parseIntDec(b[:i])
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
// //p.builder.IntValue(v)
|
||||||
|
// // TODO
|
||||||
|
// v = v
|
||||||
|
// }
|
||||||
|
// return b[i:], nil
|
||||||
|
//}
|
||||||
|
|
||||||
func parseFloat(b []byte) (float64, error) {
|
func parseFloat(b []byte) (float64, error) {
|
||||||
// TODO: inefficient
|
// TODO: inefficient
|
||||||
tok := string(b)
|
tok := string(b)
|
||||||
|
|||||||
@@ -7,6 +7,140 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestParser_Numbers(t *testing.T) {
|
||||||
|
examples := []struct {
|
||||||
|
desc string
|
||||||
|
input string
|
||||||
|
kind ast.Kind
|
||||||
|
err bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "integer just digits",
|
||||||
|
input: `1234`,
|
||||||
|
kind: ast.Integer,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "integer zero",
|
||||||
|
input: `0`,
|
||||||
|
kind: ast.Integer,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "integer sign",
|
||||||
|
input: `+99`,
|
||||||
|
kind: ast.Integer,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "integer hex uppercase",
|
||||||
|
input: `0xDEADBEEF`,
|
||||||
|
kind: ast.Integer,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "integer hex lowercase",
|
||||||
|
input: `0xdead_beef`,
|
||||||
|
kind: ast.Integer,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "integer octal",
|
||||||
|
input: `0o01234567`,
|
||||||
|
kind: ast.Integer,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "integer binary",
|
||||||
|
input: `0b11010110`,
|
||||||
|
kind: ast.Integer,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "float pi",
|
||||||
|
input: `3.1415`,
|
||||||
|
kind: ast.Float,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "float negative",
|
||||||
|
input: `-0.01`,
|
||||||
|
kind: ast.Float,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "float signed exponent",
|
||||||
|
input: `5e+22`,
|
||||||
|
kind: ast.Float,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "float exponent lowercase",
|
||||||
|
input: `1e06`,
|
||||||
|
kind: ast.Float,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "float exponent uppercase",
|
||||||
|
input: `-2E-2`,
|
||||||
|
kind: ast.Float,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "float fractional with exponent",
|
||||||
|
input: `6.626e-34`,
|
||||||
|
kind: ast.Float,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "float underscores",
|
||||||
|
input: `224_617.445_991_228`,
|
||||||
|
kind: ast.Float,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "inf",
|
||||||
|
input: `inf`,
|
||||||
|
kind: ast.Float,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "inf negative",
|
||||||
|
input: `-inf`,
|
||||||
|
kind: ast.Float,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "inf positive",
|
||||||
|
input: `+inf`,
|
||||||
|
kind: ast.Float,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "nan",
|
||||||
|
input: `nan`,
|
||||||
|
kind: ast.Float,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "nan negative",
|
||||||
|
input: `-nan`,
|
||||||
|
kind: ast.Float,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "nan positive",
|
||||||
|
input: `+nan`,
|
||||||
|
kind: ast.Float,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, e := range examples {
|
||||||
|
t.Run(e.desc, func(t *testing.T) {
|
||||||
|
p := parser{}
|
||||||
|
err := p.parse([]byte(`A = ` + e.input))
|
||||||
|
if e.err {
|
||||||
|
require.Error(t, err)
|
||||||
|
} else {
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expected := ast.Root{
|
||||||
|
ast.Node{
|
||||||
|
Kind: ast.KeyValue,
|
||||||
|
Children: []ast.Node{
|
||||||
|
{Kind: ast.Key, Data: []byte(`A`)},
|
||||||
|
{Kind: e.kind, Data: []byte(e.input)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
require.Equal(t, expected, p.tree)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestParser_AST(t *testing.T) {
|
func TestParser_AST(t *testing.T) {
|
||||||
examples := []struct {
|
examples := []struct {
|
||||||
desc string
|
desc string
|
||||||
|
|||||||
Reference in New Issue
Block a user