commit 490063ccc5cb11374e3527b2e03b1923841bfd6c Author: janis Date: Wed May 27 01:02:47 2026 +0200 asdf diff --git a/src/main.cc b/src/main.cc new file mode 100644 index 0000000..d23c22f --- /dev/null +++ b/src/main.cc @@ -0,0 +1,6 @@ +#include + +auto main() -> int { + std::printf("Hello, World!\n"); + return 0; +} diff --git a/weave.cc b/weave.cc new file mode 100755 index 0000000..5db1a8d --- /dev/null +++ b/weave.cc @@ -0,0 +1,581 @@ +///$(which true);FLAGS="-g -Wall -Wextra --std=c++23 -O3";c++ $FLAGS -o weave "$0" || { echo "Compilation failed"; exit 1; }; ./weave; exit 0 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Type aliases for convenience +using u8 = uint8_t; +using u16 = uint16_t; +using u32 = uint32_t; +using u64 = uint64_t; +using usize = std::size_t; +using i8 = int8_t; +using i16 = int16_t; +using i32 = int32_t; +using i64 = int64_t; +using isize = std::ptrdiff_t; +using f32 = float; +using f64 = double; + + +template +using RefT = std::reference_wrapper; + +template struct overloads : Ts... { + using Ts::operator()...; +}; + +class CowStr { + std::variant data_; + +public: + CowStr() : data_(std::string_view{}) {} + + CowStr(std::string_view s) : data_(s) {} + CowStr(const char* s) : data_(std::string_view{s}) {} + CowStr(std::string s) : data_(std::move(s)) {} + + [[nodiscard]] bool is_borrowed() const { + return std::holds_alternative(data_); + } + + [[nodiscard]] bool is_owned() const { + return std::holds_alternative(data_); + } + + [[nodiscard]] std::string_view view() const { + if (const auto* borrowed = std::get_if(&data_)) { + return *borrowed; + } + return std::get(data_); + } + + [[nodiscard]] const char* c_str() { + if (auto* borrowed = std::get_if(&data_)) { + data_ = std::string(*borrowed); + } + return std::get(data_).c_str(); + } + + [[nodiscard]] std::string into_owned() && { + if (auto* owned = std::get_if(&data_)) { + return std::move(*owned); + } + return std::string(std::get(data_)); + } + + [[nodiscard]] std::string to_owned() const { + if (const auto* owned = std::get_if(&data_)) { + return *owned; + } + return std::string(std::get(data_)); + } + + std::string& to_mut() { + if (auto* borrowed = std::get_if(&data_)) { + data_ = std::string(*borrowed); + } + return std::get(data_); + } +}; + +namespace json { + +static void append_utf8(std::string &out, u32 cp) { + if (cp <= 0x7F) { + out.push_back(static_cast(cp)); + } else if (cp <= 0x7FF) { + out.push_back(static_cast(0xC0 | ((cp >> 6) & 0x1F))); + out.push_back(static_cast(0x80 | (cp & 0x3F))); + } else if (cp <= 0xFFFF) { + out.push_back(static_cast(0xE0 | ((cp >> 12) & 0x0F))); + out.push_back(static_cast(0x80 | ((cp >> 6) & 0x3F))); + out.push_back(static_cast(0x80 | (cp & 0x3F))); + } else { + out.push_back(static_cast(0xF0 | ((cp >> 18) & 0x07))); + out.push_back(static_cast(0x80 | ((cp >> 12) & 0x3F))); + out.push_back(static_cast(0x80 | ((cp >> 6) & 0x3F))); + out.push_back(static_cast(0x80 | (cp & 0x3F))); + } +} + +static int hex_value(char c) { + if ('0' <= c && c <= '9') return c - '0'; + if ('a' <= c && c <= 'f') return 10 + (c - 'a'); + if ('A' <= c && c <= 'F') return 10 + (c - 'A'); + return -1; +} + +// Unescape a JSON string literal body (without surrounding quotes). +// Throws std::runtime_error on malformed escape sequences. +std::string json_unescape(const std::string &in) { + std::string out; + out.reserve(in.size()); + for (size_t i = 0; i < in.size(); ++i) { + char c = in[i]; + if (c != '\\') { + out.push_back(c); + continue; + } + + // Escape sequence + if (++i >= in.size()) throw std::runtime_error("Invalid escape: trailing backslash"); + char e = in[i]; + switch (e) { + case '"': out.push_back('"'); break; + case '\\': out.push_back('\\'); break; + case '/': out.push_back('/'); break; + case 'b': out.push_back('\b'); break; + case 'f': out.push_back('\f'); break; + case 'n': out.push_back('\n'); break; + case 'r': out.push_back('\r'); break; + case 't': out.push_back('\t'); break; + case 'u': { + // parse 4 hex digits + if (i + 4 >= in.size()) throw std::runtime_error("Invalid \\u escape: too short"); + int v = 0; + for (int k = 1; k <= 4; ++k) { + int hv = hex_value(in[i + k]); + if (hv < 0) throw std::runtime_error("Invalid hex digit in \\u escape"); + v = (v << 4) | hv; + } + i += 4; // consumed 4 hex digits + + uint32_t codepoint = static_cast(v); + + // handle UTF-16 surrogate pair + if (0xD800 <= codepoint && codepoint <= 0xDBFF) { + // high surrogate; expect another \uXXXX for low surrogate + if (i + 2 >= in.size() || in[i + 1] != '\\' || in[i + 2] != 'u') + throw std::runtime_error("Missing low surrogate after high surrogate"); + // parse low surrogate + if (i + 6 >= in.size()) throw std::runtime_error("Invalid low surrogate \\u escape"); + int lv = 0; + for (int k = 3; k <= 6; ++k) { + int hv = hex_value(in[i + k]); + if (hv < 0) throw std::runtime_error("Invalid hex digit in low surrogate"); + lv = (lv << 4) | hv; + } + i += 6; // consumed '\' 'u' and 4 hex digits (positions i+1..i+6) + uint32_t low = static_cast(lv); + if (!(0xDC00 <= low && low <= 0xDFFF)) throw std::runtime_error("Invalid low surrogate value"); + // combine surrogates to code point + codepoint = 0x10000 + (((codepoint - 0xD800) << 10) | (low - 0xDC00)); + } + + append_utf8(out, codepoint); + break; + } + default: + throw std::runtime_error(std::string("Invalid escape character: \\") + e); + } + } + return out; +} + +std::string json_escape(const std::string_view &str) { + std::ostringstream ss; + for (const auto &c : str) { + switch (c) { + case '\"': + ss << "\\\""; + break; + case '\\': + ss << "\\\\"; + break; + case '\b': + ss << "\\b"; + break; + case '\f': + ss << "\\f"; + break; + case '\n': + ss << "\\n"; + break; + case '\r': + ss << "\\r"; + break; + case '\t': + ss << "\\t"; + break; + default: + if (c >= 0 && c <= 0x1F) { + ss << "\\u" << std::hex << std::uppercase << (int)c << std::dec << std::nouppercase; + } else { + ss << c; + } + } + } + + return ss.str(); +} +enum class JsonType { + Null, + Boolean, + Number, + String, + Array, + Object, +}; + + struct JsonValue; + + struct JsonObject { + std::unordered_map members; + + JsonObject(std::unordered_map members) + : members(std::move(members)) {} + + auto get_field(const std::string_view &key) const -> std::optional>; + + auto get_field_mut(const std::string_view &key) -> std::optional>; + + auto insert_field(const std::string_view &key, const JsonValue &value) -> void; + }; + + struct JsonNull {}; + struct JsonNumber { + std::string_view value; + + JsonNumber(std::string_view value) : value(value) {} + }; + struct JsonValue { + std::variant, JsonObject> + value; + + JsonValue() : value(JsonNull{}) {} + JsonValue(JsonNull null) : value(null) {} + JsonValue(bool boolean) : value(boolean) {} + JsonValue(const std::string_view& str) : value(str) {} + JsonValue(JsonNumber number) : value(number) {} + JsonValue(std::vector array) : value(std::move(array)) {} + JsonValue(JsonObject object) : value(std::move(object)) {} + + auto is_null() const -> bool { return std::holds_alternative(value); } + auto is_boolean() const -> bool { + return std::holds_alternative(value); + } + auto is_string() const -> bool { + + return std::holds_alternative(value); + } + auto is_number() const -> bool { + return std::holds_alternative(value); + } + auto is_array() const -> bool { + return std::holds_alternative>(value); + } + auto is_object() const -> bool { + return std::holds_alternative(value); + } + auto as_null() const -> std::optional { + if (auto pval = std::get_if(&value)) { + return *pval; + } else { + return std::nullopt; + } + } + auto as_boolean() const -> std::optional { + if (auto pval = std::get_if(&value)) { + return *pval; + } else { + return std::nullopt; + } + } + auto as_string() const -> std::optional { + if (auto pval = std::get_if(&value)) { + return *pval; + } else { + return std::nullopt; + } + } + auto as_number() const -> std::optional> { + if (auto pval = std::get_if(&value)) { + return std::ref(*pval); + } else { + return std::nullopt; + } + } + auto as_array() const -> std::optional>> { + if (auto pval = std::get_if>(&value)) { + return std::ref(*pval); + } else { + return std::nullopt; + } + } + auto as_object() const -> std::optional> { + if (auto pval = std::get_if(&value)) { + return std::ref(*pval); + } else { + return std::nullopt; + } + } + + friend + auto operator<<(std::ostream &os, const JsonValue& val) -> std::ostream &; + }; + + auto operator<<(std::ostream &os, const JsonValue& val) -> std::ostream & { + + const auto visitor = + overloads{ + [&](JsonNull _) { os << "null"; }, + [&](bool b) { os << (b ? "true" : "false"); }, + [&](const JsonNumber &num) { os << num.value; }, + [&](const std::string_view &str) { os << "\"" << str << "\""; }, + [&](const JsonObject &obj) { + os << "{"; + auto first = true; + for (const auto &[key, value] : obj.members) { + if (!first) { + os << ","; + } + os << "\"" << key << "\": " << value; + first = false; + } + os << "}"; + }, + [&](const std::vector &array) { + os << "["; + auto first = true; + for (const auto &value : array) { + if (!first) { + os << ","; + } + os << value; + first = false; + } + os << "]"; + } + }; + + std::visit(visitor, val.value); + + return os; + } + + auto JsonObject::get_field(const std::string_view &key) const -> std::optional> { + auto it = members.find(key); + if (it != members.end()) { + return std::ref(it->second); + } else { + return std::nullopt; + } + } + + auto JsonObject::get_field_mut(const std::string_view &key) -> std::optional> { + auto it = members.find(key); + if (it != members.end()) { + return std::ref(it->second); + } else { + return std::nullopt; + } + } + + auto JsonObject::insert_field(const std::string_view &key, const JsonValue &value) -> void { + members[key] = value; + } + +} + +struct Source { + std::string_view path; + std::string_view flags; + std::string_view defines; + + Source(std::string_view path) + : path(std::move(path)), flags(""), defines("") {} + // rule of five + Source(const Source &) = delete; + Source(Source &&) = delete; + Source &operator=(const Source &) = delete; + Source &operator=(Source &&) = delete; +}; + +struct Project { + std::string_view name; + std::vector sources; + std::unordered_map project_defines; + std::vector project_flags; + + // rule of five + Project(const Project &) = delete; + Project(Project &&) = delete; + Project &operator=(const Project &) = delete; + Project &operator=(Project &&) = delete; + + Project(std::string_view name) + : name(std::move(name)), sources({}), project_defines({}), project_flags({}) {} + auto add_source(std::string_view source) -> void { + sources.push_back(std::move(source)); + } + + auto into_compile_commands(const std::string_view& working_dir) -> json::JsonValue { + std::vector compile_commands{}; + const auto project_args = [&]() -> std::vector { + std::vector args{}; + for (const auto &flag : project_flags) { + args.push_back(json::JsonValue(flag)); + } + for (const auto &[key, value] : project_defines) { + args.push_back(json::JsonValue("-D" + key + "=" + value)); + } + return args; + }(); + + for (const auto &source : sources) { + std::printf("Processing source: %s\n", source.data()); + auto fields = std::unordered_map{}; + fields["directory"] = json::JsonValue(working_dir); + fields["file"] = json::JsonValue(source); + fields["arguments"] = json::JsonValue(std::vector{ + json::JsonValue(std::string_view("g++")), json::JsonValue(std::string_view("-c")), + json::JsonValue(std::string_view(source)), json::JsonValue(std::string_view("-o")), + json::JsonValue(std::string_view(std::string(source) + std::string(".o"))), + json::JsonValue(std::vector(project_args))}); + + compile_commands.push_back(json::JsonValue(json::JsonObject(fields))); + } + + + return compile_commands; + } +}; +static auto ninja_escape(std::string_view s) -> std::string { + std::string out; + out.reserve(s.size()); + for (char c : s) { + switch (c) { + case '$': + out += "$$"; + break; + case ':': + out += "$:"; + break; + case ' ': + out += "$ "; + break; + case '\n': + out += "$\n"; + break; + default: + out.push_back(c); + break; + } + } + return out; +} +static auto shell_join_arguments(const std::vector &args) + -> std::string { + std::string out; + bool first = true; + for (const auto &arg0 : args) { + if (auto&& arg = arg0.as_string()) { + if (!first) { + out.push_back(' '); + } + + const bool needs_quotes = + arg->find_first_of(" \t\n\"'\\$") != std::string_view::npos; + + if (!needs_quotes) { + out.append(arg->begin(), arg->end()); + } else { + out.push_back('"'); + for (char c : *arg) { + if (c == '"' || c == '\\') { + out.push_back('\\'); + } + out.push_back(c); + } + out.push_back('"'); + } + + first = false; + } else { + throw std::runtime_error("compile_commands entry has non-string argument"); + } + } + return out; +} + +static auto default_object_path(std::string_view file) -> std::string { + return std::string(file) + ".o"; +} + +auto compile_commands_into_ninja(const json::JsonValue &compile_commands) + -> void { + if (auto arr = compile_commands.as_array()) { + + std::ostringstream ninja; + ninja << "ninja_required_version = 1.3\n\n"; + ninja << "rule cc\n"; + ninja << " command = $cmd\n"; + ninja << " description = CC $in\n\n"; + + for (const auto &entry : arr->get()) { + const json::JsonObject& obj = entry.as_object().value(); + const json::JsonValue& file_field = obj.get_field("file").value(); + auto file = file_field.as_string().value(); + + auto output = default_object_path(file); + if (const auto &output_field = obj.get_field("output")) { + if (const auto out = output_field->get().as_string()) { + output = std::string(*out); + } else { + throw std::runtime_error("'output' must be a string"); + } + } + + std::string cmd{}; + if (auto args_field = obj.get_field("arguments")) { + cmd = shell_join_arguments(args_field->get().as_array().value()); + } else if (auto command_field = obj.get_field("command")) { + cmd = command_field->get().as_string().value(); + } else { + throw std::runtime_error( + "compile_commands entry must contain either 'arguments' or 'command'"); + } + + ninja << "build " << ninja_escape(output) << ": cc " << ninja_escape(file) << "\n"; + ninja << " cmd = " << ninja_escape(cmd) << "\n\n"; + } + + std::cout << ninja.str(); + } else { + throw std::runtime_error("compile_commands root must be an array"); + } +} + +#include +#include + +auto main() -> int { + std::printf("Hello, weave!\n"); + + // Example usage + Project project("MyProject"); + project.add_source("src/main.cc"); + project.project_flags.append_range(std::list{"-g", "-Wall", "-Wextra", "--std=c++26", "-O3"}); + + auto compile_commands_json = project.into_compile_commands("/var/code/cxx/loom/"); + + + std::cout << compile_commands_json << std::endl; + // std::ofstream os("compile_commands.json"); + // os << compile_commands_json; + // os.close(); + + return 0; +}