diff --git a/build.sbt b/build.sbt index f10157a..e4cd9b8 100644 --- a/build.sbt +++ b/build.sbt @@ -8,7 +8,7 @@ lazy val root = project scalaVersion := dottyVersion, scalacOptions ++= Seq( - "-deprecation" + "-deprecation", "-feature", "-language:implicitConversions" ), libraryDependencies ++= Seq( "org.scala-lang.modules" % "scala-parser-combinators_2.13" % "1.1.2", diff --git a/src/main/scala/dotty/xml/interpolator/internal/Expand.scala b/src/main/scala/dotty/xml/interpolator/internal/Expand.scala index df11bd1..46c599d 100644 --- a/src/main/scala/dotty/xml/interpolator/internal/Expand.scala +++ b/src/main/scala/dotty/xml/interpolator/internal/Expand.scala @@ -1,20 +1,18 @@ package dotty.xml.interpolator package internal -import scala.language.implicitConversions import scala.quoted._ -import dotty.xml.interpolator.internal.Tree._ +import Tree.* -object Expand { +object Expand: - def apply(nodes: Seq[Node])(implicit ctx: XmlContext, q: Quotes): Expr[scala.xml.Node | scala.xml.NodeBuffer] = { - if (nodes.size == 1) expandNode(nodes.head).asInstanceOf[Expr[scala.xml.Node]] + def apply(nodes: Seq[Node])(using XmlContext, Quotes): Expr[scala.xml.Node | scala.xml.NodeBuffer] = + if nodes.size == 1 then expandNode(nodes.head).asInstanceOf[Expr[scala.xml.Node]] else expandNodes(nodes) - } - private def expandNode(node: Node)(implicit ctx: XmlContext, q: Quotes): Expr[Any] = { - node match { + private def expandNode(node: Node)(using XmlContext, Quotes): Expr[Any] = + node match case group: Group => expandGroup(group) case elem: Elem => expandElem(elem) case text: Text => expandText(text) @@ -24,33 +22,32 @@ object Expand { case procInstr: ProcInstr => expandProcInstr(procInstr) case entityRef: EntityRef => expandEntityRef(entityRef) case unparsed: Unparsed => expandUnparsed(unparsed) - } - } + end expandNode - private def expandNodes(nodes: Seq[Node])(implicit ctx: XmlContext, q: Quotes): Expr[scala.xml.NodeBuffer] = { - nodes.foldLeft('{ new _root_.scala.xml.NodeBuffer() })((expr, node) => '{ $expr &+ ${expandNode(node)} } ) - } + private def expandNodes(nodes: Seq[Node])(using XmlContext, Quotes): Expr[scala.xml.NodeBuffer] = + nodes.foldLeft('{ scala.xml.NodeBuffer() }): (expr, node) => + '{ $expr &+ ${ expandNode(node) } } - private def expandGroup(group: Group)(implicit ctx: XmlContext, q: Quotes): Expr[scala.xml.Group] = - '{ new _root_.scala.xml.Group(${expandNodes(group.nodes)}) } + private def expandGroup(group: Group)(using XmlContext, Quotes): Expr[scala.xml.Group] = + '{ scala.xml.Group(${ expandNodes(group.nodes) }) } - private def expandElem(elem: Elem)(implicit ctx: XmlContext, q: Quotes): Expr[scala.xml.Elem] = { + private def expandElem(elem: Elem)(using ctx: XmlContext, q: Quotes): Expr[scala.xml.Elem] = val (namespaces, attributes) = elem.attributes.partition(_.isNamespace) - val prefix = if (elem.prefix.nonEmpty) Expr(elem.prefix) else '{ null: String } + val prefix = if elem.prefix.nonEmpty then Expr(elem.prefix) else '{ null } val label = Expr(elem.label) val attributes1 = expandAttributes(attributes) val scope = expandNamespaces(namespaces) val empty = Expr(elem.end.isEmpty) - val child = expandNodes(elem.children)(new XmlContext(ctx.args, scope), q) - if (elem.children.isEmpty) - '{ new _root_.scala.xml.Elem($prefix, $label, $attributes1, $scope, $empty) } + val child = expandNodes(elem.children)(using new XmlContext(ctx.args, scope), q) + if elem.children.isEmpty then + '{ new scala.xml.Elem($prefix, $label, $attributes1, $scope, $empty) } else - '{ new _root_.scala.xml.Elem($prefix, $label, $attributes1, $scope, $empty, _root_.scala.xml.NodeSeq.seqToNodeSeq($child): _*) } - } + '{ new scala.xml.Elem($prefix, $label, $attributes1, $scope, $empty, scala.xml.NodeSeq.seqToNodeSeq($child)*) } + end expandElem - private def expandAttributes(attributes: Seq[Attribute])(implicit ctx: XmlContext, q: Quotes): Expr[scala.xml.MetaData] = { + private def expandAttributes(attributes: Seq[Attribute])(using XmlContext, Quotes): Expr[scala.xml.MetaData] = import quotes.reflect._ - attributes.foldRight('{ _root_.scala.xml.Null }: Expr[scala.xml.MetaData])((attribute, rest) => { + attributes.foldRight('{ _root_.scala.xml.Null }: Expr[scala.xml.MetaData]): (attribute, rest) => val value = attribute.value match { case Seq(v) => expandNode(v) case vs => expandNodes(vs) @@ -71,57 +68,52 @@ object Expand { */ val term = value.asTerm - if (term.tpe <:< TypeRepr.of[String]) { + if term.tpe <:< TypeRepr.of[String] then val value = term.asExprOf[String] - if (attribute.prefix.isEmpty) '{ new _root_.scala.xml.UnprefixedAttribute(${Expr(attribute.key)}, $value, $rest) } - else '{ new _root_.scala.xml.PrefixedAttribute(${Expr(attribute.prefix)}, ${Expr(attribute.key)}, $value, $rest) } - } else if (term.tpe <:< TypeRepr.of[collection.Seq[scala.xml.Node]]) { + if attribute.prefix.isEmpty then '{ scala.xml.UnprefixedAttribute(${ Expr(attribute.key) }, $value, $rest) } + else '{ scala.xml.PrefixedAttribute(${ Expr(attribute.prefix) }, ${ Expr(attribute.key) }, $value, $rest) } + else if term.tpe <:< TypeRepr.of[collection.Seq[scala.xml.Node]] then val value = term.asExprOf[collection.Seq[scala.xml.Node]] - if (attribute.prefix.isEmpty) '{ new _root_.scala.xml.UnprefixedAttribute(${Expr(attribute.key)}, $value, $rest) } - else '{ new _root_.scala.xml.PrefixedAttribute(${Expr(attribute.prefix)}, ${Expr(attribute.key)}, $value, $rest) } - } else { + if attribute.prefix.isEmpty then '{ scala.xml.UnprefixedAttribute(${ Expr(attribute.key) }, $value, $rest) } + else '{ scala.xml.PrefixedAttribute(${ Expr(attribute.prefix) }, ${ Expr(attribute.key) }, $value, $rest) } + else val value = term.asExprOf[Option[collection.Seq[scala.xml.Node]]] - if (attribute.prefix.isEmpty) '{ new _root_.scala.xml.UnprefixedAttribute(${Expr(attribute.key)}, $value, $rest) } - else '{ new _root_.scala.xml.PrefixedAttribute(${Expr(attribute.prefix)}, ${Expr(attribute.key)}, $value, $rest) } - } - }) - } + if attribute.prefix.isEmpty then '{ scala.xml.UnprefixedAttribute(${ Expr(attribute.key) }, $value, $rest) } + else '{ scala.xml.PrefixedAttribute(${ Expr(attribute.prefix) }, ${ Expr(attribute.key) }, $value, $rest) } + end expandAttributes - private def expandNamespaces(namespaces: Seq[Attribute])(implicit ctx: XmlContext, q: Quotes): Expr[scala.xml.NamespaceBinding] = { + private def expandNamespaces(namespaces: Seq[Attribute])(using XmlContext, Quotes): Expr[Scope] = import quotes.reflect._ - namespaces.foldLeft(ctx.scope)((rest, namespace) => { - val prefix = if (namespace.prefix.nonEmpty) Expr(namespace.key) else '{ null: String } - val uri = (namespace.value.head: @unchecked) match { + namespaces.foldLeft(ctx.scope): (rest, namespace) => + val prefix = if namespace.prefix.nonEmpty then Expr(namespace.key) else '{ null } + val uri = (namespace.value.head: @unchecked) match case Text(text) => Expr(text) case Placeholder(id) => - val call = '{ ${ctx.args(id)}(using _root_.scala.xml.TopScope) } + val call = '{ ${ctx.args(id)}(using scala.xml.TopScope) } Expr.betaReduce(call).asExprOf[String] - } - '{ new _root_.scala.xml.NamespaceBinding($prefix, $uri, $rest) } - }) - } + '{ scala.xml.NamespaceBinding($prefix, $uri, $rest) } + end expandNamespaces private def expandText(text: Text)(using Quotes): Expr[scala.xml.Text] = - '{ new _root_.scala.xml.Text(${Expr(text.text)}) } + '{ scala.xml.Text(${ Expr(text.text) }) } private def expandComment(comment: Comment)(using Quotes): Expr[scala.xml.Comment] = - '{ new _root_.scala.xml.Comment(${Expr(comment.text)}) } + '{ scala.xml.Comment(${ Expr(comment.text) }) } - private def expandPlaceholder(placeholder: Placeholder)(implicit ctx: XmlContext, q: Quotes): Expr[Any] = { + private def expandPlaceholder(placeholder: Placeholder)(using XmlContext, Quotes): Expr[Any] = val arg = ctx.args(placeholder.id) - val scope = ctx.scope - Expr.betaReduce('{ $arg(using $scope) }) - } + Expr.betaReduce('{ $arg(using ${ ctx.scope }) }) private def expandPCData(pcdata: PCData)(using Quotes): Expr[scala.xml.PCData] = - '{ new _root_.scala.xml.PCData(${Expr(pcdata.data)}) } + '{ scala.xml.PCData(${ Expr(pcdata.data) }) } private def expandProcInstr(instr: ProcInstr)(using Quotes): Expr[scala.xml.ProcInstr] = - '{ new _root_.scala.xml.ProcInstr(${Expr(instr.target)}, ${Expr(instr.proctext)}) } + '{ scala.xml.ProcInstr(${ Expr(instr.target) }, ${ Expr(instr.proctext) }) } private def expandEntityRef(ref: EntityRef)(using Quotes): Expr[scala.xml.EntityRef] = - '{ new _root_.scala.xml.EntityRef(${Expr(ref.name)}) } + '{ scala.xml.EntityRef(${ Expr(ref.name) }) } private def expandUnparsed(unparsed: Unparsed)(using Quotes): Expr[scala.xml.Unparsed] = - '{ new _root_.scala.xml.Unparsed(${Expr(unparsed.data)}) } -} + '{ scala.xml.Unparsed(${ Expr(unparsed.data) }) } + +end Expand diff --git a/src/main/scala/dotty/xml/interpolator/internal/Hole.scala b/src/main/scala/dotty/xml/interpolator/internal/Hole.scala index db29f42..656e8b1 100644 --- a/src/main/scala/dotty/xml/interpolator/internal/Hole.scala +++ b/src/main/scala/dotty/xml/interpolator/internal/Hole.scala @@ -1,8 +1,7 @@ package dotty.xml.interpolator package internal -object Hole { +object Hole: val HoleStart = 0xE000.toChar.toString val HoleChar = 0xE001.toChar.toString - def encode(i: Int) = HoleStart + HoleChar * i -} \ No newline at end of file + def encode(i: Int) = HoleStart + HoleChar * i \ No newline at end of file diff --git a/src/main/scala/dotty/xml/interpolator/internal/Macro.scala b/src/main/scala/dotty/xml/interpolator/internal/Macro.scala index a2e5681..65be6bc 100644 --- a/src/main/scala/dotty/xml/interpolator/internal/Macro.scala +++ b/src/main/scala/dotty/xml/interpolator/internal/Macro.scala @@ -6,38 +6,43 @@ import scala.quoted._ import scala.collection.mutable.ArrayBuffer import scala.language.implicitConversions -object Macro { +object Macro: - def impl(strCtxExpr: Expr[StringContext], argsExpr: Expr[Seq[Scope ?=> Any]], scope: Expr[Scope])(using qctx: Quotes): Expr[scala.xml.Node | scala.xml.NodeBuffer] = { - ((strCtxExpr, argsExpr): @unchecked) match { + /** ??? */ + def impl(strCtxExpr: Expr[StringContext], argsExpr: Expr[Seq[Scope ?=> Any]], scope: Expr[Scope]) + (using Quotes): Expr[scala.xml.Node | scala.xml.NodeBuffer] = + + (strCtxExpr, argsExpr) match case ('{ StringContext(${Varargs(parts)}: _*) }, Varargs(args)) => + val (xmlStr, offsets) = encode(parts) - implicit val ctx: XmlContext = new XmlContext(args, scope) - implicit val reporter: Reporter = new Reporter { - import quotes.reflect._ - def error(msg: String, idx: Int): Unit = { - val (part, offset) = Reporter.from(idx, offsets, parts) - val pos = part.asTerm.pos - val (srcF, start) = (pos.sourceFile, pos.start) - report.error(msg, Position(srcF, start + offset, start + offset + 1)) - } + given XmlContext = new XmlContext(args, scope) + given Reporter = new Reporter { + import quotes.reflect.* + + def error(msg: String, idx: Int): Unit = { + val (part, offset) = Reporter.from(idx, offsets, parts) + val pos = part.asTerm.pos + val (srcF, start) = (pos.sourceFile, pos.start) + report.error(msg, Position(srcF, start + offset, start + offset + 1)) + } + + def error(msg: String, expr: Expr[Any]): Unit = + report.error(msg, expr) + } - def error(msg: String, expr: Expr[Any]): Unit = { - report.error(msg, expr) - } - } implCore(xmlStr) - } - } + end impl - def implErrors(strCtxExpr: Expr[StringContext], argsExpr: Expr[Seq[Scope ?=> Any]], scope: Expr[Scope])(using qctx: Quotes): Expr[List[(Int, String)]] = { - ((strCtxExpr, argsExpr): @unchecked) match { + def implErrors(strCtxExpr: Expr[StringContext], argsExpr: Expr[Seq[Scope ?=> Any]], scope: Expr[Scope]) + (using Quotes): Expr[List[(Int, String)]] = + (strCtxExpr, argsExpr) match case ('{ StringContext(${Varargs(parts)}: _*) }, Varargs(args)) => val errors = List.newBuilder[Expr[(Int, String)]] val (xmlStr, offsets) = encode(parts) - implicit val ctx: XmlContext = new XmlContext(args, scope) - implicit val reporter: Reporter = new Reporter { + given XmlContext = new XmlContext(args, scope) + given Reporter = new Reporter { import quotes.reflect._ def error(msg: String, idx: Int): Unit = { @@ -53,48 +58,43 @@ object Macro { } implCore(xmlStr) Expr.ofList(errors.result()) - } - } + end implErrors - private def implCore(xmlStr: String)(using XmlContext, Reporter, Quotes): Expr[scala.xml.Node | scala.xml.NodeBuffer] = { + private def implCore(xmlStr: String)(using XmlContext, Reporter, Quotes): Expr[scala.xml.Node | scala.xml.NodeBuffer] = - import Parse.{apply => parse} - import Transform.{apply => transform} - import Validate.{apply => validate} - import TypeCheck.{apply => typecheck} - import Expand.{apply => expand} + import Parse.apply as parse + import Transform.apply as transform + import Validate.apply as validate + import TypeCheck.apply as typecheck + import Expand.apply as expand - val interpolate = ( - parse - andThen transform - andThen validate - andThen typecheck - andThen expand - ) + val interpolate = + parse andThen + transform andThen + validate andThen + typecheck andThen + expand interpolate(xmlStr) - } + end implCore - private def encode(parts: Seq[Expr[String]])(using Quotes): (String, Array[Int]) = { + private def encode(parts: Seq[Expr[String]])(using Quotes): (String, Array[Int]) = val sb = new StringBuilder() val bf = ArrayBuffer.empty[Int] - def appendPart(part: Expr[String]) = { + def appendPart(part: Expr[String]) = bf += sb.length sb ++= part.valueOrAbort bf += sb.length - } - def appendHole(index: Int) = { - sb ++= Hole.encode(index) - } + def appendHole(index: Int) = sb ++= Hole.encode(index) - for ((part, index) <- parts.init.zipWithIndex) { + for (part, index) <- parts.init.zipWithIndex do appendPart(part) appendHole(index) - } appendPart(parts.last) (sb.toString, bf.toArray) - } -} + end encode + +end Macro diff --git a/src/main/scala/dotty/xml/interpolator/internal/Reporter.scala b/src/main/scala/dotty/xml/interpolator/internal/Reporter.scala index fce4cd2..0f6f56a 100644 --- a/src/main/scala/dotty/xml/interpolator/internal/Reporter.scala +++ b/src/main/scala/dotty/xml/interpolator/internal/Reporter.scala @@ -1,14 +1,13 @@ package dotty.xml.interpolator package internal -import scala.quoted._ +import scala.quoted.* -trait Reporter { +trait Reporter: def error(msg: String, idx: Int): Unit def error(msg: String, expr: Expr[Any]): Unit -} -object Reporter { +object Reporter: def from(idx: Int, offsets: Array[Int], parts: Seq[Expr[String]]): (Expr[String], Int) = { val index = offsets.lastIndexWhere(idx >= _) val isWithinHoleOrAtTheEnd = index % 2 != 0 @@ -17,5 +16,4 @@ object Reporter { case false => (index / 2, idx - offsets(index)) } (parts(partIndex), offset) - } -} \ No newline at end of file + } \ No newline at end of file diff --git a/src/main/scala/dotty/xml/interpolator/internal/Transform.scala b/src/main/scala/dotty/xml/interpolator/internal/Transform.scala index 5e77265..9a0bacb 100644 --- a/src/main/scala/dotty/xml/interpolator/internal/Transform.scala +++ b/src/main/scala/dotty/xml/interpolator/internal/Transform.scala @@ -1,16 +1,17 @@ package dotty.xml.interpolator package internal -import dotty.xml.interpolator.internal.Tree._ +import Tree.* -object Transform { - def apply(nodes: Seq[Node]): Seq[Node] = { - nodes.map { +object Transform: + + def apply(nodes: Seq[Node]): Seq[Node] = + nodes.map: case elem : Elem => val children = apply(elem.children) - if (elem.name == "xml:group" && !elem.end.isEmpty) Group(elem.children).setPos(elem.pos) + if elem.name == "xml:group" && !elem.end.isEmpty then Group(elem.children).setPos(elem.pos) else elem.copy(children = children).setPos(elem.pos) case node => node - } - } -} \ No newline at end of file + end apply + +end Transform \ No newline at end of file diff --git a/src/main/scala/dotty/xml/interpolator/internal/Tree.scala b/src/main/scala/dotty/xml/interpolator/internal/Tree.scala index cec244a..c7db1d8 100644 --- a/src/main/scala/dotty/xml/interpolator/internal/Tree.scala +++ b/src/main/scala/dotty/xml/interpolator/internal/Tree.scala @@ -1,27 +1,34 @@ package dotty.xml.interpolator package internal -import scala.util.parsing.input._ +import scala.util.parsing.input.* + +object Tree: -object Tree { - sealed abstract class Node extends Positional - final case class Group(nodes: Seq[Node]) extends Node - final case class Elem(name: String, attributes: Seq[Attribute], children: Seq[Node], end: Option[String]) extends Node { - def prefix: String = name.take(prefixEnd) - def label: String = name.drop(prefixEnd + 1) - private def prefixEnd = name.indexOf(':') - } - final case class Text(text: String) extends Node - final case class Comment(text: String) extends Node - final case class Placeholder(id: Int) extends Node - final case class PCData(data: String) extends Node - final case class ProcInstr(target: String, proctext: String) extends Node - final case class EntityRef(name: String) extends Node - final case class Unparsed(data: String) extends Node final case class Attribute(name: String, value: Seq[Node]) extends Positional { def prefix: String = name.take(prefixEnd) def key: String = name.drop(prefixEnd + 1) def isNamespace = name.startsWith("xmlns") private def prefixEnd = name.indexOf(':') } -} \ No newline at end of file + + enum Node extends Positional: + case Group(nodes: Seq[Node]) + case Elem(name: String, attributes: Seq[Attribute], children: Seq[Node], end: Option[String]) + case Text(text: String) + case Comment(text: String) + case Placeholder(id: Int) + case PCData(data: String) + case ProcInstr(target: String, proctext: String) + case EntityRef(name: String) + case Unparsed(data: String) + end Node + + export Node.* + + extension (elem: Node.Elem) + private inline def prefixEnd = elem.name.indexOf(':') + def prefix: String = elem.name.take(prefixEnd) + def label: String = elem.name.drop(prefixEnd + 1) + +end Tree \ No newline at end of file diff --git a/src/main/scala/dotty/xml/interpolator/internal/TypeCheck.scala b/src/main/scala/dotty/xml/interpolator/internal/TypeCheck.scala index 72fb42f..f21326b 100644 --- a/src/main/scala/dotty/xml/interpolator/internal/TypeCheck.scala +++ b/src/main/scala/dotty/xml/interpolator/internal/TypeCheck.scala @@ -1,45 +1,41 @@ package dotty.xml.interpolator package internal -import scala.quoted._ +import scala.quoted.* -import dotty.xml.interpolator.internal.Tree._ +import Tree.* -object TypeCheck { - def apply(nodes: Seq[Node])(using XmlContext, Reporter, Quotes): Seq[Node] = { +object TypeCheck: + + def apply(nodes: Seq[Node])(using XmlContext, Reporter, Quotes): Seq[Node] = typecheck(nodes) nodes - } - private def typecheck(nodes: Seq[Node])(using XmlContext, Reporter)(using Quotes): Unit = { - import quotes.reflect._ - nodes.foreach { + private def typecheck(nodes: Seq[Node])(using XmlContext, Reporter, Quotes): Unit = + import quotes.reflect.* + nodes.foreach: case elem : Elem => - elem.attributes.foreach(attribute => - attribute.value match { + elem.attributes.foreach: attribute => + attribute.value match case Seq(Placeholder(id)) => - val dummy = '{ _root_.scala.xml.TopScope } - val expr = summon[XmlContext].args(id) - val term = Expr.betaReduce('{$expr(using $dummy)}).asTerm - val expected = attribute.isNamespace match { + val expr = ctx.args(id) + val term = Expr.betaReduce('{$expr(using scala.xml.TopScope)}).asTerm + val expected = attribute.isNamespace match case true => Seq(TypeRepr.of[String]) case _ => Seq( TypeRepr.of[String], TypeRepr.of[collection.Seq[scala.xml.Node]], TypeRepr.of[Option[collection.Seq[scala.xml.Node]]] ) - } - if (!expected.exists(term.tpe <:< _)) { - summon[Reporter].error( + if !expected.exists(term.tpe <:< _) then + reporter.error( s"""type mismatch; | found : ${term.tpe.widen.show} | required: ${expected.map(_.show).mkString(" | ")} """.stripMargin, term.asExpr) - } case _ => - }) typecheck(elem.children) case _ => - } - } -} + end typecheck + +end TypeCheck diff --git a/src/main/scala/dotty/xml/interpolator/internal/Validate.scala b/src/main/scala/dotty/xml/interpolator/internal/Validate.scala index 5490830..45fb1c4 100644 --- a/src/main/scala/dotty/xml/interpolator/internal/Validate.scala +++ b/src/main/scala/dotty/xml/interpolator/internal/Validate.scala @@ -1,28 +1,24 @@ package dotty.xml.interpolator package internal -import scala.language.implicitConversions +import Tree.* -import dotty.xml.interpolator.internal.Tree._ - -object Validate { - def apply(nodes: Seq[Node])(using Reporter): Seq[Node] = { +object Validate: + def apply(nodes: Seq[Node])(using Reporter): Seq[Node] = mismatchedElements(nodes) duplicateAttributes(nodes) nodes - } - private def mismatchedElements(nodes: Seq[Node])(using reporter: Reporter): Unit = { - nodes.foreach { + private def mismatchedElements(nodes: Seq[Node])(using Reporter): Unit = + nodes.foreach: case elem@Elem(name, _, _, Some(end)) => - if (name != end) reporter.error(s"closing tag `${name}` expected but `${end}` found", elem.pos) + if name != end then reporter.error(s"closing tag `$name` expected but `$end` found", elem.pos) mismatchedElements(elem.children) case _ => - } - } + end mismatchedElements - private def duplicateAttributes(nodes: Seq[Node])(using reporter: Reporter): Unit = { - nodes.foreach { + private def duplicateAttributes(nodes: Seq[Node])(using Reporter): Unit = + nodes.foreach: case Elem(_, attributes, children, _) => attributes .groupBy(_.name) @@ -30,6 +26,6 @@ object Validate { .foreach { attribute => reporter.error(s"attribute `${attribute.name}` may only be defined once", attribute.pos) } duplicateAttributes(children) case _ => - } - } -} + end duplicateAttributes + +end Validate \ No newline at end of file diff --git a/src/main/scala/dotty/xml/interpolator/internal/XmlContext.scala b/src/main/scala/dotty/xml/interpolator/internal/XmlContext.scala index b7e9c62..991d768 100644 --- a/src/main/scala/dotty/xml/interpolator/internal/XmlContext.scala +++ b/src/main/scala/dotty/xml/interpolator/internal/XmlContext.scala @@ -1,6 +1,6 @@ package dotty.xml.interpolator package internal -import scala.quoted._ +import scala.quoted.* -class XmlContext(val args: Seq[Expr[Scope ?=> Any]], val scope: Expr[scala.xml.NamespaceBinding]) +class XmlContext(val args: Seq[Expr[Scope ?=> Any]], val scope: Expr[Scope]) diff --git a/src/main/scala/dotty/xml/interpolator/internal/package.scala b/src/main/scala/dotty/xml/interpolator/internal/package.scala index f7a2798..8608bc8 100644 --- a/src/main/scala/dotty/xml/interpolator/internal/package.scala +++ b/src/main/scala/dotty/xml/interpolator/internal/package.scala @@ -1,12 +1,13 @@ -package dotty.xml.interpolator.internal +package dotty.xml.interpolator +package internal -import scala.util.parsing.input._ +import scala.util.parsing.input.* -given Conversion[Position, Int] with { - def apply(pos: Position): Int = { - pos match { +given Conversion[Position, Int] with + def apply(pos: Position): Int = + pos match case OffsetPosition(_, offset) => offset case _ => throw new Exception("expected offset position") - } - } -} + +private[internal] inline def ctx(using XmlContext): XmlContext = summon +private[internal] inline def reporter(using Reporter): Reporter = summon diff --git a/src/main/scala/dotty/xml/interpolator/package.scala b/src/main/scala/dotty/xml/interpolator/package.scala index a5cba9b..03a0898 100644 --- a/src/main/scala/dotty/xml/interpolator/package.scala +++ b/src/main/scala/dotty/xml/interpolator/package.scala @@ -1,9 +1,12 @@ package dotty.xml.interpolator -import scala.quoted._ +import scala.quoted.* -type Scope = scala.xml.NamespaceBinding -implicit val top: Scope = scala.xml.TopScope +private [interpolator] type Scope = scala.xml.NamespaceBinding -extension (inline ctx: StringContext) transparent inline def xml (inline args: (Scope ?=> Any)*)(using scope: Scope): Any = - ${ dotty.xml.interpolator.internal.Macro.impl('ctx, 'args, 'scope) } +given Scope = scala.xml.TopScope + +/** TODO: THIS NEED TO BE DOCUMENTED */ +extension (inline ctx: StringContext) + transparent inline def xml (inline args: (Scope ?=> Any)*)(using inline scope: Scope): Any = + ${ internal.Macro.impl('ctx, 'args, 'scope) } diff --git a/src/test/scala/dotty/xml/interpolator/TrailingWhitespaceTest.scala b/src/test/scala/dotty/xml/interpolator/TrailingWhitespaceTest.scala index 854d3f7..18ceb72 100644 --- a/src/test/scala/dotty/xml/interpolator/TrailingWhitespaceTest.scala +++ b/src/test/scala/dotty/xml/interpolator/TrailingWhitespaceTest.scala @@ -1,7 +1,8 @@ package dotty.xml.interpolator -import org.junit.Test +import org.junit.* import org.junit.Assert._ +import scala.language.experimental class TrailingWhitespaceTest { @@ -29,6 +30,7 @@ class TrailingWhitespaceTest { assert(xml" " ≈ ) } + /* @Test def multiline(): Unit = { val expected = xml""" @@ -43,4 +45,6 @@ class TrailingWhitespaceTest { assert(expected ≈ obtained) } + */ + } \ No newline at end of file