mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			346 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			346 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Go
		
	
	
	
// 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 LICENSE file.
 | 
						|
 | 
						|
package syntax
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"strconv"
 | 
						|
	"unicode"
 | 
						|
)
 | 
						|
 | 
						|
// Compiled program.
 | 
						|
// May not belong in this package, but convenient for now.
 | 
						|
 | 
						|
// A Prog is a compiled regular expression program.
 | 
						|
type Prog struct {
 | 
						|
	Inst   []Inst
 | 
						|
	Start  int // index of start instruction
 | 
						|
	NumCap int // number of InstCapture insts in re
 | 
						|
}
 | 
						|
 | 
						|
// An InstOp is an instruction opcode.
 | 
						|
type InstOp uint8
 | 
						|
 | 
						|
const (
 | 
						|
	InstAlt InstOp = iota
 | 
						|
	InstAltMatch
 | 
						|
	InstCapture
 | 
						|
	InstEmptyWidth
 | 
						|
	InstMatch
 | 
						|
	InstFail
 | 
						|
	InstNop
 | 
						|
	InstRune
 | 
						|
	InstRune1
 | 
						|
	InstRuneAny
 | 
						|
	InstRuneAnyNotNL
 | 
						|
)
 | 
						|
 | 
						|
var instOpNames = []string{
 | 
						|
	"InstAlt",
 | 
						|
	"InstAltMatch",
 | 
						|
	"InstCapture",
 | 
						|
	"InstEmptyWidth",
 | 
						|
	"InstMatch",
 | 
						|
	"InstFail",
 | 
						|
	"InstNop",
 | 
						|
	"InstRune",
 | 
						|
	"InstRune1",
 | 
						|
	"InstRuneAny",
 | 
						|
	"InstRuneAnyNotNL",
 | 
						|
}
 | 
						|
 | 
						|
func (i InstOp) String() string {
 | 
						|
	if uint(i) >= uint(len(instOpNames)) {
 | 
						|
		return ""
 | 
						|
	}
 | 
						|
	return instOpNames[i]
 | 
						|
}
 | 
						|
 | 
						|
// An EmptyOp specifies a kind or mixture of zero-width assertions.
 | 
						|
type EmptyOp uint8
 | 
						|
 | 
						|
const (
 | 
						|
	EmptyBeginLine EmptyOp = 1 << iota
 | 
						|
	EmptyEndLine
 | 
						|
	EmptyBeginText
 | 
						|
	EmptyEndText
 | 
						|
	EmptyWordBoundary
 | 
						|
	EmptyNoWordBoundary
 | 
						|
)
 | 
						|
 | 
						|
// EmptyOpContext returns the zero-width assertions
 | 
						|
// satisfied at the position between the runes r1 and r2.
 | 
						|
// Passing r1 == -1 indicates that the position is
 | 
						|
// at the beginning of the text.
 | 
						|
// Passing r2 == -1 indicates that the position is
 | 
						|
// at the end of the text.
 | 
						|
func EmptyOpContext(r1, r2 rune) EmptyOp {
 | 
						|
	var op EmptyOp = EmptyNoWordBoundary
 | 
						|
	var boundary byte
 | 
						|
	switch {
 | 
						|
	case IsWordChar(r1):
 | 
						|
		boundary = 1
 | 
						|
	case r1 == '\n':
 | 
						|
		op |= EmptyBeginLine
 | 
						|
	case r1 < 0:
 | 
						|
		op |= EmptyBeginText | EmptyBeginLine
 | 
						|
	}
 | 
						|
	switch {
 | 
						|
	case IsWordChar(r2):
 | 
						|
		boundary ^= 1
 | 
						|
	case r2 == '\n':
 | 
						|
		op |= EmptyEndLine
 | 
						|
	case r2 < 0:
 | 
						|
		op |= EmptyEndText | EmptyEndLine
 | 
						|
	}
 | 
						|
	if boundary != 0 { // IsWordChar(r1) != IsWordChar(r2)
 | 
						|
		op ^= (EmptyWordBoundary | EmptyNoWordBoundary)
 | 
						|
	}
 | 
						|
	return op
 | 
						|
}
 | 
						|
 | 
						|
