Skip to content

Commit

Permalink
Added leadingZeroConfig for exponents, closing #211
Browse files Browse the repository at this point in the history
  • Loading branch information
j-mie6 committed Jan 12, 2024
1 parent eb97062 commit 18428c6
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 25 deletions.
2 changes: 1 addition & 1 deletion parsley/shared/src/main/scala/parsley/token/Lexer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -664,7 +664,7 @@ final class Lexer(desc: descriptions.LexicalDesc, errConfig: errors.ErrorConfig)

private [Lexer] val _natural = new UnsignedInteger(desc.numericDesc, errConfig, generic)
private [Lexer] val _integer = new SignedInteger(desc.numericDesc, _natural, errConfig)
private [Lexer] val _positiveReal = new UnsignedReal(desc.numericDesc, _natural, errConfig, generic)
private [Lexer] val _positiveReal = new UnsignedReal(desc.numericDesc, errConfig, generic)
private [Lexer] val _real = new SignedReal(desc.numericDesc, _positiveReal, errConfig)
private [Lexer] val _unsignedCombined = new UnsignedCombined(desc.numericDesc, _natural, _positiveReal, errConfig)
private [Lexer] val _signedCombined = new SignedCombined(desc.numericDesc, _unsignedCombined, errConfig)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,14 @@ object ExponentDesc {
* @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?
* @param leadingZerosAllowed are extraneous zeros allowed at the start of the exponent?
* @since 4.0.0
*/
final case class Supported(compulsory: Boolean,
chars: Set[Char],
base: Int,
positiveSign: PlusSignPresence
positiveSign: PlusSignPresence,
leadingZerosAllowed: Boolean,
) extends ExponentDesc {
require(chars.nonEmpty, "The characters used for floating point exponents must not be empty")
}
Expand Down Expand Up @@ -137,7 +139,7 @@ final case class NumericDesc (literalBreakChar: BreakCharDesc,
decimalExponentDesc: ExponentDesc,
hexadecimalExponentDesc: ExponentDesc,
octalExponentDesc: ExponentDesc,
binaryExponentDesc: ExponentDesc
binaryExponentDesc: ExponentDesc,
) {
private def boolToInt(x: Boolean): Int = if (x) 1 else 0

Expand Down Expand Up @@ -199,10 +201,10 @@ object NumericDesc {
* 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)
* decimalExponentDesc = ExponentDesc.Supported(compulsory = false, chars = Set('e', 'E'), base = 10, positiveSign = PlusSignPresence.Optional, leadingZerosAllowed = true)
* hexadecimalExponentDesc = ExponentDesc.Supported(compulsory = true, chars = Set('p', 'P'), base = 2, positiveSign = PlusSignPresence.Optional, leadingZerosAllowed = true)
* octalExponentDesc = ExponentDesc.Supported(compulsory = true, chars = Set('e', 'E', 'p', 'P'), base = 2, positiveSign = PlusSignPresence.Optional, leadingZerosAllowed = true)
* binaryExponentDesc = ExponentDesc.Supported(compulsory = true, chars = Set('e', 'E', 'p', 'P'), base = 2, positiveSign = PlusSignPresence.Optional, leadingZerosAllowed = true)
* }}}
*
* @since 4.0.0
Expand All @@ -225,9 +227,13 @@ object NumericDesc {
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)
decimalExponentDesc =
ExponentDesc.Supported(compulsory = false, chars = Set('e', 'E'), base = 10, positiveSign = PlusSignPresence.Optional, leadingZerosAllowed = true),
hexadecimalExponentDesc =
ExponentDesc.Supported(compulsory = true, chars = Set('p', 'P'), base = 2, positiveSign = PlusSignPresence.Optional, leadingZerosAllowed = true),
octalExponentDesc =
ExponentDesc.Supported(compulsory = true, chars = Set('e', 'E', 'p', 'P'), base = 2, positiveSign = PlusSignPresence.Optional, leadingZerosAllowed = true),
binaryExponentDesc =
ExponentDesc.Supported(compulsory = true, chars = Set('e', 'E', 'p', 'P'), base = 2, positiveSign = PlusSignPresence.Optional, leadingZerosAllowed = true),
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import parsley.syntax.character.charLift
import parsley.token.descriptions.numeric.{BreakCharDesc, ExponentDesc, NumericDesc}
import parsley.token.errors.{ErrorConfig, LabelConfig}

