Newer
Older
dub_jkp / source / dub / internal / sdlang / ast.d
@WebFreak001 WebFreak001 on 4 Feb 2023 51 KB fix typo(s)
  1. // SDLang-D
  2. // Written in the D programming language.
  3.  
  4. module dub.internal.sdlang.ast;
  5.  
  6. version (Have_sdlang_d) public import sdlang.ast;
  7. else:
  8.  
  9. import std.algorithm;
  10. import std.array;
  11. import std.conv;
  12. import std.range;
  13. import std.string;
  14.  
  15. version(sdlangUnittest)
  16. version(unittest)
  17. {
  18. import std.stdio;
  19. import std.exception;
  20. }
  21.  
  22. import dub.internal.sdlang.exception;
  23. import dub.internal.sdlang.token;
  24. import dub.internal.sdlang.util;
  25.  
  26. import dub.internal.dyaml.stdsumtype;
  27.  
  28. class Attribute
  29. {
  30. Value value;
  31. Location location;
  32.  
  33. private Tag _parent;
  34. /// Get parent tag. To set a parent, attach this Attribute to its intended
  35. /// parent tag by calling 'Tag.add(...)', or by passing it to
  36. /// the parent tag's constructor.
  37. @property Tag parent()
  38. {
  39. return _parent;
  40. }
  41.  
  42. private string _namespace;
  43. @property string namespace()
  44. {
  45. return _namespace;
  46. }
  47. /// Not particularly efficient, but it works.
  48. @property void namespace(string value)
  49. {
  50. if(_parent && _namespace != value)
  51. {
  52. // Remove
  53. auto saveParent = _parent;
  54. if(_parent)
  55. this.remove();
  56.  
  57. // Change namespace
  58. _namespace = value;
  59.  
  60. // Re-add
  61. if(saveParent)
  62. saveParent.add(this);
  63. }
  64. else
  65. _namespace = value;
  66. }
  67.  
  68. private string _name;
  69. /// Not including namespace. Use 'fullName' if you want the namespace included.
  70. @property string name()
  71. {
  72. return _name;
  73. }
  74. /// Not the most efficient, but it works.
  75. @property void name(string value)
  76. {
  77. if(_parent && _name != value)
  78. {
  79. _parent.updateId++;
  80.  
  81. void removeFromGroupedLookup(string ns)
  82. {
  83. // Remove from _parent._attributes[ns]
  84. auto sameNameAttrs = _parent._attributes[ns][_name];
  85. auto targetIndex = sameNameAttrs.countUntil(this);
  86. _parent._attributes[ns][_name].removeIndex(targetIndex);
  87. }
  88.  
  89. // Remove from _parent._tags
  90. removeFromGroupedLookup(_namespace);
  91. removeFromGroupedLookup("*");
  92.  
  93. // Change name
  94. _name = value;
  95.  
  96. // Add to new locations in _parent._attributes
  97. _parent._attributes[_namespace][_name] ~= this;
  98. _parent._attributes["*"][_name] ~= this;
  99. }
  100. else
  101. _name = value;
  102. }
  103.  
  104. @property string fullName()
  105. {
  106. return _namespace==""? _name : text(_namespace, ":", _name);
  107. }
  108.  
  109. this(string namespace, string name, Value value, Location location = Location(0, 0, 0))
  110. {
  111. this._namespace = namespace;
  112. this._name = name;
  113. this.location = location;
  114. this.value = value;
  115. }
  116.  
  117. this(string name, Value value, Location location = Location(0, 0, 0))
  118. {
  119. this._namespace = "";
  120. this._name = name;
  121. this.location = location;
  122. this.value = value;
  123. }
  124.  
  125. /// Removes 'this' from its parent, if any. Returns 'this' for chaining.
  126. /// Inefficient ATM, but it works.
  127. Attribute remove()
  128. {
  129. if(!_parent)
  130. return this;
  131.  
  132. void removeFromGroupedLookup(string ns)
  133. {
  134. // Remove from _parent._attributes[ns]
  135. auto sameNameAttrs = _parent._attributes[ns][_name];
  136. auto targetIndex = sameNameAttrs.countUntil(this);
  137. _parent._attributes[ns][_name].removeIndex(targetIndex);
  138. }
  139.  
  140. // Remove from _parent._attributes
  141. removeFromGroupedLookup(_namespace);
  142. removeFromGroupedLookup("*");
  143.  
  144. // Remove from _parent.allAttributes
  145. auto allAttrsIndex = _parent.allAttributes.countUntil(this);
  146. _parent.allAttributes.removeIndex(allAttrsIndex);
  147.  
  148. // Remove from _parent.attributeIndices
  149. auto sameNamespaceAttrs = _parent.attributeIndices[_namespace];
  150. auto attrIndicesIndex = sameNamespaceAttrs.countUntil(allAttrsIndex);
  151. _parent.attributeIndices[_namespace].removeIndex(attrIndicesIndex);
  152.  
  153. // Fixup other indices
  154. foreach(ns, ref nsAttrIndices; _parent.attributeIndices)
  155. foreach(k, ref v; nsAttrIndices)
  156. if(v > allAttrsIndex)
  157. v--;
  158.  
  159. _parent.removeNamespaceIfEmpty(_namespace);
  160. _parent.updateId++;
  161. _parent = null;
  162. return this;
  163. }
  164.  
  165. override bool opEquals(Object o)
  166. {
  167. auto a = cast(Attribute)o;
  168. if(!a)
  169. return false;
  170.  
  171. return
  172. _namespace == a._namespace &&
  173. _name == a._name &&
  174. value == a.value;
  175. }
  176.  
  177. string toSDLString()()
  178. {
  179. Appender!string sink;
  180. this.toSDLString(sink);
  181. return sink.data;
  182. }
  183.  
  184. void toSDLString(Sink)(ref Sink sink) if(isOutputRange!(Sink,char))
  185. {
  186. if(_namespace != "")
  187. {
  188. sink.put(_namespace);
  189. sink.put(':');
  190. }
  191.  
  192. sink.put(_name);
  193. sink.put('=');
  194. value.toSDLString(sink);
  195. }
  196. }
  197.  
  198. class Tag
  199. {
  200. Location location;
  201. Value[] values;
  202.  
  203. private Tag _parent;
  204. /// Get parent tag. To set a parent, attach this Tag to its intended
  205. /// parent tag by calling 'Tag.add(...)', or by passing it to
  206. /// the parent tag's constructor.
  207. @property Tag parent()
  208. {
  209. return _parent;
  210. }
  211.  
  212. private string _namespace;
  213. @property string namespace()
  214. {
  215. return _namespace;
  216. }
  217. /// Not particularly efficient, but it works.
  218. @property void namespace(string value)
  219. {
  220. if(_parent && _namespace != value)
  221. {
  222. // Remove
  223. auto saveParent = _parent;
  224. if(_parent)
  225. this.remove();
  226.  
  227. // Change namespace
  228. _namespace = value;
  229.  
  230. // Re-add
  231. if(saveParent)
  232. saveParent.add(this);
  233. }
  234. else
  235. _namespace = value;
  236. }
  237.  
  238. private string _name;
  239. /// Not including namespace. Use 'fullName' if you want the namespace included.
  240. @property string name()
  241. {
  242. return _name;
  243. }
  244. /// Not the most efficient, but it works.
  245. @property void name(string value)
  246. {
  247. if(_parent && _name != value)
  248. {
  249. _parent.updateId++;
  250.  
  251. void removeFromGroupedLookup(string ns)
  252. {
  253. // Remove from _parent._tags[ns]
  254. auto sameNameTags = _parent._tags[ns][_name];
  255. auto targetIndex = sameNameTags.countUntil(this);
  256. _parent._tags[ns][_name].removeIndex(targetIndex);
  257. }
  258.  
  259. // Remove from _parent._tags
  260. removeFromGroupedLookup(_namespace);
  261. removeFromGroupedLookup("*");
  262.  
  263. // Change name
  264. _name = value;
  265.  
  266. // Add to new locations in _parent._tags
  267. _parent._tags[_namespace][_name] ~= this;
  268. _parent._tags["*"][_name] ~= this;
  269. }
  270. else
  271. _name = value;
  272. }
  273.  
  274. /// This tag's name, including namespace if one exists.
  275. @property string fullName()
  276. {
  277. return _namespace==""? _name : text(_namespace, ":", _name);
  278. }
  279.  
  280. // Tracks dirtiness. This is incremented every time a change is made which
  281. // could invalidate existing ranges. This way, the ranges can detect when
  282. // they've been invalidated.
  283. private size_t updateId=0;
  284.  
  285. this(Tag parent = null)
  286. {
  287. if(parent)
  288. parent.add(this);
  289. }
  290.  
  291. this(
  292. string namespace, string name,
  293. Value[] values=null, Attribute[] attributes=null, Tag[] children=null
  294. )
  295. {
  296. this(null, namespace, name, values, attributes, children);
  297. }
  298.  
  299. this(
  300. Tag parent, string namespace, string name,
  301. Value[] values=null, Attribute[] attributes=null, Tag[] children=null
  302. )
  303. {
  304. this._namespace = namespace;
  305. this._name = name;
  306.  
  307. if(parent)
  308. parent.add(this);
  309.  
  310. this.values = values;
  311. this.add(attributes);
  312. this.add(children);
  313. }
  314.  
  315. private Attribute[] allAttributes; // In same order as specified in SDL file.
  316. private Tag[] allTags; // In same order as specified in SDL file.
  317. private string[] allNamespaces; // In same order as specified in SDL file.
  318.  
  319. private size_t[][string] attributeIndices; // allAttributes[ attributes[namespace][i] ]
  320. private size_t[][string] tagIndices; // allTags[ tags[namespace][i] ]
  321.  
  322. private Attribute[][string][string] _attributes; // attributes[namespace or "*"][name][i]
  323. private Tag[][string][string] _tags; // tags[namespace or "*"][name][i]
  324.  
  325. /// Adds a Value, Attribute, Tag (or array of such) as a member/child of this Tag.
  326. /// Returns 'this' for chaining.
  327. /// Throws 'SDLangValidationException' if trying to add an Attribute or Tag
  328. /// that already has a parent.
  329. Tag add(Value val)
  330. {
  331. values ~= val;
  332. updateId++;
  333. return this;
  334. }
  335.  
  336. ///ditto
  337. Tag add(Value[] vals)
  338. {
  339. foreach(val; vals)
  340. add(val);
  341.  
  342. return this;
  343. }
  344.  
  345. ///ditto
  346. Tag add(Attribute attr)
  347. {
  348. if(attr._parent)
  349. {
  350. throw new SDLangValidationException(
  351. "Attribute is already attached to a parent tag. "~
  352. "Use Attribute.remove() before adding it to another tag."
  353. );
  354. }
  355.  
  356. if(!allNamespaces.canFind(attr._namespace))
  357. allNamespaces ~= attr._namespace;
  358.  
  359. attr._parent = this;
  360.  
  361. allAttributes ~= attr;
  362. attributeIndices[attr._namespace] ~= allAttributes.length-1;
  363. _attributes[attr._namespace][attr._name] ~= attr;
  364. _attributes["*"] [attr._name] ~= attr;
  365.  
  366. updateId++;
  367. return this;
  368. }
  369.  
  370. ///ditto
  371. Tag add(Attribute[] attrs)
  372. {
  373. foreach(attr; attrs)
  374. add(attr);
  375.  
  376. return this;
  377. }
  378.  
  379. ///ditto
  380. Tag add(Tag tag)
  381. {
  382. if(tag._parent)
  383. {
  384. throw new SDLangValidationException(
  385. "Tag is already attached to a parent tag. "~
  386. "Use Tag.remove() before adding it to another tag."
  387. );
  388. }
  389.  
  390. if(!allNamespaces.canFind(tag._namespace))
  391. allNamespaces ~= tag._namespace;
  392.  
  393. tag._parent = this;
  394.  
  395. allTags ~= tag;
  396. tagIndices[tag._namespace] ~= allTags.length-1;
  397. _tags[tag._namespace][tag._name] ~= tag;
  398. _tags["*"] [tag._name] ~= tag;
  399.  
  400. updateId++;
  401. return this;
  402. }
  403.  
  404. ///ditto
  405. Tag add(Tag[] tags)
  406. {
  407. foreach(tag; tags)
  408. add(tag);
  409.  
  410. return this;
  411. }
  412.  
  413. /// Removes 'this' from its parent, if any. Returns 'this' for chaining.
  414. /// Inefficient ATM, but it works.
  415. Tag remove()
  416. {
  417. if(!_parent)
  418. return this;
  419.  
  420. void removeFromGroupedLookup(string ns)
  421. {
  422. // Remove from _parent._tags[ns]
  423. auto sameNameTags = _parent._tags[ns][_name];
  424. auto targetIndex = sameNameTags.countUntil(this);
  425. _parent._tags[ns][_name].removeIndex(targetIndex);
  426. }
  427.  
  428. // Remove from _parent._tags
  429. removeFromGroupedLookup(_namespace);
  430. removeFromGroupedLookup("*");
  431.  
  432. // Remove from _parent.allTags
  433. auto allTagsIndex = _parent.allTags.countUntil(this);
  434. _parent.allTags.removeIndex(allTagsIndex);
  435.  
  436. // Remove from _parent.tagIndices
  437. auto sameNamespaceTags = _parent.tagIndices[_namespace];
  438. auto tagIndicesIndex = sameNamespaceTags.countUntil(allTagsIndex);
  439. _parent.tagIndices[_namespace].removeIndex(tagIndicesIndex);
  440.  
  441. // Fixup other indices
  442. foreach(ns, ref nsTagIndices; _parent.tagIndices)
  443. foreach(k, ref v; nsTagIndices)
  444. if(v > allTagsIndex)
  445. v--;
  446.  
  447. _parent.removeNamespaceIfEmpty(_namespace);
  448. _parent.updateId++;
  449. _parent = null;
  450. return this;
  451. }
  452.  
  453. private void removeNamespaceIfEmpty(string namespace)
  454. {
  455. // If namespace has no attributes, remove it from attributeIndices/_attributes
  456. if(namespace in attributeIndices && attributeIndices[namespace].length == 0)
  457. {
  458. attributeIndices.remove(namespace);
  459. _attributes.remove(namespace);
  460. }
  461.  
  462. // If namespace has no tags, remove it from tagIndices/_tags
  463. if(namespace in tagIndices && tagIndices[namespace].length == 0)
  464. {
  465. tagIndices.remove(namespace);
  466. _tags.remove(namespace);
  467. }
  468.  
  469. // If namespace is now empty, remove it from allNamespaces
  470. if(
  471. namespace !in tagIndices &&
  472. namespace !in attributeIndices
  473. )
  474. {
  475. auto allNamespacesIndex = allNamespaces.length - allNamespaces.find(namespace).length;
  476. allNamespaces = allNamespaces[0..allNamespacesIndex] ~ allNamespaces[allNamespacesIndex+1..$];
  477. }
  478. }
  479.  
  480. struct NamedMemberRange(T, string membersGrouped)
  481. {
  482. private Tag tag;
  483. private string namespace; // "*" indicates "all namespaces" (ok since it's not a valid namespace name)
  484. private string name;
  485. private size_t updateId; // Tag's updateId when this range was created.
  486.  
  487. this(Tag tag, string namespace, string name, size_t updateId)
  488. {
  489. this.tag = tag;
  490. this.namespace = namespace;
  491. this.name = name;
  492. this.updateId = updateId;
  493. frontIndex = 0;
  494.  
  495. if(
  496. namespace in mixin("tag."~membersGrouped) &&
  497. name in mixin("tag."~membersGrouped~"[namespace]")
  498. )
  499. endIndex = mixin("tag."~membersGrouped~"[namespace][name].length");
  500. else
  501. endIndex = 0;
  502. }
  503.  
  504. invariant()
  505. {
  506. assert(
  507. this.updateId == tag.updateId,
  508. "This range has been invalidated by a change to the tag."
  509. );
  510. }
  511.  
  512. @property bool empty()
  513. {
  514. return frontIndex == endIndex;
  515. }
  516.  
  517. private size_t frontIndex;
  518. @property T front()
  519. {
  520. return this[0];
  521. }
  522. void popFront()
  523. {
  524. if(empty)
  525. throw new SDLangRangeException("Range is empty");
  526.  
  527. frontIndex++;
  528. }
  529.  
  530. private size_t endIndex; // One past the last element
  531. @property T back()
  532. {
  533. return this[$-1];
  534. }
  535. void popBack()
  536. {
  537. if(empty)
  538. throw new SDLangRangeException("Range is empty");
  539.  
  540. endIndex--;
  541. }
  542.  
  543. alias length opDollar;
  544. @property size_t length()
  545. {
  546. return endIndex - frontIndex;
  547. }
  548.  
  549. @property typeof(this) save()
  550. {
  551. auto r = typeof(this)(this.tag, this.namespace, this.name, this.updateId);
  552. r.frontIndex = this.frontIndex;
  553. r.endIndex = this.endIndex;
  554. return r;
  555. }
  556.  
  557. typeof(this) opSlice()
  558. {
  559. return save();
  560. }
  561.  
  562. typeof(this) opSlice(size_t start, size_t end)
  563. {
  564. auto r = save();
  565. r.frontIndex = this.frontIndex + start;
  566. r.endIndex = this.frontIndex + end;
  567.  
  568. if(
  569. r.frontIndex > this.endIndex ||
  570. r.endIndex > this.endIndex ||
  571. r.frontIndex > r.endIndex
  572. )
  573. throw new SDLangRangeException("Slice out of range");
  574.  
  575. return r;
  576. }
  577.  
  578. T opIndex(size_t index)
  579. {
  580. if(empty)
  581. throw new SDLangRangeException("Range is empty");
  582.  
  583. return mixin("tag."~membersGrouped~"[namespace][name][frontIndex+index]");
  584. }
  585. }
  586.  
  587. struct MemberRange(T, string allMembers, string memberIndices, string membersGrouped)
  588. {
  589. private Tag tag;
  590. private string namespace; // "*" indicates "all namespaces" (ok since it's not a valid namespace name)
  591. private bool isMaybe;
  592. private size_t updateId; // Tag's updateId when this range was created.
  593. private size_t initialEndIndex;
  594.  
  595. this(Tag tag, string namespace, bool isMaybe)
  596. {
  597. this.tag = tag;
  598. this.namespace = namespace;
  599. this.updateId = tag.updateId;
  600. this.isMaybe = isMaybe;
  601. frontIndex = 0;
  602.  
  603. if(namespace == "*")
  604. initialEndIndex = mixin("tag."~allMembers~".length");
  605. else if(namespace in mixin("tag."~memberIndices))
  606. initialEndIndex = mixin("tag."~memberIndices~"[namespace].length");
  607. else
  608. initialEndIndex = 0;
  609.  
  610. endIndex = initialEndIndex;
  611. }
  612.  
  613. invariant()
  614. {
  615. assert(
  616. this.updateId == tag.updateId,
  617. "This range has been invalidated by a change to the tag."
  618. );
  619. }
  620.  
  621. @property bool empty()
  622. {
  623. return frontIndex == endIndex;
  624. }
  625.  
  626. private size_t frontIndex;
  627. @property T front()
  628. {
  629. return this[0];
  630. }
  631. void popFront()
  632. {
  633. if(empty)
  634. throw new SDLangRangeException("Range is empty");
  635.  
  636. frontIndex++;
  637. }
  638.  
  639. private size_t endIndex; // One past the last element
  640. @property T back()
  641. {
  642. return this[$-1];
  643. }
  644. void popBack()
  645. {
  646. if(empty)
  647. throw new SDLangRangeException("Range is empty");
  648.  
  649. endIndex--;
  650. }
  651.  
  652. alias length opDollar;
  653. @property size_t length()
  654. {
  655. return endIndex - frontIndex;
  656. }
  657.  
  658. @property typeof(this) save()
  659. {
  660. auto r = typeof(this)(this.tag, this.namespace, this.isMaybe);
  661. r.frontIndex = this.frontIndex;
  662. r.endIndex = this.endIndex;
  663. r.initialEndIndex = this.initialEndIndex;
  664. r.updateId = this.updateId;
  665. return r;
  666. }
  667.  
  668. typeof(this) opSlice()
  669. {
  670. return save();
  671. }
  672.  
  673. typeof(this) opSlice(size_t start, size_t end)
  674. {
  675. auto r = save();
  676. r.frontIndex = this.frontIndex + start;
  677. r.endIndex = this.frontIndex + end;
  678.  
  679. if(
  680. r.frontIndex > this.endIndex ||
  681. r.endIndex > this.endIndex ||
  682. r.frontIndex > r.endIndex
  683. )
  684. throw new SDLangRangeException("Slice out of range");
  685.  
  686. return r;
  687. }
  688.  
  689. T opIndex(size_t index)
  690. {
  691. if(empty)
  692. throw new SDLangRangeException("Range is empty");
  693.  
  694. if(namespace == "*")
  695. return mixin("tag."~allMembers~"[ frontIndex+index ]");
  696. else
  697. return mixin("tag."~allMembers~"[ tag."~memberIndices~"[namespace][frontIndex+index] ]");
  698. }
  699.  
  700. alias NamedMemberRange!(T,membersGrouped) ThisNamedMemberRange;
  701. ThisNamedMemberRange opIndex(string name)
  702. {
  703. if(frontIndex != 0 || endIndex != initialEndIndex)
  704. {
  705. throw new SDLangRangeException(
  706. "Cannot lookup tags/attributes by name on a subset of a range, "~
  707. "only across the entire tag. "~
  708. "Please make sure you haven't called popFront or popBack on this "~
  709. "range and that you aren't using a slice of the range."
  710. );
  711. }
  712.  
  713. if(!isMaybe && empty)
  714. throw new SDLangRangeException("Range is empty");
  715.  
  716. if(!isMaybe && name !in this)
  717. throw new SDLangRangeException(`No such `~T.stringof~` named: "`~name~`"`);
  718.  
  719. return ThisNamedMemberRange(tag, namespace, name, updateId);
  720. }
  721.  
  722. bool opBinaryRight(string op)(string name) if(op=="in")
  723. {
  724. if(frontIndex != 0 || endIndex != initialEndIndex)
  725. {
  726. throw new SDLangRangeException(
  727. "Cannot lookup tags/attributes by name on a subset of a range, "~
  728. "only across the entire tag. "~
  729. "Please make sure you haven't called popFront or popBack on this "~
  730. "range and that you aren't using a slice of the range."
  731. );
  732. }
  733.  
  734. return
  735. namespace in mixin("tag."~membersGrouped) &&
  736. name in mixin("tag."~membersGrouped~"[namespace]") &&
  737. mixin("tag."~membersGrouped~"[namespace][name].length") > 0;
  738. }
  739. }
  740.  
  741. struct NamespaceRange
  742. {
  743. private Tag tag;
  744. private bool isMaybe;
  745. private size_t updateId; // Tag's updateId when this range was created.
  746.  
  747. this(Tag tag, bool isMaybe)
  748. {
  749. this.tag = tag;
  750. this.isMaybe = isMaybe;
  751. this.updateId = tag.updateId;
  752. frontIndex = 0;
  753. endIndex = tag.allNamespaces.length;
  754. }
  755.  
  756. invariant()
  757. {
  758. assert(
  759. this.updateId == tag.updateId,
  760. "This range has been invalidated by a change to the tag."
  761. );
  762. }
  763.  
  764. @property bool empty()
  765. {
  766. return frontIndex == endIndex;
  767. }
  768.  
  769. private size_t frontIndex;
  770. @property NamespaceAccess front()
  771. {
  772. return this[0];
  773. }
  774. void popFront()
  775. {
  776. if(empty)
  777. throw new SDLangRangeException("Range is empty");
  778.  
  779. frontIndex++;
  780. }
  781.  
  782. private size_t endIndex; // One past the last element
  783. @property NamespaceAccess back()
  784. {
  785. return this[$-1];
  786. }
  787. void popBack()
  788. {
  789. if(empty)
  790. throw new SDLangRangeException("Range is empty");
  791.  
  792. endIndex--;
  793. }
  794.  
  795. alias length opDollar;
  796. @property size_t length()
  797. {
  798. return endIndex - frontIndex;
  799. }
  800.  
  801. @property NamespaceRange save()
  802. {
  803. auto r = NamespaceRange(this.tag, this.isMaybe);
  804. r.frontIndex = this.frontIndex;
  805. r.endIndex = this.endIndex;
  806. r.updateId = this.updateId;
  807. return r;
  808. }
  809.  
  810. typeof(this) opSlice()
  811. {
  812. return save();
  813. }
  814.  
  815. typeof(this) opSlice(size_t start, size_t end)
  816. {
  817. auto r = save();
  818. r.frontIndex = this.frontIndex + start;
  819. r.endIndex = this.frontIndex + end;
  820.  
  821. if(
  822. r.frontIndex > this.endIndex ||
  823. r.endIndex > this.endIndex ||
  824. r.frontIndex > r.endIndex
  825. )
  826. throw new SDLangRangeException("Slice out of range");
  827.  
  828. return r;
  829. }
  830.  
  831. NamespaceAccess opIndex(size_t index)
  832. {
  833. if(empty)
  834. throw new SDLangRangeException("Range is empty");
  835.  
  836. auto namespace = tag.allNamespaces[frontIndex+index];
  837. return NamespaceAccess(
  838. namespace,
  839. AttributeRange(tag, namespace, isMaybe),
  840. TagRange(tag, namespace, isMaybe)
  841. );
  842. }
  843.  
  844. NamespaceAccess opIndex(string namespace)
  845. {
  846. if(!isMaybe && empty)
  847. throw new SDLangRangeException("Range is empty");
  848.  
  849. if(!isMaybe && namespace !in this)
  850. throw new SDLangRangeException(`No such namespace: "`~namespace~`"`);
  851.  
  852. return NamespaceAccess(
  853. namespace,
  854. AttributeRange(tag, namespace, isMaybe),
  855. TagRange(tag, namespace, isMaybe)
  856. );
  857. }
  858.  
  859. /// Inefficient when range is a slice or has used popFront/popBack, but it works.
  860. bool opBinaryRight(string op)(string namespace) if(op=="in")
  861. {
  862. if(frontIndex == 0 && endIndex == tag.allNamespaces.length)
  863. {
  864. return
  865. namespace in tag.attributeIndices ||
  866. namespace in tag.tagIndices;
  867. }
  868. else
  869. // Slower fallback method
  870. return tag.allNamespaces[frontIndex..endIndex].canFind(namespace);
  871. }
  872. }
  873.  
  874. struct NamespaceAccess
  875. {
  876. string name;
  877. AttributeRange attributes;
  878. TagRange tags;
  879. }
  880.  
  881. alias MemberRange!(Attribute, "allAttributes", "attributeIndices", "_attributes") AttributeRange;
  882. alias MemberRange!(Tag, "allTags", "tagIndices", "_tags" ) TagRange;
  883. static assert(isRandomAccessRange!AttributeRange);
  884. static assert(isRandomAccessRange!TagRange);
  885. static assert(isRandomAccessRange!NamespaceRange);
  886.  
  887. /// Access all attributes that don't have a namespace
  888. @property AttributeRange attributes()
  889. {
  890. return AttributeRange(this, "", false);
  891. }
  892.  
  893. /// Access all direct-child tags that don't have a namespace
  894. @property TagRange tags()
  895. {
  896. return TagRange(this, "", false);
  897. }
  898.  
  899. /// Access all namespaces in this tag, and the attributes/tags within them.
  900. @property NamespaceRange namespaces()
  901. {
  902. return NamespaceRange(this, false);
  903. }
  904.  
  905. /// Access all attributes and tags regardless of namespace.
  906. @property NamespaceAccess all()
  907. {
  908. // "*" isn't a valid namespace name, so we can use it to indicate "all namespaces"
  909. return NamespaceAccess(
  910. "*",
  911. AttributeRange(this, "*", false),
  912. TagRange(this, "*", false)
  913. );
  914. }
  915.  
  916. struct MaybeAccess
  917. {
  918. Tag tag;
  919.  
  920. /// Access all attributes that don't have a namespace
  921. @property AttributeRange attributes()
  922. {
  923. return AttributeRange(tag, "", true);
  924. }
  925.  
  926. /// Access all direct-child tags that don't have a namespace
  927. @property TagRange tags()
  928. {
  929. return TagRange(tag, "", true);
  930. }
  931.  
  932. /// Access all namespaces in this tag, and the attributes/tags within them.
  933. @property NamespaceRange namespaces()
  934. {
  935. return NamespaceRange(tag, true);
  936. }
  937.  
  938. /// Access all attributes and tags regardless of namespace.
  939. @property NamespaceAccess all()
  940. {
  941. // "*" isn't a valid namespace name, so we can use it to indicate "all namespaces"
  942. return NamespaceAccess(
  943. "*",
  944. AttributeRange(tag, "*", true),
  945. TagRange(tag, "*", true)
  946. );
  947. }
  948. }
  949.  
  950. /// Access 'attributes', 'tags', 'namespaces' and 'all' like normal,
  951. /// except that looking up a non-existent name/namespace with
  952. /// opIndex(string) results in an empty array instead of a thrown SDLangRangeException.
  953. @property MaybeAccess maybe()
  954. {
  955. return MaybeAccess(this);
  956. }
  957.  
  958. override bool opEquals(Object o)
  959. {
  960. auto t = cast(Tag)o;
  961. if(!t)
  962. return false;
  963.  
  964. if(_namespace != t._namespace || _name != t._name)
  965. return false;
  966.  
  967. if(
  968. values .length != t.values .length ||
  969. allAttributes .length != t.allAttributes.length ||
  970. allNamespaces .length != t.allNamespaces.length ||
  971. allTags .length != t.allTags .length
  972. )
  973. return false;
  974.  
  975. if(values != t.values)
  976. return false;
  977.  
  978. if(allNamespaces != t.allNamespaces)
  979. return false;
  980.  
  981. if(allAttributes != t.allAttributes)
  982. return false;
  983.  
  984. // Ok because cycles are not allowed
  985. //TODO: Actually check for or prevent cycles.
  986. return allTags == t.allTags;
  987. }
  988.  
  989. /// Treats 'this' as the root tag. Note that root tags cannot have
  990. /// values or attributes, and cannot be part of a namespace.
  991. /// If this isn't a valid root tag, 'SDLangValidationException' will be thrown.
  992. string toSDLDocument()(string indent="\t", int indentLevel=0)
  993. {
  994. Appender!string sink;
  995. toSDLDocument(sink, indent, indentLevel);
  996. return sink.data;
  997. }
  998.  
  999. ///ditto
  1000. void toSDLDocument(Sink)(ref Sink sink, string indent="\t", int indentLevel=0)
  1001. if(isOutputRange!(Sink,char))
  1002. {
  1003. if(values.length > 0)
  1004. throw new SDLangValidationException("Root tags cannot have any values, only child tags.");
  1005.  
  1006. if(allAttributes.length > 0)
  1007. throw new SDLangValidationException("Root tags cannot have any attributes, only child tags.");
  1008.  
  1009. if(_namespace != "")
  1010. throw new SDLangValidationException("Root tags cannot have a namespace.");
  1011.  
  1012. foreach(tag; allTags)
  1013. tag.toSDLString(sink, indent, indentLevel);
  1014. }
  1015.  
  1016. /// Output this entire tag in SDL format. Does *not* treat 'this' as
  1017. /// a root tag. If you intend this to be the root of a standard SDL
  1018. /// document, use 'toSDLDocument' instead.
  1019. string toSDLString()(string indent="\t", int indentLevel=0)
  1020. {
  1021. Appender!string sink;
  1022. toSDLString(sink, indent, indentLevel);
  1023. return sink.data;
  1024. }
  1025.  
  1026. ///ditto
  1027. void toSDLString(Sink)(ref Sink sink, string indent="\t", int indentLevel=0)
  1028. if(isOutputRange!(Sink,char))
  1029. {
  1030. if(_name == "" && values.length == 0)
  1031. throw new SDLangValidationException("Anonymous tags must have at least one value.");
  1032.  
  1033. if(_name == "" && _namespace != "")
  1034. throw new SDLangValidationException("Anonymous tags cannot have a namespace.");
  1035.  
  1036. // Indent
  1037. foreach(i; 0..indentLevel)
  1038. sink.put(indent);
  1039.  
  1040. // Name
  1041. if(_namespace != "")
  1042. {
  1043. sink.put(_namespace);
  1044. sink.put(':');
  1045. }
  1046. sink.put(_name);
  1047.  
  1048. // Values
  1049. foreach(i, v; values)
  1050. {
  1051. // Omit the first space for anonymous tags
  1052. if(_name != "" || i > 0)
  1053. sink.put(' ');
  1054.  
  1055. v.toSDLString(sink);
  1056. }
  1057.  
  1058. // Attributes
  1059. foreach(attr; allAttributes)
  1060. {
  1061. sink.put(' ');
  1062. attr.toSDLString(sink);
  1063. }
  1064.  
  1065. // Child tags
  1066. bool foundChild=false;
  1067. foreach(tag; allTags)
  1068. {
  1069. if(!foundChild)
  1070. {
  1071. sink.put(" {\n");
  1072. foundChild = true;
  1073. }
  1074.  
  1075. tag.toSDLString(sink, indent, indentLevel+1);
  1076. }
  1077. if(foundChild)
  1078. {
  1079. foreach(i; 0..indentLevel)
  1080. sink.put(indent);
  1081.  
  1082. sink.put("}\n");
  1083. }
  1084. else
  1085. sink.put("\n");
  1086. }
  1087.  
  1088. /// Not the most efficient, but it works.
  1089. string toDebugString()
  1090. {
  1091. import std.algorithm : sort;
  1092.  
  1093. Appender!string buf;
  1094.  
  1095. buf.put("\n");
  1096. buf.put("Tag ");
  1097. if(_namespace != "")
  1098. {
  1099. buf.put("[");
  1100. buf.put(_namespace);
  1101. buf.put("]");
  1102. }
  1103. buf.put("'%s':\n".format(_name));
  1104.  
  1105. // Values
  1106. foreach(val; values)
  1107. buf.put(" (%s): %s\n".format(
  1108. val.match!(v => typeof(v).stringof),
  1109. val
  1110. ));
  1111.  
  1112. // Attributes
  1113. foreach(attrNamespace; _attributes.keys.sort())
  1114. if(attrNamespace != "*")
  1115. foreach(attrName; _attributes[attrNamespace].keys.sort())
  1116. foreach(attr; _attributes[attrNamespace][attrName])
  1117. {
  1118. string namespaceStr;
  1119. if(attr._namespace != "")
  1120. namespaceStr = "["~attr._namespace~"]";
  1121.  
  1122. buf.put(
  1123. " %s%s(%s): %s\n".format(
  1124. namespaceStr, attr._name,
  1125. attr.value.match!(v => typeof(v).stringof),
  1126. attr.value
  1127. )
  1128. );
  1129. }
  1130.  
  1131. // Children
  1132. foreach(tagNamespace; _tags.keys.sort())
  1133. if(tagNamespace != "*")
  1134. foreach(tagName; _tags[tagNamespace].keys.sort())
  1135. foreach(tag; _tags[tagNamespace][tagName])
  1136. buf.put( tag.toDebugString().replace("\n", "\n ") );
  1137.  
  1138. return buf.data;
  1139. }
  1140. }
  1141.  
  1142. version(sdlangUnittest)
  1143. {
  1144. private void testRandomAccessRange(R, E)(R range, E[] expected, bool function(E, E) equals=null)
  1145. {
  1146. static assert(isRandomAccessRange!R);
  1147. static assert(is(ElementType!R == E));
  1148. static assert(hasLength!R);
  1149. static assert(!isInfinite!R);
  1150.  
  1151. assert(range.length == expected.length);
  1152. if(range.length == 0)
  1153. {
  1154. assert(range.empty);
  1155. return;
  1156. }
  1157.  
  1158. static bool defaultEquals(E e1, E e2)
  1159. {
  1160. return e1 == e2;
  1161. }
  1162. if(equals is null)
  1163. equals = &defaultEquals;
  1164.  
  1165. assert(equals(range.front, expected[0]));
  1166. assert(equals(range.front, expected[0])); // Ensure consistent result from '.front'
  1167. assert(equals(range.front, expected[0])); // Ensure consistent result from '.front'
  1168.  
  1169. assert(equals(range.back, expected[$-1]));
  1170. assert(equals(range.back, expected[$-1])); // Ensure consistent result from '.back'
  1171. assert(equals(range.back, expected[$-1])); // Ensure consistent result from '.back'
  1172.  
  1173. // Forward iteration
  1174. auto original = range.save;
  1175. auto r2 = range.save;
  1176. foreach(i; 0..expected.length)
  1177. {
  1178. //trace("Forward iteration: ", i);
  1179.  
  1180. // Test length/empty
  1181. assert(range.length == expected.length - i);
  1182. assert(range.length == r2.length);
  1183. assert(!range.empty);
  1184. assert(!r2.empty);
  1185.  
  1186. // Test front
  1187. assert(equals(range.front, expected[i]));
  1188. assert(equals(range.front, r2.front));
  1189.  
  1190. // Test back
  1191. assert(equals(range.back, expected[$-1]));
  1192. assert(equals(range.back, r2.back));
  1193.  
  1194. // Test opIndex(0)
  1195. assert(equals(range[0], expected[i]));
  1196. assert(equals(range[0], r2[0]));
  1197.  
  1198. // Test opIndex($-1)
  1199. assert(equals(range[$-1], expected[$-1]));
  1200. assert(equals(range[$-1], r2[$-1]));
  1201.  
  1202. // Test popFront
  1203. range.popFront();
  1204. assert(range.length == r2.length - 1);
  1205. r2.popFront();
  1206. assert(range.length == r2.length);
  1207. }
  1208. assert(range.empty);
  1209. assert(r2.empty);
  1210. assert(original.length == expected.length);
  1211.  
  1212. // Backwards iteration
  1213. range = original.save;
  1214. r2 = original.save;
  1215. foreach(i; iota(0, expected.length).retro())
  1216. {
  1217. //trace("Backwards iteration: ", i);
  1218.  
  1219. // Test length/empty
  1220. assert(range.length == i+1);
  1221. assert(range.length == r2.length);
  1222. assert(!range.empty);
  1223. assert(!r2.empty);
  1224.  
  1225. // Test front
  1226. assert(equals(range.front, expected[0]));
  1227. assert(equals(range.front, r2.front));
  1228.  
  1229. // Test back
  1230. assert(equals(range.back, expected[i]));
  1231. assert(equals(range.back, r2.back));
  1232.  
  1233. // Test opIndex(0)
  1234. assert(equals(range[0], expected[0]));
  1235. assert(equals(range[0], r2[0]));
  1236.  
  1237. // Test opIndex($-1)
  1238. assert(equals(range[$-1], expected[i]));
  1239. assert(equals(range[$-1], r2[$-1]));
  1240.  
  1241. // Test popBack
  1242. range.popBack();
  1243. assert(range.length == r2.length - 1);
  1244. r2.popBack();
  1245. assert(range.length == r2.length);
  1246. }
  1247. assert(range.empty);
  1248. assert(r2.empty);
  1249. assert(original.length == expected.length);
  1250.  
  1251. // Random access
  1252. range = original.save;
  1253. r2 = original.save;
  1254. foreach(i; 0..expected.length)
  1255. {
  1256. //trace("Random access: ", i);
  1257.  
  1258. // Test length/empty
  1259. assert(range.length == expected.length);
  1260. assert(range.length == r2.length);
  1261. assert(!range.empty);
  1262. assert(!r2.empty);
  1263.  
  1264. // Test front
  1265. assert(equals(range.front, expected[0]));
  1266. assert(equals(range.front, r2.front));
  1267.  
  1268. // Test back
  1269. assert(equals(range.back, expected[$-1]));
  1270. assert(equals(range.back, r2.back));
  1271.  
  1272. // Test opIndex(i)
  1273. assert(equals(range[i], expected[i]));
  1274. assert(equals(range[i], r2[i]));
  1275. }
  1276. assert(!range.empty);
  1277. assert(!r2.empty);
  1278. assert(original.length == expected.length);
  1279. }
  1280. }
  1281.  
  1282. version(sdlangUnittest)
  1283. unittest
  1284. {
  1285. import sdlang.parser;
  1286. writeln("Unittesting sdlang ast...");
  1287. stdout.flush();
  1288.  
  1289. Tag root;
  1290. root = parseSource("");
  1291. testRandomAccessRange(root.attributes, cast( Attribute[])[]);
  1292. testRandomAccessRange(root.tags, cast( Tag[])[]);
  1293. testRandomAccessRange(root.namespaces, cast(Tag.NamespaceAccess[])[]);
  1294.  
  1295. root = parseSource(`
  1296. blue 3 "Lee" isThree=true
  1297. blue 5 "Chan" 12345 isThree=false
  1298. stuff:orange 1 2 3 2 1
  1299. stuff:square points=4 dimensions=2 points="Still four"
  1300. stuff:triangle data:points=3 data:dimensions=2
  1301. nothing
  1302. namespaces small:A=1 med:A=2 big:A=3 small:B=10 big:B=30
  1303.  
  1304. people visitor:a=1 b=2 {
  1305. chiyo "Small" "Flies?" nemesis="Car" score=100
  1306. yukari
  1307. visitor:sana
  1308. tomo
  1309. visitor:hayama
  1310. }
  1311. `);
  1312.  
  1313. auto blue3 = new Tag(
  1314. null, "", "blue",
  1315. [ Value(3), Value("Lee") ],
  1316. [ new Attribute("isThree", Value(true)) ],
  1317. null
  1318. );
  1319. auto blue5 = new Tag(
  1320. null, "", "blue",
  1321. [ Value(5), Value("Chan"), Value(12345) ],
  1322. [ new Attribute("isThree", Value(false)) ],
  1323. null
  1324. );
  1325. auto orange = new Tag(
  1326. null, "stuff", "orange",
  1327. [ Value(1), Value(2), Value(3), Value(2), Value(1) ],
  1328. null,
  1329. null
  1330. );
  1331. auto square = new Tag(
  1332. null, "stuff", "square",
  1333. null,
  1334. [
  1335. new Attribute("points", Value(4)),
  1336. new Attribute("dimensions", Value(2)),
  1337. new Attribute("points", Value("Still four")),
  1338. ],
  1339. null
  1340. );
  1341. auto triangle = new Tag(
  1342. null, "stuff", "triangle",
  1343. null,
  1344. [
  1345. new Attribute("data", "points", Value(3)),
  1346. new Attribute("data", "dimensions", Value(2)),
  1347. ],
  1348. null
  1349. );
  1350. auto nothing = new Tag(
  1351. null, "", "nothing",
  1352. null, null, null
  1353. );
  1354. auto namespaces = new Tag(
  1355. null, "", "namespaces",
  1356. null,
  1357. [
  1358. new Attribute("small", "A", Value(1)),
  1359. new Attribute("med", "A", Value(2)),
  1360. new Attribute("big", "A", Value(3)),
  1361. new Attribute("small", "B", Value(10)),
  1362. new Attribute("big", "B", Value(30)),
  1363. ],
  1364. null
  1365. );
  1366. auto chiyo = new Tag(
  1367. null, "", "chiyo",
  1368. [ Value("Small"), Value("Flies?") ],
  1369. [
  1370. new Attribute("nemesis", Value("Car")),
  1371. new Attribute("score", Value(100)),
  1372. ],
  1373. null
  1374. );
  1375. auto chiyo_ = new Tag(
  1376. null, "", "chiyo_",
  1377. [ Value("Small"), Value("Flies?") ],
  1378. [
  1379. new Attribute("nemesis", Value("Car")),
  1380. new Attribute("score", Value(100)),
  1381. ],
  1382. null
  1383. );
  1384. auto yukari = new Tag(
  1385. null, "", "yukari",
  1386. null, null, null
  1387. );
  1388. auto sana = new Tag(
  1389. null, "visitor", "sana",
  1390. null, null, null
  1391. );
  1392. auto sana_ = new Tag(
  1393. null, "visitor", "sana_",
  1394. null, null, null
  1395. );
  1396. auto sanaVisitor_ = new Tag(
  1397. null, "visitor_", "sana_",
  1398. null, null, null
  1399. );
  1400. auto tomo = new Tag(
  1401. null, "", "tomo",
  1402. null, null, null
  1403. );
  1404. auto hayama = new Tag(
  1405. null, "visitor", "hayama",
  1406. null, null, null
  1407. );
  1408. auto people = new Tag(
  1409. null, "", "people",
  1410. null,
  1411. [
  1412. new Attribute("visitor", "a", Value(1)),
  1413. new Attribute("b", Value(2)),
  1414. ],
  1415. [chiyo, yukari, sana, tomo, hayama]
  1416. );
  1417.  
  1418. assert(blue3 .opEquals( blue3 ));
  1419. assert(blue5 .opEquals( blue5 ));
  1420. assert(orange .opEquals( orange ));
  1421. assert(square .opEquals( square ));
  1422. assert(triangle .opEquals( triangle ));
  1423. assert(nothing .opEquals( nothing ));
  1424. assert(namespaces .opEquals( namespaces ));
  1425. assert(people .opEquals( people ));
  1426. assert(chiyo .opEquals( chiyo ));
  1427. assert(yukari .opEquals( yukari ));
  1428. assert(sana .opEquals( sana ));
  1429. assert(tomo .opEquals( tomo ));
  1430. assert(hayama .opEquals( hayama ));
  1431.  
  1432. assert(!blue3.opEquals(orange));
  1433. assert(!blue3.opEquals(people));
  1434. assert(!blue3.opEquals(sana));
  1435. assert(!blue3.opEquals(blue5));
  1436. assert(!blue5.opEquals(blue3));
  1437.  
  1438. alias Tag.NamespaceAccess NSA;
  1439. static bool namespaceEquals(NSA n1, NSA n2)
  1440. {
  1441. return n1.name == n2.name;
  1442. }
  1443.  
  1444. testRandomAccessRange(root.attributes, cast(Attribute[])[]);
  1445. testRandomAccessRange(root.tags, [blue3, blue5, nothing, namespaces, people]);
  1446. testRandomAccessRange(root.namespaces, [NSA(""), NSA("stuff")], &namespaceEquals);
  1447. testRandomAccessRange(root.namespaces[0].tags, [blue3, blue5, nothing, namespaces, people]);
  1448. testRandomAccessRange(root.namespaces[1].tags, [orange, square, triangle]);
  1449. assert("" in root.namespaces);
  1450. assert("stuff" in root.namespaces);
  1451. assert("foobar" !in root.namespaces);
  1452. testRandomAccessRange(root.namespaces[ ""].tags, [blue3, blue5, nothing, namespaces, people]);
  1453. testRandomAccessRange(root.namespaces["stuff"].tags, [orange, square, triangle]);
  1454. testRandomAccessRange(root.all.attributes, cast(Attribute[])[]);
  1455. testRandomAccessRange(root.all.tags, [blue3, blue5, orange, square, triangle, nothing, namespaces, people]);
  1456. testRandomAccessRange(root.all.tags[], [blue3, blue5, orange, square, triangle, nothing, namespaces, people]);
  1457. testRandomAccessRange(root.all.tags[3..6], [square, triangle, nothing]);
  1458. assert("blue" in root.tags);
  1459. assert("nothing" in root.tags);
  1460. assert("people" in root.tags);
  1461. assert("orange" !in root.tags);
  1462. assert("square" !in root.tags);
  1463. assert("foobar" !in root.tags);
  1464. assert("blue" in root.all.tags);
  1465. assert("nothing" in root.all.tags);
  1466. assert("people" in root.all.tags);
  1467. assert("orange" in root.all.tags);
  1468. assert("square" in root.all.tags);
  1469. assert("foobar" !in root.all.tags);
  1470. assert("orange" in root.namespaces["stuff"].tags);
  1471. assert("square" in root.namespaces["stuff"].tags);
  1472. assert("square" in root.namespaces["stuff"].tags);
  1473. assert("foobar" !in root.attributes);
  1474. assert("foobar" !in root.all.attributes);
  1475. assert("foobar" !in root.namespaces["stuff"].attributes);
  1476. assert("blue" !in root.attributes);
  1477. assert("blue" !in root.all.attributes);
  1478. assert("blue" !in root.namespaces["stuff"].attributes);
  1479. testRandomAccessRange(root.tags["nothing"], [nothing]);
  1480. testRandomAccessRange(root.tags["blue"], [blue3, blue5]);
  1481. testRandomAccessRange(root.namespaces["stuff"].tags["orange"], [orange]);
  1482. testRandomAccessRange(root.all.tags["nothing"], [nothing]);
  1483. testRandomAccessRange(root.all.tags["blue"], [blue3, blue5]);
  1484. testRandomAccessRange(root.all.tags["orange"], [orange]);
  1485.  
  1486. assertThrown!SDLangRangeException(root.tags["foobar"]);
  1487. assertThrown!SDLangRangeException(root.all.tags["foobar"]);
  1488. assertThrown!SDLangRangeException(root.attributes["foobar"]);
  1489. assertThrown!SDLangRangeException(root.all.attributes["foobar"]);
  1490.  
  1491. // DMD Issue #12585 causes a segfault in these two tests when using 2.064 or 2.065,
  1492. // so work around it.
  1493. //assertThrown!SDLangRangeException(root.namespaces["foobar"].tags["foobar"]);
  1494. //assertThrown!SDLangRangeException(root.namespaces["foobar"].attributes["foobar"]);
  1495. bool didCatch = false;
  1496. try
  1497. auto x = root.namespaces["foobar"].tags["foobar"];
  1498. catch(SDLangRangeException e)
  1499. didCatch = true;
  1500. assert(didCatch);
  1501.  
  1502. didCatch = false;
  1503. try
  1504. auto x = root.namespaces["foobar"].attributes["foobar"];
  1505. catch(SDLangRangeException e)
  1506. didCatch = true;
  1507. assert(didCatch);
  1508.  
  1509. testRandomAccessRange(root.maybe.tags["nothing"], [nothing]);
  1510. testRandomAccessRange(root.maybe.tags["blue"], [blue3, blue5]);
  1511. testRandomAccessRange(root.maybe.namespaces["stuff"].tags["orange"], [orange]);
  1512. testRandomAccessRange(root.maybe.all.tags["nothing"], [nothing]);
  1513. testRandomAccessRange(root.maybe.all.tags["blue"], [blue3, blue5]);
  1514. testRandomAccessRange(root.maybe.all.tags["blue"][], [blue3, blue5]);
  1515. testRandomAccessRange(root.maybe.all.tags["blue"][0..1], [blue3]);
  1516. testRandomAccessRange(root.maybe.all.tags["blue"][1..2], [blue5]);
  1517. testRandomAccessRange(root.maybe.all.tags["orange"], [orange]);
  1518. testRandomAccessRange(root.maybe.tags["foobar"], cast(Tag[])[]);
  1519. testRandomAccessRange(root.maybe.all.tags["foobar"], cast(Tag[])[]);
  1520. testRandomAccessRange(root.maybe.namespaces["foobar"].tags["foobar"], cast(Tag[])[]);
  1521. testRandomAccessRange(root.maybe.attributes["foobar"], cast(Attribute[])[]);
  1522. testRandomAccessRange(root.maybe.all.attributes["foobar"], cast(Attribute[])[]);
  1523. testRandomAccessRange(root.maybe.namespaces["foobar"].attributes["foobar"], cast(Attribute[])[]);
  1524.  
  1525. testRandomAccessRange(blue3.attributes, [ new Attribute("isThree", Value(true)) ]);
  1526. testRandomAccessRange(blue3.tags, cast(Tag[])[]);
  1527. testRandomAccessRange(blue3.namespaces, [NSA("")], &namespaceEquals);
  1528. testRandomAccessRange(blue3.all.attributes, [ new Attribute("isThree", Value(true)) ]);
  1529. testRandomAccessRange(blue3.all.tags, cast(Tag[])[]);
  1530.  
  1531. testRandomAccessRange(blue5.attributes, [ new Attribute("isThree", Value(false)) ]);
  1532. testRandomAccessRange(blue5.tags, cast(Tag[])[]);
  1533. testRandomAccessRange(blue5.namespaces, [NSA("")], &namespaceEquals);
  1534. testRandomAccessRange(blue5.all.attributes, [ new Attribute("isThree", Value(false)) ]);
  1535. testRandomAccessRange(blue5.all.tags, cast(Tag[])[]);
  1536.  
  1537. testRandomAccessRange(orange.attributes, cast(Attribute[])[]);
  1538. testRandomAccessRange(orange.tags, cast(Tag[])[]);
  1539. testRandomAccessRange(orange.namespaces, cast(NSA[])[], &namespaceEquals);
  1540. testRandomAccessRange(orange.all.attributes, cast(Attribute[])[]);
  1541. testRandomAccessRange(orange.all.tags, cast(Tag[])[]);
  1542.  
  1543. testRandomAccessRange(square.attributes, [
  1544. new Attribute("points", Value(4)),
  1545. new Attribute("dimensions", Value(2)),
  1546. new Attribute("points", Value("Still four")),
  1547. ]);
  1548. testRandomAccessRange(square.tags, cast(Tag[])[]);
  1549. testRandomAccessRange(square.namespaces, [NSA("")], &namespaceEquals);
  1550. testRandomAccessRange(square.all.attributes, [
  1551. new Attribute("points", Value(4)),
  1552. new Attribute("dimensions", Value(2)),
  1553. new Attribute("points", Value("Still four")),
  1554. ]);
  1555. testRandomAccessRange(square.all.tags, cast(Tag[])[]);
  1556.  
  1557. testRandomAccessRange(triangle.attributes, cast(Attribute[])[]);
  1558. testRandomAccessRange(triangle.tags, cast(Tag[])[]);
  1559. testRandomAccessRange(triangle.namespaces, [NSA("data")], &namespaceEquals);
  1560. testRandomAccessRange(triangle.namespaces[0].attributes, [
  1561. new Attribute("data", "points", Value(3)),
  1562. new Attribute("data", "dimensions", Value(2)),
  1563. ]);
  1564. assert("data" in triangle.namespaces);
  1565. assert("foobar" !in triangle.namespaces);
  1566. testRandomAccessRange(triangle.namespaces["data"].attributes, [
  1567. new Attribute("data", "points", Value(3)),
  1568. new Attribute("data", "dimensions", Value(2)),
  1569. ]);
  1570. testRandomAccessRange(triangle.all.attributes, [
  1571. new Attribute("data", "points", Value(3)),
  1572. new Attribute("data", "dimensions", Value(2)),
  1573. ]);
  1574. testRandomAccessRange(triangle.all.tags, cast(Tag[])[]);
  1575.  
  1576. testRandomAccessRange(nothing.attributes, cast(Attribute[])[]);
  1577. testRandomAccessRange(nothing.tags, cast(Tag[])[]);
  1578. testRandomAccessRange(nothing.namespaces, cast(NSA[])[], &namespaceEquals);
  1579. testRandomAccessRange(nothing.all.attributes, cast(Attribute[])[]);
  1580. testRandomAccessRange(nothing.all.tags, cast(Tag[])[]);
  1581.  
  1582. testRandomAccessRange(namespaces.attributes, cast(Attribute[])[]);
  1583. testRandomAccessRange(namespaces.tags, cast(Tag[])[]);
  1584. testRandomAccessRange(namespaces.namespaces, [NSA("small"), NSA("med"), NSA("big")], &namespaceEquals);
  1585. testRandomAccessRange(namespaces.namespaces[], [NSA("small"), NSA("med"), NSA("big")], &namespaceEquals);
  1586. testRandomAccessRange(namespaces.namespaces[1..2], [NSA("med")], &namespaceEquals);
  1587. testRandomAccessRange(namespaces.namespaces[0].attributes, [
  1588. new Attribute("small", "A", Value(1)),
  1589. new Attribute("small", "B", Value(10)),
  1590. ]);
  1591. testRandomAccessRange(namespaces.namespaces[1].attributes, [
  1592. new Attribute("med", "A", Value(2)),
  1593. ]);
  1594. testRandomAccessRange(namespaces.namespaces[2].attributes, [
  1595. new Attribute("big", "A", Value(3)),
  1596. new Attribute("big", "B", Value(30)),
  1597. ]);
  1598. testRandomAccessRange(namespaces.namespaces[1..2][0].attributes, [
  1599. new Attribute("med", "A", Value(2)),
  1600. ]);
  1601. assert("small" in namespaces.namespaces);
  1602. assert("med" in namespaces.namespaces);
  1603. assert("big" in namespaces.namespaces);
  1604. assert("foobar" !in namespaces.namespaces);
  1605. assert("small" !in namespaces.namespaces[1..2]);
  1606. assert("med" in namespaces.namespaces[1..2]);
  1607. assert("big" !in namespaces.namespaces[1..2]);
  1608. assert("foobar" !in namespaces.namespaces[1..2]);
  1609. testRandomAccessRange(namespaces.namespaces["small"].attributes, [
  1610. new Attribute("small", "A", Value(1)),
  1611. new Attribute("small", "B", Value(10)),
  1612. ]);
  1613. testRandomAccessRange(namespaces.namespaces["med"].attributes, [
  1614. new Attribute("med", "A", Value(2)),
  1615. ]);
  1616. testRandomAccessRange(namespaces.namespaces["big"].attributes, [
  1617. new Attribute("big", "A", Value(3)),
  1618. new Attribute("big", "B", Value(30)),
  1619. ]);
  1620. testRandomAccessRange(namespaces.all.attributes, [
  1621. new Attribute("small", "A", Value(1)),
  1622. new Attribute("med", "A", Value(2)),
  1623. new Attribute("big", "A", Value(3)),
  1624. new Attribute("small", "B", Value(10)),
  1625. new Attribute("big", "B", Value(30)),
  1626. ]);
  1627. testRandomAccessRange(namespaces.all.attributes[], [
  1628. new Attribute("small", "A", Value(1)),
  1629. new Attribute("med", "A", Value(2)),
  1630. new Attribute("big", "A", Value(3)),
  1631. new Attribute("small", "B", Value(10)),
  1632. new Attribute("big", "B", Value(30)),
  1633. ]);
  1634. testRandomAccessRange(namespaces.all.attributes[2..4], [
  1635. new Attribute("big", "A", Value(3)),
  1636. new Attribute("small", "B", Value(10)),
  1637. ]);
  1638. testRandomAccessRange(namespaces.all.tags, cast(Tag[])[]);
  1639. assert("A" !in namespaces.attributes);
  1640. assert("B" !in namespaces.attributes);
  1641. assert("foobar" !in namespaces.attributes);
  1642. assert("A" in namespaces.all.attributes);
  1643. assert("B" in namespaces.all.attributes);
  1644. assert("foobar" !in namespaces.all.attributes);
  1645. assert("A" in namespaces.namespaces["small"].attributes);
  1646. assert("B" in namespaces.namespaces["small"].attributes);
  1647. assert("foobar" !in namespaces.namespaces["small"].attributes);
  1648. assert("A" in namespaces.namespaces["med"].attributes);
  1649. assert("B" !in namespaces.namespaces["med"].attributes);
  1650. assert("foobar" !in namespaces.namespaces["med"].attributes);
  1651. assert("A" in namespaces.namespaces["big"].attributes);
  1652. assert("B" in namespaces.namespaces["big"].attributes);
  1653. assert("foobar" !in namespaces.namespaces["big"].attributes);
  1654. assert("foobar" !in namespaces.tags);
  1655. assert("foobar" !in namespaces.all.tags);
  1656. assert("foobar" !in namespaces.namespaces["small"].tags);
  1657. assert("A" !in namespaces.tags);
  1658. assert("A" !in namespaces.all.tags);
  1659. assert("A" !in namespaces.namespaces["small"].tags);
  1660. testRandomAccessRange(namespaces.namespaces["small"].attributes["A"], [
  1661. new Attribute("small", "A", Value(1)),
  1662. ]);
  1663. testRandomAccessRange(namespaces.namespaces["med"].attributes["A"], [
  1664. new Attribute("med", "A", Value(2)),
  1665. ]);
  1666. testRandomAccessRange(namespaces.namespaces["big"].attributes["A"], [
  1667. new Attribute("big", "A", Value(3)),
  1668. ]);
  1669. testRandomAccessRange(namespaces.all.attributes["A"], [
  1670. new Attribute("small", "A", Value(1)),
  1671. new Attribute("med", "A", Value(2)),
  1672. new Attribute("big", "A", Value(3)),
  1673. ]);
  1674. testRandomAccessRange(namespaces.all.attributes["B"], [
  1675. new Attribute("small", "B", Value(10)),
  1676. new Attribute("big", "B", Value(30)),
  1677. ]);
  1678.  
  1679. testRandomAccessRange(chiyo.attributes, [
  1680. new Attribute("nemesis", Value("Car")),
  1681. new Attribute("score", Value(100)),
  1682. ]);
  1683. testRandomAccessRange(chiyo.tags, cast(Tag[])[]);
  1684. testRandomAccessRange(chiyo.namespaces, [NSA("")], &namespaceEquals);
  1685. testRandomAccessRange(chiyo.all.attributes, [
  1686. new Attribute("nemesis", Value("Car")),
  1687. new Attribute("score", Value(100)),
  1688. ]);
  1689. testRandomAccessRange(chiyo.all.tags, cast(Tag[])[]);
  1690.  
  1691. testRandomAccessRange(yukari.attributes, cast(Attribute[])[]);
  1692. testRandomAccessRange(yukari.tags, cast(Tag[])[]);
  1693. testRandomAccessRange(yukari.namespaces, cast(NSA[])[], &namespaceEquals);
  1694. testRandomAccessRange(yukari.all.attributes, cast(Attribute[])[]);
  1695. testRandomAccessRange(yukari.all.tags, cast(Tag[])[]);
  1696.  
  1697. testRandomAccessRange(sana.attributes, cast(Attribute[])[]);
  1698. testRandomAccessRange(sana.tags, cast(Tag[])[]);
  1699. testRandomAccessRange(sana.namespaces, cast(NSA[])[], &namespaceEquals);
  1700. testRandomAccessRange(sana.all.attributes, cast(Attribute[])[]);
  1701. testRandomAccessRange(sana.all.tags, cast(Tag[])[]);
  1702.  
  1703. testRandomAccessRange(people.attributes, [new Attribute("b", Value(2))]);
  1704. testRandomAccessRange(people.tags, [chiyo, yukari, tomo]);
  1705. testRandomAccessRange(people.namespaces, [NSA("visitor"), NSA("")], &namespaceEquals);
  1706. testRandomAccessRange(people.namespaces[0].attributes, [new Attribute("visitor", "a", Value(1))]);
  1707. testRandomAccessRange(people.namespaces[1].attributes, [new Attribute("b", Value(2))]);
  1708. testRandomAccessRange(people.namespaces[0].tags, [sana, hayama]);
  1709. testRandomAccessRange(people.namespaces[1].tags, [chiyo, yukari, tomo]);
  1710. assert("visitor" in people.namespaces);
  1711. assert("" in people.namespaces);
  1712. assert("foobar" !in people.namespaces);
  1713. testRandomAccessRange(people.namespaces["visitor"].attributes, [new Attribute("visitor", "a", Value(1))]);
  1714. testRandomAccessRange(people.namespaces[ ""].attributes, [new Attribute("b", Value(2))]);
  1715. testRandomAccessRange(people.namespaces["visitor"].tags, [sana, hayama]);
  1716. testRandomAccessRange(people.namespaces[ ""].tags, [chiyo, yukari, tomo]);
  1717. testRandomAccessRange(people.all.attributes, [
  1718. new Attribute("visitor", "a", Value(1)),
  1719. new Attribute("b", Value(2)),
  1720. ]);
  1721. testRandomAccessRange(people.all.tags, [chiyo, yukari, sana, tomo, hayama]);
  1722.  
  1723. people.attributes["b"][0].name = "b_";
  1724. people.namespaces["visitor"].attributes["a"][0].name = "a_";
  1725. people.tags["chiyo"][0].name = "chiyo_";
  1726. people.namespaces["visitor"].tags["sana"][0].name = "sana_";
  1727.  
  1728. assert("b_" in people.attributes);
  1729. assert("a_" in people.namespaces["visitor"].attributes);
  1730. assert("chiyo_" in people.tags);
  1731. assert("sana_" in people.namespaces["visitor"].tags);
  1732.  
  1733. assert(people.attributes["b_"][0] == new Attribute("b_", Value(2)));
  1734. assert(people.namespaces["visitor"].attributes["a_"][0] == new Attribute("visitor", "a_", Value(1)));
  1735. assert(people.tags["chiyo_"][0] == chiyo_);
  1736. assert(people.namespaces["visitor"].tags["sana_"][0] == sana_);
  1737.  
  1738. assert("b" !in people.attributes);
  1739. assert("a" !in people.namespaces["visitor"].attributes);
  1740. assert("chiyo" !in people.tags);
  1741. assert("sana" !in people.namespaces["visitor"].tags);
  1742.  
  1743. assert(people.maybe.attributes["b"].length == 0);
  1744. assert(people.maybe.namespaces["visitor"].attributes["a"].length == 0);
  1745. assert(people.maybe.tags["chiyo"].length == 0);
  1746. assert(people.maybe.namespaces["visitor"].tags["sana"].length == 0);
  1747.  
  1748. people.tags["tomo"][0].remove();
  1749. people.namespaces["visitor"].tags["hayama"][0].remove();
  1750. people.tags["chiyo_"][0].remove();
  1751. testRandomAccessRange(people.tags, [yukari]);
  1752. testRandomAccessRange(people.namespaces, [NSA("visitor"), NSA("")], &namespaceEquals);
  1753. testRandomAccessRange(people.namespaces[0].tags, [sana_]);
  1754. testRandomAccessRange(people.namespaces[1].tags, [yukari]);
  1755. assert("visitor" in people.namespaces);
  1756. assert("" in people.namespaces);
  1757. assert("foobar" !in people.namespaces);
  1758. testRandomAccessRange(people.namespaces["visitor"].tags, [sana_]);
  1759. testRandomAccessRange(people.namespaces[ ""].tags, [yukari]);
  1760. testRandomAccessRange(people.all.tags, [yukari, sana_]);
  1761.  
  1762. people.attributes["b_"][0].namespace = "_";
  1763. people.namespaces["visitor"].attributes["a_"][0].namespace = "visitor_";
  1764. assert("_" in people.namespaces);
  1765. assert("visitor_" in people.namespaces);
  1766. assert("" in people.namespaces);
  1767. assert("visitor" in people.namespaces);
  1768. people.namespaces["visitor"].tags["sana_"][0].namespace = "visitor_";
  1769. assert("_" in people.namespaces);
  1770. assert("visitor_" in people.namespaces);
  1771. assert("" in people.namespaces);
  1772. assert("visitor" !in people.namespaces);
  1773.  
  1774. assert(people.namespaces["_" ].attributes["b_"][0] == new Attribute("_", "b_", Value(2)));
  1775. assert(people.namespaces["visitor_"].attributes["a_"][0] == new Attribute("visitor_", "a_", Value(1)));
  1776. assert(people.namespaces["visitor_"].tags["sana_"][0] == sanaVisitor_);
  1777.  
  1778. people.tags["yukari"][0].remove();
  1779. people.namespaces["visitor_"].tags["sana_"][0].remove();
  1780. people.namespaces["visitor_"].attributes["a_"][0].namespace = "visitor";
  1781. people.namespaces["_"].attributes["b_"][0].namespace = "";
  1782. testRandomAccessRange(people.tags, cast(Tag[])[]);
  1783. testRandomAccessRange(people.namespaces, [NSA("visitor"), NSA("")], &namespaceEquals);
  1784. testRandomAccessRange(people.namespaces[0].tags, cast(Tag[])[]);
  1785. testRandomAccessRange(people.namespaces[1].tags, cast(Tag[])[]);
  1786. assert("visitor" in people.namespaces);
  1787. assert("" in people.namespaces);
  1788. assert("foobar" !in people.namespaces);
  1789. testRandomAccessRange(people.namespaces["visitor"].tags, cast(Tag[])[]);
  1790. testRandomAccessRange(people.namespaces[ ""].tags, cast(Tag[])[]);
  1791. testRandomAccessRange(people.all.tags, cast(Tag[])[]);
  1792.  
  1793. people.namespaces["visitor"].attributes["a_"][0].remove();
  1794. testRandomAccessRange(people.attributes, [new Attribute("b_", Value(2))]);
  1795. testRandomAccessRange(people.namespaces, [NSA("")], &namespaceEquals);
  1796. testRandomAccessRange(people.namespaces[0].attributes, [new Attribute("b_", Value(2))]);
  1797. assert("visitor" !in people.namespaces);
  1798. assert("" in people.namespaces);
  1799. assert("foobar" !in people.namespaces);
  1800. testRandomAccessRange(people.namespaces[""].attributes, [new Attribute("b_", Value(2))]);
  1801. testRandomAccessRange(people.all.attributes, [
  1802. new Attribute("b_", Value(2)),
  1803. ]);
  1804.  
  1805. people.attributes["b_"][0].remove();
  1806. testRandomAccessRange(people.attributes, cast(Attribute[])[]);
  1807. testRandomAccessRange(people.namespaces, cast(NSA[])[], &namespaceEquals);
  1808. assert("visitor" !in people.namespaces);
  1809. assert("" !in people.namespaces);
  1810. assert("foobar" !in people.namespaces);
  1811. testRandomAccessRange(people.all.attributes, cast(Attribute[])[]);
  1812. }
  1813.  
  1814. // Regression test, issue #11: https://github.com/Abscissa/SDLang-D/issues/11
  1815. version(sdlangUnittest)
  1816. unittest
  1817. {
  1818. import sdlang.parser;
  1819. writeln("ast: Regression test issue #11...");
  1820. stdout.flush();
  1821.  
  1822. auto root = parseSource(
  1823. `//
  1824. a`);
  1825.  
  1826. assert("a" in root.tags);
  1827.  
  1828. root = parseSource(
  1829. `//
  1830. parent {
  1831. child
  1832. }
  1833. `);
  1834.  
  1835. auto child = new Tag(
  1836. null, "", "child",
  1837. null, null, null
  1838. );
  1839.  
  1840. assert("parent" in root.tags);
  1841. assert("child" !in root.tags);
  1842. testRandomAccessRange(root.tags["parent"][0].tags, [child]);
  1843. assert("child" in root.tags["parent"][0].tags);
  1844. }