// IsWordChar reports whether r is consider a ``word character''
 | 
						|
// during the evaluation of the \b and \B zero-width assertions.
 | 
						|
// These assertions are ASCII-only: the word characters are [A-Za-z0-9_].
 | 
						|
func IsWordChar(r rune) bool {
 | 
						|
	return 'A' <= r && r <= 'Z' || 'a' <= r && r <= 'z' || '0' <= r && r <= '9' || r == '_'
 | 
						|
}
 | 
						|
 | 
						|
// An Inst is a single instruction in a regular expression program.
 | 
						|
type Inst struct {
 | 
						|
	Op   InstOp
 | 
						|
	Out  uint32 // all but InstMatch, InstFail
 | 
						|
	Arg  uint32 // InstAlt, InstAltMatch, InstCapture, InstEmptyWidth
 | 
						|
	Rune []rune
 | 
						|
}
 | 
						|
 | 
						|
func (p *Prog) String() string {
 | 
						|
	var b bytes.Buffer
 | 
						|
	dumpProg(&b, p)
 | 
						|
	return b.String()
 | 
						|
}
 | 
						|
 | 
						|
// skipNop follows any no-op or capturing instructions
 | 
						|
// and returns the resulting pc.
 | 
						|
func (p *Prog) skipNop(pc uint32) (*Inst, uint32) {
 | 
						|
	i := &p.Inst[pc]
 | 
						|
	for i.Op == InstNop || i.Op == InstCapture {
 | 
						|
		pc = i.Out
 | 
						|
		i = &p.Inst[pc]
 | 
						|
	}
 | 
						|
	return i, pc
 | 
						|
}
 | 
						|
 | 
						|
// op returns i.Op but merges all the Rune special cases into InstRune
 | 
						|
func (i *Inst) op() InstOp {
 | 
						|
	op := i.Op
 | 
						|
	switch op {
 | 
						|
	case InstRune1, InstRuneAny, InstRuneAnyNotNL:
 | 
						|
		op = InstRune
 | 
						|
	}
 | 
						|
	return op
 | 
						|
}
 | 
						|
 | 
						|
// Prefix returns a literal string that all matches for the
 | 
						|
// regexp must start with. Complete is true if the prefix
 | 
						|
// is the entire match.
 | 
						|
func (p *Prog) Prefix() (prefix string, complete bool) {
 | 
						|
	i, _ := p.skipNop(uint32(p.Start))
 | 
						|
 | 
						|
	// Avoid allocation of buffer if prefix is empty.
 | 
						|
	if i.op() != InstRune || len(i.Rune) != 1 {
 | 
						|
		return "", i.Op == InstMatch
 | 
						|
	}
 | 
						|
 | 
						|
	// Have prefix; gather characters.
 | 
						|
	var buf bytes.Buffer
 | 
						|
	for i.op() == InstRune && len(i.Rune) == 1 && Flags(i.Arg)&FoldCase == 0 {
 | 
						|
		buf.WriteRune(i.Rune[0])
 | 
						|
		i, _ = p.skipNop(i.Out)
 | 
						|
	}
 | 
						|
	return buf.String(), i.Op == InstMatch
 | 
						|
}
 | 
						|
 | 
						|
// StartCond returns the leading empty-width conditions that must
 | 
						|
// be true in any match. It returns ^EmptyOp(0) if no matches are possible.
 | 
						|
