-
Notifications
You must be signed in to change notification settings - Fork 14
/
NumericDesc.scala
232 lines (221 loc) · 10.8 KB
/
NumericDesc.scala
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
/* SPDX-FileCopyrightText: © 2022 Parsley Contributors <https://github.com/j-mie6/Parsley/graphs/contributors>
* SPDX-License-Identifier: BSD-3-Clause
*/
package parsley.token.descriptions.numeric
/** This class, and its subtypes, describe whether or not the plus sign (`+`) is allowed
* in a specific position.
*
* @since 4.0.0
*/
sealed abstract class PlusSignPresence
/** This object contains the concrete subtypes for `PlusSignPresence`.
*
* @since 4.0.0
*/
object PlusSignPresence {
/** When writing a non-negative literal, a `+` is mandatory before the literal.
*
* @since 4.0.0
*/
case object Required extends PlusSignPresence
/** When writing a non-negative literal, a `+` can be added, but is not required.
*
* @since 4.0.0
*/
case object Optional extends PlusSignPresence
/** Positive literals must not be prefixed by a `+`.
*
* @since 4.0.0
*/
case object Illegal extends PlusSignPresence
}
/** This class, and its subtypes, describe how scientific exponent notation can be used within real literals.
*
* @since 4.0.0
*/
sealed abstract class ExponentDesc
/** This object contains the concrete subtypes of `ExponentDesc`.
*
* @since 4.0.0
*/
object ExponentDesc {
/** Exponents are not supported.
*
* @since 4.0.0
*/
case object NoExponents extends ExponentDesc
/** Exponents are supported, which may be compulsory. The base of the exponent can vary, as can whether a positive (`+`) sign
* is allowed before the exponent.
*
* @param compulsory is an exponent ''required'' for the literal (at a specific base) to be valid?
* @param chars the set of possible characters that can start an exponent part of a literal.
* @param base the base of the exponent: for instance `e3` with `base = 10` would represent multiplication by 1000.
* @param positiveSign are positive (`+`) signs allowed, required, or illegal in front of the exponent?
* @since 4.0.0
*/
final case class Supported(compulsory: Boolean,
chars: Set[Char],
base: Int,
positiveSign: PlusSignPresence
) extends ExponentDesc {
require(chars.nonEmpty, "The characters used for floating point exponents must not be empty")
}
}
/** This class, and its subtypes, describe how break characters are supported within literals.
*
* @since 4.0.0
*/
sealed abstract class BreakCharDesc
/** This object contains the concrete subtypes of `BreakCharDesc`.
*
* @since 4.0.0
*/
object BreakCharDesc {
/** Literals cannot be broken.
*
* @since 4.0.0
*/
case object NoBreakChar extends BreakCharDesc
/** Literals may be broken, and this break may be legal after a non-decimal literal prefix
*
* @param breakChar the character allowed to break a literal (often `_`).
* @param allowedAfterNonDecimalPrefix is it possible to write, say, `0x_300`?
* @since 4.0.0
*/
final case class Supported(breakChar: Char, allowedAfterNonDecimalPrefix: Boolean) extends BreakCharDesc
}
/** This class describes how numeric literals, in different bases, should be processed lexically.
*
* @define generic is it possible for generic
* @define genericInt $generic "integer numbers" to be
* @define genericReal $generic "real numbers" to be
* @define genericExp Describes how scientific exponent notation should work for
*
* @param literalBreakChar describes if breaks can be found within numeric literals.
* @param leadingDotAllowed can a real number omit a leading 0 before the point?
* @param trailingDotAllowed can a real number omit a trailing 0 after the point?
* @param leadingZerosAllowed are extraneous zeros allowed at the start of decimal numbers?
* @param positiveSign describes if positive (`+`) signs are allowed, compulsory, or illegal.
* @param integerNumbersCanBeHexadecimal $genericInt hexadecimal?
* @param integerNumbersCanBeOctal $genericInt octal?
* @param integerNumbersCanBeBinary $genericInt binary?
* @param realNumbersCanBeHexadecimal $genericReal hexadecimal?
* @param realNumbersCanBeOctal $genericReal octal?
* @param realNumbersCanBeBinary $genericReal binary?
* @param hexadecimalLeads what characters begin a hexadecimal literal following a `0` (may be empty).
* @param octalLeads what characters begin an octal literal following a `0` (may be empty).
* @param binaryLeads what characters begin a binary literal following a `0` (may be empty).
* @param decimalExponentDesc $genericExp decimal literals.
* @param hexadecimalExponentDesc $genericExp hexadecimal literals.
* @param octalExponentDesc $genericExp octal literals.
* @param binaryExponentDesc $genericExp binary literals.
* @since 4.0.0
*/
// TODO: configurable dot?
final case class NumericDesc (literalBreakChar: BreakCharDesc,
leadingDotAllowed: Boolean,
trailingDotAllowed: Boolean,
leadingZerosAllowed: Boolean,
positiveSign: PlusSignPresence,
// generic number
integerNumbersCanBeHexadecimal: Boolean,
integerNumbersCanBeOctal: Boolean,
integerNumbersCanBeBinary: Boolean,
realNumbersCanBeHexadecimal: Boolean,
realNumbersCanBeOctal: Boolean,
realNumbersCanBeBinary: Boolean,
// special literals
hexadecimalLeads: Set[Char],
octalLeads: Set[Char],
binaryLeads: Set[Char],
// exponents
decimalExponentDesc: ExponentDesc,
hexadecimalExponentDesc: ExponentDesc,
octalExponentDesc: ExponentDesc,
binaryExponentDesc: ExponentDesc
) {
private def boolToInt(x: Boolean): Int = if (x) 1 else 0
locally {
val intHex = boolToInt(integerNumbersCanBeHexadecimal)
val intOct = boolToInt(integerNumbersCanBeOctal)
val intBin = boolToInt(integerNumbersCanBeBinary)
val realHex = boolToInt(realNumbersCanBeHexadecimal)
val realOct = boolToInt(realNumbersCanBeOctal)
val realBin = boolToInt(realNumbersCanBeBinary)
// There can, for either ints or real /number/s be at most 1 empty prefix special
// they can all not require prefixes when they are used explicitly
val emptyHex = boolToInt(hexadecimalLeads.isEmpty)
val emptyOct = boolToInt(octalLeads.isEmpty)
val emptyBin = boolToInt(binaryLeads.isEmpty)
val numEmptyInt = intHex * emptyHex + intOct * emptyOct + intBin * emptyBin
val numEmptyReal = realHex * emptyHex + realOct * emptyOct + realBin * emptyBin
require(numEmptyInt <= 1 && numEmptyReal <= 1,
"More than one of hexadecimal, octal, or binary do not use a prefix in integer or real numbers, this is not allowed as it is ambiguous")
require(numEmptyInt + numEmptyReal == 0 || !leadingZerosAllowed,
"One of hexadecimal, octal, or binary do not use a prefix, so decimal numbers must not allow for leading zeros as it is ambiguous")
}
private [token] def exponentDescForRadix(x: Int): ExponentDesc = (x: @unchecked) match {
case 10 => decimalExponentDesc
case 16 => hexadecimalExponentDesc
case 8 => octalExponentDesc
case 2 => binaryExponentDesc
}
private [token] def decimalIntegersOnly: Boolean = !(integerNumbersCanBeBinary || integerNumbersCanBeHexadecimal || integerNumbersCanBeOctal)
private [token] def decimalRealsOnly: Boolean = !(realNumbersCanBeBinary || realNumbersCanBeHexadecimal || realNumbersCanBeOctal)
}
/** This object contains any preconfigured text definitions.
*
* @since 4.0.0
*/
object NumericDesc {
/** Plain definition of numeric literals. Supports leading zeros; hexadecimal, octal, and binary notation;
* and exponent notation for all four bases too. Only hexadecimal and octal are enabled for integer numbers,
* and only decimal for real numbers.
*
* {{{
* literalBreakChar = BreakCharDesc.NoBreakChar
* leadingDotAllowed = false
* trailingDotAllowed = false
* leadingZerosAllowed = true
* positiveSign = PlusSignPresence.Optional
* integerNumbersCanBeHexadecimal = true
* integerNumbersCanBeOctal = true
* integerNumbersCanBeBinary = false
* realNumbersCanBeHexadecimal = false
* realNumbersCanBeOctal = false
* realNumbersCanBeBinary = false
* hexadecimalLeads = Set('x', 'X')
* octalLeads = Set('o', 'O')
* binaryLeads = Set('b', 'B')
* decimalExponentDesc = ExponentDesc.Supported(compulsory = false, chars = Set('e', 'E'), base = 10, positiveSign = PlusSignPresence.Optional)
* hexadecimalExponentDesc = ExponentDesc.Supported(compulsory = true, chars = Set('p', 'P'), base = 2, positiveSign = PlusSignPresence.Optional)
* octalExponentDesc = ExponentDesc.Supported(compulsory = true, chars = Set('e', 'E', 'p', 'P'), base = 2, positiveSign = PlusSignPresence.Optional)
* binaryExponentDesc = ExponentDesc.Supported(compulsory = true, chars = Set('e', 'E', 'p', 'P'), base = 2, positiveSign = PlusSignPresence.Optional)
* }}}
*
* @since 4.0.0
*/
val plain: NumericDesc = NumericDesc(
literalBreakChar = BreakCharDesc.NoBreakChar,
leadingDotAllowed = false,
trailingDotAllowed = false,
leadingZerosAllowed = true,
positiveSign = PlusSignPresence.Optional,
// generic number
integerNumbersCanBeHexadecimal = true,
integerNumbersCanBeOctal = true,
integerNumbersCanBeBinary = false,
realNumbersCanBeHexadecimal = false,
realNumbersCanBeOctal = false,
realNumbersCanBeBinary = false,
// special literals
hexadecimalLeads = Set('x', 'X'),
octalLeads = Set('o', 'O'),
binaryLeads = Set('b', 'B'),
// exponents
decimalExponentDesc = ExponentDesc.Supported(compulsory = false, chars = Set('e', 'E'), base = 10, positiveSign = PlusSignPresence.Optional),
hexadecimalExponentDesc = ExponentDesc.Supported(compulsory = true, chars = Set('p', 'P'), base = 2, positiveSign = PlusSignPresence.Optional),
octalExponentDesc = ExponentDesc.Supported(compulsory = true, chars = Set('e', 'E', 'p', 'P'), base = 2, positiveSign = PlusSignPresence.Optional),
binaryExponentDesc = ExponentDesc.Supported(compulsory = true, chars = Set('e', 'E', 'p', 'P'), base = 2, positiveSign = PlusSignPresence.Optional)
)
}