-
Notifications
You must be signed in to change notification settings - Fork 0
/
parse.go
93 lines (85 loc) · 2.87 KB
/
parse.go
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
package parse
import (
"github.com/zalgonoise/gio"
"github.com/zalgonoise/lex"
)
// ParseFn is similar to the Lexer's StateFn, as a recursive function that the Tree
// will keep calling during runtime until it runs out of items received from the Lexer
//
// The ParseFn will return another ParseFn that will keep processing the items; which
// could be done in a number of ways (switch statements, helper functions, etc). When
// `nil` is returned, the parser will stop processing lex items
type ParseFn[C comparable, T any] func(t *Tree[C, T]) ParseFn[C, T]
// ProcessFn is a function that can be executed after parsing all the items, and will
// return a known-good type for the developer to work on. This is a step taken after a
// Tree is built
type ProcessFn[C comparable, T any, R any] func(t *Tree[C, T]) (R, error)
// NodeFn is a function that can be executed against a single node, when processing the
// parse.Tree
type NodeFn[C comparable, T any, R any] func(n *Node[C, T]) (R, error)
// Run encapsulates the lexer and parser runtime into a single one-shot action
//
// The caller must supply the input data []T `input`, a lex.StateFn to kick-off the lexer,
// a ParseFn to kick-off the parser, and a ProcessFn to convert the parse.Tree into the
// desired return type (or an error)
func Run[C comparable, T any, R any](
input []T,
initStateFn lex.StateFn[C, T],
initParseFn ParseFn[C, T],
processFn ProcessFn[C, T, R],
) (R, error) {
var rootEOF C
l := (lex.Lexer[C, T])(lex.New(initStateFn, input))
t := New((lex.Emitter[C, T])(l), initParseFn, rootEOF)
t.Parse()
return processFn(t)
}
// To is similar to Run, but writes the processed type to type *R `output`
func To[C comparable, T any, R any](
input []T,
initStateFn lex.StateFn[C, T],
initParseFn ParseFn[C, T],
processFn ProcessFn[C, T, R],
output *R,
) (err error) {
var rootEOF C
l := lex.New(initStateFn, input)
t := New((lex.Emitter[C, T])(l), initParseFn, rootEOF)
t.Parse()
if output == nil {
output = new(R)
}
*output, err = processFn(t)
return err
}
// Parse is similar to Run, but it consumes a generic io.Reader, instead
func Parse[C comparable, T any, R any](
input gio.Reader[T],
initStateFn lex.StateFn[C, T],
initParseFn ParseFn[C, T],
processFn ProcessFn[C, T, R],
) (R, error) {
var rootEOF C
l := (lex.Lexer[C, T])(lex.NewBuffer(initStateFn, input))
t := New((lex.Emitter[C, T])(l), initParseFn, rootEOF)
t.Parse()
return processFn(t)
}
// ParseTo is similar to Run, but writes the processed type to type *R `output`
func ParseTo[C comparable, T any, R any](
input gio.Reader[T],
initStateFn lex.StateFn[C, T],
initParseFn ParseFn[C, T],
processFn ProcessFn[C, T, R],
output *R,
) (err error) {
var rootEOF C
l := lex.NewBuffer(initStateFn, input)
t := New((lex.Emitter[C, T])(l), initParseFn, rootEOF)
t.Parse()
if output == nil {
output = new(R)
}
*output, err = processFn(t)
return err
}