#include "writer.hpp" Result Writer::write_one(Value& obj) { switch (obj.tag()) { case Tag::Nil: return write_nil(*obj.to()); case Tag::Int64: return write_int64(*obj.to()); case Tag::Float: return write_float(*obj.to()); case Tag::Bool: return write_bool(*obj.to()); case Tag::Array: return write_array(*obj.to()); case Tag::ByteArray: return write_bytearray(*obj.to()); case Tag::Dict: return write_dict(*obj.to()); case Tag::String: return write_string(*obj.to()); case Tag::Symbol: return write_symbol(*obj.to()); case Tag::Syntax: return write_syntax(*obj.to()); case Tag::Pair: return write_pair(*obj.to()); }; return String(); } Result Writer::write_int64(Int64& val) { char tmp[32]; sprintf(tmp, "%lu", val.value()); size_t len = strlen(tmp); return String::create(_arena, tmp); } bool needs_dot(char* str) { bool has_e = 0; bool has_dot = 0; for (char* c = str; *c != 0; c++) { if (*c == 'e' || *c == 'E') has_e = true; if (*c == '.') has_dot = true; } return !has_e && !has_dot; } Result Writer::write_float(Float& val) { char tmp[32]; sprintf(tmp, "%g", val.value()); size_t len = strlen(tmp); if (needs_dot(tmp)) { tmp[len] = '.'; tmp[len + 1] = '0'; tmp[len + 2] = 0; len += 2; } return String::create(_arena, tmp); } Result Writer::write_nil(Nil& val) { return String::create(_arena, "nil"); } Result Writer::write_bool(Bool& val) { if (val.value()) return String::create(_arena, "true"); return String::create(_arena, "false"); } Result Writer::write_bytearray(ByteArray& val) { return ErrorCode::NotImplemented; } Result Writer::write_string(String& val) { String res = TRY(String::create(_arena, "\"")); // TODO: optimize this for (uint64_t i = 0; i < val.size(); i++) { char32_t c = TRY(val[i]); const char* replace = 0; switch (c) { case '\r': replace = "\\r"; break; case '\n': replace = "\\n"; break; case '\t': replace = "\\t"; break; case '\\': replace = "\\\\"; break; case '"': replace = "\\\""; break; default: break; }; if (replace != 0) { res = TRY(res.concat(_arena, replace)); } else { res = TRY(res.concat(_arena, &c, 1)); } } res = TRY(res.concat(_arena, "\"")); return res; } int is_valid_symbol_char(char32_t codepoint) { return ('a' <= codepoint && codepoint <= 'z') || ('A' <= codepoint && codepoint <= 'Z') || ('0' <= codepoint && codepoint <= '9') || codepoint == '!' || codepoint == '$' || codepoint == '%' || codepoint == '&' || codepoint == '*' || codepoint == '+' || codepoint == '-' || codepoint == '.' || codepoint == '/' || codepoint == ':' || codepoint == '<' || codepoint == '=' || codepoint == '>' || codepoint == '?' || codepoint == '@' || codepoint == '^' || codepoint == '_' || codepoint == '~'; } Result Writer::write_symbol(Symbol& val) { String res = TRY(String::create(_arena, "")); // TODO: optimize this for (uint64_t i = 0; i < val.size(); i++) { char32_t c = TRY(val[i]); if (!is_valid_symbol_char(c)) return ErrorCode::InvalidSymbol; res = TRY(res.concat(_arena, &c, 1)); } return res; } Result Writer::write_syntax(Syntax& val) { return ErrorCode::NotImplemented; } Result Writer::write_pair(Pair& val) { String res = TRY(String::create(_arena, "(")); Value cur = TRY(val.copy(_arena)); bool is_first = true; while (!cur.is()) { if (!cur.is()) return ErrorCode::MalformedList; Pair& pair = *cur.to(); Value first = TRY(pair.first(_arena)); if (!is_first) res = TRY(res.concat(_arena, " ")); String first_str = TRY(write_one(first)); res = TRY(res.concat(_arena, first_str)); cur = TRY(pair.rest(_arena)); is_first = false; } res = TRY(res.concat(_arena, ")")); return res; } Result Writer::write_array(Array& val) { String res = TRY(String::create(_arena, "[")); bool is_first = true; for (uint64_t i = 0; i < val.size(); i++) { Value cur = TRY(val.get(_arena, i)); if (!is_first) res = TRY(res.concat(_arena, " ")); String first_str = TRY(write_one(cur)); res = TRY(res.concat(_arena, first_str)); is_first = false; } res = TRY(res.concat(_arena, "]")); return res; } Result Writer::write_dict(Dict& val) { String res = TRY(String::create(_arena, "{")); bool is_first = true; for (uint64_t i = 0; i < val.size(); i++) { Value k = TRY(Value::create(_arena, val._value->data[i * 2].get())); Value v = TRY(Value::create(_arena, val._value->data[i * 2 + 1].get())); if (!is_first) res = TRY(res.concat(_arena, " ")); String first_str = TRY(write_one(k)); res = TRY(res.concat(_arena, first_str)); res = TRY(res.concat(_arena, " ")); String second_str = TRY(write_one(v)); res = TRY(res.concat(_arena, second_str)); is_first = false; } res = TRY(res.concat(_arena, "}")); return res; } Result write_one(Arena& arena, Value& value) { auto w = Writer(arena); return w.write_one(value); }