Skip to content

Commit

Permalink
Provide compile-time-checkd fully-qualified classname class
Browse files Browse the repository at this point in the history
  • Loading branch information
propensive committed Mar 14, 2024
1 parent f101ff8 commit 0ac6451
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 0 deletions.
32 changes: 32 additions & 0 deletions src/core/codepoint.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package digression

import rudiments.*
import anticipation.*
import fulminate.*

import scala.quoted.*

Expand All @@ -29,10 +30,41 @@ object Codepoint:
case class Codepoint(source: Text, line: Int):
def text: Text = Text(s"${source.s.split("/").nn.last.nn}:$line")

given Realm = realm"digression"

object Digression:
def location(using Quotes): Expr[Codepoint] =
import quotes.*, reflect.*
val path = Expr(Position.ofMacroExpansion.sourceFile.path)
val line = Expr(Position.ofMacroExpansion.startLine + 1)

'{Codepoint(Text($path), $line)}

private val javaKeywords: Set[String] =
Set
("abstract", "continue", "for", "new", "switch", "assert", "default", "if", "package", "synchronized",
"boolean", "do", "goto", "private", "this", "break", "double", "implements", "protected", "throw",
"byte", "else", "import", "public", "throws", "case", "enum", "instanceof", "return", "transient",
"catch", "extends", "int", "short", "try", "char", "final", "interface", "static", "void", "class",
"finally", "long", "strictfp", "volatile", "const", "float", "native", "super", "while")

def fqcn(context: Expr[StringContext])(using Quotes): Expr[Fqcn] =
import quotes.reflect.*
val parts = IArray.from(context.valueOrAbort.parts.head.split("\\.").nn.map(_.nn))

parts.foreach: part =>
if part.length == 0 then fail(msg"a package name cannot be the empty string")
if javaKeywords.has(part) then fail(msg"a package cannot be named $part, because it is a Java keyword")

def valid(char: Char): Boolean =
char >= 'A' && char <= 'Z' || char >= 'a' && char <= 'z' || char >= '0' && char <= '9' || char == '_'

if !part.all(valid) then fail(msg"a package name may only contain the characters A-Z, a-z, 0-9 and _")

if part.head >= '0' && part.head <= '9' then fail(msg"a package name cannot start with a digit")


'{Fqcn(${Expr(parts.map(_.tt))})}

extension (inline context: StringContext)
inline def fqcn(): Fqcn = ${Digression.fqcn('context)}
9 changes: 9 additions & 0 deletions src/core/stacktrace.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ import language.experimental.captureChecking

extension (error: Throwable) def stackTrace: StackTrace = StackTrace(error)

//case class FqcnError(name: Text) extends Error(msg"the class name $name is not valid")

object Fqcn

case class Fqcn(parts: IArray[Text]):
def text: Text = parts.mkString(".").tt
def className: Text = parts.last
def packageName: Text = parts.init.mkString(".").tt

object StackTrace:
case class Method(className: Text, method: Text)
case class Frame(method: Method, file: Text, line: Optional[Int], native: Boolean)
Expand Down

0 comments on commit 0ac6451

Please sign in to comment.