Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 
 
 

536 строки
19 KiB

  1. proc visualize_lisp_machine() -> void {
  2. struct Drawn_Area {
  3. int x;
  4. int y;
  5. int width;
  6. int height;
  7. };
  8. fprintf(stderr, "Drawing visualization...");
  9. defer {
  10. fprintf(stderr, "Done!\n");
  11. };
  12. const int padding = 40;
  13. const int margin = 20;
  14. const char* draw_text_template = " <text x='%d' y='%d' font-size='20' font-family='monospace' style='fill: %s'>\n %s%s%s\n </text>\n";
  15. const char* draw_integer_template = " <text x='%d' y='%d' font-size='20' font-family='monospace'>\n %d\n </text>\n";
  16. const char* draw_float_template = " <text x='%d' y='%d' font-size='20' font-family='monospace'>\n %012.6f\n </text>\n";
  17. FILE *f = fopen("visualization.svg", "w");
  18. defer {
  19. fclose(f);
  20. };
  21. if (f == NULL) {
  22. create_generic_error("The file for writing the visualization"
  23. "could not be opened for writing");
  24. return;
  25. }
  26. int max_x = 0,
  27. max_y = 0,
  28. write_x = 0,
  29. write_y = 0;
  30. proc draw_margin = [&](int count = 1) -> Drawn_Area {
  31. write_x += margin * count;
  32. return {
  33. write_x - margin * count,
  34. write_y,
  35. margin * count,
  36. write_y
  37. };
  38. };
  39. proc draw_new_line = [&](int count = 1) {
  40. write_x = 0;
  41. write_y += 25 * count;
  42. };
  43. proc draw_text = [&](const char* text, const char* color = "#000000", bool draw_quotes = false, int max_length = 200) -> Drawn_Area {
  44. // take care of escaping sensitive chars
  45. int text_length = 0;
  46. int extra_needed_chars = draw_quotes ? 10 : 0;
  47. char* new_text = nullptr;
  48. char char_at_max_length = 0;
  49. char source;
  50. while ((source = text[text_length++]) != '\0') {
  51. switch (source) {
  52. case '\n':
  53. extra_needed_chars += 1;
  54. case '<':
  55. case '>':
  56. extra_needed_chars += 3;
  57. break;
  58. case '&':
  59. extra_needed_chars += 4;
  60. break;
  61. case '\'':
  62. case '"':
  63. extra_needed_chars += 5;
  64. }
  65. }
  66. // last char was \0 but we don't count it
  67. --text_length;
  68. if (text_length > max_length) {
  69. char_at_max_length = ((char*)text)[max_length];
  70. ((char*)text)[max_length] = '\0';
  71. text_length = max_length;
  72. }
  73. defer {
  74. if (char_at_max_length)
  75. ((char*)text)[max_length] = char_at_max_length;
  76. };
  77. // if we need to replace some chars
  78. if (extra_needed_chars > 0) {
  79. new_text = (char*)malloc((text_length + extra_needed_chars) * sizeof(char));
  80. int index_in_text = 0,
  81. index_in_new_text = 0;
  82. char source;
  83. while ((source = text[index_in_text++]) != '\0') {
  84. switch (source) {
  85. case '\n': new_text[index_in_new_text++] = '\\'; new_text[index_in_new_text++] = 'n'; break;
  86. case '<': new_text[index_in_new_text++] = '&'; new_text[index_in_new_text++] = 'l'; new_text[index_in_new_text++] = 't'; new_text[index_in_new_text++] = ';'; break;
  87. case '>': new_text[index_in_new_text++] = '&'; new_text[index_in_new_text++] = 'g'; new_text[index_in_new_text++] = 't'; new_text[index_in_new_text++] = ';'; break;
  88. case '&': new_text[index_in_new_text++] = '&'; new_text[index_in_new_text++] = 'a'; new_text[index_in_new_text++] = 'm'; new_text[index_in_new_text++] = 'p'; new_text[index_in_new_text++] = ';'; break;
  89. case '"': new_text[index_in_new_text++] = '&'; new_text[index_in_new_text++] = 'q'; new_text[index_in_new_text++] = 'u'; new_text[index_in_new_text++] = 'o'; new_text[index_in_new_text++] = 't'; new_text[index_in_new_text++] = ';'; break;
  90. case '\'': new_text[index_in_new_text++] = '&'; new_text[index_in_new_text++] = 'a'; new_text[index_in_new_text++] = 'p'; new_text[index_in_new_text++] = 'o'; new_text[index_in_new_text++] = 's'; new_text[index_in_new_text++] = ';'; break;
  91. default: new_text[index_in_new_text++] = source;
  92. }
  93. }
  94. new_text[index_in_new_text] = '\0';
  95. }
  96. int text_width = 12 * (text_length + (draw_quotes ? 2 : 0));
  97. if (write_x + text_width > max_x) max_x = write_x + text_width;
  98. if (write_y + 12 > max_y) max_y = write_y + 12;
  99. const char* quote = draw_quotes ? "&quot;" : "";
  100. if (extra_needed_chars) {
  101. fprintf(f, draw_text_template, write_x, write_y+12, color, quote, new_text, quote);
  102. free(new_text);
  103. } else {
  104. fprintf(f, draw_text_template, write_x, write_y+12, color, quote, text, quote, color);
  105. }
  106. // write_x += text_width;
  107. return {
  108. write_x - text_width,
  109. write_y,
  110. text_width,
  111. 12
  112. };
  113. };
  114. proc draw_integer = [&](int number) -> Drawn_Area {
  115. int text_width = 12 * ((int)log10(number)+1);
  116. if (write_x + text_width > max_x) max_x = write_x + text_width;
  117. if (write_y > max_y) max_y = write_y;
  118. fprintf(f, draw_integer_template, write_x, write_y+12, number);
  119. return {
  120. write_x,
  121. write_y,
  122. text_width,
  123. 12
  124. };
  125. };
  126. proc draw_float = [&](float number) -> Drawn_Area {
  127. int text_width = 12 * 12;
  128. if (write_x + text_width > max_x) max_x = write_x + text_width;
  129. if (write_y > max_y) max_y = write_y;
  130. fprintf(f, draw_float_template, write_x, write_y+12, number);
  131. return {
  132. write_x,
  133. write_y,
  134. text_width,
  135. 12
  136. };
  137. };
  138. std::function<Drawn_Area(Lisp_Object*)> draw_pair;
  139. proc draw_lisp_object = [&](Lisp_Object* obj) -> Drawn_Area {
  140. switch (Memory::get_type(obj)) {
  141. case Lisp_Object_Type::T: return draw_text("t");
  142. case Lisp_Object_Type::Nil: return draw_text("()");
  143. case Lisp_Object_Type::Pair: return draw_pair(obj);
  144. case Lisp_Object_Type::Number: return draw_float(obj->value.number);
  145. case Lisp_Object_Type::Symbol: return draw_text(&obj->value.string->data);
  146. case Lisp_Object_Type::Keyword: {
  147. Drawn_Area colon = draw_text(":", "#c61b6e");
  148. write_x += colon.width;
  149. Drawn_Area text = draw_text(&obj->value.identifier->data, "#c61b6e");
  150. write_x -= colon.width;
  151. return {
  152. colon.x,
  153. colon.y,
  154. colon.width + text.width,
  155. colon.height
  156. };
  157. }
  158. case Lisp_Object_Type::String: return draw_text(&obj->value.string->data, "#2aa198", true, 20);
  159. default: return {0};
  160. }
  161. };
  162. draw_pair = [&](Lisp_Object* pair) -> Drawn_Area {
  163. Drawn_Area ret;
  164. Drawn_Area child;
  165. ret.x = write_x;
  166. ret.y = write_y;
  167. ret.width = 100;
  168. ret.height = 100;
  169. fprintf(f,
  170. " <rect x='%d' y='%d' width='100' height='50' stroke-width='3' stroke='black' fill='white'/>"
  171. " <line x1='%d' y1='%d' x2='%d' y2='%d' stroke-width='3' stroke='black'/>",
  172. write_x, write_y, write_x+50, write_y, write_x+50, write_y+50);
  173. // arrow to first
  174. fprintf(f,
  175. " <line x1='%d' y1='%d' x2='%d' y2='%d' stroke-width='3' stroke='black'/>",
  176. write_x+25, write_y+25, write_x+25, write_y+100);
  177. write_y += 110;
  178. child = draw_lisp_object(pair->value.pair.first);
  179. if (ret.width < child.width)
  180. ret.width = child.width;
  181. if (ret.height < child.height)
  182. ret.height = child.height;
  183. write_y -= 110;
  184. if (pair->value.pair.rest == Memory::nil) {
  185. fprintf(f,
  186. " <line x1='%d' y1='%d' x2='%d' y2='%d' stroke-width='3' stroke='black'/>",
  187. write_x+50, write_y+50, write_x+100, write_y);
  188. } else {
  189. // arrow to rest
  190. int x_offset = 150;
  191. if (child.width+margin > x_offset)
  192. x_offset = child.width+margin;
  193. fprintf(f,
  194. " <line x1='%d' y1='%d' x2='%d' y2='%d' stroke-width='3' stroke='black'/>",
  195. write_x+75, write_y+25, write_x+75+x_offset, write_y+25);
  196. write_x += x_offset;
  197. ret.width += 50;
  198. child = draw_lisp_object(pair->value.pair.rest);
  199. ret.width += child.width;
  200. if (ret.height < 70 + child.height)
  201. ret.height = 70 + child.height;
  202. write_x -= x_offset;
  203. }
  204. fprintf(f, "\n");
  205. if (max_x < ret.x + ret.width)
  206. max_x = ret.x + ret.width;
  207. if (max_y < ret.y + ret.height)
  208. max_y = ret.y + ret.height;
  209. return ret;
  210. };
  211. proc draw_header = [&]() {
  212. proc draw_separator = [&]() {
  213. draw_margin();
  214. draw_text("|");
  215. draw_margin();
  216. };
  217. time_t t = time(NULL);
  218. struct tm tm = *localtime(&t);
  219. write_y = 12;
  220. // -------------------
  221. // Date
  222. // -------------------
  223. char date[12];
  224. snprintf(date, 12, "%02d.%02d.%d", tm.tm_mday, tm.tm_mon + 1, tm.tm_year + 1900);
  225. write_x += draw_text("Date: ").width;
  226. write_x += draw_text(date).width;
  227. draw_separator();
  228. // -------------------
  229. // Time
  230. // -------------------
  231. char time[12];
  232. snprintf(time, 12, "%02d:%02d:%02d", tm.tm_hour, tm.tm_min, tm.tm_sec);
  233. write_x += draw_text("Time: ").width;
  234. write_x += draw_text(time).width;
  235. draw_separator();
  236. // -------------------
  237. // String Memory
  238. // -------------------
  239. draw_new_line();
  240. int free_string_memory = Memory::next_free_spot_in_string_memory - Memory::string_memory;
  241. for (int i = 0; i < Memory::free_spots_in_string_memory->next_index; ++i) {
  242. free_string_memory += ((String*)(Memory::free_spots_in_string_memory->data[i]))->length;
  243. }
  244. int used_string_memory = Memory::string_memory_size - free_string_memory;
  245. write_x += draw_text("String Memory:").width;
  246. draw_margin();
  247. write_x += draw_text("[allocated chars] ").width;
  248. write_x += draw_integer(Memory::string_memory_size).width;
  249. draw_margin();
  250. write_x += draw_text("[free] ").width;
  251. write_x += draw_integer(free_string_memory).width;
  252. draw_margin();
  253. write_x += draw_text("[used] ").width;
  254. write_x += draw_integer(used_string_memory).width;
  255. draw_margin();
  256. write_x += draw_text("[%free] ").width;
  257. write_x += draw_float(100.0f * free_string_memory / Memory::string_memory_size).width;
  258. draw_margin();
  259. write_x += draw_text("[%used] ").width;
  260. write_x += draw_float(100.0f * used_string_memory / Memory::string_memory_size).width;
  261. draw_separator();
  262. draw_new_line();
  263. // -------------------
  264. // Object Memory
  265. // -------------------
  266. int free_object_memory_cells = Memory::object_memory_size - (Memory::next_index_in_object_memory - Memory::free_spots_in_object_memory->next_index);
  267. int used_object_memory_cells = Memory::next_index_in_object_memory - Memory::free_spots_in_object_memory->next_index;
  268. write_x += draw_text("Object Memory:").width;
  269. draw_margin();
  270. write_x += draw_text("[#allocated] ").width;
  271. write_x += draw_integer(Memory::object_memory_size).width;
  272. draw_margin();
  273. write_x += draw_text("[#free] ").width;
  274. write_x += draw_integer(free_object_memory_cells).width;
  275. draw_margin();
  276. write_x += draw_text("[#used] ").width;
  277. write_x += draw_integer(used_object_memory_cells).width;
  278. draw_margin();
  279. write_x += draw_text("[%free] ").width;
  280. write_x += draw_float(100.0f * free_object_memory_cells / Memory::object_memory_size).width;
  281. draw_margin();
  282. write_x += draw_text("[%used] ").width;
  283. write_x += draw_float(100.0f * used_object_memory_cells / Memory::object_memory_size).width;
  284. draw_separator();
  285. draw_new_line(3);
  286. };
  287. proc draw_symbols_keywords_and_numbers = [&]() {
  288. Lisp_Object_Array_List* symbols = create_Lisp_Object_array_list();
  289. Lisp_Object_Array_List* keywords = create_Lisp_Object_array_list();
  290. Lisp_Object_Array_List* numbers = create_Lisp_Object_array_list();
  291. Lisp_Object_Array_List* strings = create_Lisp_Object_array_list();
  292. Lisp_Object_Array_List* pairs = create_Lisp_Object_array_list();
  293. Lisp_Object_Array_List* lists = create_Lisp_Object_array_list();
  294. // loop over all used memory
  295. for (int i = 0; i < Memory::next_index_in_object_memory; ++i) {
  296. for (int j = 0; j < Memory::free_spots_in_object_memory->next_index; ++j) {
  297. if (i == Memory::free_spots_in_object_memory->data[j])
  298. goto next;
  299. }
  300. switch (Memory::get_type(Memory::object_memory+i)) {
  301. case Lisp_Object_Type::Symbol: append_to_array_list(symbols, Memory::object_memory+i); break;
  302. case Lisp_Object_Type::String: append_to_array_list(strings, Memory::object_memory+i); break;
  303. case Lisp_Object_Type::Keyword: append_to_array_list(keywords, Memory::object_memory+i); break;
  304. case Lisp_Object_Type::Number : append_to_array_list(numbers, Memory::object_memory+i); break;
  305. case Lisp_Object_Type::Pair : append_to_array_list(pairs, Memory::object_memory+i); break;
  306. default: break;
  307. }
  308. next: ;
  309. }
  310. // create the lists-list by filtering the pairs-list.
  311. Lisp_Object_Array_List* pairs_to_filter = create_Lisp_Object_array_list();
  312. Int_Array_List* indices_to_filter = create_Int_array_list();
  313. // helper lambda:
  314. proc remove_doubles_from_lisp_object_array_list = [&](Lisp_Object_Array_List* list) -> void {
  315. if (list->next_index == 0)
  316. return;
  317. sort_array_list(list);
  318. Int_Array_List* indices_to_filter = create_Int_array_list();
  319. size_t last = (size_t)list->data[0];
  320. for (int i = 1; i < list->next_index; ++i) {
  321. if ((size_t)list->data[i] == last)
  322. append_to_array_list(indices_to_filter, i);
  323. else
  324. last = (size_t)list->data[i];
  325. }
  326. for (int i = indices_to_filter->next_index; i >= 0; --i) {
  327. remove_index_from_array_list(list, indices_to_filter->data[i]);
  328. }
  329. // sort again as removing items destroys the order
  330. sort_array_list(list);
  331. };
  332. // recursive lambda
  333. std::function<void(Lisp_Object*)> filter_pair_and_children;
  334. filter_pair_and_children = [&](Lisp_Object* pair) {
  335. append_to_array_list(pairs_to_filter, pair);
  336. if (Memory::get_type(pair->value.pair.first) == Lisp_Object_Type::Pair)
  337. filter_pair_and_children(pair->value.pair.first);
  338. if (Memory::get_type(pair->value.pair.rest) == Lisp_Object_Type::Pair)
  339. filter_pair_and_children(pair->value.pair.rest);
  340. };
  341. for (int i = 0; i < pairs->next_index; ++i) {
  342. if (Memory::get_type(pairs->data[i]->value.pair.first) == Lisp_Object_Type::Pair)
  343. filter_pair_and_children(pairs->data[i]->value.pair.first);
  344. if (Memory::get_type(pairs->data[i]->value.pair.rest) == Lisp_Object_Type::Pair)
  345. filter_pair_and_children(pairs->data[i]->value.pair.rest);
  346. }
  347. remove_doubles_from_lisp_object_array_list(pairs_to_filter);
  348. fprintf(stderr, "removing %d pairs\n", pairs_to_filter->next_index);
  349. // okay, so pairs_to_filter now only the pairs once each that
  350. // we want to filter from the pairs list
  351. for (int i = 0; i < pairs->next_index; ++i) {
  352. if (sorted_array_list_find(pairs_to_filter, pairs->data[i]) == -1) {
  353. append_to_array_list(lists, pairs->data[i]);
  354. }
  355. }
  356. draw_text("Memory Contents:");
  357. draw_new_line();
  358. draw_new_line();
  359. int start_x = write_x,
  360. start_y = write_y;
  361. write_x += draw_text("Symbols: ").width;
  362. draw_integer(symbols->next_index);
  363. draw_new_line();
  364. write_x = start_x;
  365. for (int i = 0; i < symbols->next_index; ++i) {
  366. draw_new_line();
  367. write_x = start_x;
  368. draw_text(&symbols->data[i]->value.identifier->data);
  369. }
  370. write_x = start_x + 300;
  371. write_y = start_y;
  372. write_x += draw_text("Keywords: ").width;
  373. draw_integer(keywords->next_index);
  374. draw_new_line();
  375. write_x = start_x + 300;
  376. for (int i = 0; i < keywords->next_index; ++i) {
  377. draw_new_line();
  378. write_x = start_x + 300;
  379. draw_lisp_object(keywords->data[i]);
  380. }
  381. write_x = start_x + 600;
  382. write_y = start_y;
  383. write_x += draw_text("Numbers: ").width;
  384. draw_integer(numbers->next_index);
  385. draw_new_line();
  386. write_x = start_x + 600;
  387. for (int i = 0; i < numbers->next_index; ++i) {
  388. draw_new_line();
  389. write_x = start_x + 600;
  390. draw_float(numbers->data[i]->value.number);
  391. }
  392. write_x = start_x + 900;
  393. write_y = start_y;
  394. write_x += draw_text("Strings: ").width;
  395. draw_integer(strings->next_index);
  396. draw_new_line();
  397. write_x = start_x + 900;
  398. for (int i = 0; i < strings->next_index; ++i) {
  399. draw_new_line();
  400. write_x = start_x + 900;
  401. draw_text(&strings->data[i]->value.string->data, "#2aa198", true, 75);
  402. }
  403. write_x = start_x + 2000;
  404. write_y = start_y;
  405. write_x += draw_text("Lists, Pairs: ").width;
  406. write_x += draw_integer(lists->next_index).width;
  407. draw_margin();
  408. draw_integer(pairs->next_index);
  409. draw_new_line();
  410. write_x = start_x + 2000;
  411. for (int i = 0; i < lists->next_index; ++i) {
  412. draw_new_line(3);
  413. write_x = start_x + 2000;
  414. write_y += draw_pair(lists->data[i]).height;
  415. }
  416. };
  417. fprintf(f,
  418. "<?xml version='1.0' encoding='UTF-8'?>\n"
  419. "<svg xmlns='http://www.w3.org/2000/svg'\n"
  420. " version='1.1' baseProfile='full'\n"
  421. " viewBox='%012d %012d %012d %012d'"
  422. ">\n\n", -padding, -padding, 0, 0);
  423. draw_header();
  424. draw_symbols_keywords_and_numbers();
  425. // draw_text("DoEun", "#00aaaa", true);
  426. fprintf(f, "\n\n</svg>");
  427. // fill in the correct viewBox
  428. rewind(f);
  429. fprintf(f,
  430. "<?xml version='1.0' encoding='UTF-8'?>\n"
  431. "<svg xmlns='http://www.w3.org/2000/svg'\n"
  432. " version='1.1' baseProfile='full'\n"
  433. " viewBox='%012d %012d %012d %012d'"
  434. ">", -padding, -padding, max_x + 2*padding, max_y + 2*padding);
  435. }