diff --git a/runtime/actions.cpp b/runtime/actions.cpp index acdfaf0..c57594b 100644 --- a/runtime/actions.cpp +++ b/runtime/actions.cpp @@ -1,5 +1,11 @@ #include "actions.hpp" +#include "json.hpp" #include "vm_provider.hpp" +#include +#include +#include + +using namespace sliger::rpc::action; auto sliger::rpc::action::RunDebug::perform_action( std::unique_ptr writer, @@ -38,3 +44,35 @@ auto sliger::rpc::action::CodeCoverage::perform_action( } writer->flush(); }; + +auto sliger::rpc::action::action_from_json(const sliger::json::Value& value) + -> std::unique_ptr +{ + auto& obj = value.as(); + auto type = obj.fields.at("type")->as(); + + if (type.value == "flame-graph") { + auto action = FlameGraph(); + return std::make_unique(action); + } + + if (type.value == "code-coverage") { + auto action = CodeCoverage(); + return std::make_unique(action); + } + + if (type.value == "run-debug") { + sliger::json::ArrayValues values = std::move( + obj.fields.at("program")->as().values); + auto instructions = std::vector(); + for (auto& v : values) { + std::unique_ptr moved = std::move(v); + auto value = moved->as().value; + instructions.push_back((uint32_t)value); + } + auto action = RunDebug(instructions); + return std::make_unique(action); + } + std::cout << "error: TODO " << __FILE__ << ":" << __LINE__ << "\n"; + exit(1); +}; diff --git a/runtime/actions.hpp b/runtime/actions.hpp index cab5dd2..80916c7 100644 --- a/runtime/actions.hpp +++ b/runtime/actions.hpp @@ -5,24 +5,24 @@ namespace sliger::rpc::action { struct Action { - virtual auto perform_action( - std::unique_ptr writer, - sliger::rpc::vm_provider::VmProvider& vm_provider) -> void = 0; + virtual auto perform_action(std::unique_ptr writer, + vm_provider::VmProvider& vm_provider) -> void + = 0; virtual ~Action() = default; }; class FlameGraph : public Action { public: FlameGraph() { } - auto perform_action(std::unique_ptr writer, - sliger::rpc::vm_provider::VmProvider& vm_provider) -> void; + auto perform_action(std::unique_ptr writer, + vm_provider::VmProvider& vm_provider) -> void; }; class CodeCoverage : public Action { public: CodeCoverage() { } - auto perform_action(std::unique_ptr writer, - sliger::rpc::vm_provider::VmProvider& vm_provider) -> void; + auto perform_action(std::unique_ptr writer, + vm_provider::VmProvider& vm_provider) -> void; }; class RunDebug : public Action { @@ -31,41 +31,13 @@ public: : instructions(instructions) { } - auto perform_action(std::unique_ptr writer, - sliger::rpc::vm_provider::VmProvider& vm_provider) -> void; + auto perform_action(std::unique_ptr writer, + vm_provider::VmProvider& vm_provider) -> void; private: std::vector instructions; }; -static inline auto action_from_json( - std::unique_ptr value) -> std::unique_ptr -{ - auto& obj = value->as(); - auto type = obj.fields.at("type")->as(); +auto action_from_json(const json::Value& value) -> std::unique_ptr; - if (type.value == "flame-graph") { - auto action = FlameGraph(); - return std::make_unique(action); - } - - if (type.value == "code-coverage") { - auto action = CodeCoverage(); - return std::make_unique(action); - } - - if (type.value == "run-debug") { - sliger::json::ArrayValues values = std::move( - obj.fields.at("program")->as().values); - auto instructions = std::vector(); - for (auto& v : values) { - std::unique_ptr moved = std::move(v); - auto value = moved->as().value; - instructions.push_back((uint32_t)value); - } - auto action = RunDebug(instructions); - return std::make_unique(action); - } - throw "todo"; -}; } diff --git a/runtime/json.cpp b/runtime/json.cpp index 660e689..1359393 100644 --- a/runtime/json.cpp +++ b/runtime/json.cpp @@ -238,3 +238,14 @@ auto Parser::parse_val() -> Res> "internal error, could not parse '{}'", tok_typ_to_string(cur.typ)), }; } + +auto Parser::unexpected_tok_err(TokTyp expected, std::string_view msg) + -> Res> +{ + return Err { + .pos = this->cur.val().pos, + .msg = std::format("{}, expected '{}', got '{}'", msg, + tok_typ_to_string(expected), + tok_typ_to_string(this->cur.val().typ)), + }; +} diff --git a/runtime/json.hpp b/runtime/json.hpp index c22eca2..554ef7e 100644 --- a/runtime/json.hpp +++ b/runtime/json.hpp @@ -2,7 +2,6 @@ #include #include -#include #include #include #include @@ -97,6 +96,13 @@ struct Value { { return static_cast(*this); } + + template + requires std::derived_from + inline auto as() const& -> const T& + { + return static_cast(*this); + } }; struct Null final : public Value { @@ -275,16 +281,8 @@ public: auto parse_val() -> Res>; private: - inline auto unexpected_tok_err( - TokTyp expected, std::string_view msg) -> Res> - { - return Err { - .pos = this->cur.val().pos, - .msg = std::format("{}, expected '{}', got '{}'", msg, - tok_typ_to_string(expected), - tok_typ_to_string(this->cur.val().typ)), - }; - } + auto unexpected_tok_err(TokTyp expected, std::string_view msg) + -> Res>; inline auto step() -> Res { diff --git a/runtime/main.cpp b/runtime/main.cpp index ce28b71..f751e95 100644 --- a/runtime/main.cpp +++ b/runtime/main.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -52,7 +53,7 @@ int main(int argc, char** argv) auto rpc = sliger::rpc::RpcServer( [&](std::unique_ptr req, std::unique_ptr writer) { - auto action = sliger::rpc::action::action_from_json(std::move(req)); + auto action = sliger::rpc::action::action_from_json(*req); action->perform_action(std::move(writer), state); }); diff --git a/runtime/rpc_server.cpp b/runtime/rpc_server.cpp index c720470..4642713 100644 --- a/runtime/rpc_server.cpp +++ b/runtime/rpc_server.cpp @@ -1,37 +1,104 @@ #include "rpc_server.hpp" + +extern "C" { +#include +#include +#include #include +#include +#include #include #include +} -auto sliger::rpc::BufferedWriter::write(std::string message) -> Res +using namespace sliger::rpc; + +static auto create_address(uint16_t port) -> sockaddr_in { - for (size_t i = 0; i < message.length(); ++i) { - auto res = this->write((uint8_t)message[i]); - if (!res.is_ok()) { + return { + .sin_family = AF_INET, + .sin_port = ::htons(port), + .sin_addr = { .s_addr = ::inet_addr("127.0.0.1") }, + .sin_zero = { 0 }, + }; +} + +auto Socket::init() -> Res +{ + this->socket_fd = ::socket(AF_INET, SOCK_STREAM, 0); + if (socket_fd < 0) { + return Err { .msg + = std::format("could not get socket ({})", socket_fd) }; + }; + this->initialized = true; + + int trueValue = 1; + ::setsockopt( + this->socket_fd, SOL_SOCKET, SO_REUSEADDR, &trueValue, sizeof(int)); + + int err; + + auto address = create_address(13370); + err = ::bind(socket_fd, (struct sockaddr*)&address, sizeof(address)); + if (err < 0) { + return Err { .msg = std::format("could not bind ({})", err) }; + }; + + err = ::listen(socket_fd, 0); + if (err < 0) { + return Err { .msg = std::format("could not listen ({})", err) }; + } + return {}; +} + +auto Socket::accept() -> Res> +{ + auto client_address = create_address(13370); + socklen_t address_size = sizeof(client_address); + int client + = ::accept(socket_fd, (struct sockaddr*)&client_address, &address_size); + if (client < 0) { + return Err { .msg = std::format("could not accept ({})", client) }; + } + return std::make_unique(client); +} +auto Client::read(int8_t* buffer, size_t buffer_size) -> ssize_t +{ + return ::read(this->client_sock, buffer, buffer_size); +} +auto Client::write(uint8_t* buffer, size_t buffer_size) -> ssize_t +{ + return ::write(this->client_sock, buffer, buffer_size); +} + +auto BufferedWriter::write(std::string message) -> Res +{ + for (auto ch : message) { + if (auto res = this->write((uint8_t)ch); !res.is_ok()) { return res.err(); } } - return Unit {}; + return {}; } -auto sliger::rpc::BufferedWriter::write(uint8_t byte) -> Res +auto BufferedWriter::write(uint8_t byte) -> Res { if (this->occupied >= length) { - auto res = this->flush(); - if (!res.is_ok()) { + if (auto res = this->flush(); !res.is_ok()) { return res.err(); } } this->buffer[this->occupied] = byte; this->occupied += 1; - return Unit {}; + + return {}; } -auto sliger::rpc::BufferedWriter::flush() -> Res +auto BufferedWriter::flush() -> Res { - auto result = ::write(this->fd, this->buffer, this->occupied); + auto result = this->client->write(this->buffer, this->occupied); if (result < 0) { - return { { "unable to write" } }; + return Err("unable to write"); } this->occupied = 0; return (size_t)result; diff --git a/runtime/rpc_server.hpp b/runtime/rpc_server.hpp index 98f6c9d..2994e23 100644 --- a/runtime/rpc_server.hpp +++ b/runtime/rpc_server.hpp @@ -1,11 +1,8 @@ #pragma once #include "json.hpp" -#include -#include -#include -#include -#include +#include +#include namespace sliger::rpc { @@ -16,17 +13,18 @@ struct Err { template class Res { public: Res(T value) + : holds_value(true) + , value(std::move(value)) { - this->value = value; - this->holds_value = true; } Res(Err error) + : holds_value(false) + , error(error) { - this->error = error; - this->holds_value = false; } auto is_ok() -> bool { return this->holds_value; } - auto ok() -> T { return this->value; } + auto ok() & -> T& { return this->value; } + auto ok() && -> T&& { return std::move(this->value); } auto err() -> Err { return this->error; } private: @@ -35,6 +33,25 @@ private: Err error; }; +template <> class Res { +public: + Res() + : holds_value(true) + { + } + Res(Err error) + : holds_value(false) + , error(error) + { + } + auto is_ok() -> bool { return this->holds_value; } + auto err() -> Err { return this->error; } + +private: + bool holds_value; + Err error; +}; + class BracketFinder { public: BracketFinder() @@ -55,43 +72,70 @@ private: int layers; }; -struct Unit { }; +#define DONT_COPY_OR_MOVE(T) \ + T(const T&) = delete; \ + T operator=(const T&) = delete; \ + T(T&&) = delete; \ + T operator=(T&&) = delete; + +class Client; + +class Socket { +public: + DONT_COPY_OR_MOVE(Socket); + Socket() = default; + + ~Socket() + { + if (this->initialized) { + close(this->socket_fd); + } + } + + auto init() -> Res; + auto accept() -> Res>; + +private: + int socket_fd = 0; + bool initialized = false; +}; + +class Client { +public: + DONT_COPY_OR_MOVE(Client); + Client(int client_sock) + : client_sock(client_sock) + { + } + + ~Client() { close(client_sock); } + + auto read(int8_t* buffer, size_t buffer_size) -> ssize_t; + auto write(uint8_t* buffer, size_t buffer_size) -> ssize_t; + void flush(); + +private: + int client_sock; +}; class BufferedWriter { public: - BufferedWriter(int fd) - : fd(fd) + BufferedWriter(Client& client) + : client(&client) { } - BufferedWriter(const BufferedWriter&) = delete; - BufferedWriter operator=(const BufferedWriter&) = delete; - BufferedWriter(BufferedWriter&&) = delete; - BufferedWriter operator=(BufferedWriter&&) = delete; - ~BufferedWriter() { close(fd); } auto flush() -> Res; - auto write(uint8_t byte) -> Res; - auto write(std::string message) -> Res; + auto write(uint8_t byte) -> Res; + auto write(std::string message) -> Res; private: static const size_t length = 1024; size_t occupied = 0; uint8_t buffer[length]; - int fd; + Client* client; }; -namespace { - static inline auto create_address(uint16_t port) -> sockaddr_in - { - return { - .sin_family = AF_INET, - .sin_port = htons(port), - .sin_addr = { .s_addr = inet_addr("127.0.0.1") }, - .sin_zero = { 0 }, - }; - } -} - /// - load code /// - program input /// - run @@ -107,54 +151,31 @@ namespace { template class RpcServer { public: RpcServer(Functor&& functor) - : functor(functor) {}; + : functor(functor) { }; - auto listen() -> Res + auto listen() -> Res { - int socket; - { - socket = ::socket(AF_INET, SOCK_STREAM, 0); - if (socket < 0) { - return Err { .msg - = std::format("could not get socket ({})", socket) }; - }; - } - { - auto address = create_address(13370); - auto err - = ::bind(socket, (struct sockaddr*)&address, sizeof(address)); - if (err < 0) { - close(socket); - return Err { .msg = std::format("could not bind ({})", err) }; - }; - } - { - auto err = ::listen(socket, 0); - if (err < 0) { - close(socket); - return Err { .msg = std::format("could not listen ({})", err) }; - } + auto socket = Socket(); + if (auto res = socket.init(); not res.is_ok()) { + return res.err(); } while (true) { - - auto client_address = create_address(13370); - socklen_t address_size = sizeof(client_address); - int client = ::accept( - socket, (struct sockaddr*)&client_address, &address_size); - if (client < 0) { - close(socket); - return Err { .msg - = std::format("could not accept ({})", client) }; + auto client_res = socket.accept(); + if (!client_res.is_ok()) { + return client_res.err(); } + auto client = std::move(client_res.ok()); + const size_t buf_len = 1024; int8_t buffer[buf_len] = {}; auto bracket_finder = BracketFinder(); std::string message = {}; while (true) { - ssize_t bytes_read = read(client, buffer, buf_len); + ssize_t bytes_read = client->read(buffer, buf_len); if (bytes_read <= 0) { break; } + for (size_t i = 0; i < (size_t)bytes_read; ++i) { message += buffer[i]; bracket_finder.feed(buffer[i]); @@ -167,18 +188,17 @@ public: auto msg = std::format( "error parsing rpc message: '{}' @ {}:{}\n", err.msg, err.pos.line, err.pos.col); - close(client); - close(socket); return Err { .msg = msg, }; } - auto writer = std::make_unique(client); + auto writer = std::make_unique(*client); this->functor(std::move(req.val()), std::move(writer)); message.clear(); - break; + goto message_done; } } + message_done: } std::unreachable(); }; diff --git a/runtime/value.cpp b/runtime/value.cpp new file mode 100644 index 0000000..12a259a --- /dev/null +++ b/runtime/value.cpp @@ -0,0 +1,34 @@ +#include "value.hpp" +#include +#include + +using namespace sliger; + +auto Value::to_string() const -> std::string +{ + switch (this->m_type) { + case ValueType::Null: + return "null"; + case ValueType::Int: + return std::to_string(as_int().value); + case ValueType::Bool: + return as_bool().value ? "true" : "false"; + case ValueType::String: + return std::format("\"{}\"", escape_string(as_string().value)); + case ValueType::Ptr: + return std::to_string(as_ptr().value); + } + std::unreachable(); +} + +auto Value::to_repr_string() const -> std::string +{ + return std::format( + "{}({:.4s})", value_type_to_string(this->m_type), to_string()); +} + +void Value::print_tried_to_unwrap_error_message(ValueType vt) const +{ + std::cerr << std::format("error: tried to unwrap {} as {}\n", + value_type_to_string(this->m_type), value_type_to_string(vt)); +} diff --git a/runtime/value.hpp b/runtime/value.hpp index 7380d52..e1e3d52 100644 --- a/runtime/value.hpp +++ b/runtime/value.hpp @@ -2,8 +2,6 @@ #include #include -#include -#include #include #include #include @@ -126,8 +124,7 @@ public: try { return std::get::Type>(value); } catch (const std::bad_variant_access& ex) { - std::cerr << std::format("error: tried to unwrap {} as {}\n", - value_type_to_string(this->m_type), value_type_to_string(VT)); + print_tried_to_unwrap_error_message(VT); std::exit(1); } } @@ -138,8 +135,7 @@ public: try { return std::get::Type>(value); } catch (const std::bad_variant_access& ex) { - std::cerr << std::format("error: tried to unwrap {} as {}\n", - value_type_to_string(this->m_type), value_type_to_string(VT)); + print_tried_to_unwrap_error_message(VT); std::exit(1); } } @@ -158,30 +154,12 @@ public: inline auto as_ptr() const -> const Ptr& { return as(); } // clang-format on - inline auto to_string() const -> std::string - { - switch (this->m_type) { - case ValueType::Null: - return "null"; - case ValueType::Int: - return std::to_string(as_int().value); - case ValueType::Bool: - return as_bool().value ? "true" : "false"; - case ValueType::String: - return std::format("\"{}\"", escape_string(as_string().value)); - case ValueType::Ptr: - return std::to_string(as_ptr().value); - } - std::unreachable(); - } - - inline auto to_repr_string() const -> std::string - { - return std::format( - "{}({:.4s})", value_type_to_string(this->m_type), to_string()); - } + auto to_string() const -> std::string; + auto to_repr_string() const -> std::string; private: + void print_tried_to_unwrap_error_message(ValueType vt) const; + ValueType m_type; std::variant value; }; diff --git a/runtime/vm.cpp b/runtime/vm.cpp index 1324d13..dd9b412 100644 --- a/runtime/vm.cpp +++ b/runtime/vm.cpp @@ -131,7 +131,7 @@ void VM::run_instruction() this->pc = fn_ptr.as_ptr().value; this->bp = static_cast(this->stack.size()); for (auto&& arg = arguments.rbegin(); arg != arguments.rend(); - ++arg) { + ++arg) { stack_push(*arg); } if (this->opts.flame_graph) { @@ -399,3 +399,44 @@ void VM::run_builtin(Builtin builtin_id) } } } +auto VM::stack_repr_string(size_t max_items) const -> std::string +{ + auto result = std::string(); + result += "→"; + const auto& stack = view_stack(); + for (size_t i = 0; i < stack.size() and i < max_items; ++i) { + if (i != 0) { + result += " "; + } + result += std::format( + "{:<11}", stack[stack.size() - i - 1].to_repr_string()); + } + if (stack.size() > max_items) { + result += std::format(" ... + {}", stack.size() - max_items); + } + return result; +}; + +void VM::assert_fn_stack_has(size_t count) +{ + if (this->stack.size() - this->bp < count) { + std::cerr << std::format("stack underflow, pc = {}\n", this->pc); + std::exit(1); + } +} + +void VM::assert_stack_has(size_t count) +{ + if (this->stack.size() < count) { + std::cerr << std::format("stack underflow, pc = {}\n", this->pc); + std::exit(1); + } +} + +void VM::assert_program_has(size_t count) +{ + if (this->pc + count > program_size) { + std::cerr << std::format("malformed program, pc = {}", this->pc); + std::exit(1); + } +} diff --git a/runtime/vm.hpp b/runtime/vm.hpp index 5a1bc45..04e29d5 100644 --- a/runtime/vm.hpp +++ b/runtime/vm.hpp @@ -6,8 +6,6 @@ #include "value.hpp" #include #include -#include -#include #include #include @@ -173,23 +171,7 @@ public: return this->stack; } - inline auto stack_repr_string(size_t max_items) const -> std::string - { - auto result = std::string(); - result += "→"; - const auto& stack = view_stack(); - for (size_t i = 0; i < stack.size() and i < max_items; ++i) { - if (i != 0) { - result += " "; - } - result += std::format( - "{:<11}", stack[stack.size() - i - 1].to_repr_string()); - } - if (stack.size() > max_items) { - result += std::format(" ... + {}", stack.size() - max_items); - } - return result; - } + auto stack_repr_string(size_t max_items) const -> std::string; private: void run_builtin(Builtin builtin_id); @@ -238,20 +220,8 @@ private: { return this->stack.at(this->bp + idx); } - inline void assert_fn_stack_has(size_t count) - { - if (this->stack.size() - this->bp < count) { - std::cerr << std::format("stack underflow, pc = {}\n", this->pc); - std::exit(1); - } - } - inline void assert_stack_has(size_t count) - { - if (this->stack.size() < count) { - std::cerr << std::format("stack underflow, pc = {}\n", this->pc); - std::exit(1); - } - } + void assert_fn_stack_has(size_t count); + void assert_stack_has(size_t count); inline void stack_push(Value&& value) { this->stack.push_back(value); } inline void stack_push(Value& value) { this->stack.push_back(value); } inline auto stack_pop() -> Value @@ -260,14 +230,7 @@ private: this->stack.pop_back(); return value; } - - inline auto assert_program_has(size_t count) - { - if (this->pc + count > program_size) { - std::cerr << std::format("malformed program, pc = {}", this->pc); - std::exit(1); - } - } + void assert_program_has(size_t count); VMOpts opts; uint32_t pc = 0; diff --git a/web/main.ts b/web/main.ts index e2eb0f6..9be0750 100644 --- a/web/main.ts +++ b/web/main.ts @@ -17,7 +17,7 @@ const filepath = flags._[0] as string; const text = await Deno.readTextFile(filepath); const runtime = new Runtime(13370); -await runtime.start(); +// await runtime.start(); async function compileProgram(filepath: string) { const result = await compiler.compileWithDebug(filepath); @@ -25,14 +25,18 @@ async function compileProgram(filepath: string) { } async function runProgramWithDebug(program: number[]) { + console.log("connecting"); const connection = await runtime.connect(); + console.log("conneced"); connection.send({ type: "run-debug", program, }); + console.log("sent"); const res = await connection.receive<{ ok: boolean; }>(); + console.log("received"); connection.close(); if (!res.ok) { throw new Error("could not run code"); diff --git a/web/runtime.ts b/web/runtime.ts index b80e931..bf47d2c 100644 --- a/web/runtime.ts +++ b/web/runtime.ts @@ -33,17 +33,22 @@ export class Runtime { } async connect(): Promise { - return await new Promise((resolve) => { - setTimeout(async () => { - resolve( - new RuntimeConnection( - await Deno.connect({ - port: this.port, - }), - ), - ); - }, 1000); - }); + // return await new Promise((resolve) => { + // setTimeout(async () => { + // resolve( + // new RuntimeConnection( + // await Deno.connect({ + // port: this.port, + // }), + // ), + // ); + // }, 100); + // }); + return new RuntimeConnection( + await Deno.connect({ + port: this.port, + }), + ); } }