diff --git a/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScalametaParser.scala b/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScalametaParser.scala index 7204e29961..846d444544 100644 --- a/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScalametaParser.scala +++ b/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScalametaParser.scala @@ -1096,8 +1096,9 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser => case _: Literal => if (dialect.allowLiteralTypes) literal() else syntaxError(s"$dialect doesn't support literal types", at = path()) - case Ident("-") if dialect.allowLiteralTypes && tryAhead[NumericConstant[_]] => - numericLiteral(prevTokenPos, isNegated = true) + case Unary.Numeric((_, unary)) + if dialect.allowLiteralTypes && tryAhead[NumericConstant[_]] => + numericLiteral(prevTokenPos, unary) case _ => pathSimpleType() } simpleTypeRest(autoEndPosOpt(startPos)(res), startPos) @@ -1315,28 +1316,24 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser => if (acceptOpt[Dot]) selectors(name) else name } - private def numericLiteral(startPos: Int, isNegated: Boolean): Lit = { + private def numericLiteral(startPos: Int, unary: Unary.Numeric): Lit = { val number = token.asInstanceOf[NumericConstant[_]] next() - autoEndPos(startPos)(numericLiteralAt(number, isNegated)) + autoEndPos(startPos)(numericLiteralAt(number, unary)) } - private def numericLiteralAt(token: NumericConstant[_], isNegated: Boolean): Lit = { + private def numericLiteralAt(token: NumericConstant[_], unary: Unary.Numeric): Lit = { def getBigDecimal(tok: NumericConstant[BigDecimal]) = - if (isNegated) -tok.value else tok.value + unary(tok.value) + .getOrElse(syntaxError(s"bad unary op `${unary.op}` for floating-point", at = tok)) def getBigInt(tok: NumericConstant[BigInt], dec: BigInt, hex: BigInt, typ: String) = { // decimal never starts with `0` as octal was removed in 2.11; "hex" includes `0x` or `0b` // non-decimal literals allow signed overflow within unsigned range val max = if (tok.text(0) != '0') dec else hex - // token value is always positive as it doesn't take into account a sign - val value = tok.value - if (isNegated) { - if (value > max) syntaxError(s"integer number too small for $typ", at = token) - -value - } else { - if (value >= max) syntaxError(s"integer number too large for $typ", at = token) - value - } + val value = unary(tok.value) + if (value >= max) syntaxError(s"integer number too large for $typ", at = tok) + if (value < -max) syntaxError(s"integer number too small for $typ", at = tok) + value } token match { case tok: Constant.Int => @@ -1353,7 +1350,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser => } def literal(): Lit = atCurPosNext(token match { - case number: NumericConstant[_] => numericLiteralAt(number, false) + case number: NumericConstant[_] => numericLiteralAt(number, Unary.Noop) case Constant.Char(value) => Lit.Char(value) case Constant.String(value) => Lit.String(value) case t: Constant.Symbol => @@ -2212,15 +2209,25 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser => } } - def prefixExpr(allowRepeated: Boolean): Term = - if (!isUnaryOp) simpleExpr(allowRepeated) - else { + def prefixExpr(allowRepeated: Boolean): Term = token match { + case Unary((ident, unary)) => val startPos = tokenPos - val op = termName() + def op = atPos(startPos)(Term.Name(ident)) + next() def rest(tree: Term) = simpleExprRest(tree, canApply = true, startPos = startPos) - if (op.value == "-" && token.is[NumericConstant[_]]) - rest(numericLiteral(startPos, isNegated = true)) - else { + val withUnary = unary match { + case unary: Unary.Numeric => + token match { + case x: NumericConstant[_] => next(); Some(numericLiteralAt(x, unary)) + case _ => None + } + case unary: Unary.Logical => + token match { + case x: BooleanConstant => next(); Some(Lit.Boolean(unary(x.value))) + case _ => None + } + } + withUnary.fold { simpleExpr0(allowRepeated = true) match { case Success(result) => autoEndPos(startPos)(Term.ApplyUnary(op, result)) case Failure(_) => @@ -2228,8 +2235,9 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser => // we would fail here anyway, let's try to treat it as ident rest(op) } - } - } + }(lit => rest(autoEndPos(startPos)(lit))) + case _ => simpleExpr(allowRepeated) + } def simpleExpr(allowRepeated: Boolean): Term = simpleExpr0(allowRepeated).get @@ -2893,9 +2901,9 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser => autoEndPos(startPos)(token match { case sidToken @ (_: Ident | _: KwThis | _: Unquote) => val sid = stableId() - if (token.is[NumericConstant[_]]) { - sid match { - case Term.Name("-") => return numericLiteral(startPos, isNegated = true) + if (prevTokenPos == startPos && token.is[NumericConstant[_]]) { + sidToken match { + case Unary.Numeric((_, unary)) => return numericLiteral(startPos, unary) case _ => } } diff --git a/tests/shared/src/test/scala/scala/meta/tests/parsers/LitSuite.scala b/tests/shared/src/test/scala/scala/meta/tests/parsers/LitSuite.scala index 879221602d..e6684e0f46 100644 --- a/tests/shared/src/test/scala/scala/meta/tests/parsers/LitSuite.scala +++ b/tests/shared/src/test/scala/scala/meta/tests/parsers/LitSuite.scala @@ -271,7 +271,7 @@ class LitSuite extends ParseSuite { } test("unary: +1") { - runTestAssert[Stat]("+1")(Term.ApplyUnary(tname("+"), lit(1))) + runTestAssert[Stat]("+1", "1")(lit(1)) } test("unary: -1") { @@ -279,7 +279,7 @@ class LitSuite extends ParseSuite { } test("unary: ~1") { - runTestAssert[Stat]("~1")(Term.ApplyUnary(tname("~"), lit(1))) + runTestAssert[Stat]("~1", "-2")(lit(-2)) } test("unary: !1") { @@ -287,7 +287,7 @@ class LitSuite extends ParseSuite { } test("unary: +1.0") { - runTestAssert[Stat]("+1.0", "+1.0d")(Term.ApplyUnary(tname("+"), lit(1d))) + runTestAssert[Stat]("+1.0", "1.0d")(lit(1d)) } test("unary: -1.0") { @@ -295,7 +295,11 @@ class LitSuite extends ParseSuite { } test("unary: ~1.0") { - runTestAssert[Stat]("~1.0", "~1.0d")(Term.ApplyUnary(tname("~"), lit(1d))) + val error = + """|:1: error: bad unary op `~` for floating-point + |~1.0 + | ^""".stripMargin + runTestError[Stat]("~1.0", error) } test("unary: !1.0") { @@ -303,11 +307,11 @@ class LitSuite extends ParseSuite { } test("unary: !true") { - runTestAssert[Stat]("!true")(Term.ApplyUnary(tname("!"), lit(true))) + runTestAssert[Stat]("!true", "false")(lit(false)) } test("unary: !false") { - runTestAssert[Stat]("!false")(Term.ApplyUnary(tname("!"), lit(false))) + runTestAssert[Stat]("!false", "true")(lit(true)) } }