Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,6 @@
"update-tests": "zx scripts/update-tests.mjs",
"cxx-gen-ast": "node packages/cxx-gen-ast",
"cxx-gen-lsp": "node packages/cxx-gen-lsp packages/cxx-gen-lsp/metaModel.json packages/cxx-gen-lsp -o src/lsp/cxx/lsp",
"download-lsp-models": "zx scripts/download-lsp-models.mjs"
"download-lsp-model": "zx scripts/download-lsp-model.mjs"
}
}
66 changes: 34 additions & 32 deletions packages/cxx-gen-lsp/src/gen_fwd_h.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,24 @@ using Pattern = std::string;

class LSPObject {
public:
explicit LSPObject(json repr): repr_(std::move(repr)) {}
explicit LSPObject(json& repr): repr_(&repr) {}

[[nodiscard]] operator const json&() const { return repr_; }
[[nodiscard]] operator const json&() const { return *repr_; }
[[nodiscard]] auto get() const -> json& { return *repr_; }

protected:
json repr_;
json* repr_{nullptr};
};

template <typename T>
class Vector final : public LSPObject {
public:
using LSPObject::LSPObject;

[[nodiscard]] explicit operator bool() const { return repr_.is_array(); }
[[nodiscard]] auto size() const -> std::size_t { return repr_.size(); }
[[nodiscard]] auto empty() const -> bool { return repr_.empty(); }
[[nodiscard]] auto at(int index) const -> const T& { return repr_[index]; }
[[nodiscard]] explicit operator bool() const { return repr_->is_array(); }
[[nodiscard]] auto size() const -> std::size_t { return repr_->size(); }
[[nodiscard]] auto empty() const -> bool { return repr_->empty(); }
[[nodiscard]] auto at(int index) const -> const T& { return repr_->at(index); }
};

