Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NumberParser #351

Merged
merged 2 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ private object ExpressionParser {
ReferenceCallParser.parseOrFail |
AnnotationParser.parseOrFail |
TupleParser.parseOrFail |
NumberParser.parseOrFail |
BooleanParser.parseOrFail |
IdentifierParser.parseOrFail
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package org.alephium.ralph.lsp.access.compiler.parser.soft

import fastparse._
import fastparse.NoWhitespace.noWhitespaceImplicit
import org.alephium.ralph.lsp.access.compiler.message.SourceIndexExtra._
import org.alephium.ralph.lsp.access.compiler.parser.soft.ast.{SoftAST, Token}

object NumberParser {

def parseOrFail[Unknown: P]: P[SoftAST.Number] =
P {
Index ~
CommentParser.parseOrFail.? ~
number ~
SpaceParser.parseOrFail.? ~
TokenParser.parseOrFail(Token.AlphLowercase).? ~
Index
} map {
case (from, documentation, digits, postDigitSpace, unit, to) =>
SoftAST.Number(
index = range(from, to),
documentation = documentation,
number = digits,
space = postDigitSpace,
unit = unit
)
}

private def number[Unknown: P]: P[SoftAST.CodeString] =
P {
Index ~
isNumber ~
(!Token.AlphLowercase.lexeme ~ CharIn("[0-9a-zA-Z]._+\\-")).rep(1).! ~
Index
} map {
case (from, number, to) =>
SoftAST.CodeString(
index = range(from, to),
text = number
)
}

private def isNumber[Unknown: P]: P[Unit] =
P(&(CharIn("+\\-").? ~ CharIn("[0-9]")))

}
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,14 @@ object SoftAST {
postTupleSpace: Option[Space])
extends ExpressionAST

case class Number(
index: SourceIndex,
documentation: Option[Comments],
number: CodeString,
space: Option[Space],
unit: Option[TokenDocumented[Token.AlphLowercase.type]])
extends ExpressionAST

sealed trait SpaceAST extends SoftAST

case class Space(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,8 @@ object Token {
case object False extends PrimitiveBoolean("false") with Reserved

sealed abstract class PrimitiveUnit(override val lexeme: String) extends Primitive(lexeme)
case object Alph_Small extends PrimitiveUnit("alph") with Reserved
case object Alph_Big extends PrimitiveUnit("ALPH") with Reserved
case object AlphLowercase extends PrimitiveUnit("alph") with Reserved
case object AlphUppercase extends PrimitiveUnit("ALPH") with Reserved

sealed trait Term extends Token
case class Name(lexeme: String) extends Term
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
package org.alephium.ralph.lsp.access.compiler.parser.soft

import org.alephium.ralph.lsp.access.compiler.parser.soft.TestParser._
import org.alephium.ralph.lsp.access.compiler.parser.soft.ast.SoftAST
import org.alephium.ralph.lsp.access.compiler.parser.soft.ast.TestSoftAST._
import org.alephium.ralph.lsp.access.util.TestCodeUtil._
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec

class NumberParserSpec extends AnyWordSpec with Matchers {

def assertSimpleNumber(number: String) =
parseNumber(number) shouldBe
Number(
index = indexOf(s">>$number<<"),
text = number
)

"integer" in {
assertSimpleNumber("10")
assertSimpleNumber("-10")
assertSimpleNumber("+10")
}

"typed" in {
assertSimpleNumber("10u")
assertSimpleNumber("10i")
assertSimpleNumber("-10u")
assertSimpleNumber("-10i")
assertSimpleNumber("+10u")
assertSimpleNumber("+10i")
}

"scientific" in {
assertSimpleNumber("1e18")
assertSimpleNumber("-1e18")
assertSimpleNumber("+1e18")
assertSimpleNumber("5.12e18")
assertSimpleNumber("-5.12e18")
assertSimpleNumber("+5.12e18")
}

"underscored" in {
assertSimpleNumber("1_000_000_000")
assertSimpleNumber("-1_000_000_000")
assertSimpleNumber("+1_000_000_000")
}

"hex" in {
assertSimpleNumber("0x12")
assertSimpleNumber("-0x12")
assertSimpleNumber("+0x12")
}

"unit" when {
"invalid" in {
assertSimpleNumber("1alp")
assertSimpleNumber("-1alp")
assertSimpleNumber("+1alp")

assertSimpleNumber("1lph")
assertSimpleNumber("-1lph")
assertSimpleNumber("+1lph")

assertSimpleNumber("1hpla")
assertSimpleNumber("-1hpla")
assertSimpleNumber("+1hpla")
}

"valid" when {
"no spaces" when {
def testWithUnit(numberOnly: String) =
parseNumber(s"${numberOnly}alph") shouldBe
SoftAST.Number(
index = indexOf(s">>${numberOnly}alph<<"),
documentation = None,
number = SoftAST.CodeString(
index = indexOf(s">>$numberOnly<<alph"),
text = numberOnly
),
space = None,
unit = Some(AlphLowercase(indexOf(s"$numberOnly>>alph<<")))
)

"no sign" in {
testWithUnit("1")
testWithUnit("5.12e18")
testWithUnit("0x12")
}

"positive" in {
testWithUnit("+1")
testWithUnit("+5.12e18")
testWithUnit("+0x12")
}

"negative" in {
testWithUnit("-1")
testWithUnit("-5.12e18")
testWithUnit("-0x12")
}
}

"with space" when {
def testWithUnit(numberOnly: String) =
parseNumber(s"$numberOnly alph") shouldBe
SoftAST.Number(
index = indexOf(s">>$numberOnly alph<<"),
documentation = None,
number = SoftAST.CodeString(
index = indexOf(s">>$numberOnly<< alph"),
text = numberOnly
),
space = Some(SpaceOne(indexOf(s"$numberOnly>> <<alph"))),
unit = Some(AlphLowercase(indexOf(s"$numberOnly >>alph<<")))
)

"no sign" in {
testWithUnit("1")
testWithUnit("5.12e18")
testWithUnit("0x12")
}

"positive" in {
testWithUnit("+1")
testWithUnit("+5.12e18")
testWithUnit("+0x12")
}

"negative" in {
testWithUnit("-1")
testWithUnit("-5.12e18")
testWithUnit("-0x12")
}
}

"scientific number" when {
"without space" when {
"valid unit" in {
parseNumber("1e-18alph") shouldBe
SoftAST.Number(
index = indexOf(s">>1e-18alph<<"),
documentation = None,
number = SoftAST.CodeString(
index = indexOf(s">>1e-18<<alph"),
text = "1e-18"
),
space = None,
unit = Some(AlphLowercase(indexOf("1e-18>>alph<<")))
)
}

"invalid unit - 'alp' is typo" in {
parseNumber("1e-18alp") shouldBe
SoftAST.Number(
index = indexOf(s">>1e-18alp<<"),
documentation = None,
number = SoftAST.CodeString(
index = indexOf(s">>1e-18alp<<"),
text = "1e-18alp"
),
space = None,
unit = None
)
}
}

"with space" when {
"valid unit" in {
parseNumber("1e-18 alph") shouldBe
SoftAST.Number(
index = indexOf(s">>1e-18 alph<<"),
documentation = None,
number = SoftAST.CodeString(
index = indexOf(s">>1e-18<< alph"),
text = "1e-18"
),
space = Some(SpaceOne(indexOf("1e-18>> <<alph"))),
unit = Some(AlphLowercase(indexOf("1e-18 >>alph<<")))
)
}

"invalid unit - 'alp' is typo" in {
val body = parseSoft("1e-18 alp")
body.parts should have size 2

val number = body.parts.head.part
val alp = body.parts.last.part

// Note: alp is not a unit. So it's not parsed as part of the number.
number shouldBe
SoftAST.Number(
index = indexOf(s">>1e-18 << alp"),
documentation = None,
number = SoftAST.CodeString(
index = indexOf(s">>1e-18<< alp"),
text = "1e-18"
),
space = Some(SpaceOne(indexOf("1e-18>> <<alp"))),
unit = None
)

// alp is stored as an identifier
alp shouldBe
Identifier(
index = indexOf(s"1e-18 >>alp<<"),
text = "alp"
)
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice tests as always


}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ object TestParser {
def parseBoolean(code: String): SoftAST.TokenExpression[Token.PrimitiveBoolean] =
runSoftParser(BooleanParser.parseOrFail(_))(code)

def parseNumber(code: String): SoftAST.Number =
runSoftParser(NumberParser.parseOrFail(_))(code)

def findAnnotation(identifier: String)(code: String): Option[SoftAST.Annotation] =
findAnnotation(
identifier = identifier,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,18 @@ object TestSoftAST {
token = Token.False
)

def AlphLowercase(index: SourceIndex): SoftAST.TokenDocumented[Token.AlphLowercase.type] =
TokenDocumented(
index = index,
token = Token.AlphLowercase
)

def AlphUppercase(index: SourceIndex): SoftAST.TokenDocumented[Token.AlphUppercase.type] =
TokenDocumented(
index = index,
token = Token.AlphUppercase
)

def Identifier(
index: SourceIndex,
text: String): SoftAST.Identifier =
Expand All @@ -102,6 +114,20 @@ object TestSoftAST {
)
)

def Number(
index: SourceIndex,
text: String): SoftAST.Number =
SoftAST.Number(
index = index,
documentation = None,
number = SoftAST.CodeString(
index = index,
text = text
),
space = None,
unit = None
)

def Unresolved(
index: SourceIndex,
text: String): SoftAST.Unresolved =
Expand Down
Loading