private [token] final class UnsignedReal(desc: NumericDesc, natural: UnsignedInteger, err: ErrorConfig, generic: Generic) extends RealParsers(err) {
private [token] final class UnsignedReal(desc: NumericDesc, err: ErrorConfig, generic: Generic) extends RealParsers(err) {
override lazy val _decimal: Parsley[BigDecimal] = atomic(ofRadix(10, digit, err.labelRealDecimalEnd))
override lazy val _hexadecimal: Parsley[BigDecimal] = atomic('0' *> noZeroHexadecimal)
override lazy val _octal: Parsley[BigDecimal] = atomic('0' *> noZeroOctal)
Expand Down Expand Up @@ -104,12 +104,14 @@ private [token] final class UnsignedReal(desc: NumericDesc, natural: UnsignedInt
}
}
val (requiredExponent, exponent, base) = expDesc match {
case ExponentDesc.Supported(compulsory, exp, base, sign) =>
case ExponentDesc.Supported(compulsory, exp, base, sign, leadingZeros) =>
val expErr = new ErrorConfig {
override def labelIntegerSignedDecimal(bits: Int) = err.labelRealExponentEnd.orElse(endLabel)
override def labelIntegerDecimalEnd = err.labelRealExponentEnd.orElse(endLabel)
}
val integer = new SignedInteger(desc.copy(positiveSign = sign), natural, expErr)
val expIntDesc = desc.copy(positiveSign = sign, leadingZerosAllowed = leadingZeros)
val natural = new UnsignedInteger(expIntDesc, expErr, generic)
val integer = new SignedInteger(expIntDesc, natural, expErr)
val exponent = err.labelRealExponent.orElse(endLabel)(oneOf(exp)) *> integer.decimal32
if (compulsory) (exponent, exponent, base)
else (exponent, exponent <|> pure(0), base)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ class DescriptionRequireTests extends ParsleyTest {
}

"ExponentDesc.Supported" should "not allow for empty exponents" in {
an [IllegalArgumentException] should be thrownBy ExponentDesc.Supported(false, Set.empty, 10, PlusSignPresence.Illegal)
an [IllegalArgumentException] should be thrownBy ExponentDesc.Supported(false, Set.empty, 10, PlusSignPresence.Optional)
an [IllegalArgumentException] should be thrownBy ExponentDesc.Supported(false, Set.empty, 10, PlusSignPresence.Illegal, true)
an [IllegalArgumentException] should be thrownBy ExponentDesc.Supported(false, Set.empty, 10, PlusSignPresence.Optional, true)
}

"NumericDesc" should "not allow for multiple prefixless descriptions" in {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import org.scalactic.source.Position
class RealTests extends ParsleyTest {
val errConfig = new ErrorConfig
val generic = new Generic(errConfig)
private def makeReal(desc: NumericDesc) = new LexemeReal(new SignedReal(desc, new UnsignedReal(desc, new UnsignedInteger(desc, errConfig, generic), errConfig, generic), errConfig), LexemeImpl.empty, errConfig)
private def makeReal(desc: NumericDesc) = new LexemeReal(new SignedReal(desc, new UnsignedReal(desc, errConfig, generic), errConfig), LexemeImpl.empty, errConfig)

val plain = NumericDesc.plain.copy(decimalExponentDesc = NoExponents, hexadecimalExponentDesc = NoExponents,
octalExponentDesc = NoExponents, binaryExponentDesc = NoExponents)
Expand Down Expand Up @@ -77,7 +77,7 @@ class RealTests extends ParsleyTest {
decimalCases(withTrailingDotBreak)("0._1" -> None)
}
it should "allow for scientific notation when configured" in {
val plainExp = plain.copy(decimalExponentDesc = ExponentDesc.Supported(false, Set('e', 'E'), 10, PlusSignPresence.Optional))
val plainExp = plain.copy(decimalExponentDesc = ExponentDesc.Supported(false, Set('e', 'E'), 10, PlusSignPresence.Optional, true))
val withExtremeDotExpDesc = plainExp.copy(leadingDotAllowed = true, trailingDotAllowed = true)
decimalCases(plainExp)(
"3.0e3" -> Some(BigDecimal("3000.0")),
Expand Down Expand Up @@ -111,10 +111,11 @@ class RealTests extends ParsleyTest {
)
}
it should "allow for scientific notation when configured" in {
val plainExp = plain.copy(hexadecimalExponentDesc = ExponentDesc.Supported(true, Set('p', 'P'), 2, PlusSignPresence.Optional))
val plainExp = plain.copy(hexadecimalExponentDesc = ExponentDesc.Supported(true, Set('p', 'P'), 2, PlusSignPresence.Optional, false))
val withExtremeDotExpDesc = plainExp.copy(leadingDotAllowed = true, trailingDotAllowed = true)
hexadecimalCases(plainExp)(
"0x0.f" -> None,
"0x0.fp0002" -> None,
"0x0.fp0" -> Some(BigDecimal("0.9375")),
"0x0.fP1" -> Some(BigDecimal("1.875")),
"0xa.c7p-2" -> Some(BigDecimal("10.77734375")/4),
Expand Down Expand Up @@ -147,11 +148,11 @@ class RealTests extends ParsleyTest {
)
}
it should "allow for scientific notation when configured" in {
val plainExp = plain.copy(octalExponentDesc = ExponentDesc.Supported(true, Set('e', 'E'), 10, PlusSignPresence.Required))
val plainExp = plain.copy(octalExponentDesc = ExponentDesc.Supported(true, Set('e', 'E'), 10, PlusSignPresence.Required, true))
val withExtremeDotExpDesc = plainExp.copy(leadingDotAllowed = true, trailingDotAllowed = true)
octalCases(plainExp)(
"0o0.6" -> None,
"0o0.4e+0" -> Some(BigDecimal("0.5")),
"0o0.4e+000" -> Some(BigDecimal("0.5")),
"0o0.4E+1" -> Some(BigDecimal("5.0")),
"0o5.42e-2" -> Some(BigDecimal("5.53125")/100),
)
Expand Down Expand Up @@ -183,7 +184,7 @@ class RealTests extends ParsleyTest {
)
}
it should "allow for scientific notation when configured" in {
val plainExp = plain.copy(binaryExponentDesc = ExponentDesc.Supported(true, Set('p', 'P'), 2, PlusSignPresence.Illegal),
val plainExp = plain.copy(binaryExponentDesc = ExponentDesc.Supported(true, Set('p', 'P'), 2, PlusSignPresence.Illegal, true),
literalBreakChar = BreakCharDesc.Supported('_', false))
val withExtremeDotExpDesc = plainExp.copy(leadingDotAllowed = true, trailingDotAllowed = true)
binaryCases(plainExp)(
Expand Down Expand Up @@ -221,9 +222,9 @@ class RealTests extends ParsleyTest {

// bounded things (only decimal)
"bounded reals" should "not permit illegal numbers" in {
val represent = makeReal(plain.copy(decimalExponentDesc = ExponentDesc.Supported(false, Set('e', 'E'), 10, PlusSignPresence.Optional),
hexadecimalExponentDesc = ExponentDesc.Supported(true, Set('p'), 2, PlusSignPresence.Required),
binaryExponentDesc = ExponentDesc.Supported(true, Set('p'), 2, PlusSignPresence.Required)))
val represent = makeReal(plain.copy(decimalExponentDesc = ExponentDesc.Supported(false, Set('e', 'E'), 10, PlusSignPresence.Optional, true),
hexadecimalExponentDesc = ExponentDesc.Supported(true, Set('p'), 2, PlusSignPresence.Required, true),
binaryExponentDesc = ExponentDesc.Supported(true, Set('p'), 2, PlusSignPresence.Required, true)))
cases(represent.decimalDouble)(
"0.4" -> Some(0.4),
"0.33333333333333333333333" -> Some(0.33333333333333333333333),
Expand Down

0 comments on commit 18428c6

Please sign in to comment.