| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302 |
- // Copyright 2011 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the GO-LICENSE file.
- // Modifications of this file, if any, are
- //
- // Copyright 2023 The Regexp Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package syntax // modernc.org/regexp/syntax
- import "unicode"
- // A patchList is a list of instruction pointers that need to be filled in (patched).
- // Because the pointers haven't been filled in yet, we can reuse their storage
- // to hold the list. It's kind of sleazy, but works well in practice.
- // See https://swtch.com/~rsc/regexp/regexp1.html for inspiration.
- //
- // These aren't really pointers: they're integers, so we can reinterpret them
- // this way without using package unsafe. A value l.head denotes
- // p.inst[l.head>>1].Out (l.head&1==0) or .Arg (l.head&1==1).
- // head == 0 denotes the empty list, okay because we start every program
- // with a fail instruction, so we'll never want to point at its output link.
- type patchList struct {
- head, tail uint32
- }
- func makePatchList(n uint32) patchList {
- return patchList{n, n}
- }
- func (l patchList) patch(p *Prog, val uint32) {
- head := l.head
- for head != 0 {
- i := &p.Inst[head>>1]
- if head&1 == 0 {
- head = i.Out
- i.Out = val
- } else {
- head = i.Arg
- i.Arg = val
- }
- }
- }
- func (l1 patchList) append(p *Prog, l2 patchList) patchList {
- if l1.head == 0 {
- return l2
- }
- if l2.head == 0 {
- return l1
- }
- i := &p.Inst[l1.tail>>1]
- if l1.tail&1 == 0 {
- i.Out = l2.head
- } else {
- i.Arg = l2.head
- }
- return patchList{l1.head, l2.tail}
- }
- // A frag represents a compiled program fragment.
- type frag struct {
- i uint32 // index of first instruction
- out patchList // where to record end instruction
- nullable bool // whether fragment can match empty string
- }
- type compiler struct {
- p *Prog
- }
- // Compile compiles the regexp into a program to be executed.
- // The regexp should have been simplified already (returned from re.Simplify).
- func Compile(re *Regexp) (*Prog, error) {
- var c compiler
- c.init()
- f := c.compile(re)
- f.out.patch(c.p, c.inst(InstMatch).i)
- c.p.Start = int(f.i)
- return c.p, nil
- }
- func (c *compiler) init() {
- c.p = new(Prog)
- c.p.NumCap = 2 // implicit ( and ) for whole match $0
- c.inst(InstFail)
- }
- var anyRuneNotNL = []rune{0, '\n' - 1, '\n' + 1, unicode.MaxRune}
- var anyRune = []rune{0, unicode.MaxRune}
- func (c *compiler) compile(re *Regexp) frag {
- switch re.Op {
- case OpNoMatch:
- return c.fail()
- case OpEmptyMatch:
- return c.nop()
- case OpLiteral:
- if len(re.Rune) == 0 {
- return c.nop()
- }
- var f frag
- for j := range re.Rune {
- f1 := c.rune(re.Rune[j:j+1], re.Flags)
- if j == 0 {
- f = f1
- } else {
- f = c.cat(f, f1)
- }
- }
- return f
- case OpCharClass:
- return c.rune(re.Rune, re.Flags)
- case OpAnyCharNotNL:
- return c.rune(anyRuneNotNL, 0)
- case OpAnyChar:
- return c.rune(anyRune, 0)
- case OpBeginLine:
- return c.empty(EmptyBeginLine)
- case OpEndLine:
- return c.empty(EmptyEndLine)
- case OpBeginText:
- return c.empty(EmptyBeginText)
- case OpEndText:
- return c.empty(EmptyEndText)
- case OpWordBoundary:
- return c.empty(EmptyWordBoundary)
- case OpNoWordBoundary:
- return c.empty(EmptyNoWordBoundary)
- case OpCapture:
- bra := c.cap(uint32(re.Cap << 1))
- sub := c.compile(re.Sub[0])
- ket := c.cap(uint32(re.Cap<<1 | 1))
- return c.cat(c.cat(bra, sub), ket)
- case OpStar:
- return c.star(c.compile(re.Sub[0]), re.Flags&NonGreedy != 0)
- case OpPlus:
- return c.plus(c.compile(re.Sub[0]), re.Flags&NonGreedy != 0)
- case OpQuest:
- return c.quest(c.compile(re.Sub[0]), re.Flags&NonGreedy != 0)
- case OpConcat:
- if len(re.Sub) == 0 {
- return c.nop()
- }
- var f frag
- for i, sub := range re.Sub {
- if i == 0 {
- f = c.compile(sub)
- } else {
- f = c.cat(f, c.compile(sub))
- }
- }
- return f
- case OpAlternate:
- var f frag
- for _, sub := range re.Sub {
- f = c.alt(f, c.compile(sub))
- }
- return f
- }
- panic("regexp: unhandled case in compile")
- }
- func (c *compiler) inst(op InstOp) frag {
- // TODO: impose length limit
- f := frag{i: uint32(len(c.p.Inst)), nullable: true}
- c.p.Inst = append(c.p.Inst, Inst{Op: op})
- return f
- }
- func (c *compiler) nop() frag {
- f := c.inst(InstNop)
- f.out = makePatchList(f.i << 1)
- return f
- }
- func (c *compiler) fail() frag {
- return frag{}
- }
- func (c *compiler) cap(arg uint32) frag {
- f := c.inst(InstCapture)
- f.out = makePatchList(f.i << 1)
- c.p.Inst[f.i].Arg = arg
- if c.p.NumCap < int(arg)+1 {
- c.p.NumCap = int(arg) + 1
- }
- return f
- }
- func (c *compiler) cat(f1, f2 frag) frag {
- // concat of failure is failure
- if f1.i == 0 || f2.i == 0 {
- return frag{}
- }
- // TODO: elide nop
- f1.out.patch(c.p, f2.i)
- return frag{f1.i, f2.out, f1.nullable && f2.nullable}
- }
- func (c *compiler) alt(f1, f2 frag) frag {
- // alt of failure is other
- if f1.i == 0 {
- return f2
- }
- if f2.i == 0 {
- return f1
- }
- f := c.inst(InstAlt)
- i := &c.p.Inst[f.i]
- i.Out = f1.i
- i.Arg = f2.i
- f.out = f1.out.append(c.p, f2.out)
- f.nullable = f1.nullable || f2.nullable
- return f
- }
- func (c *compiler) quest(f1 frag, nongreedy bool) frag {
- f := c.inst(InstAlt)
- i := &c.p.Inst[f.i]
- if nongreedy {
- i.Arg = f1.i
- f.out = makePatchList(f.i << 1)
- } else {
- i.Out = f1.i
- f.out = makePatchList(f.i<<1 | 1)
- }
- f.out = f.out.append(c.p, f1.out)
- return f
- }
- // loop returns the fragment for the main loop of a plus or star.
- // For plus, it can be used after changing the entry to f1.i.
- // For star, it can be used directly when f1 can't match an empty string.
- // (When f1 can match an empty string, f1* must be implemented as (f1+)?
- // to get the priority match order correct.)
- func (c *compiler) loop(f1 frag, nongreedy bool) frag {
- f := c.inst(InstAlt)
- i := &c.p.Inst[f.i]
- if nongreedy {
- i.Arg = f1.i
- f.out = makePatchList(f.i << 1)
- } else {
- i.Out = f1.i
- f.out = makePatchList(f.i<<1 | 1)
- }
- f1.out.patch(c.p, f.i)
- return f
- }
- func (c *compiler) star(f1 frag, nongreedy bool) frag {
- if f1.nullable {
- // Use (f1+)? to get priority match order correct.
- // See golang.org/issue/46123.
- return c.quest(c.plus(f1, nongreedy), nongreedy)
- }
- return c.loop(f1, nongreedy)
- }
- func (c *compiler) plus(f1 frag, nongreedy bool) frag {
- return frag{f1.i, c.loop(f1, nongreedy).out, f1.nullable}
- }
- func (c *compiler) empty(op EmptyOp) frag {
- f := c.inst(InstEmptyWidth)
- c.p.Inst[f.i].Arg = uint32(op)
- f.out = makePatchList(f.i << 1)
- return f
- }
- func (c *compiler) rune(r []rune, flags Flags) frag {
- f := c.inst(InstRune)
- f.nullable = false
- i := &c.p.Inst[f.i]
- i.Rune = r
- flags &= FoldCase // only relevant flag is FoldCase
- if len(r) != 1 || unicode.SimpleFold(r[0]) == r[0] {
- // and sometimes not even that
- flags &^= FoldCase
- }
- i.Arg = uint32(flags)
- f.out = makePatchList(f.i << 1)
- // Special cases for exec machine.
- switch {
- case flags&FoldCase == 0 && (len(r) == 1 || len(r) == 2 && r[0] == r[1]):
- i.Op = InstRune1
- case len(r) == 2 && r[0] == 0 && r[1] == unicode.MaxRune:
- i.Op = InstRuneAny
- case len(r) == 4 && r[0] == 0 && r[1] == '\n'-1 && r[2] == '\n'+1 && r[3] == unicode.MaxRune:
- i.Op = InstRuneAnyNotNL
- }
- return f
- }
|