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