-
Notifications
You must be signed in to change notification settings - Fork 49
/
profile.dart
86 lines (75 loc) · 2.33 KB
/
profile.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
import 'package:meta/meta.dart';
import '../core/parser.dart';
import '../parser/action/continuation.dart';
import '../reflection/transform.dart';
import '../shared/types.dart';
/// Returns a transformed [Parser] that when being used measures
/// the activation count and total time of each parser.
///
/// For example, the snippet
///
/// ```dart
/// final parser = letter() & word().star();
/// profile(parser).parse('f1234567890');
/// ```
///
/// prints the following output:
///
/// ```text
/// 1 2006 SequenceParser
/// 1 697 PossessiveRepeatingParser[0..*]
/// 11 406 SingleCharacterParser[letter or digit expected]
/// 1 947 SingleCharacterParser[letter expected]
/// ```
///
/// The first number refers to the number of activations of each parser, and
/// the second number is the microseconds spent in this parser and all its
/// children.
///
/// The optional [output] callback can be used to receive [ProfileFrame]
/// objects with the full profiling information at the end of the parse.
@useResult
Parser<R> profile<R>(Parser<R> root,
{VoidCallback<ProfileFrame> output = print, Predicate<Parser>? predicate}) {
final frames = <ProfileFrame>[];
return transformParser(root, <R>(parser) {
if (predicate == null || predicate(parser)) {
final frame = _ProfileFrame(parser);
frames.add(frame);
return parser.callCC((continuation, context) {
frame.count++;
frame.stopwatch.start();
final result = continuation(context);
frame.stopwatch.stop();
return result;
});
} else {
return parser;
}
}).callCC((continuation, context) {
final result = continuation(context);
frames.forEach(output);
return result;
});
}
/// Encapsulates the data around a parser profile.
abstract class ProfileFrame {
/// Return the parser of this frame.
Parser get parser;
/// Return the number of times this parser was activated.
int get count;
/// Return the total elapsed time in this parser and its children.
Duration get elapsed;
}
class _ProfileFrame extends ProfileFrame {
_ProfileFrame(this.parser);
final stopwatch = Stopwatch();
@override
final Parser parser;
@override
int count = 0;
@override
Duration get elapsed => stopwatch.elapsed;
@override
String toString() => '$count\t${elapsed.inMicroseconds}\t$parser';
}