github.com/hirochachacha/plua@v0.0.0-20170217012138-c82f520cc725/internal/pattern/pattern.go (about) 1 package pattern 2 3 import ( 4 "bytes" 5 "errors" 6 7 "github.com/hirochachacha/plua/object" 8 ) 9 10 /* 11 12 pattern : '^'? fragment* '$'? 13 fragment : repetition | capture | captured | frontier | balance 14 repetition : single 15 | single '*' 16 | single '+' 17 | single '-' 18 | single '?' 19 capture : '(' fragment* ')' 20 captured : '%' <digit> 21 frontier : '%f' set 22 balance : '%b' char char 23 single : simple | set 24 simple : char | class 25 set : '[' '^'? set-item+ ']' 26 set-item : range | simple 27 range : char '-' char 28 char : dot | <character excluding ".()%[]"> | '%' <non-alphanumeric character> 29 dot : '.' 30 class : '%a' | '%c' | ... 31 32 */ 33 34 var decodeRune = _decodeByte 35 var lastDecodeRune = _lastDecodeByte 36 37 // var decodeRune = _decodeRune 38 // var lastDecodeRune = _lastDecodeRune 39 40 var ( 41 errInvalidEscape = errors.New("invalid escape") 42 errIncompleteFrontier = errors.New("missing '[' after '%f' in pattern") 43 errUnfinishedCapture = errors.New("unfinished capture") 44 errInvalidPatternCapture = errors.New("invalid pattern capture") 45 errMalformedSet = errors.New("malformed pattern (missing ']')") 46 errMalformedBalance = errors.New("malformed pattern (missing arguments to '%b')") 47 errMalformedEscape = errors.New("malformed pattern (ends with '%')") 48 ) 49 50 type matchType int 51 52 const ( 53 matchPrefix matchType = 1 << iota 54 matchSuffix 55 ) 56 57 const ( 58 eos = -1 59 sos = -2 60 ) 61 62 type Capture struct { 63 Begin int 64 End int 65 IsEmpty bool 66 } 67 68 func (cap Capture) Value(input string) object.Value { 69 if cap.IsEmpty { 70 return object.Integer(cap.Begin + 1) 71 } 72 return object.String(input[cap.Begin:cap.End]) 73 } 74 75 // IR for input pattern 76 type Pattern struct { 77 typ matchType 78 prefix string 79 code []instruction 80 sets []*set 81 nsaved int 82 } 83 84 func (p *Pattern) FindIndex(input string, off int) []Capture { 85 if len(input) < off { 86 return nil 87 } 88 m := &machine{ 89 saved: make([]Capture, p.nsaved), 90 } 91 if m.match(p, input, off) { 92 return m.saved 93 } 94 return nil 95 } 96 97 func (p *Pattern) FindAllIndex(input string, n int) [][]Capture { 98 if n == 0 { 99 return nil 100 } 101 102 var rets [][]Capture 103 104 if n > 0 { 105 rets = make([][]Capture, 0, n) 106 } 107 108 m := &machine{ 109 saved: make([]Capture, p.nsaved), 110 } 111 112 prev := -1 113 114 off := 0 115 for { 116 if !m.match(p, input, off) { 117 break 118 } 119 120 loc := m.saved[0] 121 122 off = loc.End 123 if off == loc.Begin { 124 off++ 125 if loc.Begin == prev { 126 continue 127 } 128 } 129 130 rets = append(rets, dup(m.saved)) 131 132 if len(rets) == n { 133 break 134 } 135 136 prev = loc.End 137 } 138 139 return rets 140 } 141 142 func (p *Pattern) ReplaceAllFunc(input string, repl func([]Capture) (string, error), n int) (string, int, error) { 143 allCaptures := p.FindAllIndex(input, n) 144 if allCaptures == nil { 145 return input, 0, nil 146 } 147 148 var buf bytes.Buffer 149 150 last := 0 151 for _, caps := range allCaptures { 152 loc := caps[0] 153 154 buf.WriteString(input[last:loc.Begin]) 155 156 alt, err := repl(caps) 157 if err != nil { 158 return "", -1, err 159 } 160 161 buf.WriteString(alt) 162 163 last = loc.End 164 } 165 166 buf.WriteString(input[last:]) 167 168 return buf.String(), len(allCaptures), nil 169 } 170 171 func dup(caps []Capture) []Capture { 172 return append([]Capture{}, caps...) 173 } 174 175 func FindIndex(input, pat string, off int) ([]Capture, error) { 176 if len(input) < off { 177 return nil, nil 178 } 179 p, err := Compile(pat) 180 if err != nil { 181 return nil, err 182 } 183 return p.FindIndex(input, off), nil 184 } 185 186 func FindAllIndex(input, pat string, n int) ([][]Capture, error) { 187 p, err := Compile(pat) 188 if err != nil { 189 return nil, err 190 } 191 return p.FindAllIndex(input, n), nil 192 } 193 194 func ReplaceAllFunc(input, pat string, repl func([]Capture) (string, error), n int) (string, int, error) { 195 p, err := Compile(pat) 196 if err != nil { 197 return "", -1, err 198 } 199 return p.ReplaceAllFunc(input, repl, n) 200 }