@@ -132,7 +132,7 @@ func Compile(in string, vc VariableCompiler) (StringFormatter, error) {
132132}
133133
134134func compile (ctx * compileCtx , in string ) (StringFormatter , error ) {
135- lexer := makeLexer (in )
135+ lexer := MakeLexer (in )
136136 defer lexer .Finish ()
137137
138138 // parse format string
@@ -265,33 +265,44 @@ func (e variableElement) compile(ctx *compileCtx) (FormatEvaler, error) {
265265 return ctx .compileVariable (e .field , e .ops )
266266}
267267
268- func parse (lex lexer ) ([]formatElement , error ) {
269- var elems []formatElement
268+ // parseFormatTokens runs the shared lexer-driven loop
269+ func parseFormatTokens [T any ](
270+ lex lexer ,
271+ appendLiteral func (* []T , string ),
272+ parseVar func (lexer ) (T , error ),
273+ ) ([]T , error ) {
274+ var elems []T
270275
271276 for token := range lex .Tokens () {
272277 switch token .typ {
273278 case tokErr :
274279 return nil , errors .New (token .val )
275280
276281 case tokString :
277- elems = append ( elems , StringElement { token .val } )
282+ appendLiteral ( & elems , token .val )
278283
279284 case tokOpen :
280- elem , err := parseVariable (lex )
285+ elem , err := parseVar (lex )
281286 if err != nil {
282287 return nil , err
283288 }
284289 elems = append (elems , elem )
285290
286291 case tokClose , tokOperator :
287292 // should not happen, but let's return error just in case
288- return nil , fmt .Errorf ("Token '%v'(%v) not allowed" , token .val , token .typ )
293+ return nil , fmt .Errorf ("token '%v'(%v) not allowed" , token .val , token .typ )
289294 }
290295 }
291296
292297 return elems , nil
293298}
294299
300+ func parse (lex lexer ) ([]formatElement , error ) {
301+ return parseFormatTokens (lex , func (elems * []formatElement , s string ) {
302+ * elems = append (* elems , StringElement {s })
303+ }, parseVariable )
304+ }
305+
295306func parseVariable (lex lexer ) (formatElement , error ) {
296307 var strings []string
297308 var ops []string
@@ -312,7 +323,7 @@ func parseVariable(lex lexer) (formatElement, error) {
312323
313324 case tokString :
314325 if len (strings ) != len (ops ) {
315- return nil , fmt .Errorf ("Unexpected string token %v, expected operator" , token .val )
326+ return nil , fmt .Errorf ("unexpected string token %v, expected operator" , token .val )
316327 }
317328 strings = append (strings , token .val )
318329
@@ -322,18 +333,82 @@ func parseVariable(lex lexer) (formatElement, error) {
322333 }
323334 ops = append (ops , token .val )
324335 if len (ops ) > len (strings ) {
325- return nil , fmt .Errorf ("Consecutive operator tokens '%v'" , token .val )
336+ return nil , fmt .Errorf ("consecutive operator tokens '%v'" , token .val )
326337 }
327338
328339 default :
329- return nil , fmt .Errorf ("Unexpected token '%v' (%v)" , token .val , token .typ )
340+ return nil , fmt .Errorf ("unexpected token '%v' (%v)" , token .val , token .typ )
330341 }
331342 }
332343
333344 return nil , errMissingClose
334345}
335346
336- func makeLexer (in string ) lexer {
347+ type VariableToken string
348+
349+ // ParseRawTokens returns a slice of tokens as they occur.
350+ // variable tokens are stored as typed value
351+ func ParseRawTokens (lex lexer ) ([]any , error ) {
352+ return parseFormatTokens (lex , func (elems * []any , s string ) {
353+ * elems = append (* elems , s )
354+ }, func (lex lexer ) (any , error ) {
355+ s , err := parseVariableToken (lex )
356+ if err != nil {
357+ return nil , err
358+ }
359+ return VariableToken (s ), nil
360+ })
361+ }
362+
363+ // parseVariableToken consumes lexer tokens inside %{...} and returns the full inner
364+ // expression as one string e.g. "unknown:default".
365+ // Callers must parse operators if needed
366+ func parseVariableToken (lex lexer ) (string , error ) {
367+ // finalValue appends each string chunk and operator as they appear.
368+ var finalValue string
369+ var strings []string
370+ var ops []string
371+
372+ for token := range lex .Tokens () {
373+ switch token .typ {
374+ case tokErr :
375+ return "" , errors .New (token .val )
376+
377+ case tokOpen :
378+ return "" , errNestedVar
379+
380+ case tokClose :
381+ if len (strings ) == 0 {
382+ return "" , errEmptyFormat
383+ }
384+ return finalValue , nil
385+
386+ case tokString :
387+ if len (strings ) != len (ops ) {
388+ return "" , fmt .Errorf ("unexpected string token %v, expected operator" , token .val )
389+ }
390+ strings = append (strings , token .val )
391+ finalValue += token .val
392+
393+ case tokOperator :
394+ if len (strings ) == 0 {
395+ return "" , errUnexpectedOperator
396+ }
397+ ops = append (ops , token .val )
398+ if len (ops ) > len (strings ) {
399+ return "" , fmt .Errorf ("consecutive operator tokens '%v'" , token .val )
400+ }
401+ finalValue += token .val
402+
403+ default :
404+ return "" , fmt .Errorf ("unexpected token '%v' (%v)" , token .val , token .typ )
405+ }
406+ }
407+
408+ return "" , errMissingClose
409+ }
410+
411+ func MakeLexer (in string ) lexer {
337412 lex := make (chan token , 1 )
338413
339414 go func () {
@@ -359,7 +434,10 @@ func makeLexer(in string) lexer {
359434
360435 varcount := 0
361436 for len (content ) > 0 {
362- idx := - 1
437+ if off > len (content ) {
438+ return
439+ }
440+ var idx int
363441 if varcount == 0 {
364442 idx = strings .IndexAny (content [off :], `%\` )
365443 } else {
@@ -387,7 +465,8 @@ func makeLexer(in string) lexer {
387465 op := ":"
388466 if strings .ContainsRune ("!@#&*=+<>?" , rune (content [off ])) {
389467 off ++
390- op = content [idx : off + 1 ]
468+ // Two-byte op e.g. ":!"
469+ op = content [idx :off ]
391470 }
392471 lex <- opToken (op )
393472
0 commit comments