expression builder
This commit is contained in:
187
main.go
187
main.go
@@ -2,30 +2,193 @@ package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func matchEngine(pattern string, line string) {
|
||||
// sliding window approach for simple full pattern matching
|
||||
for i := 0; i < len(line)-len(pattern); i++ {
|
||||
slice := line[i : i+len(pattern)]
|
||||
if slice == pattern {
|
||||
println(line)
|
||||
type Repeats int
|
||||
|
||||
const (
|
||||
NoRepeat Repeats = iota
|
||||
ZeroOrMore
|
||||
OneOrMore
|
||||
)
|
||||
|
||||
type Character struct {
|
||||
Character rune
|
||||
Repeats
|
||||
}
|
||||
|
||||
type RenderedCharacterClass struct {
|
||||
Characters []rune
|
||||
Repeats
|
||||
}
|
||||
|
||||
type ComponentType int
|
||||
|
||||
const (
|
||||
Literal ComponentType = iota
|
||||
Rendered
|
||||
)
|
||||
|
||||
type Range struct {
|
||||
Start, End rune
|
||||
}
|
||||
type Component struct {
|
||||
Ctype ComponentType
|
||||
Literal Character
|
||||
Rendered RenderedCharacterClass
|
||||
}
|
||||
|
||||
func alphabetRange(start, end rune) []rune {
|
||||
if start > end {
|
||||
start, end = end, start
|
||||
}
|
||||
out := make([]rune, int(end-start)+1)
|
||||
for i := range out {
|
||||
out[i] = start + rune(i)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func parseClassBody(content []rune) ([]rune, error) {
|
||||
var runes []rune
|
||||
for i := 0; i < len(content); {
|
||||
ch := content[i]
|
||||
if i+2 < len(content) && content[i+1] == '-' {
|
||||
start, end := ch, content[i+2]
|
||||
runes = append(runes, alphabetRange(start, end)...)
|
||||
i += 3
|
||||
continue
|
||||
}
|
||||
runes = append(runes, ch)
|
||||
i++
|
||||
}
|
||||
return runes, nil
|
||||
}
|
||||
|
||||
func buildRenderedCharacterClass(body []rune) (RenderedCharacterClass, error) {
|
||||
rc := RenderedCharacterClass{}
|
||||
runes, err := parseClassBody(body)
|
||||
if err != nil {
|
||||
return rc, err
|
||||
}
|
||||
rc.Characters = runes
|
||||
return rc, nil
|
||||
}
|
||||
|
||||
func buildComponents(pattern string) ([]Component, error) {
|
||||
var comps []Component
|
||||
rs := []rune(pattern)
|
||||
i := 0
|
||||
|
||||
for i < len(rs) {
|
||||
ch := rs[i]
|
||||
|
||||
if ch != '[' && ch != '\\' { // normal char
|
||||
comps = append(comps, Component{
|
||||
Ctype: Literal,
|
||||
Literal: Character{Character: ch, Repeats: NoRepeat},
|
||||
})
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
if ch == '\\' {
|
||||
i++ // skip the backslash
|
||||
if i >= len(rs) {
|
||||
return nil, fmt.Errorf("dangling escape")
|
||||
}
|
||||
comps = append(comps, Component{
|
||||
Ctype: Literal,
|
||||
Literal: Character{Character: rs[i], Repeats: NoRepeat},
|
||||
})
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
if ch == '[' {
|
||||
j := i + 1
|
||||
for j < len(rs) && rs[j] != ']' {
|
||||
j++
|
||||
}
|
||||
if j >= len(rs) {
|
||||
return nil, fmt.Errorf("unterminated [")
|
||||
}
|
||||
|
||||
rc, err := buildRenderedCharacterClass(rs[i+1 : j])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r := NoRepeat
|
||||
if j+1 < len(rs) {
|
||||
switch rs[j+1] {
|
||||
case '*':
|
||||
r = ZeroOrMore
|
||||
case '+':
|
||||
r = OneOrMore
|
||||
}
|
||||
}
|
||||
rc.Repeats = r
|
||||
|
||||
comps = append(comps, Component{
|
||||
Ctype: Rendered,
|
||||
Rendered: rc,
|
||||
})
|
||||
|
||||
if j+1 < len(rs) && (rs[j+1] == '*' || rs[j+1] == '+') {
|
||||
i = j + 2
|
||||
} else {
|
||||
i = j + 1
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
i++
|
||||
}
|
||||
|
||||
return comps, nil
|
||||
}
|
||||
|
||||
func matchEngine(pattern string, line string, lineNum int, exp bool) {
|
||||
if !exp {
|
||||
// sliding window approach for simple full pattern matching
|
||||
for i := 0; i < len(line)-len(pattern); i++ {
|
||||
slice := line[i : i+len(pattern)]
|
||||
if slice == pattern {
|
||||
println(lineNum, line)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func processReader(src string, r *os.File, pattern string) {
|
||||
func processReader(r *os.File, pattern string, exp bool) {
|
||||
var wg sync.WaitGroup
|
||||
scanner := bufio.NewScanner(r)
|
||||
line := 0
|
||||
lineN := 0
|
||||
for scanner.Scan() {
|
||||
line++
|
||||
lineN++
|
||||
text := scanner.Text()
|
||||
matchEngine(pattern, text)
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
matchEngine(pattern, text, lineN, exp)
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func main() {
|
||||
pattern := os.Args[1]
|
||||
processReader("stdin", os.Stdin, pattern)
|
||||
regexModePtr := flag.Bool("ex", false, "set if using g2eb expressions, leave for full string matching")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
pattern := flag.Args()[0]
|
||||
processReader(os.Stdin, pattern, *regexModePtr)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user