Optimization round completed (for now :-) All tests successful.
This commit is contained in:
parent
30a0d80d84
commit
be5f3b6f55
|
|
@ -67,7 +67,7 @@ func (t *parser) parseKey(p *parse.API, key ast.Key) (ast.Key, bool) {
|
|||
var ok bool
|
||||
switch {
|
||||
case p.Accept(bareKey):
|
||||
keyPart, ok = p.Result().String(), true
|
||||
keyPart, ok = p.Result.String(), true
|
||||
case p.Peek(a.SingleQuote):
|
||||
keyPart, ok = t.parseLiteralString("key", p)
|
||||
case p.Peek(a.DoubleQuote):
|
||||
|
|
|
|||
|
|
@ -91,16 +91,15 @@ func (t *parser) parseDateTime(p *parse.API) (*ast.Value, bool) {
|
|||
p.Expected("a date and/or time")
|
||||
return nil, false
|
||||
}
|
||||
result := p.Result()
|
||||
token := result.Token(0)
|
||||
token := p.Result.Tokens[0]
|
||||
|
||||
layout := ""
|
||||
for _, l := range token.Value.([]tokenize.Token) {
|
||||
layout += l.Type.(string)
|
||||
}
|
||||
value, err := time.Parse(layout, string(token.Runes))
|
||||
value, err := time.Parse(layout, p.Result.String())
|
||||
if err != nil {
|
||||
p.Error("invalid date/time value %s: %s", string(token.Runes), err)
|
||||
p.Error("invalid date/time value %s: %s", p.Result.String(), err)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ func TestStartDateTime(t *testing.T) {
|
|||
|
||||
func TestDateTime(t *testing.T) {
|
||||
for _, test := range []parseTest{
|
||||
{`x=1979-05-27`, `{"x": 1979-05-27}`, ``},
|
||||
{`x=1978-05-27`, `{"x": 1978-05-27}`, ``},
|
||||
{`x=00:00:00`, `{"x": 00:00:00}`, ``},
|
||||
{`x=23:59:59`, `{"x": 23:59:59}`, ``},
|
||||
{`x=12:10:08.12121212121212`, `{"x": 12:10:08.121212121}`, ``},
|
||||
|
|
|
|||
|
|
@ -71,18 +71,18 @@ var (
|
|||
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
|
||||
return ast.NewValue(ast.TypeFloat, p.Result.Tokens[0].Value.(float64)), true
|
||||
case p.Accept(nan):
|
||||
return ast.NewValue(ast.TypeFloat, math.NaN()), true
|
||||
case p.Accept(inf):
|
||||
if p.Result().Rune(0) == '-' {
|
||||
if p.Result.Runes[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
|
||||
return ast.NewValue(ast.TypeInteger, p.Result.Tokens[0].Value.(int64)), true
|
||||
default:
|
||||
p.Expected("a number")
|
||||
return nil, false
|
||||
|
|
@ -94,17 +94,17 @@ func (t *parser) parseIntegerStartingWithZero(p *parse.API) (*ast.Value, bool) {
|
|||
var err error
|
||||
switch {
|
||||
case p.Accept(hexadecimal):
|
||||
value, err = strconv.ParseInt(p.Result().Value(0).(string), 16, 64)
|
||||
value, err = strconv.ParseInt(p.Result.Tokens[0].Value.(string), 16, 64)
|
||||
case p.Accept(octal):
|
||||
value, err = strconv.ParseInt(p.Result().Value(0).(string), 8, 64)
|
||||
value, err = strconv.ParseInt(p.Result.Tokens[0].Value.(string), 8, 64)
|
||||
case p.Accept(binary):
|
||||
value, err = strconv.ParseInt(p.Result().Value(0).(string), 2, 64)
|
||||
value, err = strconv.ParseInt(p.Result.Tokens[0].Value.(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)
|
||||
p.Error("invalid integer value 0%s: %s", p.Result.String(), err)
|
||||
return nil, false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ func (t *parser) parseBasicString(name string, p *parse.API) (string, bool) {
|
|||
for {
|
||||
switch {
|
||||
case p.Peek(controlCharacter):
|
||||
p.Error("invalid character in %s: %q (must be escaped)", name, p.Result().Rune(0))
|
||||
p.Error("invalid character in %s: %q (must be escaped)", name, p.Result.Runes[0])
|
||||
return sb.String(), false
|
||||
case p.Accept(validEscape):
|
||||
if !appendEscapedRune(p, sb) {
|
||||
|
|
@ -108,7 +108,7 @@ func (t *parser) parseBasicString(name string, p *parse.API) (string, bool) {
|
|||
p.Error("invalid UTF8 rune")
|
||||
return sb.String(), false
|
||||
case p.Accept(a.ValidRune):
|
||||
sb.WriteString(p.Result().String())
|
||||
sb.WriteString(p.Result.String())
|
||||
default:
|
||||
p.Expected(`closing quotation marks`)
|
||||
return sb.String(), false
|
||||
|
|
@ -136,13 +136,13 @@ func (t *parser) parseLiteralString(name string, p *parse.API) (string, bool) {
|
|||
case p.Accept(a.Tab):
|
||||
sb.WriteString("\t")
|
||||
case p.Peek(controlCharacter):
|
||||
p.Error("invalid character in %s: %q (no control chars allowed, except for tab)", name, p.Result().Rune(0))
|
||||
p.Error("invalid character in %s: %q (no control chars allowed, except for tab)", name, p.Result.Runes[0])
|
||||
return sb.String(), false
|
||||
case p.Peek(a.InvalidRune):
|
||||
p.Error("invalid UTF8 rune")
|
||||
return sb.String(), false
|
||||
case p.Accept(a.ValidRune):
|
||||
sb.WriteString(p.Result().String())
|
||||
sb.WriteString(p.Result.String())
|
||||
default:
|
||||
p.Expected("closing single quote")
|
||||
return sb.String(), false
|
||||
|
|
@ -184,7 +184,7 @@ func (t *parser) parseMultiLineBasicString(p *parse.API) (string, bool) {
|
|||
case p.Accept(newline):
|
||||
sb.WriteString("\n")
|
||||
case p.Peek(controlCharacter):
|
||||
p.Error("invalid character in multi-line basic string: %q (must be escaped)", p.Result().Rune(0))
|
||||
p.Error("invalid character in multi-line basic string: %q (must be escaped)", p.Result.Runes[0])
|
||||
return sb.String(), false
|
||||
case p.Accept(validEscape):
|
||||
if !appendEscapedRune(p, sb) {
|
||||
|
|
@ -198,7 +198,7 @@ func (t *parser) parseMultiLineBasicString(p *parse.API) (string, bool) {
|
|||
case p.Accept(m.Drop(doubleQuote3)):
|
||||
return sb.String(), true
|
||||
case p.Accept(a.ValidRune):
|
||||
sb.WriteString(p.Result().String())
|
||||
sb.WriteString(p.Result.String())
|
||||
case p.Peek(a.InvalidRune):
|
||||
p.Error("invalid UTF8 rune")
|
||||
return sb.String(), false
|
||||
|
|
@ -210,7 +210,7 @@ func (t *parser) parseMultiLineBasicString(p *parse.API) (string, bool) {
|
|||
}
|
||||
|
||||
func appendEscapedRune(p *parse.API, sb *strings.Builder) bool {
|
||||
s := p.Result().String()
|
||||
s := p.Result.String()
|
||||
switch s {
|
||||
case `\b`:
|
||||
sb.WriteRune('\b')
|
||||
|
|
@ -268,10 +268,10 @@ func (t *parser) parseMultiLineLiteralString(p *parse.API) (string, bool) {
|
|||
case p.Accept(newline):
|
||||
sb.WriteString("\n")
|
||||
case p.Peek(controlCharacter):
|
||||
p.Error("invalid character in literal string: %q (no control chars allowed, except for tab and newline)", p.Result().Rune(0))
|
||||
p.Error("invalid character in literal string: %q (no control chars allowed, except for tab and newline)", p.Result.Runes[0])
|
||||
return sb.String(), false
|
||||
case p.Accept(a.ValidRune):
|
||||
sb.WriteString(p.Result().String())
|
||||
sb.WriteString(p.Result.String())
|
||||
case p.Peek(a.InvalidRune):
|
||||
p.Error("invalid UTF8 rune")
|
||||
return sb.String(), false
|
||||
|
|
|
|||
|
|
@ -12,20 +12,27 @@ import (
|
|||
"github.com/pkg/profile"
|
||||
)
|
||||
|
||||
const runProfiler = false
|
||||
|
||||
func main() {
|
||||
bytes, _ := ioutil.ReadAll(os.Stdin)
|
||||
input := string(bytes)
|
||||
|
||||
toml := BuildGrammar()
|
||||
|
||||
t := profile.Start() //profile.CPUProfile)
|
||||
var result *tokenize.Result
|
||||
var result *tokenize.API
|
||||
var err error
|
||||
for i := 0; i < 10; i++ {
|
||||
|
||||
if runProfiler {
|
||||
t := profile.Start() //profile.CPUProfile)
|
||||
for i := 0; i < 20; i++ {
|
||||
fmt.Printf(".")
|
||||
result, err = toml.Match(input)
|
||||
}
|
||||
t.Stop()
|
||||
} else {
|
||||
result, err = toml.Match(input)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Error in parsing TOML: %s\n", err)
|
||||
|
|
@ -49,7 +56,7 @@ func (g Grammar) Get(name string) tokenize.Handler {
|
|||
if handler, ok := g[name]; ok {
|
||||
return handler
|
||||
}
|
||||
return func(t tokenize.API) bool {
|
||||
return func(t *tokenize.API) bool {
|
||||
if handler, ok := g[name]; ok {
|
||||
return handler(t)
|
||||
}
|
||||
|
|
@ -186,8 +193,8 @@ func BuildGrammar() tokenize.Handler {
|
|||
|
||||
R("float", c.Any(
|
||||
tok.Float64("float", G("standard-float")),
|
||||
tok.ByCallback("float", G("inf-float"), func(t tokenize.API) interface{} {
|
||||
if t.Result().Rune(0) == '-' {
|
||||
tok.ByCallback("float", G("inf-float"), func(t *tokenize.API) interface{} {
|
||||
if t.Rune(0) == '-' {
|
||||
return math.Inf(-1)
|
||||
}
|
||||
return math.Inf(+1)
|
||||
|
|
@ -229,14 +236,14 @@ func BuildGrammar() tokenize.Handler {
|
|||
R("local-date", G("date"))
|
||||
R("local-time", G("time"))
|
||||
|
||||
makeDateTimeValue := func(t tokenize.API) interface{} {
|
||||
makeDateTimeValue := func(t *tokenize.API) interface{} {
|
||||
layout := ""
|
||||
input := ""
|
||||
for _, t := range t.Result().Tokens() {
|
||||
for _, t := range t.Tokens() {
|
||||
layout += t.Type.(string)
|
||||
input += t.Value.(string)
|
||||
}
|
||||
t.Result().ClearTokens()
|
||||
t.ClearTokens()
|
||||
value, err := time.Parse(layout, input)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Ow, we must implement a way to report date parse errors: %s", err))
|
||||
|
|
|
|||
291
parse2/long.toml
291
parse2/long.toml
|
|
@ -9999,6 +9999,297 @@
|
|||
# --------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# From: https://github.com/toml-lang/toml/blob/master/examples/example-v0.3.0.toml
|
||||
################################################################################
|
||||
## Comment
|
||||
|
||||
# Speak your mind with the hash symbol. They go from the symbol to the end of
|
||||
# the line.
|
||||
|
||||
|
||||
################################################################################
|
||||
## Table
|
||||
|
||||
# Tables (also known as hash tables or dictionaries) are collections of
|
||||
# key/value pairs. They appear in square brackets on a line by themselves.
|
||||
|
||||
[table]
|
||||
|
||||
key = "value" # Yeah, you can do this.
|
||||
|
||||
# Nested tables are denoted by table names with dots in them. Name your tables
|
||||
# whatever crap you please, just don't use #, ., [ or ].
|
||||
|
||||
[table.subtable]
|
||||
|
||||
key = "another value"
|
||||
|
||||
# You don't need to specify all the super-tables if you don't want to. TOML
|
||||
# knows how to do it for you.
|
||||
|
||||
# [x] you
|
||||
# [x.y] don't
|
||||
# [x.y.z] need these
|
||||
[x.y.z.w] # for this to work
|
||||
|
||||
|
||||
################################################################################
|
||||
## Inline Table
|
||||
|
||||
# Inline tables provide a more compact syntax for expressing tables. They are
|
||||
# especially useful for grouped data that can otherwise quickly become verbose.
|
||||
# Inline tables are enclosed in curly braces `{` and `}`. No newlines are
|
||||
# allowed between the curly braces unless they are valid within a value.
|
||||
|
||||
[table.inline]
|
||||
|
||||
name = { first = "Tom", last = "Preston-Werner" }
|
||||
point = { x = 1, y = 2 }
|
||||
|
||||
|
||||
################################################################################
|
||||
## String
|
||||
|
||||
# There are four ways to express strings: basic, multi-line basic, literal, and
|
||||
# multi-line literal. All strings must contain only valid UTF-8 characters.
|
||||
|
||||
[string.basic]
|
||||
|
||||
basic = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF."
|
||||
|
||||
[string.multiline]
|
||||
|
||||
# The following strings are byte-for-byte equivalent:
|
||||
key1 = "One\nTwo"
|
||||
key2 = """One\nTwo"""
|
||||
key3 = """
|
||||
One
|
||||
Two"""
|
||||
|
||||
[string.multiline.continued]
|
||||
|
||||
# The following strings are byte-for-byte equivalent:
|
||||
key1 = "The quick brown fox jumps over the lazy dog."
|
||||
|
||||
key2 = """
|
||||
The quick brown \
|
||||
fox jumps over \
|
||||
the lazy dog."""
|
||||
|
||||
key3 = """\
|
||||
The quick brown \
|
||||
fox jumps over \
|
||||
the lazy dog.\
|
||||
"""
|
||||
|
||||
[string.literal]
|
||||
|
||||
# What you see is what you get.
|
||||
winpath = 'C:\Users\nodejs\templates'
|
||||
winpath2 = '\\ServerX\admin$\system32\'
|
||||
quoted = 'Tom "Dubs" Preston-Werner'
|
||||
regex = '<\i\c*\s*>'
|
||||
|
||||
|
||||
[string.literal.multiline]
|
||||
|
||||
regex2 = '''I [dw]on't need \d{2} apples'''
|
||||
lines = '''
|
||||
The first newline is
|
||||
trimmed in raw strings.
|
||||
All other whitespace
|
||||
is preserved.
|
||||
'''
|
||||
|
||||
|
||||
################################################################################
|
||||
## Integer
|
||||
|
||||
# Integers are whole numbers. Positive numbers may be prefixed with a plus sign.
|
||||
# Negative numbers are prefixed with a minus sign.
|
||||
|
||||
[integer]
|
||||
|
||||
key1 = +99
|
||||
key2 = 42
|
||||
key3 = 0
|
||||
key4 = -17
|
||||
|
||||
[integer.underscores]
|
||||
|
||||
# For large numbers, you may use underscores to enhance readability. Each
|
||||
# underscore must be surrounded by at least one digit.
|
||||
key1 = 1_000
|
||||
key2 = 5_349_221
|
||||
key3 = 1_2_3_4_5 # valid but inadvisable
|
||||
|
||||
|
||||
################################################################################
|
||||
## Float
|
||||
|
||||
# A float consists of an integer part (which may be prefixed with a plus or
|
||||
# minus sign) followed by a fractional part and/or an exponent part.
|
||||
|
||||
[float.fractional]
|
||||
|
||||
key1 = +1.0
|
||||
key2 = 3.1415
|
||||
key3 = -0.01
|
||||
|
||||
[float.exponent]
|
||||
|
||||
key1 = 5e+22
|
||||
key2 = 1e6
|
||||
key3 = -2E-2
|
||||
|
||||
[float.both]
|
||||
|
||||
key = 6.626e-34
|
||||
|
||||
[float.underscores]
|
||||
|
||||
key1 = 9_224_617.445_991_228_313
|
||||
key2 = 1e1_00 # modified by mmakaay, because 1e1000 yields an out of range error
|
||||
|
||||
################################################################################
|
||||
## Boolean
|
||||
|
||||
# Booleans are just the tokens you're used to. Always lowercase.
|
||||
|
||||
[boolean]
|
||||
|
||||
True = true
|
||||
False = false
|
||||
|
||||
|
||||
################################################################################
|
||||
## Datetime
|
||||
|
||||
# Datetimes are RFC 3339 dates.
|
||||
|
||||
[datetime]
|
||||
|
||||
key1 = 1979-05-27T07:32:00Z
|
||||
key2 = 1979-05-27T00:32:00-07:00
|
||||
key3 = 1979-05-27T00:32:00.999999-07:00
|
||||
|
||||
|
||||
################################################################################
|
||||
## Array
|
||||
|
||||
# Arrays are square brackets with other primitives inside. Whitespace is
|
||||
# ignored. Elements are separated by commas. Data types may not be mixed.
|
||||
|
||||
[array]
|
||||
|
||||
key1 = [ 1, 2, 3 ]
|
||||
key2 = [ "red", "yellow", "green" ]
|
||||
key3 = [ [ 1, 2 ], [3, 4, 5] ]
|
||||
key4 = [ [ 1, 2 ], ["a", "b", "c"] ] # this is ok
|
||||
|
||||
# Arrays can also be multiline. So in addition to ignoring whitespace, arrays
|
||||
# also ignore newlines between the brackets. Terminating commas are ok before
|
||||
# the closing bracket.
|
||||
|
||||
key5 = [
|
||||
1, 2, 3
|
||||
]
|
||||
key6 = [
|
||||
1,
|
||||
2, # this is ok
|
||||
]
|
||||
|
||||
|
||||
################################################################################
|
||||
## Array of Tables
|
||||
|
||||
# These can be expressed by using a table name in double brackets. Each table
|
||||
# with the same double bracketed name will be an element in the array. The
|
||||
# tables are inserted in the order encountered.
|
||||
|
||||
[[products]]
|
||||
|
||||
name = "Hammer"
|
||||
sku = 738594937
|
||||
|
||||
[[products]]
|
||||
|
||||
[[products]]
|
||||
|
||||
name = "Nail"
|
||||
sku = 284758393
|
||||
color = "gray"
|
||||
|
||||
|
||||
# You can create nested arrays of tables as well.
|
||||
|
||||
[[fruit]]
|
||||
name = "apple"
|
||||
|
||||
[fruit.physical]
|
||||
color = "red"
|
||||
shape = "round"
|
||||
|
||||
[[fruit.variety]]
|
||||
name = "red delicious"
|
||||
|
||||
[[fruit.variety]]
|
||||
name = "granny smith"
|
||||
|
||||
[[fruit]]
|
||||
name = "banana"
|
||||
|
||||
[[fruit.variety]]
|
||||
name = "plantain"
|
||||
|
||||
[int.special]
|
||||
|
||||
no1 = []
|
||||
no2 = [ ]
|
||||
no3 = [
|
||||
]
|
||||
go1 = [1,2,3,]
|
||||
go2 = [
|
||||
1,
|
||||
2,
|
||||
3,]
|
||||
go3 = [ 1, #one
|
||||
2, #two
|
||||
3 #three
|
||||
]
|
||||
|
||||
go4 =[
|
||||
|
||||
|
||||
|
||||
1 ,
|
||||
|
||||
|
||||
|
||||
2,
|
||||
|
||||
|
||||
|
||||
|
||||
3,
|
||||
|
||||
|
||||
|
||||
# hi
|
||||
# hi
|
||||
# hi
|
||||
]
|
||||
|
||||
k="kaka"
|
||||
|
||||
[date.types]
|
||||
the.d1=2019-01-01
|
||||
the.d2=2019-01-01 12:12:12
|
||||
the.d3=2019-01-01T12:12:12
|
||||
the.d4=2019-01-01T12:12:12Z
|
||||
the.d5=2019-01-01 12:12:12Z
|
||||
the.d6=2019-01-01 12:12:12+03:45
|
||||
|
||||
# --------------------------------------------------------------------------------
|
||||
# --------------------------------------------------------------------------------
|
||||
# --------------------------------------------------------------------------------
|
||||
|
|
|
|||
BIN
parse2/parse2
BIN
parse2/parse2
Binary file not shown.
Loading…
Reference in New Issue