2019-11-05 22:00:57 +00:00
|
|
|
package sii
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"bytes"
|
|
|
|
"io"
|
|
|
|
"reflect"
|
|
|
|
"regexp"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
)
|
|
|
|
|
|
|
|
var blockStartRegex = regexp.MustCompile(`^([^\s:]+)\s?:\s?([^\s]+)(?:\s?\{)?$`)
|
|
|
|
|
2019-12-24 13:59:42 +00:00
|
|
|
func ParseSIIPlainFile(r io.Reader) (*Unit, error) {
|
2019-11-05 22:00:57 +00:00
|
|
|
var (
|
|
|
|
blockContent []byte
|
|
|
|
blockName string
|
|
|
|
blockClass string
|
|
|
|
inBlock = false
|
|
|
|
inComment = false
|
|
|
|
inUnit = false
|
|
|
|
scanner = bufio.NewScanner(r)
|
|
|
|
unit = &Unit{}
|
|
|
|
)
|
|
|
|
|
|
|
|
for scanner.Scan() {
|
|
|
|
var line = strings.TrimSpace(scanner.Text())
|
|
|
|
|
|
|
|
switch {
|
|
|
|
|
|
|
|
case line == "{":
|
|
|
|
if !inUnit {
|
|
|
|
inUnit = true
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if !inBlock {
|
|
|
|
if blockClass == "" || blockName == "" {
|
|
|
|
return nil, errors.New("Unpexpected block open without unit class / name")
|
|
|
|
}
|
|
|
|
|
|
|
|
inBlock = true
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, errors.New("Unexpected opening braces")
|
|
|
|
|
|
|
|
case line == "}":
|
|
|
|
if inBlock {
|
2019-12-10 22:20:25 +00:00
|
|
|
if err := processBlock(unit, blockClass, blockName, bytes.Trim(blockContent, "\n")); err != nil {
|
2019-11-05 22:00:57 +00:00
|
|
|
return nil, errors.Wrap(err, "Unable to process block")
|
|
|
|
}
|
|
|
|
|
|
|
|
inBlock = false
|
|
|
|
blockClass = ""
|
2019-11-10 13:37:37 +00:00
|
|
|
blockContent = nil
|
2019-11-05 22:00:57 +00:00
|
|
|
blockName = ""
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if inUnit {
|
|
|
|
inUnit = false
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, errors.New("Unexpected closing braces")
|
|
|
|
|
2020-01-03 23:37:58 +00:00
|
|
|
case !inBlock && blockStartRegex.MatchString(line):
|
2019-11-05 22:00:57 +00:00
|
|
|
if !inUnit {
|
|
|
|
return nil, errors.New("Unexpected block start outside unit")
|
|
|
|
}
|
|
|
|
|
|
|
|
groups := blockStartRegex.FindStringSubmatch(line)
|
|
|
|
|
|
|
|
blockClass = groups[1]
|
|
|
|
blockName = groups[2]
|
|
|
|
|
|
|
|
if strings.HasSuffix(line, `{`) {
|
|
|
|
inBlock = true
|
|
|
|
}
|
|
|
|
|
|
|
|
case (strings.HasPrefix(line, `\*`) && strings.HasSuffix(line, `*\`)) || strings.HasPrefix(line, `#`) || strings.HasPrefix(line, `//`):
|
|
|
|
// one-line-comment, just drop
|
|
|
|
|
|
|
|
case strings.HasPrefix(line, `/*`):
|
|
|
|
inComment = true
|
|
|
|
|
|
|
|
case strings.HasSuffix(line, `*/`):
|
|
|
|
inComment = false
|
|
|
|
|
2019-11-05 22:35:45 +00:00
|
|
|
case strings.HasPrefix(line, `@include`):
|
|
|
|
return nil, errors.New("File uses includes (unsupported feature)")
|
|
|
|
|
2019-11-05 22:00:57 +00:00
|
|
|
default:
|
|
|
|
if inComment {
|
|
|
|
// Inside multi-line-comment, just drop
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if !inBlock {
|
|
|
|
// Outside block, drop line
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// Append line to block content
|
|
|
|
blockContent = bytes.Join([][]byte{
|
|
|
|
blockContent,
|
|
|
|
scanner.Bytes(),
|
|
|
|
}, []byte{'\n'})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if scanner.Err() != nil {
|
|
|
|
return nil, errors.Wrap(scanner.Err(), "Unable to scan file")
|
|
|
|
}
|
|
|
|
|
|
|
|
return unit, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func processBlock(unit *Unit, blockClass, blockName string, blockContent []byte) error {
|
|
|
|
block := getBlockInstance(blockClass)
|
|
|
|
block.Init(blockClass, blockName)
|
|
|
|
|
|
|
|
var err error
|
|
|
|
if reflect.TypeOf(block).Implements(reflect.TypeOf((*Unmarshaler)(nil)).Elem()) {
|
|
|
|
err = block.(Unmarshaler).UnmarshalSII(blockContent)
|
|
|
|
} else {
|
2019-11-10 14:03:08 +00:00
|
|
|
err = genericUnmarshal(blockContent, block, unit)
|
2019-11-05 22:00:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "Unable to unmarshal block content")
|
|
|
|
}
|
|
|
|
|
|
|
|
unit.Entries = append(unit.Entries, block)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|