summaryrefslogtreecommitdiff
path: root/parser.go
blob: 806652647ec63982d807a3cffe644eba42e8416c (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
115
116
117
118
119
120
121
122
package parsec

import (
	"fmt"
)

type Parser func(Pointer) (Node, Pointer)

// Parserish types are any type that can be turned into a Parser by Parsify
// These currently include *Parser and string literals.
//
// This makes recursive grammars cleaner and allows string literals to be used directly in most contexts.
// eg, matching balanced paren:
// ```go
// var group Parser
// group = And("(", Maybe(&group), ")")
// ```
// vs
// ```go
// var group ParserPtr{}
// group.P = And(Exact("("), Maybe(group.Parse), Exact(")"))
// ```
type Parserish interface{}

func Parsify(p Parserish) Parser {
	switch p := p.(type) {
	case func(Pointer) (Node, Pointer):
		return Parser(p)
	case Parser:
		return p
	case *Parser:
		// Todo: Maybe capture this stack and on nil show it? Is there a good error library to do this?
		return func(ptr Pointer) (Node, Pointer) {
			return (*p)(ptr)
		}
	case string:
		return Exact(p)
	default:
		panic(fmt.Errorf("cant turn a `%T` into a parser", p))
	}
}

func ParsifyAll(parsers ...Parserish) []Parser {
	ret := make([]Parser, len(parsers))
	for i, parser := range parsers {
		ret[i] = Parsify(parser)
	}
	return ret
}

func Exact(match string) Parser {
	return func(p Pointer) (Node, Pointer) {
		if !p.HasPrefix(match) {
			return NewError(p.pos, "Expected "+match), p
		}

		return NewToken(p.pos, match), p.Advance(len(match))
	}
}

func Char(match string) Parser {
	return func(p Pointer) (Node, Pointer) {
		r, p2 := p.Accept(match)
		if r == "" {
			return NewError(p.pos, "Expected one of "+string(match)), p
		}

		return NewToken(p.pos, string(r)), p2
	}
}

func CharRun(match string) Parser {
	return func(p Pointer) (Node, Pointer) {
		s, p2 := p.AcceptRun(match)
		if s == "" {
			return NewError(p.pos, "Expected some of "+match), p
		}

		return NewToken(p.pos, s), p2
	}
}

func CharRunUntil(match string) Parser {
	return func(p Pointer) (Node, Pointer) {
		s, p2 := p.AcceptUntil(match)
		if s == "" {
			return NewError(p.pos, "Expected some of "+match), p
		}

		return NewToken(p.pos, s), p2
	}
}

func Range(r string) string {
	runes := []rune(r)
	if len(runes)%3 != 0 {
		panic("ranges should be in the form a-z0-9")
	}

	match := ""

	for i := 0; i < len(runes); i += 3 {
		start := runes[i]
		end := runes[i+2]
		if start > end {
			tmp := start
			start = end
			end = tmp
		}
		for c := start; c <= end; c++ {
			match += string(c)
		}
	}

	return match
}

func WS(p Pointer) (Node, Pointer) {
	_, p2 := p.AcceptRun("\t\n\v\f\r \x85\xA0")

	return nil, p2
}