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  }