156 p.raiseError(key, "invalid table array key: %s", err) |
155 p.raiseError(key, "invalid table array key: %s", err) |
157 } |
156 } |
158 if err := p.tree.createSubTree(keys, startToken.Position); err != nil { |
157 if err := p.tree.createSubTree(keys, startToken.Position); err != nil { |
159 p.raiseError(key, "%s", err) |
158 p.raiseError(key, "%s", err) |
160 } |
159 } |
|
160 destTree := p.tree.GetPath(keys) |
|
161 if target, ok := destTree.(*Tree); ok && target != nil && target.inline { |
|
162 p.raiseError(key, "could not re-define exist inline table or its sub-table : %s", |
|
163 strings.Join(keys, ".")) |
|
164 } |
161 p.assume(tokenRightBracket) |
165 p.assume(tokenRightBracket) |
162 p.currentTable = keys |
166 p.currentTable = keys |
163 return p.parseStart |
167 return p.parseStart |
164 } |
168 } |
165 |
169 |
199 default: |
203 default: |
200 p.raiseError(key, "Unknown table type for path: %s", |
204 p.raiseError(key, "Unknown table type for path: %s", |
201 strings.Join(tableKey, ".")) |
205 strings.Join(tableKey, ".")) |
202 } |
206 } |
203 |
207 |
|
208 if targetNode.inline { |
|
209 p.raiseError(key, "could not add key or sub-table to exist inline table or its sub-table : %s", |
|
210 strings.Join(tableKey, ".")) |
|
211 } |
|
212 |
204 // assign value to the found table |
213 // assign value to the found table |
205 keyVal := parsedKey[len(parsedKey)-1] |
214 keyVal := parsedKey[len(parsedKey)-1] |
206 localKey := []string{keyVal} |
215 localKey := []string{keyVal} |
207 finalKey := append(tableKey, keyVal) |
216 finalKey := append(tableKey, keyVal) |
208 if targetNode.GetPath(localKey) != nil { |
217 if targetNode.GetPath(localKey) != nil { |
219 } |
228 } |
220 targetNode.values[keyVal] = toInsert |
229 targetNode.values[keyVal] = toInsert |
221 return p.parseStart |
230 return p.parseStart |
222 } |
231 } |
223 |
232 |
224 var numberUnderscoreInvalidRegexp *regexp.Regexp |
233 var errInvalidUnderscore = errors.New("invalid use of _ in number") |
225 var hexNumberUnderscoreInvalidRegexp *regexp.Regexp |
|
226 |
234 |
227 func numberContainsInvalidUnderscore(value string) error { |
235 func numberContainsInvalidUnderscore(value string) error { |
228 if numberUnderscoreInvalidRegexp.MatchString(value) { |
236 // For large numbers, you may use underscores between digits to enhance |
229 return errors.New("invalid use of _ in number") |
237 // readability. Each underscore must be surrounded by at least one digit on |
|
238 // each side. |
|
239 |
|
240 hasBefore := false |
|
241 for idx, r := range value { |
|
242 if r == '_' { |
|
243 if !hasBefore || idx+1 >= len(value) { |
|
244 // can't end with an underscore |
|
245 return errInvalidUnderscore |
|
246 } |
|
247 } |
|
248 hasBefore = isDigit(r) |
230 } |
249 } |
231 return nil |
250 return nil |
232 } |
251 } |
233 |
252 |
|
253 var errInvalidUnderscoreHex = errors.New("invalid use of _ in hex number") |
|
254 |
234 func hexNumberContainsInvalidUnderscore(value string) error { |
255 func hexNumberContainsInvalidUnderscore(value string) error { |
235 if hexNumberUnderscoreInvalidRegexp.MatchString(value) { |
256 hasBefore := false |
236 return errors.New("invalid use of _ in hex number") |
257 for idx, r := range value { |
|
258 if r == '_' { |
|
259 if !hasBefore || idx+1 >= len(value) { |
|
260 // can't end with an underscore |
|
261 return errInvalidUnderscoreHex |
|
262 } |
|
263 } |
|
264 hasBefore = isHexDigit(r) |
237 } |
265 } |
238 return nil |
266 return nil |
239 } |
267 } |
240 |
268 |
241 func cleanupNumberToken(value string) string { |
269 func cleanupNumberToken(value string) string { |
310 val, err := strconv.ParseFloat(cleanedVal, 64) |
338 val, err := strconv.ParseFloat(cleanedVal, 64) |
311 if err != nil { |
339 if err != nil { |
312 p.raiseError(tok, "%s", err) |
340 p.raiseError(tok, "%s", err) |
313 } |
341 } |
314 return val |
342 return val |
315 case tokenDate: |
343 case tokenLocalTime: |
316 layout := time.RFC3339Nano |
344 val, err := ParseLocalTime(tok.val) |
317 if !strings.Contains(tok.val, "T") { |
|
318 layout = strings.Replace(layout, "T", " ", 1) |
|
319 } |
|
320 val, err := time.ParseInLocation(layout, tok.val, time.UTC) |
|
321 if err != nil { |
345 if err != nil { |
322 p.raiseError(tok, "%s", err) |
346 p.raiseError(tok, "%s", err) |
323 } |
347 } |
324 return val |
348 return val |
325 case tokenLocalDate: |
349 case tokenLocalDate: |
326 v := strings.Replace(tok.val, " ", "T", -1) |
350 // a local date may be followed by: |
327 isDateTime := false |
351 // * nothing: this is a local date |
328 isTime := false |
352 // * a local time: this is a local date-time |
329 for _, c := range v { |
353 |
330 if c == 'T' || c == 't' { |
354 next := p.peek() |
331 isDateTime = true |
355 if next == nil || next.typ != tokenLocalTime { |
332 break |
356 val, err := ParseLocalDate(tok.val) |
333 } |
357 if err != nil { |
334 if c == ':' { |
358 p.raiseError(tok, "%s", err) |
335 isTime = true |
359 } |
336 break |
360 return val |
337 } |
361 } |
338 } |
362 |
339 |
363 localDate := tok |
340 var val interface{} |
364 localTime := p.getToken() |
341 var err error |
365 |
342 |
366 next = p.peek() |
343 if isDateTime { |
367 if next == nil || next.typ != tokenTimeOffset { |
344 val, err = ParseLocalDateTime(v) |
368 v := localDate.val + "T" + localTime.val |
345 } else if isTime { |
369 val, err := ParseLocalDateTime(v) |
346 val, err = ParseLocalTime(v) |
370 if err != nil { |
347 } else { |
371 p.raiseError(tok, "%s", err) |
348 val, err = ParseLocalDate(v) |
372 } |
349 } |
373 return val |
350 |
374 } |
|
375 |
|
376 offset := p.getToken() |
|
377 |
|
378 layout := time.RFC3339Nano |
|
379 v := localDate.val + "T" + localTime.val + offset.val |
|
380 val, err := time.ParseInLocation(layout, v, time.UTC) |
351 if err != nil { |
381 if err != nil { |
352 p.raiseError(tok, "%s", err) |
382 p.raiseError(tok, "%s", err) |
353 } |
383 } |
354 return val |
384 return val |
355 case tokenLeftBracket: |
385 case tokenLeftBracket: |
358 return p.parseInlineTable() |
388 return p.parseInlineTable() |
359 case tokenEqual: |
389 case tokenEqual: |
360 p.raiseError(tok, "cannot have multiple equals for the same key") |
390 p.raiseError(tok, "cannot have multiple equals for the same key") |
361 case tokenError: |
391 case tokenError: |
362 p.raiseError(tok, "%s", tok) |
392 p.raiseError(tok, "%s", tok) |
363 } |
393 default: |
364 |
394 panic(fmt.Errorf("unhandled token: %v", tok)) |
365 p.raiseError(tok, "never reached") |
395 } |
366 |
396 |
367 return nil |
397 return nil |
368 } |
398 } |
369 |
399 |
370 func tokenIsComma(t *token) bool { |
400 func tokenIsComma(t *token) bool { |
409 previous = follow |
439 previous = follow |
410 } |
440 } |
411 if tokenIsComma(previous) { |
441 if tokenIsComma(previous) { |
412 p.raiseError(previous, "trailing comma at the end of inline table") |
442 p.raiseError(previous, "trailing comma at the end of inline table") |
413 } |
443 } |
|
444 tree.inline = true |
414 return tree |
445 return tree |
415 } |
446 } |
416 |
447 |
417 func (p *tomlParser) parseArray() interface{} { |
448 func (p *tomlParser) parseArray() interface{} { |
418 var array []interface{} |
449 var array []interface{} |
419 arrayType := reflect.TypeOf(nil) |
450 arrayType := reflect.TypeOf(newTree()) |
420 for { |
451 for { |
421 follow := p.peek() |
452 follow := p.peek() |
422 if follow == nil || follow.typ == tokenEOF { |
453 if follow == nil || follow.typ == tokenEOF { |
423 p.raiseError(follow, "unterminated array") |
454 p.raiseError(follow, "unterminated array") |
424 } |
455 } |
425 if follow.typ == tokenRightBracket { |
456 if follow.typ == tokenRightBracket { |
426 p.getToken() |
457 p.getToken() |
427 break |
458 break |
428 } |
459 } |
429 val := p.parseRvalue() |
460 val := p.parseRvalue() |
430 if arrayType == nil { |
|
431 arrayType = reflect.TypeOf(val) |
|
432 } |
|
433 if reflect.TypeOf(val) != arrayType { |
461 if reflect.TypeOf(val) != arrayType { |
434 p.raiseError(follow, "mixed types in array") |
462 arrayType = nil |
435 } |
463 } |
436 array = append(array, val) |
464 array = append(array, val) |
437 follow = p.peek() |
465 follow = p.peek() |
438 if follow == nil || follow.typ == tokenEOF { |
466 if follow == nil || follow.typ == tokenEOF { |
439 p.raiseError(follow, "unterminated array") |
467 p.raiseError(follow, "unterminated array") |
442 p.raiseError(follow, "missing comma") |
470 p.raiseError(follow, "missing comma") |
443 } |
471 } |
444 if follow.typ == tokenComma { |
472 if follow.typ == tokenComma { |
445 p.getToken() |
473 p.getToken() |
446 } |
474 } |
|
475 } |
|
476 |
|
477 // if the array is a mixed-type array or its length is 0, |
|
478 // don't convert it to a table array |
|
479 if len(array) <= 0 { |
|
480 arrayType = nil |
447 } |
481 } |
448 // An array of Trees is actually an array of inline |
482 // An array of Trees is actually an array of inline |
449 // tables, which is a shorthand for a table array. If the |
483 // tables, which is a shorthand for a table array. If the |
450 // array was not converted from []interface{} to []*Tree, |
484 // array was not converted from []interface{} to []*Tree, |
451 // the two notations would not be equivalent. |
485 // the two notations would not be equivalent. |