diff options
author | Martin Odersky <odersky@gmail.com> | 2013-04-19 15:36:22 +0200 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2013-04-19 15:36:53 +0200 |
commit | 4a93c8c132941ff3dae9ebf2dc27b37cd4f7cdec (patch) | |
tree | 851095e9376d26b2624a5676a765d04de3ea87f8 /src/dotty | |
parent | 6b58c275293b2c2b0bf3390eaee94ec31bcbdab9 (diff) | |
download | dotty-4a93c8c132941ff3dae9ebf2dc27b37cd4f7cdec.tar.gz dotty-4a93c8c132941ff3dae9ebf2dc27b37cd4f7cdec.tar.bz2 dotty-4a93c8c132941ff3dae9ebf2dc27b37cd4f7cdec.zip |
Added symbolic XML builder
Diffstat (limited to 'src/dotty')
-rw-r--r-- | src/dotty/tools/dotc/core/Definitions.scala | 1 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/StdNames.scala | 4 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Trees.scala | 3 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/TypedTrees.scala | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/parsing/SymbolicXMLBuilder.scala | 262 | ||||
-rw-r--r-- | src/dotty/tools/dotc/util/Positions.scala | 1 |
6 files changed, 271 insertions, 2 deletions
diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index 049e882e7..95b2db5a5 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -139,6 +139,7 @@ class Definitions(implicit ctx: Context) { ScalaPackageClass, tpnme.Null, AbstractFinal, List(AnyRefAlias.typeConstructor)).entered lazy val PredefModule = requiredModule("scala.Predef") + lazy val NilModule = requiredModule("scala.collection.immutable.Nil") // lazy val FunctionClass: ClassSymbol = requiredClass("scala.Function") lazy val SingletonClass: ClassSymbol = diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala index 6e25cdf99..4ef3456df 100644 --- a/src/dotty/tools/dotc/core/StdNames.scala +++ b/src/dotty/tools/dotc/core/StdNames.scala @@ -601,7 +601,7 @@ object StdNames { } class ScalaTermNames extends ScalaNames[TermName] { - protected def fromString(s: String) = termName(s) + protected implicit def fromString(s: String): TermName = termName(s) @switch def syntheticParamName(i: Int): TermName = i match { case 0 => x_0 @@ -627,7 +627,7 @@ object StdNames { } class ScalaTypeNames extends ScalaNames[TypeName] { - protected def fromString(s: String) = typeName(s) + protected implicit def fromString(s: String): TypeName = typeName(s) @switch def syntheticTypeParamName(i: Int): TypeName = typeName("T"+i) diff --git a/src/dotty/tools/dotc/core/Trees.scala b/src/dotty/tools/dotc/core/Trees.scala index 664b7f41b..7befc9956 100644 --- a/src/dotty/tools/dotc/core/Trees.scala +++ b/src/dotty/tools/dotc/core/Trees.scala @@ -275,6 +275,9 @@ object Trees { val pos = cpos union tpt.pos } + def New[T >: Untyped](tpt: Tree[T], argss: List[List[Tree[T]]])(implicit cpos: Position): Tree[T] = + ((Select(New(tpt), nme.CONSTRUCTOR): Tree[T]) /: argss)(Apply(_, _)) + /** (left, right) */ case class Pair[T >: Untyped](left: Tree[T], right: Tree[T])(implicit cpos: Position) extends TermTree[T] { diff --git a/src/dotty/tools/dotc/core/TypedTrees.scala b/src/dotty/tools/dotc/core/TypedTrees.scala index 414cd6e2f..bec986ea2 100644 --- a/src/dotty/tools/dotc/core/TypedTrees.scala +++ b/src/dotty/tools/dotc/core/TypedTrees.scala @@ -239,6 +239,8 @@ object TypedTrees { case pre => SelectFromTypeTree(TypeTree(pre), tp) } // no checks necessary + def ref(sym: TermSymbol)implicit ctx: Context): tpd.NameTree = ref(sym.termRef) + /** new C(args) */ def New(tp: Type, args: List[Tree])(implicit ctx: Context): Apply = Apply( diff --git a/src/dotty/tools/dotc/parsing/SymbolicXMLBuilder.scala b/src/dotty/tools/dotc/parsing/SymbolicXMLBuilder.scala new file mode 100644 index 000000000..e5890b122 --- /dev/null +++ b/src/dotty/tools/dotc/parsing/SymbolicXMLBuilder.scala @@ -0,0 +1,262 @@ +package dotty.tools +package dotc +package parsing + +import scala.collection.{ mutable, immutable } +import scala.xml.{ EntityRef, Text } +import scala.xml.XML.{ xmlns } +import core._ +import Flags.Mutable +import Names._, NameOps._, StdNames._, Decorators._, Trees._, TypedTrees._, UntypedTrees._, Constants._ +import Symbols._, Contexts._ +import util.Positions._ +import scala.reflect.internal.util.StringOps.splitWhere +import scala.language.implicitConversions + +/** This class builds instance of `Tree` that represent XML. + * + * Note from martin: This needs to have its position info reworked. I don't + * understand exactly what's done here. To make validation pass, I set many + * positions to be transparent. Not sure this is a good idea for navigating + * XML trees in the IDE but it's the best I can do right now. If someone + * who understands this part better wants to give it a shot, please do! + * + * @author Burak Emir + * @version 1.0 + */ +abstract class SymbolicXMLBuilder(preserveWS: Boolean)(implicit ctx: Context) { + + import Constants.Constant + import untpd._ + + private[parsing] var isPattern: Boolean = _ + + private object xmltypes extends ScalaTypeNames { + val _Comment: TypeName = "Comment" + val _Elem: TypeName = "Elem" + val _EntityRef: TypeName = "EntityRef" + val _Group: TypeName = "Group" + val _MetaData: TypeName = "MetaData" + val _NamespaceBinding: TypeName = "NamespaceBinding" + val _NodeBuffer: TypeName = "NodeBuffer" + val _PrefixedAttribute: TypeName = "PrefixedAttribute" + val _ProcInstr: TypeName = "ProcInstr" + val _Text: TypeName = "Text" + val _Unparsed: TypeName = "Unparsed" + val _UnprefixedAttribute: TypeName = "UnprefixedAttribute" + } + + private object xmlterms extends ScalaTermNames { + val _Null: TermName = "Null" + val __Elem: TermName = "Elem" + val __Text: TermName = "Text" + val _buf: TermName = "$buf" + val _md: TermName = "$md" + val _plus: TermName = "$amp$plus" + val _scope: TermName = "$scope" + val _tmpscope: TermName = "$tmpscope" + val _xml: TermName = "xml" + } + + import xmltypes.{_Comment, _Elem, _EntityRef, _Group, _MetaData, _NamespaceBinding, _NodeBuffer, + _PrefixedAttribute, _ProcInstr, _Text, _Unparsed, _UnprefixedAttribute} + + import xmlterms.{_Null, __Elem, __Text, _buf, _md, _plus, _scope, _tmpscope, _xml} + + // convenience methods + private def LL[A](x: A*): List[List[A]] = List(List(x: _*)) + private def const(x: Any)(implicit cpos: Position) = Literal(Constants.Constant(x)) + private def wild(implicit cpos: Position) = Ident(nme.WILDCARD) + private def wildStar(implicit cpos: Position) = Ident(tpnme.WILDCARD_STAR) + private def _scala(name: Name)(implicit cpos: Position) = Select(Select(Ident(nme.ROOTPKG), nme.scala_), name) + private def _scala_xml(name: Name)(implicit cpos: Position) = Select(_scala(_xml), name) + + private def _scala_xml_Comment(implicit cpos: Position) = _scala_xml(_Comment) + private def _scala_xml_Elem(implicit cpos: Position) = _scala_xml(_Elem) + private def _scala_xml_EntityRef(implicit cpos: Position) = _scala_xml(_EntityRef) + private def _scala_xml_Group(implicit cpos: Position) = _scala_xml(_Group) + private def _scala_xml_MetaData(implicit cpos: Position) = _scala_xml(_MetaData) + private def _scala_xml_NamespaceBinding(implicit cpos: Position) = _scala_xml(_NamespaceBinding) + private def _scala_xml_NodeBuffer(implicit cpos: Position) = _scala_xml(_NodeBuffer) + private def _scala_xml_Null(implicit cpos: Position) = _scala_xml(_Null) + private def _scala_xml_PrefixedAttribute(implicit cpos: Position) = _scala_xml(_PrefixedAttribute) + private def _scala_xml_ProcInstr(implicit cpos: Position) = _scala_xml(_ProcInstr) + private def _scala_xml_Text(implicit cpos: Position) = _scala_xml(_Text) + private def _scala_xml_Unparsed (implicit cpos: Position) = _scala_xml(_Unparsed) + private def _scala_xml_UnprefixedAttribute(implicit cpos: Position)= _scala_xml(_UnprefixedAttribute) + private def _scala_xml__Elem(implicit cpos: Position) = _scala_xml(__Elem) + private def _scala_xml__Text(implicit cpos: Position) = _scala_xml(__Text) + + /** Wildly wrong documentation deleted in favor of "self-documenting code." */ + protected def mkXML( + isPattern: Boolean, + pre: Tree, + label: Tree, + attrs: Tree, + scope: Tree, + empty: Boolean, + children: Seq[Tree])(implicit cpos: Position): Tree = + { + def starArgs = + if (children.isEmpty) Nil + else List(Typed(makeXMLseq(children), wildStar)) + + def pat = Apply(_scala_xml__Elem, List(pre, label, wild, wild) ::: convertToTextPat(children)) + def nonpat = New(_scala_xml_Elem, List(List(pre, label, attrs, scope, if (empty) Literal(Constant(true)) else Literal(Constant(false))) ::: starArgs)) + + if (isPattern) pat else nonpat + } + + final def entityRef(n: String)(implicit cpos: Position) = + New(_scala_xml_EntityRef, LL(const(n))) + + // create scala.xml.Text here <: scala.xml.Node + final def text(txt: String)(implicit cpos: Position): Tree = + if (isPattern) makeTextPat(const(txt)) + else makeText1(const(txt)) + + def makeTextPat(txt: Tree)(implicit cpos: Position) = Apply(_scala_xml__Text, List(txt)) + def makeText1(txt: Tree)(implicit cpos: Position) = New(_scala_xml_Text, LL(txt)) + def comment(text: String)(implicit cpos: Position) = Comment(const(text)) + def charData(txt: String)(implicit cpos: Position) = makeText1(const(txt)) + + def procInstr(target: String, txt: String)(implicit cpos: Position) = + ProcInstr(const(target), const(txt)) + + protected def Comment(txt: Tree)(implicit cpos: Position) = New(_scala_xml_Comment, LL(txt)) + protected def ProcInstr(target: Tree, txt: Tree)(implicit cpos: Position) = New(_scala_xml_ProcInstr, LL(target, txt)) + + /** @todo: attributes */ + def makeXMLpat(n: String, args: Seq[Tree])(implicit cpos: Position): Tree = { + val (prepat, labpat) = splitPrefix(n) match { + case (Some(pre), rest) => (const(pre), const(rest)) + case _ => (wild, const(n)) + } + mkXML(true, prepat, labpat, null, null, false, args) + } + + protected def convertToTextPat(t: Tree)(implicit cpos: Position): Tree = t match { + case _: Literal => makeTextPat(t) + case _ => t + } + protected def convertToTextPat(buf: Seq[Tree])(implicit cpos: Position): List[Tree] = + (buf map convertToTextPat).toList + + def parseAttribute(s: String)(implicit cpos: Position): Tree = { + val ts = scala.xml.Utility.parseAttributeValue(s) map { + case Text(s) => text(s) + case EntityRef(s) => entityRef(s) + } + ts.length match { + case 0 => TypedSplice(tpd.ref(defn.NilModule)) + case 1 => ts.head + case _ => makeXMLseq(ts.toList) + } + } + + def isEmptyText(t: Tree) = t match { + case Literal(Constant("")) => true + case _ => false + } + + /** could optimize if args.length == 0, args.length == 1 AND args(0) is <: Node. */ + def makeXMLseq(args: Seq[Tree])(implicit cpos: Position) = { + val buffer = ValDef(Modifiers(), _buf, TypeTree(), New(_scala_xml_NodeBuffer, ListOfNil)) + val applies = args filterNot isEmptyText map (t => Apply(Select(Ident(_buf), _plus), List(t))) + + Block(buffer :: applies.toList, Ident(_buf)) + } + + /** Returns (Some(prefix) | None, rest) based on position of ':' */ + def splitPrefix(name: String): (Option[String], String) = splitWhere(name, _ == ':', true) match { + case Some((pre, rest)) => (Some(pre), rest) + case _ => (None, name) + } + + /** Various node constructions. */ + def group(args: Seq[Tree])(implicit cpos: Position): Tree = + New(_scala_xml_Group, LL(makeXMLseq(args))) + + def unparsed(str: String)(implicit cpos: Position): Tree = + New(_scala_xml_Unparsed, LL(const(str))) + + def element(qname: String, attrMap: mutable.Map[String, Tree], empty: Boolean, args: Seq[Tree])(implicit cpos: Position): Tree = { + val tpos = cpos.transparent + implicit val cpos: Position = tpos + + def handleNamespaceBinding(pre: String, z: String): Tree = { + def mkAssign(t: Tree): Tree = Assign( + Ident(_tmpscope), + New(_scala_xml_NamespaceBinding, LL(const(pre), t, Ident(_tmpscope))) + ) + + val uri1 = attrMap(z) match { + case Apply(_, List(uri @ Literal(Constant(_)))) => mkAssign(uri) + case Select(_, nme.Nil) => mkAssign(const(null)) // allow for xmlns="" -- bug #1626 + case x => mkAssign(x) + } + attrMap -= z + uri1 + } + + /** Extract all the namespaces from the attribute map. */ + val namespaces: List[Tree] = + for (z <- attrMap.keys.toList ; if z startsWith xmlns) yield { + val ns = splitPrefix(z) match { + case (Some(_), rest) => rest + case _ => null + } + handleNamespaceBinding(ns, z) + } + + val (pre, newlabel) = splitPrefix(qname) match { + case (Some(p), x) => (p, x) + case (None, x) => (null, x) + } + + def mkAttributeTree(pre: String, key: String, value: Tree) = { + // XXX this is where we'd like to put Select(value, nme.toString_) for #1787 + // after we resolve the Some(foo) situation. + val baseArgs = List(const(key), value, Ident(_md)) + val (clazz, attrArgs) = + if (pre == null) (_scala_xml_UnprefixedAttribute, baseArgs) + else (_scala_xml_PrefixedAttribute , const(pre) :: baseArgs) + + Assign(Ident(_md), New(clazz, LL(attrArgs: _*))) + } + + def handlePrefixedAttribute(pre: String, key: String, value: Tree) = mkAttributeTree(pre, key, value) + def handleUnprefixedAttribute(key: String, value: Tree) = mkAttributeTree(null, key, value) + + val attributes: List[Tree] = + for ((k, v) <- attrMap.toList.reverse) yield splitPrefix(k) match { + case (Some(pre), rest) => handlePrefixedAttribute(pre, rest, v) + case _ => handleUnprefixedAttribute(k, v) + } + + lazy val scopeDef = ValDef(Modifiers(), _scope, _scala_xml_NamespaceBinding, Ident(_tmpscope)) + lazy val tmpScopeDef = ValDef(Modifiers(Mutable), _tmpscope, _scala_xml_NamespaceBinding, Ident(_scope)) + lazy val metadataDef = ValDef(Modifiers(Mutable), _md, _scala_xml_MetaData, _scala_xml_Null) + val makeSymbolicAttrs = if (!attributes.isEmpty) Ident(_md) else _scala_xml_Null + + val (attrResult, nsResult) = + (attributes.isEmpty, namespaces.isEmpty) match { + case (true , true) => (Nil, Nil) + case (true , false) => (scopeDef :: Nil, tmpScopeDef :: namespaces) + case (false, true) => (metadataDef :: attributes, Nil) + case (false, false) => (scopeDef :: metadataDef :: attributes, tmpScopeDef :: namespaces) + } + + val body = mkXML( + false, + const(pre), + const(newlabel), + makeSymbolicAttrs, + Ident(_scope), + empty, + args + ) + + Block(nsResult, Block(attrResult, body)) + } +} diff --git a/src/dotty/tools/dotc/util/Positions.scala b/src/dotty/tools/dotc/util/Positions.scala index bf7bc7414..2ab6c1920 100644 --- a/src/dotty/tools/dotc/util/Positions.scala +++ b/src/dotty/tools/dotc/util/Positions.scala @@ -46,6 +46,7 @@ object Positions { def withStart(start: Int) = Position(start, this.end, this.point - start) def withEnd(end: Int) = Position(this.start, end, this.point - this.start) def withPoint(point: Int) = Position(this.start, this.end, point - this.start) + def transparent: Position = this /* for now */ } def Position(start: Int, end: Int, pointOffset: Int = 0): Position = |