func (p *Prog) StartCond() EmptyOp {
 | 
						|
	var flag EmptyOp
 | 
						|
	pc := uint32(p.Start)
 | 
						|
	i := &p.Inst[pc]
 | 
						|
Loop:
 | 
						|
	for {
 | 
						|
		switch i.Op {
 | 
						|
		case InstEmptyWidth:
 | 
						|
			flag |= EmptyOp(i.Arg)
 | 
						|
		case InstFail:
 | 
						|
			return ^EmptyOp(0)
 | 
						|
		case InstCapture, InstNop:
 | 
						|
			// skip
 | 
						|
		default:
 | 
						|
			break Loop
 | 
						|
		}
 | 
						|
		pc = i.Out
 | 
						|
		i = &p.Inst[pc]
 | 
						|
	}
 | 
						|
	return flag
 | 
						|
}
 | 
						|
 | 
						|
const noMatch = -1
 | 
						|
 | 
						|
// MatchRune reports whether the instruction matches (and consumes) r.
 | 
						|
// It should only be called when i.Op == InstRune.
 | 
						|
func (i *Inst) MatchRune(r rune) bool {
 | 
						|
	return i.MatchRunePos(r) != noMatch
 | 
						|
}
 | 
						|
 | 
						|
// MatchRunePos checks whether the instruction matches (and consumes) r.
 | 
						|
// If so, MatchRunePos returns the index of the matching rune pair
 | 
						|
// (or, when len(i.Rune) == 1, rune singleton).
 | 
						|
// If not, MatchRunePos returns -1.
 | 
						|
// MatchRunePos should only be called when i.Op == InstRune.
 | 
						|
