#include "CangjieWriter.h"
#include <fstream>
#include <iostream>
#include <set>
#include <sstream>
#include "Logging.h"
#include "Mode.h"
#include "Package.h"
#include "SingleDeclarationSymbolVisitor.h"
#include "Strings.h"
#include "Universe.h"
namespace objcgen {
static constexpr char INDENT[] = " ";
static constexpr std::size_t INDENT_LENGTH = sizeof(INDENT) - 1;
static_assert(INDENT_LENGTH == 4);
constexpr char COMMENT[] = "// ";
constexpr auto COMMENT_LENGTH = sizeof(COMMENT) - 1;
class IndentingStringBuf final : public std::streambuf {
public:
IndentingStringBuf() : buf_(std::ios_base::out)
{
}
void indent() noexcept
{
indentation_ += INDENT;
}
void dedent() noexcept
{
assert(ends_with(indentation_, INDENT));
indentation_.resize(indentation_.size() - INDENT_LENGTH);
}
void set_comment() noexcept
{
indentation_ += COMMENT;
}
void reset_comment() noexcept
{
assert(ends_with(indentation_, COMMENT));
indentation_.resize(indentation_.size() - COMMENT_LENGTH);
}
std::string str() const
{
return buf_.str();
}
protected:
int_type overflow(const int_type ch) override
{
if (start_line_) {
auto count = static_cast<std::streamsize>(indentation_.length());
if (buf_.sputn(indentation_.data(), count) != count) {
return traits_type::eof();
}
}
start_line_ = ch == '\n';
return buf_.sputc(traits_type::to_char_type(ch));
}
private:
std::stringbuf buf_;
* Indentation spaces printed currently at the beginning of each line.
* Includes `//` comments, if any.
*/
std::string indentation_;
bool start_line_ = true;
};
class IndentingStringStream : public std::ostream {
public:
IndentingStringStream() : std::ostream(&fos_buf)
{
}
std::string str() const
{
return fos_buf.str();
}
void indent() noexcept
{
fos_buf.indent();
}
void dedent() noexcept
{
fos_buf.dedent();
}
void set_comment() noexcept
{
fos_buf.set_comment();
}
void reset_comment() noexcept
{
fos_buf.reset_comment();
}
private:
IndentingStringBuf fos_buf;
};
static std::string_view current_package_name;
static std::set<std::string> imports;
class PackageFileScope final {
const std::string_view package_name_;
public:
[[nodiscard]] explicit PackageFileScope(std::string_view package_name) noexcept : package_name_(package_name)
{
assert(current_package_name.empty());
assert(!package_name.empty());
assert(imports.empty());
current_package_name = package_name;
}
~PackageFileScope()
{
assert(current_package_name == package_name_);
current_package_name = {};
imports.clear();
}
PackageFileScope(const PackageFileScope& other) = delete;
PackageFileScope(PackageFileScope&& other) noexcept = delete;
PackageFileScope& operator=(const PackageFileScope& other) = delete;
PackageFileScope& operator=(PackageFileScope&& other) noexcept = delete;
};
static std::string symbol_to_import_name(const FileLevelSymbol& symbol)
{
assert(!current_package_name.empty());
const auto& symbol_package_name = symbol.cangjie_package_name();
if (!symbol_package_name.empty() && symbol_package_name != current_package_name) {
auto import_name = symbol_package_name;
import_name += ".";
import_name += symbol.name();
return import_name;
}
return {};
}
class ImportCollectVisitor final : public SingleDeclarationSymbolVisitor {
public:
[[nodiscard]] explicit ImportCollectVisitor() : SingleDeclarationSymbolVisitor(false)
{
}
ImportCollectVisitor(const ImportCollectVisitor& other) = delete;
ImportCollectVisitor(ImportCollectVisitor&& other) noexcept = delete;
ImportCollectVisitor& operator=(const ImportCollectVisitor& other) = delete;
ImportCollectVisitor& operator=(ImportCollectVisitor&& other) noexcept = delete;
protected:
void visit_impl(FileLevelSymbol* owner, FileLevelSymbol* value, SymbolProperty symbol_property, bool) override
{
assert(value);
if (dynamic_cast<NamedTypeSymbol*>(owner) && symbol_property == SymbolProperty::TypeArgument) {
return;
}
auto* constructed_type = dynamic_cast<ConstructedTypeSymbol*>(value);
if (constructed_type) {
value = constructed_type->original();
assert(value);
}
if (auto import_name = symbol_to_import_name(*value); !import_name.empty()) {
imports.emplace(std::move(import_name));
}
}
};
static void collect_import(TypeLikeSymbol& symbol)
{
ImportCollectVisitor().visit(&symbol);
}
static bool is_objc_compatible(TypeLikeSymbol& type)
{
assert(normal_mode());
if (&type == &Universe::get().sel()) {
return false;
}
if (dynamic_cast<const TypeParameterSymbol*>(&type)) {
return true;
}
const auto* ptr = dynamic_cast<const PointerTypeSymbol*>(&type);
if (ptr) {
return is_objc_compatible(ptr->pointee());
}
const auto* func = dynamic_cast<const FuncLikeTypeSymbol*>(&type);
if (func) {
const auto& parameters = *func->parameters();
auto n = parameters.item_count();
for (size_t i = 0; i < n; ++i) {
if (!is_objc_compatible(*parameters.item(i))) {
return false;
}
}
auto* return_type = func->return_type();
return !return_type || is_objc_compatible(*return_type);
}
const auto* named = dynamic_cast<const NamedTypeSymbol*>(&type);
if (!named) {
return false;
}
switch (named->kind()) {
case NamedTypeSymbol::Kind::Struct:
case NamedTypeSymbol::Kind::Union:
return type.is_ctype();
case NamedTypeSymbol::Kind::Interface:
return type.name() != "Protocol";
case NamedTypeSymbol::Kind::Primitive:
case NamedTypeSymbol::Kind::Protocol:
case NamedTypeSymbol::Kind::Enum:
return true;
case NamedTypeSymbol::Kind::TypeDef:
return is_objc_compatible(type.canonical_type());
default:
return false;
}
}
static bool write_type_alias(IndentingStringStream& output, TypeAliasSymbol& alias)
{
auto* target = alias.target();
assert(target);
if (alias.name() == target->name()) {
return false;
}
auto supported = !normal_mode() || alias.is_ctype() || is_objc_compatible(alias);
if (supported) {
collect_import(*target);
} else {
output.set_comment();
}
output << "public type " << emit_cangjie(alias) << " = " << emit_cangjie(*target) << '\n';
if (!supported) {
output.reset_comment();
}
return true;
}
class DefaultValuePrinter;
static std::ostream& operator<<(std::ostream& stream, const DefaultValuePrinter& op);
class DefaultValuePrinter {
public:
explicit DefaultValuePrinter(const NonTypeSymbol& symbol, SymbolPrinter type_printer) noexcept
: symbol_(symbol), type_printer_(type_printer)
{
}
const auto& symbol() const noexcept
{
return symbol_;
}
const auto& type_printer() const noexcept
{
return type_printer_;
}
private:
const NonTypeSymbol& symbol_;
const SymbolPrinter type_printer_;
};
static DefaultValuePrinter default_value(const NonTypeSymbol& symbol, TypeLikeSymbol& type, SymbolPrintFormat format)
{
return DefaultValuePrinter(symbol, SymbolPrinter(type, format));
}
static std::ostream& operator<<(std::ostream& stream, const DefaultValuePrinter& op)
{
const auto& type_printer = op.type_printer();
assert(dynamic_cast<const TypeLikeSymbol*>(&type_printer.symbol()));
auto& type = static_cast<TypeLikeSymbol&>(type_printer.symbol());
type.print_default_value(stream, op.symbol(), type_printer.format(), type);
return stream;
}
static void print_enum_constant_value(
std::ostream& output, TypeLikeSymbol& underlying_type, const EnumConstantSymbol& constant)
{
const auto& canonical_type = underlying_type.canonical_type();
const auto* primitive_type = dynamic_cast<const PrimitiveTypeSymbol*>(&canonical_type);
if (primitive_type) {
switch (primitive_type->category()) {
case PrimitiveTypeCategory::SignedInteger:
switch (primitive_type->size()) {
case PrimitiveSize::One:
output << static_cast<int>(constant.value<int8_t>());
return;
case PrimitiveSize::Two:
output << constant.value<int16_t>();
return;
case PrimitiveSize::Four:
output << constant.value<int32_t>();
return;
case PrimitiveSize::Eight:
output << constant.value<int64_t>();
return;
default:
break;
}
break;
case PrimitiveTypeCategory::UnsignedInteger:
switch (primitive_type->size()) {
case PrimitiveSize::One:
output << static_cast<uint32_t>(constant.value<uint8_t>());
return;
case PrimitiveSize::Two:
output << constant.value<uint16_t>();
return;
case PrimitiveSize::Four:
output << constant.value<uint32_t>();
return;
default:
break;
}
break;
default:
break;
}
} else {
auto& universe = Universe::get();
if (&canonical_type == &universe.int128()) {
output << '[' << constant.value128_lo<int64_t>() << ", " << constant.value128_hi<int64_t>() << ']';
return;
}
if (&canonical_type == &universe.uint128()) {
output << '[' << constant.value128_lo<uint64_t>() << ", " << constant.value128_hi<uint64_t>() << ']';
return;
}
}
output << constant.value<uint64_t>();
}
static bool is_objc_compatible_parameters(NonTypeSymbol& method) noexcept
{
for (const auto& parameter : method.parameters()) {
if (!is_objc_compatible(*parameter.type())) {
return false;
}
}
return true;
}
template <class Symbol>
void write_type(std::ostream& output, const Symbol& symbol, TypeLikeSymbol& type, SymbolPrintFormat format)
{
output << ": ";
if (symbol.is_nullable()) {
output << '?';
}
output << SymbolPrinter(type, format);
}
static void write_method_parameters(std::ostream& output, const NonTypeSymbol& method, SymbolPrintFormat format)
{
output << '(';
auto parameter_count = method.parameter_count();
for (std::size_t j = 0; j < parameter_count; ++j) {
if (j != 0) {
output << ", ";
}
auto& parameter_symbol = method.parameter(j);
auto* parameter_type = parameter_symbol.type();
assert(parameter_type);
output << escape_keyword(parameter_symbol.name());
write_type(output, parameter_symbol, *parameter_type, format);
collect_import(*parameter_type);
}
output << ')';
}
static void write_foreign_name(
std::ostream& output, std::string_view attribute, std::string_view value, bool hide_foreign_name)
{
if (hide_foreign_name) {
output << "/* ";
}
output << attribute << "[\"" << value << "\"]";
if (hide_foreign_name) {
output << " */";
}
output << ' ';
}
static void write_foreign_name(std::ostream& output, const NonTypeSymbol& method)
{
if (method.is_override()) {
return;
}
constexpr std::string_view attribute = "@ForeignName";
const auto& selector_attribute = method.selector_attribute();
if (!selector_attribute.empty()) {
write_foreign_name(output, attribute, selector_attribute, generate_definitions_mode());
} else if (method.is_constructor() && method.name() != "init") {
write_foreign_name(output, attribute, method.name(), generate_definitions_mode());
}
}
static bool same_types(TypeLikeSymbol* type1, TypeLikeSymbol* type2)
{
auto* alias = dynamic_cast<TypeAliasSymbol*>(type1);
if (alias) {
type1 = &alias->canonical_type();
}
alias = dynamic_cast<TypeAliasSymbol*>(type2);
if (alias) {
type2 = &alias->canonical_type();
}
auto* constructed1 = dynamic_cast<ConstructedTypeSymbol*>(type1);
if (constructed1) {
auto* constructed2 = dynamic_cast<ConstructedTypeSymbol*>(type2);
return constructed2 && same_types(constructed1->original(), constructed2->original());
}
const auto* pointer1 = dynamic_cast<const PointerTypeSymbol*>(type1);
if (pointer1) {
auto* pointer2 = dynamic_cast<const PointerTypeSymbol*>(type2);
return pointer2 && same_types(&pointer1->pointee(), &pointer2->pointee());
}
const auto* func1 = dynamic_cast<const FuncLikeTypeSymbol*>(type1);
if (func1) {
auto* func2 = dynamic_cast<const FuncTypeSymbol*>(type2);
if (!func2 || !same_types(func1->return_type(), func2->return_type())) {
return false;
}
const auto& parameters1 = *func1->parameters();
auto n1 = parameters1.item_count();
const auto& parameters2 = *func2->parameters();
auto n2 = parameters2.item_count();
if (n1 != n2) {
return false;
}
for (size_t i = 0; i < n1; ++i) {
if (!same_types(parameters1.item(i), parameters2.item(i))) {
return false;
}
}
return true;
}
auto* varray1 = dynamic_cast<VArraySymbol*>(type1);
if (varray1) {
auto* varray2 = dynamic_cast<VArraySymbol*>(type2);
return varray2 && varray1->size() == varray2->size() &&
same_types(&varray1->element_type(), &varray2->element_type());
}
return type1 == type2;
}
static bool is_overloading_constructor(TypeDeclarationSymbol& type, const NonTypeSymbol& constructor)
{
assert(constructor.is_constructor());
auto parameter_count = constructor.parameter_count();
for (const auto& member : type.members()) {
if (&member == &constructor || !member.is_constructor() || member.parameter_count() != parameter_count) {
continue;
}
auto overloading = true;
for (size_t i = 0; i < parameter_count; ++i) {
if (!same_types(member.parameter(i).type(), constructor.parameter(i).type())) {
overloading = false;
break;
}
}
if (overloading) {
return true;
}
}
return false;
}
static NonTypeSymbol* get_overridden_method(TypeDeclarationSymbol& decl, const NonTypeSymbol& prop)
{
const auto& selector = prop.getter();
auto is_static = prop.is_static();
for (auto& base_decl : decl.bases()) {
for (auto& member : base_decl.members()) {
if (member.is_member_method() && member.is_static() == is_static && member.selector() == selector) {
return &member;
}
}
auto* overridden_method = get_overridden_method(base_decl, prop);
if (overridden_method) {
return overridden_method;
}
}
return nullptr;
}
static NonTypeSymbol* get_property(TypeDeclarationSymbol& decl, const NonTypeSymbol& getter_or_setter)
{
const auto& selector = getter_or_setter.selector();
auto is_static = getter_or_setter.is_static();
for (auto& member : decl.members()) {
if (member.is_property() && member.is_static() == is_static &&
(member.getter() == selector || member.setter() == selector)) {
return &member;
}
}
return nullptr;
}
static NonTypeSymbol* get_method_by_selector(TypeDeclarationSymbol& decl, const std::string& selector, bool is_static)
{
for (auto& member : decl.members()) {
if (member.is_member_method() && member.is_static() == is_static && member.selector() == selector) {
return &member;
}
}
return nullptr;
}
static NonTypeSymbol* get_overridden_property(TypeDeclarationSymbol& decl, const std::string& getter, bool is_static)
{
for (auto& base_decl : decl.bases()) {
for (auto& member : base_decl.members()) {
if (member.is_property() && member.is_static() == is_static && member.getter() == getter) {
return &member;
}
}
auto* overridden_prop = get_overridden_property(base_decl, getter, is_static);
if (overridden_prop) {
return overridden_prop;
}
}
return nullptr;
}
static void print_optional(std::ostream& output, const NonTypeSymbol& member)
{
if (member.is_optional()) {
if (member.is_property() && normal_mode()) {
return;
}
if (generate_definitions_mode()) {
output << "// ";
}
output << "@ObjCOptional\n";
}
}
static bool is_standard_setter_name(std::string_view prop_name, std::string_view setter_name)
{
assert(!prop_name.empty());
constexpr char standard_setter_prefix[] = "set";
constexpr auto standard_setter_prefix_size = std::size(standard_setter_prefix) - 1;
return setter_name.size() > standard_setter_prefix_size + 1 && setter_name.back() == ':' &&
starts_with(setter_name, standard_setter_prefix) &&
setter_name[standard_setter_prefix_size] == static_cast<char>(toupper(prop_name.front())) &&
setter_name.substr(standard_setter_prefix_size + 1, prop_name.size() - 1) == prop_name.substr(1);
}
static void print_getter_setter_names(std::ostream& output, const NonTypeSymbol& prop)
{
assert(prop.kind() == NonTypeSymbol::Kind::Property);
const auto& name = prop.name();
const auto& getter_name = prop.getter();
bool standard_getter = getter_name == name;
auto hide_foreign_name = generate_definitions_mode();
if (prop.is_readonly()) {
if (!standard_getter) {
write_foreign_name(output, "@ForeignGetterName", getter_name, hide_foreign_name);
}
} else {
const auto& setter_name = prop.setter();
if (is_standard_setter_name(name, setter_name)) {
if (!standard_getter) {
write_foreign_name(output, "@ForeignGetterName", getter_name, hide_foreign_name);
}
} else if (standard_getter) {
write_foreign_name(output, "@ForeignSetterName", setter_name, hide_foreign_name);
} else if (is_standard_setter_name(getter_name, setter_name)) {
write_foreign_name(output, "@ForeignName", getter_name, hide_foreign_name);
} else {
write_foreign_name(output, "@ForeignGetterName", getter_name, hide_foreign_name);
write_foreign_name(output, "@ForeignSetterName", setter_name, hide_foreign_name);
}
}
}
[[nodiscard]] static SymbolPrintFormat write_top_level_func_attribs(
std::ostream& output, NonTypeSymbol& function, SymbolPrintFormat format, bool is_ctype)
{
const auto& selector_attribute = function.selector_attribute();
if (is_ctype) {
output << "foreign ";
if (!selector_attribute.empty()) {
write_foreign_name(output, "@ForeignName", selector_attribute, true);
}
} else {
auto generate_definitions = generate_definitions_mode();
if (!generate_definitions) {
output << "@ObjCMirror\n";
format = SymbolPrintFormat::EmitCangjieStrict;
}
if (!selector_attribute.empty()) {
write_foreign_name(output, "@ForeignName", selector_attribute, generate_definitions);
}
output << "public ";
}
return format;
}
enum class FuncKind { TopLevelFunc, InterfaceMethod, ClassMethod };
static void write_function(
IndentingStringStream& output, FuncKind kind, NonTypeSymbol& function, SymbolPrintFormat format)
{
auto* return_type = function.return_type();
assert(return_type);
auto supported = !normal_mode() || (is_objc_compatible(*return_type) && is_objc_compatible_parameters(function));
if (!supported) {
output.set_comment();
}
bool is_ctype;
switch (kind) {
case FuncKind::TopLevelFunc: {
is_ctype = function.is_ctype();
format = write_top_level_func_attribs(output, function, format, is_ctype);
break;
}
case FuncKind::InterfaceMethod:
is_ctype = false;
print_optional(output, function);
write_foreign_name(output, function);
break;
default:
assert(kind == FuncKind::ClassMethod);
is_ctype = false;
write_foreign_name(output, function);
output << "public ";
break;
}
if (function.is_static()) {
if constexpr ((false)) {
if (function.is_override()) {
output << "redef ";
}
}
output << "static ";
} else {
if (kind == FuncKind::ClassMethod) {
output << "open ";
}
if constexpr ((false)) {
if (function.is_override()) {
output << "override ";
}
}
}
output << "func " << escape_keyword(function.name());
write_method_parameters(output, function, format);
write_type(output, function, *return_type, format);
if (generate_definitions_mode() && !is_ctype) {
if (return_type->is_unit()) {
output << " { }";
} else {
output << " { " << default_value(function, *return_type, format) << " }";
}
}
if (supported) {
collect_import(*return_type);
} else {
output.reset_comment();
}
output << '\n';
}
enum class DeclKind { CStruct, ObjCStruct, Interface, Class };
static bool is_field_type_supported(DeclKind decl_kind, TypeLikeSymbol& type, const std::string& name)
{
if (!normal_mode()) {
return true;
}
switch (decl_kind) {
case DeclKind::CStruct:
assert(type.is_ctype());
return true;
case DeclKind::ObjCStruct:
return true;
default:
assert(decl_kind == DeclKind::Class || decl_kind == DeclKind::Interface);
return name != type.name() && is_objc_compatible(type);
}
}
static void print_objcmirror_attribute(std::ostream& output, bool supported)
{
auto hide_objcmirror_attribute = !supported;
if (hide_objcmirror_attribute) {
output << "/* ";
}
output << "@ObjCMirror";
if (hide_objcmirror_attribute) {
output << " */";
}
output << '\n';
}
class TypeDeclarationWriter {
public:
TypeDeclarationWriter(IndentingStringStream& output, TypeDeclarationSymbol& type) noexcept;
void write();
private:
void write_property(const NonTypeSymbol& prop);
void write_constructor(NonTypeSymbol& constructor);
void write_instance_variable(const NonTypeSymbol& ivar);
void write_field(const NonTypeSymbol& field);
IndentingStringStream& output_;
TypeDeclarationSymbol& type_;
DeclKind decl_kind_;
SymbolPrintFormat format_;
bool any_constructor_exists_;
bool default_constructor_exists_;
};
TypeDeclarationWriter::TypeDeclarationWriter(IndentingStringStream& output, TypeDeclarationSymbol& type) noexcept
: output_(output), type_(type), any_constructor_exists_(false), default_constructor_exists_(false)
{
}
void TypeDeclarationWriter::write_property(const NonTypeSymbol& prop)
{
assert(prop.is_property());
auto is_static = prop.is_static();
const auto& getter_name = prop.getter();
auto* getter = get_method_by_selector(type_, getter_name, is_static);
assert(getter);
if (!get_overridden_method(type_, prop) && !get_overridden_property(type_, getter_name, is_static)) {
auto* return_type = getter->return_type();
assert(return_type);
assert(!return_type->is_unit());
const auto& name = prop.name();
auto supported = is_field_type_supported(decl_kind_, *return_type, name);
if (!supported) {
output_.set_comment();
}
assert(!prop.is_optional() || decl_kind_ == DeclKind::Interface);
if (decl_kind_ == DeclKind::Interface) {
print_optional(output_, prop);
}
print_getter_setter_names(output_, prop);
if (decl_kind_ != DeclKind::Interface) {
output_ << "public ";
}
if (is_static) {
output_ << "static ";
} else if (decl_kind_ != DeclKind::Interface) {
output_ << "open ";
}
if (!prop.is_readonly()) {
output_ << "mut ";
}
output_ << "prop " << escape_keyword(name);
write_type(output_, *getter, *return_type, format_);
if (generate_definitions_mode()) {
output_ << " {\n";
output_.indent();
output_ << "get() { " << default_value(*getter, *return_type, format_) << " }\n";
if (!prop.is_readonly()) {
output_ << "set(v) { }\n";
}
output_.dedent();
output_ << '}';
}
if (supported) {
collect_import(*return_type);
} else {
output_.reset_comment();
}
output_ << '\n';
}
}
void TypeDeclarationWriter::write_constructor(NonTypeSymbol& constructor)
{
assert(constructor.is_constructor());
auto supported =
decl_kind_ != DeclKind::Interface && (!normal_mode() || is_objc_compatible_parameters(constructor));
if (supported) {
any_constructor_exists_ = true;
if (!default_constructor_exists_) {
default_constructor_exists_ = constructor.parameter_count() == 0;
}
} else {
output_.set_comment();
}
if (is_overloading_constructor(type_, constructor)) {
if (!generate_definitions_mode()) {
output_ << "@ObjCInit ";
}
write_foreign_name(output_, constructor);
if (decl_kind_ != DeclKind::Interface) {
output_ << "public ";
}
output_ << "static func " << escape_keyword(constructor.name());
write_method_parameters(output_, constructor, format_);
auto* return_type = normal_mode() ? &type_ : constructor.return_type();
assert(return_type);
write_type(output_, constructor, *return_type, format_);
if (generate_definitions_mode() && decl_kind_ != DeclKind::Interface) {
output_ << " { " << default_value(constructor, *return_type, format_) << " }";
}
if (supported) {
collect_import(*return_type);
}
} else {
write_foreign_name(output_, constructor);
if (decl_kind_ != DeclKind::Interface) {
output_ << "public ";
}
output_ << "init";
write_method_parameters(output_, constructor, format_);
if (generate_definitions_mode() && decl_kind_ != DeclKind::Interface) {
output_ << " { }";
}
}
if (!supported) {
output_.reset_comment();
}
output_ << '\n';
}
void TypeDeclarationWriter::write_instance_variable(const NonTypeSymbol& ivar)
{
assert(ivar.is_instance_variable());
assert(ivar.is_instance());
auto* return_type = ivar.return_type();
assert(return_type);
assert(!return_type->is_unit());
assert(ivar.is_public() || ivar.is_protected());
const auto& name = ivar.name();
auto supported = is_field_type_supported(decl_kind_, *return_type, name);
if (!supported) {
output_.set_comment();
}
output_ << (ivar.is_public() ? "public" : "protected") << " var " << escape_keyword(name);
write_type(output_, ivar, *return_type, format_);
if (generate_definitions_mode()) {
output_ << " = " << default_value(ivar, *return_type, format_);
}
if (supported) {
collect_import(*return_type);
} else {
output_.reset_comment();
}
output_ << '\n';
}
void TypeDeclarationWriter::write_field(const NonTypeSymbol& field)
{
assert(field.is_field());
assert(field.is_instance());
if (field.is_bit_field() && field.name().empty()) {
return;
}
auto* return_type = field.return_type();
assert(return_type);
assert(!return_type->is_unit());
const auto& name = field.name();
auto supported = is_field_type_supported(decl_kind_, *return_type, name);
if (!supported) {
output_.set_comment();
}
output_ << "public var " << escape_keyword(name);
write_type(output_, field, *return_type, format_);
if (mode != Mode::EXPERIMENTAL) {
output_ << " = " << default_value(field, *return_type, format_);
}
if (supported) {
collect_import(*return_type);
} else {
output_.reset_comment();
}
output_ << '\n';
}
void TypeDeclarationWriter::write()
{
switch (type_.kind()) {
case NamedTypeSymbol::Kind::Protocol:
decl_kind_ = DeclKind::Interface;
format_ = SymbolPrintFormat::EmitCangjieStrict;
print_objcmirror_attribute(output_, !generate_definitions_mode());
break;
case NamedTypeSymbol::Kind::Struct:
case NamedTypeSymbol::Kind::Union:
if (type_.is_ctype()) {
decl_kind_ = DeclKind::CStruct;
format_ = SymbolPrintFormat::EmitCangjie;
output_ << "@C\n";
} else {
decl_kind_ = DeclKind::ObjCStruct;
format_ = SymbolPrintFormat::EmitCangjie;
print_objcmirror_attribute(output_, mode == Mode::EXPERIMENTAL);
}
break;
default:
assert(type_.kind() == NamedTypeSymbol::Kind::Interface);
decl_kind_ = DeclKind::Class;
format_ = SymbolPrintFormat::EmitCangjieStrict;
print_objcmirror_attribute(output_, !generate_definitions_mode());
break;
}
output_ << "public ";
switch (decl_kind_) {
case DeclKind::Interface:
output_ << "interface";
break;
case DeclKind::CStruct:
case DeclKind::ObjCStruct:
output_ << "struct";
break;
default:
assert(decl_kind_ == DeclKind::Class);
output_ << "open class";
break;
}
output_ << " " << escape_keyword(type_.name());
if (const auto parameter_count = type_.parameter_count()) {
output_ << "/*<";
for (std::size_t i = 0; i < parameter_count; ++i) {
if (i != 0) {
output_ << ", ";
}
auto* parameter = type_.parameter(i);
assert(parameter);
output_ << parameter->name();
}
output_ << ">*/";
}
const auto base_count = type_.base_count();
if (base_count) {
output_ << " <: ";
auto* base = type_.base(0);
assert(base);
output_ << emit_cangjie(*base);
collect_import(*base);
for (std::size_t i = 1; i < base_count; ++i) {
output_ << " & ";
auto* base = type_.base(i);
assert(base);
output_ << emit_cangjie(*base);
collect_import(*base);
}
}
output_ << " {\n";
output_.indent();
for (auto&& member : type_.members()) {
if (member.is_property()) {
write_property(member);
} else if (member.is_constructor()) {
write_constructor(member);
} else if (member.is_member_method()) {
if (!get_property(type_, member) &&
!get_overridden_property(type_, member.selector(), member.is_static())) {
write_function(output_,
decl_kind_ == DeclKind::Interface ? FuncKind::InterfaceMethod : FuncKind::ClassMethod, member,
format_);
}
} else if (member.is_instance_variable()) {
write_instance_variable(member);
} else if (member.is_field()) {
write_field(member);
} else {
assert(false);
}
}
if (generate_definitions_mode() && any_constructor_exists_ && !default_constructor_exists_) {
output_ << "public init() { }";
}
output_.dedent();
output_ << "}" << std::endl;
}
static void write_enum_declaration(IndentingStringStream& output, EnumDeclarationSymbol& enum_decl)
{
auto format = SymbolPrintFormat::EmitCangjie;
auto& underlying_type = enum_decl.underlying_type();
collect_import(underlying_type);
output << "public type " << emit_cangjie(enum_decl) << " = " << emit_cangjie(underlying_type) << '\n';
enum_decl.for_each_constant([&output, format, &enum_decl, &underlying_type](const auto& constant) {
output << "public const " << escape_keyword(constant.name()) << ": "
<< SymbolPrinter(enum_decl, format) << " = ";
print_enum_constant_value(output, underlying_type, constant);
output << '\n';
});
}
static void add_package_dependencies(const FileLevelSymbol& symbol)
{
if (auto* package_file = symbol.package_file()) {
auto* edge_from = package_file->package();
assert(edge_from);
for (const auto* reference : symbol.references_symbols()) {
auto* edge_to = reference->package();
if (edge_to && edge_from != edge_to) {
edge_from->add_dependency_edge(edge_to);
}
}
}
}
void write_cangjie()
{
std::uint64_t generated_files = 0;
for (auto&& package : packages) {
for (auto&& package_file : package) {
assert(package_file.package() == &package);
PackageFileScope scope(package_file.package()->cangjie_name());
auto file_path = package_file.output_path();
create_directories(file_path.parent_path());
IndentingStringStream output;
for (auto* symbol : package_file) {
if (auto* alias = dynamic_cast<TypeAliasSymbol*>(symbol)) {
if (!write_type_alias(output, *alias)) {
continue;
}
} else if (auto* type = dynamic_cast<TypeDeclarationSymbol*>(symbol)) {
TypeDeclarationWriter(output, *type).write();
} else if (auto* enum_decl = dynamic_cast<EnumDeclarationSymbol*>(symbol)) {
write_enum_declaration(output, *enum_decl);
} else {
assert(dynamic_cast<NonTypeSymbol*>(symbol));
auto& top_level = static_cast<NonTypeSymbol&>(*symbol);
assert(top_level.kind() == NonTypeSymbol::Kind::GlobalFunction);
if (top_level.has_internal_linkage()) {
continue;
}
write_function(output, FuncKind::TopLevelFunc, top_level, SymbolPrintFormat::EmitCangjie);
}
output << std::endl;
}
std::ofstream file_output(file_path);
file_output << "// Generated by ObjCInteropGen" << std::endl;
file_output << std::endl;
file_output << "package " << package.cangjie_name() << std::endl;
file_output << std::endl;
for (auto&& import : imports) {
file_output << "import " << import << std::endl;
}
if (!generate_definitions_mode()) {
file_output << "import objc.lang.*\n\n";
}
file_output << output.str();
generated_files++;
}
}
if (generated_files == 0) {
std::cerr << "No output files are generated" << std::endl;
} else {
std::cout << "Generated " << generated_files << " files for " << packages.size() << " packages" << std::endl;
}
if (verbosity >= LogLevel::INFO) {
for (const auto* input_directory : inputs) {
for (const auto* input_file : *input_directory) {
for (const auto* symbol : *input_file) {
assert(symbol);
add_package_dependencies(*symbol);
}
}
}
for (auto&& package : packages) {
auto& depends_on = package.depends_on();
std::cout << "Package `" << package.cangjie_name() << "` depends on " << depends_on.size();
if (depends_on.size() == 1) {
auto* dependency = *depends_on.begin();
std::cout << " package: `" << dependency->cangjie_name() << "`" << std::endl;
} else if (depends_on.size() > 1) {
std::cout << " packages:" << std::endl;
for (auto* dependency : depends_on) {
std::cout << "* " << dependency->cangjie_name() << std::endl;
}
} else {
std::cout << " packages" << std::endl;
}
}
}
}
}