namespace details {
Expand All @@ -57,7 +58,7 @@ struct TryEmplace;

template <std::derived_from<LSPObject> T>
struct TryEmplace<T> {
auto operator()(auto& result, const json& value) const -> bool {
auto operator()(auto& result, json& value) const -> bool {
auto obj = T{value};
if (!obj) return false;
result.template emplace<T>(std::move(obj));
Expand All @@ -66,7 +67,7 @@ struct TryEmplace<T> {
};

template <typename... Ts>
auto try_emplace(std::variant<Ts...>& result, const json& value) -> bool {
auto try_emplace(std::variant<Ts...>& result, json& value) -> bool {
return (details::TryEmplace<Ts>{}(result, value) || ...);
}

Expand All @@ -77,7 +78,7 @@ struct TryEmplace<std::monostate> {

template <>
struct TryEmplace<std::nullptr_t> {
auto operator()(auto& result, const json& value) const -> bool {
auto operator()(auto& result, json& value) const -> bool {
if (!value.is_null()) return false;
result.template emplace<std::nullptr_t>(nullptr);
return true;
Expand All @@ -86,7 +87,7 @@ struct TryEmplace<std::nullptr_t> {

template <>
struct TryEmplace<bool> {
auto operator()(auto& result, const json& value) const -> bool {
auto operator()(auto& result, json& value) const -> bool {
if (!value.is_boolean()) return false;
result.template emplace<bool>(value);
return true;
Expand All @@ -95,7 +96,7 @@ struct TryEmplace<bool> {

template <>
struct TryEmplace<int> {
auto operator()(auto& result, const json& value) const -> bool {
auto operator()(auto& result, json& value) const -> bool {
if (!value.is_number_integer()) return false;
result.template emplace<int>(value);
return true;
Expand All @@ -104,7 +105,7 @@ struct TryEmplace<int> {

template <>
struct TryEmplace<long> {
auto operator()(auto& result, const json& value) const -> bool {
auto operator()(auto& result, json& value) const -> bool {
if (!value.is_number_integer()) return false;
result.template emplace<long>(value);
return true;
Expand All @@ -113,7 +114,7 @@ struct TryEmplace<long> {

template <>
struct TryEmplace<double> {
auto operator()(auto& result, const json& value) const -> bool {
auto operator()(auto& result, json& value) const -> bool {
if (!value.is_number_float()) return false;
result.template emplace<double>(value);
return true;
Expand All @@ -122,7 +123,7 @@ struct TryEmplace<double> {

template <>
struct TryEmplace<std::string> {
auto operator()(auto& result, const json& value) const -> bool {
auto operator()(auto& result, json& value) const -> bool {
if (!value.is_string()) return false;
result.template emplace<std::string>(value);
return true;
Expand All @@ -131,31 +132,32 @@ struct TryEmplace<std::string> {

template <typename... Ts>
struct TryEmplace<std::variant<Ts...>> {
auto operator()(auto& result, const json& value) const -> bool {
auto operator()(auto& result, json& value) const -> bool {
return try_emplace(result, value);
}
};

template <typename... Ts>
struct TryEmplace<std::tuple<Ts...>> {
auto operator()(auto& result, const json& value) const -> bool {
auto operator()(auto& result, json& value) const -> bool {
lsp_runtime_error("todo: TryEmplace<std::tuple<Ts...>>");
return false;
}
};

template <>
struct TryEmplace<json> {
auto operator()(auto& result, const json& value) const -> bool {
auto operator()(auto& result, json& value) const -> bool {
result = value;
return true;
}
};

template <>
struct TryEmplace<TextDocumentSyncKind> {
auto operator()(auto& result, const json& value) const -> bool {
lsp_runtime_error("todo: TextDocumentSyncKind<json>");
auto operator()(auto& result, json& value) const -> bool {
if (!value.is_number_integer()) return false;
result = TextDocumentSyncKind(value.get<int>());
return true;
}
};
Expand All @@ -167,12 +169,12 @@ class Vector<std::variant<Ts...>> final : public LSPObject {
public:
using LSPObject::LSPObject;

[[nodiscard]] explicit operator bool() const { return repr_.is_array(); }
[[nodiscard]] auto size() const -> std::size_t { return repr_.size(); }
[[nodiscard]] auto empty() const -> bool { return repr_.empty(); }
[[nodiscard]] explicit operator bool() const { return repr_->is_array(); }
[[nodiscard]] auto size() const -> std::size_t { return repr_->size(); }
[[nodiscard]] auto empty() const -> bool { return repr_->empty(); }
[[nodiscard]] auto at(int index) const -> std::variant<Ts...> {
std::variant<Ts...> result;
details::try_emplace(result, repr_[index]);
details::try_emplace(result, repr_->at(index));
return result;
}
};
Expand All @@ -182,24 +184,24 @@ class Map final : public LSPObject {
public:
using LSPObject::LSPObject;

[[nodiscard]] explicit operator bool() const { return repr_.is_object(); }
[[nodiscard]] auto size() const -> std::size_t { return repr_.size(); }
[[nodiscard]] auto empty() const -> bool { return repr_.empty(); }
[[nodiscard]] auto at(const Key& key) const -> const Value& { return repr_[key]; }
[[nodiscard]] explicit operator bool() const { return repr_->is_object(); }
[[nodiscard]] auto size() const -> std::size_t { return repr_->size(); }
[[nodiscard]] auto empty() const -> bool { return repr_->empty(); }
[[nodiscard]] auto at(const Key& key) const -> const Value& { return repr_->at(key); }
};

template <typename Key, typename... Ts>
class Map<Key, std::variant<Ts...>> final : public LSPObject {
public:
using LSPObject::LSPObject;

[[nodiscard]] explicit operator bool() const { return repr_.is_object(); }
[[nodiscard]] auto size() const -> std::size_t { return repr_.size(); }
[[nodiscard]] auto empty() const -> bool { return repr_.empty(); }
[[nodiscard]] explicit operator bool() const { return repr_->is_object(); }
[[nodiscard]] auto size() const -> std::size_t { return repr_->size(); }
[[nodiscard]] auto empty() const -> bool { return repr_->empty(); }

[[nodiscard]] auto at(const Key& key) const -> std::variant<Ts...> {
std::variant<Ts...> result;
details::try_emplace(result, repr_[key]);
details::try_emplace(result, repr_->at(key));
return result;
}
};
Expand Down
126 changes: 114 additions & 12 deletions packages/cxx-gen-lsp/src/gen_types_cc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,6 @@ class TypeGenerator {
}

generateGetters({ structure, properties }: { structure: Structure; properties: Property[] }) {
const typeName = structure.name;

properties.forEach((property) => {
this.beginPropertyGetter({ structure, property });
this.generatePropertyGetter({ structure, property });
Expand All @@ -125,15 +123,15 @@ class TypeGenerator {
generateValidator({ structure, properties }: { structure: Structure; properties: Property[] }) {
this.emit();
this.emit(`${structure.name}::operator bool() const {`);
this.emit(`if (!repr_.is_object() || repr_.is_null()) return false;`);
this.emit(`if (!repr_->is_object() || repr_->is_null()) return false;`);

const requiredProperties = properties.filter((p) => !p.optional);

requiredProperties.forEach(({ name, type }) => {
this.emit(`if (!repr_.contains("${name}")) return false;`);
this.emit(`if (!repr_->contains("${name}")) return false;`);

if (type.kind === "stringLiteral") {
this.emit(`if (repr_["${name}"] != "${type.value}")`);
this.emit(`if ((*repr_)["${name}"] != "${type.value}")`);
this.emit(` return false;`);
}
});
Expand All @@ -149,11 +147,11 @@ class TypeGenerator {
this.emit(`auto ${structure.name}::${property.name}() const -> ${returnType} {`);

if (property.optional) {
this.emit(`if (!repr_.contains("${property.name}")) return std::nullopt;`);
this.emit(`if (!repr_->contains("${property.name}")) return std::nullopt;`);
this.emit();
}

this.emit(`const auto& value = repr_["${property.name}"];`);
this.emit(`auto& value = (*repr_)["${property.name}"];`);
this.emit();
}

Expand Down Expand Up @@ -216,7 +214,7 @@ class TypeGenerator {
this.emit(`lsp_runtime_error("${structure.name}::${property.name}: not implement yet");`);
}

generatePropertyGetterBase({ structure, property }: { structure: Structure; property: Property }): boolean {
generatePropertyGetterBase({ property }: { structure: Structure; property: Property }): boolean {
if (property.type.kind !== "base") return false;

switch (property.type.name) {
Expand Down Expand Up @@ -323,13 +321,117 @@ class TypeGenerator {
generateSetters({ structure, properties }: { structure: Structure; properties: Property[] }) {
const typeName = structure.name;

properties.forEach(({ name, type, optional }) => {
const argumentType = this.getPropertyType({ type, optional });
properties.forEach((property) => {
const argumentType = this.getPropertyType(property);
this.emit();
this.emit(`auto ${typeName}::${name}(${argumentType} ${name})`);
this.emit(`-> ${typeName}& { return *this; }`);
this.emit(`auto ${typeName}::${property.name}(${argumentType} ${property.name})`);
this.emit(`-> ${typeName}& {`);

if (property.optional) {
this.emit(`if (!${property.name}.has_value()) {`);
this.emit(`repr_->erase("${property.name}");`);
this.emit(`return *this;`);
this.emit(`}`);
}

this.generatePropertySetter({ property, structure });

this.emit(`return *this;`);
this.emit(`}`);
});
}

private generatePropertySetter({ property, structure }: { property: Property; structure: Structure }): void {
const typeName = structure.name;
const value = property.optional ? `${property.name}.value()` : property.name;

switch (property.type.kind) {
case "base":
this.emit(`repr_->emplace("${property.name}", std::move(${value}));`);
return;

case "reference":
if (!this.generatePropertySetterReference({ structure, property, value })) break;
return;

case "or":
if (!this.generatePropertySetterOr({ structure, property, value })) break;
return;

default:
break;
} // switch

this.emit(`lsp_runtime_error("${typeName}::${property.name}: not implement yet");`);
}

generatePropertySetterReference({
property,
value,
}: {
structure: Structure;
property: Property;
value: string;
}): boolean {
if (property.type.kind !== "reference") return false;

if (this.enumByName.has(property.type.name)) {
const enumeration = this.enumByName.get(property.type.name)!;

if (enumeration.type.name !== "string") {
const enumBaseType = toCppType(enumeration.type);
this.emit(`repr_->emplace("${property.name}", static_cast<${enumBaseType}>(${value}));`);
return true;
}

// TODO: string-like enumeration
return false;
}

if (this.structByName.has(property.type.name)) {
this.emit(`repr_->emplace("${property.name}", ${value});`);
return true;
}

return false;
}

generatePropertySetterOr({
structure,
property,
value,
}: {
structure: Structure;
property: Property;
value: string;
}): boolean {
if (property.type.kind !== "or") return false;

this.emit();
this.emit("// or type");
this.emit();
this.emit(`struct {`);
this.emit(`json* repr_;`);
this.emit();
this.emit(`void operator()(std::monostate) {`);
this.emit(`lsp_runtime_error("monostate is not a valid a property value");`);
this.emit(`}`);

property.type.items.forEach((item) => {
const itemType = this.getPropertyType({ type: item });
this.emit();
this.emit(`void operator()(${itemType} ${property.name}) {`);
this.generatePropertySetter({ property: { name: property.name, type: item, optional: false }, structure });
this.emit(`}`);
});

this.emit(`} v{repr_};`);
this.emit();
this.emit(`std::visit(v, ${value});`);
this.emit();

return true;
}
}

export function gen_types_cc({ model, outputDirectory }: { model: MetaModel; outputDirectory: string }) {
Expand Down
2 changes: 2 additions & 0 deletions src/lsp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ if (EMSCRIPTEN)
target_compile_options(cxx-lsp PUBLIC -fno-exceptions)
endif()

add_subdirectory(tests)

if(CXX_INSTALL_LSP)

install(
Expand Down
Loading