@@ -60,118 +60,79 @@ function parseSingleStatistic(statistic: string, prefix: string): Omit<SingleSta
60
60
return undefined ;
61
61
}
62
62
63
- let r : RegExpExecArray | null = null ;
63
+ // A decimal positive number regex (1, 1.2, 99.999, etc)
64
+ const reDecimal = '\\d+(?:\\.\\d+)?' ;
64
65
65
66
// p99.99
66
- // /^p(\d{1,2}(?:\.\d+)?)$/
67
- r = new RegExp ( `^${ prefixLower } (\\d{1,2}(?:\\.\\d+)?)$` ) . exec ( statistic ) ;
68
- if ( r ) {
69
- return {
70
- type : 'single' ,
71
- rawStatistic : statistic ,
72
- statPrefix : prefixLower ,
73
- value : parseFloat ( r [ 1 ] ) ,
74
- } ;
67
+ // /^p(\d+(?:\.\d+)?)$/
68
+ const r = new RegExp ( `^${ prefixLower } (${ reDecimal } )$` ) . exec ( statistic ) ;
69
+ if ( ! r ) {
70
+ return undefined ;
75
71
}
76
72
77
- return undefined ;
73
+ const value = parseFloat ( r [ 1 ] ) ;
74
+ if ( value < 0 || value > 100 ) {
75
+ return undefined ;
76
+ }
77
+ return {
78
+ type : 'single' ,
79
+ rawStatistic : statistic ,
80
+ statPrefix : prefixLower ,
81
+ value,
82
+ } ;
78
83
}
79
84
85
+ /**
86
+ * Parse a statistic that looks like `tm( LOWER : UPPER )`.
87
+ */
80
88
function parsePairStatistic ( statistic : string , prefix : string ) : Omit < PairStatistic , 'statName' > | undefined {
81
- const prefixUpper = prefix . toUpperCase ( ) ;
82
-
83
- // Allow `tm(10%:90%)` lowercase
84
- statistic = statistic . toUpperCase ( ) ;
85
-
86
- if ( ! statistic . startsWith ( prefixUpper ) ) {
89
+ const r = new RegExp ( `^${ prefix } \\(([^)]+)\\)$` , 'i' ) . exec ( statistic ) ;
90
+ if ( ! r ) {
87
91
return undefined ;
88
92
}
89
93
90
94
const common : Omit < PairStatistic , 'statName' | 'isPercent' > = {
91
95
type : 'pair' ,
92
96
canBeSingleStat : false ,
93
97
rawStatistic : statistic ,
94
- statPrefix : prefixUpper ,
98
+ statPrefix : prefix . toUpperCase ( ) ,
95
99
} ;
96
100
97
- let r : RegExpExecArray | null = null ;
98
-
99
- // TM(99.999:)
100
- // /TM\((\d{1,2}(?:\.\d+)?):\)/
101
- r = new RegExp ( `^${ prefixUpper } \\((\\d+(?:\\.\\d+)?)\\:\\)$` ) . exec ( statistic ) ;
102
- if ( r ) {
103
- return {
104
- ...common ,
105
- lower : parseFloat ( r [ 1 ] ) ,
106
- upper : undefined ,
107
- isPercent : false ,
108
- } ;
109
- }
110
-
111
- // TM(99.999%:)
112
- // /TM\((\d{1,2}(?:\.\d+)?)%:\)/
113
- r = new RegExp ( `^${ prefixUpper } \\((\\d{1,2}(?:\\.\\d+)?)%\\:\\)$` ) . exec ( statistic ) ;
114
- if ( r ) {
115
- return {
116
- ...common ,
117
- lower : parseFloat ( r [ 1 ] ) ,
118
- upper : undefined ,
119
- isPercent : true ,
120
- } ;
101
+ const [ lhs , rhs ] = r [ 1 ] . split ( ':' ) ;
102
+ if ( rhs === undefined ) {
103
+ // Doesn't have 2 parts
104
+ return undefined ;
121
105
}
122
106
123
- // TM(:99.999)
124
- // /TM\(:(\d{1,2}(?:\.\d+)?)\)/
125
- r = new RegExp ( `^${ prefixUpper } \\(\\:(\\d+(?:\\.\\d+)?)\\)$` ) . exec ( statistic ) ;
126
- if ( r ) {
127
- return {
128
- ...common ,
129
- lower : undefined ,
130
- upper : parseFloat ( r [ 1 ] ) ,
131
- isPercent : false ,
132
- } ;
133
- }
107
+ const parseNumberAndPercent = ( x : string ) : [ number | undefined | 'fail' , boolean ] => {
108
+ x = x . trim ( ) ;
109
+ if ( ! x ) {
110
+ return [ undefined , false ] ;
111
+ }
112
+ const value = parseFloat ( x . replace ( / % $ / , '' ) ) ;
113
+ const percent = x . endsWith ( '%' ) ;
114
+ if ( isNaN ( value ) || value < 0 || ( percent && value > 100 ) ) {
115
+ return [ 'fail' , false ] ;
116
+ }
117
+ return [ value , percent ] ;
118
+ } ;
134
119
135
- // TM(:99.999%)
136
- // /TM\(:(\d{1,2}(?:\.\d+)?)%\)/
137
- // Note: this can be represented as a single stat! TM(:90%) = tm90
138
- r = new RegExp ( `^${ prefixUpper } \\(\\:(\\d{1,2}(?:\\.\\d+)?)%\\)$` ) . exec ( statistic ) ;
139
- if ( r ) {
140
- return {
141
- ...common ,
142
- canBeSingleStat : true ,
143
- asSingleStatStr : `${ prefix . toLowerCase ( ) } ${ r [ 1 ] } ` ,
144
- lower : undefined ,
145
- upper : parseFloat ( r [ 1 ] ) ,
146
- isPercent : true ,
147
- } ;
120
+ const [ lower , lhsPercent ] = parseNumberAndPercent ( lhs ) ;
121
+ const [ upper , rhsPercent ] = parseNumberAndPercent ( rhs ) ;
122
+ if ( lower === 'fail' || upper === 'fail' || ( lower === undefined && upper === undefined ) ) {
123
+ return undefined ;
148
124
}
149
125
150
- // TM(99.999:99.999)
151
- // /TM\((\d{1,2}(?:\.\d+)?):(\d{1,2}(?:\.\d+)?)\)/
152
- r = new RegExp ( `^${ prefixUpper } \\((\\d+(?:\\.\\d+)?)\\:(\\d+(?:\\.\\d+)?)\\)$` ) . exec ( statistic ) ;
153
- if ( r ) {
154
- return {
155
- ...common ,
156
- lower : parseFloat ( r [ 1 ] ) ,
157
- upper : parseFloat ( r [ 2 ] ) ,
158
- isPercent : false ,
159
- } ;
126
+ if ( lower !== undefined && upper !== undefined && lhsPercent !== rhsPercent ) {
127
+ // If one value is a percentage, the other one must be too
128
+ return undefined ;
160
129
}
161
130
162
- // TM(99.999%:99.999%)
163
- // /TM\((\d{1,2}(?:\.\d+)?)%:(\d{1,2}(?:\.\d+)?)%\)/
164
- r = new RegExp ( `^${ prefixUpper } \\((\\d{1,2}(?:\\.\\d+)?)%\\:(\\d{1,2}(?:\\.\\d+)?)%\\)$` ) . exec ( statistic ) ;
165
- if ( r ) {
166
- return {
167
- ...common ,
168
- lower : parseFloat ( r [ 1 ] ) ,
169
- upper : parseFloat ( r [ 2 ] ) ,
170
- isPercent : true ,
171
- } ;
172
- }
131
+ const isPercent = lhsPercent || rhsPercent ;
132
+ const canBeSingleStat = lower === undefined && isPercent ;
133
+ const asSingleStatStr = canBeSingleStat ? `${ prefix . toLowerCase ( ) } ${ upper } ` : undefined ;
173
134
174
- return undefined ;
135
+ return { ... common , lower , upper , isPercent , canBeSingleStat , asSingleStatStr } ;
175
136
}
176
137
177
138
export function singleStatisticToString ( parsed : SingleStatistic ) : string {
0 commit comments