1
0
mirror of https://github.com/golang/go synced 2024-11-06 11:26:12 -07:00
go/src/pkg/regexp/syntax/compile.go

289 lines
6.0 KiB
Go
Raw Normal View History

package syntax
import (
"os"
"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 http://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 denotes
// p.inst[l>>1].Out (l&1==0) or .Arg (l&1==1).
// l == 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 uint32
func (l patchList) next(p *Prog) patchList {
i := &p.Inst[l>>1]
if l&1 == 0 {
return patchList(i.Out)
}
return patchList(i.Arg)
}
func (l patchList) patch(p *Prog, val uint32) {
for l != 0 {
i := &p.Inst[l>>1]
if l&1 == 0 {
l = patchList(i.Out)
i.Out = val
} else {
l = patchList(i.Arg)
i.Arg = val
}
}
}
func (l1 patchList) append(p *Prog, l2 patchList) patchList {
if l1 == 0 {
return l2
}
if l2 == 0 {
return l1
}
last := l1
for {
next := last.next(p)
if next == 0 {
break
}
last = next
}
i := &p.Inst[last>>1]
if last&1 == 0 {
i.Out = uint32(l2)
} else {
i.Arg = uint32(l2)
}
return l1
}
// A frag represents a compiled program fragment.
type frag struct {
i uint32 // index of first instruction
out patchList // where to record end instruction
}
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, os.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))}
c.p.Inst = append(c.p.Inst, Inst{Op: op})
return f
}
func (c *compiler) nop() frag {
f := c.inst(InstNop)
f.out = patchList(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 = patchList(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}
}
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)
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 = patchList(f.i << 1)
} else {
i.Out = f1.i
f.out = patchList(f.i<<1 | 1)
}
f.out = f.out.append(c.p, f1.out)
return f
}
func (c *compiler) star(f1 frag, nongreedy bool) frag {
f := c.inst(InstAlt)
i := &c.p.Inst[f.i]
if nongreedy {
i.Arg = f1.i
f.out = patchList(f.i << 1)
} else {
i.Out = f1.i
f.out = patchList(f.i<<1 | 1)
}
f1.out.patch(c.p, f.i)
return f
}
func (c *compiler) plus(f1 frag, nongreedy bool) frag {
return frag{f1.i, c.star(f1, nongreedy).out}
}
func (c *compiler) empty(op EmptyOp) frag {
f := c.inst(InstEmptyWidth)
c.p.Inst[f.i].Arg = uint32(op)
f.out = patchList(f.i << 1)
return f
}
func (c *compiler) rune(r []rune, flags Flags) frag {
f := c.inst(InstRune)
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 = patchList(f.i << 1)
regexp: speedups MatchEasy0_1K 500000 4207 ns/op 243.35 MB/s MatchEasy0_1K_Old 500000 4625 ns/op 221.40 MB/s MatchEasy0_1M 500 3948932 ns/op 265.53 MB/s MatchEasy0_1M_Old 500 3943926 ns/op 265.87 MB/s MatchEasy0_32K 10000 122974 ns/op 266.46 MB/s MatchEasy0_32K_Old 10000 123270 ns/op 265.82 MB/s MatchEasy0_32M 10 127265400 ns/op 263.66 MB/s MatchEasy0_32M_Old 10 127123500 ns/op 263.95 MB/s MatchEasy1_1K 500000 5637 ns/op 181.63 MB/s MatchEasy1_1K_Old 10000 100690 ns/op 10.17 MB/s MatchEasy1_1M 200 7683150 ns/op 136.48 MB/s MatchEasy1_1M_Old 10 145774000 ns/op 7.19 MB/s MatchEasy1_32K 10000 239887 ns/op 136.60 MB/s MatchEasy1_32K_Old 500 4508182 ns/op 7.27 MB/s MatchEasy1_32M 10 247103500 ns/op 135.79 MB/s MatchEasy1_32M_Old 1 4660191000 ns/op 7.20 MB/s MatchMedium_1K 10000 160567 ns/op 6.38 MB/s MatchMedium_1K_Old 10000 158367 ns/op 6.47 MB/s MatchMedium_1M 10 162928000 ns/op 6.44 MB/s MatchMedium_1M_Old 10 159699200 ns/op 6.57 MB/s MatchMedium_32K 500 5090758 ns/op 6.44 MB/s MatchMedium_32K_Old 500 5005800 ns/op 6.55 MB/s MatchMedium_32M 1 5233973000 ns/op 6.41 MB/s MatchMedium_32M_Old 1 5109676000 ns/op 6.57 MB/s MatchHard_1K 10000 249087 ns/op 4.11 MB/s MatchHard_1K_Old 5000 364569 ns/op 2.81 MB/s MatchHard_1M 5 256050000 ns/op 4.10 MB/s MatchHard_1M_Old 5 372446400 ns/op 2.82 MB/s MatchHard_32K 200 7944525 ns/op 4.12 MB/s MatchHard_32K_Old 100 11609380 ns/op 2.82 MB/s MatchHard_32M 1 8144503000 ns/op 4.12 MB/s MatchHard_32M_Old 1 11885434000 ns/op 2.82 MB/s R=r, bradfitz CC=golang-dev https://golang.org/cl/5134049
2011-09-28 10:00:31 -06:00
// Special cases for exec machine.
switch {
case flags&FoldCase == 0 && (len(r) == 1 || len(r) == 2 && r[0] == r[1]):
regexp: speedups MatchEasy0_1K 500000 4207 ns/op 243.35 MB/s MatchEasy0_1K_Old 500000 4625 ns/op 221.40 MB/s MatchEasy0_1M 500 3948932 ns/op 265.53 MB/s MatchEasy0_1M_Old 500 3943926 ns/op 265.87 MB/s MatchEasy0_32K 10000 122974 ns/op 266.46 MB/s MatchEasy0_32K_Old 10000 123270 ns/op 265.82 MB/s MatchEasy0_32M 10 127265400 ns/op 263.66 MB/s MatchEasy0_32M_Old 10 127123500 ns/op 263.95 MB/s MatchEasy1_1K 500000 5637 ns/op 181.63 MB/s MatchEasy1_1K_Old 10000 100690 ns/op 10.17 MB/s MatchEasy1_1M 200 7683150 ns/op 136.48 MB/s MatchEasy1_1M_Old 10 145774000 ns/op 7.19 MB/s MatchEasy1_32K 10000 239887 ns/op 136.60 MB/s MatchEasy1_32K_Old 500 4508182 ns/op 7.27 MB/s MatchEasy1_32M 10 247103500 ns/op 135.79 MB/s MatchEasy1_32M_Old 1 4660191000 ns/op 7.20 MB/s MatchMedium_1K 10000 160567 ns/op 6.38 MB/s MatchMedium_1K_Old 10000 158367 ns/op 6.47 MB/s MatchMedium_1M 10 162928000 ns/op 6.44 MB/s MatchMedium_1M_Old 10 159699200 ns/op 6.57 MB/s MatchMedium_32K 500 5090758 ns/op 6.44 MB/s MatchMedium_32K_Old 500 5005800 ns/op 6.55 MB/s MatchMedium_32M 1 5233973000 ns/op 6.41 MB/s MatchMedium_32M_Old 1 5109676000 ns/op 6.57 MB/s MatchHard_1K 10000 249087 ns/op 4.11 MB/s MatchHard_1K_Old 5000 364569 ns/op 2.81 MB/s MatchHard_1M 5 256050000 ns/op 4.10 MB/s MatchHard_1M_Old 5 372446400 ns/op 2.82 MB/s MatchHard_32K 200 7944525 ns/op 4.12 MB/s MatchHard_32K_Old 100 11609380 ns/op 2.82 MB/s MatchHard_32M 1 8144503000 ns/op 4.12 MB/s MatchHard_32M_Old 1 11885434000 ns/op 2.82 MB/s R=r, bradfitz CC=golang-dev https://golang.org/cl/5134049
2011-09-28 10:00:31 -06:00
i.Op = InstRune1
case len(r) == 2 && r[0] == 0 && r[1] == unicode.MaxRune:
regexp: speedups MatchEasy0_1K 500000 4207 ns/op 243.35 MB/s MatchEasy0_1K_Old 500000 4625 ns/op 221.40 MB/s MatchEasy0_1M 500 3948932 ns/op 265.53 MB/s MatchEasy0_1M_Old 500 3943926 ns/op 265.87 MB/s MatchEasy0_32K 10000 122974 ns/op 266.46 MB/s MatchEasy0_32K_Old 10000 123270 ns/op 265.82 MB/s MatchEasy0_32M 10 127265400 ns/op 263.66 MB/s MatchEasy0_32M_Old 10 127123500 ns/op 263.95 MB/s MatchEasy1_1K 500000 5637 ns/op 181.63 MB/s MatchEasy1_1K_Old 10000 100690 ns/op 10.17 MB/s MatchEasy1_1M 200 7683150 ns/op 136.48 MB/s MatchEasy1_1M_Old 10 145774000 ns/op 7.19 MB/s MatchEasy1_32K 10000 239887 ns/op 136.60 MB/s MatchEasy1_32K_Old 500 4508182 ns/op 7.27 MB/s MatchEasy1_32M 10 247103500 ns/op 135.79 MB/s MatchEasy1_32M_Old 1 4660191000 ns/op 7.20 MB/s MatchMedium_1K 10000 160567 ns/op 6.38 MB/s MatchMedium_1K_Old 10000 158367 ns/op 6.47 MB/s MatchMedium_1M 10 162928000 ns/op 6.44 MB/s MatchMedium_1M_Old 10 159699200 ns/op 6.57 MB/s MatchMedium_32K 500 5090758 ns/op 6.44 MB/s MatchMedium_32K_Old 500 5005800 ns/op 6.55 MB/s MatchMedium_32M 1 5233973000 ns/op 6.41 MB/s MatchMedium_32M_Old 1 5109676000 ns/op 6.57 MB/s MatchHard_1K 10000 249087 ns/op 4.11 MB/s MatchHard_1K_Old 5000 364569 ns/op 2.81 MB/s MatchHard_1M 5 256050000 ns/op 4.10 MB/s MatchHard_1M_Old 5 372446400 ns/op 2.82 MB/s MatchHard_32K 200 7944525 ns/op 4.12 MB/s MatchHard_32K_Old 100 11609380 ns/op 2.82 MB/s MatchHard_32M 1 8144503000 ns/op 4.12 MB/s MatchHard_32M_Old 1 11885434000 ns/op 2.82 MB/s R=r, bradfitz CC=golang-dev https://golang.org/cl/5134049
2011-09-28 10:00:31 -06:00
i.Op = InstRuneAny
case len(r) == 4 && r[0] == 0 && r[1] == '\n'-1 && r[2] == '\n'+1 && r[3] == unicode.MaxRune:
regexp: speedups MatchEasy0_1K 500000 4207 ns/op 243.35 MB/s MatchEasy0_1K_Old 500000 4625 ns/op 221.40 MB/s MatchEasy0_1M 500 3948932 ns/op 265.53 MB/s MatchEasy0_1M_Old 500 3943926 ns/op 265.87 MB/s MatchEasy0_32K 10000 122974 ns/op 266.46 MB/s MatchEasy0_32K_Old 10000 123270 ns/op 265.82 MB/s MatchEasy0_32M 10 127265400 ns/op 263.66 MB/s MatchEasy0_32M_Old 10 127123500 ns/op 263.95 MB/s MatchEasy1_1K 500000 5637 ns/op 181.63 MB/s MatchEasy1_1K_Old 10000 100690 ns/op 10.17 MB/s MatchEasy1_1M 200 7683150 ns/op 136.48 MB/s MatchEasy1_1M_Old 10 145774000 ns/op 7.19 MB/s MatchEasy1_32K 10000 239887 ns/op 136.60 MB/s MatchEasy1_32K_Old 500 4508182 ns/op 7.27 MB/s MatchEasy1_32M 10 247103500 ns/op 135.79 MB/s MatchEasy1_32M_Old 1 4660191000 ns/op 7.20 MB/s MatchMedium_1K 10000 160567 ns/op 6.38 MB/s MatchMedium_1K_Old 10000 158367 ns/op 6.47 MB/s MatchMedium_1M 10 162928000 ns/op 6.44 MB/s MatchMedium_1M_Old 10 159699200 ns/op 6.57 MB/s MatchMedium_32K 500 5090758 ns/op 6.44 MB/s MatchMedium_32K_Old 500 5005800 ns/op 6.55 MB/s MatchMedium_32M 1 5233973000 ns/op 6.41 MB/s MatchMedium_32M_Old 1 5109676000 ns/op 6.57 MB/s MatchHard_1K 10000 249087 ns/op 4.11 MB/s MatchHard_1K_Old 5000 364569 ns/op 2.81 MB/s MatchHard_1M 5 256050000 ns/op 4.10 MB/s MatchHard_1M_Old 5 372446400 ns/op 2.82 MB/s MatchHard_32K 200 7944525 ns/op 4.12 MB/s MatchHard_32K_Old 100 11609380 ns/op 2.82 MB/s MatchHard_32M 1 8144503000 ns/op 4.12 MB/s MatchHard_32M_Old 1 11885434000 ns/op 2.82 MB/s R=r, bradfitz CC=golang-dev https://golang.org/cl/5134049
2011-09-28 10:00:31 -06:00
i.Op = InstRuneAnyNotNL
}
return f
}