Optimization round completed (for now :-) All tests successful.

This commit is contained in:
Maurice Makaay 2019-07-11 12:43:27 +00:00
parent 30a0d80d84
commit be5f3b6f55
8 changed files with 330 additions and 33 deletions

View File

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

View File

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

View File

@ -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}`, ``},

View File

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

View File

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

View File

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

View File

@ -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
# --------------------------------------------------------------------------------
# --------------------------------------------------------------------------------
# --------------------------------------------------------------------------------

Binary file not shown.