<- Back to shtanton's homepage
summaryrefslogtreecommitdiff
path: root/main/parse.go
blob: 59104c1308a1fa4ad44ce5968527e5ebaed4ffea (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
package main

func parseReplacement(l *RuneReader) (output []TransducerOutput) {
	loop: for {
		r := l.next()
		switch r {
			case eof:
				panic("Missing closing \"")
			case '"':
				break loop
			case '$':
				slot := l.next()
				if slot == eof {
					panic("Missing slot character")
				}
				output = append(output, TransducerReplacementLoad(slot))
			default:
				output = append(output, TransducerReplacementRune(r))
		}
	}
	return output
}

func parseSubex(l *RuneReader, minPower int) SubexAST {
	var lhs SubexAST
	r := l.next()
	switch r {
		case eof:
			return nil
		case '(':
			lhs = parseSubex(l, 0)
			if !l.accept(")") {
				panic("Missing matching )")
			}
		case ')', '*', '-', '|', '!', '?', ';':
			l.rewind()
			return nil
		case '$':
			slot := l.next()
			if slot == eof {
				panic("Missing slot character")
			}
			match := parseSubex(l, 100)
			if match == nil {
				panic("Missing regex for store")
			}
			lhs = SubexASTStore{
				match: match,
				slot: slot,
			}
		case '"':
			replacement := parseReplacement(l)
			lhs = SubexASTOutput{replacement}
		case '.':
			lhs = SubexASTCopyAny{}
		default:
			lhs = SubexASTCopyRune(r)
	}
	loop: for {
		if minPower <= 0 {
			next := parseSubex(l, 1)
			if next != nil {
				lhs = SubexASTConcat{lhs, next}
				continue loop
			}
		}
		r := l.next()
		switch {
			case r == '*' && minPower <= 8:
				lhs = SubexASTMaximise{lhs}
			case r == '-' && minPower <= 8:
				lhs = SubexASTMinimise{lhs}
			case r == '!' && minPower <= 8:
				lhs = SubexASTTry{lhs}
			case r == '?' && minPower <= 8:
				lhs = SubexASTMaybe{lhs}
			case r == '|' && minPower <= 4:
				rhs := parseSubex(l, 5)
				if rhs == nil {
					panic("Missing subex after |")
				}
				lhs = SubexASTOr{lhs, rhs}
			case r == ';' && minPower <= 2:
				rhs := parseSubex(l, 3)
				if rhs == nil {
					panic("Missing subex after ;")
				}
				lhs = SubexASTJoin{
					content: lhs,
					delimiter: rhs,
				}
			default:
				l.rewind()
				break loop
		}
	}
	return lhs
}

func parse(input string) SubexAST {
	l := RuneReader {
		input: input,
		pos: 0,
		width: 0,
	}
	return parseSubex(&l, 0)
}