|
- proc visualize_lisp_machine() -> void {
- struct Drawn_Area {
- int x;
- int y;
- int width;
- int height;
- };
-
- fprintf(stderr, "Drawing visualization...");
- defer {
- fprintf(stderr, "Done!\n");
- };
-
- const int padding = 40;
- const int margin = 20;
-
- 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";
- const char* draw_integer_template = " <text x='%d' y='%d' font-size='20' font-family='monospace'>\n %d\n </text>\n";
- const char* draw_float_template = " <text x='%d' y='%d' font-size='20' font-family='monospace'>\n %012.6f\n </text>\n";
-
-
- FILE *f = fopen("visualization.svg", "w");
- defer {
- fclose(f);
- };
-
- if (f == NULL) {
- create_generic_error("The file for writing the visualization"
- "could not be opened for writing");
- return;
- }
-
- int max_x = 0,
- max_y = 0,
- write_x = 0,
- write_y = 0;
-
-
- proc draw_margin = [&](int count = 1) -> Drawn_Area {
- write_x += margin * count;
- return {
- write_x - margin * count,
- write_y,
- margin * count,
- write_y
- };
- };
- proc draw_new_line = [&](int count = 1) {
- write_x = 0;
- write_y += 25 * count;
- };
- proc draw_text = [&](const char* text, const char* color = "#000000", bool draw_quotes = false, int max_length = 200) -> Drawn_Area {
- // take care of escaping sensitive chars
- int text_length = 0;
- int extra_needed_chars = draw_quotes ? 10 : 0;
- char* new_text = nullptr;
- char char_at_max_length = 0;
-
- char source;
- while ((source = text[text_length++]) != '\0') {
- switch (source) {
- case '\n':
- extra_needed_chars += 1;
- case '<':
- case '>':
- extra_needed_chars += 3;
- break;
- case '&':
- extra_needed_chars += 4;
- break;
- case '\'':
- case '"':
- extra_needed_chars += 5;
- }
- }
- // last char was \0 but we don't count it
- --text_length;
-
- if (text_length > max_length) {
- char_at_max_length = ((char*)text)[max_length];
- ((char*)text)[max_length] = '\0';
- text_length = max_length;
- }
- defer {
- if (char_at_max_length)
- ((char*)text)[max_length] = char_at_max_length;
- };
-
- // if we need to replace some chars
- if (extra_needed_chars > 0) {
- new_text = (char*)malloc((text_length + extra_needed_chars) * sizeof(char));
-
- int index_in_text = 0,
- index_in_new_text = 0;
-
- char source;
- while ((source = text[index_in_text++]) != '\0') {
- switch (source) {
- case '\n': new_text[index_in_new_text++] = '\\'; new_text[index_in_new_text++] = 'n'; break;
- 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;
- 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;
- 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;
- 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;
- 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;
- default: new_text[index_in_new_text++] = source;
- }
- }
- new_text[index_in_new_text] = '\0';
- }
-
- int text_width = 12 * (text_length + (draw_quotes ? 2 : 0));
- if (write_x + text_width > max_x) max_x = write_x + text_width;
- if (write_y + 12 > max_y) max_y = write_y + 12;
-
- const char* quote = draw_quotes ? """ : "";
- if (extra_needed_chars) {
- fprintf(f, draw_text_template, write_x, write_y+12, color, quote, new_text, quote);
- free(new_text);
- } else {
- fprintf(f, draw_text_template, write_x, write_y+12, color, quote, text, quote, color);
- }
-
- // write_x += text_width;
-
- return {
- write_x - text_width,
- write_y,
- text_width,
- 12
- };
- };
- proc draw_integer = [&](int number) -> Drawn_Area {
- int text_width = 12 * ((int)log10(number)+1);
-
- if (write_x + text_width > max_x) max_x = write_x + text_width;
- if (write_y > max_y) max_y = write_y;
-
- fprintf(f, draw_integer_template, write_x, write_y+12, number);
-
- return {
- write_x,
- write_y,
- text_width,
- 12
- };
- };
- proc draw_float = [&](float number) -> Drawn_Area {
- int text_width = 12 * 12;
-
- if (write_x + text_width > max_x) max_x = write_x + text_width;
- if (write_y > max_y) max_y = write_y;
-
- fprintf(f, draw_float_template, write_x, write_y+12, number);
-
- return {
- write_x,
- write_y,
- text_width,
- 12
- };
- };
- std::function<Drawn_Area(Lisp_Object*)> draw_pair;
- proc draw_lisp_object = [&](Lisp_Object* obj) -> Drawn_Area {
- switch (Memory::get_type(obj)) {
- case Lisp_Object_Type::T: return draw_text("t");
- case Lisp_Object_Type::Nil: return draw_text("()");
- case Lisp_Object_Type::Pair: return draw_pair(obj);
- case Lisp_Object_Type::Number: return draw_float(obj->value.number);
- case Lisp_Object_Type::Symbol: return draw_text(&obj->value.string->data);
- case Lisp_Object_Type::Keyword: {
- Drawn_Area colon = draw_text(":", "#c61b6e");
- write_x += colon.width;
- Drawn_Area text = draw_text(&obj->value.identifier->data, "#c61b6e");
- write_x -= colon.width;
- return {
- colon.x,
- colon.y,
- colon.width + text.width,
- colon.height
- };
- }
- case Lisp_Object_Type::String: return draw_text(&obj->value.string->data, "#2aa198", true, 20);
- default: return {0};
- }
- };
- draw_pair = [&](Lisp_Object* pair) -> Drawn_Area {
- Drawn_Area ret;
- Drawn_Area child;
-
- ret.x = write_x;
- ret.y = write_y;
- ret.width = 100;
- ret.height = 100;
-
- fprintf(f,
- " <rect x='%d' y='%d' width='100' height='50' stroke-width='3' stroke='black' fill='white'/>"
- " <line x1='%d' y1='%d' x2='%d' y2='%d' stroke-width='3' stroke='black'/>",
- write_x, write_y, write_x+50, write_y, write_x+50, write_y+50);
-
- // arrow to first
- fprintf(f,
- " <line x1='%d' y1='%d' x2='%d' y2='%d' stroke-width='3' stroke='black'/>",
- write_x+25, write_y+25, write_x+25, write_y+100);
-
- write_y += 110;
- child = draw_lisp_object(pair->value.pair.first);
- if (ret.width < child.width)
- ret.width = child.width;
- if (ret.height < child.height)
- ret.height = child.height;
-
- write_y -= 110;
-
- if (pair->value.pair.rest == Memory::nil) {
- fprintf(f,
- " <line x1='%d' y1='%d' x2='%d' y2='%d' stroke-width='3' stroke='black'/>",
- write_x+50, write_y+50, write_x+100, write_y);
- } else {
- // arrow to rest
- int x_offset = 150;
- if (child.width+margin > x_offset)
- x_offset = child.width+margin;
-
- fprintf(f,
- " <line x1='%d' y1='%d' x2='%d' y2='%d' stroke-width='3' stroke='black'/>",
- write_x+75, write_y+25, write_x+75+x_offset, write_y+25);
-
- write_x += x_offset;
- ret.width += 50;
-
- child = draw_lisp_object(pair->value.pair.rest);
- ret.width += child.width;
- if (ret.height < 70 + child.height)
- ret.height = 70 + child.height;
-
- write_x -= x_offset;
- }
-
- fprintf(f, "\n");
-
- if (max_x < ret.x + ret.width)
- max_x = ret.x + ret.width;
- if (max_y < ret.y + ret.height)
- max_y = ret.y + ret.height;
-
- return ret;
- };
- proc draw_header = [&]() {
- proc draw_separator = [&]() {
- draw_margin();
- draw_text("|");
- draw_margin();
- };
-
- time_t t = time(NULL);
- struct tm tm = *localtime(&t);
-
- write_y = 12;
-
- // -------------------
- // Date
- // -------------------
- char date[12];
- snprintf(date, 12, "%02d.%02d.%d", tm.tm_mday, tm.tm_mon + 1, tm.tm_year + 1900);
-
- write_x += draw_text("Date: ").width;
- write_x += draw_text(date).width;
-
- draw_separator();
-
- // -------------------
- // Time
- // -------------------
- char time[12];
- snprintf(time, 12, "%02d:%02d:%02d", tm.tm_hour, tm.tm_min, tm.tm_sec);
-
- write_x += draw_text("Time: ").width;
- write_x += draw_text(time).width;
-
- draw_separator();
-
- // -------------------
- // String Memory
- // -------------------
- draw_new_line();
-
- int free_string_memory = Memory::next_free_spot_in_string_memory - Memory::string_memory;
- for (int i = 0; i < Memory::free_spots_in_string_memory->next_index; ++i) {
- free_string_memory += ((String*)(Memory::free_spots_in_string_memory->data[i]))->length;
- }
- int used_string_memory = Memory::string_memory_size - free_string_memory;
-
- write_x += draw_text("String Memory:").width;
- draw_margin();
- write_x += draw_text("[allocated chars] ").width;
- write_x += draw_integer(Memory::string_memory_size).width;
- draw_margin();
- write_x += draw_text("[free] ").width;
- write_x += draw_integer(free_string_memory).width;
- draw_margin();
- write_x += draw_text("[used] ").width;
- write_x += draw_integer(used_string_memory).width;
- draw_margin();
- write_x += draw_text("[%free] ").width;
- write_x += draw_float(100.0f * free_string_memory / Memory::string_memory_size).width;
- draw_margin();
- write_x += draw_text("[%used] ").width;
- write_x += draw_float(100.0f * used_string_memory / Memory::string_memory_size).width;
-
- draw_separator();
- draw_new_line();
-
- // -------------------
- // Object Memory
- // -------------------
-
- int free_object_memory_cells = Memory::object_memory_size - (Memory::next_index_in_object_memory - Memory::free_spots_in_object_memory->next_index);
- int used_object_memory_cells = Memory::next_index_in_object_memory - Memory::free_spots_in_object_memory->next_index;
-
- write_x += draw_text("Object Memory:").width;
- draw_margin();
- write_x += draw_text("[#allocated] ").width;
- write_x += draw_integer(Memory::object_memory_size).width;
- draw_margin();
- write_x += draw_text("[#free] ").width;
- write_x += draw_integer(free_object_memory_cells).width;
- draw_margin();
- write_x += draw_text("[#used] ").width;
- write_x += draw_integer(used_object_memory_cells).width;
- draw_margin();
- write_x += draw_text("[%free] ").width;
- write_x += draw_float(100.0f * free_object_memory_cells / Memory::object_memory_size).width;
- draw_margin();
- write_x += draw_text("[%used] ").width;
- write_x += draw_float(100.0f * used_object_memory_cells / Memory::object_memory_size).width;
-
- draw_separator();
-
- draw_new_line(3);
- };
- proc draw_symbols_keywords_and_numbers = [&]() {
- Lisp_Object_Array_List* symbols = create_Lisp_Object_array_list();
- Lisp_Object_Array_List* keywords = create_Lisp_Object_array_list();
- Lisp_Object_Array_List* numbers = create_Lisp_Object_array_list();
- Lisp_Object_Array_List* strings = create_Lisp_Object_array_list();
- Lisp_Object_Array_List* pairs = create_Lisp_Object_array_list();
- Lisp_Object_Array_List* lists = create_Lisp_Object_array_list();
-
- // loop over all used memory
- for (int i = 0; i < Memory::next_index_in_object_memory; ++i) {
- for (int j = 0; j < Memory::free_spots_in_object_memory->next_index; ++j) {
- if (i == Memory::free_spots_in_object_memory->data[j])
- goto next;
- }
-
- switch (Memory::get_type(Memory::object_memory+i)) {
- case Lisp_Object_Type::Symbol: append_to_array_list(symbols, Memory::object_memory+i); break;
- case Lisp_Object_Type::String: append_to_array_list(strings, Memory::object_memory+i); break;
- case Lisp_Object_Type::Keyword: append_to_array_list(keywords, Memory::object_memory+i); break;
- case Lisp_Object_Type::Number : append_to_array_list(numbers, Memory::object_memory+i); break;
- case Lisp_Object_Type::Pair : append_to_array_list(pairs, Memory::object_memory+i); break;
- default: break;
- }
-
- next: ;
- }
-
- // create the lists-list by filtering the pairs-list.
- Lisp_Object_Array_List* pairs_to_filter = create_Lisp_Object_array_list();
- Int_Array_List* indices_to_filter = create_Int_array_list();
-
- // helper lambda:
- proc remove_doubles_from_lisp_object_array_list = [&](Lisp_Object_Array_List* list) -> void {
- if (list->next_index == 0)
- return;
-
- sort_array_list(list);
- Int_Array_List* indices_to_filter = create_Int_array_list();
-
- size_t last = (size_t)list->data[0];
- for (int i = 1; i < list->next_index; ++i) {
- if ((size_t)list->data[i] == last)
- append_to_array_list(indices_to_filter, i);
- else
- last = (size_t)list->data[i];
- }
-
- for (int i = indices_to_filter->next_index; i >= 0; --i) {
- remove_index_from_array_list(list, indices_to_filter->data[i]);
- }
-
- // sort again as removing items destroys the order
- sort_array_list(list);
- };
-
- // recursive lambda
- std::function<void(Lisp_Object*)> filter_pair_and_children;
- filter_pair_and_children = [&](Lisp_Object* pair) {
- append_to_array_list(pairs_to_filter, pair);
-
- if (Memory::get_type(pair->value.pair.first) == Lisp_Object_Type::Pair)
- filter_pair_and_children(pair->value.pair.first);
-
- if (Memory::get_type(pair->value.pair.rest) == Lisp_Object_Type::Pair)
- filter_pair_and_children(pair->value.pair.rest);
- };
- for (int i = 0; i < pairs->next_index; ++i) {
- if (Memory::get_type(pairs->data[i]->value.pair.first) == Lisp_Object_Type::Pair)
- filter_pair_and_children(pairs->data[i]->value.pair.first);
-
- if (Memory::get_type(pairs->data[i]->value.pair.rest) == Lisp_Object_Type::Pair)
- filter_pair_and_children(pairs->data[i]->value.pair.rest);
-
- }
-
- remove_doubles_from_lisp_object_array_list(pairs_to_filter);
- // fprintf(stderr, "removing %d pairs\n", pairs_to_filter->next_index);
- // okay, so pairs_to_filter now only the pairs once each that
- // we want to filter from the pairs list
- for (int i = 0; i < pairs->next_index; ++i) {
- if (sorted_array_list_find(pairs_to_filter, pairs->data[i]) == -1) {
- append_to_array_list(lists, pairs->data[i]);
- }
- }
-
- draw_text("Memory Contents:");
- draw_new_line();
- draw_new_line();
-
- int start_x = write_x,
- start_y = write_y;
-
- write_x += draw_text("Symbols: ").width;
- draw_integer(symbols->next_index);
- draw_new_line();
- write_x = start_x;
-
- for (int i = 0; i < symbols->next_index; ++i) {
- draw_new_line();
- write_x = start_x;
-
- draw_text(&symbols->data[i]->value.identifier->data);
- }
-
-
- write_x = start_x + 300;
- write_y = start_y;
-
- write_x += draw_text("Keywords: ").width;
- draw_integer(keywords->next_index);
- draw_new_line();
- write_x = start_x + 300;
-
- for (int i = 0; i < keywords->next_index; ++i) {
- draw_new_line();
- write_x = start_x + 300;
-
- draw_lisp_object(keywords->data[i]);
- }
-
-
-
- write_x = start_x + 600;
- write_y = start_y;
-
- write_x += draw_text("Numbers: ").width;
- draw_integer(numbers->next_index);
- draw_new_line();
- write_x = start_x + 600;
-
- for (int i = 0; i < numbers->next_index; ++i) {
- draw_new_line();
- write_x = start_x + 600;
-
- draw_float(numbers->data[i]->value.number);
- }
-
- write_x = start_x + 900;
- write_y = start_y;
-
- write_x += draw_text("Strings: ").width;
- draw_integer(strings->next_index);
- draw_new_line();
- write_x = start_x + 900;
-
- for (int i = 0; i < strings->next_index; ++i) {
- draw_new_line();
- write_x = start_x + 900;
-
- draw_text(&strings->data[i]->value.string->data, "#2aa198", true, 75);
- }
-
-
- write_x = start_x + 2000;
- write_y = start_y;
-
- write_x += draw_text("Lists, Pairs: ").width;
- write_x += draw_integer(lists->next_index).width;
- draw_margin();
- draw_integer(pairs->next_index);
- draw_new_line();
- write_x = start_x + 2000;
-
- for (int i = 0; i < lists->next_index; ++i) {
- draw_new_line(3);
- write_x = start_x + 2000;
-
- write_y += draw_pair(lists->data[i]).height;
- }
- };
-
- fprintf(f,
- "<?xml version='1.0' encoding='UTF-8'?>\n"
- "<svg xmlns='http://www.w3.org/2000/svg'\n"
- " version='1.1' baseProfile='full'\n"
- " viewBox='%012d %012d %012d %012d'"
- ">\n\n", -padding, -padding, 0, 0);
-
- draw_header();
- draw_symbols_keywords_and_numbers();
- // draw_text("DoEun", "#00aaaa", true);
-
- fprintf(f, "\n\n</svg>");
-
- // fill in the correct viewBox
- rewind(f);
-
- fprintf(f,
- "<?xml version='1.0' encoding='UTF-8'?>\n"
- "<svg xmlns='http://www.w3.org/2000/svg'\n"
- " version='1.1' baseProfile='full'\n"
- " viewBox='%012d %012d %012d %012d'"
- ">", -padding, -padding, max_x + 2*padding, max_y + 2*padding);
-
- }
|