-
Notifications
You must be signed in to change notification settings - Fork 47
/
trace.dart
96 lines (83 loc) · 2.69 KB
/
trace.dart
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
import 'package:meta/meta.dart';
import '../core/context.dart';
import '../core/parser.dart';
import '../core/result.dart';
import '../parser/action/continuation.dart';
import '../reflection/transform.dart';
import '../shared/types.dart';
/// Returns a transformed [Parser] that when being used to read input prints a
/// trace of all activated parsers and their respective parse results.
///
/// For example, the snippet
///
/// ```dart
/// final parser = letter() & word().star();
/// trace(parser).parse('f1');
/// ```
///
/// produces the following output:
///
/// ```text
/// SequenceParser<dynamic>
/// SingleCharacterParser[letter expected]
/// Success[1:2]: f
/// PossessiveRepeatingParser<String>[0..*]
/// SingleCharacterParser[letter or digit expected]
/// Success[1:3]: 1
/// SingleCharacterParser[letter or digit expected]
/// Failure[1:3]: letter or digit expected
/// Success[1:3]: [1]
/// Success[1:3]: [f, [1]]
/// ```
///
/// Indentation signifies the activation of a parser object. Reverse indentation
/// signifies the returning of a parse result either with a success or failure
/// context.
///
/// The optional [output] callback can be used to continuously receive
/// [TraceEvent] objects with current enter and exit data.
@useResult
Parser<R> trace<R>(Parser<R> root,
{VoidCallback<TraceEvent> output = print, Predicate<Parser>? predicate}) {
TraceEvent? parent;
return transformParser(root, <R>(parser) {
if (predicate == null || predicate(parser)) {
return parser.callCC((continuation, context) {
final currentParent = parent;
output(parent = _TraceEvent(currentParent, parser, context));
final result = continuation(context);
output(_TraceEvent(currentParent, parser, context, result));
parent = currentParent;
return result;
});
} else {
return parser;
}
});
}
/// Encapsulates the entry and exit data around a parser trace.
abstract class TraceEvent {
/// Returns the parent trace event.
TraceEvent? get parent;
/// Returns the parser of this event.
Parser get parser;
/// Returns the activation context of this event.
Context get context;
/// Returns the result if this is a exit event, otherwise `null`.
Result<dynamic>? get result;
/// Returns the nesting level of this event.
int get level => parent != null ? parent!.level + 1 : 0;
}
class _TraceEvent extends TraceEvent {
_TraceEvent(this.parent, this.parser, this.context, [this.result]);
@override
final TraceEvent? parent;
@override
final Parser parser;
@override
final Context context;
@override
final Result<dynamic>? result;
@override
String toString() => '${' ' * level}${result ?? parser}';
}