summaryrefslogtreecommitdiff
path: root/combinator.go
blob: 2b6b8a3fc86b290e5093702527966c92c3528c98 (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
108
109
110
111
112
113
114
package parsec

func Nil(p Pointer) (Node, Pointer) {
	return nil, p
}

func Never(p Pointer) (Node, Pointer) {
	return Error{p.pos, "Never matches"}, p
}

func And(parsers ...Parserish) Parser {
	if len(parsers) == 0 {
		return Nil
	}

	ps := ParsifyAll(parsers...)

	return func(p Pointer) (Node, Pointer) {
		var nodes = make([]Node, 0, len(ps))
		var node Node
		newP := p
		for _, parser := range ps {
			node, newP = parser(newP)
			if node == nil {
				continue
			}
			if IsError(node) {
				return node, p
			}
			nodes = append(nodes, node)
		}
		return NewSequence(p.pos, nodes...), newP
	}
}

func Any(parsers ...Parserish) Parser {
	if len(parsers) == 0 {
		return Nil
	}

	ps := ParsifyAll(parsers...)

	return func(p Pointer) (Node, Pointer) {
		errors := []Error{}
		for _, parser := range ps {
			node, newP := parser(p)
			if err, isErr := node.(Error); isErr {
				errors = append(errors, err)
				continue
			}
			return node, newP
		}

		longestError := errors[0]
		for _, e := range errors[1:] {
			if e.pos > longestError.pos {
				longestError = e
			}
		}

		return longestError, p
	}
}

func Kleene(opScan Parserish, sepScan ...Parserish) Parser {
	return manyImpl(0, opScan, Never, sepScan...)
}

func KleeneUntil(opScan Parserish, untilScan Parserish, sepScan ...Parserish) Parser {
	return manyImpl(0, opScan, untilScan, sepScan...)
}

func Many(opScan Parserish, sepScan ...Parserish) Parser {
	return manyImpl(1, opScan, Never, sepScan...)
}

func ManyUntil(opScan Parserish, untilScan Parserish, sepScan ...Parserish) Parser {
	return manyImpl(1, opScan, untilScan, sepScan...)
}

func manyImpl(min int, op Parserish, until Parserish, sep ...Parserish) Parser {
	opParser := Parsify(op)
	untilParser := Parsify(until)
	sepParser := Nil
	if len(sep) > 0 {
		sepParser = Parsify(sep[0])
	}

	return func(p Pointer) (Node, Pointer) {
		var node Node
		nodes := make([]Node, 0)
		newP := p
		for {
			if node, _ := untilParser(newP); !IsError(node) {
				if len(nodes) < min {
					return NewError(newP.pos, "Unexpected input"), p
				}
				break
			}

			if node, newP = opParser(newP); IsError(node) {
				if len(nodes) < min {
					return node, p
				}
				break
			}
			nodes = append(nodes, node)
			if node, newP = sepParser(newP); IsError(node) {
				break
			}
		}
		return NewSequence(p.pos, nodes...), newP
	}
}