2024-07-28 16:54:04 +00:00
|
|
|
#include "writer.hpp"
|
|
|
|
|
2024-08-11 01:25:44 +00:00
|
|
|
#include "error.hpp"
|
|
|
|
#include "opcode.hpp"
|
2024-09-05 22:22:45 +00:00
|
|
|
#include "sourcerange.hpp"
|
2024-08-11 01:25:44 +00:00
|
|
|
|
2024-08-11 20:37:37 +00:00
|
|
|
Result<String> Writer::write_one(const Value& obj) {
|
2024-07-28 16:54:04 +00:00
|
|
|
switch (obj.tag()) {
|
|
|
|
case Tag::Nil:
|
|
|
|
return write_nil(*obj.to<Nil>());
|
|
|
|
case Tag::Int64:
|
|
|
|
return write_int64(*obj.to<Int64>());
|
|
|
|
case Tag::Float:
|
|
|
|
return write_float(*obj.to<Float>());
|
|
|
|
case Tag::Bool:
|
|
|
|
return write_bool(*obj.to<Bool>());
|
2024-07-30 22:52:23 +00:00
|
|
|
case Tag::Array:
|
|
|
|
return write_array(*obj.to<Array>());
|
2024-07-28 16:54:04 +00:00
|
|
|
case Tag::ByteArray:
|
|
|
|
return write_bytearray(*obj.to<ByteArray>());
|
2024-08-01 17:56:38 +00:00
|
|
|
case Tag::Dict:
|
|
|
|
return write_dict(*obj.to<Dict>());
|
2024-07-28 16:54:04 +00:00
|
|
|
case Tag::String:
|
|
|
|
return write_string(*obj.to<String>());
|
|
|
|
case Tag::Symbol:
|
|
|
|
return write_symbol(*obj.to<Symbol>());
|
2024-09-05 22:22:45 +00:00
|
|
|
case Tag::SrcLoc:
|
|
|
|
return write_srcloc(*obj.to<SrcLoc>());
|
2024-07-28 16:54:04 +00:00
|
|
|
case Tag::Syntax:
|
|
|
|
return write_syntax(*obj.to<Syntax>());
|
|
|
|
case Tag::Pair:
|
|
|
|
return write_pair(*obj.to<Pair>());
|
2024-08-10 17:24:16 +00:00
|
|
|
case Tag::Opcode:
|
|
|
|
return write_opcode(*obj.to<Opcode>());
|
2024-08-11 20:37:37 +00:00
|
|
|
case Tag::Function:
|
|
|
|
return write_function(*obj.to<Function>());
|
2024-08-24 23:55:11 +00:00
|
|
|
case Tag::StdlibFunction:
|
|
|
|
return write_stdlib_function(*obj.to<StdlibFunction>());
|
2024-08-23 23:28:29 +00:00
|
|
|
case Tag::Module:
|
|
|
|
return write_module(*obj.to<Module>());
|
2024-08-12 22:05:40 +00:00
|
|
|
case Tag::Stack:
|
|
|
|
return write_stack(*obj.to<Stack>());
|
2024-07-28 16:54:04 +00:00
|
|
|
};
|
|
|
|
return String();
|
|
|
|
}
|
|
|
|
|
2024-08-18 21:08:47 +00:00
|
|
|
Result<String> Writer::write_multiple(const Value& val) {
|
|
|
|
String res = TRY(String::create(""));
|
|
|
|
|
|
|
|
Value cur = TRY(val.copy());
|
|
|
|
|
|
|
|
bool is_first = true;
|
|
|
|
while (!cur.is<Nil>()) {
|
|
|
|
if (!cur.is<Pair>()) return ERROR(MalformedList);
|
|
|
|
|
|
|
|
Pair& pair = *cur.to<Pair>();
|
|
|
|
|
|
|
|
Value first = TRY(pair.first());
|
|
|
|
if (!is_first) res = TRY(res.concat("\n"));
|
|
|
|
|
|
|
|
String first_str = TRY(write_one(first));
|
|
|
|
res = TRY(res.concat(first_str));
|
|
|
|
|
|
|
|
cur = TRY(pair.rest());
|
|
|
|
is_first = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
res = TRY(res.concat(""));
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2024-08-11 20:37:37 +00:00
|
|
|
Result<String> Writer::write_int64(const Int64& val) {
|
2024-07-28 16:54:04 +00:00
|
|
|
char tmp[32];
|
2024-08-23 23:28:29 +00:00
|
|
|
sprintf(tmp, "%ld", val.value());
|
2024-07-28 16:54:04 +00:00
|
|
|
size_t len = strlen(tmp);
|
|
|
|
|
2024-08-09 22:45:06 +00:00
|
|
|
return String::create(tmp);
|
2024-07-28 16:54:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2024-08-11 20:37:37 +00:00
|
|
|
Result<String> Writer::write_float(const Float& val) {
|
2024-07-28 16:54:04 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2024-08-09 22:45:06 +00:00
|
|
|
return String::create(tmp);
|
2024-07-28 16:54:04 +00:00
|
|
|
}
|
|
|
|
|
2024-08-11 20:37:37 +00:00
|
|
|
Result<String> Writer::write_nil(const Nil& val) {
|
|
|
|
return String::create("nil");
|
|
|
|
}
|
2024-07-28 16:54:04 +00:00
|
|
|
|
2024-08-11 20:37:37 +00:00
|
|
|
Result<String> Writer::write_bool(const Bool& val) {
|
2024-08-09 22:45:06 +00:00
|
|
|
if (val.value()) return String::create("true");
|
2024-07-28 16:54:04 +00:00
|
|
|
|
2024-08-09 22:45:06 +00:00
|
|
|
return String::create("false");
|
2024-07-28 16:54:04 +00:00
|
|
|
}
|
|
|
|
|
2024-08-11 20:37:37 +00:00
|
|
|
Result<String> Writer::write_bytearray(const ByteArray& val) {
|
2024-08-10 10:17:20 +00:00
|
|
|
return ERROR(NotImplemented);
|
2024-07-28 16:54:04 +00:00
|
|
|
}
|
|
|
|
|
2024-08-11 20:37:37 +00:00
|
|
|
Result<String> Writer::write_string(const String& val) {
|
2024-08-09 22:45:06 +00:00
|
|
|
String res = TRY(String::create("\""));
|
2024-07-28 16:54:04 +00:00
|
|
|
|
|
|
|
// 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) {
|
2024-08-09 22:45:06 +00:00
|
|
|
res = TRY(res.concat(replace));
|
2024-07-28 16:54:04 +00:00
|
|
|
} else {
|
2024-08-09 22:45:06 +00:00
|
|
|
res = TRY(res.concat(&c, 1));
|
2024-07-28 16:54:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-09 22:45:06 +00:00
|
|
|
res = TRY(res.concat("\""));
|
2024-07-28 16:54:04 +00:00
|
|
|
|
|
|
|
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 == '~';
|
|
|
|
}
|
|
|
|
|
2024-08-11 20:37:37 +00:00
|
|
|
Result<String> Writer::write_symbol(const Symbol& val) {
|
2024-08-09 22:45:06 +00:00
|
|
|
String res = TRY(String::create(""));
|
2024-07-28 16:54:04 +00:00
|
|
|
|
|
|
|
// TODO: optimize this
|
|
|
|
for (uint64_t i = 0; i < val.size(); i++) {
|
|
|
|
char32_t c = TRY(val[i]);
|
|
|
|
|
2024-08-10 10:17:20 +00:00
|
|
|
if (!is_valid_symbol_char(c)) return ERROR(InvalidSymbol);
|
2024-07-28 16:54:04 +00:00
|
|
|
|
2024-08-09 22:45:06 +00:00
|
|
|
res = TRY(res.concat(&c, 1));
|
2024-07-28 16:54:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2024-09-05 22:22:45 +00:00
|
|
|
Result<String> Writer::write_srcloc(const SrcLoc& val) {
|
|
|
|
char buf[1024];
|
|
|
|
String res = TRY(String::create("#<srcloc "));
|
|
|
|
|
|
|
|
SourceRange sr = val.sourcerange();
|
|
|
|
snprintf(buf, 1024, "%lu:%lu %lu:%lu", sr.start.line, sr.start.column,
|
|
|
|
sr.end.line, sr.end.column);
|
|
|
|
|
|
|
|
res = TRY(res.concat(buf));
|
|
|
|
res = TRY(res.concat(">"));
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2024-08-11 20:37:37 +00:00
|
|
|
Result<String> Writer::write_syntax(const Syntax& val) {
|
2024-09-06 22:32:05 +00:00
|
|
|
return TRY(String::create("#<syntax>"));
|
2024-07-28 16:54:04 +00:00
|
|
|
}
|
|
|
|
|
2024-08-11 20:37:37 +00:00
|
|
|
Result<String> Writer::write_pair(const Pair& val) {
|
2024-08-09 22:45:06 +00:00
|
|
|
String res = TRY(String::create("("));
|
2024-07-28 16:54:04 +00:00
|
|
|
|
2024-08-13 00:11:23 +00:00
|
|
|
Value cur = TRY(val.copy_value());
|
2024-07-28 16:54:04 +00:00
|
|
|
|
|
|
|
bool is_first = true;
|
|
|
|
while (!cur.is<Nil>()) {
|
2024-08-10 10:17:20 +00:00
|
|
|
if (!cur.is<Pair>()) return ERROR(MalformedList);
|
2024-07-28 16:54:04 +00:00
|
|
|
|
|
|
|
Pair& pair = *cur.to<Pair>();
|
|
|
|
|
2024-08-09 22:45:06 +00:00
|
|
|
Value first = TRY(pair.first());
|
|
|
|
if (!is_first) res = TRY(res.concat(" "));
|
2024-07-28 16:54:04 +00:00
|
|
|
|
|
|
|
String first_str = TRY(write_one(first));
|
2024-08-09 22:45:06 +00:00
|
|
|
res = TRY(res.concat(first_str));
|
2024-07-28 16:54:04 +00:00
|
|
|
|
2024-08-09 22:45:06 +00:00
|
|
|
cur = TRY(pair.rest());
|
2024-07-28 23:14:30 +00:00
|
|
|
is_first = false;
|
2024-07-28 16:54:04 +00:00
|
|
|
}
|
|
|
|
|
2024-08-09 22:45:06 +00:00
|
|
|
res = TRY(res.concat(")"));
|
2024-07-28 16:54:04 +00:00
|
|
|
return res;
|
|
|
|
}
|
2024-07-30 22:52:23 +00:00
|
|
|
|
2024-08-11 20:37:37 +00:00
|
|
|
Result<String> Writer::write_array(const Array& val) {
|
2024-08-09 22:45:06 +00:00
|
|
|
String res = TRY(String::create("["));
|
2024-07-30 22:52:23 +00:00
|
|
|
|
|
|
|
bool is_first = true;
|
|
|
|
for (uint64_t i = 0; i < val.size(); i++) {
|
2024-08-09 22:45:06 +00:00
|
|
|
Value cur = TRY(val.get(i));
|
2024-07-30 22:52:23 +00:00
|
|
|
|
2024-08-09 22:45:06 +00:00
|
|
|
if (!is_first) res = TRY(res.concat(" "));
|
2024-07-30 22:52:23 +00:00
|
|
|
|
|
|
|
String first_str = TRY(write_one(cur));
|
2024-08-09 22:45:06 +00:00
|
|
|
res = TRY(res.concat(first_str));
|
2024-07-30 22:52:23 +00:00
|
|
|
|
|
|
|
is_first = false;
|
|
|
|
}
|
|
|
|
|
2024-08-09 22:45:06 +00:00
|
|
|
res = TRY(res.concat("]"));
|
2024-07-30 22:52:23 +00:00
|
|
|
return res;
|
|
|
|
}
|
2024-08-01 17:56:38 +00:00
|
|
|
|
2024-08-11 20:37:37 +00:00
|
|
|
Result<String> Writer::write_dict(const Dict& val) {
|
2024-08-09 22:45:06 +00:00
|
|
|
String res = TRY(String::create("{"));
|
2024-08-01 21:45:16 +00:00
|
|
|
|
|
|
|
bool is_first = true;
|
|
|
|
for (uint64_t i = 0; i < val.size(); i++) {
|
2024-08-09 22:45:06 +00:00
|
|
|
Value k = TRY(Value::create(val._value->data[i * 2].get()));
|
|
|
|
Value v = TRY(Value::create(val._value->data[i * 2 + 1].get()));
|
2024-08-01 21:45:16 +00:00
|
|
|
|
2024-08-09 22:45:06 +00:00
|
|
|
if (!is_first) res = TRY(res.concat(" "));
|
2024-08-01 21:45:16 +00:00
|
|
|
|
|
|
|
String first_str = TRY(write_one(k));
|
2024-08-09 22:45:06 +00:00
|
|
|
res = TRY(res.concat(first_str));
|
2024-08-01 21:45:16 +00:00
|
|
|
|
2024-08-09 22:45:06 +00:00
|
|
|
res = TRY(res.concat(" "));
|
2024-08-01 21:45:16 +00:00
|
|
|
|
|
|
|
String second_str = TRY(write_one(v));
|
2024-08-09 22:45:06 +00:00
|
|
|
res = TRY(res.concat(second_str));
|
2024-08-01 21:45:16 +00:00
|
|
|
|
|
|
|
is_first = false;
|
|
|
|
}
|
|
|
|
|
2024-08-09 22:45:06 +00:00
|
|
|
res = TRY(res.concat("}"));
|
2024-08-01 21:45:16 +00:00
|
|
|
return res;
|
2024-08-01 17:56:38 +00:00
|
|
|
}
|
2024-08-02 22:27:36 +00:00
|
|
|
|
2024-08-11 20:37:37 +00:00
|
|
|
Result<String> Writer::write_opcode(const Opcode& val) {
|
2024-08-11 01:25:44 +00:00
|
|
|
String res = TRY(String::create("#<opcode "));
|
|
|
|
|
|
|
|
const char* name = opcode_name(val.opcode());
|
|
|
|
OpcodeType ot = opcode_type(val.opcode());
|
|
|
|
|
|
|
|
char buf[256];
|
|
|
|
switch (ot) {
|
|
|
|
case OpcodeType::Unknown:
|
|
|
|
return ERROR(TypeMismatch);
|
|
|
|
case OpcodeType::Reg0:
|
|
|
|
sprintf(buf, "%s", name);
|
|
|
|
break;
|
|
|
|
case OpcodeType::Reg0I:
|
|
|
|
sprintf(buf, "%s %ld", name, val.arg1().arg);
|
|
|
|
break;
|
|
|
|
case OpcodeType::Reg1:
|
|
|
|
sprintf(buf, "%s %s%ld", name, (val.arg1().is_const) ? "c" : "r",
|
|
|
|
val.arg1().arg);
|
|
|
|
break;
|
|
|
|
case OpcodeType::Reg1I:
|
|
|
|
sprintf(buf, "%s %s%ld %ld", name, (val.arg1().is_const) ? "c" : "r",
|
|
|
|
val.arg1().arg, val.arg2().arg);
|
|
|
|
break;
|
|
|
|
case OpcodeType::Reg2:
|
|
|
|
sprintf(buf, "%s %s%ld %s%ld", name, (val.arg1().is_const) ? "c" : "r",
|
|
|
|
val.arg1().arg, (val.arg2().is_const) ? "c" : "r",
|
|
|
|
val.arg2().arg);
|
|
|
|
break;
|
|
|
|
case OpcodeType::Reg2I:
|
|
|
|
sprintf(buf, "%s %s%ld %s%ld %ld", name,
|
|
|
|
(val.arg1().is_const) ? "c" : "r", val.arg1().arg,
|
|
|
|
(val.arg2().is_const) ? "c" : "r", val.arg2().arg,
|
|
|
|
val.arg3().arg);
|
|
|
|
break;
|
|
|
|
case OpcodeType::Reg3:
|
|
|
|
sprintf(buf, "%s %s%ld %s%ld %s%ld", name,
|
|
|
|
(val.arg1().is_const) ? "c" : "r", val.arg1().arg,
|
|
|
|
(val.arg2().is_const) ? "c" : "r", val.arg2().arg,
|
|
|
|
(val.arg3().is_const) ? "c" : "r", val.arg3().arg);
|
|
|
|
break;
|
|
|
|
case OpcodeType::Reg4:
|
|
|
|
sprintf(buf, "%s %s%ld %s%ld %s%ld %s%ld", name,
|
|
|
|
(val.arg1().is_const) ? "c" : "r", val.arg1().arg,
|
|
|
|
(val.arg2().is_const) ? "c" : "r", val.arg2().arg,
|
|
|
|
(val.arg3().is_const) ? "c" : "r", val.arg3().arg,
|
|
|
|
(val.arg4().is_const) ? "c" : "r", val.arg4().arg);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
res = TRY(res.concat(buf));
|
2024-08-10 20:47:07 +00:00
|
|
|
|
2024-08-11 01:25:44 +00:00
|
|
|
res = TRY(res.concat(">"));
|
2024-08-10 20:47:07 +00:00
|
|
|
return res;
|
2024-08-10 17:24:16 +00:00
|
|
|
}
|
|
|
|
|
2024-08-11 20:37:37 +00:00
|
|
|
Result<String> Writer::write_function(const Function& val) {
|
|
|
|
auto name = TRY(val.name());
|
|
|
|
if (name.is<Nil>()) {
|
|
|
|
return TRY(String::create("#<function>"));
|
|
|
|
}
|
|
|
|
|
2024-08-23 21:08:02 +00:00
|
|
|
if (!name.is<Symbol>()) return ERROR(TypeMismatch);
|
|
|
|
String name_str = TRY(write_symbol(*name.to<Symbol>()));
|
2024-08-11 20:37:37 +00:00
|
|
|
|
|
|
|
String res = TRY(String::create("#<function "));
|
|
|
|
res = TRY(res.concat(name_str));
|
|
|
|
res = TRY(res.concat(">"));
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2024-08-24 23:55:11 +00:00
|
|
|
Result<String> Writer::write_stdlib_function(const StdlibFunction& val) {
|
|
|
|
auto name = TRY(val.name());
|
|
|
|
String name_str = TRY(write_symbol(name));
|
|
|
|
|
|
|
|
String res = TRY(String::create("#<function "));
|
|
|
|
res = TRY(res.concat(name_str));
|
|
|
|
res = TRY(res.concat(">"));
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2024-08-23 23:28:29 +00:00
|
|
|
Result<String> Writer::write_module(const Module& val) {
|
|
|
|
auto name = TRY(val.name());
|
|
|
|
if (name.is<Nil>()) {
|
|
|
|
return TRY(String::create("#<module>"));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!name.is<Symbol>()) return ERROR(TypeMismatch);
|
|
|
|
String name_str = TRY(write_symbol(*name.to<Symbol>()));
|
|
|
|
|
|
|
|
String res = TRY(String::create("#<module "));
|
|
|
|
res = TRY(res.concat(name_str));
|
|
|
|
res = TRY(res.concat(">"));
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2024-08-12 22:05:40 +00:00
|
|
|
Result<String> Writer::write_stack(const Stack& val) {
|
|
|
|
return TRY(String::create("#<stack>"));
|
|
|
|
}
|
|
|
|
|
2024-08-11 20:37:37 +00:00
|
|
|
Result<String> write_one(const Value& value) {
|
2024-08-09 22:45:06 +00:00
|
|
|
auto w = Writer();
|
2024-08-02 22:27:36 +00:00
|
|
|
return w.write_one(value);
|
|
|
|
}
|
2024-08-18 21:08:47 +00:00
|
|
|
|
|
|
|
Result<String> write_multiple(const Value& value) {
|
|
|
|
auto w = Writer();
|
|
|
|
return w.write_multiple(value);
|
|
|
|
}
|