117 lines
2.4 KiB
Go
117 lines
2.4 KiB
Go
|
// scanner.go
|
||
|
package text
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
type ScannerFlag uint16
|
||
|
|
||
|
const (
|
||
|
ValueRequired = ScannerFlag(1 << iota)
|
||
|
DirectValue
|
||
|
EncryptValue
|
||
|
)
|
||
|
|
||
|
// Flags symbols in the abova iota order
|
||
|
const flagSymbols = "!=*"
|
||
|
|
||
|
type VariableHandler interface {
|
||
|
Handle(varSpec string, flags ScannerFlag) (value string, err error)
|
||
|
}
|
||
|
|
||
|
func Scan(handler VariableHandler, source string) (result string, err error) {
|
||
|
var spec, value string
|
||
|
var flags ScannerFlag
|
||
|
var backSlash bool
|
||
|
var sb strings.Builder
|
||
|
offset := 0
|
||
|
for offset < len(source) {
|
||
|
ch := source[offset]
|
||
|
offset++
|
||
|
if backSlash {
|
||
|
switch ch {
|
||
|
case '\\':
|
||
|
sb.WriteByte('\\')
|
||
|
case varIntro:
|
||
|
sb.WriteByte(varIntro)
|
||
|
default:
|
||
|
sb.WriteByte(ch)
|
||
|
}
|
||
|
backSlash = false
|
||
|
} else if ch == '\\' {
|
||
|
backSlash = true
|
||
|
} else if ch == varIntro {
|
||
|
if spec, flags, offset, err = getVarSpec(source, offset); err != nil {
|
||
|
//err = fmt.Errorf("Source:\n%s", source)
|
||
|
return
|
||
|
}
|
||
|
if handler != nil {
|
||
|
if value, err = handler.Handle(spec, flags); err != nil {
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
sb.WriteString(value)
|
||
|
} else {
|
||
|
sb.WriteByte(ch)
|
||
|
}
|
||
|
}
|
||
|
result = sb.String()
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func getVarSpec(source string, startOffset int) (spec string, flags ScannerFlag, offset int, err error) {
|
||
|
var ch byte
|
||
|
var lastOpenBrace int = -1
|
||
|
|
||
|
braceCount := 0
|
||
|
if startOffset < len(source) && source[startOffset] == '{' {
|
||
|
lastOpenBrace = startOffset
|
||
|
braceCount++
|
||
|
startOffset++
|
||
|
}
|
||
|
count := 0
|
||
|
beginning := true
|
||
|
for offset = startOffset; offset < len(source); offset++ {
|
||
|
ch = source[offset]
|
||
|
if ch == '{' && braceCount > 0 {
|
||
|
lastOpenBrace = offset
|
||
|
count++
|
||
|
braceCount++
|
||
|
} else if ch == '}' {
|
||
|
braceCount--
|
||
|
if braceCount == 0 {
|
||
|
break
|
||
|
}
|
||
|
count++
|
||
|
} else if braceCount == 0 && strings.IndexByte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_", ch) < 0 {
|
||
|
break
|
||
|
} else if braceCount == 1 && beginning {
|
||
|
if flagIndex := strings.IndexByte(flagSymbols, ch); flagIndex >= 0 {
|
||
|
startOffset++
|
||
|
flags |= ScannerFlag(1 << flagIndex)
|
||
|
} else {
|
||
|
count++
|
||
|
beginning = false
|
||
|
}
|
||
|
} else {
|
||
|
count++
|
||
|
beginning = false
|
||
|
}
|
||
|
}
|
||
|
if err == nil {
|
||
|
if braceCount > 0 {
|
||
|
err = fmt.Errorf("unbalanced open braces at offset %d", lastOpenBrace)
|
||
|
} else {
|
||
|
if offset < len(source) && ch == '}' {
|
||
|
offset++
|
||
|
}
|
||
|
//fmt.Println("count:", count)
|
||
|
spec = source[startOffset : startOffset+count]
|
||
|
}
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|