expression builder

This commit is contained in:
2026-01-15 14:18:34 +01:00
parent 1ffb58452a
commit a0df0dd7fe

187
main.go
View File

@@ -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)
}