func (i *Inst) MatchRunePos(r rune) int {
 | 
						|
	rune := i.Rune
 | 
						|
 | 
						|
	// Special case: single-rune slice is from literal string, not char class.
 | 
						|
	if len(rune) == 1 {
 | 
						|
		r0 := rune[0]
 | 
						|
		if r == r0 {
 | 
						|
			return 0
 | 
						|
		}
 | 
						|
		if Flags(i.Arg)&FoldCase != 0 {
 | 
						|
			for r1 := unicode.SimpleFold(r0); r1 != r0; r1 = unicode.SimpleFold(r1) {
 | 
						|
				if r == r1 {
 | 
						|
					return 0
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		return noMatch
 | 
						|
	}
 | 
						|
 | 
						|
	// Peek at the first few pairs.
 | 
						|
	// Should handle ASCII well.
 | 
						|
	for j := 0; j < len(rune) && j <= 8; j += 2 {
 | 
						|
		if r < rune[j] {
 | 
						|
			return noMatch
 | 
						|
		}
 | 
						|
		if r <= rune[j+1] {
 | 
						|
			return j / 2
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// Otherwise binary search.
 | 
						|
	lo := 0
 | 
						|
	hi := len(rune) / 2
 | 
						|
	for lo < hi {
 | 
						|
		m := lo + (hi-lo)/2
 | 
						|
		if c := rune[2*m]; c <= r {
 | 
						|
			if r <= rune[2*m+1] {
 | 
						|
				return m
 | 
						|
			}
 | 
						|
			lo = m + 1
 | 
						|
		} else {
 | 
						|
			hi = m
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return noMatch
 | 
						|
}
 | 
						|
 | 
						|
// As per re2's Prog::IsWordChar. Determines whether rune is an ASCII word char.
 | 
						|
// Since we act on runes, it would be easy to support Unicode here.
 | 
						|
func wordRune(r rune) bool {
 | 
						|
	return r == '_' ||
 | 
						|
		('A' <= r && r <= 'Z') ||
 | 
						|
		('a' <= r && r <= 'z') ||
 | 
						|
		('0' <= r && r <= '9')
 | 
						|
}
 | 
						|
 | 
						|
// MatchEmptyWidth reports whether the instruction matches
 | 
						|
// an empty string between the runes before and after.
 | 
						|
// It should only be called when i.Op == InstEmptyWidth.
 | 
						|
func (i *Inst) MatchEmptyWidth(before rune, after rune) bool {
 | 
						|
	switch EmptyOp(i.Arg) {
 | 
						|
	case EmptyBeginLine:
 | 
						|
		return before == '\n' || before == -1
 | 
						|
	case EmptyEndLine:
 | 
						|
		return after == '\n' || after == -1
 | 
						|
	case EmptyBeginText:
 | 
						|
		return before == -1
 | 
						|
	case EmptyEndText:
 | 
						|
		return after == -1
 | 
						|
	case EmptyWordBoundary:
 | 
						|
		return wordRune(before) != wordRune(after)
 | 
						|
	case EmptyNoWordBoundary:
 | 
						|
		return wordRune(before) == wordRune(after)
 | 
						|
	}
 | 
						|
	panic("unknown empty width arg")
 | 
						|
}
 | 
						|
 | 
						|
func (i *Inst) String() string {
 | 
						|
	var b bytes.Buffer
 | 
						|
	dumpInst(&b, i)
 | 
						|
	return b.String()
 | 
						|
}
 | 
						|
 | 
						|
func bw(b *bytes.Buffer, args ...string) {
 | 
						|
	for _, s := range args {
 | 
						|
		b.WriteString(s)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func dumpProg(b *bytes.Buffer, p *Prog) {
 | 
						|
	for j := range p.Inst {
 | 
						|
		i := &p.Inst[j]
 | 
						|
		pc := strconv.Itoa(j)
 | 
						|
		if len(pc) < 3 {
 | 
						|
			b.WriteString("   "[len(pc):])
 | 
						|
		}
 | 
						|
		if j == p.Start {
 | 
						|
			pc += "*"
 | 
						|
		}
 | 
						|
		bw(b, pc, "\t")
 | 
						|
		dumpInst(b, i)
 | 
						|
		bw(b, "\n")
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func u32(i uint32) string {
 | 
						|
	return strconv.FormatUint(uint64(i), 10)
 | 
						|
}
 | 
						|
 | 
						|
func dumpInst(b *bytes.Buffer, i *Inst) {
 | 
						|
	switch i.Op {
 | 
						|
	case InstAlt:
 | 
						|
		bw(b, "alt -> ", u32(i.Out), ", ", u32(i.Arg))
 | 
						|
	case InstAltMatch:
 | 
						|
		bw(b, "altmatch -> ", u32(i.Out), ", ", u32(i.Arg))
 | 
						|
	case InstCapture:
 | 
						|
		bw(b, "cap ", u32(i.Arg), " -> ", u32(i.Out))
 | 
						|
	case InstEmptyWidth:
 | 
						|
		bw(b, "empty ", u32(i.Arg), " -> ", u32(i.Out))
 | 
						|
	case InstMatch:
 | 
						|
		bw(b, "match")
 | 
						|
	case InstFail:
 | 
						|
		bw(b, "fail")
 | 
						|
	case InstNop:
 | 
						|
		bw(b, "nop -> ", u32(i.Out))
 | 
						|
	case InstRune:
 | 
						|
		if i.Rune == nil {
 | 
						|
			// shouldn't happen
 | 
						|
			bw(b, "rune <nil>")
 | 
						|
		}
 | 
						|
		bw(b, "rune ", strconv.QuoteToASCII(string(i.Rune)))
 | 
						|
		if Flags(i.Arg)&FoldCase != 0 {
 | 
						|
			bw(b, "/i")
 | 
						|
		}
 | 
						|
		bw(b, " -> ", u32(i.Out))
 | 
						|
	case InstRune1:
 | 
						|
		bw(b, "rune1 ", strconv.QuoteToASCII(string(i.Rune)), " -> ", u32(i.Out))
 | 
						|
	case InstRuneAny:
 | 
						|
		bw(b, "any -> ", u32(i.Out))
 | 
						|
	case InstRuneAnyNotNL:
 | 
						|
		bw(b, "anynotnl -> ", u32(i.Out))
 | 
						|
	}
 | 
						|
}
 |