go.uber.org/yarpc@v1.72.1/internal/interpolate/parse.rl (about) 1 package interpolate 2 3 import "fmt" 4 5 %%{ 6 machine interpolate; 7 write data; 8 }%% 9 10 // Parse parses a string for interpolation. 11 // 12 // Variables may be specified anywhere in the string in the format ${foo} or 13 // ${foo:default} where 'default' will be used if the variable foo was unset. 14 func Parse(data string) (out String, _ error) { 15 var ( 16 // Variables used by Ragel 17 cs = 0 // current state 18 p = 0 // current position in data 19 pe = len(data) 20 eof = pe // eof == pe if this is the last data block 21 22 // We use the following variables to actually build the String. 23 24 // Index in data where the currently captured string started. 25 idx int 26 27 v variable // variable being read, if any 28 l literal // literal being read, if any 29 30 // Current term. This is either the variable that we just read or the 31 // literal. We will append it to `out` and move on. 32 t term 33 ) 34 35 %%{ 36 # Record the current position as the start of a string. This is 37 # usually used with the entry transition (>) to start capturing the 38 # string when a state machine is entered. 39 # 40 # fpc is the current position in the string (basically the same as the 41 # variable `p` but a special Ragel keyword) so after executing 42 # `start`, data[idx:fpc+1] is the string from when start was called to 43 # the current position (inclusive). 44 action start { idx = fpc } 45 46 # A variable always starts with an alphabet or an underscore and 47 # contains alphanumeric characters, underscores, and non-consecutive 48 # dots or dashes. 49 var_name 50 = ( [a-zA-Z_] ([a-zA-Z0-9_] 51 | ('.' | '-') [a-zA-Z0-9_])* 52 ) 53 >start 54 @{ v.Name = data[idx:fpc+1] } 55 ; 56 57 var_default 58 = (any - '}')* >start @{ v.Default = data[idx:fpc+1] }; 59 60 # var is a reference to a variable and optionally has a default value 61 # for that variable. 62 var = '${' var_name (':' @{ v.HasDefault = true } var_default)? '}' 63 ; 64 65 # Anything followed by a '\' is used as-is. 66 escaped_lit = '\\' any @{ l = literal(data[fpc:fpc+1]) }; 67 68 # Anything followed by a '$' that is not a '{' is used as-is with the 69 # dollar. 70 dollar_lit = '$' (any - '{') @{ l = literal(data[fpc-1:fpc+1]) }; 71 72 # Literal strings that don't contain '$' or '\'. 73 simple_lit 74 = (any - '$' - '\\')+ 75 >start 76 @{ l = literal(data[idx:fpc + 1]) } 77 ; 78 79 lit = escaped_lit | dollar_lit | simple_lit; 80 81 # Terms are the two possible components in a string. Either a literal 82 # or a variable reference. 83 term = (var @{ t = v }) | (lit @{ t = l }); 84 85 main := (term %{ out = append(out, t) })**; 86 87 write init; 88 write exec; 89 }%% 90 91 if cs < %%{ write first_final; }%% { 92 return out, fmt.Errorf("cannot parse string %q", data) 93 } 94 95 return out, nil 96 }