fork download
  1. import std.stdio;
  2. import std.string;
  3. import std.conv;
  4. import std.outbuffer;
  5. import std.algorithm;
  6. import std.array;
  7. import std.json;
  8. import std.xml;
  9. import std.file;
  10. import std.path;
  11.  
  12. void main()
  13. {
  14. writeln(Tree.fromJSON(`{ "users" : [
  15. {
  16. "name" : "John",
  17. "age" : 30
  18. },
  19. {
  20. "name" : "John",
  21. "age" : 30
  22. }]}`).toString());
  23. }
  24.  
  25. class Tree
  26. {
  27.  
  28. string name;
  29. string value;
  30. string baseUri;
  31. size_t row;
  32. size_t col;
  33. Tree[] childs;
  34.  
  35. this(string name, string value, Tree[] childs, string baseUri = "", size_t row = 0,
  36. size_t col = 0)
  37. {
  38. this.name = name;
  39. this.value = value;
  40. this.childs = childs;
  41. this.baseUri = baseUri;
  42. this.row = row;
  43. this.col = col;
  44. }
  45.  
  46. this(DirEntry input, string baseUri = null, size_t row = 1, size_t col = 1)
  47. {
  48. if (baseUri is null)
  49. baseUri = absolutePath(input.name);
  50. this(cast(string) read(input.name), baseUri, row, col);
  51. }
  52.  
  53. this(File input, string baseUri = null, size_t row = 1, size_t col = 1)
  54. {
  55. if (baseUri is null)
  56. baseUri = input.name.absolutePath.asNormalizedPath.array;
  57. this(cast(string) read(input.name), baseUri, row, col);
  58. }
  59.  
  60. unittest
  61. {
  62. assert(new Tree("foo\nbar\n", "").length == 2);
  63. assert(new Tree("foo\nbar\n", "")[1].name == "bar");
  64. assert(new Tree("foo\n\n\n", "").length == 1);
  65.  
  66. assert(new Tree("\\foo\n\\bar\n", "").value == "foo\nbar");
  67. assert(new Tree("\\foo\n\\bar\n", "").length == 0);
  68.  
  69. assert(new Tree("foo bar \\pol", "")[0][0].value == "pol");
  70. assert(new Tree("foo bar\n\t\\pol\n\t\\men", "")[0][0].value == "pol\nmen");
  71. }
  72.  
  73. this(string input, string baseUri, size_t row = 1, size_t col = 1)
  74. {
  75. this("", null, [], baseUri, row, col);
  76. Tree[] stack = [this];
  77.  
  78. Tree parent = this;
  79. while (input.length)
  80. {
  81.  
  82. auto name = input.takeUntil("\t\n \\");
  83. if (name.length)
  84. {
  85. auto next = new Tree(name, null, [], baseUri, row, col);
  86. parent.childs ~= next;
  87. parent = next;
  88. col += name.length + input.take(" ").length;
  89. continue;
  90. }
  91. if (!input.length)
  92. break;
  93.  
  94. if (input[0] == '\\')
  95. {
  96. auto value = input.takeUntil("\n")[1 .. $];
  97. if (parent.value is null)
  98. parent.value = value;
  99. else
  100. parent.value ~= "\n" ~ value;
  101. }
  102. if (!input.length)
  103. break;
  104.  
  105. if (input[0] != '\n')
  106. {
  107. throw new Exception("Unexpected symbol (" ~ input[0] ~ ")");
  108. }
  109. input = input[1 .. $];
  110. col = 1;
  111. row += 1;
  112.  
  113. auto indent = input.take("\t").length;
  114. col = indent;
  115. if (indent > stack.length)
  116. {
  117. throw new Exception("Too many TABs " ~ row.to!string ~ ":" ~ col.to!string);
  118. }
  119.  
  120. stack ~= parent;
  121. stack.length = indent + 1;
  122. parent = stack[indent];
  123. }
  124. }
  125.  
  126. Tree make(string name = null, string value = null, Tree[] childs = null,
  127. string baseUri = null, size_t row = 0, size_t col = 0)
  128. {
  129. return new Tree(name ? name : this.name, value ? value : this.value,
  130. childs ? childs : this.childs, baseUri ? baseUri : this.baseUri,
  131. row ? row : this.row, col ? col : this.col);
  132. }
  133.  
  134. static Tree fromJSON(string json)
  135. {
  136. return Tree.fromJSON(parseJSON(json));
  137. }
  138.  
  139. static Tree fromJSON(JSONValue json)
  140. {
  141. switch (json.type)
  142. {
  143. case JSON_TYPE.FALSE:
  144. return new Tree("false", "", []);
  145. case JSON_TYPE.TRUE:
  146. return new Tree("true", "", []);
  147. case JSON_TYPE.NULL:
  148. return new Tree("null", "", []);
  149. case JSON_TYPE.FLOAT:
  150. return new Tree("float", json.floating.to!string, []);
  151. case JSON_TYPE.INTEGER:
  152. return new Tree("int", json.integer.to!string, []);
  153. case JSON_TYPE.UINTEGER:
  154. return new Tree("int", json.uinteger.to!string, []);
  155. case JSON_TYPE.STRING:
  156. return new Tree("string", json.str, []);
  157. case JSON_TYPE.ARRAY:
  158. return new Tree("list", "",
  159. json.array.map!(json => Tree.fromJSON(json)).array);
  160. case JSON_TYPE.OBJECT:
  161. Tree[] childs = [];
  162. foreach (key, value; json.object)
  163. {
  164. childs ~= new Tree("*", key, [new Tree(":", "", [Tree.fromJSON(value)])]);
  165. }
  166. return new Tree("dict", "", childs);
  167. default:
  168. throw new Error("Unsupported type: " ~ json.type);
  169. }
  170. }
  171.  
  172. static Tree fromXML(string xml)
  173. {
  174. return Tree.fromXML(new Document(xml));
  175. }
  176.  
  177. static Tree fromXML(Item xml)
  178. {
  179.  
  180. auto el = cast(Element) xml;
  181. if (el)
  182. {
  183. Tree[] attrs;
  184. foreach (key, val; el.tag.attr)
  185. {
  186. attrs ~= new Tree("@", "", [new Tree(key, val)]);
  187. }
  188. auto childs = el.items
  189. .map!(Tree.fromXML)
  190. .filter!(a => a)
  191. .array;
  192. return new Tree(el.tag.name, "", attrs ~ childs);
  193. }
  194.  
  195. auto com = cast(Comment) xml;
  196. if (com)
  197. {
  198. return new Tree("--", com.to!string[4 .. $ - 3], []);
  199. }
  200.  
  201. auto txt = cast(Text) xml;
  202. if (txt)
  203. {
  204. if (txt.to!string
  205. .all!(isSpace))
  206. return null;
  207. return new Tree("'", com.to!string, []);
  208. }
  209.  
  210. throw new Error("Unsupported node type!");
  211. }
  212.  
  213. OutputType pipe(OutputType)(OutputType output, string prefix = "")
  214. {
  215. if (this.name.length)
  216. output.write(this.name ~ " ");
  217.  
  218. auto chunks = this.value.length ? this.value.split("\n") : [];
  219.  
  220. if (chunks.length + this.childs.length == 1)
  221. {
  222. if (chunks.length)
  223. output.write("\\" ~ chunks[0] ~ "\n");
  224. else
  225. childs[0].pipe(output, prefix);
  226. }
  227. else
  228. {
  229. output.write("\n");
  230. if (this.name.length)
  231. prefix ~= "\t";
  232.  
  233. foreach (chunk; chunks)
  234. output.write(prefix ~ "\\" ~ chunk ~ "\n");
  235.  
  236. foreach (child; this.childs)
  237. {
  238. output.write(prefix);
  239. child.pipe(output, prefix);
  240. }
  241. }
  242.  
  243. return output;
  244. }
  245.  
  246. override string toString()
  247. {
  248. OutBuffer buf = new OutBuffer;
  249. this.pipe(buf);
  250. return buf.to!string;
  251. }
  252.  
  253. Tree expand()
  254. {
  255. return this.make(null, null, [new Tree("@", this.uri,
  256. [])] ~ this.childs.map!(child => child.expand).array);
  257. }
  258.  
  259. string uri()
  260. {
  261. return this.baseUri ~ "#" ~ this.row.to!string ~ ":" ~ this.col.to!string;
  262. }
  263.  
  264. unittest
  265. {
  266. auto tree = new Tree("foo \\1\nbar \\2", "");
  267. assert(tree["bar"][0].to!string == "bar \\2\n");
  268. }
  269.  
  270. auto opIndex(string path)
  271. {
  272. return this[path.split(" ")];
  273. }
  274.  
  275. unittest
  276. {
  277. assert(new Tree("foo bar \\2", "")[["foo", "bar"]].to!string == "bar \\2\n");
  278. }
  279.  
  280. auto opIndex(string[] path)
  281. {
  282. Tree[] next = [this];
  283. foreach (string name; path)
  284. {
  285. if (!next.length)
  286. break;
  287. Tree[] prev = next;
  288. next = [];
  289. foreach (Tree item; prev)
  290. {
  291. foreach (Tree child; item.childs)
  292. {
  293. if (child.name != name)
  294. continue;
  295. next ~= child;
  296. }
  297. }
  298. }
  299. return new Tree("", "", next);
  300. }
  301.  
  302. Tree opIndex(size_t index)
  303. {
  304. return this.childs[index];
  305. }
  306.  
  307. Tree[] opSlice(size_t start, size_t end)
  308. {
  309. return this.childs[start .. end];
  310. }
  311.  
  312. size_t length()
  313. {
  314. return this.childs.length;
  315. }
  316.  
  317. size_t opDollar()
  318. {
  319. return this.childs.length;
  320. }
  321.  
  322. }
  323.  
  324. string take(ref string input, string symbols)
  325. {
  326. auto i = 0;
  327. while (i < input.length)
  328. {
  329. auto symbol = input[i];
  330. if (symbols.indexOf(symbol) == -1)
  331. {
  332. break;
  333. }
  334. else
  335. {
  336. i += 1;
  337. }
  338. }
  339. auto res = input[0 .. i];
  340. input = input[i .. $];
  341. return res;
  342. }
  343.  
  344. string takeUntil(ref string input, string symbols)
  345. {
  346. auto i = 0;
  347. while (i < input.length)
  348. {
  349. auto symbol = input[i];
  350. if (symbols.indexOf(symbol) == -1)
  351. {
  352. i += 1;
  353. }
  354. else
  355. {
  356. break;
  357. }
  358. }
  359. auto res = input[0 .. i];
  360. input = input[i .. $];
  361. return res;
  362. }
Success #stdin #stdout 0s 4348KB
stdin
Standard input is empty
stdout
dict * 
	\users
	: list 
		dict 
			* 
				\name
				: string \John
			* 
				\age
				: int \30
		dict 
			* 
				\name
				: string \John
			* 
				\age
				: int \30