diff --git a/.gitignore b/.gitignore index 140a80eb0..68cda85e2 100644 --- a/.gitignore +++ b/.gitignore @@ -60,11 +60,13 @@ build*/ src/git_head.h backup/ -# LLVMprivateGenerator -/LLVMprivateGenerator/* -!/LLVMprivateGenerator/Makefile -!/LLVMprivateGenerator/main.cpp -!/LLVMprivateGenerator/registered_structs.cpp +# Wrapper helper +/wrapperhelper/bin +/wrapperhelper/makedir +/wrapperhelper/obj +/wrapperhelper/sanaddress +/wrapperhelper/sanleak +/wrapperhelper/sanundefined # macOS .DS_Store diff --git a/LLVMprivateGenerator/Makefile b/LLVMprivateGenerator/Makefile deleted file mode 100644 index 61864262b..000000000 --- a/LLVMprivateGenerator/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -all: dumpSigs - -dumpSigs: main.o registered_structs.o - g++ -g3 -std=gnu++17 -fno-rtti main.o registered_structs.o -o dumpSigs "-L$(LLVM_install_dir)/lib" -lclang-cpp -lclangTooling -Wl,-rpath "-Wl,$(LLVM_install_dir)/lib" - -main.o: main.cpp - g++ -g3 -std=gnu++17 -fno-rtti -c main.cpp -Wfatal-errors "-I$(LLVM_install_dir)/include" -o main.o -registered_structs.o: registered_structs.cpp - g++ -g3 -std=gnu++17 -fno-rtti -c registered_structs.cpp -Wfatal-errors -o registered_structs.o - -clean: - $(RM) dumpSigs main.o registered_structs.o diff --git a/LLVMprivateGenerator/main.cpp b/LLVMprivateGenerator/main.cpp deleted file mode 100644 index fe070cb07..000000000 --- a/LLVMprivateGenerator/main.cpp +++ /dev/null @@ -1,458 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -clang::MangleContext *mangler = nullptr; - -std::unordered_map> funMap; -std::vector funList; - -bool isTypeTrivial(const clang::QualType &q, const clang::QualType *qorig); -clang::QualType getPointedType(const clang::QualType q) { - if (const clang::PointerType *p = q->getAs()) - return getPointedType(p->getPointeeType()); - else if (const clang::ReferenceType *r = q->getAs()) - return getPointedType(r->getPointeeType()); - else - return q; -} - -std::string record2name(const clang::RecordDecl &q, const clang::QualType *qorig) { - std::string s = q.getNameAsString(); - if (s == "") { - if (!qorig) return "????.!"; - if (const clang::TypedefType *tt = (*qorig)->getAs()) { - // Typedef - if (clang::TypedefNameDecl *td = tt->getDecl()) { - return td->getNameAsString(); - } else { - return ""; - } - } else { - return std::string("getTypeClassName() + ">"; - } - } else { - return s; - } -} - -char ptr2char(const std::string &str) __attribute__((const)); -const char *ptr2str(const std::string &str) __attribute__((const)); -char type2char(const clang::QualType &qual /* Canonical */, const clang::QualType *qorig) { - if (qual->isBuiltinType()) { - switch (static_cast(*qual).getKind()) { - case clang::BuiltinType::Kind::Void: - return 'v'; - case clang::BuiltinType::Kind::Bool: - return 'i'; - case clang::BuiltinType::Kind::Char_U: - return 'C'; - case clang::BuiltinType::Kind::Char_S: - return 'c'; - case clang::BuiltinType::Kind::Char8: - return 'c'; - case clang::BuiltinType::Kind::UChar: - return 'C'; - case clang::BuiltinType::Kind::SChar: - return 'c'; - case clang::BuiltinType::Kind::WChar_U: - return 'W'; - case clang::BuiltinType::Kind::UShort: - return 'W'; - case clang::BuiltinType::Kind::WChar_S: - return 'w'; - case clang::BuiltinType::Kind::Char16: - return 'w'; - case clang::BuiltinType::Kind::Short: - return 'w'; - case clang::BuiltinType::Kind::UInt: - return 'u'; - case clang::BuiltinType::Kind::Char32: - return 'i'; - case clang::BuiltinType::Kind::Int: - return 'i'; - case clang::BuiltinType::Kind::ULong: - return 'L'; - case clang::BuiltinType::Kind::Long: - return 'l'; - case clang::BuiltinType::Kind::ULongLong: - return 'U'; - case clang::BuiltinType::Kind::LongLong: - return 'I'; - case clang::BuiltinType::Kind::UInt128: - return 'H'; - case clang::BuiltinType::Kind::Int128: - return 'H'; - case clang::BuiltinType::Kind::Float: - return 'f'; - case clang::BuiltinType::Kind::Double: - return 'd'; - case clang::BuiltinType::Kind::LongDouble: - return 'D'; - case clang::BuiltinType::Kind::NullPtr: - return 'p'; // nullptr_t - - case clang::BuiltinType::Kind::Half: - case clang::BuiltinType::Kind::BFloat16: - case clang::BuiltinType::Kind::ShortAccum: - case clang::BuiltinType::Kind::Accum: - case clang::BuiltinType::Kind::LongAccum: - case clang::BuiltinType::Kind::UShortAccum: - case clang::BuiltinType::Kind::UAccum: - case clang::BuiltinType::Kind::ULongAccum: - case clang::BuiltinType::Kind::ShortFract: - case clang::BuiltinType::Kind::Fract: - case clang::BuiltinType::Kind::LongFract: - case clang::BuiltinType::Kind::UShortFract: - case clang::BuiltinType::Kind::UFract: - case clang::BuiltinType::Kind::ULongFract: - case clang::BuiltinType::Kind::SatShortAccum: - case clang::BuiltinType::Kind::SatAccum: - case clang::BuiltinType::Kind::SatLongAccum: - case clang::BuiltinType::Kind::SatUShortAccum: - case clang::BuiltinType::Kind::SatUAccum: - case clang::BuiltinType::Kind::SatULongAccum: - case clang::BuiltinType::Kind::SatShortFract: - case clang::BuiltinType::Kind::SatFract: - case clang::BuiltinType::Kind::SatLongFract: - case clang::BuiltinType::Kind::SatUShortFract: - case clang::BuiltinType::Kind::SatUFract: - case clang::BuiltinType::Kind::SatULongFract: - case clang::BuiltinType::Kind::Float16: - case clang::BuiltinType::Kind::Float128: - case clang::BuiltinType::Kind::Overload: - case clang::BuiltinType::Kind::BoundMember: - case clang::BuiltinType::Kind::PseudoObject: - case clang::BuiltinType::Kind::Dependent: - case clang::BuiltinType::Kind::UnknownAny: - case clang::BuiltinType::Kind::ARCUnbridgedCast: - case clang::BuiltinType::Kind::BuiltinFn: - case clang::BuiltinType::Kind::ObjCId: - case clang::BuiltinType::Kind::ObjCClass: - case clang::BuiltinType::Kind::ObjCSel: -#define IMAGE_TYPE(it, id, si, a, s) case clang::BuiltinType::Kind::id: -#include -#undef IMAGE_TYPE - case clang::BuiltinType::Kind::OCLSampler: - case clang::BuiltinType::Kind::OCLEvent: - case clang::BuiltinType::Kind::OCLClkEvent: - case clang::BuiltinType::Kind::OCLQueue: - case clang::BuiltinType::Kind::OCLReserveID: - case clang::BuiltinType::Kind::IncompleteMatrixIdx: - case clang::BuiltinType::Kind::OMPArraySection: - case clang::BuiltinType::Kind::OMPArrayShaping: - case clang::BuiltinType::Kind::OMPIterator: -#define EXT_OPAQUE_TYPE(et, id, e) case clang::BuiltinType::Kind::id: -#include -#define SVE_TYPE(n, id, si) case clang::BuiltinType::Kind::id: -#include -#define PPC_VECTOR_TYPE(n, id, s) case clang::BuiltinType::Kind::id: -#include -#undef EXT_OPAQUE_TYPE -#undef SVE_TYPE -#undef PPC_VECTOR_TYPE - return '!'; - default: - return ':'; - } - } else if (qual->isEnumeralType()) { - const clang::EnumDecl *ed = qual->getAs()->getDecl(); - if (!ed) { - return 'i'; - } else { - return type2char(ed->getIntegerType().getCanonicalType(), qorig); - } - } else if (qual->isFunctionPointerType()) { - return '@'; - } else if (qual->isAnyPointerType() || qual->isReferenceType()) { - const clang::QualType &pointed = getPointedType(qual); - if (isTypeTrivial(pointed, qorig)) { - return 'p'; - } else if (const clang::RecordType *rct = pointed->getAs()) { - clang::RecordDecl *rc = rct->getDecl(); - if (!rc) { - return '!'; - } else if (!rc->isCompleteDefinition()) { - return 'p'; - } else { - std::string str; - if (qorig) { - const clang::QualType qpted = getPointedType(*qorig); - str = record2name(*rc, &qpted); - } else { - str = record2name(*rc, nullptr); - } - char ret = ptr2char(str); - if (ret) return ret; - else { - return '!'; - } - } - } else { - return '!'; - } - } else if (const clang::RecordType *rct = qual->getAs()) { - clang::RecordDecl *rc = rct->getDecl(); - if (!rc) { - return '?'; - } else if (rc->getNameAsString() == "__builtin_va_list") { - // va_list - return 'A'; - } else { - return '?'; - } - } else { - return '?'; - } -} -bool isTypeTrivial(const clang::QualType &q, const clang::QualType *qorig) { - const char c = type2char(q, qorig); -#define GO(chr) || (c == chr) - return (c == 'v') - GO('i') GO('u') - GO('I') GO('U') - GO('l') GO('L') - GO('f') GO('d') - GO('D') GO('K') - GO('0') GO('1') - GO('C') GO('c') - GO('W') GO('w') - GO('H') - GO('p'); -#undef GO -} -bool isTypeValid(const clang::QualType &q, const clang::QualType *qorig) { - const char c = type2char(q, qorig); - if (c == 'A') return false; - if (c == 'V') return false; - return ((c >= '0') && (c <= '9')) || ((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')); -} - -const std::string type2string(const clang::QualType &qual, const clang::QualType *qorig) { - if (qual->isBuiltinType()) { - return std::string("(builtin) ") + static_cast(*qual).getName(clang::PrintingPolicy{{}}).data(); - } else if (qual->isFunctionPointerType()) { - return "Callback (function pointer)"; - } else if (qual->isAnyPointerType() || qual->isReferenceType()) { - std::string prefix = qual->isAnyPointerType() ? "Pointer to " : "Reference to "; - const clang::QualType &pointed = getPointedType(qual); - if (isTypeTrivial(pointed, qorig)) { - return prefix + "trivial object " + type2string(pointed, qorig) + " (" + type2char(pointed, qorig) + ")"; - } else if (const clang::RecordType *rct = pointed->getAs()) { - clang::RecordDecl *rc = rct->getDecl(); - if (!rc) { - return prefix + "unknown record"; - } else if (!rc->isCompleteDefinition()) { - return prefix + "incomplete record " + rc->getNameAsString(); - } else { - std::string str; - if (qorig) { - const clang::QualType qpted = getPointedType(*qorig); - str = record2name(*rc, &qpted); - } else { - str = record2name(*rc, nullptr); - } - const char *ret = ptr2str(str); - if (ret[0] != '\0') { - return prefix + ret; - } else { - if (mangler && mangler->shouldMangleDeclName(rc)) { - std::string mangled; - { - llvm::raw_string_ostream strstr{mangled}; - mangler->mangleName(rc, strstr); - } - return prefix + "unknown record " + str + " (=== " + mangled + ")"; - } else { - return prefix + "unknown record " + str; - } - } - } - } else { - return prefix + "non-trivial or typedef'ed object " + type2string(pointed, qorig) + " (" + type2char(pointed, qorig) + ")"; - //return "Pointer (maybe to callback)"; - } - } else if (qual->isEnumeralType()) { - const clang::EnumDecl *ed = qual->getAs()->getDecl(); - if (!ed) { - return "Enumeration with unknown underlying integer type (assuming int)"; - } else { - return "Enumeration with underlying type " + type2string(ed->getIntegerType().getCanonicalType(), nullptr); - } - } else if (const clang::RecordType *rct = qual->getAs()) { - clang::RecordDecl *rc = rct->getDecl(); - if (!rc) { - return "Unknown record"; - } else if (rc->getNameAsString() == "__builtin_va_list") { - return "va_list"; - } else { - return "Unknown record " + std::string(rc->getName().data()); - } - } else { - return std::string("??? ") + qual->getTypeClassName(); - } -} - -class Visitor : public clang::RecursiveASTVisitor { -public: - clang::ASTContext &context; - - bool shouldVisitTemplateInstantiations() const /* override */ { return true; } - - Visitor(clang::CompilerInstance &ci) : context(ci.getASTContext()) { - if (!mangler) { - mangler = clang::ItaniumMangleContext::create(context, ci.getDiagnostics()); - } - } - - ~Visitor() { - if (mangler) { - delete mangler; - mangler = nullptr; - } - } - - bool VisitDecl(clang::Decl *decl) /* override */ { - std::cerr << std::flush; - if (!decl) return true; - - if ((decl->getKind() >= clang::Decl::Kind::firstFunction) && (decl->getKind() <= clang::Decl::Kind::lastFunction)) { - clang::DeclaratorDecl *ddecl = static_cast(decl); - std::cout << "Function detected!\n"; - - std::string funName{ddecl->getName()}; - - auto niceprint = [](const std::string &infotype, const auto &dat){ std::cout << " " << infotype << ": " << dat << "\n"; }; - niceprint("Function name", funName); - if (mangler && mangler->shouldMangleDeclName(ddecl)) { - std::string mangled; - { - llvm::raw_string_ostream strstr{mangled}; - mangler->mangleName(ddecl, strstr); - } - niceprint("Function mangled name", mangled); - funName = std::move(mangled); - } - - bool valid; - std::string funTypeStr{""}; - if (ddecl->getFunctionType()->isFunctionNoProtoType()) { - const clang::FunctionNoProtoType *funType = static_cast(ddecl->getFunctionType()); - const auto &retType = funType->getReturnType(); - - niceprint("Function return type", type2string(retType, &retType)); - niceprint("Canonical function return type", - type2string(retType.getCanonicalType(), &retType) + - " (" + type2char(retType.getCanonicalType(), &retType) + ")"); - niceprint("Is sugared", funType->isSugared()); - if (funType->isSugared()) { - clang::QualType qft{funType, 0}; - niceprint("Desugared", type2string(funType->desugar(), &qft)); - } - - funTypeStr = type2char(retType.getCanonicalType(), &retType) + std::string("Fv"); - valid = isTypeValid(retType.getCanonicalType(), &retType); - } else { - const clang::FunctionProtoType *funType = static_cast(ddecl->getFunctionType()); - const auto &retType = funType->getReturnType(); - - niceprint("Function return type", type2string(retType, &retType)); - niceprint("Canonical function return type", - type2string(retType.getCanonicalType(), &retType) - + " (" + type2char(retType.getCanonicalType(), &retType) + ")"); - niceprint("Parameter count", funType->getNumParams()); - for (const clang::QualType &type : funType->getParamTypes()) { - niceprint(" " + type2string(type, &type), - type2string(type.getCanonicalType(), &type) + " (" + type2char(type.getCanonicalType(), &type) + ")"); - } - niceprint("Variadic function", funType->isVariadic() ? "yes" : "no"); - - funTypeStr = - type2char(retType.getCanonicalType(), &retType) + - ((funType->getNumParams() == 0) - ? std::string("Fv") : std::accumulate(funType->getParamTypes().begin(), funType->getParamTypes().end(), std::string("F"), - [](const std::string &acc, const clang::QualType &qual){ return acc + type2char(qual.getCanonicalType(), &qual); })); - if (funType->isVariadic()) funTypeStr += "V"; - valid = !funType->isVariadic() && - std::accumulate(funType->getParamTypes().begin(), funType->getParamTypes().end(), isTypeValid(retType.getCanonicalType(), &retType), - [](bool acc, const clang::QualType &qual){ return acc && isTypeValid(qual.getCanonicalType(), &qual); }); - } - - niceprint("Conclusion", ""); - niceprint("Function final name", funName); - niceprint("Function type", funTypeStr); - niceprint("Valid function type", valid ? "yes" : "no"); - std::cout << "\n"; - - funMap[funName] = std::make_pair(funTypeStr, valid); - funList.push_back(funName); - } - - return true; - } -}; - -class Consumer : public clang::ASTConsumer { -public: - Visitor visitor; - - Consumer(clang::CompilerInstance &ci) : visitor(ci) { - } - - void HandleTranslationUnit(clang::ASTContext &context) override { - visitor.TraverseDecl(context.getTranslationUnitDecl()); - } -}; - -class Action : public clang::ASTFrontendAction { -public: - virtual std::unique_ptr CreateASTConsumer(clang::CompilerInstance &ci, llvm::StringRef inFile) override { - return std::make_unique(ci); - } -}; - -int main(int argc, const char **argv) { - if (argc < 2) { - std::cerr << "Usage: " << argv[0] << " (filenames) -- [-I...]" << std::endl; - return 2; - } - - /*int fakeargc = argc + 1; - const char **fakeargv = new const char*[fakeargc]; - memcpy(fakeargv, argv, argc * sizeof(char*)); - fakeargv[fakeargc - 1] = "--";*/ - llvm::cl::OptionCategory opcat{""}; - clang::tooling::CommonOptionsParser op{argc, argv, opcat}; - std::vector paths; for (int i = 1; i < argc; ++i) paths.push_back(argv[i]); - - clang::tooling::ClangTool tool{op.getCompilations(), paths}; - - tool.run(clang::tooling::newFrontendActionFactory().get()); - - std::cout << "Done, outputing output.h" << std::endl; - std::sort(funList.begin(), funList.end()); - std::fstream file{"output.h", std::ios_base::out}; - for (const std::string &funName : funList) { - if (!funMap[funName].second) { - file << "//"; - } - file << "GO(" << funName << ", " << funMap[funName].first << ")\n"; - } - - return 0; -} diff --git a/LLVMprivateGenerator/registered_structs.cpp b/LLVMprivateGenerator/registered_structs.cpp deleted file mode 100644 index 0473deb56..000000000 --- a/LLVMprivateGenerator/registered_structs.cpp +++ /dev/null @@ -1,267 +0,0 @@ -#include - -#define ALL START() \ - /* libc */ \ - STRUCT("_IO_FILE", "a FILE") \ - STRUCT("_G_fpos_t", "a file position") \ - STRUCT("sockaddr", "a socket address") \ - STRUCT("itimerspec", "an itimerspec") \ - STRUCT("timespec", "a timespec") \ - STRUCT("itimerval", "an itimerval") \ - STRUCT("timeval", "a timeval") \ - STRUCT("timex", "a timex") \ - STRUCT("timezone", "a timezone") \ - STRUCT("dirent", "a dirent") \ - STRUCT("dirent64", "a dirent64") \ - STRUCT("__dirstream", "a dir stream") \ - STRUCT("tm", "a time structure (tm)") \ - STRUCT("cmsghdr", "a cmsghdr") \ - STRUCT("msghdr", "a msghdr") \ - STRUCT("rpcent", "an rpcent") \ - STRUCT("random_data", "a random_data structure") \ - STRUCT("drand48_data", "a drand48_data structure") \ - STRUCT("termios", "a termios") \ - STRUCT("iovec", "an iovec") \ - STRUCT("file_handle", "a file handle") \ - STRUCT("lconv", "an lconv") \ - STRUCT("__locale_struct", "a locale structure") \ - STRUCT("aliasent", "an alias") \ - STRUCT("fstab", "an fstab") \ - STRUCT("group", "a group") \ - STRUCT("hostent", "a hostent") \ - STRUCT("protoent", "a protoent") \ - STRUCT("passwd", "a password") \ - STRUCT("spwd", "an spwd") \ - STRUCT("ttyent", "a ttyent") \ - STRUCT("utmp", "an utmp structure") \ - STRUCT("utmpx", "an utmpx structure") \ - STRUCT("ifaddrs", "an ifaddrs structure") \ - STRUCT("statfs", "a statfs structure") \ - STRUCT("statfs64", "a statfs64 structure") \ - STRUCT("statvfs", "a statvfs structure") \ - STRUCT("statvfs64", "a statvfs64 structure") \ - STRUCT("timeb", "a timeb structure") \ - STRUCT("_ftsent", "an _ftsent structure") \ - STRUCT("sysinfo", "a sysinfo structure") \ - STRUCT("rlimit", "an rlimit structure") \ - STRUCT("rlimit64", "an rlimit64 structure") \ - STRUCT("rusage", "an rusage structure") \ - STRUCT("entry", "an entry structure") \ - STRUCT("pollfd", "a pollfd structure") \ - STRUCT("re_pattern_buffer", "a re_pattern_buffer structure") \ - STRUCT("sembuf", "a sembuf structure") \ - STRUCT("tms", "a tms structure") \ - STRUCT("utsname", "an utsname structure") \ - STRUCT("utimbuf", "an utimbuf structure") \ - STRUCT2("__va_list_tag", "__va_list_tag (aka, a va_list)", 'A') \ - /* ncurses */ \ - STRUCT("_win_st", "a _win_st structure") \ - STRUCT("MEVENT", "an MEVENT structure") \ - TYPEDEF("cchar_t", "a cchar_t") \ - /* zlib */ \ - STRUCT("gz_header_s", "a gz_header_s structure") \ - STRUCT("gzFile_s", "a gzFile_s structure") \ - STRUCT("z_stream_s", "a z_stream_s structure") \ - \ - END() - -#define START() -#define STRUCT(s, ret) if (str == s) { return 'p'; } else -#define STRUCT2(s, ret, c) if (str == s) { return c; } else -#define TYPEDEF(s, ret) if (str == s) { return 'p'; } else -#define END() { return 0; } -char ptr2char(const std::string &str) { - /*if ((str == "_IO_FILE") - || (str == "_G_fpos_t") - || (str == "sockaddr") - || (str == "itimerspec") - || (str == "timespec") - || (str == "itimerval") - || (str == "timeval") - || (str == "timex") - || (str == "timezone") - || (str == "dirent") - || (str == "dirent64") - || (str == "__dirstream") - || (str == "tm") - || (str == "cmsghdr") - || (str == "msghdr") - || (str == "rpcent") - || (str == "random_data") - || (str == "drand48_data") - || (str == "termios") - || (str == "iovec") - || (str == "file_handle") - || (str == "lconv") - || (str == "__locale_struct") - || (str == "aliasent") - || (str == "fstab") - || (str == "group") - || (str == "hostent") - || (str == "protoent") - || (str == "passwd") - || (str == "spwd") - || (str == "ttyent") - || (str == "utmp") - || (str == "utmpx") - || (str == "ifaddrs") - || (str == "statfs") - || (str == "statfs64") - || (str == "statvfs") - || (str == "timeb") - || (str == "_ftsent") - || (str == "sysinfo") - || (str == "rlimit") - || (str == "rlimit64") - || (str == "rusage") - || (str == "entry") - || (str == "pollfd") - || (str == "re_pattern_buffer") - || (str == "sembuf") - || (str == "tms") - || (str == "utsname") - || (str == "utimbuf") - // ncurses - || (str == "_win_st") - - || (str == "cchar_t") - ) { - // FILE*, fpos_t*, ... - return 'p'; - } else if (str == "__va_list_tag") { - return 'A'; - } else { - return 0; - }*/ - ALL -} -#undef END -#undef TYPEDEF -#undef STRUCT2 -#undef STRUCT -#undef START - -#define START() -#define STRUCT(s, ret) if (str == s) { return ret; } else -#define STRUCT2(s, ret, c) if (str == s) { return ret; } else -#define TYPEDEF(s, ret) if (str == s) { return ret; } else -#define END() return ""; -const char *ptr2str(const std::string &str) { - /*if (str == "_IO_FILE") { - return "a FILE"; - } else if (str == "_G_fpos_t") { - return "a file position"; - } else if (str == "sockaddr") { - return "a socket address"; - } else if (str == "itimerspec") { - return "an itimerspec"; - } else if (str == "timespec") { - return "a timespec"; - } else if (str == "itimerval") { - return "an itimerval"; - } else if (str == "timeval") { - return "a timeval"; - } else if (str == "timex") { - return "a timex"; - } else if (str == "timezone") { - return "a timezone"; - } else if (str == "dirent") { - return "a dirent"; - } else if (str == "dirent64") { - return "a dirent64"; - } else if (str == "__dirstream") { - return "a dir stream"; - } else if (str == "tm") { - return "a time structure (tm)"; - } else if (str == "cmsghdr") { - return "a cmsghdr"; - } else if (str == "msghdr") { - return "a msghdr"; - } else if (str == "rpcent") { - return "an rpcent"; - } else if (str == "random_data") { - return "a random_data structure"; - } else if (str == "drand48_data") { - return "a drand48_data structure"; - } else if (str == "termios") { - return "a termios"; - } else if (str == "iovec") { - return "an iovec"; - } else if (str == "file_handle") { - return "a file handle"; - } else if (str == "lconv") { - return "an lconv"; - } else if (str == "__locale_struct") { - return "a locale structure"; - } else if (str == "aliasent") { - return "an alias"; - } else if (str == "fstab") { - return "an fstab"; - } else if (str == "group") { - return "a group"; - } else if (str == "hostent") { - return "a hostent"; - } else if (str == "protoent") { - return "a protoent"; - } else if (str == "passwd") { - return "a password"; - } else if (str == "spwd") { - return "an spwd"; - } else if (str == "ttyent") { - return "a ttyent"; - } else if (str == "utmp") { - return "an utmp structure"; - } else if (str == "utmpx") { - return "an utmpx structure"; - } else if (str == "ifaddrs") { - return "an ifaddrs structure"; - } else if (str == "statfs") { - return "a statfs structure"; - } else if (str == "statfs64") { - return "a statfs64 structure"; - } else if (str == "statvfs") { - return "a statvfs structure"; - } else if (str == "statvfs64") { - return "a statvfs64 structure"; - } else if (str == "timeb") { - return "a timeb structure"; - } else if (str == "_ftsent") { - return "an _ftsent structure"; - } else if (str == "sysinfo") { - return "a sysinfo structure"; - } else if (str == "rlimit") { - return "an rlimit structure"; - } else if (str == "rlimit64") { - return "an rlimit64 structure"; - } else if (str == "rusage") { - return "an rusage structure"; - } else if (str == "entry") { - return "an entry structure"; - } else if (str == "pollfd") { - return "a pollfd structure"; - } else if (str == "re_pattern_buffer") { - return "a re_pattern_buffer structure"; - } else if (str == "sembuf") { - return "a sembuf structure"; - } else if (str == "tms") { - return "a tms structure"; - } else if (str == "utsname") { - return "an utsname structure"; - } else if (str == "utimbuf") { - return "an utimbuf structure"; - } else if (str == "__va_list_tag") { - return "__va_list_tag (aka, a va_list)"; - // ncurses - } else if (str == "_win_st") { - return "a _win_st structure"; - - } else if (str == "cchar_t") { - return "a cchar_t"; - } else return "";*/ - ALL -} -#undef END -#undef TYPEDEF -#undef STRUCT2 -#undef STRUCT -#undef START diff --git a/wrapperhelper/CMakeLists.txt b/wrapperhelper/CMakeLists.txt deleted file mode 100644 index 3ab9cf14a..000000000 --- a/wrapperhelper/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -cmake_minimum_required(VERSION 3.10) -project("helper") -find_package(Clang REQUIRED CONFIG) - -add_executable(helper main.cpp gen.cpp) -target_include_directories(helper SYSTEM PUBLIC ${CLANG_INCLUDE_DIRS}) -target_link_libraries(helper PUBLIC clang-cpp LLVM) diff --git a/wrapperhelper/Makefile b/wrapperhelper/Makefile new file mode 100755 index 000000000..9f1a6c5ff --- /dev/null +++ b/wrapperhelper/Makefile @@ -0,0 +1,299 @@ +all: +.PHONY: all + +OPTIM?=2 +DEBUG?=1 +# FORCE_COLOR: set to non-empty, non 0 to force colorized output +# ECHO: set to non-empty, non 0 to echo commands out + +help: + @echo 'Targets:' + @echo ' help (current target)' + @echo ' all (default target)' + @echo ' [EXE] - wrapperhelper' + @printf ' $(foreach obj,$(OBJLIST_wrapperhelper), [OBJ] - $(obj)\n)' + @printf ' $(foreach test,$(TESTS), [TST] - $(test)\n)' + @echo ' clean' + @echo ' distclean' + @echo '' + @echo 'Options:' + @echo ' OPTIM: GCC optimization level (-O is prepended) [default: 2]' + @echo ' DEBUG: set to 0 for release build, set to non-0 for debug build [default: 1]' + @echo ' FORCE_COLOR: set to non-0 to force colorized output' + @echo ' ECHO: set to non-0 to echo out commands executed' + @echo '' + @echo 'Current flags:' + @echo ' CPPFLAGS = $(CPPFLAGS)' + @echo ' CFLAGS = $(CFLAGS)' +# @echo ' CXXFLAGS = $(CXXFLAGS)' unused + @echo ' LDFLAGS = $(LDFLAGS)' + @echo ' LDLIBS = $(LDLIBS)' + @echo '' + @echo 'Sanitizers:' + @echo " address ------------ `[ $(ASAN_ON) -eq 1 ] && printf '\033[92mON\033[m' || printf '\033[91mOFF\033[m'`" + @echo " leak --------------- `[ $(LSAN_ON) -eq 1 ] && printf '\033[92mON\033[m' || printf '\033[91mOFF\033[m'`" + @echo " undefined behavior - `[ $(USAN_ON) -eq 1 ] && printf '\033[92mON\033[m' || printf '\033[91mOFF\033[m'`" +.PHONY: help + +ifeq ($(ECHO:0=),) +SILENCER:=@ +else +SILENCER:= +endif + +ifneq ($(strip $(DEBUG)),0) +CPPFLAGS+= -DDEBUG -D_NRELEASE +CFLAGS+= -g +CXXFLAGS+= -g +LDFLAGS+= -g +OBJDIR?=debug +else +CPPFLAGS+= -DRELEASE -D_NDEBUG +OBJDIR?=release +endif + +COMMON_WARNINGS:=-Wfatal-errors -fanalyzer -Wall -Wextra +COMMON_WARNINGS+= -Walloc-zero -Wcast-align=strict -Wcast-qual -Wconversion -Wdate-time +COMMON_WARNINGS+= -Wdisabled-optimization -Wduplicated-branches -Wfloat-equal -Wformat-truncation=2 +COMMON_WARNINGS+= -Wimplicit-fallthrough=3 -Wlogical-op -Wmissing-format-attribute -Wmissing-include-dirs +COMMON_WARNINGS+= -Wmissing-noreturn -Wnull-dereference -Wredundant-decls -Wundef -Wunreachable-code -Wshift-overflow=2 +COMMON_WARNINGS+= -Wstringop-overflow=4 +#COMMON_WARNINGS+= -Wstringop-overflow=4 -Wsuggest-attribute=cold -Wsuggest-attribute=const -Wsuggest-attribute=format +#COMMON_WARNINGS+= -Wsuggest-attribute=malloc -Wsuggest-attribute=noreturn -Wsuggest-attribute=pure +COMMON_WARNINGS+= -Wunknown-pragmas -Wunused-macros -Wwrite-strings +COMMON_WARNINGS+= -Werror=attribute-alias=2 -Werror=duplicated-cond -Werror=format=2 -Werror=format-overflow=2 +COMMON_WARNINGS+= -Werror=format-signedness -Werror=pointer-arith +COMMON_WARNINGS+= -Werror=return-type -Werror=shadow -Werror=strict-overflow -Werror=switch-enum +CFLAGS_WARNINGS:=-Werror=implicit-function-declaration -Werror=jump-misses-init -Werror=strict-prototypes +CXXFLAGS_WARNINGS:=-Werror=overloaded-virtual -fdiagnostics-show-template-tree -Wno-analyzer-use-of-uninitialized-value + +CPPFLAGS+= +CFLAGS:=$(COMMON_WARNINGS) $(CFLAGS_WARNINGS) $(CFLAGS) -std=gnu18 -O$(OPTIM) +CXXFLAGS:=$(COMMON_WARNINGS) $(CXXFLAGS_WARNINGS) $(CXXFLAGS) -std=c++20 -O$(OPTIM) +LDFLAGS+= -O$(OPTIM) + +#CPPFLAGS+= -I/usr/include/SDL2 -D_REENTRANT -pthread +#CFLAGS+= -pthread +#CXXFLAGS+= -pthread +#LDLIBS+= -pthread -lSDL2 + +ifeq (,$(wildcard $(CURDIR)/sanaddress)) +ASAN_ON:=0 +else +ASAN_ON:=1 +CFLAGS+= -fsanitize=address +CXXFLAGS+= -fsanitize=address +LDFLAGS+= -fsanitize=address +endif +ifeq (,$(wildcard $(CURDIR)/sanleak)) +LSAN_ON:=0 +else +LSAN_ON:=1 +CFLAGS+= -fsanitize=leak +CXXFLAGS+= -fsanitize=leak +LDFLAGS+= -fsanitize=leak +endif +ifeq (,$(wildcard $(CURDIR)/sanundefined)) +USAN_ON:=0 +else +USAN_ON:=1 +CFLAGS+= -fsanitize=undefined +CXXFLAGS+= -fsanitize=undefined +LDFLAGS+= -fsanitize=undefined +endif + +# Default +# .SUFFIXES: .out .a .ln .o .c .cc .C .cpp .p .f .F .m .r .y .l .ym .yl .s .S .mod +# .sym .def .h .info .dvi .tex .texinfo .texi .txinfo .w .ch .web .sh .elc .el +SUFFIXES = +.SUFFIXES: +.SECONDEXPANSION: + +ifneq ($(MAKECMDGOALS:distclean=clean),clean) +.: ; +bin obj: ; $(SILENCER)test -d $@ || mkdir $@ +# $(eval $(call reproduce_tree,)) +define reproduce_tree = +$(1) $(1)/parser: | $$$$(@D) ; $(SILENCER)test -d $$@ || mkdir $$@ +endef +$(eval $(call reproduce_tree,obj/$(OBJDIR))) +$(eval $(call reproduce_tree,obj/$(OBJDIR)/tests)) +$(eval $(call reproduce_tree,makedir)) +$(eval $(call reproduce_tree,makedir/tests)) +$(eval $(call reproduce_tree,tests)) +endif + +# Colors: +# ------- +# +--------+-----+ +# | 3 | 9 | +# +-+--------+-----+ +# |0| | | Black +# |1| | RM | Red +# |2| |[MSG]| Green +# |3|Creating| | Yellow +# |4| | CP | Blue +# |5| | LD | Purple +# |6| C++ | | Cyan +# |7| | | Gray/white +# +-+--------+-----+ + +# $(call colorize,,,,) +ifdef $(if $(FORCE_COLOR:0=),FORCE_COLOR,MAKE_TERMOUT) +CFLAGS:=$(CFLAGS) -fdiagnostics-color +CXXFLAGS:=$(CFLAGS) -fdiagnostics-color +colorize=@printf "\033[$(1)m[$(2)]\033[m \033[$(3)m$(4)\033[m\n" +else +ifeq ($(SILENCER),) +colorize= +else +colorize=@echo "[$(2)] $(4)" +endif +endif + +define newline := + + +endef + +# $(call remove,) +define remove = +$(call colorize,1;91,RM ,91,Removing $(1)) +$(SILENCER)$(RM) -r $(1) +endef + +# $(eval $(call add_deptree,,,)) +ifeq ($(MAKECMDGOALS),distclean) +add_deptree= +else +define add_deptree = +makedir/$(2).mk: | $$$$(@D) + $(call colorize,95,DEP,33,Creating $(3) dependancies) + $(SILENCER)set -e; $(1) -MM src/$(3) \ + | sed 's,\($$(notdir $$(basename $(3)))\)\.o[ :]*,$$(dir obj/$(OBJDIR)/$(3))\1.o: $$@'"\n"'$$(dir obj/$(OBJDIR)/$(3))\1.o $$@: ,g' >$$@ +include makedir/$(2).mk +endef +endif + +OBJLIST=$(OBJLIST_wrapperhelper) $(foreach test,$(TESTS),$(call test_o,$(test))) +OBJLIST_wrapperhelper:= +TESTS:= + +# $(call wrapperhelper_o,,,) +wrapperhelper_o=obj/$(OBJDIR)/$(1)$(3).o +# $(eval $(call compile_wrapperhelper_c,,,)) +define compile_wrapperhelper_c = +$$(eval $$(call add_deptree,$$(CC) $$(CPPFLAGS) $$(CFLAGS),$(1)$(3),$(1)$(2).c)) +OBJLIST_wrapperhelper+= $(call wrapperhelper_o,$(1),$(2),$(3)) +$(call wrapperhelper_o,$(1),$(2),$(3)): src/$(1)$(2).c | $$$$(@D) + $(call colorize,36, C ,92,Compiling $$@) + $(SILENCER)$$(CC) $$(CPPFLAGS) $$(CFLAGS) -c src/$(1)$(2).c -o $$@ +endef +# $(eval $(call compile_wrapperhelper_cxx,,,)) +define compile_wrapperhelper_cxx = +$$(eval $$(call add_deptree,$$(CXX) $$(CPPFLAGS) $$(CXXFLAGS),$(1)$(3),$(1)$(2).cpp)) +OBJLIST_wrapperhelper+= $(call wrapperhelper_o,$(1),$(2),$(3)) +$(call wrapperhelper_o,$(1),$(2),$(3)): src/$(1)$(2).cpp | $$$$(@D) + $(call colorize,36,C++,92,Compiling $$@) + $(SILENCER)$$(CXX) $$(CPPFLAGS) $$(CXXFLAGS) -c src/$(1)$(2).cpp -o $$@ +endef + +# $(eval $(call compile_test_c,)) +define compile_test_c = +$$(eval $$(call add_deptree,$$(CC) $$(CPPFLAGS) -Isrc/tests -Isrc $$(CFLAGS),tests/$(1),tests/$(1).c)) +TESTS+= $(1) +tests/$(1): obj/$(OBJDIR)/tests/$(1).o | $$$$(@D) + $(call colorize,95,LD ,92,Linking $$@) + $(SILENCER)$$(CC) $$(LDFLAGS) -o $$@ obj/$(OBJDIR)/tests/$(1).o $$(LDLIBS) + +obj/$(OBJDIR)/tests/$(1).o: src/tests/$(1).c | $$$$(@D) + $(call colorize,36,C++,92,Compiling $$@) + $(SILENCER)$$(CC) $$(CPPFLAGS) -Isrc/tests -Isrc $$(CFLAGS) -c src/tests/$(1).c -o $$@ +endef +# $(eval $(call compile_test_cxx,)) +define compile_test_cxx = +$$(eval $$(call add_deptree,$$(CXX) $$(CPPFLAGS) -Isrc/tests -Isrc $$(CXXFLAGS),tests/$(1),tests/$(1).cpp)) +TESTS+= $(1) +tests/$(1): obj/$(OBJDIR)/tests/$(1).o | $$$$(@D) + $(call colorize,95,LD ,92,Linking $$@) + $(SILENCER)$$(CXX) $$(LDFLAGS) -o $$@ obj/$(OBJDIR)/tests/$(1).o $$(LDLIBS) + +obj/$(OBJDIR)/tests/$(1).o: src/tests/$(1).cpp | $$$$(@D) + $(call colorize,36,C++,92,Compiling $$@) + $(SILENCER)$$(CXX) $$(CPPFLAGS) -Isrc/tests -Isrc $$(CXXFLAGS) -c src/tests/$(1).cpp -o $$@ +endef + +$(eval $(call compile_wrapperhelper_c,,cstring,cstring)) +$(eval $(call compile_wrapperhelper_c,,generator,generator)) +$(eval $(call compile_wrapperhelper_c,,lang,lang)) +$(eval $(call compile_wrapperhelper_c,,main,main)) +$(eval $(call compile_wrapperhelper_c,,parse,parse)) +$(eval $(call compile_wrapperhelper_c,,prepare,prepare)) +$(eval $(call compile_wrapperhelper_c,,preproc,preproc)) +$(eval $(call compile_wrapperhelper_c,,vector,vector)) +$(call wrapperhelper_o,,preproc,preproc): CFLAGS+= -fno-analyzer +$(call wrapperhelper_o,,parse,parse): CFLAGS+= -fno-analyzer + +#$(eval $(call compile_test_cxx,core/number)) + +bin/wrapperhelper: $$(OBJLIST_wrapperhelper) | $$(@D) + $(call colorize,95,LD ,92,Linking $@) + $(SILENCER)$(CXX) $(LDFLAGS) -o $@ $(OBJLIST_wrapperhelper) $(LDLIBS) + +wrapperhelper: bin/wrapperhelper +alltests: $(TESTS:%=tests/%) +.PHONY: wrapperhelper alltests + +all: wrapperhelper alltests + +clean: + $(call remove,$(OBJLIST)) + $(call remove,bin/wrapperhelper) + $(call remove,$(TESTS:%=obj/$(OBJDIR)/tests/%.o)) + $(call remove,$(TESTS:%=tests/%)) +.PHONY: clean +distclean: + $(call remove,makedir) + $(call remove,obj) + $(call remove,bin tests) +.PHONY: distclean + +sanitize/help: + @echo "Sanitizers:" + @echo "- address (removes leak)" + @echo "- leak (removes address)" + @echo "- undefined behavior" + @echo "" + @echo "Currently active options:" + @[ $(ASAN_ON) -eq 0 ] || echo "- address" + @[ $(LSAN_ON) -eq 0 ] || echo "- leak" + @[ $(USAN_ON) -eq 0 ] || echo "- undefined behavior" +sanitize/address: + @[ $(ASAN_ON) -eq 0 ] && echo "Not sanitizing address" || echo "Sanitizing address" +sanitize/leak: + @[ $(LSAN_ON) -eq 0 ] && echo "Not sanitizing leak" || echo "Sanitizing leak" +sanitize/undefined: + @[ $(USAN_ON) -eq 0 ] && echo "Not sanitizing undefined behavior" || echo "Sanitizing undefined behavior" +sanitize/address/on: + $(SILENCER)touch sanaddress + $(SILENCER)$(RM) sanleak +sanitize/leak/on: + $(SILENCER)touch sanleak + $(SILENCER)$(RM) sanaddress +sanitize/undefined/on: + $(SILENCER)touch sanundefined +sanitize/address/off: + $(SILENCER)$(RM) sanaddress +sanitize/leak/off: + $(SILENCER)$(RM) sanleak +sanitize/undefined/off: + $(SILENCER)$(RM) sanundefined +.PHONY: sanitize/address sanitize/leak sanitize/undefined +.PHONY: sanitize/address/on sanitize/leak/on sanitize/undefined/on +.PHONY: sanitize/address/off sanitize/leak/off sanitize/undefined/off + +tree: + @tree src +.PHONY: tree + +.DELETE_ON_ERROR: diff --git a/wrapperhelper/README.md b/wrapperhelper/README.md index 1edd4db11..efbcfb51b 100644 --- a/wrapperhelper/README.md +++ b/wrapperhelper/README.md @@ -1,99 +1,122 @@ # Wrapper helper -**WARNING: There are still many problems with this tool. Please do NOT submit code generated directly by the tool, you should only use it as a preliminary reference.** +This folder is semi-independent from the parent project (`box64`). This sub-project aims to (partially) automating the generation of the private headers in `src/wrapped`. This is, however, still a work-in-progress and in alpha. +As such, **this sub-project is mainly aimed at people who know how to read code and are familiar with the wrapped libraries part of `box64`**. -This tool is based on libclangtooling. +## Licensing -It parses the AST of the library header files, generating the required structures of the wrapping library, including: -- structure definitions, -- export function signatures, -- callback function wrapping, -etc. Of course, this cannot completely automate everything, it can only be used as a reference. +This program is under the MIT license. However, some system header files under the LGPL license (copied from a GNU libc Arch Linux installation) have been adapted into the `include-fixed` folder; these files are not copied into the output and simply serve as data. As such, I believe this falls under fair use, and does not lead to the output of this program (used in the parent `box64` project) being under the (L)GPL license. -At the same time, this tool is also quite rough, and may even have errors. +## Compiling -## Build +You need a C compiler and GNU Make. No library is required. -``` -sudo apt install libclang-14-dev -cd wrapperhelper -mkdir build; cd build; cmake .. -make -``` +Go to this folder, then run the `make` command. This will produce a binary called `bin/wrapperhelper`. -## Usage: +This project has been compiled and tested with `GCC 14.2.1 20240805` on an `x86_64` machine, with no warning emitted. - helper [guest_triple] [host_triple] -- - : set the header file to be parsed - : set libname required for wrapping func - [guest_triple]: set guest triple: can be arm32/arm64/x86/x64, default is x64 - [host_triple] : set host triple: can be arm32/arm64/x86/x64, default is arm64 - -- : mandatory - : extra compiler flags +You may also use the `make clean` and `make distclean` commands to remove output files (`clean`) and directories (`distclean`). -### Usage example: +## Usage -`./helper /usr/include/jpeglib.h libjpeg x64 arm64 -- -I /usr/lib/gcc/x86_*/12.2.0/include --include /usr/lib/gcc/x86_*/12.2.0/include/stddef.h --include /usr/include/stdio.h` +To use the wrapper helper, run the following command in the folder containing this `README.md`: +```sh +bin/wrapperhelper "path_to_support_file" "path_to_private.h" "path_to_private.h" +``` -You would see an output similar to the files `src/wrapped/wrappedlibjpeg.c` and `src/wrapped/wrappedlibjpeg_private.h`, should they exist. +The first file is a `C` file containing every declaration required. The second file is the "requests" input. The third file is the output file, which may be a different file. -If there are multiple header files to process, write them into a custom header file as input. +The support file may contain pragma declarations of the form +```c +#pragma wrappers explicit_simple TYPE +``` +where `TYPE` is a `typedef` to a structure. This marks the structure pointed to by `TYPE` as "simple", which means that functions taking such structures are not required to be `GOM`-like. + +System headers included (directly or indirectly) by the support file are overriden by the files in `include-fixed`. -### Output sample +The first three lines of the input are ignored. -Using the command above, we get the following (trimmed) files: +A "request" is a structure containing an object name and, eventually, a default value (`GO`, `GO2` with type `vFiV` to function `xxx`, `DATA`...) and/or a "solved" value (which is similar, but deduced from the support file). -In `wrappedlibjpeg_private.h`: +Valid requests (in the reference file) are: ```c -... -GO(jpeg_quality_scaling, iFi) -... -GOM(jpeg_destroy, vFEp) -... +{GO/GOM/GOW/GOWM} ( name , type ) +{GOD/GO2/GOWD/GOW2} ( name , type , name ) +// {GO/GOM/GOW/GOWM} ( name , +// {GO/GOM/GOW/GOWM} ( name , type ) +// {GOD/GO2/GOWD/GOW2} ( name , +// {GOD/GO2/GOWD/GOW2} ( name , type , name ) +DATA[V/B/M] ( name , int ) +// DATA[V/B/M] ( name , +// DATA[V/B/M] ( name , int ) +``` +(where `{A/B}` means `A` or `B` and `[A/B]` means `A`, `B` or nothing). All other comments are ignored. + +If you want to explore the output of the different stages of the helper, you can use the following forms: +```sh +bin/wrapperhelper --prepare "path_to_support_file" # (1) +bin/wrapperhelper --preproc "path_to_support_file" # (2) +bin/wrapperhelper --proc "path_to_support_file" # (3) +bin/wrapperhelper "path_to_support_file" # (3) as well ``` +1. This form outputs the list of preprocessor tokens (the "post-prepare" phase). +2. This form outputs the list of processor tokens (the "post-preprocessor" phase). +3. This form outputs the list of constants, type definitions, structure definitions, and declarations (the "post-processor" phase). -In `wrappedlibjpeg.c`: -```c -... -typedef struct jpeg_source_mgr { - void *next_input_byte; - unsigned long bytes_in_buffer; - vFp_t init_source; - iFp_t fill_input_buffer; - vFpI_t skip_input_data; - iFpi_t resync_to_restart; - vFp_t term_source; -} jpeg_source_mgr, *jpeg_source_mgr_ptr; -... -#define GO(A) \ -static uintptr_t my_term_source_fct_##A = 0; \ -void my_term_source_##A(struct jpeg_decompress_struct * a0) { \ - return RunFunction(my_context, my_term_source_fct_##A, 1, a0); \ -} -SUPER() -#undef GO -static void* findterm_sourceFct(void* fct) { - if(!fct) return fct; - if(GetNativeFnc((uintptr_t)fct)) return GetNativeFnc((uintptr_t)fct); - #define GO(A) if(my_term_source_fct_##A == (uintptr_t)fct) return my_term_source_##A;} - SUPER() - #undef GO - #define GO(A) if(my_term_source_fct_##A == 0) {my_term_source_fct_##A = (uintptr_t)fct;return my_term_source_##A;} - SUPER() - #undef GO - return NULL; -} -... -EXPORT int my_jpeg_quality_scaling(void *emu, int quality) { - libjpeg_my_t *my = (libjpeg_my_t*)my_lib->priv.w.p2; - my->jpeg_quality_scaling(quality); -} -... -EXPORT void my_jpeg_destroy(void *emu, struct jpeg_common_struct * cinfo) { - // WARN: This function's arg has a structure ptr which is special, may need to wrap it for the host - libjpeg_my_t *my = (libjpeg_my_t*)my_lib->priv.w.p2; - my->jpeg_destroy(cinfo); -} -... +### Example + +To remake the `wrappedlibc_private.h` file, use the following command: +```sh +bin/wrapperhelper example-libc.h ../src/wrapped/wrappedlibc_private.h ../src/wrapped/wrappedlibc_private.h ``` +This will emit a few marnings and (non-fatal) errors, then write the result directly in `wrappedlibc_private.h`. + +## Maintaining + +All of the source code is included in the `src` folder. + +The `main` function is in `main.c`. + +The first phase of compilation (steps 1-3 and a part of step 5 of the translation phases) is implemented in `prepare.c`. + +The second phase of compilation (steps 4 and 6) is implemented in `preproc.c`. + +The third phase of compilation (step 7) is implemented in `parse.c`, though no actual parsing of function definitions takes place. + +The reading and writing of the `_private.h` files is implemented in `generator.c`. + +## Known issues + +This project only works for `box64`; more work is required for this to be compatible with `box32`. + +Only native structures are read. This means that the current version of `wrapperhelper` does not detect an issue when a structure has different members or alignments in two different architectures. + +The include paths are hard-coded. There should instead be a structure passed around containing all arch-dependent informations. + +Similarly, structure letters (i.e. `S` for `struct _IO_FILE*`) are hard-coded. A pragma should be used instead (`#pragma wrappers type_letter IDENT type-name`, parsed as `PTOK_PRAGMA: Type is letter: `, followed by `type-name`, followed by `PTOK_NEWLINE`). + +Conditionals in the `_private.h` files are ignored, except for taking only the negative branch. Manual cleanup of the output is required. + +Line numbers are missing entirely. For most errors, finding the corresponding file is difficult (though possible). + +Phase 5 is partially implemented, but could be greatly improved. + +The following features are missing from the generator: +- Large structures as a parameter +- Large structure as a return type (more than 16 bytes) +- Atomic types + +The following features are missing from the preprocessor: +- Error display (`#error` will stop the compilation, but a generic error message will be written) +- General token concatenation (though the concatenation of two `PTOK_IDENT` works without issue) +- Stringify +- Skipped unexpected token warnings +- Proper out-of-memory error handling + +The following features are missing from the parser: +- `inline` and `_Noreturn` +- `_Atomic(type-name)` +- `_Alignas(type-name)` and `_Alignas(constant-expression)` +- `(type-name){initializer-list}` +- Function definitions are ignored, not parsed diff --git a/wrapperhelper/ast.h b/wrapperhelper/ast.h deleted file mode 100644 index 2746d9884..000000000 --- a/wrapperhelper/ast.h +++ /dev/null @@ -1,189 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gen.h" -#include "utils.h" - -static void ParseParameter(clang::ASTContext* AST, WrapperGenerator* Gen, clang::QualType ParmType, FuncInfo* Func) { - using namespace clang; - (void)AST; (void)Func; - if (ParmType->isFunctionPointerType()) { - auto ProtoType = ParmType->getPointeeType()->getAs(); - for (unsigned i = 0; i < ProtoType->getNumParams(); i++) { - ParseParameter(AST, Gen, ProtoType->getParamType(i), Func); - } - } else if (ParmType->isPointerType()) { - auto PointeeType = ParmType->getPointeeType(); - if (PointeeType->isRecordType()) { - if (Gen->records.find(StripTypedef(PointeeType)) == Gen->records.end()) { - auto Record = &Gen->records[StripTypedef(PointeeType)]; - if (PointeeType->isUnionType()) { - Record->is_union = true; - } - Record->type = StripTypedef(PointeeType); - Record->decl = PointeeType->getAs()->getDecl(); - Record->type_name = Record->decl->getIdentifier() ? Record->decl->getIdentifier()->getName().str() : ""; - } - } else if (PointeeType->isPointerType()) { - PointeeType = PointeeType->getPointeeType(); - if (PointeeType->isRecordType()) { - if (Gen->records.find(StripTypedef(PointeeType)) == Gen->records.end()) { - auto Record = &Gen->records[StripTypedef(PointeeType)]; - if (PointeeType->isUnionType()) { - Record->is_union = true; - } - - Record->type = StripTypedef(PointeeType); - Record->decl = PointeeType->getAs()->getDecl(); - Record->type_name = Record->decl->getIdentifier() ? Record->decl->getIdentifier()->getName().str() : ""; - } - } - } - } else if (ParmType->isRecordType()) { - if (Gen->records.find(StripTypedef(ParmType)) == Gen->records.end()) { - auto Record = &Gen->records[StripTypedef(ParmType)]; - if (ParmType->isUnionType()) { - Record->is_union = true; - } - Record->type = StripTypedef(ParmType); - Record->decl = ParmType->getAs()->getDecl(); - Record->type_name = Record->decl->getIdentifier() ? Record->decl->getIdentifier()->getName().str() : ""; - } - } -} - -static void ParseFunction(clang::ASTContext* AST, WrapperGenerator* Gen, clang::FunctionDecl* Decl) { - using namespace clang; - auto Type = Decl->getType().getTypePtr(); - auto FuncInfo = &Gen->funcs[Type]; - FuncInfo->type = Type; - FuncInfo->func_name = Decl->getNameAsString(); - FuncInfo->decl = Decl; - FuncInfo->callback_args.resize(Decl->getNumParams()); - if (Decl->getAttr()) { - FuncInfo->is_weak = true; - } - if (Decl->isVariadic()) { - FuncInfo->is_variadaic = true; - } - for (unsigned i = 0; i < Decl->getNumParams(); i++) { - auto ParmDecl = Decl->getParamDecl(i); - if (ParmDecl->getType()->isFunctionPointerType()) { - FuncInfo->callback_args[i] = ParmDecl->getType().getTypePtr(); - FuncInfo->has_callback_arg = true; - } else { - FuncInfo->callback_args[i] = nullptr; - } - ParseParameter(AST, Gen, ParmDecl->getType(), FuncInfo); - } -} - -class MyASTVisitor : public clang::RecursiveASTVisitor { -public: - MyASTVisitor(clang::ASTContext* ctx) : Ctx(ctx) {} - MyASTVisitor(clang::ASTContext* ctx, WrapperGenerator* gen) : Ctx(ctx), Gen(gen) {} - - bool VisitFunctionDecl(clang::FunctionDecl* Decl) { - ParseFunction(Ctx, Gen, Decl); - return true; - } -private: - clang::ASTContext* Ctx; - WrapperGenerator* Gen; -}; - -class MyASTConsumer : public clang::ASTConsumer { -public: - MyASTConsumer(clang::ASTContext* Context, const std::string& libname, const std::string& host_triple, const std::string& guest_triple) - : Visitor(Context, &Generator) { - Generator.Init(libname, host_triple, guest_triple); - } - void HandleTranslationUnit(clang::ASTContext &Ctx) override { - Visitor.TraverseDecl(Ctx.getTranslationUnitDecl()); - std::cout << "--------------- Libclangtooling parse complete -----------------\n"; - Generator.Prepare(&Ctx); - std::cout << "--------------- Generator prepare complete -----------------\n"; - std::ofstream FuncDeclFile("wrapped" + Generator.libname + "_private.h", std::ios::out); - FuncDeclFile << Generator.GenFuncDeclare(&Ctx); - FuncDeclFile.close(); - std::ofstream FuncDefineFile("wrapped" + Generator.libname + ".c", std::ios::out); - FuncDefineFile << "#include \n" - "#include \n" - "#include \n" - "#define _GNU_SOURCE /* See feature_test_macros(7) */\n" - "#include \n" - "\n" - "#include \"wrappedlibs.h\"\n" - "\n" - "#include \"debug.h\"\n" - "#include \"wrapper.h\"\n" - "#include \"bridge.h\"\n" - "#include \"x64emu.h\"\n" - "#include \"box64context.h\"\n" - "\n" - "const char* " + Generator.libname + "Name = \"" + Generator.libname + "\";\n" - "#define LIBNAME " + Generator.libname + "\n" - "\n" - "#define ADDED_FUNCTIONS() \\\n" - "\n" - "#include \"generated/wrapped" + Generator.libname + "types.h\"\n"; - FuncDefineFile << Generator.GenRecordDeclare(&Ctx); - FuncDefineFile << Generator.GenRecordConvert(&Ctx); - FuncDefineFile << Generator.GenCallbackWrap(&Ctx); - FuncDefineFile << Generator.GenFuncDefine(&Ctx); - FuncDefineFile.close(); - std::cout << "--------------- Generator gen complete -----------------\n"; - } -private: - MyASTVisitor Visitor; - WrapperGenerator Generator; -}; - -class MyGenAction : public clang::ASTFrontendAction { -public: - MyGenAction(const std::string& libname, const std::string& host_triple, const std::string& guest_triple) : - libname(libname), host_triple(host_triple), guest_triple(guest_triple) {} - std::unique_ptr CreateASTConsumer(clang::CompilerInstance& Compiler, clang::StringRef file) override { - (void)file; - return std::make_unique(&Compiler.getASTContext(), libname, host_triple, guest_triple); - } -private: - std::string libname; - std::string host_triple; - std::string guest_triple; -}; - -class MyFrontendActionFactory : public clang::tooling::FrontendActionFactory { -public: - MyFrontendActionFactory(const std::string& libname, const std::string& host_triple, const std::string& guest_triple) : - libname(libname), host_triple(host_triple), guest_triple(guest_triple) {} -private: - std::unique_ptr create() override { - return std::make_unique(libname, host_triple, guest_triple); - } -private: - std::string libname; - std::string host_triple; - std::string guest_triple; -}; diff --git a/wrapperhelper/example-libc.h b/wrapperhelper/example-libc.h new file mode 100644 index 000000000..24dea13d6 --- /dev/null +++ b/wrapperhelper/example-libc.h @@ -0,0 +1,170 @@ +#define __x86_64__ +#define __WCHAR_MAX__ 2147483647 +#define __WCHAR_MIN__ (-__WCHAR_MAX - 1) +#define _GNU_SOURCE 1 +#define __USE_MISC 1 +#define PORTMAP +#define __WORDSIZE 64 + +// Based on /usr/include/clang/Basic/TokenKinds.def +// Alternate spelling for various tokens. There are GCC extensions in all +// languages, but should not be disabled in strict conformance mode. +#define __alignof__ __alignof +#define __asm asm +#define __asm__ asm +#define __complex _Complex +#define __complex__ _Complex +#define __const const +#define __const__ const +#define __decltype decltype +#define __imag__ __imag +#define __inline inline +#define __inline__ inline +#define __nullptr nullptr +#define __real__ __real +#define __restrict restrict +#define __restrict__ restrict +#define __signed signed +#define __signed__ signed +#define __typeof typeof +#define __typeof__ typeof +#define __volatile volatile +#define __volatile__ volatile + +typedef __int128 __int128_t; +typedef unsigned __int128 __uint128_t; + +// TODO +#define inline + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma wrappers explicit_simple FTS +#pragma wrappers explicit_simple FTS64 +#pragma wrappers explicit_simple glob_t +#pragma wrappers explicit_simple glob64_t diff --git a/wrapperhelper/gen.cpp b/wrapperhelper/gen.cpp deleted file mode 100644 index 743474cd8..000000000 --- a/wrapperhelper/gen.cpp +++ /dev/null @@ -1,1059 +0,0 @@ -#include "gen.h" -#include "utils.h" -#include -#include -#include -#include - -using namespace clang; -using namespace clang::tooling; - -static std::vector GetRecordFieldOff(const std::string& Code, const std::string& Triple) { - std::vector FieldOff; - std::vector Args = {"-target", Triple}; - std::unique_ptr AST = clang::tooling::buildASTFromCodeWithArgs(Code, Args); - auto& Ctx = AST->getASTContext(); - auto TranslateDecl = Ctx.getTranslationUnitDecl(); - for (const auto& Decl : TranslateDecl->decls()) { - if (const auto RecordDecl = clang::dyn_cast(Decl)) { - auto& RecordLayout = Ctx.getASTRecordLayout(RecordDecl); - for (unsigned i = 0; i < RecordLayout.getFieldCount(); i++) { - FieldOff.push_back(RecordLayout.getFieldOffset(i) / 8); - } - break; - } - } - return FieldOff; -} - -static uint64_t GetRecordSize(const std::string& Code, const std::string& Triple) { - std::vector Args = {"-target", Triple}; - std::unique_ptr AST = buildASTFromCodeWithArgs(Code, Args); - auto& Ctx = AST->getASTContext(); - auto TranslateDecl = Ctx.getTranslationUnitDecl(); - for (const auto& Decl : TranslateDecl->decls()) { - if (const auto recordDecl = dyn_cast(Decl)) { - return Ctx.getTypeSize(recordDecl->getTypeForDecl()) / 8; - } - } - return 0; -} - -static CharUnits::QuantityType GetRecordAlign(const std::string& Code, const std::string& Triple) { - std::vector Args = {"-target", Triple}; - std::unique_ptr AST = buildASTFromCodeWithArgs(Code, Args); - auto& Ctx = AST->getASTContext(); - auto TranslateDecl = Ctx.getTranslationUnitDecl(); - for (const auto& Decl : TranslateDecl->decls()) { - if (const auto recordDecl = dyn_cast(Decl)) { - auto& RecordLayout = Ctx.getASTRecordLayout(recordDecl); - for (unsigned i = 0; i < RecordLayout.getFieldCount(); i++) { - return RecordLayout.getAlignment().getQuantity() / 8; - } - break; - } - } - return 0; -} - -static uint64_t GetTypeSize(const Type* Type, const std::string& Triple) { - std::string Code = Type->getCanonicalTypeInternal().getAsString() + " dummy;"; - std::vector Args = {"-target", Triple}; - std::unique_ptr AST = buildASTFromCodeWithArgs(Code, Args); - auto& Ctx = AST->getASTContext(); - auto TranslateDecl = Ctx.getTranslationUnitDecl(); - for (const auto& Decl : TranslateDecl->decls()) { - if (const auto varDecl = dyn_cast(Decl)) { - return Ctx.getTypeSize(varDecl->getType()) / 8; - } - } - return 0; -} - -static std::string TypeToSig(ASTContext* Ctx, const Type* Type) { - if (Type->isBuiltinType()) { - switch (Type->getAs()->getKind()) { - case clang::BuiltinType::Kind::Void: - return "v"; - case clang::BuiltinType::Kind::Bool: - return "i"; - case clang::BuiltinType::Kind::Char_U: - return "C"; - case clang::BuiltinType::Kind::Char_S: - return "c"; - case clang::BuiltinType::Kind::Char8: - return "c"; - case clang::BuiltinType::Kind::UChar: - return "C"; - case clang::BuiltinType::Kind::SChar: - return "c"; - case clang::BuiltinType::Kind::WChar_U: - return "W"; - case clang::BuiltinType::Kind::UShort: - return "W"; - case clang::BuiltinType::Kind::WChar_S: - return "w"; - case clang::BuiltinType::Kind::Char16: - return "w"; - case clang::BuiltinType::Kind::Short: - return "w"; - case clang::BuiltinType::Kind::UInt: - return "u"; - case clang::BuiltinType::Kind::Char32: - return "i"; - case clang::BuiltinType::Kind::Int: - return "i"; - case clang::BuiltinType::Kind::ULong: - return "L"; - case clang::BuiltinType::Kind::Long: - return "l"; - case clang::BuiltinType::Kind::ULongLong: - return "U"; - case clang::BuiltinType::Kind::LongLong: - return "I"; - case clang::BuiltinType::Kind::UInt128: - return "H"; - case clang::BuiltinType::Kind::Int128: - return "H"; - case clang::BuiltinType::Kind::Float: - return "f"; - case clang::BuiltinType::Kind::Double: - return "d"; - case clang::BuiltinType::Kind::LongDouble: - return "D"; - case clang::BuiltinType::Kind::NullPtr: - return "p"; // nullptr_t - default: - std::cout << "Unsupported BuiltinType: " << Type->getCanonicalTypeInternal().getAsString() << std::endl; - } - } else { - if (Type->isPointerType()) { - return "p"; - } else if (Type->isVoidType()) { - return "v"; - } else if (Type->isUnsignedIntegerOrEnumerationType()) { - switch(Ctx->getTypeSizeInChars(Type).getQuantity()) { - case 1: - return "C"; - case 2: - return "W"; - case 4: - return "u"; - case 8: - return "U"; - default: - std::cout << "Unsupported UnsignedInteger Type: " << Type->getCanonicalTypeInternal().getAsString() << std::endl; - } - } else if (Type->isSignedIntegerOrEnumerationType()) { - switch(Ctx->getTypeSizeInChars(Type).getQuantity()) { - case 1: - return "c"; - case 2: - return "w"; - case 4: - return "i"; - case 8: - return "I"; - default: - std::cout << "Unsupported SignedInteger Type: " << Type->getCanonicalTypeInternal().getAsString() << std::endl; - } - } else if (Type->isCharType()) { - return "c"; - } else if (Type->isFloatingType()) { - switch(Ctx->getTypeSizeInChars(Type).getQuantity()) { - case 4: - return "f"; - case 8: - return "d"; - case 16: - return "D"; - default: - std::cout << "Unsupported Floating Type: " << Type->getCanonicalTypeInternal().getAsString() - << " (quantity = " << Ctx->getTypeSizeInChars(Type).getQuantity() << ")" << std::endl; - } - } else { - std::cout << "Unsupported Type: " << Type->getCanonicalTypeInternal().getAsString() << std::endl; - } - } - return "?"; -} - -// Prepare for generation, collect the structures and functions that need to be prcessed -void WrapperGenerator::Prepare(ASTContext *Ctx) { - for (const auto &func_pair : funcs) { - for (auto Type : func_pair.second.callback_args) { - if (Type && Type->isTypedefNameType()) { - callbacks[StripTypedef(Type->getPointeeType())] = - Type->getAs()->getDecl()->getNameAsString(); - } else if (Type) { - callbacks[StripTypedef(Type->getPointeeType())] = - GetFuncSig(Ctx, Type->getPointeeType().getTypePtr()) + "_t"; - } - } - } - std::vector Types; - for (const auto &record_pair : records) { - Types.push_back(record_pair.first); - } - for (auto type : Types) { - std::set Visited{type}; - bool Special = false; - ParseRecordRecursive(Ctx, type, Special, Visited); - } - for (auto it = records.begin(); it != records.end();) { - if (!it->second.is_special) { - it = records.erase(it); - } else { - for (auto field : it->second.callback_fields) { - if (field->isTypedefNameType()) { - callbacks[StripTypedef(field->getPointeeType())] = - field->getAs()->getDecl()->getNameAsString(); - } else { - callbacks[StripTypedef(field->getPointeeType())] = - GetFuncSig(Ctx, field->getPointeeType().getTypePtr()) + "_t"; - } - } - ++it; - } - } - for (auto &func_pair : funcs) { - for (unsigned i = 0; i < func_pair.second.decl->getNumParams(); i++) { - auto ParamDecl = func_pair.second.decl->getParamDecl(i); - auto ParamType = ParamDecl->getType(); - if (ParamType->isPointerType() && - ParamType->getPointeeType()->isRecordType()) { - if (records.find(StripTypedef(ParamType->getPointeeType())) != - records.end()) { - func_pair.second.has_special_arg = true; - break; - } - } else if (ParamType->isRecordType()) { - if (records.find(StripTypedef(ParamType)) != records.end()) { - func_pair.second.has_special_arg = true; - break; - } - } - } - auto RetType = func_pair.second.decl->getReturnType(); - if (RetType->isPointerType() && RetType->getPointeeType()->isRecordType()) { - if (records.find(StripTypedef(RetType->getPointeeType())) != - records.end()) { - func_pair.second.has_special_ret = true; - } - } else if (RetType->isRecordType()) { - if (records.find(StripTypedef(RetType)) != records.end()) { - func_pair.second.has_special_ret = true; - } - } - } -} - -// Gen callback typedef -std::string WrapperGenerator::GenCallbackTypeDefs(ASTContext *Ctx) { - (void)Ctx; - std::string res; - for (auto callback : callbacks) { - auto Type = callback.first; - auto Definition = GetFuncDefinition(Type); - res += "typedef " + Definition.ret_str + "(*" + callback.second + ")("; - for (int i = 0; i < Definition.arg_size - 1; i++) { - res += Definition.arg_types_str[i] + Definition.arg_names[i] + ", "; - } - if (Definition.arg_size) { - res += Definition.arg_types_str[Definition.arg_size - 1] + Definition.arg_names[Definition.arg_size - 1]; - } - res += ");\n"; - } - return res; -} - -// Gen function declare -std::string WrapperGenerator::GenDeclare(ASTContext *Ctx, - const FuncInfo &Func) { - std::string res; - std::string sig = GetFuncSig(Ctx, Func); - res += "GO"; - if (Func.is_weak) { - res += "W"; - } - if (sig.find('E') != std::string::npos) { - res += "M"; - } - res += "(" + Func.func_name + ", " + sig + ")\n"; - ; - return res; -} - -// Gen structure declare -std::string WrapperGenerator::GenDeclare(ASTContext *Ctx, - const RecordInfo &Record) { - (void)Ctx; - std::string RecordStr; - std::string PreDecl; - RecordStr += "\ntypedef "; - RecordStr += - (Record.is_union ? "union " : "struct ") + Record.type_name + " {\n"; - for (const auto &Field : Record.decl->fields()) { - auto Type = Field->getType(); - std::string Name = Field->getNameAsString(); - RecordStr += " "; - if (Type->isFunctionPointerType()) { - auto FuncType = StripTypedef(Type->getPointeeType()); - if (callbacks.count(FuncType)) { - std::string FieldStr = callbacks[FuncType]; - FieldStr += " "; - FieldStr += Name; - RecordStr += FieldStr; - } else { - std::cout << "Err: FuncPtr(" << Record.type_name << "." << Name << ") is not supported\n"; - } - } else if (Type->isPointerType()) { - auto PointeeType = Type->getPointeeType(); - if (PointeeType->isRecordType()) { - if (records.count(PointeeType.getTypePtr())) { - std::string FieldStr = records[PointeeType.getTypePtr()].type_name; - FieldStr += "_ptr "; - FieldStr += Name; - RecordStr += FieldStr; - } else { - RecordStr += "void *" + Name; - } - } else { - RecordStr += "void *" + Name; - } - } else if (Type->isRecordType()) { - if (records.count(Type.getTypePtr())) { - std::string FieldStr = records[Type.getTypePtr()].type_name; - FieldStr += " "; - FieldStr += Name; - RecordStr += FieldStr; - } else { - RecordStr += TypeStringify(StripTypedef(Type), Field, nullptr, PreDecl); - } - } else { - RecordStr += TypeStringify(StripTypedef(Type), Field, nullptr, PreDecl); - } - RecordStr += ";\n"; - } - RecordStr += "} "; - RecordStr += Record.type_name + ", *" + Record.type_name + "_ptr;\n"; - return RecordStr; -} - -std::string WrapperGenerator::GenCallbackWrap(ASTContext *Ctx, - const FuncInfo &Func) { - (void)Ctx; - std::string res; - - for (unsigned i = 0; i < Func.decl->getNumParams(); i++) { - auto ParamType = Func.decl->getParamDecl(i)->getType(); - if (ParamType->isFunctionPointerType()) { - - auto PointeeType = ParamType->getPointeeType(); - auto Definition = GetFuncDefinition(PointeeType.getTypePtr()); - std::string my_funcname = - std::string("my_") + Func.decl->getParamDecl(i)->getNameAsString(); - std::string funcname = Func.decl->getParamDecl(i)->getNameAsString(); - res += "\n#define GO(A) \\\n" - "static uintptr_t " + my_funcname + "_fct_##A = 0; \\\n" + - Definition.ret_str + " " + my_funcname + "("; - int arg_size = Definition.arg_names.size(); - if (arg_size) { - for (int i = 0; i < arg_size - 1; i++) { - res += Definition.arg_types_str[i] + " " + Definition.arg_names[i] + ", "; - } - res += Definition.arg_types_str[arg_size - 1] + " " + Definition.arg_names[arg_size - 1]; - } - res += ") { \\\n" - " return RunFunction(my_context, " + my_funcname + "_fct_##A" + ", " + std::to_string(arg_size); - for (int i = 0; i < arg_size; i++) { - res += ", " + Definition.arg_names[i]; - } - res += "); \\\n" - "}\n" - "SUPER()\n" - "#undef GO\n" - "static void* find" + funcname + "Fct(void* fct) {\n" - " if (!fct) return fct;\n" - " if (GetNativeFnc((uintptr_t)fct)) return GetNativeFnc((uintptr_t)fct);\n" - " #define GO(A) if (" + my_funcname + "_fct_##A == (uintptr_t)fct) return " + my_funcname + "_##A;\n" - " SUPER()\n" - " #undef GO\n" - " #define GO(A) if (" + my_funcname + "_fct_##A == 0) { " + my_funcname + "_fct_##A = (uintptr_t)fct; return " + my_funcname + "_##A; }\n" - " SUPER()\n" - " #undef GO\n" - " return NULL;\n" - "}\n"; - } - } - return res; -} - -std::string WrapperGenerator::GenCallbackWrap(ASTContext *Ctx, - const RecordInfo &Struct) { - (void)Ctx; - std::string res; - for (const auto &field : Struct.decl->fields()) { - auto FieldType = field->getType(); - if (FieldType->isFunctionPointerType()) { - auto PointeeType = FieldType->getPointeeType(); - auto Definition = GetFuncDefinition(PointeeType.getTypePtr()); - std::string my_funcname = std::string("my_") + field->getNameAsString(); - std::string funcname = field->getNameAsString(); - res += "\n#define GO(A) \\\n" - "static uintptr_t " + my_funcname + "_fct_##A = 0; \\\n" + - Definition.ret_str + " " + my_funcname + "_##A("; - int arg_size = Definition.arg_names.size(); - if (arg_size) { - for (int i = 0; i < arg_size - 1; i++) { - res += Definition.arg_types_str[i] + " " + Definition.arg_names[i] + ", "; - } - res += Definition.arg_types_str[arg_size - 1] + " " + Definition.arg_names[arg_size - 1]; - } - res += ") { \\\n" - " return RunFunction(my_context, " + my_funcname + "_fct_##A" + ", " + std::to_string(arg_size); - for (int i = 0; i < arg_size; i++) { - res += ", " + Definition.arg_names[i]; - } - res += "); \\\n" - "}\n" - "SUPER()\n" - "#undef GO\n" - "static void* find" + funcname + "Fct(void* fct) {\n" - " if(!fct) return fct;\n" - " if(GetNativeFnc((uintptr_t)fct)) return GetNativeFnc((uintptr_t)fct);\n" - " #define GO(A) if(" + my_funcname + "_fct_##A == (uintptr_t)fct) return " + my_funcname + "_##A;}\n" - " SUPER()\n" - " #undef GO\n" - " #define GO(A) if(" + my_funcname + "_fct_##A == 0) {" + my_funcname + "_fct_##A = (uintptr_t)fct;" + "return " + my_funcname + "_##A;}\n" - " SUPER()\n" - " #undef GO\n" - " return NULL;\n" - "}\n"; - } - } - return res; -} - -std::string WrapperGenerator::GenDefine(ASTContext *Ctx, - const FuncInfo &Func) { - std::string res; - auto Definition = GetFuncDefinition(Func.decl); - std::string Sig = GetFuncSig(Ctx, Func.type); - res += "\nEXPORT " + Definition.ret_str + "my_" + Func.func_name + "("; - if (Sig.find('E')) { - res += "void *emu, "; - } - int arg_size = Definition.arg_names.size(); - if (arg_size) { - for (int i = 0; i < arg_size - 1; i++) { - if (Definition.arg_types[i]->isPointerType()) { - auto PointeeType = Definition.arg_types[i]->getPointeeType(); - if (records.count(PointeeType.getTypePtr())) { - res += - Definition.arg_types[i]->getCanonicalTypeInternal().getAsString(); - } else { - res += Definition.arg_types_str[i]; - } - } else { - res += Definition.arg_types_str[i]; - } - res += " " + Definition.arg_names[i] + ", "; - } - if (Definition.arg_types[arg_size - 1]->isPointerType()) { - auto PointeeType = Definition.arg_types[arg_size - 1]->getPointeeType(); - if (records.count(PointeeType.getTypePtr())) { - res += Definition.arg_types[arg_size - 1] - ->getCanonicalTypeInternal() - .getAsString(); - } else { - res += Definition.arg_types_str[arg_size - 1]; - } - } else { - res += Definition.arg_types_str[arg_size - 1]; - } - res += " "; - res += Definition.arg_names[arg_size - 1]; - } - res += ") {\n"; - if (Func.has_special_arg) { - res += " // WARN: This function's arg has a structure ptr which is " - "special, may need to wrap it for the host\n"; - - } else if (Func.has_special_ret) { - res += " // WARN: This function's ret is a structure ptr which is " - "special, may need to wrap it for the guest\n"; - } - if (Func.has_callback_arg) { - res += " " + my_lib_type + " *my = " + "(" + my_lib_type + "*)" + - my_lib + "->priv.w.p2;\n" - " my->" + Func.func_name + "("; - if (arg_size) { - for (int i = 0; i < arg_size - 1; i++) { - if (Func.callback_args[i]) { - if (!Func.callback_args[i]->isTypedefNameType()) { - res += - "find" + Func.func_name + "_arg" + std::to_string(i) + "Fct"; - } else { - res += "find" + - Func.callback_args[i] - ->getAs() - ->getDecl() - ->getNameAsString() + - "Fct"; - } - res += "(" + Definition.arg_names[i] + ")"; - } else { - res += Definition.arg_names[i]; - } - res += ", "; - } - if (Func.callback_args[arg_size - 1]) { - if (!Func.callback_args[arg_size - 1]->isTypedefNameType()) { - res += "find" + Func.func_name + "_arg" + - std::to_string(arg_size - 1) + "Fct"; - } else { - res += "find" + - Func.callback_args[arg_size - 1] - ->getAs() - ->getDecl() - ->getNameAsString() + - "Fct"; - } - res += "(" + Definition.arg_names[arg_size - 1] + ")"; - } else { - res += Definition.arg_names[arg_size - 1]; - } - res += ")\n"; - } - } else { - res += " " + my_lib_type + " *my = (" + my_lib_type + "*)" + my_lib + "->priv.w.p2;\n" - " my->" + Func.func_name + "("; - if (arg_size) { - for (int i = 0; i < arg_size - 1; i++) { - res += Definition.arg_names[i] + ", "; - } - res += Definition.arg_names[arg_size - 1]; - } - res += ");\n"; - } - - res += "}\n"; - return res; -} - -std::string WrapperGenerator::GenDeclareDiffTriple( - ASTContext *Ctx, const RecordInfo &Record, - const std::string &GuestTriple, const std::string &HostTriple) { - (void)Ctx; - std::string GuestRecord; - std::string HostRecord; - std::string PreDecl; - std::vector GuestFieldOff; - std::vector HostFieldOff; - GuestRecord += "typedef "; - HostRecord += "typedef "; - GuestRecord += - (Record.is_union ? "union " : "struct ") + Record.type_name + " {\n"; - HostRecord += (Record.is_union ? "union " : "struct ") + - std::string("host_") + Record.type_name + " {\n"; - auto OffDiff = GetRecordFieldOffDiff(Record.type, GuestTriple, HostTriple, - GuestFieldOff, HostFieldOff); - uint64_t GuestRecordSize = GetRecordSize(Record.type, GuestTriple); - uint64_t HostRecordSize = GetRecordSize(Record.type, HostTriple); - uint64_t SizeDiff = GuestRecordSize - HostRecordSize; - int FieldIndex = 0; - std::set AlignDiffFields; - for (const auto &Field : Record.decl->fields()) { - if (OffDiff[FieldIndex] == 0) { - FieldIndex++; - continue; - } - std::string Name = Field->getNameAsString(); - if (OffDiff[FieldIndex] != SizeDiff) { - auto Diff = OffDiff[FieldIndex]; - AlignDiffFields.insert(Field); - for (size_t i = FieldIndex; i < OffDiff.size(); i++) { - if (OffDiff[i] == Diff) { - OffDiff[i] = 0; - } else { - break; - } - } - } else { - AlignDiffFields.insert(Field); - break; - } - FieldIndex++; - } - for (const auto &Field : Record.decl->fields()) { - auto Type = Field->getType(); - std::string Name = Field->getNameAsString(); - GuestRecord += " "; - HostRecord += " "; - if (AlignDiffFields.find(Field) != AlignDiffFields.end()) { - auto typeSize = GetTypeSize(StripTypedef(Field->getType()), guest_triple); - switch (typeSize) { - // FIXME: should test more case in different triple - case 4: GuestRecord += "int " + Name ; break; - case 8: GuestRecord += "int " + Name + "[2]"; break; - default: - std::cout << "Err: unknown type size " << typeSize << std::endl; - break; - } - HostRecord += TypeStringify(StripTypedef(Type), Field, nullptr, PreDecl); - } else if (Type->isFunctionPointerType()) { - auto FuncType = StripTypedef(Type->getPointeeType()); - if (callbacks.count(FuncType)) { - std::string FieldStr = callbacks[FuncType]; - FieldStr += " "; - FieldStr += Name; - GuestRecord += FieldStr; - HostRecord += FieldStr; - } else { - std::cout << "Err: FuncPtr(" << Record.type_name << "." << Name << ") is not supported" << std::endl; - } - } else if (Type->isPointerType()) { - auto PointeeType = Type->getPointeeType(); - if (PointeeType->isRecordType()) { - if (records.count(PointeeType.getTypePtr())) { - std::string FieldStr = records[PointeeType.getTypePtr()].type_name; - FieldStr += "_ptr " + Name; - GuestRecord += FieldStr; - HostRecord += "host_" + FieldStr; - } else { - GuestRecord += "void *" + Name; - HostRecord += "void *" + Name; - } - } else { - GuestRecord += "void *" + Name; - HostRecord += "void *" + Name; - } - } else if (Type->isRecordType()) { - if (records.count(Type.getTypePtr())) { - std::string FieldStr = records[Type.getTypePtr()].type_name; - FieldStr += " " + Name; - GuestRecord += FieldStr; - HostRecord += "host_" + FieldStr; - } else { - GuestRecord += TypeStringify(StripTypedef(Type), Field, nullptr, PreDecl); - HostRecord += TypeStringify(StripTypedef(Type), Field, nullptr, PreDecl); - } - } else { - HostRecord += TypeStringify(StripTypedef(Type), Field, nullptr, PreDecl); - GuestRecord += TypeStringify(StripTypedef(Type), Field, nullptr, PreDecl); - } - GuestRecord += ";\n"; - HostRecord += ";\n"; - } - GuestRecord += "} " + Record.type_name + ", *" + Record.type_name + "_ptr;\n"; - HostRecord += "} host_" + Record.type_name + ", *host_" + Record.type_name + "_ptr;\n"; - return GuestRecord + HostRecord; -} - -// Gen record convert function between host and guest -std::string WrapperGenerator::GenRecordConvert(const RecordInfo &Record) { - std::string res; - if (Record.guest_size != Record.host_size) { - auto RecordDecl = Record.decl; - std::vector GuestFieldOff; - std::vector HostFieldOff; - auto OffDiff = GetRecordFieldOffDiff(Record.type, guest_triple, host_triple, - GuestFieldOff, HostFieldOff); - int FieldIndex = 0; - std::vector AlignDiffFields; - uint64_t SizeDiff = Record.guest_size - Record.host_size; - for (const auto &Field : RecordDecl->fields()) { - if (OffDiff[FieldIndex] == 0) { - FieldIndex++; - continue; - } - if (OffDiff[FieldIndex] != SizeDiff) { - auto Diff = OffDiff[FieldIndex]; - AlignDiffFields.push_back(Field); - for (size_t i = FieldIndex; i < OffDiff.size(); i++) { - if (OffDiff[i] == Diff) { - OffDiff[i] = 0; - } else { - break; - } - } - } else { - AlignDiffFields.push_back(Field); - break; - } - FieldIndex++; - } - if (!AlignDiffFields.size()) { - return res; - } - res += "void g2h_" + Record.type_name + "(" + "struct host_" + Record.type_name + " *d, struct " + Record.type_name + " *s) {\n"; - std::string body = " memcpy(d, s, offsetof(struct " + Record.type_name + - ", " + AlignDiffFields[0]->getNameAsString() + "));\n"; - std::string offstr = "offsetof(struct " + Record.type_name + ", " + - AlignDiffFields[0]->getNameAsString() + ")"; - for (size_t i = 0; i < AlignDiffFields.size() - 1; i++) { - body += " memcpy(d->" + AlignDiffFields[i]->getNameAsString() + ", " + - "s->" + AlignDiffFields[i]->getNameAsString() + ", " + - "offsetof(struct " + Record.type_name + ", " + - AlignDiffFields[i + 1]->getNameAsString() + ") - " + offstr + - ");\n"; - offstr = "offsetof(struct " + Record.type_name + ", " + - AlignDiffFields[i + 1]->getNameAsString() + ")"; - } - body += " memcpy(d->" + - AlignDiffFields[AlignDiffFields.size() - 1]->getNameAsString() + - ", " + "s->" + - AlignDiffFields[AlignDiffFields.size() - 1]->getNameAsString() + - ", " + std::to_string(GetRecordSize(Record.type, guest_triple)) + - " - " + offstr + ");\n"; - res += body + "}\n"; - - res += "void h2g_" + Record.type_name + "(struct " + Record.type_name + " *d, " + "struct host_" + Record.type_name + " *s) {\n"; - res += body; - res += "}\n"; - } - return res; -} - -void WrapperGenerator::ParseRecordRecursive( - ASTContext *Ctx, const Type *type, bool &Special, - std::set &Visited) { - auto recordType = type->getAs(); - auto RecordDecl = recordType->getDecl(); - for (const auto &field : RecordDecl->fields()) { - auto FieldType = field->getType(); - if (FieldType->isFunctionPointerType()) { - auto Record = &records[type]; - Record->callback_fields.push_back(field->getType().getTypePtr()); - // Record->type_name = - Special = true; - } else if (FieldType->isPointerType() && - FieldType->getPointeeType()->isRecordType()) { - auto FieldRecordType = StripTypedef(FieldType->getPointeeType()); - if (Visited.find(FieldRecordType) != Visited.end()) - continue; - Visited.insert(FieldRecordType); - bool _Special = false; - ParseRecordRecursive(Ctx, FieldRecordType, _Special, Visited); - if (_Special) - Special = true; - } else if (FieldType->isRecordType()) { - auto FieldRecordType = StripTypedef(FieldType); - if (Visited.find(FieldRecordType) != Visited.end()) - continue; - Visited.insert(FieldRecordType); - bool _Special = false; - ParseRecordRecursive(Ctx, FieldRecordType, _Special, Visited); - if (_Special) - Special = true; - } - } - uint64_t GuestSize = GetRecordSize(type, guest_triple); - uint64_t HostSize = GetRecordSize(type, host_triple); - - auto Record = &records[type]; - if (GuestSize != HostSize) { - Special = 1; - } - if (type->isUnionType()) { - Record->is_union = true; - } - if (!Record->decl) { - Record->type = type; - Record->decl = RecordDecl; - if (RecordDecl->getIdentifier()) - Record->type_name = RecordDecl->getIdentifier()->getName().str(); - } - Record->guest_size = GuestSize; - Record->host_size = HostSize; - if (Record->type_name.empty()) { - Record->is_special = false; - } else - Record->is_special = Special; -} - -// Type to String -std::string WrapperGenerator::TypeStringify(const Type *Type, - FieldDecl *FieldDecl, - ParmVarDecl *ParmDecl, - std::string& PreDecl, - std::string indent, - std::string Name) { - std::string res; - std::string name = FieldDecl - ? FieldDecl->getNameAsString() - : (ParmDecl ? ParmDecl->getNameAsString() : Name); - if (Type->isPointerType()) { - auto PointeeType = Type->getPointeeType(); - if (PointeeType->isBuiltinType()) { - res += - StripTypedef(PointeeType)->getCanonicalTypeInternal().getAsString(); - } else if (PointeeType->isRecordType()) { - if (records.find(StripTypedef(PointeeType)) != records.end() && - records[StripTypedef(PointeeType)].is_special) { - res += (PointeeType->isUnionType() ? "union " : "struct ") + records[StripTypedef(PointeeType)].type_name; - } else { - res += "void"; - } - } else { - res += "void"; - } - res += " *" + name; - } else if (Type->isEnumeralType()) { - res += "int " + name; - } else if (Type->isRecordType()) { - if (records.find(StripTypedef(Type->getCanonicalTypeInternal())) != - records.end() && - records[StripTypedef(Type->getCanonicalTypeInternal())].is_special) { - res += Type->isUnionType() ? "union " : "struct "; - res += records[StripTypedef(Type->getCanonicalTypeInternal())].type_name; - res += " "; - } else { - res += AnonRecordDecl(Type->getAs(), PreDecl, indent + " "); - } - res += name; - } else if (Type->isConstantArrayType()) { - auto ArrayType = - dyn_cast(Type->getAsArrayTypeUnsafe()); - int EleSize = ArrayType->getSize().getZExtValue(); - if (ArrayType->getElementType()->isPointerType()) { - res += "void *"; - } else if (ArrayType->getElementType()->isEnumeralType()) { - res += "int "; - } else if (ArrayType->getElementType()->isRecordType()) { - auto RecordType = ArrayType->getElementType()->getAs(); - auto RecordDecl = RecordType->getDecl(); - if (RecordDecl->isCompleteDefinition()) { - auto& Ctx = RecordDecl->getDeclContext()->getParentASTContext(); - PreDecl += "#include \""; - PreDecl += GetDeclHeaderFile(Ctx, RecordDecl); - PreDecl += "\""; - PreDecl += "\n"; - } - res += StripTypedef(ArrayType->getElementType()) - ->getCanonicalTypeInternal() - .getAsString(); - } else { - res += StripTypedef(ArrayType->getElementType()) - ->getCanonicalTypeInternal() - .getAsString(); - } - res += " "; - res += name; - res += "["; - res += std::to_string(EleSize); - res += "]"; - } else { - res += StripTypedef(Type->getCanonicalTypeInternal()) - ->getCanonicalTypeInternal() - .getAsString(); - res += " "; - res += name; - } - return indent + res; -} - -// Type to String, less detail -std::string WrapperGenerator::SimpleTypeStringify(const Type *Type, - FieldDecl *FieldDecl, - ParmVarDecl *ParmDecl, - std::string indent, - std::string Name) { - std::string res; - std::string name = FieldDecl - ? FieldDecl->getNameAsString() - : (ParmDecl ? ParmDecl->getNameAsString() : Name); - if (Type->isPointerType()) { - res += "void *" + name; - } else if (Type->isEnumeralType()) { - res += "int "; - res += name; - } else if (Type->isRecordType()) { - if (records.find(StripTypedef(Type->getCanonicalTypeInternal())) != - records.end()) { - res += Type->isUnionType() ? "union " : "struct "; - res += records[StripTypedef(Type->getCanonicalTypeInternal())].type_name; - res += " "; - } else { - res += SimpleAnonRecordDecl(Type->getAs(), indent); - } - res += name; - } else if (Type->isConstantArrayType()) { - auto ArrayType = - dyn_cast(Type->getAsArrayTypeUnsafe()); - int EleSize = ArrayType->getSize().getZExtValue(); - if (ArrayType->getElementType()->isPointerType()) { - res += "void *"; - } else { - res += StripTypedef(ArrayType->getElementType()) - ->getCanonicalTypeInternal() - .getAsString(); - } - res += " "; - res += name; - res += "["; - res += std::to_string(EleSize); - res += "]"; - } else { - res += StripTypedef(Type->getCanonicalTypeInternal()) - ->getCanonicalTypeInternal() - .getAsString(); - res += " "; - res += name; - } - return indent + res; -} - -std::string WrapperGenerator::AnonRecordDecl(const RecordType *Type, std::string& PreDecl, std::string indent) { - auto RecordDecl = Type->getDecl(); - std::string res; - res += Type->isUnionType() ? "union {\n" : "struct {\n"; - for (const auto &field : RecordDecl->fields()) { - auto FieldType = field->getType(); - res += TypeStringify(StripTypedef(FieldType), field, nullptr, PreDecl, indent + " "); - res += ";\n"; - } - res += indent + "} "; - return res; -} - -std::string -WrapperGenerator::SimpleAnonRecordDecl(const RecordType *Type, std::string indent) { - auto RecordDecl = Type->getDecl(); - std::string res; - res += Type->isUnionType() ? "union {\n" : "struct {\n"; - for (const auto &field : RecordDecl->fields()) { - auto FieldType = field->getType(); - res += SimpleTypeStringify(StripTypedef(FieldType), field, nullptr, indent + " "); - res += ";\n"; - } - res += indent + "} "; - return res; -} - -// Get func info from FunctionType -FuncDefinition WrapperGenerator::GetFuncDefinition(const Type *Type) { - FuncDefinition res; - std::string PreDecl; - auto ProtoType = Type->getAs(); - res.ret = StripTypedef(ProtoType->getReturnType()); - res.ret_str = - TypeStringify(StripTypedef(ProtoType->getReturnType()), nullptr, nullptr, PreDecl); - for (unsigned i = 0; i < ProtoType->getNumParams(); i++) { - auto ParamType = ProtoType->getParamType(i); - res.arg_types.push_back(StripTypedef(ParamType)); - res.arg_types_str.push_back( - TypeStringify(StripTypedef(ParamType), nullptr, nullptr, PreDecl)); - res.arg_names.push_back(std::string("a") + std::to_string(i)); - } - if (ProtoType->isVariadic()) { - res.is_variadaic = true; - } - res.arg_size = ProtoType->getNumParams(); - return res; -} - -// Get funcdecl info from FunctionDecl -FuncDefinition WrapperGenerator::GetFuncDefinition(FunctionDecl *Decl) { - FuncDefinition res; - std::string PreDecl; - auto RetType = Decl->getReturnType(); - res.ret = RetType.getTypePtr(); - res.ret_str = TypeStringify(StripTypedef(RetType), nullptr, nullptr, PreDecl); - for (unsigned i = 0; i < Decl->getNumParams(); i++) { - auto ParamDecl = Decl->getParamDecl(i); - auto ParamType = ParamDecl->getType(); - res.arg_types.push_back(ParamType.getTypePtr()); - res.arg_types_str.push_back( - TypeStringify(StripTypedef(ParamType), nullptr, nullptr, PreDecl)); - res.arg_names.push_back(ParamDecl->getNameAsString()); - } - if (Decl->isVariadic()) { - res.is_variadaic = true; - } - return res; -} - -// Get the offset diff between two different triple -std::vector WrapperGenerator::GetRecordFieldOffDiff( - const Type *Type, const std::string &GuestTriple, - const std::string &HostTriple, std::vector &GuestFieldOff, - std::vector &HostFieldOff) { - std::string PreDecl; - std::string Code = TypeStringify(Type, nullptr, nullptr, PreDecl, "", "dummy;"); - std::vector OffsetDiff; - GuestFieldOff = GetRecordFieldOff(Code, GuestTriple); - HostFieldOff = GetRecordFieldOff(Code, HostTriple); - if (GuestFieldOff.size() != HostFieldOff.size()) { - // Should not happen - std::cout << "Greater field offsets in guest than in host" << std::endl; - return OffsetDiff; - } - for (size_t i = 0; i < GuestFieldOff.size(); i++) { - OffsetDiff.push_back(GuestFieldOff[i] - HostFieldOff[i]); - } - return OffsetDiff; -} - -// Get the size under a specific triple -uint64_t WrapperGenerator::GetRecordSize(const Type *Type, - const std::string &Triple) { - std::string PreDecl; - std::string Code = TypeStringify(Type, nullptr, nullptr, PreDecl, "", "dummy;"); - auto Size = ::GetRecordSize(PreDecl + Code, Triple); - return Size; -} - -// Get the align under a specific triple -CharUnits::QuantityType WrapperGenerator::GetRecordAlign(const Type *Type, - const std::string &Triple) { - std::string PreDecl{}; - std::string Code = TypeStringify(Type, nullptr, nullptr, PreDecl, "", "dummy;"); - return ::GetRecordAlign(PreDecl + Code, Triple); -} - -// Generate the func sig by type, used for export func -std::string WrapperGenerator::GetFuncSig(ASTContext *CTX, - const FuncInfo &Func) { - std::string sig; - auto Decl = Func.decl; - auto Type = Decl->getType().getTypePtr(); - auto ProtoType = Type->getAs(); - auto RetType = ProtoType->getReturnType(); - - sig += TypeToSig(CTX, RetType.getTypePtr()); - sig += "F"; - if (Func.has_special_arg || Func.has_special_ret || Func.has_callback_arg) { - sig += "E"; - } - if (ProtoType->getNumParams()) { - for (unsigned i = 0; i < ProtoType->getNumParams(); i++) { - sig += TypeToSig(CTX, ProtoType->getParamType(i).getTypePtr()); - } - } else { - sig += "v"; - } - if (Decl->isVariadic()) { - sig += "VV"; - } - return sig; -} - -// Generate the func sig by type, used for callbacks -std::string WrapperGenerator::GetFuncSig(ASTContext *CTX, - const Type *Type) { - std::string sig; - auto ProtoType = Type->getAs(); - auto RetType = ProtoType->getReturnType(); - sig += TypeToSig(CTX, RetType.getTypePtr()); - sig += "F"; - if (ProtoType->getNumParams()) { - for (unsigned i = 0; i < ProtoType->getNumParams(); i++) { - sig += TypeToSig(CTX, ProtoType->getParamType(i).getTypePtr()); - } - } else { - sig += "v"; - } - return sig; -} diff --git a/wrapperhelper/gen.h b/wrapperhelper/gen.h deleted file mode 100644 index 55ecd202c..000000000 --- a/wrapperhelper/gen.h +++ /dev/null @@ -1,144 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include - -struct FuncDefinition { - std::vector arg_types; - std::vector arg_types_str; - std::vector arg_names; - const clang::Type* ret; - std::string ret_str; - int arg_size; - bool is_variadaic; -}; - -struct FuncInfo { - const clang::Type* type; - clang::FunctionDecl* decl; - std::string func_name; - bool is_weak; - bool is_variadaic; - bool has_special_arg; - bool has_special_ret; - bool has_callback_arg; - std::vector callback_args; -}; - -struct RecordInfo { - const clang::Type* type; - clang::RecordDecl* decl; - std::string type_name; - bool is_union; - bool is_special; - - uint64_t guest_size; - uint64_t host_size; - std::vector callback_fields; -}; - -struct ObjectInfo { - const clang::Type* type; - std::string object_name; -}; - -struct WrapperGenerator { - - void Init(const std::string& libname, const std::string& host_triple, const std::string& guest_triple) { - this->host_triple = host_triple; - this->guest_triple = guest_triple; - this->libname = libname; - this->my_lib_type = libname + "_my_t"; - this->my_lib = "my_lib"; - } - - void Prepare(clang::ASTContext* Ctx); - - std::string GenCallbackTypeDefs(clang::ASTContext* Ctx); - - std::string GenFuncDeclare(clang::ASTContext* Ctx) { - std::string res{}; - for (const auto& func : funcs) { - res += GenDeclare(Ctx, func.second); - } - return res; - } - std::string GenRecordDeclare(clang::ASTContext* Ctx) { - std::string res{}; - for (const auto& st : records) { - if (st.second.host_size == st.second.guest_size) - res += GenDeclare(Ctx, st.second); - else { - res += GenDeclareDiffTriple(Ctx, st.second, guest_triple, host_triple); - } - } - return res; - } - - std::string GenFuncDefine(clang::ASTContext* Ctx) { - std::string res{}; - for (const auto& func : funcs) { - res += GenDefine(Ctx, func.second); - } - return res; - } - - std::string GenCallbackWrap(clang::ASTContext* Ctx) { - std::string res{}; - for (const auto& func : funcs) { - res += GenCallbackWrap(Ctx, func.second); - } - for (const auto& st : records) { - res += GenCallbackWrap(Ctx, st.second); - } - return res; - } - - std::string GenRecordConvert(clang::ASTContext* Ctx) { - (void)Ctx; - std::string res; - for (const auto& record : records) { - if (record.second.host_size != record.second.guest_size) { - res += GenRecordConvert(record.second); - } - } - return res; - } - - std::map funcs; - std::map records; - std::map objects; - - std::map callbacks; - - std::string host_triple; - std::string guest_triple; - std::string libname; - std::string my_lib_type; - std::string my_lib; -private: - std::string GenRecordConvert(const RecordInfo& Record); - std::string GenDeclare(clang::ASTContext* Ctx, const FuncInfo& Func); - std::string GenDefine(clang::ASTContext* Ctx, const FuncInfo& Func); - std::string GenCallbackWrap(clang::ASTContext* Ctx, const FuncInfo& Func); - std::string GenDeclareDiffTriple(clang::ASTContext* Ctx, const RecordInfo& Record, const std::string& GuestTriple, const std::string& HostTriple); - std::string GenDeclare(clang::ASTContext* Ctx, const RecordInfo& Struct); - std::string GenCallbackWrap(clang::ASTContext* Ctx, const RecordInfo& Struct); - - void ParseRecordRecursive(clang::ASTContext* Ctx, const clang::Type* Type, bool& Special, std::set& Visited); - std::string TypeStringify(const clang::Type* Type, clang::FieldDecl* FieldDecl, clang::ParmVarDecl* ParmDecl, std::string& PreDecl, std::string indent = "", std::string Name = ""); - std::string SimpleTypeStringify(const clang::Type* Type, clang::FieldDecl* FieldDecl, clang::ParmVarDecl* ParmDecl, std::string indent = "", std::string Name = ""); - std::string AnonRecordDecl(const clang::RecordType* Type, std::string& PreDecl, std::string indent); - std::string SimpleAnonRecordDecl(const clang::RecordType* Type, std::string indent); - FuncDefinition GetFuncDefinition(const clang::Type* Type); - FuncDefinition GetFuncDefinition(clang::FunctionDecl* Decl); - uint64_t GetRecordSize(const clang::Type* Type, const std::string& Triple); - std::vector GetRecordFieldOffDiff(const clang::Type* Type, const std::string& GuestTriple, const std::string& HostTriple, std::vector& GuestFieldOff, std::vector& HostFieldOff); - clang::CharUnits::QuantityType GetRecordAlign(const clang::Type* Type, const std::string& Triple); - - std::string GetFuncSig(clang::ASTContext* CTX, const FuncInfo& Decl); - std::string GetFuncSig(clang::ASTContext* CTX, const clang::Type* Type); -}; diff --git a/wrapperhelper/include-fixed/bits/stdint-intn.h b/wrapperhelper/include-fixed/bits/stdint-intn.h new file mode 100644 index 000000000..14abdb46e --- /dev/null +++ b/wrapperhelper/include-fixed/bits/stdint-intn.h @@ -0,0 +1,32 @@ +/* Define intN_t types. + Copyright (C) 2017-2024 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . + +This file has been adapted to work with the 'wrapperhelper' project on the 09/06/2024. +*/ + +#ifndef _BITS_STDINT_INTN_H +#define _BITS_STDINT_INTN_H 1 + +#include + +typedef __int8_t int8_t; +typedef __int16_t int16_t; +typedef __int32_t int32_t; +typedef __int64_t int64_t; + +#endif /* bits/stdint-intn.h */ diff --git a/wrapperhelper/include-fixed/bits/stdint-least.h b/wrapperhelper/include-fixed/bits/stdint-least.h new file mode 100644 index 000000000..d9753d7dc --- /dev/null +++ b/wrapperhelper/include-fixed/bits/stdint-least.h @@ -0,0 +1,39 @@ +/* Define int_leastN_t and uint_leastN types. + Copyright (C) 2024 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . + +This file has been adapted to work with the 'wrapperhelper' project on the 09/06/2024. +*/ + +#ifndef _BITS_STDINT_LEAST_H +#define _BITS_STDINT_LEAST_H 1 + +#include + +/* Signed. */ +typedef __int_least8_t int_least8_t; +typedef __int_least16_t int_least16_t; +typedef __int_least32_t int_least32_t; +typedef __int_least64_t int_least64_t; + +/* Unsigned. */ +typedef __uint_least8_t uint_least8_t; +typedef __uint_least16_t uint_least16_t; +typedef __uint_least32_t uint_least32_t; +typedef __uint_least64_t uint_least64_t; + +#endif /* bits/stdint-least.h */ diff --git a/wrapperhelper/include-fixed/bits/stdint-uintn.h b/wrapperhelper/include-fixed/bits/stdint-uintn.h new file mode 100644 index 000000000..cf2e8b4e3 --- /dev/null +++ b/wrapperhelper/include-fixed/bits/stdint-uintn.h @@ -0,0 +1,32 @@ +/* Define uintN_t types. + Copyright (C) 2017-2024 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . + +This file has been adapted to work with the 'wrapperhelper' project on the 09/06/2024. +*/ + +#ifndef _BITS_STDINT_UINTN_H +#define _BITS_STDINT_UINTN_H 1 + +#include + +typedef __uint8_t uint8_t; +typedef __uint16_t uint16_t; +typedef __uint32_t uint32_t; +typedef __uint64_t uint64_t; + +#endif /* bits/stdint-uintn.h */ diff --git a/wrapperhelper/include-fixed/bits/types.h b/wrapperhelper/include-fixed/bits/types.h new file mode 100644 index 000000000..7d1eddda4 --- /dev/null +++ b/wrapperhelper/include-fixed/bits/types.h @@ -0,0 +1,192 @@ +/* bits/types.h -- definitions of __*_t types underlying *_t types. + Copyright (C) 2002-2024 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . + +This file has been adapted to work with the 'wrapperhelper' project on the 09/06/2024. +*/ + +/* + * Never include this file directly; use instead. + */ + +#ifndef _BITS_TYPES_H +#define _BITS_TYPES_H 1 + +#include +#include +#include + +/* Convenience types. */ +typedef unsigned char __u_char; +typedef unsigned short int __u_short; +typedef unsigned int __u_int; +typedef unsigned long int __u_long; + +/* Fixed-size types, underlying types depend on word size and compiler. */ +#pragma wrappers allow_ints_ext + +/* Smallest types with at least a given width. */ +typedef __int8_t __int_least8_t; +typedef __uint8_t __uint_least8_t; +typedef __int16_t __int_least16_t; +typedef __uint16_t __uint_least16_t; +typedef __int32_t __int_least32_t; +typedef __uint32_t __uint_least32_t; +typedef __int64_t __int_least64_t; +typedef __uint64_t __uint_least64_t; + +/* quad_t is also 64 bits. */ +typedef __int64_t __quad_t; +typedef __uint64_t __u_quad_t; + +/* Largest integral types. */ +typedef __int64_t __intmax_t; +typedef __uint64_t __uintmax_t; + + +/* The machine-dependent file defines __*_T_TYPE + macros for each of the OS types we define below. The definitions + of those macros must use the following macros for underlying types. + We define __S_TYPE and __U_TYPE for the signed and unsigned + variants of each of the following integer types on this machine. + + 16 -- "natural" 16-bit type (always short) + 32 -- "natural" 32-bit type (always int) + 64 -- "natural" 64-bit type (long or long long) + LONG32 -- 32-bit type, traditionally long + QUAD -- 64-bit type, traditionally long long + WORD -- natural type of __WORDSIZE bits (int or long) + LONGWORD -- type of __WORDSIZE bits, traditionally long + + We distinguish WORD/LONGWORD, 32/LONG32, and 64/QUAD so that the + conventional uses of `long' or `long long' type modifiers match the + types we define, even when a less-adorned type would be the same size. + This matters for (somewhat) portably writing printf/scanf formats for + these types, where using the appropriate l or ll format modifiers can + make the typedefs and the formats match up across all GNU platforms. If + we used `long' when it's 64 bits where `long long' is expected, then the + compiler would warn about the formats not matching the argument types, + and the programmer changing them to shut up the compiler would break the + program's portability. + + Here we assume what is presently the case in all the GCC configurations + we support: long long is always 64 bits, long is always word/address size, + and int is always 32 bits. */ + +#define __S16_TYPE __int16_t +#define __U16_TYPE __uint16_t +#define __S32_TYPE __int32_t +#define __U32_TYPE __uint32_t +#define __SLONGWORD_TYPE long int +#define __ULONGWORD_TYPE unsigned long int +# define __SQUAD_TYPE __int64_t +# define __UQUAD_TYPE __uint64_t +# define __SWORD_TYPE long int +# define __UWORD_TYPE unsigned long int +# define __SLONG32_TYPE __int32_t +# define __ULONG32_TYPE __uint32_t +# define __S64_TYPE __int64_t +# define __U64_TYPE __uint64_t +# define __STD_TYPE typedef +#include /* Defines __*_T_TYPE macros. */ +#include /* Defines __TIME*_T_TYPE macros. */ + + +__STD_TYPE __DEV_T_TYPE __dev_t; /* Type of device numbers. */ +__STD_TYPE __UID_T_TYPE __uid_t; /* Type of user identifications. */ +__STD_TYPE __GID_T_TYPE __gid_t; /* Type of group identifications. */ +__STD_TYPE __INO_T_TYPE __ino_t; /* Type of file serial numbers. */ +__STD_TYPE __INO64_T_TYPE __ino64_t; /* Type of file serial numbers (LFS).*/ +__STD_TYPE __MODE_T_TYPE __mode_t; /* Type of file attribute bitmasks. */ +__STD_TYPE __NLINK_T_TYPE __nlink_t; /* Type of file link counts. */ +__STD_TYPE __OFF_T_TYPE __off_t; /* Type of file sizes and offsets. */ +__STD_TYPE __OFF64_T_TYPE __off64_t; /* Type of file sizes and offsets (LFS). */ +__STD_TYPE __PID_T_TYPE __pid_t; /* Type of process identifications. */ +__STD_TYPE __FSID_T_TYPE __fsid_t; /* Type of file system IDs. */ +__STD_TYPE __CLOCK_T_TYPE __clock_t; /* Type of CPU usage counts. */ +__STD_TYPE __RLIM_T_TYPE __rlim_t; /* Type for resource measurement. */ +__STD_TYPE __RLIM64_T_TYPE __rlim64_t; /* Type for resource measurement (LFS). */ +__STD_TYPE __ID_T_TYPE __id_t; /* General type for IDs. */ +__STD_TYPE __TIME_T_TYPE __time_t; /* Seconds since the Epoch. */ +__STD_TYPE __USECONDS_T_TYPE __useconds_t; /* Count of microseconds. */ +__STD_TYPE __SUSECONDS_T_TYPE __suseconds_t; /* Signed count of microseconds. */ +__STD_TYPE __SUSECONDS64_T_TYPE __suseconds64_t; + +__STD_TYPE __DADDR_T_TYPE __daddr_t; /* The type of a disk address. */ +__STD_TYPE __KEY_T_TYPE __key_t; /* Type of an IPC key. */ + +/* Clock ID used in clock and timer functions. */ +__STD_TYPE __CLOCKID_T_TYPE __clockid_t; + +/* Timer ID returned by `timer_create'. */ +__STD_TYPE __TIMER_T_TYPE __timer_t; + +/* Type to represent block size. */ +__STD_TYPE __BLKSIZE_T_TYPE __blksize_t; + +/* Types from the Large File Support interface. */ + +/* Type to count number of disk blocks. */ +__STD_TYPE __BLKCNT_T_TYPE __blkcnt_t; +__STD_TYPE __BLKCNT64_T_TYPE __blkcnt64_t; + +/* Type to count file system blocks. */ +__STD_TYPE __FSBLKCNT_T_TYPE __fsblkcnt_t; +__STD_TYPE __FSBLKCNT64_T_TYPE __fsblkcnt64_t; + +/* Type to count file system nodes. */ +__STD_TYPE __FSFILCNT_T_TYPE __fsfilcnt_t; +__STD_TYPE __FSFILCNT64_T_TYPE __fsfilcnt64_t; + +/* Type of miscellaneous file system fields. */ +__STD_TYPE __FSWORD_T_TYPE __fsword_t; + +__STD_TYPE __SSIZE_T_TYPE __ssize_t; /* Type of a byte count, or error. */ + +/* Signed long type used in system calls. */ +__STD_TYPE __SYSCALL_SLONG_TYPE __syscall_slong_t; +/* Unsigned long type used in system calls. */ +__STD_TYPE __SYSCALL_ULONG_TYPE __syscall_ulong_t; + +/* These few don't really vary by system, they always correspond + to one of the other defined types. */ +typedef __off64_t __loff_t; /* Type of file sizes and offsets (LFS). */ +typedef char *__caddr_t; + +/* Duplicates info from stdint.h but this is used in unistd.h. */ +__STD_TYPE __SWORD_TYPE __intptr_t; + +/* Duplicate info from sys/socket.h. */ +__STD_TYPE __U32_TYPE __socklen_t; + +/* C99: An integer type that can be accessed as an atomic entity, + even in the presence of asynchronous interrupts. + It is not currently necessary for this to be machine-specific. */ +typedef int __sig_atomic_t; + +/* Seconds since the Epoch, visible to user code when time_t is too + narrow only for consistency with the old way of widening too-narrow + types. User code should never use __time64_t. */ +#if __TIMESIZE == 64 && defined __LIBC +# define __time64_t __time_t +#elif __TIMESIZE != 64 +__STD_TYPE __TIME64_T_TYPE __time64_t; +#endif + +#undef __STD_TYPE + +#endif /* bits/types.h */ diff --git a/wrapperhelper/include-fixed/stdint.h b/wrapperhelper/include-fixed/stdint.h new file mode 100644 index 000000000..a3341ad28 --- /dev/null +++ b/wrapperhelper/include-fixed/stdint.h @@ -0,0 +1,276 @@ +/* Copyright (C) 1997-2024 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . + +This file has been adapted to work with the 'wrapperhelper' project on the 09/06/2024. +*/ + +/* + * ISO C99: 7.18 Integer types + */ + +#ifndef _STDINT_H +#define _STDINT_H 1 + +#define __GLIBC_INTERNAL_STARTING_HEADER_IMPLEMENTATION +#include +#include +#include +#include + +#pragma wrappers allow_ints_ext + +/* Exact integral types. */ + +/* Signed. */ +#include + +/* Unsigned. */ +#include + + +/* Small types. */ +#include + + +/* Fast types. */ + +/* Signed. */ +typedef int8_t int_fast8_t; +typedef int16_t int_fast16_t; +typedef int32_t int_fast32_t; +typedef int64_t int_fast64_t; + +/* Unsigned. */ +typedef uint8_t uint_fast8_t; +typedef uint16_t uint_fast16_t; +typedef uint32_t uint_fast32_t; +typedef uint64_t uint_fast64_t; + + +/* Types for `void *' pointers. */ +# ifndef __intptr_t_defined +typedef signed long intptr_t; +# define __intptr_t_defined +# endif +typedef unsigned long uintptr_t; + + +/* Largest integral types. */ +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; + + +# if __WORDSIZE == 64 +# define __INT64_C(c) c ## L +# define __UINT64_C(c) c ## UL +# else +# define __INT64_C(c) c ## LL +# define __UINT64_C(c) c ## ULL +# endif + +/* Limits of integral types. */ + +/* Minimum of signed integral types. */ +# define INT8_MIN (-128) +# define INT16_MIN (-32767-1) +# define INT32_MIN (-2147483647-1) +# define INT64_MIN (-__INT64_C(9223372036854775807)-1) +/* Maximum of signed integral types. */ +# define INT8_MAX (127) +# define INT16_MAX (32767) +# define INT32_MAX (2147483647) +# define INT64_MAX (__INT64_C(9223372036854775807)) + +/* Maximum of unsigned integral types. */ +# define UINT8_MAX (255) +# define UINT16_MAX (65535) +# define UINT32_MAX (4294967295U) +# define UINT64_MAX (__UINT64_C(18446744073709551615)) + + +/* Minimum of signed integral types having a minimum size. */ +# define INT_LEAST8_MIN (-128) +# define INT_LEAST16_MIN (-32767-1) +# define INT_LEAST32_MIN (-2147483647-1) +# define INT_LEAST64_MIN (-__INT64_C(9223372036854775807)-1) +/* Maximum of signed integral types having a minimum size. */ +# define INT_LEAST8_MAX (127) +# define INT_LEAST16_MAX (32767) +# define INT_LEAST32_MAX (2147483647) +# define INT_LEAST64_MAX (__INT64_C(9223372036854775807)) + +/* Maximum of unsigned integral types having a minimum size. */ +# define UINT_LEAST8_MAX (255) +# define UINT_LEAST16_MAX (65535) +# define UINT_LEAST32_MAX (4294967295U) +# define UINT_LEAST64_MAX (__UINT64_C(18446744073709551615)) + + +/* Minimum of fast signed integral types having a minimum size. */ +# define INT_FAST8_MIN (-128) +# define INT_FAST16_MIN INT16_MIN +# define INT_FAST32_MIN INT32_MIN +# define INT_FAST64_MIN (-__INT64_C(9223372036854775807)-1) +/* Maximum of fast signed integral types having a minimum size. */ +# define INT_FAST8_MAX (127) +# define INT_FAST16_MAX INT16_MAX +# define INT_FAST32_MAX INT32_MAX +# define INT_FAST64_MAX (__INT64_C(9223372036854775807)) + +/* Maximum of fast unsigned integral types having a minimum size. */ +# define UINT_FAST8_MAX (255) +# define UINT_FAST16_MAX UINT16_MAX +# define UINT_FAST32_MAX UINT32_MAX +# define UINT_FAST64_MAX (__UINT64_C(18446744073709551615)) + + +/* Values to test for integral types holding `void *' pointer. */ +# if __WORDSIZE == 64 +# define INTPTR_MIN (-9223372036854775807L-1) +# define INTPTR_MAX (9223372036854775807L) +# define UINTPTR_MAX (18446744073709551615UL) +# else +# define INTPTR_MIN (-2147483647-1) +# define INTPTR_MAX (2147483647) +# define UINTPTR_MAX (4294967295U) +# endif + + +/* Minimum for largest signed integral type. */ +# define INTMAX_MIN (-__INT64_C(9223372036854775807)-1) +/* Maximum for largest signed integral type. */ +# define INTMAX_MAX (__INT64_C(9223372036854775807)) + +/* Maximum for largest unsigned integral type. */ +# define UINTMAX_MAX (__UINT64_C(18446744073709551615)) + + +/* Limits of other integer types. */ + +/* Limits of `ptrdiff_t' type. */ +# if __WORDSIZE == 64 +# define PTRDIFF_MIN (-9223372036854775807L-1) +# define PTRDIFF_MAX (9223372036854775807L) +# else +# if __WORDSIZE32_PTRDIFF_LONG +# define PTRDIFF_MIN (-2147483647L-1) +# define PTRDIFF_MAX (2147483647L) +# else +# define PTRDIFF_MIN (-2147483647-1) +# define PTRDIFF_MAX (2147483647) +# endif +# endif + +/* Limits of `sig_atomic_t'. */ +# define SIG_ATOMIC_MIN (-2147483647-1) +# define SIG_ATOMIC_MAX (2147483647) + +/* Limit of `size_t' type. */ +# if __WORDSIZE == 64 +# define SIZE_MAX (18446744073709551615UL) +# else +# if __WORDSIZE32_SIZE_ULONG +# define SIZE_MAX (4294967295UL) +# else +# define SIZE_MAX (4294967295U) +# endif +# endif + +/* Limits of `wchar_t'. */ +# ifndef WCHAR_MIN +/* These constants might also be defined in . */ +# define WCHAR_MIN __WCHAR_MIN +# define WCHAR_MAX __WCHAR_MAX +# endif + +/* Limits of `wint_t'. */ +# define WINT_MIN (0u) +# define WINT_MAX (4294967295u) + +/* Signed. */ +# define INT8_C(c) c +# define INT16_C(c) c +# define INT32_C(c) c +# if __WORDSIZE == 64 +# define INT64_C(c) c ## L +# else +# define INT64_C(c) c ## LL +# endif + +/* Unsigned. */ +# define UINT8_C(c) c +# define UINT16_C(c) c +# define UINT32_C(c) c ## U +# if __WORDSIZE == 64 +# define UINT64_C(c) c ## UL +# else +# define UINT64_C(c) c ## ULL +# endif + +/* Maximal type. */ +# if __WORDSIZE == 64 +# define INTMAX_C(c) c ## L +# define UINTMAX_C(c) c ## UL +# else +# define INTMAX_C(c) c ## LL +# define UINTMAX_C(c) c ## ULL +# endif + +#if __GLIBC_USE (IEC_60559_BFP_EXT_C23) + +# define INT8_WIDTH 8 +# define UINT8_WIDTH 8 +# define INT16_WIDTH 16 +# define UINT16_WIDTH 16 +# define INT32_WIDTH 32 +# define UINT32_WIDTH 32 +# define INT64_WIDTH 64 +# define UINT64_WIDTH 64 + +# define INT_LEAST8_WIDTH 8 +# define UINT_LEAST8_WIDTH 8 +# define INT_LEAST16_WIDTH 16 +# define UINT_LEAST16_WIDTH 16 +# define INT_LEAST32_WIDTH 32 +# define UINT_LEAST32_WIDTH 32 +# define INT_LEAST64_WIDTH 64 +# define UINT_LEAST64_WIDTH 64 + +# define INT_FAST8_WIDTH 8 +# define UINT_FAST8_WIDTH 8 +# define INT_FAST16_WIDTH __WORDSIZE +# define UINT_FAST16_WIDTH __WORDSIZE +# define INT_FAST32_WIDTH __WORDSIZE +# define UINT_FAST32_WIDTH __WORDSIZE +# define INT_FAST64_WIDTH 64 +# define UINT_FAST64_WIDTH 64 + +# define INTPTR_WIDTH __WORDSIZE +# define UINTPTR_WIDTH __WORDSIZE + +# define INTMAX_WIDTH 64 +# define UINTMAX_WIDTH 64 + +# define PTRDIFF_WIDTH __WORDSIZE +# define SIG_ATOMIC_WIDTH 32 +# define SIZE_WIDTH __WORDSIZE +# define WCHAR_WIDTH 32 +# define WINT_WIDTH 32 + +#endif + +#endif /* stdint.h */ diff --git a/wrapperhelper/main.cpp b/wrapperhelper/main.cpp deleted file mode 100644 index b5a18a168..000000000 --- a/wrapperhelper/main.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include "ast.h" -#include "utils.h" - -static void dump_usage() { - std::string Usage = R"usage( - usage: command [guest_triple] [host_triple] -- - : set the header file to be parsed - : set libname required for wrapping func - [guest_triple]: set guest triple: can be arm32/arm64/x86/x64, default is x64 - [host_triple] : set host triple: can be arm32/arm64/x86/x64, default is arm64 - -- : mandatory - : extra compiler flags - )usage"; - std::cerr << Usage << std::endl; -} - -std::string parse_triple(const char* arg) { - if (strcmp(arg, "x86") == 0) { - return "i386-pc-linux-gnu"; - } else if (strcmp(arg, "x64") == 0) { - return "x86_64-pc-linux-gnu"; - } else if (strcmp(arg, "arm32") == 0) { - return "armv7-unknown-linux-gnueabihf"; - } else if (strcmp(arg, "arm64") == 0) { - return "aarch64-unknown-linux-gnu"; - } else { - std::cerr << "Invalid triple: '" << arg << "'\n"; - dump_usage(); - return ""; - } -} - -int main(int argc, const char* argv[]) { - if (argc < 4) { - dump_usage(); - return 0; - } - std::string libname = argv[2]; - std::string guest_triple = parse_triple("x64"); - std::string host_triple = parse_triple("arm64"); - if (argc >= 5) { - guest_triple = parse_triple(argv[3]); - } - if (argc >= 6) { - host_triple = parse_triple(argv[4]); - } - bool has_necessary_tag = false; - for (int i = 0; i < argc; i++) { - if (strcmp(argv[i], "--") == 0) { - has_necessary_tag = true; - break; - } - } - if (!has_necessary_tag) { - std::cerr << "Please add '--' after the triples" << std::endl; - dump_usage(); - return 0; - } - std::string err; - auto compile_db = clang::tooling::FixedCompilationDatabase::loadFromCommandLine(argc, argv, err); - clang::tooling::ClangTool Tool(*compile_db, {argv[1]}); - Tool.appendArgumentsAdjuster([&guest_triple](const clang::tooling::CommandLineArguments &args, clang::StringRef) { - clang::tooling::CommandLineArguments adjusted_args = args; - adjusted_args.push_back(std::string{"-target"}); - adjusted_args.push_back(guest_triple); - return adjusted_args; - }); - return Tool.run(std::make_unique(libname, host_triple, guest_triple).get()); -} diff --git a/wrapperhelper/src/cstring.c b/wrapperhelper/src/cstring.c new file mode 100644 index 000000000..b38a43e62 --- /dev/null +++ b/wrapperhelper/src/cstring.c @@ -0,0 +1,115 @@ +#include "cstring.h" + +#include + +#define STRING_MIN_CAP 8 + +string_t *string_new(void) { + string_t *ret = malloc(sizeof(*ret)); + if (!ret) return NULL; + char *buf = malloc(sizeof(char)); + if (!buf) { + free(ret); + return NULL; + } + buf[0] = '\0'; + ret->ssize = ret->scap = 0; ret->buf = buf; + return ret; +} + +string_t *string_new_cap(size_t cap) { + string_t *ret = malloc(sizeof(*ret)); + if (!ret) return NULL; + cap = (cap < STRING_MIN_CAP) ? STRING_MIN_CAP : cap; + ret->buf = malloc((cap + 1) * sizeof(char)); + if (!ret->buf) { + free(ret); + return NULL; + } + ret->buf[0] = '\0'; + ret->scap = cap; + ret->ssize = 0; + return ret; +} + +int string_reserve(string_t *s, size_t cap) { + size_t new_cap = (cap < STRING_MIN_CAP) ? STRING_MIN_CAP : cap; + if (new_cap <= s->scap) return 1; + + void *new_buf = realloc(s->buf, sizeof(char) * (new_cap + 1)); + if (!new_buf) return 0; + s->buf = new_buf; + s->scap = new_cap; + return 1; +} + +void string_del(string_t *s) { + if (s->buf) free(s->buf); + free(s); +} + +char *string_steal(string_t *s) { + char *ret = s->buf; + free(s); + return ret; +} + +int string_add_char(string_t *s, char elem) { + if (s->ssize >= s->scap) { + size_t new_cap = (s->scap < STRING_MIN_CAP) ? STRING_MIN_CAP : s->scap * 2; + char *new_buf = realloc(s->buf, sizeof(char) * (new_cap + 1)); + if (!new_buf) return 0; + s->buf = new_buf; + s->scap = new_cap; + } + s->buf[s->ssize++] = elem; + s->buf[s->ssize] = '\0'; + return 1; +} + +int string_add_string(string_t *s1, string_t *s2) { + if (s1->ssize + s2->ssize > s1->scap) { + size_t new_cap = (s1->scap < STRING_MIN_CAP) ? STRING_MIN_CAP : s1->scap * 2; + while (s1->ssize + s2->ssize > new_cap) { + new_cap = new_cap * 2; + } + char *new_buf = realloc(s1->buf, sizeof(char) * (new_cap + 1)); + if (!new_buf) return 0; + s1->buf = new_buf; + s1->scap = new_cap; + } + memcpy(s1->buf + s1->ssize, s2->buf, s2->ssize); + s1->ssize += s2->ssize; + s1->buf[s1->ssize] = '\0'; + return 1; +} + +void string_pop(string_t *s) { + if (!s->ssize) return; + s->buf[--s->ssize] = '\0'; + if (s->ssize < s->scap / 4) { + size_t new_cap = (s->scap / 2 < STRING_MIN_CAP) ? STRING_MIN_CAP : s->scap / 2; + if (new_cap == s->scap) return; + void *new_buf = realloc(s->buf, sizeof(char) * (new_cap + 1)); + if (!new_buf) return; // We don't really care if the realloc fails, we just need to not update anything + s->buf = new_buf; + s->scap = new_cap; + } +} + +string_t *string_dup(string_t const *s) { + string_t *ret = string_new_cap(s->ssize); + if (!ret) return NULL; + memcpy(ret->buf, s->buf, s->ssize + 1); + ret->ssize = s->ssize; + return ret; +} + +string_t *string_concat(string_t const *l, string_t const *r) { + string_t *ret = string_new_cap(l->ssize + r->ssize); + if (!ret) return NULL; + memcpy(ret->buf, l->buf, l->ssize); + memcpy(ret->buf + l->ssize, r->buf, r->ssize); + ret->buf[ret->ssize] = '\0'; + return ret; +} diff --git a/wrapperhelper/src/cstring.h b/wrapperhelper/src/cstring.h new file mode 100644 index 000000000..14032bd68 --- /dev/null +++ b/wrapperhelper/src/cstring.h @@ -0,0 +1,78 @@ +#pragma once + +#ifndef STRING_H +#define STRING_H + +#include +#include +#include + +/** Thread-unsafe string implementation, with support for raw NULL bytes + * USAGE: + * ====== + * string_t ------------ The string type. + * string_new ---------- Creates a new string. + * string_new_cap ------ Creates a new string with a given capacity. Takes the capacity. + * string_reserve ------ Ensures a string has at least a given capacity. Takes the string and the capacity. + * string_del ---------- Frees a string. Takes the string. + * string_steal -------- Frees a string, keeping the content alive. Takes the string. The content (also returned) needs to be freed separately. + * string_add_char ----- Add a character at the end. Takes the string and the new character. May increase the string capacity. + * string_add_string --- Add a string at the end in-place. Takes both strings. May increase the string capacity. + * string_pop ---------- Pops the last character. Takes the string. May reduce the string capacity. + * string_dup ---------- Duplicate a string. Takes the string. Does not free the old string. + * string_concat ------- Concatenate two strings. Takes both strings. Does not free any string. + * string_len ---------- String length. Takes the string. + * string_cap ---------- String capacity. Takes the string. + * string_content ------ Pointer to the string content. Valid C string. Takes the string. + * string_begin -------- Start of the string. Takes the string. + * string_end ---------- End of the string. Points to unmanaged memory. Takes the string. + * string_last --------- Last element of the string. Points to invalid memory if size is zero. Takes the string. + * string_for ---------- Iterate over the characters of a string. This is a for loop. Takes the iterator name and the string. + * + * EXAMPLE: + * ======== + * Source main.c: + * ------------------- +// ... +int main() { + string_t *str = string_new_cap(2); + if (!str) { + printf("Error: failed to allocate new string\n"); + return 2; + } + string_add_char(str, 'H'); // Cannot fail + string_add_char(str, 'i'); // Cannot fail + if (!string_add_char(str, '!')) { + printf("Error: failed to add char to string\n"); + return 2; + } + printf("String length: %zu: \"%s\"\n", string_len(str), string_content(str)); // 3, "Hi!" + string_del(str); +} + */ + +typedef struct string_s { + size_t ssize, scap; + char *buf; +} string_t; + +string_t *string_new(void); +string_t *string_new_cap(size_t cap); +int string_reserve(string_t *s, size_t cap); +void string_del(string_t *s); +char *string_steal(string_t *s); +int string_add_char(string_t *s, char elem); +int string_add_string(string_t *s1, string_t *s2); +void string_pop(string_t *s); +string_t *string_dup(string_t const *s); +string_t *string_concat(string_t const *l, string_t const *r); +#define string_len(s) ((s)->ssize) +#define string_cap(s) ((s)->scap) +#define string_content(s) ((s)->buf) +#define string_begin(s) ((s)->buf) +#define string_end(s) ((s)->buf + (s)->ssize) +#define string_last(s) ((s)->buf[(s)->ssize - 1]) +#define string_for(itname, s) \ + for (char *itname = string_begin((s)); itname != string_end((s)); ++itname) + +#endif // STRING_H diff --git a/wrapperhelper/src/generator.c b/wrapperhelper/src/generator.c new file mode 100644 index 000000000..3df744cb7 --- /dev/null +++ b/wrapperhelper/src/generator.c @@ -0,0 +1,922 @@ +#include "generator.h" + +#include "lang.h" +#include "prepare.h" + +static const char *rft2str[8] = { + [RQT_FUN] = "", + [RQT_FUN_2] = "", + [RQT_FUN_MY] = "(my) ", + [RQT_FUN_D] = "(D) ", + [RQT_DATA] = "", + [RQT_DATAV] = "(V) ", + [RQT_DATAB] = "(B) ", + [RQT_DATAM] = "(my) ", +}; +#define IS_RQT_FUN2(rty) (((rty) == RQT_FUN_2) || ((rty) == RQT_FUN_D)) +#define IS_RQT_FUNCTION(rty) ((rty) < RQT_DATA) + +void request_print(request_t *req) { + printf("%s", string_content(req->obj_name)); + if (req->has_default && req->has_val && (IS_RQT_FUNCTION(req->def.rty) != IS_RQT_FUNCTION(req->val.rty))) { + printf(" => conflict: was/is data, is/was function\n"); + } + int is_fun; + if (req->has_default) is_fun = IS_RQT_FUNCTION(req->def.rty); + else if (req->has_val) is_fun = IS_RQT_FUNCTION(req->val.rty); + else { + printf(" => (no value)\n"); + return; + } + + if (is_fun) { + printf(" => %sfunction", req->weak ? "weak " : ""); + if (req->has_default) { + printf(" with %sdefault %s%s%s%s", + req->default_comment ? "commented " : "", + rft2str[req->def.rty], + string_content(req->def.fun.typ), + (req->def.rty == RQT_FUN_2) ? " -> " : "", + (req->def.rty == RQT_FUN_2) ? string_content(req->def.fun.fun2) : ""); + } + if (req->has_val) { + printf(" with solved %s%s%s%s", + rft2str[req->val.rty], + string_content(req->val.fun.typ), + (req->val.rty == RQT_FUN_2) ? " -> " : "", + (req->val.rty == RQT_FUN_2) ? string_content(req->val.fun.fun2) : ""); + } + } else { + printf(" => %sdata", req->weak ? "weak " : ""); + if (req->has_default) { + if (req->def.dat.has_size) printf(" with default %zu", req->def.dat.sz); + else printf(" with no default"); + } + if (req->has_val) { + if (req->def.dat.has_size) printf(" with solved %zu", req->def.dat.sz); + else printf(" with no solved"); + } + } + printf("\n"); +} +void request_print_check(request_t *req) { + if (req->has_default && req->has_val && (IS_RQT_FUNCTION(req->def.rty) != IS_RQT_FUNCTION(req->val.rty))) { + printf("%s => conflict: was/is data, is/was function\n", string_content(req->obj_name)); + } + int is_fun; + if (req->has_default) is_fun = IS_RQT_FUNCTION(req->def.rty); + else if (req->has_val) is_fun = IS_RQT_FUNCTION(req->val.rty); + else { + printf(" => (no value)\n"); + return; + } + if (is_fun) { + if (req->has_val) { + if (req->has_default) { + int similar = 1; + // if (req->def.rty != req->val.rty) similar = 0; + if (similar && strcmp(string_content(req->def.fun.typ), string_content(req->val.fun.typ))) { + // similar = 0; + // TODO + if (req->def.rty == RQT_FUN_MY) { + similar = + string_len(req->def.fun.typ) >= 3 && + string_content(req->def.fun.typ)[2] == 'E' && + !strncmp(string_content(req->def.fun.typ), string_content(req->val.fun.typ), 2) && + !strcmp(string_content(req->def.fun.typ) + 3, string_content(req->val.fun.typ) + 2); + if (similar) { + // We need to add the 'E' back here + } + } else { + similar = 0; + } + } + if (!similar) { + printf("%s => %sfunction with %sdefault %s%s%s%s and different solved %s%s%s%s\n", + string_content(req->obj_name), + req->weak ? "weak " : "", + req->default_comment ? "commented " : "", + rft2str[req->def.rty], + string_content(req->def.fun.typ), + (req->def.rty == RQT_FUN_2) ? " -> " : "", + (req->def.rty == RQT_FUN_2) ? string_content(req->def.fun.fun2) : "", + rft2str[req->val.rty], + string_content(req->val.fun.typ), + (req->val.rty == RQT_FUN_2) ? " -> " : "", + (req->val.rty == RQT_FUN_2) ? string_content(req->val.fun.fun2) : ""); + } + } else { + printf("%s => %sfunction with solved %s%s%s%s\n", + string_content(req->obj_name), + req->weak ? "weak " : "", + rft2str[req->val.rty], + string_content(req->val.fun.typ), + (req->val.rty == RQT_FUN_2) ? " -> " : "", + (req->val.rty == RQT_FUN_2) ? string_content(req->val.fun.fun2) : ""); + } + } else if (req->has_default) { + /* printf("%s => unsolved %sfunction with %sdefault %s%s%s%s\n", + string_content(req->obj_name), + req->weak ? "weak " : "", + req->default_comment ? "commented " : "", + rft2str[req->def.rty], + string_content(req->def.fun.typ), + (req->def.rty == RQT_FUN_2) ? " -> " : "", + (req->def.rty == RQT_FUN_2) ? string_content(req->def.fun.fun2) : ""); */ + } + } else { + printf("%s => %sdata", string_content(req->obj_name), req->weak ? "weak " : ""); + if (req->has_default) { + if (req->def.dat.has_size) printf(" with default %zu", req->def.dat.sz); + else printf(" with no default"); + } + if (req->has_val) { + if (req->def.dat.has_size) printf(" with solved %zu", req->def.dat.sz); + else printf(" with no solved"); + } + } +} +void request_del(request_t *req) { + string_del(req->obj_name); + if (req->has_default) { + switch (req->def.rty) { + case RQT_FUN: string_del(req->def.fun.typ); break; + case RQT_FUN_2: string_del(req->def.fun.typ); string_del(req->def.fun.fun2); break; + case RQT_FUN_MY: string_del(req->def.fun.typ); break; + case RQT_FUN_D: string_del(req->def.fun.typ); string_del(req->def.fun.fun2); break; + case RQT_DATA: break; + case RQT_DATAV: break; + case RQT_DATAB: break; + case RQT_DATAM: break; + } + } + if (req->has_val) { + switch (req->val.rty) { + case RQT_FUN: string_del(req->val.fun.typ); break; + case RQT_FUN_2: string_del(req->val.fun.typ); string_del(req->val.fun.fun2); break; + case RQT_FUN_MY: string_del(req->val.fun.typ); break; + case RQT_FUN_D: string_del(req->val.fun.typ); string_del(req->val.fun.fun2); break; + case RQT_DATA: break; + case RQT_DATAV: break; + case RQT_DATAB: break; + case RQT_DATAM: break; + } + } +} + +static int valid_reqtype(string_t *t) { + const char *s = string_content(t); + if (!((s[0] >= 'A') && (s[0] <= 'Z')) && !((s[0] >= 'a') && (s[0] <= 'z'))) return 0; + if (s[1] != 'F') return 0; + for (size_t i = 2; i < string_len(t); ++i) { + if (!((s[i] >= 'A') && (s[i] <= 'Z')) && !((s[i] >= 'a') && (s[i] <= 'z'))) return 0; + } + return 1; +} +static const char *rqt_suffix[8] = { + [RQT_FUN] = "", + [RQT_FUN_2] = "2", + [RQT_FUN_MY] = "M", + [RQT_FUN_D] = "D", + [RQT_DATA] = "", + [RQT_DATAV] = "V", + [RQT_DATAB] = "B", + [RQT_DATAM] = "M", +}; + +static void request_output(FILE *f, request_t *req) { + if (!req->has_val) { + if (!req->has_default) { + // printf("Warning: %s has no value and no default, assuming function\n", string_content(req->obj_name)); + fprintf(f, "//GO%s(%s, \n", req->weak ? "W" : "", string_content(req->obj_name)); + } else if (IS_RQT_FUNCTION(req->def.rty)) { + fprintf(f, "%sGO%s%s(%s, %s%s%s%s%s)%s\n", + req->default_comment ? "//" : "", + req->weak ? "W" : "", + rqt_suffix[req->def.rty], + string_content(req->obj_name), + valid_reqtype(req->def.fun.typ) ? "" : "\"", + string_content(req->def.fun.typ), + valid_reqtype(req->def.fun.typ) ? "" : "\"", + IS_RQT_FUN2(req->def.rty) ? ", " : "", + IS_RQT_FUN2(req->def.rty) ? string_content(req->def.fun.fun2) : "", + req->default_comment ? "" : " // Warning: failed to confirm"); + } else { + if (req->def.dat.has_size) { + fprintf(f, "%sDATA%s%s(%s, %zu) // Warning: failed to confirm\n", + req->default_comment ? "//" : "", + req->weak ? "W" : "", + rqt_suffix[req->def.rty], + string_content(req->obj_name), + req->def.dat.sz); + } else { + fprintf(f, "//DATA%s%s(%s, \n", + req->weak ? "W" : "", + rqt_suffix[req->def.rty], + string_content(req->obj_name)); + } + } + } else { + if (IS_RQT_FUNCTION(req->val.rty)) { + int is_comment = + (req->has_default && !req->default_comment) ? (req->val.rty != req->def.rty) : (req->val.rty != RQT_FUN); + fprintf(f, "%sGO%s%s(%s, %s%s%s)\n", + is_comment ? "//" : "", + req->weak ? "W" : "", + rqt_suffix[req->val.rty], + string_content(req->obj_name), + string_content(req->val.fun.typ), + IS_RQT_FUN2(req->val.rty) ? ", " : "", + IS_RQT_FUN2(req->val.rty) ? req->val.fun.fun2 ? string_content(req->val.fun.fun2) : "" : ""); + } else { + if (req->val.dat.has_size) { + int is_comment = !req->has_default || req->default_comment || (req->def.rty != req->val.rty); + fprintf(f, "%sDATA%s(%s, %zu)\n", + is_comment ? "//" : "", + rqt_suffix[req->val.rty], + string_content(req->obj_name), + req->val.dat.sz); + } else { + fprintf(f, "//DATA%s(%s, \n", + rqt_suffix[req->val.rty], + string_content(req->obj_name)); + } + } + } +} +void output_from_requests(FILE *f, VECTOR(requests) *reqs) { + fprintf(f, "#if !(defined(GO) && defined(GOM) && defined(GO2) && defined(DATA))\n#error Meh...\n#endif\n\n"); + vector_for(requests, req, reqs) { + request_output(f, req); + } +} + +VECTOR_IMPL(requests, request_del) + +VECTOR(requests) *requests_from_file(const char *filename, FILE *f) { + prepare_t *prep = prepare_new_file(f, filename); + if (!prep) { + printf("Failed to create the prepare structure for the requests file\n"); + return NULL; + } + + VECTOR(requests) *ret = vector_new(requests); + if (!ret) { + prepare_del(prep); + return NULL; + } + + int lineno = 1; + + // Ignore the first 3 lines + preproc_token_t tok; + do { + tok = pre_next_token(prep, 0); + if (tok.tokt == PPTOK_NEWLINE) ++lineno; + } while (!preproc_token_isend(&tok) && (lineno < 4)); + + // TODO: better conditionals handling + // Also, for now assume we have no definition + int if_depth = 0, entered_depth = 0; + while (1) { + int is_comment = 0; + tok = pre_next_token(prep, 1); + while (tok.tokt == PPTOK_START_LINE_COMMENT) { + is_comment = 1; + // Empty destructor + tok = pre_next_token(prep, 1); + } + if ((tok.tokt == PPTOK_SYM) && (tok.tokv.sym == SYM_HASH)) { + if (is_comment) { + preproc_token_del(&tok); + tok = pre_next_newline_token(prep); // Returns a newline + ++lineno; + continue; + } + tok = pre_next_token(prep, 0); + if (tok.tokt != PPTOK_IDENT) { + printf("Error: invalid requests file: invalid preprocessor line\n"); + preproc_token_del(&tok); + goto failed; + } + if (!strcmp(string_content(tok.tokv.str), "ifdef")) { + string_del(tok.tokv.str); + tok = pre_next_token(prep, 0); + if (tok.tokt != PPTOK_IDENT) { + printf("Error: invalid requests file: invalid '#ifdef' line\n"); + preproc_token_del(&tok); + goto failed; + } + ++if_depth; + string_del(tok.tokv.str); + tok = pre_next_token(prep, 0); + } else if (!strcmp(string_content(tok.tokv.str), "ifndef")) { + string_del(tok.tokv.str); + tok = pre_next_token(prep, 0); + if (tok.tokt != PPTOK_IDENT) { + printf("Error: invalid requests file: invalid '#ifndef' line\n"); + preproc_token_del(&tok); + goto failed; + } + if (if_depth == entered_depth) ++entered_depth; + ++if_depth; + string_del(tok.tokv.str); + tok = pre_next_token(prep, 0); + } else if (!strcmp(string_content(tok.tokv.str), "else")) { + string_del(tok.tokv.str); + tok = pre_next_token(prep, 0); + if (if_depth == entered_depth + 1) ++entered_depth; + else if (if_depth == entered_depth) --entered_depth; + } else if (!strcmp(string_content(tok.tokv.str), "endif")) { + string_del(tok.tokv.str); + tok = pre_next_token(prep, 0); + if (if_depth == entered_depth) --entered_depth; + --if_depth; + } else { + printf("Error: invalid requests file: invalid preprocessor command '%s'\n", string_content(tok.tokv.str)); + string_del(tok.tokv.str); + goto failed; + } + while (!preproc_token_isend(&tok) && (tok.tokt != PPTOK_NEWLINE)) { + preproc_token_del(&tok); + tok = pre_next_token(prep, 0); + } + ++lineno; + if (preproc_token_isend(&tok)) { + if (tok.tokt == PPTOK_EOF) goto success; + else { + preproc_token_del(&tok); + goto failed; + } + } + } else if (tok.tokt == PPTOK_NEWLINE) { + ++lineno; + } else if (tok.tokt == PPTOK_EOF) { + goto success; + } else if ((tok.tokt == PPTOK_IDENT) + && (!strcmp(string_content(tok.tokv.str), "GO") + || !strcmp(string_content(tok.tokv.str), "GO2") + || !strcmp(string_content(tok.tokv.str), "GOD") + || !strcmp(string_content(tok.tokv.str), "GOM") + || !strcmp(string_content(tok.tokv.str), "GOW") + || !strcmp(string_content(tok.tokv.str), "GOW2") + || !strcmp(string_content(tok.tokv.str), "GOWD") + || !strcmp(string_content(tok.tokv.str), "GOWM"))) { + int isweak = (string_content(tok.tokv.str)[2] == 'W'); + request_t req = { + .has_default = 0, + .default_comment = is_comment, + .has_val = 0, + .obj_name = NULL, + .weak = isweak, + .def = { + .rty = + (string_content(tok.tokv.str)[isweak ? 3 : 2] == '2') ? RQT_FUN_2 : + (string_content(tok.tokv.str)[isweak ? 3 : 2] == 'D') ? RQT_FUN_D : + (string_content(tok.tokv.str)[isweak ? 3 : 2] == 'M') ? RQT_FUN_MY : RQT_FUN, + .fun.typ = NULL, + .fun.fun2 = NULL, + }, + }; + string_del(tok.tokv.str); + tok = pre_next_token(prep, 0); + if ((tok.tokt != PPTOK_SYM) || (tok.tokv.sym != SYM_LPAREN)) { + printf("Error: invalid requests file: invalid GO line %d (lparen)\n", lineno); + preproc_token_del(&tok); + goto failed; + } + // Empty destructor + tok = pre_next_token(prep, 0); + if (tok.tokt != PPTOK_IDENT) { + printf("Error: invalid requests file: invalid GO line %d (obj_name)\n", lineno); + preproc_token_del(&tok); + goto failed; + } + req.obj_name = tok.tokv.str; + // Token moved + tok = pre_next_token(prep, 0); + if ((tok.tokt != PPTOK_SYM) || (tok.tokv.sym != SYM_COMMA)) { + printf("Error: invalid requests file: invalid GO line %d (comma)\n", lineno); + string_del(req.obj_name); + preproc_token_del(&tok); + goto failed; + } + // Empty destructor + tok = pre_next_token(prep, 0); + if ((tok.tokt == PPTOK_IDENT) || (tok.tokt == PPTOK_STRING)) { + req.def.fun.typ = (tok.tokt == PPTOK_STRING) ? tok.tokv.sstr : tok.tokv.str; + req.has_default = 1; + // Token moved + tok = pre_next_token(prep, 0); + if ((req.def.rty == RQT_FUN_2) || (req.def.rty == RQT_FUN_D)) { + if ((tok.tokt != PPTOK_SYM) || (tok.tokv.sym != SYM_COMMA)) { + printf("Error: invalid requests file: invalid GO line %d (comma 2)\n", lineno); + string_del(req.obj_name); + string_del(req.def.fun.typ); + preproc_token_del(&tok); + goto failed; + } + // Empty destructor + tok = pre_next_token(prep, 0); + if (tok.tokt != PPTOK_IDENT) { + printf("Error: invalid requests file: invalid GO line %d (redirect)\n", lineno); + string_del(req.obj_name); + string_del(req.def.fun.typ); + preproc_token_del(&tok); + goto failed; + } + req.def.fun.fun2 = tok.tokv.str; + // Token moved + tok = pre_next_token(prep, 0); + } + if ((tok.tokt != PPTOK_SYM) || (tok.tokv.sym != SYM_RPAREN)) { + printf("Error: invalid requests file: invalid GO line %d (rparen)\n", lineno); + string_del(req.obj_name); + string_del(req.def.fun.typ); + if (req.def.fun.fun2) string_del(req.def.fun.fun2); + preproc_token_del(&tok); + goto failed; + } + // Empty destructor + tok = pre_next_token(prep, 0); + } + if (tok.tokt != PPTOK_NEWLINE) { + printf("Error: invalid requests file: invalid GO line %d (newline)\n", lineno); + string_del(req.obj_name); + if (req.def.fun.typ) string_del(req.def.fun.typ); + if (req.def.fun.fun2) string_del(req.def.fun.fun2); + preproc_token_del(&tok); + goto failed; + } + if (if_depth == entered_depth) { + if (!vector_push(requests, ret, req)) { + printf("Error: failed to add request for %s\n", string_content(req.obj_name)); + string_del(req.obj_name); + if (req.def.fun.typ) string_del(req.def.fun.typ); + if (req.def.fun.fun2) string_del(req.def.fun.fun2); + // Empty destructor + goto failed; + } + } + ++lineno; + } else if ((tok.tokt == PPTOK_IDENT) + && (!strcmp(string_content(tok.tokv.str), "DATA") + || !strcmp(string_content(tok.tokv.str), "DATAV") + || !strcmp(string_content(tok.tokv.str), "DATAB") + || !strcmp(string_content(tok.tokv.str), "DATAM"))) { + request_t req = { + .has_default = 1, + .default_comment = is_comment, + .has_val = 0, + .obj_name = NULL, + .weak = (string_content(tok.tokv.str)[4] == 'V'), + .def = { + .rty = + (string_content(tok.tokv.str)[4] == 'V') ? RQT_DATAV : + (string_content(tok.tokv.str)[4] == 'B') ? RQT_DATAB : + (string_content(tok.tokv.str)[4] == 'M') ? RQT_DATAM : RQT_DATA, + .dat.has_size = 0, + .dat.sz = 0, + }, + }; + string_del(tok.tokv.str); + tok = pre_next_token(prep, 0); + if ((tok.tokt != PPTOK_SYM) || (tok.tokv.sym != SYM_LPAREN)) { + printf("Error: invalid requests file: invalid DATA line %d (lparen)\n", lineno); + preproc_token_del(&tok); + goto failed; + } + // Empty destructor + tok = pre_next_token(prep, 0); + if (tok.tokt != PPTOK_IDENT) { + printf("Error: invalid requests file: invalid DATA line %d (obj_name)\n", lineno); + preproc_token_del(&tok); + goto failed; + } + req.obj_name = tok.tokv.str; + // Token moved + tok = pre_next_token(prep, 0); + if ((tok.tokt != PPTOK_SYM) || (tok.tokv.sym != SYM_COMMA)) { + printf("Error: invalid requests file: invalid DATA line %d (comma)\n", lineno); + string_del(req.obj_name); + preproc_token_del(&tok); + goto failed; + } + // Empty destructor + tok = pre_next_token(prep, 0); + if (tok.tokt == PPTOK_NUM) { + num_constant_t cst; + if (!num_constant_convert(tok.tokv.str, &cst)) { + printf("Error: invalid requests file: invalid DATA line %d (num conversion)\n", lineno); + string_del(req.obj_name); + preproc_token_del(&tok); + goto failed; + } + switch (cst.typ) { + case NCT_FLOAT: + case NCT_DOUBLE: + case NCT_LDOUBLE: + printf("Error: invalid requests file: invalid DATA line %d (num conversion)\n", lineno); + string_del(req.obj_name); + preproc_token_del(&tok); + goto failed; + case NCT_INT32: req.def.dat.sz = (size_t)cst.val.i32; break; + case NCT_UINT32: req.def.dat.sz = (size_t)cst.val.u32; break; + case NCT_INT64: req.def.dat.sz = (size_t)cst.val.i64; break; + case NCT_UINT64: req.def.dat.sz = (size_t)cst.val.u64; break; + } + req.def.dat.has_size = 1; + // Token moved + tok = pre_next_token(prep, 0); + if ((tok.tokt != PPTOK_SYM) || (tok.tokv.sym != SYM_RPAREN)) { + printf("Error: invalid requests file: invalid DATA line %d (rparen)\n", lineno); + string_del(req.obj_name); + preproc_token_del(&tok); + goto failed; + } + // Empty destructor + tok = pre_next_token(prep, 0); + } + if (tok.tokt != PPTOK_NEWLINE) { + printf("Error: invalid requests file: invalid DATA line %d (newline)\n", lineno); + string_del(req.obj_name); + preproc_token_del(&tok); + goto failed; + } + if (if_depth == entered_depth) { + if (!vector_push(requests, ret, req)) { + printf("Error: failed to add request for %s\n", string_content(req.obj_name)); + string_del(req.obj_name); + // Empty destructor + goto failed; + } + } + ++lineno; + } else { + if (is_comment) { + preproc_token_del(&tok); + tok = pre_next_newline_token(prep); // Returns a newline + ++lineno; + continue; + } + printf("Error: invalid requests file: invalid token:\n"); + preproc_token_print(&tok); + preproc_token_del(&tok); + goto failed; + } + } + +failed: + prepare_del(prep); + vector_del(requests, ret); + return NULL; + +success: + prepare_del(prep); + return ret; +} + +static int is_simple_type_ptr_to(type_t *typ, int *needs_D, int *needs_my) { + switch (typ->typ) { + case TYPE_BUILTIN: + return 1; // Assume pointers to builtin are simple + case TYPE_ARRAY: + if (typ->val.array.array_sz == (size_t)-1) return 0; // VLA are not simple + return is_simple_type_ptr_to(typ->val.array.typ, needs_D, needs_my); + case TYPE_STRUCT_UNION: + if (typ->val.st->explicit_simple) return 1; + if (typ->_internal_use) return 1; // Recursive structures are OK as long as every other members are OK + if (!typ->val.st->is_defined) return 1; // Undefined structures are OK since they are opaque + typ->_internal_use = 1; + for (size_t i = 0; i < typ->val.st->nmembers; ++i) { + st_member_t *mem = &typ->val.st->members[i]; + if (!is_simple_type_ptr_to(mem->typ, needs_D, needs_my)) { + typ->_internal_use = 0; + return 0; + } + } + typ->_internal_use = 0; + return 1; + case TYPE_ENUM: + return is_simple_type_ptr_to(typ->val.typ, needs_D, needs_my); + case TYPE_PTR: + return is_simple_type_ptr_to(typ->val.typ, needs_D, needs_my); + case TYPE_FUNCTION: + *needs_my = 1; + return 1; + default: + printf("Error: is_simple_type_ptr_to on unknown type %u\n", typ->typ); + return 0; + } +} +static int is_simple_type(type_t *typ, int *needs_D, int *needs_my) { + switch (typ->typ) { + case TYPE_BUILTIN: + return 1; // Assume pointers to builtin are simple + case TYPE_ARRAY: + if (typ->val.array.array_sz == (size_t)-1) return 0; // VLA are not simple + return is_simple_type_ptr_to(typ->val.array.typ, needs_D, needs_my); + case TYPE_STRUCT_UNION: + if (typ->val.st->explicit_simple) return 1; + if (typ->_internal_use) return 1; // Recursive structures are OK as long as every other members are OK + // if (!typ->val.st->is_defined) return 1; // Undefined structures are OK since they are opaque + // To be safe, don't allow opaque structures + if (!typ->val.st->is_defined) return 0; + typ->_internal_use = 1; + for (size_t i = 0; i < typ->val.st->nmembers; ++i) { + st_member_t *mem = &typ->val.st->members[i]; + if (!is_simple_type(mem->typ, needs_D, needs_my)) { + typ->_internal_use = 0; + return 0; + } + } + typ->_internal_use = 0; + return 1; + case TYPE_ENUM: + return is_simple_type(typ->val.typ, needs_D, needs_my); + case TYPE_PTR: + return is_simple_type_ptr_to(typ->val.typ, needs_D, needs_my); + case TYPE_FUNCTION: + // Functions should be handled differently (GO instead of DATA) + return 0; + default: + printf("Error: is_simple_type on unknown type %u\n", typ->typ); + return 0; + } +} + +static int convert_type(string_t *dest, type_t *typ, int is_ret, int *needs_D, int *needs_my, string_t *obj_name) { + if (typ->is_atomic) { + printf("TODO: convert_type for atomic types\n"); + return 0; + } + switch (typ->typ) { + case TYPE_BUILTIN: { + int has_char = 0; + char c; + switch (typ->val.builtin) { + case BTT_VOID: has_char = 1; c = 'v'; break; + case BTT_BOOL: has_char = 1; c = 'i'; break; + case BTT_CHAR: has_char = 1; c = 'c'; break; + case BTT_SCHAR: has_char = 1; c = 'c'; break; + case BTT_UCHAR: has_char = 1; c = 'C'; break; + case BTT_SHORT: has_char = 1; c = 'w'; break; + case BTT_SSHORT: has_char = 1; c = 'w'; break; + case BTT_USHORT: has_char = 1; c = 'W'; break; + case BTT_INT: has_char = 1; c = 'i'; break; + case BTT_SINT: has_char = 1; c = 'i'; break; + case BTT_UINT: has_char = 1; c = 'u'; break; + case BTT_LONG: has_char = 1; c = 'l'; break; + case BTT_SLONG: has_char = 1; c = 'l'; break; + case BTT_ULONG: has_char = 1; c = 'L'; break; + case BTT_LONGLONG: has_char = 1; c = 'I'; break; + case BTT_SLONGLONG: has_char = 1; c = 'I'; break; + case BTT_ULONGLONG: has_char = 1; c = 'U'; break; + case BTT_INT128: has_char = 1; c = 'H'; break; // TODO: Is 'H' for signed and unsigned? + case BTT_SINT128: has_char = 1; c = 'H'; break; // Is 'H' for signed and unsigned? + case BTT_UINT128: has_char = 1; c = 'H'; break; // Is 'H' for signed and unsigned? + case BTT_S8: has_char = 1; c = 'c'; break; + case BTT_U8: has_char = 1; c = 'C'; break; + case BTT_S16: has_char = 1; c = 'w'; break; + case BTT_U16: has_char = 1; c = 'W'; break; + case BTT_S32: has_char = 1; c = 'i'; break; + case BTT_U32: has_char = 1; c = 'u'; break; + case BTT_S64: has_char = 1; c = 'I'; break; + case BTT_U64: has_char = 1; c = 'U'; break; + case BTT_FLOAT: has_char = 1; c = 'f'; break; + case BTT_CFLOAT: has_char = 1; c = 'x'; break; + case BTT_IFLOAT: has_char = 1; c = 'f'; break; + case BTT_DOUBLE: has_char = 1; c = 'd'; break; + case BTT_CDOUBLE: has_char = 1; c = 'X'; break; + case BTT_IDOUBLE: has_char = 1; c = 'd'; break; + case BTT_LONGDOUBLE: *needs_D = 1; has_char = 1; c = 'D'; break; + case BTT_CLONGDOUBLE: *needs_D = 1; has_char = 1; c = 'Y'; break; + case BTT_ILONGDOUBLE: *needs_D = 1; has_char = 1; c = 'D'; break; + case BTT_VA_LIST: has_char = 1; c = 'A'; break; + default: + printf("Error: convert_type on unknown builtin %u\n", typ->val.builtin); + return 0; + } + if (has_char) { + if (!string_add_char(dest, c)) { + printf("Error: failed to add type char for %s\n", builtin2str[typ->val.builtin]); + return 0; + } + return 1; + } else { + printf("Internal error: unknown state builtin=%u\n", typ->val.builtin); + return 0; + } } + case TYPE_ARRAY: + printf("Error: convert_type on raw array\n"); + return 0; + case TYPE_STRUCT_UNION: + if (!typ->is_validated || typ->is_incomplete) { + printf("Error: incomplete return type for %s\n", string_content(obj_name)); + return 0; + } + if (is_ret) { + if (typ->szinfo.size <= 8) { + if (!string_add_char(dest, 'U')) { + printf("Error: failed to add type char for structure return\n"); + return 0; + } + return 1; + } else if (typ->szinfo.size <= 16) { + if (!string_add_char(dest, 'H')) { + printf("Error: failed to add type char for large structure return\n"); + return 0; + } + return 1; + } else { + if (!string_add_char(dest, 'p')) { + printf("Error: failed to add type char for very large structure return\n"); + return 0; + } + return 1; + } + } else { + if (typ->val.st->nmembers == 1) { + return convert_type(dest, typ->val.st->members[0].typ, is_ret, needs_D, needs_my, obj_name); + } + printf("TODO: convert_type on structure as argument (%s)\n", string_content(obj_name)); + return 0; + } + case TYPE_ENUM: + return convert_type(dest, typ->val.typ, is_ret, needs_D, needs_my, obj_name); + case TYPE_PTR: + if ((typ->val.typ->typ == TYPE_STRUCT_UNION) && typ->val.typ->val.st->tag && !strcmp(string_content(typ->val.typ->val.st->tag), "_IO_FILE")) { + if (!string_add_char(dest, 'S')) { + printf("Error: failed to add type char for %s\n", builtin2str[typ->val.builtin]); + return 0; + } + return 1; + } + if (is_simple_type_ptr_to(typ->val.typ, needs_D, needs_my)) { + if (!string_add_char(dest, 'p')) { + printf("Error: failed to add type char for simple pointer\n"); + return 0; + } + return 1; + } else { + *needs_my = 1; + if (!string_add_char(dest, 'p')) { + printf("Error: failed to add type char for %s\n", builtin2str[typ->val.builtin]); + return 0; + } + return 1; + } + case TYPE_FUNCTION: + printf("Error: convert_type on raw function\n"); + return 0; + default: + printf("Error: convert_type on unknown type %u\n", typ->typ); + return 0; + } +} +static int convert_type_post(string_t *dest, type_t *typ, string_t *obj_name) { + if (typ->is_atomic) { + printf("TODO: convert_type_post for atomic types\n"); + return 0; + } + switch (typ->typ) { + case TYPE_BUILTIN: return 1; + case TYPE_ARRAY: return 1; + case TYPE_STRUCT_UNION: + if (!typ->is_validated || typ->is_incomplete) { + printf("Error: incomplete return type for %s\n", string_content(obj_name)); + return 0; + } + if (typ->szinfo.size <= 16) { + return 1; + } else { + if (!string_add_char(dest, 'p')) { + printf("Error: failed to add type char for very large structure return as parameter\n"); + return 0; + } + return 2; + } + case TYPE_ENUM: return 1; + case TYPE_PTR: return 1; + case TYPE_FUNCTION: return 1; + default: + printf("Error: convert_type_post on unknown type %u\n", typ->typ); + return 0; + } +} + +int solve_request(request_t *req, type_t *typ) { + if (typ->typ == TYPE_FUNCTION) { + int needs_D = 0, needs_my = req->has_default && (req->def.rty == RQT_FUN_MY), needs_2 = 0; + int convert_post; + req->val.fun.typ = string_new(); + if (!req->val.fun.typ) { + printf("Error: failed to create function type string\n"); + return 0; + } + if (!convert_type(req->val.fun.typ, typ->val.fun.ret, 1, &needs_D, &needs_my, req->obj_name)) goto fun_fail; + if (!string_add_char(req->val.fun.typ, 'F')) { + printf("Error: failed to add convention char\n"); + goto fun_fail; + } + if (req->has_default && (req->def.rty == RQT_FUN_MY) && (string_content(req->def.fun.typ)[2] == 'E')) { + if (!string_add_char(req->val.fun.typ, 'E')) { + printf("Error: failed to add emu char\n"); + goto fun_fail; + } + } + convert_post = convert_type_post(req->val.fun.typ, typ->val.fun.ret, req->obj_name); + if (!convert_post) goto fun_fail; + if (typ->val.fun.nargs == (size_t)-1) { + printf("Warning: assuming empty specification is void specification\n"); + if (convert_post == 1) { + if (!string_add_char(req->val.fun.typ, 'v')) { + printf("Error: failed to add void specification char\n"); + goto fun_fail; + } + } + } else if (!typ->val.fun.nargs && !typ->val.fun.has_varargs) { + if (convert_post == 1) { + if (!string_add_char(req->val.fun.typ, 'v')) { + printf("Error: failed to add void specification char\n"); + goto fun_fail; + } + } + } else { + for (size_t i = 0; i < typ->val.fun.nargs; ++i) { + if (!convert_type(req->val.fun.typ, typ->val.fun.args[i], 0, &needs_D, &needs_my, req->obj_name)) goto fun_fail; + } + if (typ->val.fun.has_varargs) { + if (!string_add_char(req->val.fun.typ, 'V')) { + printf("Error: failed to add type char for %s\n", builtin2str[typ->val.builtin]); + goto fun_fail; + } + } + } + + // fun_succ: + if (req->has_default && (req->def.rty == RQT_FUN_2) && !needs_my) { + needs_2 = 1; + req->val.fun.fun2 = string_dup(req->def.fun.fun2); + if (!req->val.fun.fun2) { + printf("Error: failed to duplicate string (request for function %s with default redirection)\n", string_content(req->obj_name)); + return 0; + } + } else if (req->has_default && (req->def.rty == RQT_FUN_D) && !needs_my) { + needs_2 = 0; + req->val.fun.fun2 = string_dup(req->def.fun.fun2); + if (!req->val.fun.fun2) { + printf("Error: failed to duplicate string (request for function %s with long double types)\n", string_content(req->obj_name)); + return 0; + } + } else if (!needs_my && needs_D) { + req->val.fun.fun2 = string_new(); + if (!req->val.fun.fun2) { + printf("Error: failed to create empty string (request for function %s with long double types)\n", string_content(req->obj_name)); + return 0; + } + } + req->val.rty = + needs_my ? RQT_FUN_MY : + needs_2 ? RQT_FUN_2 : + needs_D ? RQT_FUN_D : RQT_FUN; + req->has_val = 1; + return 1; + + fun_fail: + string_del(req->val.fun.typ); + return 0; + } else { + int needs_D = 0, needs_my = req->has_default && (req->def.rty == RQT_FUN_MY); + if (is_simple_type(typ, &needs_D, &needs_my)) { + // TODO: Hmm... + req->val.rty = needs_my ? RQT_DATAM : req->has_default ? req->def.rty : req->weak ? RQT_DATAV : RQT_DATA; + req->val.dat.has_size = 1; + req->val.dat.sz = typ->szinfo.size; + req->has_val = 1; + return 1; + } else { + printf("Error: TODO: solve_request for data %s with non-simple type ", string_content(req->obj_name)); + type_print(typ); + printf("\n"); + return 0; + } + } +} +int solve_request_map(request_t *req, khash_t(type_map) *decl_map) { + khiter_t it = kh_get(type_map, decl_map, string_content(req->obj_name)); + if (it == kh_end(decl_map)) { + if (string_content(req->obj_name)[0] != '_') { + printf("Error: %s was not declared\n", string_content(req->obj_name)); + } + return 0; + } + return solve_request(req, kh_val(decl_map, it)); +} +int solve_requests(VECTOR(requests) *reqs, khash_t(type_map) *decl_map) { + int ret = 1; + vector_for(requests, req, reqs) { + if (!solve_request_map(req, decl_map)) ret = 0; + } + return ret; +} diff --git a/wrapperhelper/src/generator.h b/wrapperhelper/src/generator.h new file mode 100644 index 000000000..ef27ad0a3 --- /dev/null +++ b/wrapperhelper/src/generator.h @@ -0,0 +1,50 @@ +#pragma once + +#ifndef GENERATOR_H +#define GENERATOR_H + +#include + +#include "cstring.h" +#include "lang.h" + +typedef struct request_s { + string_t *obj_name; + _Bool has_default, default_comment; + _Bool has_val; + _Bool weak; + struct { + enum request_type_e { + RQT_FUN, + RQT_FUN_2, + RQT_FUN_MY, + RQT_FUN_D, + + RQT_DATA, + RQT_DATAV, + RQT_DATAB, + RQT_DATAM, + } rty; + union { + struct { + string_t *typ; + string_t *fun2; + } fun; + struct { + int has_size; + size_t sz; + } dat; + }; + } def, val; +} request_t; +VECTOR_DECLARE(requests, request_t) +void request_print(request_t *req); +void request_print_check(request_t *req); +void output_from_requests(FILE *f, VECTOR(requests) *reqs); + +VECTOR(requests) *requests_from_file(const char *filename, FILE *f); // Takes ownership of f +int solve_request(request_t *req, type_t *typ); +int solve_request_map(request_t *req, khash_t(type_map) *decl_map); +int solve_requests(VECTOR(requests) *reqs, khash_t(type_map) *decl_map); + +#endif // GENERATOR_H diff --git a/wrapperhelper/src/khash.h b/wrapperhelper/src/khash.h new file mode 100644 index 000000000..57a98bb4d --- /dev/null +++ b/wrapperhelper/src/khash.h @@ -0,0 +1,699 @@ +/* The MIT License + + Copyright (c) 2008, 2009, 2011 by Attractive Chaos + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +/* + An example: + +#include "khash.h" +KHASH_MAP_INIT_INT(32, char) +int main() { + int ret, is_missing; + khiter_t k; + khash_t(32) *h = kh_init(32); + k = kh_put(32, h, 5, &ret); + kh_value(h, k) = 10; + k = kh_get(32, h, 10); + is_missing = (k == kh_end(h)); + k = kh_get(32, h, 5); + kh_del(32, h, k); + for (k = kh_begin(h); k != kh_end(h); ++k) + if (kh_exist(h, k)) kh_value(h, k) = 1; + kh_destroy(32, h); + return 0; +} +*/ + +/* + 2013-05-02 (0.2.8): + + * Use quadratic probing. When the capacity is power of 2, stepping function + i*(i+1)/2 guarantees to traverse each bucket. It is better than double + hashing on cache performance and is more robust than linear probing. + + In theory, double hashing should be more robust than quadratic probing. + However, my implementation is probably not for large hash tables, because + the second hash function is closely tied to the first hash function, + which reduce the effectiveness of double hashing. + + Reference: http://research.cs.vt.edu/AVresearch/hashing/quadratic.php + + 2011-12-29 (0.2.7): + + * Minor code clean up; no actual effect. + + 2011-09-16 (0.2.6): + + * The capacity is a power of 2. This seems to dramatically improve the + speed for simple keys. Thank Zilong Tan for the suggestion. Reference: + + - http://code.google.com/p/ulib/ + - http://nothings.org/computer/judy/ + + * Allow to optionally use linear probing which usually has better + performance for random input. Double hashing is still the default as it + is more robust to certain non-random input. + + * Added Wang's integer hash function (not used by default). This hash + function is more robust to certain non-random input. + + 2011-02-14 (0.2.5): + + * Allow to declare global functions. + + 2009-09-26 (0.2.4): + + * Improve portability + + 2008-09-19 (0.2.3): + + * Corrected the example + * Improved interfaces + + 2008-09-11 (0.2.2): + + * Improved speed a little in kh_put() + + 2008-09-10 (0.2.1): + + * Added kh_clear() + * Fixed a compiling error + + 2008-09-02 (0.2.0): + + * Changed to token concatenation which increases flexibility. + + 2008-08-31 (0.1.2): + + * Fixed a bug in kh_get(), which has not been tested previously. + + 2008-08-31 (0.1.1): + + * Added destructor +*/ + + +#ifndef __AC_KHASH_H +#define __AC_KHASH_H + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsign-conversion" +#pragma GCC diagnostic ignored "-Wconversion" + +/*! + @header + + Generic hash table library. + */ + +#define AC_VERSION_KHASH_H "0.2.8" + +#include +#include +#include + +/* compiler specific configuration */ + +#if UINT_MAX == 0xffffffffu +typedef unsigned int khint32_t; +#elif ULONG_MAX == 0xffffffffu +typedef unsigned long khint32_t; +#endif + +#if ULONG_MAX == ULLONG_MAX +typedef unsigned long khint64_t; +#else +typedef unsigned long long khint64_t; +#endif + +#ifdef _MSC_VER +#define kh_inline __inline +#else +#define kh_inline inline +#endif + +typedef khint32_t khint_t; +typedef khint_t khiter_t; + +#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2) +#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1) +#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3) +#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1u<<((i&0xfU)<<1))) +#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2u<<((i&0xfU)<<1))) +#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3u<<((i&0xfU)<<1))) +#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1u<<((i&0xfU)<<1)) + +#define __ac_fsize(m) ((m) < 16? 1 : (m)>>4) + +#ifndef kroundup32 +#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x)) +#endif + +#ifndef kcalloc +#define kcalloc(N,Z) calloc(N,Z) +#endif +#ifndef kmalloc +#define kmalloc(Z) malloc(Z) +#endif +#ifndef krealloc +#define krealloc(P,Z) realloc(P,Z) +#endif +#ifndef kfree +#define kfree(P) free(P) +#endif + +static const double __ac_HASH_UPPER = 0.77; + +#define __KHASH_TYPE(name, khkey_t, khval_t) \ + typedef struct kh_##name##_s{ \ + khint_t n_buckets, size, n_occupied, upper_bound; \ + khint32_t *flags; \ + khkey_t *keys; \ + khval_t *vals; \ + } kh_##name##_t; + +#define __KHASH_PROTOTYPES(name, khkey_t, khval_t) \ + extern kh_##name##_t *kh_init_##name(void); \ + extern void kh_destroy_##name(kh_##name##_t *h); \ + extern void kh_clear_##name(kh_##name##_t *h); \ + extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); \ + extern int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \ + extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \ + extern void kh_del_##name(kh_##name##_t *h, khint_t x); + +#define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ + SCOPE kh_##name##_t *kh_init_##name(void) { \ + return (kh_##name##_t*)kcalloc(1, sizeof(kh_##name##_t)); \ + } \ + SCOPE void kh_destroy_##name(kh_##name##_t *h) \ + { \ + if (h) { \ + kfree((void *)h->keys); kfree(h->flags); \ + kfree((void *)h->vals); \ + kfree(h); \ + } \ + } \ + SCOPE void kh_clear_##name(kh_##name##_t *h) \ + { \ + if (h && h->flags) { \ + memset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \ + h->size = h->n_occupied = 0; \ + } \ + } \ + __attribute__((pure)) SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \ + { \ + if (h->n_buckets) { \ + khint_t k, i, last, mask, step = 0; \ + mask = h->n_buckets - 1; \ + k = __hash_func(key); i = k & mask; \ + last = i; \ + while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \ + i = (i + (++step)) & mask; \ + if (i == last) return h->n_buckets; \ + } \ + return __ac_iseither(h->flags, i)? h->n_buckets : i; \ + } else return 0; \ + } \ + SCOPE int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \ + { /* This function uses 0.25*n_buckets bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \ + khint32_t *new_flags = 0; \ + khint_t j = 1; \ + { \ + kroundup32(new_n_buckets); \ + if (new_n_buckets < 4) new_n_buckets = 4; \ + if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0; /* requested size is too small */ \ + else { /* hash table size to be changed (shrink or expand); rehash */ \ + new_flags = (khint32_t*)kmalloc(__ac_fsize(new_n_buckets) * sizeof(khint32_t)); \ + if (!new_flags) return -1; \ + memset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \ + if (h->n_buckets < new_n_buckets) { /* expand */ \ + khkey_t *new_keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \ + if (!new_keys) { kfree(new_flags); return -1; } \ + h->keys = new_keys; \ + if (kh_is_map) { \ + khval_t *new_vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \ + if (!new_vals) { kfree(new_flags); return -1; } \ + h->vals = new_vals; \ + } \ + } /* otherwise shrink */ \ + } \ + } \ + if (j) { /* rehashing is needed */ \ + for (j = 0; j != h->n_buckets; ++j) { \ + if (__ac_iseither(h->flags, j) == 0) { \ + khkey_t key = h->keys[j]; \ + khval_t val; \ + khint_t new_mask; \ + new_mask = new_n_buckets - 1; \ + if (kh_is_map) val = h->vals[j]; \ + __ac_set_isdel_true(h->flags, j); \ + while (1) { /* kick-out process; sort of like in Cuckoo hashing */ \ + khint_t k, i, step = 0; \ + k = __hash_func(key); \ + i = k & new_mask; \ + while (!__ac_isempty(new_flags, i)) i = (i + (++step)) & new_mask; \ + __ac_set_isempty_false(new_flags, i); \ + if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \ + { khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \ + if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \ + __ac_set_isdel_true(h->flags, i); /* mark it as deleted in the old hash table */ \ + } else { /* write the element and jump out of the loop */ \ + h->keys[i] = key; \ + if (kh_is_map) h->vals[i] = val; \ + break; \ + } \ + } \ + } \ + } \ + if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \ + h->keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \ + if (kh_is_map) h->vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \ + } \ + kfree(h->flags); /* free the working space */ \ + h->flags = new_flags; \ + h->n_buckets = new_n_buckets; \ + h->n_occupied = h->size; \ + h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \ + } \ + return 0; \ + } \ + SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \ + { \ + khint_t x; \ + if (h->n_occupied >= h->upper_bound) { /* update the hash table */ \ + if (h->n_buckets > (h->size<<1)) { \ + if (kh_resize_##name(h, h->n_buckets - 1) < 0) { /* clear "deleted" elements */ \ + *ret = -1; return h->n_buckets; \ + } \ + } else if (kh_resize_##name(h, h->n_buckets + 1) < 0) { /* expand the hash table */ \ + *ret = -1; return h->n_buckets; \ + } \ + } /* TODO: to implement automatically shrinking; resize() already support shrinking */ \ + { \ + khint_t k, i, site, last, mask = h->n_buckets - 1, step = 0; \ + x = site = h->n_buckets; k = __hash_func(key); i = k & mask; \ + if (__ac_isempty(h->flags, i)) x = i; /* for speed up */ \ + else { \ + last = i; \ + while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \ + if (__ac_isdel(h->flags, i)) site = i; \ + i = (i + (++step)) & mask; \ + if (i == last) { x = site; break; } \ + } \ + if (x == h->n_buckets) { \ + if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \ + else x = i; \ + } \ + } \ + } \ + if (__ac_isempty(h->flags, x)) { /* not present at all */ \ + h->keys[x] = key; \ + __ac_set_isboth_false(h->flags, x); \ + ++h->size; ++h->n_occupied; \ + *ret = 1; \ + } else if (__ac_isdel(h->flags, x)) { /* deleted */ \ + h->keys[x] = key; \ + __ac_set_isboth_false(h->flags, x); \ + ++h->size; \ + *ret = 2; \ + } else *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \ + return x; \ + } \ + SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x) \ + { \ + if (x != h->n_buckets && !__ac_iseither(h->flags, x)) { \ + __ac_set_isdel_true(h->flags, x); \ + --h->size; \ + } \ + } + +#define KHASH_DECLARE(name, khkey_t, khval_t) \ + __KHASH_TYPE(name, khkey_t, khval_t) \ + __KHASH_PROTOTYPES(name, khkey_t, khval_t) + +#define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ + __KHASH_TYPE(name, khkey_t, khval_t) \ + __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) + +#define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ + KHASH_INIT2(name, static kh_inline, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) + +/* --- BEGIN OF HASH FUNCTIONS --- */ + +/*! @function + @abstract Integer hash function + @param key The integer [khint32_t] + @return The hash value [khint_t] + */ +#define kh_int_hash_func(key) (khint32_t)(key) +/*! @function + @abstract Integer comparison function + */ +#define kh_int_hash_equal(a, b) ((a) == (b)) +/*! @function + @abstract 64-bit integer hash function + @param key The integer [khint64_t] + @return The hash value [khint_t] + */ +#define kh_int64_hash_func(key) (khint32_t)((key)>>33^(key)^(key)<<11) +/*! @function + @abstract 64-bit integer comparison function + */ +#define kh_int64_hash_equal(a, b) ((a) == (b)) +/*! @function + @abstract const char* hash function + @param s Pointer to a null terminated string + @return The hash value + */ +__attribute__((pure)) static kh_inline khint_t __ac_X31_hash_string(const char *s) +{ + khint_t h = (khint_t)*s; + if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)*s; + return h; +} +/*! @function + @abstract Another interface to const char* hash function + @param key Pointer to a null terminated string [const char*] + @return The hash value [khint_t] + */ +#define kh_str_hash_func(key) __ac_X31_hash_string(key) +/*! @function + @abstract Const char* comparison function + */ +#define kh_str_hash_equal(a, b) ((!a && !b) || (a && b && strcmp(a, b) == 0)) + +static kh_inline khint_t __ac_Wang_hash(khint_t key) +{ + key += ~(key << 15); + key ^= (key >> 10); + key += (key << 3); + key ^= (key >> 6); + key += ~(key << 11); + key ^= (key >> 16); + return key; +} +#define kh_int_hash_func2(k) __ac_Wang_hash((khint_t)key) + +/* --- END OF HASH FUNCTIONS --- */ + +/* Other convenient macros... */ + +/*! + @abstract Type of the hash table. + @param name Name of the hash table [symbol] + */ +#define khash_t(name) kh_##name##_t + +/*! @function + @abstract Initiate a hash table. + @param name Name of the hash table [symbol] + @return Pointer to the hash table [khash_t(name)*] + */ +#define kh_init(name) kh_init_##name() + +/*! @function + @abstract Destroy a hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + */ +#define kh_destroy(name, h) kh_destroy_##name(h) + +/*! @function + @abstract Reset a hash table without deallocating memory. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + */ +#define kh_clear(name, h) kh_clear_##name(h) + +/*! @function + @abstract Resize a hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param s New size [khint_t] + */ +#define kh_resize(name, h, s) kh_resize_##name(h, s) + +/*! @function + @abstract Insert a key to the hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param k Key [type of keys] + @param r Extra return code: 0 if the key is present in the hash table; + 1 if the bucket is empty (never used); 2 if the element in + the bucket has been deleted [int*] + @return Iterator to the inserted element [khint_t] + */ +#define kh_put(name, h, k, r) kh_put_##name(h, k, r) + +/*! @function + @abstract Retrieve a key from the hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param k Key [type of keys] + @return Iterator to the found element, or kh_end(h) if the element is absent [khint_t] + */ +#define kh_get(name, h, k) kh_get_##name(h, k) + +/*! @function + @abstract Remove a key from the hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param k Iterator to the element to be deleted [khint_t] + */ +#define kh_del(name, h, k) kh_del_##name(h, k) + +/*! @function + @abstract Test whether a bucket contains data. + @param h Pointer to the hash table [khash_t(name)*] + @param x Iterator to the bucket [khint_t] + @return 1 if containing data; 0 otherwise [int] + */ +#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x))) + +/*! @function + @abstract Get key given an iterator + @param h Pointer to the hash table [khash_t(name)*] + @param x Iterator to the bucket [khint_t] + @return Key [type of keys] + */ +#define kh_key(h, x) ((h)->keys[x]) + +/*! @function + @abstract Get value given an iterator + @param h Pointer to the hash table [khash_t(name)*] + @param x Iterator to the bucket [khint_t] + @return Value [type of values] + @discussion For hash sets, calling this results in segfault. + */ +#define kh_val(h, x) ((h)->vals[x]) + +/*! @function + @abstract Alias of kh_val() + */ +#define kh_value(h, x) ((h)->vals[x]) + +/*! @function + @abstract Get the start iterator + @param h Pointer to the hash table [khash_t(name)*] + @return The start iterator [khint_t] + */ +#define kh_begin(h) (khint_t)(0) + +/*! @function + @abstract Get the end iterator + @param h Pointer to the hash table [khash_t(name)*] + @return The end iterator [khint_t] + */ +#define kh_end(h) ((h)->n_buckets) + +/*! @function + @abstract Get the number of elements in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @return Number of elements in the hash table [khint_t] + */ +#define kh_size(h) ((h)->size) + +/*! @function + @abstract Get the number of buckets in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @return Number of buckets in the hash table [khint_t] + */ +#define kh_n_buckets(h) ((h)->n_buckets) + +/*! @function + @abstract Iterate over the entries in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @param kvar Variable to which key will be assigned + @param vvar Variable to which value will be assigned + @param code Block of code to execute + */ +#define kh_foreach(h, kvar, vvar, code) { khint_t __i; \ + for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \ + if (!kh_exist(h,__i)) continue; \ + (kvar) = kh_key(h,__i); \ + (vvar) = kh_val(h,__i); \ + code; \ + } } + +/*! @function + @abstract Iterate over the entries in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @param kvar Variable to which key will be assigned + @param code Block of code to execute + */ +#define kh_foreach_key(h, kvar, code) { khint_t __i; \ + for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \ + if (!kh_exist(h,__i)) continue; \ + (kvar) = kh_key(h,__i); \ + code; \ + } } + +/*! @function + @abstract Iterate over the values in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @param vvar Variable to which value will be assigned + @param code Block of code to execute + */ +#define kh_foreach_value(h, vvar, code) { khint_t __i; \ + for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \ + if (!kh_exist(h,__i)) continue; \ + (vvar) = kh_val(h,__i); \ + code; \ + } } + +/*! @function + @abstract Iterate over the entries in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @param kvar Variable to which key will be assigned + @param rvar Variable to which value will be assigned + @param code Block of code to execute + */ +#define kh_foreach_key_value_ref(h, kvar, rvar, code) { khint_t __i; \ + for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \ + if (!kh_exist(h,__i)) continue; \ + (kvar) = kh_key(h,__i); \ + (rvar) = &kh_val(h,__i); \ + code; \ + } } + +/*! @function + @abstract Iterate over the values in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @param rvar Variable to which value will be assigned + @param code Block of code to execute + */ +#define kh_foreach_value_ref(h, rvar, code) { khint_t __i; \ + for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \ + if (!kh_exist(h,__i)) continue; \ + (rvar) = &kh_val(h,__i); \ + code; \ + } } + +/* More conenient interfaces */ + +/*! @function + @abstract Instantiate a hash set containing integer keys + @param name Name of the hash table [symbol] + */ +#define KHASH_SET_INIT_INT(name) \ + KHASH_INIT(name, khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal) + +#define KHASH_SET_DECLARE_INT(name) \ + KHASH_DECLARE(name, khint32_t, char) + +#define KHASH_SET_IMPL_INT(name) \ + __KHASH_IMPL(name, , khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing integer keys + @param name Name of the hash table [symbol] + @param khval_t Type of values [type] + */ +#define KHASH_MAP_INIT_INT(name, khval_t) \ + KHASH_INIT(name, khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal) + +#define KHASH_MAP_DECLARE_INT(name, khval_t) \ + KHASH_DECLARE(name, khint32_t, khval_t) + +#define KHASH_MAP_IMPL_INT(name, khval_t) \ + __KHASH_IMPL(name, , khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing 64-bit integer keys + @param name Name of the hash table [symbol] + */ +#define KHASH_SET_INIT_INT64(name) \ + KHASH_INIT(name, khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal) + +#define KHASH_SET_DECLARE_INT64(name) \ + KHASH_DECLARE(name, khint64_t, char) + +#define KHASH_SET_IMPL_INT64(name) \ + __KHASH_IMPL(name, , khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing 64-bit integer keys + @param name Name of the hash table [symbol] + @param khval_t Type of values [type] + */ +#define KHASH_MAP_INIT_INT64(name, khval_t) \ + KHASH_INIT(name, khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal) + +#define KHASH_MAP_DECLARE_INT64(name, khval_t) \ + KHASH_DECLARE(name, khint64_t, khval_t) + +#define KHASH_MAP_IMPL_INT64(name, khval_t) \ + __KHASH_IMPL(name, , khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal) + +typedef const char *kh_cstr_t; +/*! @function + @abstract Instantiate a hash map containing const char* keys + @param name Name of the hash table [symbol] + */ +#define KHASH_SET_INIT_STR(name) \ + KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal) + +#define KHASH_SET_DECLARE_STR(name) \ + KHASH_DECLARE(name, kh_cstr_t, char) + +#define KHASH_SET_IMPL_STR(name) \ + __KHASH_IMPL(name, , kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing const char* keys + @param name Name of the hash table [symbol] + @param khval_t Type of values [type] + */ +#define KHASH_MAP_INIT_STR(name, khval_t) \ + KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal) + +#define KHASH_MAP_DECLARE_STR(name, khval_t) \ + KHASH_DECLARE(name, kh_cstr_t, khval_t) + +#define KHASH_MAP_IMPL_STR(name, khval_t) \ + __KHASH_IMPL(name, , kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal) + +#pragma GCC diagnostic pop + +#endif /* __AC_KHASH_H */ diff --git a/wrapperhelper/src/lang.c b/wrapperhelper/src/lang.c new file mode 100644 index 000000000..9ca51b15d --- /dev/null +++ b/wrapperhelper/src/lang.c @@ -0,0 +1,1164 @@ +#include "lang.h" + +#include +#include +#include +#include +#include +#include + +#if 1 +#define DISP_ADDR_FMT "" +#define DISP_ADDR_ARG(v) +#else +#define DISP_ADDR_FMT "%p " +#define DISP_ADDR_ARG(v) v, +#endif + +void preproc_token_del(preproc_token_t *tok) { + switch (tok->tokt) { + case PPTOK_IDENT: + case PPTOK_IDENT_UNEXP: + case PPTOK_NUM: + string_del(tok->tokv.str); + break; + case PPTOK_STRING: + case PPTOK_INCL: + string_del(tok->tokv.sstr); + break; + case PPTOK_INVALID: + case PPTOK_SYM: + case PPTOK_NEWLINE: + case PPTOK_BLANK: + case PPTOK_START_LINE_COMMENT: + case PPTOK_EOF: + break; + } +} + +VECTOR_IMPL(preproc, preproc_token_del) + +void proc_token_del(proc_token_t *tok) { + switch (tok->tokt) { + case PTOK_IDENT: + case PTOK_NUM: + string_del(tok->tokv.str); + break; + case PTOK_STRING: + string_del(tok->tokv.sstr); + break; + case PTOK_PRAGMA: + switch (tok->tokv.pragma.typ) { + case PRAGMA_MARK_SIMPLE: + string_del(tok->tokv.pragma.val); + break; + case PRAGMA_ALLOW_INTS: + break; + } + break; + case PTOK_INVALID: + case PTOK_KEYWORD: + case PTOK_SYM: + case PTOK_EOF: + break; + } +} + +VECTOR_IMPL(proc, proc_token_del) + +const char *sym2str[LAST_SYM + 1] = { + [SYM_LBRACKET] = "{", + [SYM_RBRACKET] = "}", + [SYM_LSQBRACKET] = "[", + [SYM_RSQBRACKET] = "]", + [SYM_LPAREN] = "(", + [SYM_RPAREN] = ")", + [SYM_HASH] = "#", + [SYM_HASHHASH] = "##", + [SYM_SEMICOLON] = ";", + [SYM_COLON] = ":", + [SYM_COLONCOLON] = "::", + [SYM_VARIADIC] = "...", + [SYM_QUESTION] = "?", + [SYM_DOT] = ".", + [SYM_DASHGT] = "->", + [SYM_TILDE] = "~", + [SYM_EXCL] = "!", + [SYM_PLUS] = "+", + [SYM_DASH] = "-", + [SYM_STAR] = "*", + [SYM_SLASH] = "/", + [SYM_PERCENT] = "%", + [SYM_HAT] = "^", + [SYM_AMP] = "&", + [SYM_PIPE] = "|", + [SYM_EQ] = "=", + [SYM_PLUSEQ] = "+=", + [SYM_DASHEQ] = "-=", + [SYM_STAREQ] = "*=", + [SYM_SLASHEQ] = "/=", + [SYM_PERCENTEQ] = "%=", + [SYM_HATEQ] = "^=", + [SYM_AMPEQ] = "&=", + [SYM_PIPEEQ] = "|=", + [SYM_EQEQ] = "==", + [SYM_EXCLEQ] = "!=", + [SYM_LT] = "<", + [SYM_GT] = ">", + [SYM_LTEQ] = "<=", + [SYM_GTEQ] = ">=", + [SYM_AMPAMP] = "&&", + [SYM_PIPEPIPE] = "||", + [SYM_LTLT] = "<<", + [SYM_GTGT] = ">>", + [SYM_LTLTEQ] = "<<=", + [SYM_GTGTEQ] = ">>=", + [SYM_PLUSPLUS] = "++", + [SYM_DASHDASH] = "--", + [SYM_COMMA] = ",", +}; + +void preproc_token_print(const preproc_token_t *tok) { + switch (tok->tokt) { + case PPTOK_INVALID: + printf("Token: %7s %hhd (%c)\n", "#INVAL#", tok->tokv.c, (tok->tokv.c >= 0x20) && (tok->tokv.c < 0x7F) ? tok->tokv.c : '?'); + break; + case PPTOK_IDENT: + printf("Token: %7s '%s'\n", "IDENT", string_content(tok->tokv.str)); + break; + case PPTOK_IDENT_UNEXP: + printf("Token: %7s '%s'\n", "IDENT'", string_content(tok->tokv.str)); + break; + case PPTOK_NUM: + printf("Token: %7s '%s'\n", "NUM", string_content(tok->tokv.str)); + break; + case PPTOK_STRING: + printf("Token: %7s %c%s%c\n", "STRING", + tok->tokv.sisstr ? '"' : '\'', string_content(tok->tokv.sstr), tok->tokv.sisstr ? '"' : '\''); + break; + case PPTOK_INCL: + printf("Token: %7s %c%s%c\n", "INCL", + tok->tokv.sisstr ? '"' : '<', string_content(tok->tokv.sstr), tok->tokv.sisstr ? '"' : '>'); + break; + case PPTOK_SYM: + printf("Token: %7s %-3s (%u)\n", "SYM", sym2str[tok->tokv.sym], tok->tokv.sym); + break; + case PPTOK_NEWLINE: + printf("Token: %7s\n", "NEWLINE"); + break; + case PPTOK_BLANK: + printf("Token: %7s\n", "\e[2;31m(blank)\e[m"); + break; + case PPTOK_START_LINE_COMMENT: + printf("Token: %7s\n", "\e[2;31m( // ) \e[m"); + break; + case PPTOK_EOF: + printf("Token: %7s\n", "EOF"); + break; + default: + printf("Token: ??? %u\n", tok->tokt); + } +} + +int preproc_token_isend(const preproc_token_t *tok) { + switch (tok->tokt) { + case PPTOK_IDENT: + case PPTOK_IDENT_UNEXP: + case PPTOK_NUM: + case PPTOK_STRING: + case PPTOK_INCL: + case PPTOK_SYM: + case PPTOK_NEWLINE: + case PPTOK_BLANK: + case PPTOK_START_LINE_COMMENT: + return 0; + case PPTOK_INVALID: + case PPTOK_EOF: + default: + return 1; + } +} + +const char *kw2str[LAST_KEYWORD + 1] = { + [KW_ALIGNAS] = "_Alignas", + [KW_ALIGNOF] = "_Alignof", + [KW_ATOMIC] = "_Atomic", + [KW_AUTO] = "auto", + [KW_BOOL] = "_Bool", + [KW_BREAK] = "break", + [KW_CASE] = "case", + [KW_CHAR] = "char", + [KW_COMPLEX] = "_Complex", + [KW_CONST] = "const", + [KW_CONTINUE] = "continue", + [KW_DEFAULT] = "default", + [KW_DO] = "do", + [KW_DOUBLE] = "double", + [KW_ELSE] = "else", + [KW_ENUM] = "enum", + [KW_EXTERN] = "extern", + [KW_FLOAT] = "float", + [KW_FOR] = "for", + [KW_GENERIC] = "_Generic", + [KW_GOTO] = "goto", + [KW_IF] = "if", + [KW_IMAGINARY] = "_Imaginary", + [KW_INLINE] = "inline", + [KW_INT] = "int", + [KW_INT128] = "__int128", + [KW_LONG] = "long", + [KW_NORETURN] = "_Noreturn", + [KW_REGISTER] = "register", + [KW_RESTRICT] = "restrict", + [KW_RETURN] = "return", + [KW_SHORT] = "short", + [KW_SIGNED] = "signed", + [KW_SIZEOF] = "sizeof", + [KW_STATIC] = "static", + [KW_STATIC_ASSERT] = "_Static_assert", + [KW_STRUCT] = "struct", + [KW_SWITCH] = "switch", + [KW_THREAD_LOCAL] = "_Thread_local", + [KW_TYPEDEF] = "typedef", + [KW_UNION] = "union", + [KW_UNSIGNED] = "unsigned", + [KW_VOID] = "void", + [KW_VOLATILE] = "volatile", + [KW_WHILE] = "while", +}; + +void proc_token_print(const proc_token_t *tok) { + switch (tok->tokt) { + case PTOK_INVALID: + printf("Token: %7s %hhd (%c)\n", "#INVAL#", tok->tokv.c, (tok->tokv.c >= 0x20) && (tok->tokv.c < 0x7F) ? tok->tokv.c : '?'); + break; + case PTOK_IDENT: + printf("Token: %7s '%s'\n", "IDENT", string_content(tok->tokv.str)); + break; + case PTOK_KEYWORD: + printf("Token: %7s '%s' (%u)\n", "KEYWORD", kw2str[tok->tokv.kw], tok->tokv.kw); + break; + case PTOK_NUM: + printf("Token: %7s '%s'\n", "NUM", string_content(tok->tokv.str)); + break; + case PTOK_STRING: + printf("Token: %7s %c%s%c\n", "STRING", + tok->tokv.sisstr ? '"' : '\'', string_content(tok->tokv.sstr), tok->tokv.sisstr ? '"' : '\''); + break; + case PTOK_SYM: + printf("Token: %7s %-3s (%u)\n", "SYM", sym2str[tok->tokv.sym], tok->tokv.sym); + break; + case PTOK_PRAGMA: + switch (tok->tokv.pragma.typ) { + case PRAGMA_ALLOW_INTS: + printf("Token: %7s Allow ints\n", "PRAGMA"); + break; + case PRAGMA_MARK_SIMPLE: + printf("Token: %7s Mark simple: %s\n", "PRAGMA", string_content(tok->tokv.pragma.val)); + break; + default: + printf("Token: %7s ???\n", "PRAGMA"); + } + break; + case PTOK_EOF: + printf("Token: %7s\n", "EOF"); + break; + default: + printf("Token: ??? %u\n", tok->tokt); + } +} + +int proc_token_iserror(const proc_token_t *tok) { + switch (tok->tokt) { + case PTOK_IDENT: + case PTOK_KEYWORD: + case PTOK_NUM: + case PTOK_STRING: + case PTOK_SYM: + case PTOK_PRAGMA: + case PTOK_EOF: + return 0; + case PTOK_INVALID: + default: + return 1; + } +} + +int proc_token_isend(const proc_token_t *tok) { + switch (tok->tokt) { + case PTOK_IDENT: + case PTOK_KEYWORD: + case PTOK_NUM: + case PTOK_STRING: + case PTOK_SYM: + case PTOK_PRAGMA: + return 0; + case PTOK_INVALID: + case PTOK_EOF: + default: + return 1; + } +} + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wanalyzer-null-dereference" +KHASH_MAP_IMPL_STR(str2kw, enum token_keyword_type_e) +KHASH_MAP_IMPL_STR(type_map, type_t*) +KHASH_MAP_IMPL_STR(struct_map, struct_t*) +KHASH_MAP_IMPL_STR(const_map, num_constant_t) +#pragma GCC diagnostic pop +khash_t(str2kw) *str2kw; + +int init_str2kw(void) { + if (!(str2kw = kh_init(str2kw))) { + printf("Failed to create the string to keyword map (init)\n"); + return 0; + } + for (enum token_keyword_type_e kw = 0; kw <= LAST_KEYWORD; ++kw) { + int iret; + khiter_t it = kh_put(str2kw, str2kw, kw2str[kw], &iret); + if (iret < 0) { + printf("Failed to create the string to keyword map (keyword %u)\n", kw); + kh_destroy(str2kw, str2kw); + return 0; + } + kh_val(str2kw, it) = kw; + } + return 1; +} +void del_str2kw(void) { + kh_destroy(str2kw, str2kw); +} + +int num_constant_convert(string_t *str, num_constant_t *cst) { + if (string_len(str) == 0) return 0; // Should never happen +#define contains(c) strchr(string_content(str), c) + if (contains('.') + || (!(contains('X') || contains('x')) && (contains('E') || contains('e'))) + || ((contains('X') || contains('x')) && (contains('P') || contains('p')))) { +#undef contains + int ok; + if ((string_end(str)[-1] == 'l') || (string_end(str)[-1] == 'L')) { + cst->typ = NCT_LDOUBLE; + char *endc; + cst->val.l = strtold(string_content(str), &endc); + ok = (endc == string_end(str) - 1); + } else if ((string_end(str)[-1] == 'f') || (string_end(str)[-1] == 'F')) { + cst->typ = NCT_FLOAT; + char *endc; + cst->val.f = strtof(string_content(str), &endc); + ok = (endc == string_end(str) - 1); + } else { + cst->typ = NCT_DOUBLE; + char *endc; + cst->val.d = strtod(string_content(str), &endc); + ok = (endc == string_end(str)); + } + if (!ok) { + printf("Error: '%s' is not a valid number\n", string_content(str)); + return 0; + } else if (errno == ERANGE) { + printf("Warning: floating point constant is too large\n"); + return 1; + } + return 1; + } else { + uint64_t ret = 0; + unsigned base = (string_content(str)[0] == '0') ? (string_content(str)[1] == 'x') ? 16 : 8 : 10; + size_t startidx = (base == 16) ? 2 : 0; + size_t endidx = string_len(str); + int suffix_type = 0; +#define SUFFIX_U 1 +#define SUFFIX_L 2 +#define SUFFIX_LL 4 +#define SUFFIX_SGN SUFFIX_U +#define SUFFIX_SZ (SUFFIX_L | SUFFIX_LL) + remove_suffix: + if ((string_content(str)[endidx - 1] == 'l') || (string_content(str)[endidx - 1] == 'L')) { + if (suffix_type & SUFFIX_SZ) { + printf("Error: '%s' is not a valid number (invalid suffix)\n", string_content(str)); + return 0; + } + if (endidx == 1) return 0; // Should never happen + if ((string_content(str)[endidx - 2] == 'l') || (string_content(str)[endidx - 2] == 'L')) { + if (endidx == 2) return 0; // Should never happen + if (string_content(str)[endidx - 2] != string_content(str)[endidx - 1]) { + printf("Error: '%s' is not a valid number (invalid suffix)\n", string_content(str)); + return 0; + } + endidx -= 2; + suffix_type |= SUFFIX_LL; + } else { + endidx -= 1; + suffix_type |= SUFFIX_L; + } + goto remove_suffix; + } + if ((string_content(str)[endidx - 1] == 'u') || (string_content(str)[endidx - 1] == 'U')) { + if (suffix_type & SUFFIX_SGN) { + printf("Error: '%s' is not a valid number (invalid suffix)\n", string_content(str)); + return 0; + } + endidx -= 1; + suffix_type |= SUFFIX_U; + goto remove_suffix; + } + // 0u has startidx=0 endidx=1 + if (endidx <= startidx) { + printf("Error: '%s' is not a valid number\n", string_content(str)); + } + for (size_t i = startidx; i < endidx; ++i) { + if ((string_content(str)[i] >= '0') && (string_content(str)[i] <= ((base == 8) ? '7' : '9'))) { + ret = base * ret + (unsigned)(string_content(str)[i] - '0'); + } else if ((base == 16) && (string_content(str)[i] >= 'A') && (string_content(str)[i] <= 'F')) { + ret = base * ret + 10 + (unsigned)(string_content(str)[i] - 'A'); + } else if ((base == 16) && (string_content(str)[i] >= 'a') && (string_content(str)[i] <= 'f')) { + ret = base * ret + 10 + (unsigned)(string_content(str)[i] - 'a'); + } else { + printf("Error: '%s' is not a valid number\n", string_content(str)); + return 0; + } + } + // If base == 10, keep the signness; in any case, try 32 bits if available else use 64 bits + cst->typ = + ((suffix_type & SUFFIX_SGN) == SUFFIX_U) ? + ((suffix_type & SUFFIX_SZ) == SUFFIX_L) ? LONG_IS_32BITS ? NCT_UINT32 : NCT_UINT64 : + ((suffix_type & SUFFIX_SZ) == SUFFIX_LL) ? NCT_UINT64 : + NCT_UINT32 : + ((suffix_type & SUFFIX_SZ) == SUFFIX_L) ? LONG_IS_32BITS ? NCT_INT32 : NCT_INT64 : + ((suffix_type & SUFFIX_SZ) == SUFFIX_LL) ? NCT_INT64 : + NCT_INT32; + if (cst->typ == NCT_INT32) { + if (ret < 1ULL << 31) { + cst->val.i32 = (int32_t)ret; + return 1; + } + // Not in signed 32-bits = int => try long/long long for decimal, unsigned for hexadecimal/octal + if (base != 10) cst->typ = NCT_UINT32; + else cst->typ = NCT_INT64; + } + if (cst->typ == NCT_UINT32) { + if (ret < 1ULL << 32) { + cst->val.u32 = (uint32_t)ret; + return 1; + } + // Not in unsigned 32-bits = unsigned => try unsigned long/long long for decimal and unsigned, long/long long for signed hexadecimal/octal + if ((base != 10) && ((suffix_type & SUFFIX_SGN) != SUFFIX_U)) cst->typ = NCT_INT64; + else cst->typ = NCT_UINT64; + } + if (cst->typ == NCT_INT64) { + if (ret < 1ULL << 63) { + cst->val.i64 = (int64_t)ret; + return 1; + } + // Not in signed 64-bits = long/long long => fail for decimal, try unsigned long/long long for hexadecimal/octal + if (base != 10) cst->typ = NCT_UINT64; + } + if (cst->typ == NCT_UINT64) { + cst->val.u64 = ret; + return 1; + } + // The constant cannot be typed... (Or internal error) + return 0; + } +} + +void expr_del(expr_t *e) { + switch (e->typ) { + case ETY_VAR: + string_del(e->val.var); + break; + + case ETY_CONST: + break; + + // case ETY_GENERIC: + // TODO + + case ETY_CALL: + expr_del(e->val.call.fun); + for (size_t i = 0; i < e->val.call.nargs; ++i) { + expr_del(e->val.call.args[i]); + } + if (e->val.call.args) free(e->val.call.args); + break; + + case ETY_ACCESS: + case ETY_PTRACCESS: + expr_del(e->val.access.val); + string_del(e->val.access.member); + break; + + case ETY_UNARY: + expr_del(e->val.unary.e); + break; + + case ETY_BINARY: + expr_del(e->val.binary.e1); + expr_del(e->val.binary.e2); + break; + + case ETY_TERNARY: + expr_del(e->val.ternary.e1); + expr_del(e->val.ternary.e2); + expr_del(e->val.ternary.e3); + break; + + // case ETY_INIT_LIST: + // TODO + + case ETY_CAST: + type_del(e->val.cast.typ); + expr_del(e->val.cast.e); + break; + } + free(e); +} + +void struct_del_weak(struct_t *st) { + if (--st->nrefs) return; + if (!st->tag) struct_del(st); +} +void type_del(type_t *typ) { + if (--typ->nrefs) return; + switch (typ->typ) { + case TYPE_BUILTIN: + break; + case TYPE_ARRAY: + type_del(typ->val.array.typ); + break; + case TYPE_STRUCT_UNION: + struct_del_weak(typ->val.st); + break; + case TYPE_ENUM: + case TYPE_PTR: + type_del(typ->val.typ); + break; + case TYPE_FUNCTION: + type_del(typ->val.fun.ret); + if (typ->val.fun.nargs != (size_t)-1) { + for (size_t i = 0; i < typ->val.fun.nargs; ++i) { + type_del(typ->val.fun.args[i]); + } + } + if (typ->val.fun.args) free(typ->val.fun.args); + break; + } + free(typ); +} +void type_map_del(khash_t(type_map) *map) { + kh_cstr_t str; + type_t **it; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" + kh_foreach_key_value_ref(map, str, it, free((void*)str); type_del(*it)) +#pragma GCC diagnostic pop + kh_destroy(type_map, map); +} +void type_set_del(khash_t(type_set) *set) { + type_t *it; + kh_foreach_key(set, it, type_del(it)) + kh_destroy(type_set, set); +} +void const_map_del(khash_t(const_map) *map) { + kh_cstr_t str; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" + kh_foreach_key(map, str, free((void*)str)) +#pragma GCC diagnostic pop + kh_destroy(const_map, map); +} + +void st_member_del(st_member_t *member) { + if (member->name) string_del(member->name); + type_del(member->typ); +} + +void struct_del(struct_t *st) { + if (st->tag) { + string_del(st->tag); + st->tag = NULL; + } + if (st->is_defined) { + for (size_t i = 0; i < st->nmembers; ++i) { + st_member_del(&st->members[i]); + } + free(st->members); + st->is_defined = 0; + } + if (!st->nrefs) free(st); +} +void struct_map_del(khash_t(struct_map) *map) { + // The keys are the tag in the struct_t + struct_t **it; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" + kh_foreach_value_ref(map, it, struct_del(*it)) +#pragma GCC diagnostic pop + kh_destroy(struct_map, map); +} + +type_t *type_new(void) { + type_t *ret = malloc(sizeof *ret); + if (!ret) { + printf("Failed to create a new type\n"); + return NULL; + } + ret->szinfo.align = ret->szinfo.size = 0; + ret->is_atomic = ret->is_const = ret->is_restrict = ret->is_volatile = ret->is_incomplete = ret->is_validated = ret->_internal_use = 0; + ret->nrefs = 1; + ret->typ = TYPE_BUILTIN; + ret->val.builtin = BTT_INT; + return ret; +} +type_t *type_new_ptr(type_t *target) { + type_t *ret = malloc(sizeof *ret); + if (!ret) { + printf("Failed to create a new pointer type\n"); + return NULL; + } + ret->szinfo.align = ret->szinfo.size = 0; + ret->is_atomic = ret->is_const = ret->is_restrict = ret->is_volatile = ret->is_incomplete = ret->is_validated = ret->_internal_use = 0; + ret->nrefs = 1; + ret->typ = TYPE_PTR; + ret->val.typ = target; + return ret; +} +// The following functions do not work for functions (val.args needs to be duplicated) +/* type_t *type_do_copy(type_t *ref) { + type_t *ret = type_do_copy_nodec(ref); + if (!ret) return NULL; + type_del(ref); + return ret; +} +type_t *type_do_copy_nodec(const type_t *ref) { + type_t *ret = malloc(sizeof *ret); + if (!ret) { + printf("Failed to duplicate type\n"); + return NULL; + } + memcpy(ret, ref, sizeof *ret); + switch (ref->typ) { + case TYPE_BUILTIN: + break; + case TYPE_ARRAY: + ++ref->val.array.typ->nrefs; + break; + case TYPE_STRUCT_UNION: + ++ret->val.st->nrefs; + break; + case TYPE_ENUM: + case TYPE_PTR: + ++ref->val.typ->nrefs; + break; + case TYPE_FUNCTION: + ++ref->val.fun.ret->nrefs; + if (ref->val.fun.nargs != (size_t)-1) { + for (size_t i = 0; i < ref->val.fun.nargs; ++i) { + ++ref->val.fun.args[i]->nrefs; + } + } + break; + } + ret->nrefs = 1; + return ret; +} +type_t *type_maybe_copy(type_t *ref) { + if (ref->nrefs == 1) return ref; + else return type_do_copy(ref); +} */ +int type_copy_into(type_t *dest, const type_t *ref) { + size_t nrefs = dest->nrefs; + _Bool is_atomic = dest->is_atomic; + _Bool is_const = dest->is_const; + _Bool is_restrict = dest->is_restrict; + _Bool is_volatile = dest->is_volatile; + memcpy(dest, ref, sizeof *dest); + switch (ref->typ) { + case TYPE_BUILTIN: + break; + case TYPE_ARRAY: + ++ref->val.array.typ->nrefs; + break; + case TYPE_STRUCT_UNION: + ++dest->val.st->nrefs; + break; + case TYPE_ENUM: + case TYPE_PTR: + ++ref->val.typ->nrefs; + break; + case TYPE_FUNCTION: + ++ref->val.fun.ret->nrefs; + if (ref->val.fun.nargs != (size_t)-1) { + if (!ref->val.fun.args) { + dest->typ = TYPE_BUILTIN; + return 0; + } + type_t **args = malloc(sizeof *args * ref->val.fun.nargs); + if (!args) { + printf("Error: failed to allocate new argument array\n"); + return 0; + } + for (size_t i = 0; i < ref->val.fun.nargs; ++i) { + ++ref->val.fun.args[i]->nrefs; + args[i] = ref->val.fun.args[i]; + } + dest->val.fun.args = args; + } + break; + } + dest->is_atomic |= is_atomic; + dest->is_const |= is_const; + dest->is_restrict |= is_restrict; + dest->is_volatile |= is_volatile; + dest->nrefs = nrefs; + return 1; +} + +struct_t *struct_new(int is_struct, string_t *tag) { + struct_t *ret = malloc(sizeof *ret); + if (!ret) { + printf("Failed to create a new struct\n"); + return NULL; + } + ret->is_struct = is_struct; + ret->tag = tag; + ret->is_defined = 0; + ret->nrefs = 1; + ret->explicit_simple = 0; + return ret; +} + +_Static_assert(sizeof(type_t*) == sizeof(khint64_t), "Not a 64-bits machine"); +khint_t type_t_hash(type_t *typ) { + switch (typ->typ) { + case TYPE_BUILTIN: return kh_int_hash_func(typ->val.builtin); + case TYPE_ARRAY: return kh_int_hash_func((typ->val.array.array_sz << 12) + type_t_hash(typ->val.array.typ)); + case TYPE_STRUCT_UNION: + if (typ->val.st->tag) { + return kh_str_hash_func(string_content(typ->val.st->tag)); + } else { + khint64_t acc = (typ->val.st->nmembers << 5) + 1; + for (size_t i = 0; i < typ->val.st->nmembers; ++i) { + acc += (khint64_t)type_t_hash(typ->val.st->members[i].typ) << ((1ull << (i % 64)) % 61); + } + return kh_int64_hash_func(acc); + } + case TYPE_ENUM: + case TYPE_PTR: + return kh_int_hash_func(type_t_hash(typ->val.typ)); + case TYPE_FUNCTION: { + khint64_t acc = type_t_hash(typ->val.fun.ret) + typ->val.fun.nargs * 73 + (size_t)typ->val.fun.has_varargs * 77; + if (typ->val.fun.nargs != (size_t)-1) { + for (size_t i = 0; i < typ->val.fun.nargs; ++i) { + acc += (khint64_t)type_t_hash(typ->val.fun.args[i]) << ((3ull << (i % 64)) % 53); + } + } + return kh_int64_hash_func(acc); } + default: return 0; + } +} +int type_t_equal(type_t *typ1, type_t *typ2) { + if (typ1->typ != typ2->typ) return 0; + if ((typ1->is_atomic != typ2->is_atomic) || (typ1->is_const != typ2->is_const) + || (typ1->is_restrict != typ2->is_restrict) || (typ1->is_volatile != typ2->is_volatile)) { + return 0; + } + switch (typ1->typ) { + case TYPE_BUILTIN: return typ1->val.builtin == typ2->val.builtin; + case TYPE_ARRAY: return (typ1->val.array.array_sz == typ2->val.array.array_sz) && type_t_equal(typ1->val.array.typ, typ2->val.array.typ); + case TYPE_STRUCT_UNION: + // This will not do an infinite recursion since only unnamed struct/unions will compare their members + if (!typ1->val.st->tag != !typ2->val.st->tag) return 0; + if (typ1->val.st->tag) return !strcmp(string_content(typ1->val.st->tag), string_content(typ2->val.st->tag)); + if (!typ1->val.st->is_defined || !typ2->val.st->is_defined) { + printf("Warning: incomplete anonymous structure/union\n"); + return 0; + } + if (typ1->val.st->nmembers != typ2->val.st->nmembers) return 0; + for (size_t i = 0; i < typ1->val.st->nmembers; ++i) { + if (!!typ1->val.st->members[i].name != !!typ2->val.st->members[i].name) return 0; + if (typ1->val.st->members[i].name + && strcmp(string_content(typ1->val.st->members[i].name), string_content(typ2->val.st->members[i].name))) + return 0; + if (!type_t_equal(typ1->val.st->members[i].typ, typ2->val.st->members[i].typ)) return 0; + } + return 1; + case TYPE_ENUM: + case TYPE_PTR: + return type_t_equal(typ1->val.typ, typ2->val.typ); + case TYPE_FUNCTION: + if (typ1->val.fun.nargs != typ2->val.fun.nargs) return 0; + if (typ1->val.fun.has_varargs != typ2->val.fun.has_varargs) return 0; + if (!type_t_equal(typ1->val.fun.ret, typ2->val.fun.ret)) return 0; + if (typ1->val.fun.nargs != (size_t)-1) { + for (size_t i = 0; i < typ1->val.fun.nargs; ++i) { + if (!type_t_equal(typ1->val.fun.args[i], typ2->val.fun.args[i])) return 0; + } + } + return 1; + default: return 0; + } +} +__KHASH_IMPL(type_set, , type_t*, char, 0, type_t_hash, type_t_equal) + +type_t *type_try_merge(type_t *typ, khash_t(type_set) *set) { + int iret; + khiter_t it = kh_put(type_set, set, typ, &iret); + if (iret < 0) { + printf("Error: failed to add type to type_set\n"); + return NULL; + } else if (iret == 0) { + if (typ == kh_key(set, it)) return typ; + kh_key(set, it)->is_incomplete &= typ->is_incomplete; // In case we have a recursive structure + type_del(typ); + ++kh_key(set, it)->nrefs; + return kh_key(set, it); + } else { + ++typ->nrefs; + } + type_t *typ2; + switch (typ->typ) { + case TYPE_BUILTIN: + return typ; + case TYPE_ARRAY: + typ2 = type_try_merge(typ->val.array.typ, set); + if (typ2) typ->val.array.typ = typ2; + else return NULL; + return typ; + case TYPE_STRUCT_UNION: + if (typ->val.st->is_defined) { + for (size_t i = 0; i < typ->val.st->nmembers; ++i) { + typ2 = type_try_merge(typ->val.st->members[i].typ, set); + if (typ2) typ->val.st->members[i].typ = typ2; + else return NULL; + } + } + return typ; + case TYPE_ENUM: + case TYPE_PTR: + typ2 = type_try_merge(typ->val.typ, set); + if (typ2) typ->val.typ = typ2; + else return NULL; + return typ; + case TYPE_FUNCTION: + typ2 = type_try_merge(typ->val.fun.ret, set); + if (typ2) typ->val.fun.ret = typ2; + else return NULL; + + if (typ->val.fun.nargs != (size_t)-1) { + for (size_t i = 0; i < typ->val.fun.nargs; ++i) { + typ2 = type_try_merge(typ->val.fun.args[i], set); + if (typ2) typ->val.fun.args[i] = typ2; + else return NULL; + } + } + return typ; + + default: return NULL; + } +} + +const char *builtin2str[LAST_BUILTIN + 1] = { + [BTT_VOID] = "void", + [BTT_BOOL] = "_Bool", + [BTT_CHAR] = "char", + [BTT_SCHAR] = "signed char", + [BTT_UCHAR] = "unsigned char", + [BTT_SHORT] = "short", + [BTT_SSHORT] = "signed short", + [BTT_USHORT] = "unsigned short", + [BTT_INT] = "int", + [BTT_SINT] = "signed", + [BTT_UINT] = "unsigned", + [BTT_LONG] = "long", + [BTT_SLONG] = "signed long", + [BTT_ULONG] = "unsigned long", + [BTT_LONGLONG] = "long long", + [BTT_SLONGLONG] = "signed long long", + [BTT_ULONGLONG] = "unsigned long long", + [BTT_INT128] = "__int128", + [BTT_SINT128] = "signed __int128", + [BTT_UINT128] = "unsigned __int128", + [BTT_S8] = "int8_t", + [BTT_U8] = "uint8_t", + [BTT_S16] = "int16_t", + [BTT_U16] = "uint16_t", + [BTT_S32] = "int32_t", + [BTT_U32] = "uint32_t", + [BTT_S64] = "int64_t", + [BTT_U64] = "uint64_t", + [BTT_FLOAT] = "float", + [BTT_CFLOAT] = "float _Complex", + [BTT_IFLOAT] = "float _Imaginary", + [BTT_DOUBLE] = "double", + [BTT_CDOUBLE] = "double _Complex", + [BTT_IDOUBLE] = "double _Imaginary", + [BTT_LONGDOUBLE] = "long double", + [BTT_CLONGDOUBLE] = "long double _Complex", + [BTT_ILONGDOUBLE] = "long double _Imaginary", + [BTT_VA_LIST] = "__builtin_va_list", +}; +void type_print(type_t *typ) { + if (typ->_internal_use) { + printf("...%p", typ); + return; + } + typ->_internal_use = 1; + printf("<" DISP_ADDR_FMT "n_uses=%zu> ", DISP_ADDR_ARG(typ) typ->nrefs); + if (!typ->is_validated) printf("! "); + if (typ->is_incomplete) printf(" "); + if (typ->is_validated && !typ->is_incomplete) printf(" ", typ->szinfo.size, typ->szinfo.align); + if (typ->is_const) printf("const "); + if (typ->is_restrict) printf("restrict "); + if (typ->is_volatile) printf("volatile "); + if (typ->is_atomic) printf("_Atomic "); + switch (typ->typ) { + case TYPE_BUILTIN: + printf("", builtin2str[typ->val.builtin], typ->val.builtin); + break; + case TYPE_ARRAY: + if (typ->val.array.array_sz == (size_t)-1) + printf("[] "); + else printf("[%zu] ", typ->val.array.array_sz); + type_print(typ->val.array.typ); + break; + case TYPE_STRUCT_UNION: + struct_print(typ->val.st); + break; + case TYPE_ENUM: + printf(" "); + type_print(typ->val.typ); + break; + case TYPE_PTR: + printf("*"); + type_print(typ->val.typ); + break; + case TYPE_FUNCTION: + printf(""); + } else { + printf(", args=("); + for (size_t i = 0; i < typ->val.fun.nargs; ++i) { + if (i) printf("), ("); + type_print(typ->val.fun.args[i]); + } + if (typ->val.fun.has_varargs && typ->val.fun.nargs) printf(", ...)>"); + else if (typ->val.fun.has_varargs && !typ->val.fun.nargs) printf("...)>"); + else printf(")>"); + } + break; + } + typ->_internal_use = 0; +} +void struct_print(const struct_t *st) { + printf("<" DISP_ADDR_FMT "n_uses=%zu> ", DISP_ADDR_ARG(st) st->nrefs); + if (st->explicit_simple) { + printf(" "); + } + if (st->is_defined) { + printf( + "%s %s { ", + st->is_struct ? "struct" : "union", + st->tag ? string_content(st->tag) : "", + st->nmembers, + st->has_incomplete ? ", with incomplete" : ""); + for (size_t i = 0; i < st->nmembers; ++i) { + if (i) printf(", "); + type_print(st->members[i].typ); + printf(" %s", st->members[i].name ? string_content(st->members[i].name) : ""); + if (st->members[i].is_bitfield) { + printf(" : %zu", st->members[i].bitfield_width); + } + } + printf(" }"); + } else { + printf("%s %s ", st->is_struct ? "struct" : "union", st->tag ? string_content(st->tag) : ""); + } +} + +// TODO: per-arch array +size_t sizeof_btt[LAST_BUILTIN + 1] = { + [BTT_VOID] = 0, + [BTT_BOOL] = 1, + [BTT_CHAR] = 1, + [BTT_SCHAR] = 1, + [BTT_UCHAR] = 1, + [BTT_SHORT] = 2, + [BTT_SSHORT] = 2, + [BTT_USHORT] = 2, + [BTT_INT] = 4, + [BTT_SINT] = 4, + [BTT_UINT] = 4, + [BTT_LONG] = LONG_IS_32BITS ? 4 : 8, + [BTT_SLONG] = LONG_IS_32BITS ? 4 : 8, + [BTT_ULONG] = LONG_IS_32BITS ? 4 : 8, + [BTT_LONGLONG] = 8, + [BTT_SLONGLONG] = 8, + [BTT_ULONGLONG] = 8, + [BTT_INT128] = 16, + [BTT_SINT128] = 16, + [BTT_UINT128] = 16, + [BTT_S8] = 1, + [BTT_U8] = 1, + [BTT_S16] = 2, + [BTT_U16] = 2, + [BTT_S32] = 4, + [BTT_U32] = 4, + [BTT_S64] = 8, + [BTT_U64] = 8, + [BTT_FLOAT] = 4, + [BTT_CFLOAT] = 8, + [BTT_IFLOAT] = 4, + [BTT_DOUBLE] = 8, + [BTT_CDOUBLE] = 16, + [BTT_IDOUBLE] = 8, + [BTT_LONGDOUBLE] = 16, + [BTT_CLONGDOUBLE] = 32, + [BTT_ILONGDOUBLE] = 16, + [BTT_VA_LIST] = LONG_IS_32BITS ? 0 : 24, // TODO +}; +// The following assumes sizeof(unsigned long) == sizeof(void*) +file_t *file_new(void) { + file_t *ret = malloc(sizeof *ret); + if (!ret) { + printf("Failed to create a new translation unit structure (init)\n"); + return NULL; + } + if (!(ret->struct_map = kh_init(struct_map))) { + printf("Failed to create a new translation unit structure (structure map)\n"); + free(ret); + return NULL; + } + if (!(ret->type_map = kh_init(type_map))) { + printf("Failed to create a new translation unit structure (type map)\n"); + kh_destroy(struct_map, ret->struct_map); + free(ret); + return NULL; + } + if (!(ret->enum_map = kh_init(type_map))) { + printf("Failed to create a new translation unit structure (enumeration map)\n"); + kh_destroy(struct_map, ret->struct_map); + kh_destroy(type_map, ret->type_map); + free(ret); + return NULL; + } + if (!(ret->decl_map = kh_init(type_map))) { + printf("Failed to create a new translation unit structure (declaration map)\n"); + kh_destroy(struct_map, ret->struct_map); + kh_destroy(type_map, ret->type_map); + kh_destroy(type_map, ret->enum_map); + free(ret); + return NULL; + } + if (!(ret->type_set = kh_init(type_set))) { + printf("Failed to create a new translation unit structure (type set)\n"); + kh_destroy(struct_map, ret->struct_map); + kh_destroy(type_map, ret->type_map); + kh_destroy(type_map, ret->enum_map); + kh_destroy(type_map, ret->decl_map); + free(ret); + return NULL; + } + if (!(ret->const_map = kh_init(const_map))) { + printf("Failed to create a new translation unit structure (const map)\n"); + kh_destroy(struct_map, ret->struct_map); + kh_destroy(type_map, ret->type_map); + kh_destroy(type_map, ret->enum_map); + kh_destroy(type_map, ret->decl_map); + kh_destroy(type_set, ret->type_set); + free(ret); + return NULL; + } + + // Now fill in the builtin types + int iret; + for (enum type_builtin_e i = 0; i < LAST_BUILTIN + 1; ++i) { + type_t *t = malloc(sizeof *t); + if (!t) { + printf("Failed to create a new translation unit structure (builtin type)\n"); + for (; i--;) { + free(ret->builtins[i]); + } + kh_destroy(struct_map, ret->struct_map); + kh_destroy(type_map, ret->type_map); + kh_destroy(type_map, ret->enum_map); + kh_destroy(type_map, ret->decl_map); + kh_destroy(type_set, ret->type_set); + kh_destroy(const_map, ret->const_map); + free(ret); + return NULL; + } + t->is_atomic = t->is_const = t->is_restrict = t->is_volatile = t->_internal_use = 0; + t->is_incomplete = (i == BTT_VOID); + t->is_validated = 1; + t->nrefs = 2; + t->typ = TYPE_BUILTIN; + t->val.builtin = i; + t->szinfo.align = t->szinfo.size = sizeof_btt[i]; + ret->builtins[i] = t; + kh_put(type_set, ret->type_set, t, &iret); + if (iret < 0) { + printf("Failed to create a new translation unit structure (failed to add intrinsic type to type_set)\n"); + kh_destroy(struct_map, ret->struct_map); + kh_destroy(type_map, ret->type_map); + kh_destroy(type_map, ret->enum_map); + kh_destroy(type_map, ret->decl_map); + kh_destroy(type_set, ret->type_set); + kh_destroy(const_map, ret->const_map); + free(ret); + return NULL; + } else if (iret == 0) { + printf("Failed to create a new translation unit structure (duplicate intrinsic type in type_set)\n"); + for (++i; i--;) { + free(ret->builtins[i]); + } + kh_destroy(struct_map, ret->struct_map); + kh_destroy(type_map, ret->type_map); + kh_destroy(type_map, ret->enum_map); + kh_destroy(type_map, ret->decl_map); + kh_destroy(type_set, ret->type_set); + kh_destroy(const_map, ret->const_map); + free(ret); + return NULL; + } + } + // ret is valid + + // Add __builtin_va_list as a typedef + char *sdup = strdup("__builtin_va_list"); + if (!sdup) { + printf("Failed to create a new translation unit structure (va_list name)\n"); + file_del(ret); + return NULL; + } + khiter_t it = kh_put(type_map, ret->type_map, sdup, &iret); + if (iret < 0) { + printf("Failed to create a new translation unit structure (add va_list typedef)\n"); + free(sdup); + file_del(ret); + return NULL; + } else if (iret == 0) { + printf("Failed to create a new translation unit structure (__builtin_va_list is already a typedef)\n"); + file_del(ret); + return NULL; + } + kh_val(ret->type_map, it) = ret->builtins[BTT_VA_LIST]; + ++ret->builtins[BTT_VA_LIST]->nrefs; + + return ret; +} +void file_del(file_t *f) { + struct_map_del(f->struct_map); + type_map_del(f->type_map); + type_map_del(f->enum_map); + type_map_del(f->decl_map); + type_set_del(f->type_set); + const_map_del(f->const_map); + for (enum type_builtin_e i = 0; i < LAST_BUILTIN + 1; ++i) { + type_del(f->builtins[i]); + } + free(f); +} diff --git a/wrapperhelper/src/lang.h b/wrapperhelper/src/lang.h new file mode 100644 index 000000000..568ab8551 --- /dev/null +++ b/wrapperhelper/src/lang.h @@ -0,0 +1,439 @@ +#pragma once + +#ifndef LANG_H +#define LANG_H + +#include "cstring.h" +#include "khash.h" +#include "vector.h" + +#define LONG_IS_32BITS 0 + +enum token_sym_type_e { + SYM_LBRACKET, + SYM_RBRACKET, + SYM_LSQBRACKET, + SYM_RSQBRACKET, + SYM_LPAREN, + SYM_RPAREN, + SYM_HASH, + SYM_HASHHASH, + SYM_SEMICOLON, + SYM_COLON, + SYM_COLONCOLON, + SYM_VARIADIC, + SYM_QUESTION, + SYM_DOT, + SYM_DASHGT, + SYM_TILDE, + SYM_EXCL, + SYM_PLUS, + SYM_DASH, + SYM_STAR, + SYM_SLASH, + SYM_PERCENT, + SYM_HAT, + SYM_AMP, + SYM_PIPE, + SYM_EQ, + SYM_PLUSEQ, + SYM_DASHEQ, + SYM_STAREQ, + SYM_SLASHEQ, + SYM_PERCENTEQ, + SYM_HATEQ, + SYM_AMPEQ, + SYM_PIPEEQ, + SYM_EQEQ, + SYM_EXCLEQ, + SYM_LT, + SYM_GT, + SYM_LTEQ, + SYM_GTEQ, + SYM_AMPAMP, + SYM_PIPEPIPE, + SYM_LTLT, + SYM_GTGT, + SYM_LTLTEQ, + SYM_GTGTEQ, + SYM_PLUSPLUS, + SYM_DASHDASH, + SYM_COMMA, +}; +#define LAST_SYM SYM_COMMA + +typedef struct preproc_token_s { + enum preproc_token_e { + PPTOK_INVALID = 0, + PPTOK_IDENT, // Expandable ident + PPTOK_IDENT_UNEXP, // Unexpandable ident + PPTOK_NUM, + PPTOK_STRING, + PPTOK_INCL, + PPTOK_SYM, + PPTOK_NEWLINE, + PPTOK_BLANK, + PPTOK_START_LINE_COMMENT, + PPTOK_EOF + } tokt; + union { + string_t *str; + struct { + string_t *sstr; // The string literal content + int sisstr; // 0 for '' or <>, 1 for "" + }; + char c; + enum token_sym_type_e sym; + } tokv; +} preproc_token_t; +VECTOR_DECLARE(preproc, preproc_token_t) +void preproc_token_del(preproc_token_t *tok); + +enum token_keyword_type_e { + KW_ALIGNAS = 0, + KW_ALIGNOF, + KW_ATOMIC, + KW_AUTO, + KW_BOOL, + KW_BREAK, + KW_CASE, + KW_CHAR, + KW_COMPLEX, + KW_CONST, + KW_CONTINUE, + KW_DEFAULT, + KW_DO, + KW_DOUBLE, + KW_ELSE, + KW_ENUM, + KW_EXTERN, + KW_FLOAT, + KW_FOR, + KW_GENERIC, + KW_GOTO, + KW_IF, + KW_IMAGINARY, + KW_INLINE, + KW_INT, + KW_INT128, + KW_LONG, + KW_NORETURN, + KW_REGISTER, + KW_RESTRICT, + KW_RETURN, + KW_SHORT, + KW_SIGNED, + KW_SIZEOF, + KW_STATIC, + KW_STATIC_ASSERT, + KW_STRUCT, + KW_SWITCH, + KW_THREAD_LOCAL, + KW_TYPEDEF, + KW_UNION, + KW_UNSIGNED, + KW_VOID, + KW_VOLATILE, + KW_WHILE, +}; +#define LAST_KEYWORD KW_WHILE + +typedef struct proc_token_s { + enum proc_token_e { + PTOK_INVALID = 0, + PTOK_IDENT, + PTOK_KEYWORD, + PTOK_NUM, + PTOK_STRING, + PTOK_SYM, + PTOK_PRAGMA, + PTOK_EOF + } tokt; + union proc_token_val_u { + string_t *str; + struct { + string_t *sstr; // The string literal content + int sisstr; // 0 for '' or <>, 1 for "" + }; + char c; + enum token_sym_type_e sym; + enum token_keyword_type_e kw; + struct { + enum proc_pragma_e { + PRAGMA_ALLOW_INTS, + PRAGMA_MARK_SIMPLE, + } typ; + string_t *val; + } pragma; + } tokv; +} proc_token_t; +VECTOR_DECLARE(proc, proc_token_t) +void proc_token_del(proc_token_t *tok); + +typedef struct num_constant_s { + enum num_constant_e { + NCT_FLOAT, + NCT_DOUBLE, + NCT_LDOUBLE, + NCT_INT32, + NCT_UINT32, + NCT_INT64, + NCT_UINT64, + } typ; + union { + float f; + double d; + long double l; + int32_t i32; + uint32_t u32; + int64_t i64; + uint64_t u64; + } val; +} num_constant_t; +int num_constant_convert(string_t *str, num_constant_t *cst); +KHASH_MAP_DECLARE_STR(const_map, num_constant_t) + +typedef struct expr_s { + enum expr_type_e { + ETY_VAR, + ETY_CONST, + // ETY_GENERIC, + ETY_CALL, + ETY_ACCESS, + ETY_PTRACCESS, // Convertible to DEREF + ACCESS + ETY_UNARY, + ETY_BINARY, + ETY_TERNARY, + // ETY_INIT_LIST, + ETY_CAST, + } typ; + union { + string_t *var; + num_constant_t cst; + // TODO: _Generic + struct { + struct expr_s *fun, **args; + size_t nargs; + } call; + struct { + struct expr_s *val; + string_t *member; + } access; + struct { + enum unary_op_e { + UOT_POSTINCR, + UOT_POSTDECR, + UOT_PREINCR, + UOT_PREDECR, + UOT_REF, + UOT_POS, + UOT_NEG, + UOT_DEREF, + UOT_ANOT, // Arithmetic not, ie '~' + UOT_BNOT, // Boolean not, ie '!' + } typ; + struct expr_s *e; + } unary; + struct { + enum binary_op_e { + BOT_ADD, + BOT_SUB, + BOT_MUL, + BOT_DIV, + BOT_MOD, + BOT_LSH, + BOT_RSH, + BOT_LT, + BOT_GT, + BOT_LE, + BOT_GE, + BOT_EQ, + BOT_NE, + BOT_AAND, + BOT_AXOR, + BOT_AOR, + BOT_BAND, + BOT_BOR, + BOT_ASSGN_EQ, + BOT_ASSGN_ADD, + BOT_ASSGN_SUB, + BOT_ASSGN_MUL, + BOT_ASSGN_DIV, + BOT_ASSGN_MOD, + BOT_ASSGN_LSH, + BOT_ASSGN_RSH, + BOT_ASSGN_AAND, + BOT_ASSGN_AXOR, + BOT_ASSGN_AOR, + BOT_COMMA, + + BOT_ARRAY, // Convertible to DEREF + ADD + } typ; + struct expr_s *e1, *e2; + } binary; + struct { + enum ternary_op_e { + TOT_COND, + } typ; + struct expr_s *e1, *e2, *e3; + } ternary; + // TODO: (type){init} + struct { + struct type_s *typ; + struct expr_s *e; + } cast; + } val; +} expr_t; +void expr_del(expr_t *e); + +typedef struct size_info_s { + size_t size, align; +} size_info_t; + +typedef struct type_s { + struct { + unsigned is_atomic : 1; + unsigned is_const : 1; + unsigned is_restrict : 1; + unsigned is_volatile : 1; + unsigned is_incomplete : 1; // \ The type needs to be complete and + unsigned is_validated : 1; // / validated for the size_info to be populated + unsigned _internal_use : 1; + }; + size_t nrefs; + enum type_type_e { + TYPE_BUILTIN, // builtin + TYPE_ARRAY, // array + TYPE_STRUCT_UNION, // st + TYPE_ENUM, // typ which points to TYPE_BUILTIN + TYPE_PTR, // typ + TYPE_FUNCTION, // fun + } typ; + union { + enum type_builtin_e { + BTT_VOID, + BTT_BOOL, + BTT_CHAR, + BTT_SCHAR, + BTT_UCHAR, + BTT_SHORT, + BTT_SSHORT, + BTT_USHORT, + BTT_INT, + BTT_SINT, + BTT_UINT, + BTT_LONG, + BTT_SLONG, + BTT_ULONG, + BTT_LONGLONG, + BTT_SLONGLONG, + BTT_ULONGLONG, + BTT_INT128, + BTT_SINT128, + BTT_UINT128, + BTT_S8, + BTT_U8, + BTT_S16, + BTT_U16, + BTT_S32, + BTT_U32, + BTT_S64, + BTT_U64, +#define BTT_START_INT_EXT BTT_S8 +#define BTT_INT_EXTS "__int8_t", "__uint8_t", "__int16_t", "__uint16_t", "__int32_t", "__uint32_t", "__int64_t", "__uint64_t" + BTT_FLOAT, + BTT_CFLOAT, + BTT_IFLOAT, + BTT_DOUBLE, + BTT_CDOUBLE, + BTT_IDOUBLE, + BTT_LONGDOUBLE, + BTT_CLONGDOUBLE, + BTT_ILONGDOUBLE, + BTT_VA_LIST, + } builtin; +#define LAST_BUILTIN BTT_VA_LIST + struct type_s *typ; + struct { + struct type_s *typ; + size_t array_sz; // -1 for VLA + } array; + struct struct_s *st; + struct { + struct type_s *ret; + size_t nargs; // -1 for no specification + struct type_s **args; + int has_varargs; + } fun; + } val; + size_info_t szinfo; +} type_t; +void type_del(type_t *typ); +KHASH_MAP_DECLARE_STR(type_map, type_t*) +void type_map_del(khash_t(type_map) *map); + +int type_t_equal(type_t*, type_t*); + +typedef struct st_member_s { + string_t *name; // May be NULL + type_t *typ; + _Bool is_bitfield; + size_t bitfield_width; +} st_member_t; +typedef struct struct_s { + string_t *tag; + int is_defined; + size_t nrefs; + int is_struct; // 0 = union, 1 = struct + int has_incomplete; // 1 if the last element of the structure is a VLA or if an element of the union recursively contains a VLA + int explicit_simple; + size_t nmembers; + st_member_t *members; +} struct_t; +void st_member_del(st_member_t *member); +void struct_del(struct_t *st); +KHASH_MAP_DECLARE_STR(struct_map, struct_t*) + +type_t *type_new(void); // Create a new (complete) builtin type +type_t *type_new_ptr(type_t *target); // Create a new pointer type; doesn't increment the use counter of the target +// type_t *type_do_copy(type_t *ref); // Always duplicate ref; decrements the use counter +// type_t *type_do_copy_nodec(const type_t *ref); // Always duplicate ref; doesn't decrements the use counter +// type_t *type_maybe_copy(type_t *ref); // Only duplicate ref if it is used elsewhere; in that case, decrements the use counter +int type_copy_into(type_t *dest, const type_t *ref); // Copy ref into dest, keeping additional qualifiers and without changing any use counter + +struct_t *struct_new(int is_struct, string_t *tag); // Create a new struct + +// Try to merge some types with other types; this may delete ptr and increase a use counter in a type referenced by the table +KHASH_DECLARE(type_set, type_t*, char) +type_t *type_try_merge(type_t *ptr, khash_t(type_set) *set); + +extern const char *builtin2str[LAST_BUILTIN + 1]; +void type_print(type_t *typ); +void struct_print(const struct_t *st); + +typedef struct file_s { + khash_t(struct_map) *struct_map; + khash_t(type_map) *type_map; + khash_t(type_map) *enum_map; + khash_t(type_map) *decl_map; + type_t *builtins[LAST_BUILTIN + 1]; + khash_t(const_map) *const_map; + khash_t(type_set) *type_set; +} file_t; +file_t *file_new(void); +void file_del(file_t *f); + +extern const char *sym2str[LAST_SYM + 1]; +extern const char *kw2str[LAST_KEYWORD + 1]; +void preproc_token_print(const preproc_token_t *tok); +int preproc_token_isend(const preproc_token_t *tok); +void proc_token_print(const proc_token_t *tok); +int proc_token_iserror(const proc_token_t *tok); +int proc_token_isend(const proc_token_t *tok); + +KHASH_MAP_DECLARE_STR(str2kw, enum token_keyword_type_e) +extern khash_t(str2kw) *str2kw; +int init_str2kw(void); +void del_str2kw(void); + +#endif // LANG_H diff --git a/wrapperhelper/src/main.c b/wrapperhelper/src/main.c new file mode 100644 index 000000000..8a397b6b0 --- /dev/null +++ b/wrapperhelper/src/main.c @@ -0,0 +1,184 @@ +#include +#include +#include +#include +#include + +#include "generator.h" +#include "lang.h" +#include "parse.h" +#include "khash.h" + +static void help(char *arg0) { + printf("Usage: %s --help\n" + " %s [--prepare|--preproc|--proc] \n" + " %s \n" + "\n" + " --prepare Dump all preprocessor tokens (prepare phase)\n" + " --preproc Dump all processor tokens (preprocessor phase)\n" + " --proc Dump all typedefs, declarations and constants (processor phase)\n" + "\n" + " Parsing file\n" + " Reference file (example: wrappedlibc_private.h)\n" + " Output file\n", + arg0, arg0, arg0); +} + +enum main_state { + MAIN_RUN, + MAIN_PREPARE, + MAIN_PREPROC, + MAIN_PROC, +}; + +int main(int argc, char **argv) { + setbuf(stdout, NULL); + if (!setlocale(LC_NUMERIC, "C")) { + printf("Error: failed to set LC_NUMERIC to C\n"); + return 2; + } + + enum main_state ms; + int off; + + if ((argc == 2) && !strcmp(argv[1], "--help")) { + help(argv[0]); + return 0; + } else if (argc == 2) { + ms = MAIN_PROC; + off = 1; + } else if ((argc == 3) && !strcmp(argv[1], "--prepare")) { + ms = MAIN_PREPARE; + off = 2; + } else if ((argc == 3) && !strcmp(argv[1], "--preproc")) { + ms = MAIN_PREPROC; + off = 2; + } else if ((argc == 3) && !strcmp(argv[1], "--proc")) { + ms = MAIN_PROC; + off = 2; + } else if (argc == 4) { + ms = MAIN_RUN; + off = 1; + } else { + help(argv[0]); + return 2; + } + + if (!init_str2kw()) { + return 2; + } + + FILE *f = fopen(argv[off], "r"); + if (!f) { + err(2, "Error: failed to open %s", argv[off]); + return 2; + } + switch (ms) { + case MAIN_RUN: { + file_t *content = parse_file(argv[off], f); // Takes ownership of f + if (!content) { + printf("Error: failed to parse the file\n"); + del_str2kw(); + return 0; + } + + FILE *ref = fopen(argv[off + 1], "r"); + if (!ref) { + err(2, "Error: failed to open %s", argv[off + 1]); + del_str2kw(); + return 2; + } + VECTOR(requests) *reqs = requests_from_file(argv[off + 1], ref); + if (!reqs) { + file_del(content); + del_str2kw(); + return 2; + } + // vector_for(requests, req, reqs) request_print(req); + if (!solve_requests(reqs, content->decl_map)) { + printf("Warning: failed to solve all default requests\n"); + } + // vector_for(requests, req, reqs) request_print(req); + //vector_for(requests, req, reqs) request_print_check(req); + FILE *out = fopen(argv[off + 2], "w"); + if (!out) { + err(2, "Error: failed to open %s", argv[off + 1]); + file_del(content); + vector_del(requests, reqs); + del_str2kw(); + return 2; + } + output_from_requests(out, reqs); + fclose(out); + vector_del(requests, reqs); + file_del(content); + del_str2kw(); + return 0; } + case MAIN_PROC: { + file_t *content = parse_file(argv[off], f); // Takes ownership of f + if (!content) { + printf("Error: failed to parse the file\n"); + del_str2kw(); + return 0; + } + // print content + const char *name; + struct_t *st; + type_t *typ; + num_constant_t cst; + /* for (enum type_builtin_e i = 0; i < LAST_BUILTIN; ++i) { + printf("Builtin %u: %p, ", i, content->builtins[i]); + type_print(content->builtins[i]); + printf("\n"); + } */ + kh_foreach(content->struct_map, name, st, + printf("Struct: %s -> %p = ", name, st); + struct_print(st); + printf("\n") + ) + kh_foreach(content->type_map, name, typ, + printf("Typedef: %s -> %p = ", name, typ); + type_print(typ); + printf("\n") + ) + kh_foreach(content->enum_map, name, typ, + printf("Enum: %s -> %p = ", name, typ); + type_print(typ); + printf("\n") + ) + kh_foreach(content->const_map, name, cst, + printf("Constant: %s -> ", name); + switch (cst.typ) { + case NCT_FLOAT: printf("%ff", cst.val.f); break; + case NCT_DOUBLE: printf("%f", cst.val.d); break; + case NCT_LDOUBLE: printf("%Lfl", cst.val.l); break; + case NCT_INT32: printf("%d", cst.val.i32); break; + case NCT_UINT32: printf("%uu", cst.val.u32); break; + case NCT_INT64: printf("%ldll", cst.val.i64); break; + case NCT_UINT64: printf("%lullu", cst.val.u64); break; + } + printf("\n") + ) + kh_foreach(content->decl_map, name, typ, + printf("Declaration: %s -> %p = ", name, typ); + type_print(typ); + printf("\n") + ) + file_del(content); + del_str2kw(); + return 0; } + + case MAIN_PREPARE: + dump_prepare(argv[off], f); // Takes ownership of f + del_str2kw(); + return 0; + + case MAIN_PREPROC: + dump_preproc(argv[off], f); // Takes ownership of f + del_str2kw(); + return 0; + } + + printf(" Failed to run mode %u\n", ms); + return 2; +} diff --git a/wrapperhelper/src/parse.c b/wrapperhelper/src/parse.c new file mode 100644 index 000000000..cba0d9de6 --- /dev/null +++ b/wrapperhelper/src/parse.c @@ -0,0 +1,3164 @@ +#include "parse.h" + +#include +#include + +#include "cstring.h" +#include "khash.h" +#include "prepare.h" +#include "preproc.h" + +void dump_prepare(const char *filename, FILE *file) { + prepare_t *prep = prepare_new_file(file, filename); + if (!prep) { + printf("Failed to create the prepare structure\n"); + return; + } + while (1) { + preproc_token_t tok = pre_next_token(prep, 0); + preproc_token_print(&tok); + if (preproc_token_isend(&tok)) { + preproc_token_del(&tok); + prepare_del(prep); + return; + } + preproc_token_del(&tok); + } +} +void dump_preproc(const char *filename, FILE *file) { + char *dirname = strchr(filename, '/') ? strndup(filename, (size_t)(strrchr(filename, '/') - filename)) : NULL; + preproc_t *prep = preproc_new_file(file, dirname, filename); + if (!prep) { + printf("Failed to create the preproc structure\n"); + if (dirname) free(dirname); + return; + } + while (1) { + proc_token_t tok = proc_next_token(prep); + proc_token_print(&tok); + if (proc_token_isend(&tok)) { + proc_token_del(&tok); + preproc_del(prep); + return; + } + proc_token_del(&tok); + } +} + +enum decl_storage { + STORAGE_NONE, + STORAGE_TYPEDEF, + STORAGE_EXTERN, + STORAGE_STATIC, + STORAGE_TLS, + STORAGE_TLS_EXTERN, + STORAGE_TLS_STATIC, + STORAGE_AUTO, + STORAGE_REG, +}; +enum decl_spec { + SPEC_NONE, + SPEC_BUILTIN, + SPEC_COMPLEX, + SPEC_IMAGINARY, + SPEC_LONGCOMPLEX, + SPEC_LONGIMAGINARY, + SPEC_BUILTIN_NOINT, + SPEC_TYPE, +}; +VECTOR_DECLARE_STATIC(size_t, size_t) +VECTOR_IMPL_STATIC(size_t, (void)) + +#define VALIDATION_DECL 1 +#define VALIDATION_LAST_DECL 2 +#define VALIDATION_FUN 3 +// Assumes sizeof(void*) == sizeof(unsigned long) +static int validate_type(type_t *typ, type_t *(*builtins)[LAST_BUILTIN + 1]) { + if (typ->is_validated) return 1; + typ->is_validated = 1; + if (typ->is_restrict) { + if (typ->typ != TYPE_PTR) { + printf("Error: only pointers to object types may be restrict-qualified\n"); + return 0; + } + if (typ->val.typ->typ == TYPE_FUNCTION) { + printf("Error: only pointers to object types may be restrict-qualified\n"); + return 0; + } + } + if (typ->is_atomic) { + if ((typ->typ == TYPE_ARRAY) || (typ->typ == TYPE_FUNCTION)) { + printf("Error: array types and function types may not be atomic-qualified\n"); + return 0; + } + } + switch (typ->typ) { + case TYPE_BUILTIN: + typ->szinfo = (*builtins)[typ->val.builtin]->szinfo; + return 1; + case TYPE_ARRAY: + if (typ->val.array.typ->is_incomplete || (typ->val.array.typ->typ == TYPE_FUNCTION)) { + printf("Error: array types must point to complete object types\n"); + return 0; + } + if ((typ->val.array.typ->typ == TYPE_STRUCT_UNION) && typ->val.array.typ->val.st->has_incomplete) { + printf("Error: array types may not (inductively) point to structures which last element is incomplete\n"); + return 0; + } + if ((typ->is_atomic) || (typ->is_const) || (typ->is_restrict) || (typ->is_volatile)) { + // qualifier-type-list in array declaration is only allowed in function argument declaration under certain circumstances + printf("Error: array types may not be qualified\n"); + return 0; + } + if (!validate_type(typ->val.array.typ, builtins)) return 0; + if (typ->val.array.array_sz == (size_t)-1) { + typ->szinfo.size = 0; + typ->szinfo.align = (typ->val.array.typ->szinfo.align < 16) ? 16 : typ->val.array.typ->szinfo.align; + } else { + typ->szinfo.size = typ->val.array.array_sz * typ->val.array.typ->szinfo.size; + typ->szinfo.align = + ((typ->szinfo.size >= 16) && (typ->val.array.typ->szinfo.align < 16)) ? + 16 : + typ->val.array.typ->szinfo.align; + } + return 1; + case TYPE_PTR: + typ->szinfo.size = LONG_IS_32BITS ? 4 : 8; + typ->szinfo.align = LONG_IS_32BITS ? 4 : 8; + return validate_type(typ->val.typ, builtins); + case TYPE_FUNCTION: + if ((typ->val.fun.ret->typ == TYPE_FUNCTION) || (typ->val.fun.ret->typ == TYPE_ARRAY)) { + printf("Error: function types may not return function or array types\n"); + return 0; + } + if (typ->val.fun.nargs != (size_t)-1) { + for (size_t i = 0; i < typ->val.fun.nargs; ++i) { + // Adjust the argument if necessary + if (typ->val.fun.args[i]->typ == TYPE_ARRAY) { + // Adjustment to pointer + typ->val.fun.args[i]->typ = TYPE_PTR; + typ->val.fun.args[i]->val.typ = typ->val.fun.args[i]->val.array.typ; + } else if (typ->val.fun.args[i]->typ == TYPE_FUNCTION) { + // Adjustment to pointer + type_t *t2 = type_new_ptr(typ->val.fun.args[i]); + if (!t2) { + printf("Error: failed to adjust type of argument from function to pointer\n"); + return 0; + } + typ->val.fun.args[i] = t2; + } + if (!validate_type(typ->val.fun.args[i], builtins)) return 0; + } + } + typ->szinfo.size = 0; + typ->szinfo.align = 0; + return validate_type(typ->val.fun.ret, builtins); + case TYPE_STRUCT_UNION: { + if (!typ->val.st->is_defined) return typ->is_incomplete; + size_t max_align = 1, cur_sz = 0, cur_bit = 0; + for (size_t i = 0; i < typ->val.st->nmembers; ++i) { + // Adjust the argument if necessary + st_member_t *mem = &typ->val.st->members[i]; + if (mem->typ->typ == TYPE_FUNCTION) { + printf("Error: structures may not contain function members\n"); + return 0; + } + if (mem->typ->is_incomplete) { + if ((i != typ->val.st->nmembers - 1) || !typ->val.st->is_struct || (mem->typ->typ != TYPE_ARRAY)) { + // The last element of a structure may be a VLA + printf("Error: structures may not contain incomplete members\n"); + return 0; + } + typ->val.st->has_incomplete = 1; + } + if (!validate_type(mem->typ, builtins)) return 0; + if (!typ->val.st->is_struct && (mem->typ->typ == TYPE_STRUCT_UNION)) { + typ->val.st->has_incomplete |= mem->typ->val.st->has_incomplete; + } + if (mem->is_bitfield) { + if (!typ->val.st->is_struct) { + printf("Error: TODO: bitfield in union\n"); + return 0; + } + if (mem->typ->is_atomic) { + printf("Error: atomic bitfields are not supported\n"); + return 0; + } + if (mem->typ->typ != TYPE_BUILTIN) { + printf("Error: bitfields can only have a specific subset of types\n"); + return 0; + } + if ((mem->typ->val.builtin != BTT_BOOL) && (mem->typ->val.builtin != BTT_INT) + && (mem->typ->val.builtin != BTT_SINT) && (mem->typ->val.builtin != BTT_UINT)) { + printf("Error: bitfields can only have a specific subset of types\n"); + return 0; + } + if (!mem->name && (mem->typ->szinfo.align > max_align)) { + printf("Error: TODO: unnamed bitfield member with greater alignment (width=%zu)\n", mem->bitfield_width); + return 0; + } + if (mem->bitfield_width) { + if (mem->name && (max_align < mem->typ->szinfo.align)) max_align = mem->typ->szinfo.align; + size_t cur_block = cur_sz / mem->typ->szinfo.align; + size_t end_block = (cur_sz + (cur_bit + mem->bitfield_width - 1) / 8) / mem->typ->szinfo.align; + if (cur_block == end_block) { + cur_bit += mem->bitfield_width; + cur_sz += cur_bit / 8; + cur_bit %= 8; + } else { + cur_sz = ((cur_sz + mem->typ->szinfo.align - 1) & ~(mem->typ->szinfo.align - 1)) + (mem->bitfield_width / 8); + cur_bit = mem->bitfield_width % 8; + } + } else { + if (max_align < mem->typ->szinfo.align) max_align = mem->typ->szinfo.align; + cur_sz = ((cur_sz + mem->typ->szinfo.align - 1) & ~(mem->typ->szinfo.align - 1)) + mem->typ->szinfo.size; + printf("Error: TODO: unnamed zero-width bitfield member\n"); + return 0; + } + } else { + if (max_align < mem->typ->szinfo.align) max_align = mem->typ->szinfo.align; + if (typ->val.st->is_struct) { + if (cur_bit) { + cur_bit = 0; + ++cur_sz; + } + cur_sz = ((cur_sz + mem->typ->szinfo.align - 1) & ~(mem->typ->szinfo.align - 1)) + mem->typ->szinfo.size; + } else { + if (cur_sz < mem->typ->szinfo.size) cur_sz = mem->typ->szinfo.size; + } + } + } + if (cur_bit) { + cur_bit = 0; + ++cur_sz; + } + typ->szinfo.align = max_align; + typ->szinfo.size = (cur_sz + max_align - 1) & ~(max_align - 1); + return 1; } + case TYPE_ENUM: + if (typ->val.typ->typ != TYPE_BUILTIN) return 0; + typ->szinfo = typ->val.typ->szinfo; + return 1; + } + return 0; +} +static int validate_storage_type(enum decl_storage storage, type_t *(*builtins)[LAST_BUILTIN + 1], type_t *typ, enum token_sym_type_e sym) { + // We may still do adjustments here + if (!validate_type(typ, builtins)) return 0; + if (typ->typ == TYPE_FUNCTION) { + if ((storage == STORAGE_TLS) || (storage == STORAGE_TLS_EXTERN) || (storage == STORAGE_TLS_STATIC)) { + printf("Error: functions cannot be thread local\n"); + return 0; + } + if ((sym == SYM_COMMA) || (sym == SYM_RPAREN) || (sym == SYM_SEMICOLON)) { + return (sym == SYM_SEMICOLON) ? VALIDATION_LAST_DECL : VALIDATION_DECL; + } else if (sym == SYM_LBRACKET) { + return VALIDATION_FUN; + } + } else { + if ((sym == SYM_COMMA) || (sym == SYM_RPAREN) || (sym == SYM_SEMICOLON) || ((storage != STORAGE_TYPEDEF) && (sym == SYM_EQ))) { + return (sym == SYM_SEMICOLON) ? VALIDATION_LAST_DECL : VALIDATION_DECL; + } + } + printf("TODO: validate_storage_type on storage %u, sym %s (%u), valid type ", storage, sym2str[sym], sym); + type_print(typ); + printf("\n"); + return 0; +} + +static void promote_csts(num_constant_t *v1, num_constant_t *v2) { + switch (v1->typ) { + case NCT_FLOAT: + switch (v2->typ) { + case NCT_FLOAT: break; + case NCT_DOUBLE: v1->typ = NCT_DOUBLE; v1->val.d = v1->val.f; break; + case NCT_LDOUBLE: v1->typ = NCT_LDOUBLE; v1->val.l = v1->val.f; break; + case NCT_INT32: v2->typ = NCT_FLOAT; v2->val.f = (float)v2->val.i32; break; + case NCT_UINT32: v2->typ = NCT_FLOAT; v2->val.f = (float)v2->val.u32; break; + case NCT_INT64: v2->typ = NCT_FLOAT; v2->val.f = (float)v2->val.i64; break; + case NCT_UINT64: v2->typ = NCT_FLOAT; v2->val.f = (float)v2->val.u64; break; + } + break; + case NCT_DOUBLE: + switch (v2->typ) { + case NCT_FLOAT: v2->typ = NCT_DOUBLE; v2->val.d = v2->val.f; break; + case NCT_DOUBLE: break; + case NCT_LDOUBLE: v1->typ = NCT_LDOUBLE; v1->val.l = v1->val.d; break; + case NCT_INT32: v2->typ = NCT_DOUBLE; v2->val.d = (double)v2->val.i32; break; + case NCT_UINT32: v2->typ = NCT_DOUBLE; v2->val.d = (double)v2->val.u32; break; + case NCT_INT64: v2->typ = NCT_DOUBLE; v2->val.d = (double)v2->val.i64; break; + case NCT_UINT64: v2->typ = NCT_DOUBLE; v2->val.d = (double)v2->val.u64; break; + } + break; + case NCT_LDOUBLE: + switch (v2->typ) { + case NCT_FLOAT: v2->typ = NCT_LDOUBLE; v2->val.l = v2->val.f; break; + case NCT_DOUBLE: v2->typ = NCT_LDOUBLE; v2->val.l = v2->val.d; break; + case NCT_LDOUBLE: break; + case NCT_INT32: v2->typ = NCT_LDOUBLE; v2->val.l = (long double)v2->val.i32; break; + case NCT_UINT32: v2->typ = NCT_LDOUBLE; v2->val.l = (long double)v2->val.u32; break; + case NCT_INT64: v2->typ = NCT_LDOUBLE; v2->val.l = (long double)v2->val.i64; break; + case NCT_UINT64: v2->typ = NCT_LDOUBLE; v2->val.l = (long double)v2->val.u64; break; + } + break; + case NCT_INT32: + switch (v2->typ) { + case NCT_FLOAT: v1->typ = NCT_FLOAT; v1->val.f = (float)v1->val.i32; break; + case NCT_DOUBLE: v1->typ = NCT_DOUBLE; v1->val.d = (double)v1->val.i32; break; + case NCT_LDOUBLE: v1->typ = NCT_LDOUBLE; v1->val.l = (long double)v1->val.i32; break; + case NCT_INT32: break; + case NCT_UINT32: v1->typ = NCT_UINT32; v1->val.u32 = (uint32_t)v1->val.i32; break; + case NCT_INT64: v1->typ = NCT_INT64; v1->val.i64 = v1->val.i32; break; + case NCT_UINT64: v1->typ = NCT_UINT64; v1->val.u64 = (uint64_t)v1->val.i32; break; + } + break; + case NCT_UINT32: + switch (v2->typ) { + case NCT_FLOAT: v1->typ = NCT_FLOAT; v1->val.f = (float)v1->val.u32; break; + case NCT_DOUBLE: v1->typ = NCT_DOUBLE; v1->val.d = (double)v1->val.u32; break; + case NCT_LDOUBLE: v1->typ = NCT_LDOUBLE; v1->val.l = (long double)v1->val.u32; break; + case NCT_INT32: v2->typ = NCT_UINT32; v2->val.u32 = (uint32_t)v2->val.i32; break; + case NCT_UINT32: break; + case NCT_INT64: v1->typ = NCT_INT64; v1->val.i64 = v1->val.u32; break; + case NCT_UINT64: v1->typ = NCT_UINT64; v1->val.u64 = v1->val.u32; break; + } + break; + case NCT_INT64: + switch (v2->typ) { + case NCT_FLOAT: v1->typ = NCT_FLOAT; v1->val.f = (float)v1->val.i64; break; + case NCT_DOUBLE: v1->typ = NCT_DOUBLE; v1->val.d = (double)v1->val.i64; break; + case NCT_LDOUBLE: v1->typ = NCT_LDOUBLE; v1->val.l = (long double)v1->val.i64; break; + case NCT_INT32: v2->typ = NCT_INT64; v2->val.i64 = v2->val.i32; break; + case NCT_UINT32: v2->typ = NCT_INT64; v2->val.i64 = v2->val.u32; break; + case NCT_INT64: break; + case NCT_UINT64: v1->typ = NCT_UINT64; v1->val.u64 = (uint64_t)v1->val.i64; break; + } + break; + case NCT_UINT64: + switch (v2->typ) { + case NCT_FLOAT: v1->typ = NCT_FLOAT; v1->val.f = (float)v1->val.u64; break; + case NCT_DOUBLE: v1->typ = NCT_DOUBLE; v1->val.d = (double)v1->val.u64; break; + case NCT_LDOUBLE: v1->typ = NCT_LDOUBLE; v1->val.l = (long double)v1->val.u64; break; + case NCT_INT32: v2->typ = NCT_UINT64; v2->val.u64 = (uint64_t)v2->val.i32; break; + case NCT_UINT32: v2->typ = NCT_UINT64; v2->val.u64 = v2->val.u32; break; + case NCT_INT64: v2->typ = NCT_UINT64; v2->val.u64 = (uint64_t)v2->val.i64; break; + case NCT_UINT64: break; + } + break; + } +} + +VECTOR_DECLARE_STATIC(exprs, expr_t*) +#define expr_del_ptr(p) expr_del(*(p)) +VECTOR_IMPL_STATIC(exprs, expr_del_ptr) +#undef expr_del_ptr +struct expr_partial_op { + enum expr_partial_op_e { + EPO_LPAREN, + EPO_SIZEOF, + EPO_CAST, + EPO_FUNCALL, + EPO_UNARY, + EPO_BINARY_ARG, + EPO_BINARY_ARG_SYM, + EPO_TERNARY_ARG1, + EPO_TERNARY_ARG2, + } typ; + int once_done_want_level; + int once_done_is_level; + union { + char c; // Empty destructor + type_t *typ; + struct { + expr_t *f; + VECTOR(exprs) *exprs; + } funcall; + enum unary_op_e unop; + struct { + enum binary_op_e op; + expr_t *e1; + enum token_sym_type_e last_sym; + } binop; + struct { + enum ternary_op_e op; + expr_t *e1; + expr_t *e2; + int once_done2_want_level; + enum token_sym_type_e arg23_sep; + } ternop; + } val; +}; +void expr_partial_op_del(struct expr_partial_op *epo) { + switch (epo->typ) { + case EPO_LPAREN: + case EPO_SIZEOF: + return; + case EPO_CAST: + type_del(epo->val.typ); + return; + case EPO_FUNCALL: + expr_del(epo->val.funcall.f); + vector_del(exprs, epo->val.funcall.exprs); + return; + case EPO_UNARY: + return; + case EPO_BINARY_ARG: + case EPO_BINARY_ARG_SYM: + expr_del(epo->val.binop.e1); + return; + case EPO_TERNARY_ARG1: + expr_del(epo->val.ternop.e1); + return; + case EPO_TERNARY_ARG2: + expr_del(epo->val.ternop.e1); + expr_del(epo->val.ternop.e2); + return; + } +} +VECTOR_DECLARE_STATIC(expr_pops, struct expr_partial_op) +VECTOR_IMPL_STATIC(expr_pops, expr_partial_op_del) + +VECTOR_DECLARE_STATIC(types, type_t*) +#define type_ptr_del(t) type_del(*(t)) +VECTOR_IMPL_STATIC(types, type_ptr_del) +#undef type_ptr_del +VECTOR_DECLARE_STATIC(st_members, st_member_t) +VECTOR_IMPL_STATIC(st_members, st_member_del) + +struct parse_declarator_dest_s { + file_t *f; // is_init, is_list + struct { + khash_t(struct_map) *struct_map; + khash_t(type_map) *type_map; + khash_t(type_map) *enum_map; + khash_t(type_set) *type_set; + type_t *(*builtins)[LAST_BUILTIN + 1]; + khash_t(const_map) *const_map; + type_t *dest; + } argt; // !is_init, !is_list + struct { + khash_t(struct_map) *struct_map; + khash_t(type_map) *type_map; + khash_t(type_map) *enum_map; + khash_t(type_set) *type_set; + type_t *(*builtins)[LAST_BUILTIN + 1]; + khash_t(const_map) *const_map; + VECTOR(st_members) *dest; + } structms; // !is_init, is_list +}; +#define PDECL_STRUCT_MAP ((is_init && is_list) ? dest->f->struct_map : (!is_init && is_list) ? dest->structms.struct_map : dest->argt.struct_map) +#define PDECL_TYPE_MAP ((is_init && is_list) ? dest->f->type_map : (!is_init && is_list) ? dest->structms.type_map : dest->argt.type_map) +#define PDECL_ENUM_MAP ((is_init && is_list) ? dest->f->enum_map : (!is_init && is_list) ? dest->structms.enum_map : dest->argt.enum_map) +#define PDECL_TYPE_SET ((is_init && is_list) ? dest->f->type_set : (!is_init && is_list) ? dest->structms.type_set : dest->argt.type_set) +#define PDECL_BUILTINS ((is_init && is_list) ? &dest->f->builtins : (!is_init && is_list) ? dest->structms.builtins : dest->argt.builtins) +#define PDECL_CONST_MAP ((is_init && is_list) ? dest->f->const_map : (!is_init && is_list) ? dest->structms.const_map : dest->argt.const_map) +static int parse_declarator(struct parse_declarator_dest_s *dest, preproc_t *prep, proc_token_t *tok, enum decl_storage storage, type_t *base_type, + int is_init, int is_list, int allow_decl, int allow_abstract); + +// declaration-specifier with storage != NULL +// specifier-qualifier-list + static_assert-declaration with storage == NULL +static int parse_declaration_specifier(khash_t(struct_map) *struct_map, khash_t(type_map) *type_map, khash_t(type_map) *enum_map, + type_t *(*builtins)[LAST_BUILTIN + 1], khash_t(const_map) *const_map, + khash_t(type_set) *type_set, preproc_t *prep, proc_token_t *tok, enum decl_storage *storage, enum decl_spec *spec, type_t *typ); + + +void expr_print(expr_t *e) { + switch (e->typ) { + case ETY_VAR: printf("%s", string_content(e->val.var)); break; + case ETY_CONST: + switch (e->val.cst.typ) { + case NCT_FLOAT: printf("%ff", e->val.cst.val.f); break; + case NCT_DOUBLE: printf("%f", e->val.cst.val.d); break; + case NCT_LDOUBLE: printf("%Lfl", e->val.cst.val.l); break; + case NCT_INT32: printf("%d", e->val.cst.val.i32); break; + case NCT_UINT32: printf("%uu", e->val.cst.val.u32); break; + case NCT_INT64: printf("%ldll", e->val.cst.val.i64); break; + case NCT_UINT64: printf("%lullu", e->val.cst.val.u64); break; + } + break; + case ETY_CALL: printf("(call)"); break; + case ETY_ACCESS: printf("(.)"); break; + case ETY_PTRACCESS: printf("(->)"); break; + case ETY_UNARY: + switch (e->val.unary.typ) { + case UOT_POSTINCR: printf("(++)"); expr_print(e->val.unary.e); break; + case UOT_POSTDECR: printf("(--)"); expr_print(e->val.unary.e); break; + case UOT_PREINCR: printf("++"); expr_print(e->val.unary.e); break; + case UOT_PREDECR: printf("--"); expr_print(e->val.unary.e); break; + case UOT_REF: printf("&"); expr_print(e->val.unary.e); break; + case UOT_POS: printf("(+)"); expr_print(e->val.unary.e); break; + case UOT_NEG: printf("(-)"); expr_print(e->val.unary.e); break; + case UOT_DEREF: printf("(*)"); expr_print(e->val.unary.e); break; + case UOT_ANOT: printf("~"); expr_print(e->val.unary.e); break; + case UOT_BNOT: printf("!"); expr_print(e->val.unary.e); break; + } + break; + case ETY_BINARY: { + printf("("); + expr_print(e->val.binary.e1); + switch (e->val.binary.typ) { + case BOT_ADD: printf(") + ("); break; + case BOT_SUB: printf(") - ("); break; + case BOT_MUL: printf(") * ("); break; + case BOT_DIV: printf(") / ("); break; + case BOT_MOD: printf(") %% ("); break; + case BOT_LSH: printf(") << ("); break; + case BOT_RSH: printf(") >> ("); break; + case BOT_LT: printf(") < ("); break; + case BOT_GT: printf(") > ("); break; + case BOT_LE: printf(") <= ("); break; + case BOT_GE: printf(") >= ("); break; + case BOT_EQ: printf(") == ("); break; + case BOT_NE: printf(") != ("); break; + case BOT_AAND: printf(") & ("); break; + case BOT_AXOR: printf(") ^ ("); break; + case BOT_AOR: printf(") | ("); break; + case BOT_BAND: printf(") && ("); break; + case BOT_BOR: printf(") || ("); break; + case BOT_ASSGN_EQ: printf(") = ("); break; + case BOT_ASSGN_ADD: printf(") += ("); break; + case BOT_ASSGN_SUB: printf(") -= ("); break; + case BOT_ASSGN_MUL: printf(") *= ("); break; + case BOT_ASSGN_DIV: printf(") /= ("); break; + case BOT_ASSGN_MOD: printf(") %%= ("); break; + case BOT_ASSGN_LSH: printf(") <<= ("); break; + case BOT_ASSGN_RSH: printf(") >>= ("); break; + case BOT_ASSGN_AAND: printf(") &= ("); break; + case BOT_ASSGN_AXOR: printf(") ^= ("); break; + case BOT_ASSGN_AOR: printf(") |= ("); break; + case BOT_COMMA: printf("), ("); break; + case BOT_ARRAY: printf(")["); break; + } + expr_print(e->val.binary.e2); + printf(e->val.binary.typ == BOT_ARRAY ? "]" : ")"); + break; } + case ETY_TERNARY: { + printf("("); + expr_print(e->val.ternary.e1); + switch (e->val.ternary.typ) { + case TOT_COND: printf(") ? "); break; + } + expr_print(e->val.ternary.e2); + switch (e->val.ternary.typ) { + case TOT_COND: printf(" : ("); break; + } + expr_print(e->val.ternary.e3); + printf(")"); + break; } + case ETY_CAST: printf("("); type_print(e->val.cast.typ); printf(")"); expr_print(e->val.cast.e); break; + } +} + + +static int is_type_spec_qual_kw(enum token_keyword_type_e kw) { + return + (kw == KW_ATOMIC) || + (kw == KW_BOOL) || + (kw == KW_CHAR) || + (kw == KW_COMPLEX) || + (kw == KW_CONST) || + (kw == KW_DOUBLE) || + (kw == KW_ENUM) || + (kw == KW_FLOAT) || + (kw == KW_IMAGINARY) || + (kw == KW_INT) || + (kw == KW_INT128) || + (kw == KW_LONG) || + (kw == KW_RESTRICT) || + (kw == KW_SHORT) || + (kw == KW_SIGNED) || + (kw == KW_STRUCT) || + (kw == KW_UNION) || + (kw == KW_UNSIGNED) || + (kw == KW_VOID) || + (kw == KW_VOLATILE) || + 0; +} +#define IS_BEGIN_TYPE_NAME \ + (((tok->tokt == PTOK_KEYWORD) && is_type_spec_qual_kw(tok->tokv.kw)) || \ + ((tok->tokt == PTOK_IDENT) && ((it = kh_get(type_map, type_map, string_content(tok->tokv.str))) != kh_end(type_map)))) +static int parse_type_name(khash_t(struct_map) *struct_map, khash_t(type_map) *type_map, khash_t(type_map) *enum_map, + type_t *(*builtins)[LAST_BUILTIN + 1], khash_t(const_map) *const_map, + khash_t(type_set) *type_set, preproc_t *prep, proc_token_t *tok, enum token_sym_type_e end_sym, type_t **typ) { + enum decl_spec spec = SPEC_NONE; + if (!parse_declaration_specifier(struct_map, type_map, enum_map, builtins, const_map, type_set, prep, tok, NULL, &spec, *typ)) { + type_del(*typ); + goto failed; + } + *typ = type_try_merge(*typ, type_set); + if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == end_sym)) { + return validate_type(*typ, builtins); + } + struct parse_declarator_dest_s dest2; + dest2.argt.dest = NULL; + dest2.argt.struct_map = struct_map; + dest2.argt.type_map = type_map; + dest2.argt.enum_map = enum_map; + dest2.argt.type_set = type_set; + dest2.argt.builtins = builtins; + dest2.argt.const_map = const_map; + if (!parse_declarator(&dest2, prep, tok, STORAGE_NONE, *typ, 0, 0, 0, 1)) { + // Token is deleted + type_del(*typ); + goto failed; + } + type_del(*typ); + if (!dest2.argt.dest) { + printf("Internal error: argument type is NULL\n"); + // Empty destructor + goto failed; + } + if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != end_sym)) { + type_del(dest2.argt.dest); + proc_token_del(tok); + goto failed; + } + *typ = dest2.argt.dest; + *typ = type_try_merge(*typ, type_set); + return validate_type(*typ, builtins); + +failed: + return 0; +} + +static expr_t *parse_expression(khash_t(struct_map) *struct_map, khash_t(type_map) *type_map, khash_t(type_map) *enum_map, + type_t *(*builtins)[LAST_BUILTIN + 1], khash_t(const_map) *const_map, + khash_t(type_set) *type_set, preproc_t *prep, proc_token_t *tok, int expr_level) { + // Note that expr_level >= 1; expr_level = 0 doesn't appear in the grammar + if ((expr_level < 1) || (expr_level > 16)) { + printf("Internal error: invalid expression level %d\n", expr_level); + return NULL; + } + + VECTOR(expr_pops) *op_stack = vector_new(expr_pops); + if (!op_stack) { + printf("Error: failed to create operation stack"); + proc_token_del(tok); + return NULL; + } + + int has_level; + expr_t *e; +pushed_expr: + has_level = -1; + e = NULL; + +expr_new_token: + // printf("expr_new_token with level %d / %d\n", has_level, expr_level); + // proc_token_print(tok); + if (tok->tokt == PTOK_IDENT) { + if (has_level != -1) { + printf("Error: invalid expression: unexpected identifier '%s'\n", string_content(tok->tokv.str)); + string_del(tok->tokv.str); + goto failed; + } + has_level = 0; + e = malloc(sizeof *e); + if (!e) { + printf("Error: failed to create new expression atom\n"); + string_del(tok->tokv.str); + goto failed; + } + e->typ = ETY_VAR; + e->val.var = tok->tokv.str; + *tok = proc_next_token(prep); + goto expr_new_token; + } else if (tok->tokt == PTOK_NUM) { + if (has_level != -1) { + printf("Error: invalid expression: unexpected number '%s'\n", string_content(tok->tokv.str)); + string_del(tok->tokv.str); + goto failed; + } + has_level = 0; + e = malloc(sizeof *e); + if (!e) { + printf("Error: failed to create new expression atom\n"); + string_del(tok->tokv.str); + goto failed; + } + e->typ = ETY_CONST; + if (!num_constant_convert(tok->tokv.str, &e->val.cst)) { + string_del(tok->tokv.str); + goto failed; + } + string_del(tok->tokv.str); + *tok = proc_next_token(prep); + goto expr_new_token; + } + +#define UNOP(toksym, opt, main_lv, right_lv) \ + if ((expr_level >= main_lv) && (tok->tokt == PTOK_SYM) && (tok->tokv.sym == toksym)) { \ + if (has_level == -1) { \ + struct expr_partial_op pop = { \ + .once_done_want_level = expr_level, \ + .once_done_is_level = main_lv, \ + .typ = EPO_UNARY, \ + .val.unop = opt \ + }; \ + if (!vector_push(expr_pops, op_stack, pop)) { \ + printf("Error: failed to add partial operation to operation stack\n"); \ + /* Empty destructor */ \ + goto failed; \ + } \ + expr_level = right_lv; \ + *tok = proc_next_token(prep); \ + goto pushed_expr; \ + } \ + } +#define BINOP(toksym, opt, main_lv, left_lv, right_lv) \ + if ((expr_level >= main_lv) && (tok->tokt == PTOK_SYM) && (tok->tokv.sym == toksym)) { \ + if ((has_level != -1) && (has_level <= left_lv)) { \ + struct expr_partial_op pop = { \ + .once_done_want_level = expr_level, \ + .once_done_is_level = main_lv, \ + .typ = EPO_BINARY_ARG, \ + .val.binop = {.op = opt, .e1 = e} \ + }; \ + if (!vector_push(expr_pops, op_stack, pop)) { \ + printf("Error: failed to add partial operation to operation stack\n"); \ + /* Empty destructor */ \ + goto failed; \ + } \ + expr_level = right_lv; \ + *tok = proc_next_token(prep); \ + goto pushed_expr; \ + } \ + } + if ((expr_level >= 1) && (tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_PLUSPLUS)) { + if ((has_level != -1) && (has_level <= 1)) { + has_level = 1; + expr_t *new_e = malloc(sizeof *new_e); + if (!new_e) { + printf("Error: failed to create new expression atom\n"); + string_del(tok->tokv.str); + goto failed; + } + new_e->typ = ETY_UNARY; + new_e->val.unary.typ = UOT_POSTINCR; + new_e->val.unary.e = e; + e = new_e; + *tok = proc_next_token(prep); + goto expr_new_token; + } + } + if ((expr_level >= 1) && (tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_DASHDASH)) { + if ((has_level != -1) && (has_level <= 1)) { + has_level = 1; + expr_t *new_e = malloc(sizeof *new_e); + if (!new_e) { + printf("Error: failed to create new expression atom\n"); + string_del(tok->tokv.str); + goto failed; + } + new_e->typ = ETY_UNARY; + new_e->val.unary.typ = UOT_POSTDECR; + new_e->val.unary.e = e; + e = new_e; + *tok = proc_next_token(prep); + goto expr_new_token; + } + } + if ((expr_level >= 1) && (tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_LSQBRACKET)) { + if ((has_level != -1) && (has_level <= 1)) { + struct expr_partial_op pop = { + .once_done_want_level = expr_level, + .once_done_is_level = 1, + .typ = EPO_BINARY_ARG_SYM, + .val.binop = {.last_sym = SYM_RSQBRACKET, .op = BOT_ARRAY, .e1 = e} + }; + if (!vector_push(expr_pops, op_stack, pop)) { + printf("Error: failed to add partial operation to operation stack\n"); + /* Empty destructor */ + goto failed; + } + expr_level = 16; + *tok = proc_next_token(prep); + goto pushed_expr; + } + } + if ((expr_level >= 1) && (tok->tokt == PTOK_SYM) && ((tok->tokv.sym == SYM_DOT) || (tok->tokv.sym == SYM_DASHGT))) { + if ((has_level != -1) && (has_level <= 1)) { + has_level = 1; + int is_ptr = tok->tokv.sym == SYM_DASHGT; + + *tok = proc_next_token(prep); + if (tok->tokt != PTOK_IDENT) { + printf("Error: invalid expression: unexpected token after access symbol\n"); + proc_token_del(tok); + goto failed; + } + expr_t *new_e = malloc(sizeof *new_e); + if (!new_e) { + printf("Error: failed to create new expression atom\n"); + string_del(tok->tokv.str); + goto failed; + } + new_e->typ = is_ptr ? ETY_PTRACCESS : ETY_ACCESS; + new_e->val.access.val = e; + new_e->val.access.member = tok->tokv.str; + e = new_e; + *tok = proc_next_token(prep); + goto expr_new_token; + } + } + UNOP(SYM_PLUSPLUS, UOT_PREINCR, 2, 2) + UNOP(SYM_DASHDASH, UOT_PREDECR, 2, 2) + UNOP(SYM_AMP, UOT_REF, 2, 3) + UNOP(SYM_PLUS, UOT_POS, 2, 3) + UNOP(SYM_DASH, UOT_NEG, 2, 3) + UNOP(SYM_STAR, UOT_DEREF, 2, 3) + UNOP(SYM_TILDE, UOT_ANOT, 2, 3) + UNOP(SYM_EXCL, UOT_BNOT, 2, 3) + BINOP(SYM_STAR, BOT_MUL, 4, 4, 3) + BINOP(SYM_SLASH, BOT_DIV, 4, 4, 3) + BINOP(SYM_PERCENT, BOT_MOD, 4, 4, 3) + BINOP(SYM_PLUS, BOT_ADD, 5, 5, 4) + BINOP(SYM_DASH, BOT_SUB, 5, 5, 4) + BINOP(SYM_LTLT, BOT_LSH, 6, 6, 5) + BINOP(SYM_GTGT, BOT_RSH, 6, 6, 5) + BINOP(SYM_LT, BOT_LT, 7, 7, 6) + BINOP(SYM_GT, BOT_GT, 7, 7, 6) + BINOP(SYM_LTEQ, BOT_LE, 7, 7, 6) + BINOP(SYM_GTEQ, BOT_GE, 7, 7, 6) + BINOP(SYM_EQEQ, BOT_EQ, 8, 8, 7) + BINOP(SYM_EXCLEQ, BOT_NE, 8, 8, 7) + BINOP(SYM_AMP, BOT_AAND, 9, 9, 8) + BINOP(SYM_HAT, BOT_AXOR, 10, 10, 9) + BINOP(SYM_PIPE, BOT_AOR, 11, 11, 10) + BINOP(SYM_AMPAMP, BOT_BAND, 12, 12, 11) + BINOP(SYM_PIPEPIPE, BOT_BOR, 13, 13, 12) + if ((expr_level >= 14) && (tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_QUESTION)) { + if ((has_level != -1) && (has_level <= 13)) { + struct expr_partial_op pop = { + .once_done_want_level = expr_level, + .once_done_is_level = 14, + .typ = EPO_TERNARY_ARG1, + .val.ternop = {.arg23_sep = SYM_COLON, .once_done2_want_level = 14, .op = TOT_COND, .e1 = e} + }; + if (!vector_push(expr_pops, op_stack, pop)) { + printf("Error: failed to add partial operation to operation stack\n"); + // Empty destructor + goto failed; + } + expr_level = 16; + *tok = proc_next_token(prep); + goto pushed_expr; + } + } + BINOP(SYM_EQ, BOT_ASSGN_EQ, 15, 2, 15) + BINOP(SYM_STAREQ, BOT_ASSGN_MUL, 15, 2, 15) + BINOP(SYM_SLASHEQ, BOT_ASSGN_DIV, 15, 2, 15) + BINOP(SYM_PERCENTEQ, BOT_ASSGN_MOD, 15, 2, 15) + BINOP(SYM_PLUSEQ, BOT_ASSGN_ADD, 15, 2, 15) + BINOP(SYM_DASHEQ, BOT_ASSGN_SUB, 15, 2, 15) + BINOP(SYM_LTLTEQ, BOT_ASSGN_LSH, 15, 2, 15) + BINOP(SYM_GTGTEQ, BOT_ASSGN_RSH, 15, 2, 15) + BINOP(SYM_AMPEQ, BOT_ASSGN_AAND, 15, 2, 15) + BINOP(SYM_HATEQ, BOT_ASSGN_AXOR, 15, 2, 15) + BINOP(SYM_PIPEEQ, BOT_ASSGN_AOR, 15, 2, 15) + BINOP(SYM_COMMA, BOT_COMMA, 16, 16, 15) + + // expr2 ::= sizeof expr2 + // which includes expr2 ::= sizeof ( expr16 ) + // expr2 ::= sizeof ( type-name ) + if ((has_level == -1) && (expr_level >= 2) && (tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw = KW_SIZEOF)) { + *tok = proc_next_token(prep); + if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_LPAREN)) { + struct expr_partial_op pop = { + .once_done_want_level = expr_level, + .once_done_is_level = 2, + .typ = EPO_SIZEOF, + .val.c = '\0' + }; + if (!vector_push(expr_pops, op_stack, pop)) { + printf("Error: failed to add partial operation to operation stack\n"); + proc_token_del(tok); + goto failed; + } + expr_level = 2; + goto pushed_expr; + } + // Empty destructor + *tok = proc_next_token(prep); + khiter_t it; + if (IS_BEGIN_TYPE_NAME) { + type_t *typ = type_new(); + if (!typ) { + printf("Error: failed to create new type info structure\n"); + proc_token_del(tok); + goto failed; + } + if (!parse_type_name(struct_map, type_map, enum_map, builtins, const_map, type_set, prep, tok, SYM_RPAREN, &typ)) { + proc_token_del(tok); + goto failed; + } + if (!typ->is_validated || typ->is_incomplete) { + printf("Error: cannot get the size of an incomplete type\n"); + proc_token_del(tok); + goto failed; + } + e = malloc(sizeof *e); + if (!e) { + printf("Error: failed to create new expression atom\n"); + type_del(typ); + proc_token_del(tok); + goto failed; + } + e->typ = ETY_CONST; + e->val.cst.typ = NCT_UINT64; + e->val.cst.val.u64 = typ->szinfo.size; + has_level = 2; + type_del(typ); + if (!e->val.cst.val.u64) { + proc_token_del(tok); + goto failed; + } + // Empty destructor + *tok = proc_next_token(prep); + goto expr_new_token; + } else { + struct expr_partial_op pop = { + .once_done_want_level = expr_level, + .once_done_is_level = 2, + .typ = EPO_SIZEOF, + .val.c = 0 + }; + if (!vector_push(expr_pops, op_stack, pop)) { + printf("Error: failed to add partial operation to operation stack\n"); + proc_token_del(tok); + goto failed; + } + pop = (struct expr_partial_op){ + .once_done_want_level = 2, + .once_done_is_level = 0, + .typ = EPO_LPAREN, + .val.c = 0 + }; + if (!vector_push(expr_pops, op_stack, pop)) { + printf("Error: failed to add partial operation to operation stack\n"); + proc_token_del(tok); + goto failed; + } + expr_level = 16; + goto pushed_expr; + } + } + + // expr0 ::= ( expr16 ) + // expr1 ::= expr1 ( ) + // expr1 ::= expr1 ( expr15 {, expr15}* ) + // expr1 ::= ( type-name ) { initializer-list ,? } + // expr3 ::= ( type-name ) expr3 + if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_LPAREN)) { + if ((has_level != -1) && (expr_level >= 1) && (has_level <= 1)) { + // Empty destructor + *tok = proc_next_token(prep); + if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_RPAREN)) { + expr_t *new_e = malloc(sizeof *new_e); + new_e = malloc(sizeof *new_e); + if (!new_e) { + printf("Error: failed to create new expression atom\n"); + // Empty destructor + goto failed; + } + new_e->typ = ETY_CALL; + new_e->val.call.fun = e; + new_e->val.call.nargs = 0; + new_e->val.call.args = NULL; + e = new_e; + *tok = proc_next_token(prep); + goto expr_new_token; + } + // We want a function call with at least one argument + VECTOR(exprs) *exprs = vector_new_cap(exprs, 1); + if (!exprs) { + printf("Error: failed to add partial operation to operation stack\n"); + proc_token_del(tok); + goto failed; + } + struct expr_partial_op pop = { + .once_done_want_level = expr_level, + .once_done_is_level = 1, + .typ = EPO_FUNCALL, + .val.funcall = {.f = e, .exprs = exprs} + }; + if (!vector_push(expr_pops, op_stack, pop)) { + printf("Error: failed to add partial operation to operation stack\n"); + vector_del(exprs, exprs); + proc_token_del(tok); + goto failed; + } + expr_level = 15; + goto pushed_expr; + } + if (has_level == -1) { + *tok = proc_next_token(prep); + khiter_t it; + if ((expr_level >= 1) && IS_BEGIN_TYPE_NAME) { + type_t *typ = type_new(); + if (!typ) { + printf("Error: failed to create new type info structure\n"); + proc_token_del(tok); + goto failed; + } + if (!parse_type_name(struct_map, type_map, enum_map, builtins, const_map, type_set, prep, tok, SYM_RPAREN, &typ)) { + type_del(typ); + proc_token_del(tok); + goto failed; + } + if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_LBRACKET)) { + printf("Error: TODO: initializer-list\n"); + type_del(typ); + proc_token_del(tok); + goto failed; + } else if (expr_level < 3) { + printf("Error: cast expression (level 3) but the expression level is at most 2\n"); + type_del(typ); + proc_token_del(tok); + goto failed; + } else { + struct expr_partial_op pop = { + .once_done_want_level = expr_level, + .once_done_is_level = 3, + .typ = EPO_CAST, + .val.typ = typ + }; + if (!vector_push(expr_pops, op_stack, pop)) { + printf("Error: failed to add partial operation to operation stack\n"); + /* Empty destructor */ + goto failed; + } + expr_level = 3; + *tok = proc_next_token(prep); + goto pushed_expr; + } + } else { + struct expr_partial_op pop = { + .once_done_want_level = expr_level, + .once_done_is_level = 0, + .typ = EPO_LPAREN, + .val.c = 0 + }; + if (!vector_push(expr_pops, op_stack, pop)) { + printf("Error: failed to add partial operation to operation stack\n"); + proc_token_del(tok); + goto failed; + } + expr_level = 16; + goto pushed_expr; + } + } + } + + if (vector_size(expr_pops, op_stack)) { + if (has_level != -1) { + struct expr_partial_op *pop = &vector_last(expr_pops, op_stack); + expr_t *new_e; + switch (pop->typ) { + case EPO_LPAREN: + if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_RPAREN)) { + has_level = pop->once_done_is_level; + expr_level = pop->once_done_want_level; + vector_pop_nodel(expr_pops, op_stack); + // Empty destructor + *tok = proc_next_token(prep); + goto expr_new_token; + } + break; + case EPO_SIZEOF: + printf("Error: TODO: find type of expression\n"); + proc_token_del(tok); + goto failed; + case EPO_CAST: + new_e = malloc(sizeof *new_e); + if (!new_e) { + printf("Error: failed to create new expression atom\n"); + proc_token_del(tok); + goto failed; + } + has_level = pop->once_done_is_level; + expr_level = pop->once_done_want_level; + new_e->typ = ETY_CAST; + new_e->val.cast.typ = pop->val.typ; + new_e->val.cast.e = e; + e = new_e; + vector_pop_nodel(expr_pops, op_stack); + // Keep the same token + goto expr_new_token; + case EPO_FUNCALL: + if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_RPAREN)) { + has_level = pop->once_done_is_level; + expr_level = pop->once_done_want_level; + if (!vector_push(exprs, pop->val.funcall.exprs, e)) { + printf("Error: failed to add argument to argument stack\n"); + // Empty destructor + goto failed; + } + e = malloc(sizeof *e); + if (!e) { + printf("Error: failed to create new expression atom\n"); + // Empty destructor + goto failed; + } + e->typ = ETY_CALL; + e->val.call.fun = pop->val.funcall.f; + e->val.call.nargs = vector_size(exprs, pop->val.funcall.exprs); + e->val.call.args = vector_steal(exprs, pop->val.funcall.exprs); + vector_pop_nodel(expr_pops, op_stack); + // Empty destructor + *tok = proc_next_token(prep); + goto expr_new_token; + } else if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_COMMA)) { + if (!vector_push(exprs, pop->val.funcall.exprs, e)) { + printf("Error: failed to add argument to argument stack\n"); + // Empty destructor + goto failed; + } + // expr_level is already 15 + // Empty destructor + *tok = proc_next_token(prep); + goto pushed_expr; + } + break; + case EPO_UNARY: + new_e = malloc(sizeof *new_e); + if (!new_e) { + printf("Error: failed to create new expression atom\n"); + proc_token_del(tok); + goto failed; + } + has_level = pop->once_done_is_level; + expr_level = pop->once_done_want_level; + new_e->typ = ETY_UNARY; + new_e->val.unary.typ = pop->val.unop; + new_e->val.unary.e = e; + e = new_e; + vector_pop_nodel(expr_pops, op_stack); + // Keep the same token + goto expr_new_token; + case EPO_BINARY_ARG_SYM: + if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != pop->val.binop.last_sym)) break; + // Empty destructor + *tok = proc_next_token(prep); + /* FALLTHROUGH */ + case EPO_BINARY_ARG: + new_e = malloc(sizeof *new_e); + if (!new_e) { + printf("Error: failed to create new expression atom\n"); + proc_token_del(tok); + goto failed; + } + has_level = pop->once_done_is_level; + expr_level = pop->once_done_want_level; + new_e->typ = ETY_BINARY; + new_e->val.binary.typ = pop->val.binop.op; + new_e->val.binary.e1 = pop->val.binop.e1; + new_e->val.binary.e2 = e; + e = new_e; + vector_pop_nodel(expr_pops, op_stack); + // Keep the same token + goto expr_new_token; + case EPO_TERNARY_ARG1: + if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == pop->val.ternop.arg23_sep)) { + pop->typ = EPO_TERNARY_ARG2; + pop->val.ternop.e2 = e; + expr_level = pop->val.ternop.once_done2_want_level; + // Empty destructor + *tok = proc_next_token(prep); + goto pushed_expr; + } + break; + case EPO_TERNARY_ARG2: + new_e = malloc(sizeof *new_e); + if (!new_e) { + printf("Error: failed to create new expression atom\n"); + proc_token_del(tok); + goto failed; + } + has_level = pop->once_done_is_level; + expr_level = pop->once_done_want_level; + new_e->typ = ETY_TERNARY; + new_e->val.ternary.typ = pop->val.ternop.op; + new_e->val.ternary.e1 = pop->val.ternop.e1; + new_e->val.ternary.e2 = pop->val.ternop.e2; + new_e->val.ternary.e3 = e; + e = new_e; + vector_pop_nodel(expr_pops, op_stack); + // Keep the same token + goto expr_new_token; + } + } + } + + if ((vector_size(expr_pops, op_stack) == 0) && (has_level != -1)) { + vector_del(expr_pops, op_stack); + return e; + } + printf("Error: invalid expression: unexpected token\n"); + proc_token_print(tok); + proc_token_del(tok); +failed: + vector_del(expr_pops, op_stack); + if (e) expr_del(e); + return NULL; +} +static int eval_expression(expr_t *e, khash_t(const_map) *const_map, num_constant_t *dest) { + // Evaluate the expression (we suppose it is constant) + switch (e->typ) { + case ETY_VAR: { + khiter_t it = kh_get(const_map, const_map, string_content(e->val.var)); + if (it != kh_end(const_map)) { + *dest = kh_val(const_map, it); + return 1; + } + printf("Error: failed to evaluate expression: expression is not constant (variable)\n"); + return 0; } + + case ETY_CONST: + *dest = e->val.cst; + return 1; + + // case ETY_GENERIC: + + case ETY_CALL: + printf("Error: failed to evaluate expression: expression is not constant (function call)\n"); + return 0; + + case ETY_ACCESS: + case ETY_PTRACCESS: + printf("Error: failed to evaluate expression: expression is not constant (member access)\n"); + return 0; + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" + case ETY_UNARY: + if (!eval_expression(e->val.unary.e, const_map, dest)) return 0; + + switch (e->val.unary.typ) { + case UOT_POSTINCR: + case UOT_POSTDECR: + case UOT_PREINCR: + case UOT_PREDECR: + case UOT_REF: + case UOT_DEREF: + printf("Error: failed to evaluate expression: expression is not constant (assignment or memory accesses)\n"); + return 0; + case UOT_POS: + return 1; // Nothing to do + case UOT_NEG: + switch (dest->typ) { + case NCT_FLOAT: dest->val.f = -dest->val.f; return 1; + case NCT_DOUBLE: dest->val.d = -dest->val.d; return 1; + case NCT_LDOUBLE: dest->val.l = -dest->val.l; return 1; + case NCT_INT32: dest->val.i32 = -dest->val.i32; return 1; + case NCT_UINT32: dest->val.u32 = -dest->val.u32; return 1; + case NCT_INT64: dest->val.i64 = -dest->val.i64; return 1; + case NCT_UINT64: dest->val.u64 = -dest->val.u64; return 1; + default: return 0; + } + case UOT_ANOT: + switch (dest->typ) { + case NCT_FLOAT: + case NCT_DOUBLE: + case NCT_LDOUBLE: + printf("Error: failed to evaluate expression: cannot bitwise-negate a floating point number\n"); + return 0; + case NCT_INT32: dest->val.i32 = ~dest->val.i32; return 1; + case NCT_UINT32: dest->val.u32 = ~dest->val.u32; return 1; + case NCT_INT64: dest->val.i64 = ~dest->val.i64; return 1; + case NCT_UINT64: dest->val.u64 = ~dest->val.u64; return 1; + default: return 0; + } + case UOT_BNOT: + switch (dest->typ) { + case NCT_FLOAT: dest->typ = NCT_INT32; dest->val.i32 = !dest->val.f; return 1; + case NCT_DOUBLE: dest->typ = NCT_INT32; dest->val.i32 = !dest->val.d; return 1; + case NCT_LDOUBLE: dest->typ = NCT_INT32; dest->val.i32 = !dest->val.l; return 1; + case NCT_INT32: dest->typ = NCT_INT32; dest->val.i32 = !dest->val.i32; return 1; + case NCT_UINT32: dest->typ = NCT_INT32; dest->val.i32 = !dest->val.u32; return 1; + case NCT_INT64: dest->typ = NCT_INT32; dest->val.i32 = !dest->val.i64; return 1; + case NCT_UINT64: dest->typ = NCT_INT32; dest->val.i32 = !dest->val.u64; return 1; + default: return 0; + } + default: return 0; + } + + case ETY_BINARY: { + num_constant_t dest1, dest2; + if (!eval_expression(e->val.binary.e1, const_map, &dest1)) return 0; + if (!eval_expression(e->val.binary.e2, const_map, &dest2)) return 0; + + switch (e->val.binary.typ) { + case BOT_ASSGN_EQ: + case BOT_ASSGN_ADD: + case BOT_ASSGN_SUB: + case BOT_ASSGN_MUL: + case BOT_ASSGN_DIV: + case BOT_ASSGN_MOD: + case BOT_ASSGN_LSH: + case BOT_ASSGN_RSH: + case BOT_ASSGN_AAND: + case BOT_ASSGN_AXOR: + case BOT_ASSGN_AOR: + case BOT_ARRAY: // Is this possible? + printf("Error: failed to evaluate expression: expression is not constant (assignments)\n"); + return 0; +#define DOIT(op) \ + promote_csts(&dest1, &dest2); \ + switch ((dest->typ = dest1.typ)) { \ + case NCT_FLOAT: dest->val.f = dest1.val.f op dest2.val.f ; return 1; \ + case NCT_DOUBLE: dest->val.d = dest1.val.d op dest2.val.d ; return 1; \ + case NCT_LDOUBLE: dest->val.l = dest1.val.l op dest2.val.l ; return 1; \ + case NCT_INT32: dest->val.i32 = dest1.val.i32 op dest2.val.i32; return 1; \ + case NCT_UINT32: dest->val.u32 = dest1.val.u32 op dest2.val.u32; return 1; \ + case NCT_INT64: dest->val.i64 = dest1.val.i64 op dest2.val.i64; return 1; \ + case NCT_UINT64: dest->val.u64 = dest1.val.u64 op dest2.val.u64; return 1; \ + default: return 0; \ + } +#define DOIT_INT(op) \ + promote_csts(&dest1, &dest2); \ + switch ((dest->typ = dest1.typ)) { \ + case NCT_FLOAT: \ + case NCT_DOUBLE: \ + case NCT_LDOUBLE: \ + printf("Error: failed to evaluate expression: binary operation %u incompatible with floating point numbers\n", e->val.binary.typ); \ + return 0; \ + case NCT_INT32: dest->val.i32 = dest1.val.i32 op dest2.val.i32; return 1; \ + case NCT_UINT32: dest->val.u32 = dest1.val.u32 op dest2.val.u32; return 1; \ + case NCT_INT64: dest->val.i64 = dest1.val.i64 op dest2.val.i64; return 1; \ + case NCT_UINT64: dest->val.u64 = dest1.val.u64 op dest2.val.u64; return 1; \ + default: return 0; \ + } +#define DOIT_BOOL(op) \ + promote_csts(&dest1, &dest2); \ + switch (dest1.typ) { \ + case NCT_FLOAT: dest->typ = NCT_INT32; dest->val.i32 = dest1.val.f op dest2.val.f ; return 1; \ + case NCT_DOUBLE: dest->typ = NCT_INT32; dest->val.i32 = dest1.val.d op dest2.val.d ; return 1; \ + case NCT_LDOUBLE: dest->typ = NCT_INT32; dest->val.i32 = dest1.val.l op dest2.val.l ; return 1; \ + case NCT_INT32: dest->typ = NCT_INT32; dest->val.i32 = dest1.val.i32 op dest2.val.i32; return 1; \ + case NCT_UINT32: dest->typ = NCT_INT32; dest->val.i32 = dest1.val.u32 op dest2.val.u32; return 1; \ + case NCT_INT64: dest->typ = NCT_INT32; dest->val.i32 = dest1.val.i64 op dest2.val.i64; return 1; \ + case NCT_UINT64: dest->typ = NCT_INT32; dest->val.i32 = dest1.val.u64 op dest2.val.u64; return 1; \ + default: return 0; \ + } + case BOT_ADD: DOIT(+) + case BOT_SUB: DOIT(-) + case BOT_MUL: DOIT(*) + case BOT_DIV: DOIT(/) + case BOT_MOD: DOIT_INT(%) + case BOT_LSH: DOIT_INT(<<) + case BOT_RSH: DOIT_INT(>>) + case BOT_LT: DOIT_BOOL(<) + case BOT_GT: DOIT_BOOL(>) + case BOT_LE: DOIT_BOOL(<=) + case BOT_GE: DOIT_BOOL(>=) + case BOT_EQ: DOIT_BOOL(==) + case BOT_NE: DOIT_BOOL(!=) + case BOT_AAND: DOIT_INT(&) + case BOT_AXOR: DOIT_INT(^) + case BOT_AOR: DOIT_INT(|) + case BOT_BAND: DOIT(&&) + case BOT_BOR: DOIT(||) + case BOT_COMMA: *dest = dest2; return 1; +#undef DOIT_BOOL +#undef DOIT_INT +#undef DOIT + default: return 0; + } } + + case ETY_TERNARY: { + num_constant_t dest1, dest2, dest3; + if (!eval_expression(e->val.ternary.e1, const_map, &dest1)) return 0; + if (!eval_expression(e->val.ternary.e2, const_map, &dest2)) return 0; + if (!eval_expression(e->val.ternary.e3, const_map, &dest3)) return 0; + + switch (e->val.ternary.typ) { + case TOT_COND: + promote_csts(&dest2, &dest3); + switch (dest1.typ) { + case NCT_FLOAT: *dest = dest1.val.f ? dest2 : dest3; return 1; + case NCT_DOUBLE: *dest = dest1.val.d ? dest2 : dest3; return 1; + case NCT_LDOUBLE: *dest = dest1.val.l ? dest2 : dest3; return 1; + case NCT_INT32: *dest = dest1.val.i32 ? dest2 : dest3; return 1; + case NCT_UINT32: *dest = dest1.val.u32 ? dest2 : dest3; return 1; + case NCT_INT64: *dest = dest1.val.i64 ? dest2 : dest3; return 1; + case NCT_UINT64: *dest = dest1.val.u64 ? dest2 : dest3; return 1; + default: return 0; + } + default: return 0; + } } +#pragma GCC diagnostic pop + + // case ETY_INIT_LIST: + + case ETY_CAST: + if (!eval_expression(e->val.cast.e, const_map, dest)) return 0; + + if (e->val.cast.typ->typ == TYPE_BUILTIN) { + switch (e->val.cast.typ->val.builtin) { + case BTT_INT: + switch (dest->typ) { + case NCT_FLOAT: dest->val.i32 = (int32_t)dest->val.f; break; + case NCT_DOUBLE: dest->val.i32 = (int32_t)dest->val.d; break; + case NCT_LDOUBLE: dest->val.i32 = (int32_t)dest->val.l; break; + case NCT_INT32: break; + case NCT_UINT32: dest->val.i32 = (int32_t)dest->val.u32; break; + case NCT_INT64: dest->val.i32 = (int32_t)dest->val.i64; break; + case NCT_UINT64: dest->val.i32 = (int32_t)dest->val.u64; break; + } + dest->typ = NCT_INT32; + return 1; + case BTT_VOID: + case BTT_BOOL: + case BTT_CHAR: + case BTT_SCHAR: + case BTT_UCHAR: + case BTT_SHORT: + case BTT_SSHORT: + case BTT_USHORT: + case BTT_SINT: + case BTT_UINT: + case BTT_LONG: + case BTT_SLONG: + case BTT_ULONG: + case BTT_LONGLONG: + case BTT_SLONGLONG: + case BTT_ULONGLONG: + case BTT_INT128: + case BTT_SINT128: + case BTT_UINT128: + case BTT_S8: + case BTT_U8: + case BTT_S16: + case BTT_U16: + case BTT_S32: + case BTT_U32: + case BTT_S64: + case BTT_U64: + case BTT_FLOAT: + case BTT_CFLOAT: + case BTT_IFLOAT: + case BTT_DOUBLE: + case BTT_CDOUBLE: + case BTT_IDOUBLE: + case BTT_LONGDOUBLE: + case BTT_CLONGDOUBLE: + case BTT_ILONGDOUBLE: + case BTT_VA_LIST: + default: + printf("Error: TODO: cast to builtin in constant expression\n"); + return 0; + } + } else { + printf("Error: cast in constant expression\n"); + return 0; + } + + default: return 0; + } +} + +// declaration-specifier with storage != NULL +// specifier-qualifier-list + static_assert-declaration with storage == NULL +static int parse_declaration_specifier(khash_t(struct_map) *struct_map, khash_t(type_map) *type_map, khash_t(type_map) *enum_map, + type_t *(*builtins)[LAST_BUILTIN + 1], khash_t(const_map) *const_map, + khash_t(type_set) *type_set, preproc_t *prep, proc_token_t *tok, enum decl_storage *storage, enum decl_spec *spec, type_t *typ) { + if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_STATIC_ASSERT)) { + // _Static_assert ( constant-expression , string-literal ) ; + // Empty destructor + *tok = proc_next_token(prep); + if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_LPAREN)) { + printf("Error: unexpected token in static assertion declaration\n"); + proc_token_del(tok); + goto failed; + } + // Empty destructor + *tok = proc_next_token(prep); + expr_t *e = parse_expression(struct_map, type_map, enum_map, builtins, const_map, type_set, prep, tok, 14); + if (!e) { + goto failed; + } + if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_COMMA)) { + printf("Error: unexpected token in static assertion expression\n"); + expr_del(e); + proc_token_del(tok); + goto failed; + } + num_constant_t eval; + if (!eval_expression(e, const_map, &eval)) { + expr_del(e); + // Empty destructor + goto failed; + } + expr_del(e); + *tok = proc_next_token(prep); + if ((tok->tokt != PTOK_STRING) || !tok->tokv.sisstr) { + printf("Error: unexpected token in static assertion message\n"); + proc_token_del(tok); + goto failed; + } + string_t *errmsg = tok->tokv.str; + *tok = proc_next_token(prep); + if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_RPAREN)) { + printf("Error: unexpected token in static assertion message\n"); + proc_token_del(tok); + goto failed; + } + *tok = proc_next_token(prep); + if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_SEMICOLON)) { + printf("Error: unexpected token after static assertion\n"); + proc_token_del(tok); + goto failed; + } + + int iserror; + switch (eval.typ) { + case NCT_FLOAT: iserror = (int)eval.val.f == 0; break; + case NCT_DOUBLE: iserror = (int)eval.val.d == 0; break; + case NCT_LDOUBLE: iserror = (int)eval.val.l == 0; break; + case NCT_INT32: iserror = eval.val.i32 == 0; break; + case NCT_UINT32: iserror = eval.val.u32 == 0; break; + case NCT_INT64: iserror = eval.val.i64 == 0; break; + case NCT_UINT64: iserror = eval.val.u64 == 0; break; + default: iserror = 1; + } + if (iserror) { + printf("Error: static assertion failed: %s\n", string_content(errmsg)); + string_del(errmsg); + // Empty destructor + goto failed; + } + string_del(errmsg); + // Empty destructor + return 1; + } + +parse_cur_token_decl: + if (tok->tokt == PTOK_EOF) { + printf("Error: unexpected end of file in declaration\n"); + proc_token_print(tok); + goto failed; + } + // Storage + if (storage && (tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_AUTO)) { + if (*storage == STORAGE_NONE) *storage = STORAGE_AUTO; + else { + printf("Error: unexpected storage class specifier '%s' in declaration\n", kw2str[tok->tokv.kw]); + goto failed; + } + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } else if (storage && (tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_EXTERN)) { + if (*storage == STORAGE_NONE) *storage = STORAGE_EXTERN; + else if (*storage == STORAGE_TLS) *storage = STORAGE_TLS_EXTERN; + else { + printf("Error: unexpected storage class specifier '%s' in declaration\n", kw2str[tok->tokv.kw]); + goto failed; + } + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } else if (storage && (tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_REGISTER)) { + if (*storage == STORAGE_NONE) *storage = STORAGE_REG; + else { + printf("Error: unexpected storage class specifier '%s' in declaration\n", kw2str[tok->tokv.kw]); + goto failed; + } + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } else if (storage && (tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_STATIC)) { + if (*storage == STORAGE_NONE) *storage = STORAGE_STATIC; + else if (*storage == STORAGE_TLS) *storage = STORAGE_TLS_STATIC; + else { + printf("Error: unexpected storage class specifier '%s' in declaration\n", kw2str[tok->tokv.kw]); + goto failed; + } + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } else if (storage && (tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_THREAD_LOCAL)) { + if (*storage == STORAGE_NONE) *storage = STORAGE_TLS; + else if (*storage == STORAGE_EXTERN) *storage = STORAGE_TLS_EXTERN; + else if (*storage == STORAGE_STATIC) *storage = STORAGE_TLS_STATIC; + else { + printf("Error: unexpected storage class specifier '%s' in declaration\n", kw2str[tok->tokv.kw]); + goto failed; + } + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } else if (storage && (tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_TYPEDEF)) { + if (*storage == STORAGE_NONE) *storage = STORAGE_TYPEDEF; + else { + printf("Error: unexpected storage class specifier '%s' in declaration\n", kw2str[tok->tokv.kw]); + goto failed; + } + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } + + // Qualifier + if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_ATOMIC)) { + // Empty destructor + *tok = proc_next_token(prep); + if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_LPAREN)) { + printf("Error: TODO: _Atomic(type-name)\n"); + // Empty destructor + goto failed; + } else { + typ->is_atomic = 1; + goto parse_cur_token_decl; + } + } else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_CONST)) { + typ->is_const = 1; + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_RESTRICT)) { + typ->is_restrict = 1; + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_VOLATILE)) { + typ->is_volatile = 1; + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } + + // Specifier +#define SPEC(bt,post) \ + if (*spec == SPEC_NONE) { \ + *spec = SPEC_BUILTIN; \ + typ->typ = TYPE_BUILTIN; \ + typ->val.builtin = BTT_ ## bt; \ + post \ + } else { \ + printf("Error: unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]); \ + goto failed; \ + } \ + *tok = proc_next_token(prep); \ + goto parse_cur_token_decl; +#define SPEC_SIGNED(bt, allow_int) \ + if ((*spec == SPEC_NONE) || (allow_int && (*spec == SPEC_BUILTIN) && (typ->val.builtin == BTT_INT))) { \ + *spec = (allow_int && (*spec == SPEC_NONE)) ? SPEC_BUILTIN_NOINT : SPEC_BUILTIN; \ + typ->typ = TYPE_BUILTIN; \ + typ->val.builtin = BTT_ ## bt; \ + } else if (((*spec == SPEC_BUILTIN_NOINT) || (allow_int && (*spec == SPEC_BUILTIN))) && (typ->val.builtin == BTT_SINT)) { \ + *spec = allow_int ? *spec : SPEC_BUILTIN; \ + typ->val.builtin = BTT_S ## bt; \ + } else if (((*spec == SPEC_BUILTIN_NOINT) || (allow_int && (*spec == SPEC_BUILTIN))) && (typ->val.builtin == BTT_UINT)) { \ + *spec = allow_int ? *spec : SPEC_BUILTIN; \ + typ->val.builtin = BTT_U ## bt; \ + } else { \ + printf("Error: unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]); \ + goto failed; \ + } \ + *tok = proc_next_token(prep); \ + goto parse_cur_token_decl; + if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_BOOL)) { + SPEC(BOOL,) + } else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_COMPLEX)) { + if (*spec == SPEC_NONE) { + *spec = SPEC_COMPLEX; + } else if ((*spec == SPEC_BUILTIN_NOINT) && (typ->val.builtin == BTT_LONG)) { + *spec = SPEC_LONGCOMPLEX; + } else if ((*spec == SPEC_BUILTIN) && (typ->val.builtin == BTT_FLOAT)) { + typ->val.builtin = BTT_CFLOAT; + } else if ((*spec == SPEC_BUILTIN) && (typ->val.builtin == BTT_DOUBLE)) { + typ->val.builtin = BTT_CDOUBLE; + } else if ((*spec == SPEC_BUILTIN) && (typ->val.builtin == BTT_LONGDOUBLE)) { + typ->val.builtin = BTT_CLONGDOUBLE; + } else { + printf("Error: unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]); + goto failed; + } + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_IMAGINARY)) { + if (*spec == SPEC_NONE) { + *spec = SPEC_IMAGINARY; + } else if ((*spec == SPEC_BUILTIN_NOINT) && (typ->val.builtin == BTT_LONG)) { + *spec = SPEC_LONGIMAGINARY; + } else if ((*spec == SPEC_BUILTIN) && (typ->val.builtin == BTT_FLOAT)) { + typ->val.builtin = BTT_IFLOAT; + } else if ((*spec == SPEC_BUILTIN) && (typ->val.builtin == BTT_DOUBLE)) { + typ->val.builtin = BTT_IDOUBLE; + } else if ((*spec == SPEC_BUILTIN) && (typ->val.builtin == BTT_LONGDOUBLE)) { + typ->val.builtin = BTT_ILONGDOUBLE; + } else { + printf("Error: unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]); + goto failed; + } + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_DOUBLE)) { + if (*spec == SPEC_NONE) { + *spec = SPEC_BUILTIN; + typ->typ = TYPE_BUILTIN; + typ->val.builtin = BTT_DOUBLE; + } else if (*spec == SPEC_COMPLEX) { + *spec = SPEC_BUILTIN; + typ->typ = TYPE_BUILTIN; + typ->val.builtin = BTT_CDOUBLE; + } else if (*spec == SPEC_IMAGINARY) { + *spec = SPEC_BUILTIN; + typ->typ = TYPE_BUILTIN; + typ->val.builtin = BTT_IDOUBLE; + } else if (*spec == SPEC_LONGCOMPLEX) { + *spec = SPEC_BUILTIN; + typ->typ = TYPE_BUILTIN; + typ->val.builtin = BTT_CLONGDOUBLE; + } else if (*spec == SPEC_LONGIMAGINARY) { + *spec = SPEC_BUILTIN; + typ->typ = TYPE_BUILTIN; + typ->val.builtin = BTT_ILONGDOUBLE; + } else if ((*spec == SPEC_BUILTIN_NOINT) && (typ->val.builtin == BTT_LONG)) { + *spec = SPEC_BUILTIN; + typ->val.builtin = BTT_LONGDOUBLE; + } else { + printf("Error: unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]); + goto failed; + } + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_FLOAT)) { + if (*spec == SPEC_NONE) { + *spec = SPEC_BUILTIN; + typ->typ = TYPE_BUILTIN; + typ->val.builtin = BTT_FLOAT; + } else if (*spec == SPEC_COMPLEX) { + *spec = SPEC_BUILTIN; + typ->typ = TYPE_BUILTIN; + typ->val.builtin = BTT_CFLOAT; + } else if (*spec == SPEC_IMAGINARY) { + *spec = SPEC_BUILTIN; + typ->typ = TYPE_BUILTIN; + typ->val.builtin = BTT_IFLOAT; + } else { + printf("Error: unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]); + goto failed; + } + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_CHAR)) { + SPEC_SIGNED(CHAR, 0) + } else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_INT)) { + if (*spec == SPEC_NONE) { + *spec = SPEC_BUILTIN; + typ->typ = TYPE_BUILTIN; + typ->val.builtin = BTT_INT; + } else if (*spec == SPEC_BUILTIN_NOINT) { + *spec = SPEC_BUILTIN; + } else { + printf("Error: unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]); + goto failed; + } + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_INT128)) { + SPEC_SIGNED(INT128, 0) + } else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_LONG)) { + if ((*spec == SPEC_NONE) || ((*spec == SPEC_BUILTIN) && (typ->val.builtin == BTT_INT))) { + *spec = (*spec == SPEC_NONE) ? SPEC_BUILTIN_NOINT : SPEC_BUILTIN; + typ->typ = TYPE_BUILTIN; + typ->val.builtin = BTT_LONG; + } else if (((*spec == SPEC_BUILTIN_NOINT) || (*spec == SPEC_BUILTIN)) && (typ->val.builtin == BTT_SINT)) { + typ->val.builtin = BTT_SLONG; + } else if (((*spec == SPEC_BUILTIN_NOINT) || (*spec == SPEC_BUILTIN)) && (typ->val.builtin == BTT_UINT)) { + typ->val.builtin = BTT_ULONG; + } else if (((*spec == SPEC_BUILTIN_NOINT) || (*spec == SPEC_BUILTIN)) && (typ->val.builtin == BTT_LONG)) { + typ->val.builtin = BTT_LONGLONG; + } else if (((*spec == SPEC_BUILTIN_NOINT) || (*spec == SPEC_BUILTIN)) && (typ->val.builtin == BTT_ULONG)) { + typ->val.builtin = BTT_ULONGLONG; + } else if (((*spec == SPEC_BUILTIN_NOINT) || (*spec == SPEC_BUILTIN)) && (typ->val.builtin == BTT_SLONG)) { + typ->val.builtin = BTT_SLONGLONG; + } else if ((*spec == SPEC_BUILTIN) && (typ->val.builtin == BTT_DOUBLE)) { + typ->val.builtin = BTT_LONGDOUBLE; + } else if ((*spec == SPEC_BUILTIN) && (typ->val.builtin == BTT_CDOUBLE)) { + typ->val.builtin = BTT_CLONGDOUBLE; + } else if ((*spec == SPEC_BUILTIN) && (typ->val.builtin == BTT_IDOUBLE)) { + typ->val.builtin = BTT_ILONGDOUBLE; + } else if (*spec == SPEC_COMPLEX) { + *spec = SPEC_LONGCOMPLEX; + } else if (*spec == SPEC_IMAGINARY) { + *spec = SPEC_LONGIMAGINARY; + } else { + printf("Error: unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]); + goto failed; + } + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_SHORT)) { + SPEC_SIGNED(SHORT, 1) + } else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_SIGNED)) { + if (*spec == SPEC_NONE) { + *spec = SPEC_BUILTIN_NOINT; + typ->typ = TYPE_BUILTIN; + typ->val.builtin = BTT_SINT; + } else if (((*spec == SPEC_BUILTIN_NOINT) || (*spec == SPEC_BUILTIN)) && (typ->val.builtin == BTT_CHAR)) { + typ->val.builtin = BTT_SCHAR; + } else if ((*spec == SPEC_BUILTIN) && (typ->val.builtin == BTT_INT)) { + typ->val.builtin = BTT_SINT; + } else if (((*spec == SPEC_BUILTIN_NOINT) || (*spec == SPEC_BUILTIN)) && (typ->val.builtin == BTT_LONG)) { + typ->val.builtin = BTT_SLONG; + } else if (((*spec == SPEC_BUILTIN_NOINT) || (*spec == SPEC_BUILTIN)) && (typ->val.builtin == BTT_LONGLONG)) { + typ->val.builtin = BTT_SLONGLONG; + } else if (((*spec == SPEC_BUILTIN_NOINT) || (*spec == SPEC_BUILTIN)) && (typ->val.builtin == BTT_SHORT)) { + typ->val.builtin = BTT_SSHORT; + } else { + printf("Error: unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]); + goto failed; + } + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_UNSIGNED)) { + if (*spec == SPEC_NONE) { + *spec = SPEC_BUILTIN_NOINT; + typ->typ = TYPE_BUILTIN; + typ->val.builtin = BTT_UINT; + } else if (((*spec == SPEC_BUILTIN_NOINT) || (*spec == SPEC_BUILTIN)) && (typ->val.builtin == BTT_CHAR)) { + typ->val.builtin = BTT_UCHAR; + } else if ((*spec == SPEC_BUILTIN) && (typ->val.builtin == BTT_INT)) { + typ->val.builtin = BTT_UINT; + } else if (((*spec == SPEC_BUILTIN_NOINT) || (*spec == SPEC_BUILTIN)) && (typ->val.builtin == BTT_LONG)) { + typ->val.builtin = BTT_ULONG; + } else if (((*spec == SPEC_BUILTIN_NOINT) || (*spec == SPEC_BUILTIN)) && (typ->val.builtin == BTT_LONGLONG)) { + typ->val.builtin = BTT_ULONGLONG; + } else if (((*spec == SPEC_BUILTIN_NOINT) || (*spec == SPEC_BUILTIN)) && (typ->val.builtin == BTT_SHORT)) { + typ->val.builtin = BTT_USHORT; + } else { + printf("Error: unexpected type specifier '%s' in declaration\n", kw2str[tok->tokv.kw]); + goto failed; + } + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_VOID)) { + SPEC(VOID, typ->is_incomplete = 1;) + } +#undef SPEC +#undef SPEC_SIGNED + if ((tok->tokt == PTOK_IDENT) && (*spec == SPEC_NONE)) { + // The ident is the type-specifier + khiter_t it = kh_get(type_map, type_map, string_content(tok->tokv.str)); + if (it == kh_end(type_map)) { + printf("Error: invalid type '%s' (ident is not a typedef)\n" + "Current state:\n" + " storage: %p/%u\n" + " spec: %u\n" + " type: ", string_content(tok->tokv.str), storage, storage ? *storage : STORAGE_NONE, *spec); + type_print(typ); + printf("\n"); + string_del(tok->tokv.str); + goto failed; + } else { + *spec = SPEC_TYPE; + if (!type_copy_into(typ, kh_val(type_map, it))) { + printf("Failed to duplicate type infos\n"); + string_del(tok->tokv.str); + goto failed; + } + string_del(tok->tokv.str); + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } + } + if ((tok->tokt == PTOK_KEYWORD) && (*spec == SPEC_NONE) && ((tok->tokv.kw == KW_STRUCT) || (tok->tokv.kw == KW_UNION))) { + int is_struct = tok->tokv.kw == KW_STRUCT; + *spec = SPEC_TYPE; + + // Empty destructor + *tok = proc_next_token(prep); + khiter_t it = kh_end(struct_map); // Iterator into the struct_map + if (tok->tokt == PTOK_IDENT) { + string_t *tag = tok->tokv.str; + // Token moved + *tok = proc_next_token(prep); + + int iret; + it = kh_put(struct_map, struct_map, string_content(tag), &iret); + if (iret < 0) { + printf("Error: failed to add structure to struct map\n"); + proc_token_del(tok); + goto failed; + } else if (iret == 0) { + // Structure already declared or defined + if (kh_val(struct_map, it)->is_struct != is_struct) { + printf("Error: incoherent struct/union tagging of %s\n", string_content(tag)); + string_del(tag); + proc_token_del(tok); + goto failed; + } + ++kh_val(struct_map, it)->nrefs; + string_del(tag); + } else { + kh_val(struct_map, it) = struct_new(is_struct, tag); + if (!kh_val(struct_map, it)) { + printf("Error: failed to create new structure metadata structure\n"); + string_del(tag); + proc_token_del(tok); + goto failed; + } + } + typ->typ = TYPE_STRUCT_UNION; + typ->val.st = kh_val(struct_map, it); + typ->is_incomplete = !typ->val.st->is_defined; + } else { + typ->typ = TYPE_STRUCT_UNION; + typ->val.st = struct_new(is_struct, NULL); + if (!typ->val.st) { + printf("Error: failed to create new structure metadata structure\n"); + proc_token_del(tok); + goto failed; + } + typ->is_incomplete = 1; + } + if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_LBRACKET)) { + if (typ->val.st->is_defined) { + printf("Error: TODO: struct redefinition\n" + "Current state:\n" + " storage: %p/%u\n" + " spec: %u\n" + " type: ", storage, storage ? *storage : STORAGE_NONE, *spec); + type_print(typ); + printf("\n"); + goto failed; + } + + VECTOR(st_members) *members = vector_new(st_members); + if (!members) { + printf("Failed to create a members vector\n"); + goto failed; + } + type_t *typ2 = type_new(); + if (!typ2) { + printf("Failed to create a type info structure\n"); + vector_del(st_members, members); + goto failed; + } + *tok = proc_next_token(prep); + while (!proc_token_isend(tok) && ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_RBRACKET))) { + enum decl_spec spec2 = SPEC_NONE; + if (!parse_declaration_specifier(struct_map, type_map, enum_map, builtins, const_map, type_set, prep, tok, NULL, &spec2, typ2)) { + vector_del(st_members, members); + type_del(typ2); + goto failed; + } + if (spec2 == SPEC_NONE) { + // Empty destructor + *tok = proc_next_token(prep); + continue; // Declaration was an assert, typ2 is unchanged + } + typ2 = type_try_merge(typ2, type_set); + if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_SEMICOLON)) { + // A struct-declaration that does not declare an anonymous structure or anonymous union + // shall contain a struct-declarator-list. + if ((typ2->typ != TYPE_STRUCT_UNION) || typ2->val.st->tag) { + printf("Error: missing struct-declarator-list\n"); + vector_del(st_members, members); + type_del(typ2); + goto failed; + } + if (!vector_push(st_members, members, ((st_member_t){.name = NULL, .typ = typ2, .is_bitfield = 0}))) { + printf("Error: failed to add anonymous structure member\n"); + vector_del(st_members, members); + type_del(typ2); + // Empty destructor + goto failed; + } + typ2 = type_new(); + if (!typ2) { + printf("Failed to create a type info structure\n"); + vector_del(st_members, members); + // Empty destructor + goto failed; + } + // Empty destructor + *tok = proc_next_token(prep); + continue; + } + + struct parse_declarator_dest_s dest2; + dest2.structms.struct_map = struct_map; + dest2.structms.type_map = type_map; + dest2.structms.enum_map = enum_map; + dest2.structms.type_set = type_set; + dest2.structms.builtins = builtins; + dest2.structms.const_map = const_map; + dest2.structms.dest = members; + if (!parse_declarator(&dest2, prep, tok, STORAGE_NONE, typ2, 0, 1, 1, 1)) { + printf("Error parsing struct-declarator-list\n"); + vector_del(st_members, members); + type_del(typ2); + // Token is deleted + goto failed; + } + type_del(typ2); + if ((tok->tokt != PTOK_SYM) && (tok->tokv.sym != SYM_SEMICOLON)) { + printf("Error parsing struct-declarator-list (invalid next token)\n"); + vector_del(st_members, members); + proc_token_del(tok); + goto failed; + } + typ2 = type_new(); + if (!typ2) { + printf("Failed to create a type info structure\n"); + vector_del(st_members, members); + // Empty destructor + goto failed; + } + *tok = proc_next_token(prep); + } + type_del(typ2); + if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_RBRACKET)) { + printf("Error parsing struct-declarator-list (invalid next token)\n"); + vector_del(st_members, members); + proc_token_del(tok); + goto failed; + } + + typ->is_incomplete = 0; + typ->val.st->has_incomplete = 0; // Filled by the validate_type step + typ->val.st->nmembers = vector_size(st_members, members); + typ->val.st->members = vector_steal(st_members, members); + typ->val.st->is_defined = 1; + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } else { + if (it == kh_end(struct_map)) { + printf("Error: invalid structure declaration: missing tag and/or definition\n"); + proc_token_del(tok); + goto failed; + } + goto parse_cur_token_decl; + } + } + if ((tok->tokt == PTOK_KEYWORD) && (*spec == SPEC_NONE) && (tok->tokv.kw == KW_ENUM)) { + *spec = SPEC_TYPE; + + // Empty destructor + *tok = proc_next_token(prep); + string_t *tag = NULL; + if (tok->tokt == PTOK_IDENT) { + tag = tok->tokv.str; + *tok = proc_next_token(prep); + } + if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_LBRACKET)) { + if (!tag) { + printf("Error: unexpected token after keyword 'enum'\n"); + proc_token_del(tok); + goto failed; + } + khiter_t it = kh_get(type_map, enum_map, string_content(tag)); + if (it == kh_end(enum_map)) { + printf("Error: enumeration %s has not been defined yet\n", string_content(tag)); // TODO? + string_del(tag); + proc_token_del(tok); + goto failed; + } + if (!type_copy_into(typ, kh_val(enum_map, it))) { + printf("Failed to duplicate enum type infos\n"); + string_del(tag); + proc_token_del(tok); + goto failed; + } + string_del(tag); + goto parse_cur_token_decl; + } + // We are defining the enum + // Try in order: + // If all values are 0 <= . <= UINT32_MAX, BTT_U32 + // If any value is negative and all values are <= INT32_MAX, BTT_S32 + // If any value is negative and all values are <= INT64_MAX, BTT_S64 + // If all values are 0 <= . <= UINT64_MAX, BTT_U64 + // Otherwise, error + // By default, BTT_U32* and the constant is an NCT_INT32 + // *The AMD ABI says this should rather be BTT_INT (or BTT_S32) + // Note that BTT_S32 only when has_neg is true + int has_neg = 0, not_in_i32 = 0; + enum type_builtin_e btt = BTT_U32; + num_constant_t cst = { .typ = NCT_INT32, .val.i32 = -1 }; + // Empty destructor + *tok = proc_next_token(prep); + int iret; + while ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_RBRACKET)) { + if (tok->tokt != PTOK_IDENT) { + printf("Error: invalid token in enumeration definition\n"); + if (tag) string_del(tag); + proc_token_del(tok); + goto failed; + } + char *ident = string_steal(tok->tokv.str); + *tok = proc_next_token(prep); + khiter_t it = kh_put(const_map, const_map, ident, &iret); + if (iret < 0) { + printf("Error: failed to add constant %s to the constants map\n", ident); + free(ident); + if (tag) string_del(tag); + proc_token_del(tok); + goto failed; + } else if (iret == 0) { + printf("Error: constant %s is already in the constants map\n", ident); + free(ident); + if (tag) string_del(tag); + proc_token_del(tok); + goto failed; + } + if ((tok->tokt == PTOK_SYM) && ((tok->tokv.sym == SYM_COMMA) || tok->tokv.sym == SYM_RBRACKET)) { + switch (cst.typ) { + case NCT_INT32: + if (cst.val.i32 == INT32_MAX) { + cst.typ = NCT_UINT32; + cst.val.u32 = (uint32_t)INT32_MAX + 1; + } else ++cst.val.i32; + break; + case NCT_UINT32: + if (cst.val.u32 == UINT32_MAX) { + cst.typ = NCT_INT64; + cst.val.i64 = (int64_t)UINT32_MAX + 1; + } else ++cst.val.u32; + break; + case NCT_INT64: + if (cst.val.i64 == INT64_MAX) { + cst.typ = NCT_UINT64; + cst.val.u64 = (uint64_t)INT64_MAX + 1; + } else ++cst.val.i64; + break; + case NCT_UINT64: + if (cst.val.u64 == UINT64_MAX) { + printf("Error: enum constant is too big\n"); + if (tag) string_del(tag); + proc_token_del(tok); + goto failed; + } else ++cst.val.u64; + break; + case NCT_FLOAT: + case NCT_DOUBLE: + case NCT_LDOUBLE: + default: + printf("Internal error: enum constant is a float/double/ldouble\n"); + if (tag) string_del(tag); + proc_token_del(tok); + goto failed; + } + } else if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_EQ)) { + // Empty destructor + *tok = proc_next_token(prep); + expr_t *e = parse_expression(struct_map, type_map, enum_map, builtins, const_map, type_set, prep, tok, 14); + if (!e) { + goto failed; + } + if ((tok->tokt != PTOK_SYM) || ((tok->tokv.sym != SYM_COMMA) && (tok->tokv.sym != SYM_RBRACKET))) { + printf("Error: unexpected token during enumeration declaration\n"); + expr_del(e); + if (tag) string_del(tag); + proc_token_del(tok); + goto failed; + } + if (!eval_expression(e, const_map, &cst)) { + expr_del(e); + if (tag) string_del(tag); + // Empty destructor + goto failed; + } + expr_del(e); + } + switch (cst.typ) { + case NCT_INT32: + if (cst.val.i32 < 0) { + has_neg = 1; + if (btt == BTT_U64) { + printf("Error: enum constant is too big\n"); + if (tag) string_del(tag); + // Empty destructor + goto failed; + } + btt = not_in_i32 ? BTT_S64 : BTT_S32; + } + break; + case NCT_UINT32: + if (cst.val.u32 > (uint32_t)INT32_MAX) { + not_in_i32 = 1; + btt = has_neg ? BTT_S64 : BTT_U32; + } + break; + case NCT_INT64: + if (cst.val.i64 < 0) { + has_neg = 1; + if (cst.val.i64 < (int64_t)INT32_MIN) { + not_in_i32 = 1; + } + if (btt == BTT_U64) { + printf("Error: enum constant is too big\n"); + if (tag) string_del(tag); + // Empty destructor + goto failed; + } + btt = + not_in_i32 ? BTT_S64 : + BTT_S32; + } + if ((cst.val.i64 > (int64_t)INT32_MAX)) { + not_in_i32 = 1; + if (has_neg) btt = BTT_S64; + else if (((btt == BTT_S32) || (btt == BTT_U32)) && (cst.val.i64 <= (int64_t)UINT32_MAX)) + btt = BTT_U32; + else if (btt != BTT_U64) btt = BTT_S64; + } + break; + case NCT_UINT64: + if (cst.val.u64 > (uint32_t)INT32_MAX) { + not_in_i32 = 1; + if (has_neg && (cst.val.u64 > (uint64_t)INT64_MAX)) { + printf("Error: enum constant is too big\n"); + if (tag) string_del(tag); + // Empty destructor + goto failed; + } + btt = (cst.val.u64 > (uint64_t)INT64_MAX) ? BTT_U64 : (has_neg || (cst.val.u64 > (uint64_t)UINT32_MAX)) ? BTT_S64 : BTT_U32; + } + break; + case NCT_FLOAT: + case NCT_DOUBLE: + case NCT_LDOUBLE: + default: + printf("Error: invalid floating-point enumeration constant\n"); + if (tag) string_del(tag); + // Empty destructor + goto failed; + } + kh_val(const_map, it) = cst; + if (tok->tokv.sym == SYM_COMMA) { + *tok = proc_next_token(prep); + } + } + if (tag) { + char *ctag = string_steal(tag); + khiter_t it = kh_put(type_map, enum_map, ctag, &iret); + if (iret < 0) { + printf("Error: failed to add enumeration %s to the type map\n", ctag); + free(ctag); + // Empty destructor + goto failed; + } else if (iret == 0) { + printf("Error: enumeration %s already exists\n", ctag); + free(ctag); + // Empty destructor + goto failed; + } + type_t *new_typ = type_new(); + if (!new_typ) { + printf("Error: failed to create type info for enumeration %s\n", ctag); + free(ctag); + kh_del(type_map, enum_map, it); + // Empty destructor + goto failed; + } + typ->typ = new_typ->typ = TYPE_ENUM; + typ->is_incomplete = new_typ->is_incomplete = 0; + typ->val.typ = new_typ->val.typ = (*builtins)[btt]; + typ->val.typ->nrefs += 2; + new_typ = type_try_merge(new_typ, type_set); + validate_type(new_typ, builtins); // Assume it returns 1 + kh_val(enum_map, it) = new_typ; + } else { + typ->typ = TYPE_ENUM; + typ->is_incomplete = 0; + typ->val.typ = (*builtins)[btt]; + ++typ->val.typ->nrefs; + } + *tok = proc_next_token(prep); + goto parse_cur_token_decl; + } + + if ((*spec != SPEC_BUILTIN) && (*spec != SPEC_BUILTIN_NOINT) && (*spec != SPEC_TYPE)) goto invalid_token; + if ((tok->tokt != PTOK_IDENT) && ((tok->tokt != PTOK_SYM) || ((tok->tokv.sym != SYM_COMMA) && (tok->tokv.sym != SYM_SEMICOLON) + && (tok->tokv.sym != SYM_STAR) && (tok->tokv.sym != SYM_LSQBRACKET) + && (tok->tokv.sym != SYM_LPAREN) && (tok->tokv.sym != SYM_RPAREN) + && (tok->tokv.sym != SYM_COLON)))) + goto invalid_token; + + return 1; + +invalid_token: + printf("Error: unexpected token (parse_declaration_specifier)\n" + "Current state:\n" + " storage: %p/%u\n" + " spec: %u\n" + " type: ", storage, storage ? *storage : STORAGE_NONE, *spec); + type_print(typ); + printf("\n"); + proc_token_print(tok); + proc_token_del(tok); + +failed: + return 0; +} + +static int parse_declarator(struct parse_declarator_dest_s *dest, preproc_t *prep, proc_token_t *tok, enum decl_storage storage, type_t *base_type, + int is_init, int is_list, int allow_decl, int allow_abstract) { + int has_list = 0, has_ident = 0; + // TODO: allow_abstract and 'direct-abstract-declarator(opt) ( parameter-type-list(opt) )' + + string_t *cur_ident = NULL; + type_t *typ = base_type; ++typ->nrefs; + type_t *cur_bottom = NULL; + VECTOR(size_t) *nptr_stack = vector_new_cap(size_t, 1); + if (!nptr_stack) { + printf("Failed to parse init_declarator_list (allocate nptr_stack)\n"); + proc_token_del(tok); + goto failed0; + } + vector_push(size_t, nptr_stack, 0); // Always succeed (size < cap) + while (1) { + switch (tok->tokt) { + case PTOK_IDENT: + if (!has_ident) { + if (allow_decl) { + cur_ident = tok->tokv.str; + has_ident = 1; + *tok = proc_next_token(prep); + break; + } else { + printf("Error: unexpected identifier: abstract declarator do not contain a name\n" + "Current state:\n" + " storage: %u\n" + " type: ", storage); + type_print(typ); + printf("\n"); + proc_token_print(tok); + proc_token_del(tok); + goto failed; + } + } else { + printf("Error: unexpected identifier\n" + "Current state:\n" + " storage: %u\n" + " type: ", storage); + type_print(typ); + printf("\n"); + proc_token_print(tok); + proc_token_del(tok); + goto failed; + } + case PTOK_SYM: + if (tok->tokv.sym == SYM_LPAREN) { + if (has_ident) { + type_t *new_typ; + + VECTOR(types) *args = vector_new(types); + if (!args) { + printf("Error: failed to create new type (function argument)\n"); + // Empty destructor + goto failed; + } + int has_varargs = 0; + *tok = proc_next_token(prep); + if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_VOID)) { + // Empty destructor + *tok = proc_next_token(prep); + if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_RPAREN)) { + vector_del(types, args); + new_typ = type_new(); + if (!new_typ) { + printf("Error: failed to create new function type\n"); + // Empty destructor + goto failed; + } + new_typ->typ = TYPE_FUNCTION; + new_typ->val.fun.has_varargs = 0; + new_typ->val.fun.nargs = 0; + new_typ->val.fun.args = NULL; + // ret will be set later + goto end_fun; + } + if (!proc_unget_token(prep, tok)) { + printf("Error: failed to unget processor token\n"); + // Empty destructor + vector_del(types, args); + goto failed; + } + tok->tokt = PTOK_KEYWORD; + tok->tokv.kw = KW_VOID; + } else if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_RPAREN)) { + goto no_arg; + } + while (1) { + if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_VARIADIC)) { + has_varargs = 1; + // Empty destructor + *tok = proc_next_token(prep); + if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_RPAREN)) { + printf("Error: invalid token after function variadic argument\n"); + proc_token_del(tok); + vector_del(types, args); + goto failed; + } + break; + } else { + type_t *typ2 = type_new(); + if (!typ2) { + printf("Error: failed to create new type (function argument)\n"); + // Empty destructor + vector_del(types, args); + goto failed; + } + enum decl_storage storage2 = STORAGE_NONE; + enum decl_spec spec2 = SPEC_NONE; + if (!parse_declaration_specifier(PDECL_STRUCT_MAP, PDECL_TYPE_MAP, PDECL_ENUM_MAP, PDECL_BUILTINS, + PDECL_CONST_MAP, PDECL_TYPE_SET, prep, tok, &storage2, &spec2, typ2)) { + // Token is deleted + vector_del(types, args); + type_del(typ2); + goto failed; + } + if (spec2 == SPEC_NONE) { + // _Static_assert declaration; empty destructor + printf("Invalid _Static_assert declaration\n"); + vector_del(types, args); + type_del(typ2); + goto failed; + } + if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_RPAREN)) { + // Unnamed argument + if (!vector_push(types, args, typ2)) { + printf("Error: failed to add argument to argument vector\n"); + vector_del(types, args); + type_del(typ2); + // Empty destructor + goto failed; + } + break; + } else if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_COMMA)) { + // Unnamed argument + if (!vector_push(types, args, typ2)) { + printf("Error: failed to add argument to argument vector\n"); + vector_del(types, args); + type_del(typ2); + // Empty destructor + goto failed; + } + // Empty destructor + *tok = proc_next_token(prep); + continue; + } + // FIXME: Storage specifiers are ignored most of the time? + struct parse_declarator_dest_s dest2; + dest2.argt.dest = NULL; + dest2.argt.struct_map = PDECL_STRUCT_MAP; + dest2.argt.type_map = PDECL_TYPE_MAP; + dest2.argt.enum_map = PDECL_ENUM_MAP; + dest2.argt.type_set = PDECL_TYPE_SET; + dest2.argt.builtins = PDECL_BUILTINS; + dest2.argt.const_map = PDECL_CONST_MAP; + if (!parse_declarator(&dest2, prep, tok, STORAGE_NONE, typ2, 0, 0, 1, 1)) { + // Token is deleted + vector_del(types, args); + type_del(typ2); + goto failed; + } + type_del(typ2); + if (!dest2.argt.dest) { + printf("Internal error: argument type is NULL\n"); + vector_del(types, args); + // Empty destructor + goto failed; + } + if (!vector_push(types, args, dest2.argt.dest)) { + printf("Error: failed to add argument to argument vector\n"); + vector_del(types, args); + type_del(dest2.argt.dest); + // Empty destructor + goto failed; + } + if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_RPAREN)) { + break; + } else if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_COMMA)) { + // Empty destructor + *tok = proc_next_token(prep); + continue; + } + printf("Error: invalid token after function argument\n"); + vector_del(types, args); + proc_token_del(tok); + goto failed; + } + } + no_arg: + new_typ = type_new(); + if (!new_typ) { + printf("Error: failed to create new function type\n"); + // Empty destructor + goto failed; + } + new_typ->typ = TYPE_FUNCTION; + new_typ->val.fun.has_varargs = has_varargs; + new_typ->val.fun.nargs = vector_size(types, args) ? vector_size(types, args) : (size_t)-1; + new_typ->val.fun.args = vector_steal(types, args); + + end_fun: // (void) + if (cur_bottom) { + // cur_bottom is a pointer, an array or a function + // We have cur_bottom(old) --> below, we want cur_bottom(old) --> array=cur_bottom(new) --> below + new_typ->val.fun.ret = + (cur_bottom->typ == TYPE_PTR) ? cur_bottom->val.typ : + (cur_bottom->typ == TYPE_ARRAY) ? cur_bottom->val.array.typ : + cur_bottom->val.fun.ret; + *((cur_bottom->typ == TYPE_PTR) ? &cur_bottom->val.typ : + (cur_bottom->typ == TYPE_ARRAY) ? &cur_bottom->val.array.typ : &cur_bottom->val.fun.ret) = new_typ; + cur_bottom = new_typ; + } else { + // We have top(old), we want array=top(new)=cur_bottom(new) --> top(old) + new_typ->val.fun.ret = typ; + cur_bottom = typ = new_typ; + } + *tok = proc_next_token(prep); + break; + } else { + if (!vector_push(size_t, nptr_stack, 0)) { + printf("Failed to parse init_declarator_list (open parenthesis)\n"); + // Empty destructor + goto failed; + } + *tok = proc_next_token(prep); + break; + } + } else if (tok->tokv.sym == SYM_STAR) { + if (has_ident) { + printf("Error: invalid token '*' after identifier in declaration\n"); + // Empty destructor + goto failed; + } else { + type_t *new_typ = type_new_ptr(typ); + if (!new_typ) { + printf("Failed to parse init_declarator_list (create new pointer typ)\n"); + // Empty destructor + goto failed; + } + typ = new_typ; + ++vector_last(size_t, nptr_stack); + *tok = proc_next_token(prep); + break; + } + } else if (tok->tokv.sym == SYM_LSQBRACKET) { + if (!has_ident) { + if (!allow_abstract) { + printf("Error: invalid token '[' before identifier in declaration\n"); + // Empty destructor + goto failed; + } + has_ident = 1; + } + // Empty destructor + *tok = proc_next_token(prep); + // Here we have only two array constructors: + // direct-declaration [ assignment-expression(opt) ] + // direct-declaration [ * ] (complete VLA) + size_t nelems; _Bool is_incomplete; + if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_RSQBRACKET)) { + // Incomplete VLA + nelems = (size_t)-1; + is_incomplete = 1; + } else if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_STAR)) { + // Complete VLA, expecting a ']' + nelems = (size_t)-1; + is_incomplete = 0; + // Empty destructor + *tok = proc_next_token(prep); + if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_RSQBRACKET)) { + printf("Error: unexpected token during variable length array declaration\n"); + proc_token_del(tok); + goto failed; + } + } else { + // Constant expression, followed by ']' + is_incomplete = 0; + expr_t *e = parse_expression(PDECL_STRUCT_MAP, PDECL_TYPE_MAP, PDECL_ENUM_MAP, PDECL_BUILTINS, PDECL_CONST_MAP, PDECL_TYPE_SET, prep, tok, 15); + if (!e) { + goto failed; + } + if ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_RSQBRACKET)) { + printf("Error: unexpected token during array declaration\n"); + expr_del(e); + proc_token_del(tok); + goto failed; + } + num_constant_t cst; + if (!eval_expression(e, PDECL_CONST_MAP, &cst)) { + expr_del(e); + // Empty destructor + goto failed; + } + expr_del(e); + int is_neg; + switch (cst.typ) { + case NCT_FLOAT: is_neg = cst.val.f < 0; nelems = (size_t)cst.val.f; break; + case NCT_DOUBLE: is_neg = cst.val.d < 0; nelems = (size_t)cst.val.d; break; + case NCT_LDOUBLE: is_neg = cst.val.l < 0; nelems = (size_t)cst.val.l; break; + case NCT_INT32: is_neg = cst.val.i32 < 0; nelems = (size_t)cst.val.i32; break; + case NCT_UINT32: is_neg = 0; nelems = (size_t)cst.val.u32; break; + case NCT_INT64: is_neg = cst.val.i64 < 0; nelems = (size_t)cst.val.i64; break; + case NCT_UINT64: is_neg = 0; nelems = (size_t)cst.val.u64; break; + default: is_neg = 1; + } + if (is_neg) { + printf("Error: the size of an array must be nonnegative"); + // Empty destructor + goto failed; + } + } + // Token is ']' + + if (cur_bottom) { + type_t *tmp = type_new(); + if (!tmp) { + printf("Failed to parse init_declarator_list (create new array type)\n"); + // Empty destructor + goto failed; + } + tmp->typ = TYPE_ARRAY; + tmp->val.array.array_sz = nelems; + tmp->is_incomplete = is_incomplete; + // cur_bottom is a pointer, an array or a function + // We have cur_bottom(old) --> below, we want cur_bottom(old) --> array=cur_bottom(new) --> below + tmp->val.array.typ = + (cur_bottom->typ == TYPE_PTR) ? cur_bottom->val.typ : + (cur_bottom->typ == TYPE_ARRAY) ? cur_bottom->val.array.typ : + cur_bottom->val.fun.ret; + *((cur_bottom->typ == TYPE_PTR) ? &cur_bottom->val.typ : + (cur_bottom->typ == TYPE_ARRAY) ? &cur_bottom->val.array.typ : &cur_bottom->val.fun.ret) = tmp; + cur_bottom = tmp; + } else { + type_t *new_typ = type_new(); + if (!new_typ) { + printf("Failed to parse init_declarator_list (create new array type)\n"); + // Empty destructor + goto failed; + } + new_typ->typ = TYPE_ARRAY; + new_typ->val.array.array_sz = nelems; + new_typ->is_incomplete = is_incomplete; + // We have top(old), we want array=top(new)=cur_bottom(new) --> top(old) + new_typ->val.array.typ = typ; + cur_bottom = typ = new_typ; + } + *tok = proc_next_token(prep); + break; + } else if (tok->tokv.sym == SYM_RPAREN) { + if (!has_ident) { + if (!allow_abstract) { + printf("Error: invalid token ')' before identifier in declaration\n"); + // Empty destructor + goto failed; + } + has_ident = 1; + } + if (vector_size(size_t, nptr_stack) == 1) { + if (!is_init && !is_list) goto rparen_ok_ret; + printf("Error: closing unopened parenthesis in declaration\n"); + // Empty destructor + goto failed; + } + size_t ndecr; + if (cur_bottom) { + ndecr = vector_last(size_t, nptr_stack); + } else { + if (vector_last(size_t, nptr_stack)) { + cur_bottom = typ; + ndecr = vector_last(size_t, nptr_stack) - 1; + } else ndecr = 0; + } + for (size_t i = 0; i < ndecr; ++i) { + cur_bottom = + (cur_bottom->typ == TYPE_PTR) ? cur_bottom->val.typ : + (cur_bottom->typ == TYPE_ARRAY) ? cur_bottom->val.array.typ : + cur_bottom->val.fun.ret; + } + vector_pop(size_t, nptr_stack); + *tok = proc_next_token(prep); + break; + } else if ((is_init && (tok->tokv.sym == SYM_EQ)) + || (tok->tokv.sym == SYM_COMMA) + || (tok->tokv.sym == SYM_SEMICOLON) + || (is_init && (tok->tokv.sym == SYM_LBRACKET))) { + rparen_ok_ret: // Last function argument + if (!allow_abstract && !has_ident) { + printf("Error: invalid symbol '%s' before identifier\n", sym2str[tok->tokv.sym]); + // Empty destructor + goto failed; + } + if (vector_size(size_t, nptr_stack) != 1) { + printf("Error: invalid symbol '%s' (missing parenthesis?)\n", sym2str[tok->tokv.sym]); + // Empty destructor + goto failed; + } + + // Try to free some redundant types + typ = type_try_merge(typ, PDECL_TYPE_SET); + + int validation = validate_storage_type(storage, PDECL_BUILTINS, typ, tok->tokv.sym); + if (!validation) { + // Empty destructor + goto failed; + } + + if (validation == VALIDATION_FUN) { + // Function definition; tok is '{' + if (!is_init || !is_list || has_list) { + // We are not at the top-level or we have an initialization list + printf("Error: invalid function definition\n"); + // Empty destructor + goto failed; + } + // Note that here, dest is a file_t + // No argument in function definition means that the function takes no argument, + // whereas no argument in function declaration means the function takes an unspecified number of arguments + if (typ->val.fun.nargs == (size_t)-1) typ->val.fun.nargs = 0; + + int iret; + char *cident = string_steal(cur_ident); cur_ident = NULL; + khiter_t it = kh_put(type_map, dest->f->decl_map, cident, &iret); + if (iret < 0) { + printf("Failed to add function '%s' to the declaration map\n", cident); + free(cident); + // Empty destructor + goto failed; + } else if (iret == 0) { + printf("Error: function '%s' is already in the declaration map\n", cident); + free(cident); + // Empty destructor + goto failed; + } + + kh_val(dest->f->decl_map, it) = typ; + + // Skip the function body + int nlbraces = 0; + do { + proc_token_del(tok); + *tok = proc_next_token(prep); + if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_LBRACKET)) ++nlbraces; + else if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_RBRACKET)) { + if (nlbraces) --nlbraces; + else goto success; + } + } while (!proc_token_isend(tok)); + printf("Error: unexpected token in function body\n"); + goto failed; + } + + if (storage == STORAGE_TYPEDEF) { + if (!is_init || !is_list) { + // We are not at the top-level (note that storage is set to NONE in function arguments) + printf("Error: invalid function definition\n"); + // Empty destructor + goto failed; + } + // Note that here, dest is a file_t + int iret; + char *cident = string_steal(cur_ident); cur_ident = NULL; + khiter_t it = kh_put(type_map, dest->f->type_map, cident, &iret); + if (iret < 0) { + printf("Failed to add '%s' to the type map\n", cident); + free(cident); + // Empty destructor + goto failed; + } else if (iret == 0) { + if (!type_t_equal(typ, kh_val(dest->f->type_map, it))) { + printf("Error: '%s' is already in the type map with a different type\n", cident); + free(cident); + type_del(typ); + // Empty destructor + goto failed; + } + // We can safely ignore this since we have typedef-ed the same type + free(cident); + type_del(typ); + } else { + kh_val(dest->f->type_map, it) = typ; + } + } else if ((storage != STORAGE_STATIC) && (storage != STORAGE_TLS_STATIC)) { + if (is_init && is_list) { + // static variables/functions are not exposed + int iret; + char *cident = string_steal(cur_ident); cur_ident = NULL; + khiter_t it = kh_put(type_map, dest->f->decl_map, cident, &iret); + if (iret < 0) { + printf("Failed to add '%s' to the declaration map\n", cident); + free(cident); + // Empty destructor + goto failed; + } else if (iret == 0) { + /* if ((storage == STORAGE_NONE) || !type_t_equal(typ, kh_val(dest->f->decl_map, it))) { + printf("Error: '%s' is already in the declaration map (storage=%u)\n", cident, storage); + free(cident); + type_del(typ); + // Empty destructor + goto failed; + } else */ { + printf("Warning: '%s' is already in the declaration map with the same type\n", cident); + free(cident); + type_del(typ); + } + } else { + kh_val(dest->f->decl_map, it) = typ; + } + } else if (!is_init && !is_list) { + dest->argt.dest = typ; + if (cur_ident) string_del(cur_ident); + goto success; + } else if (!is_init && is_list) { + if (!vector_push(st_members, dest->structms.dest, ((st_member_t){.name = cur_ident, .typ = typ, .is_bitfield = 0}))) { + printf("Error: failed to add structure member %s\n", string_content(cur_ident)); + string_del(cur_ident); + // Empty destructor + goto failed; + } + } else { + printf("Internal error: unknown is_init/is_list combination %d%d\n", is_init, is_list); + // Empty destructor + goto failed; + } + } + if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_EQ)) { + // Initialization + if (!is_init) { + printf("Error: unexpected initializer\n"); + goto failed; + } + *tok = proc_next_token(prep); + if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_LBRACKET)) { + // { ... } + int nlbraces = 0; + do { + proc_token_del(tok); + *tok = proc_next_token(prep); + if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_LBRACKET)) ++nlbraces; + else if ((tok->tokt == PTOK_SYM) && (tok->tokv.sym == SYM_RBRACKET)) { + if (nlbraces) --nlbraces; + else break; + } + } while (!proc_token_isend(tok)); + if (proc_token_isend(tok)) { + printf("Error: unexpected token in declaration initializer\n"); + proc_token_del(tok); + goto failed; + } + } else { + expr_t *e = parse_expression(PDECL_STRUCT_MAP, PDECL_TYPE_MAP, PDECL_ENUM_MAP, PDECL_BUILTINS, PDECL_CONST_MAP, PDECL_TYPE_SET, prep, tok, 15); + if (!e) { + printf("Error: invalid declaration initializer\n"); + goto failed; + } + expr_del(e); + } + if ((tok->tokt != PTOK_SYM) || ((tok->tokv.sym != SYM_COMMA) && (tok->tokv.sym != SYM_SEMICOLON))) { + printf("Error: unexpected token in declaration initializer\n"); + proc_token_del(tok); + goto failed; + } + validation = (tok->tokv.sym == SYM_SEMICOLON) ? VALIDATION_LAST_DECL : VALIDATION_DECL; + } + if (validation == VALIDATION_LAST_DECL) goto success; + else { + cur_ident = NULL; has_ident = 0; + typ = base_type; ++typ->nrefs; + cur_bottom = NULL; + vector_last(size_t, nptr_stack) = 0; + *tok = proc_next_token(prep); + break; + } + } else if (!is_init && is_list && (tok->tokv.sym == SYM_COLON)) { + if (vector_size(size_t, nptr_stack) != 1) { + printf("Error: invalid symbol '%s' (missing parenthesis?)\n", sym2str[tok->tokv.sym]); + // Empty destructor + goto failed; + } + // Try to free some redundant types + typ = type_try_merge(typ, PDECL_TYPE_SET); + + // storage == STORAGE_NONE + *tok = proc_next_token(prep); + expr_t *e = parse_expression(dest->structms.struct_map, dest->structms.type_map, dest->structms.enum_map, + dest->structms.builtins, dest->structms.const_map, dest->structms.type_set, + prep, tok, 14); + if (!e) { + goto failed; + } + if ((tok->tokt != PTOK_SYM) || ((tok->tokv.sym != SYM_COMMA) && (tok->tokv.sym != SYM_SEMICOLON))) { + printf("Error: unexpected token in bitfield width\n"); + expr_del(e); + proc_token_del(tok); + goto failed; + } + num_constant_t eval; + if (!eval_expression(e, dest->structms.const_map, &eval)) { + expr_del(e); + // Empty destructor + goto failed; + } + expr_del(e); + + int validation = validate_storage_type(storage, PDECL_BUILTINS, typ, tok->tokv.sym); + if (!validation) { + // Empty destructor + goto failed; + } + + if (validation == VALIDATION_FUN) { + // We are not at the top-level or we have an initialization list + // Should never happen + printf("Error: invalid function definition in structure definition\n"); + // Empty destructor + goto failed; + } + + size_t width; + switch (eval.typ) { + case NCT_INT32: + if (eval.val.i32 < 0) { + printf("Error: invalid negative bitfield width\n"); + goto failed; + } + width = (size_t)eval.val.i32; + break; + case NCT_UINT32: + width = (size_t)eval.val.u32; + break; + case NCT_INT64: + if (eval.val.i64 < 0) { + printf("Error: invalid negative bitfield width\n"); + goto failed; + } + width = (size_t)eval.val.i64; + break; + case NCT_UINT64: + width = (size_t)eval.val.u64; + break; + case NCT_FLOAT: + case NCT_DOUBLE: + case NCT_LDOUBLE: + default: + printf("Error: invalid non-integer bitfield width\n"); + goto failed; + } + + if (!vector_push(st_members, dest->structms.dest, + ((st_member_t){.name = cur_ident, .typ = typ, .is_bitfield = 1, .bitfield_width = width}))) { + printf("Error: failed to add structure member %s\n", string_content(cur_ident)); + string_del(cur_ident); + // Empty destructor + goto failed; + } + if (validation == VALIDATION_LAST_DECL) goto success; + else { + cur_ident = NULL; has_ident = 0; + typ = base_type; ++typ->nrefs; + cur_bottom = NULL; + vector_last(size_t, nptr_stack) = 0; + *tok = proc_next_token(prep); + break; + } + } + /* FALLTHROUGH */ + case PTOK_KEYWORD: + if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_ATOMIC)) { + if (has_ident) { + printf("Error: invalid keyword '_Atomic' after identifier\n"); + proc_token_print(tok); + // Empty destructor + goto failed; + } else if (!vector_last(size_t, nptr_stack)) { + printf("Error: invalid keyword '_Atomic' before symbol '*'\n"); + proc_token_print(tok); + // Empty destructor + goto failed; + } else { + typ->is_atomic = 1; + *tok = proc_next_token(prep); + break; + } + } else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_CONST)) { + if (has_ident) { + printf("Error: invalid keyword 'const' after identifier\n"); + proc_token_print(tok); + // Empty destructor + goto failed; + } else if (!vector_last(size_t, nptr_stack)) { + printf("Error: invalid keyword 'const' before symbol '*'\n"); + proc_token_print(tok); + // Empty destructor + goto failed; + } else { + typ->is_const = 1; + *tok = proc_next_token(prep); + break; + } + } else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_RESTRICT)) { + if (has_ident) { + printf("Error: invalid keyword 'restrict' after identifier\n"); + proc_token_print(tok); + // Empty destructor + goto failed; + } else if (!vector_last(size_t, nptr_stack)) { + printf("Error: invalid keyword 'restrict' before symbol '*'\n"); + proc_token_print(tok); + // Empty destructor + goto failed; + } else { + typ->is_restrict = 1; + *tok = proc_next_token(prep); + break; + } + } else if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_VOLATILE)) { + if (has_ident) { + printf("Error: invalid keyword 'volatile' after identifier\n"); + proc_token_print(tok); + // Empty destructor + goto failed; + } else if (!vector_last(size_t, nptr_stack)) { + printf("Error: invalid keyword 'volatile' before symbol '*'\n"); + proc_token_print(tok); + // Empty destructor + goto failed; + } else { + typ->is_volatile = 1; + *tok = proc_next_token(prep); + break; + } + } + /* FALLTHROUGH */ + case PTOK_INVALID: + case PTOK_NUM: + case PTOK_STRING: + case PTOK_PRAGMA: + case PTOK_EOF: + printf("Error: unexpected token (parse_declarator %d%d%d%d)\n" + "Current state:\n" + " storage: %u\n" + " type: ", is_init, is_list, allow_decl, allow_abstract, storage); + type_print(typ); + printf("\n"); + proc_token_print(tok); + proc_token_del(tok); + goto failed; + } + } + +success: + vector_del(size_t, nptr_stack); + // typ has moved, we must not destroy it + return 1; +failed: + vector_del(size_t, nptr_stack); +failed0: + if (cur_ident) string_del(cur_ident); + type_del(typ); + return 0; +} + +file_t *parse_file(const char *filename, FILE *file) { + char *dirname = strchr(filename, '/') ? strndup(filename, (size_t)(strrchr(filename, '/') - filename)) : NULL; + preproc_t *prep = preproc_new_file(file, dirname, filename); + if (!prep) { + printf("Failed to create the preproc structure\n"); + if (dirname) free(dirname); + return NULL; + } + file_t *ret = file_new(); + if (!ret) { + printf("Failed to create the file structure\n"); + preproc_del(prep); + return NULL; + } + + type_t *typ = type_new(); + if (!typ) { + printf("Failed to create a type info structure\n"); + goto failed; + } + while (1) { + proc_token_t tok = proc_next_token(prep); + if (tok.tokt == PTOK_EOF) { + goto success; + } else if (tok.tokt == PTOK_PRAGMA) { + switch (tok.tokv.pragma.typ) { + case PRAGMA_ALLOW_INTS: { + const char *typenames[] = {BTT_INT_EXTS}; + for (size_t i = 0; i < sizeof typenames / sizeof *typenames; ++i) { + int iret; + char *dup = strdup(typenames[i]); + if (!dup) { + printf("Failed to create a type info structure\n"); + goto failed; + } + type_t *t = ret->builtins[BTT_START_INT_EXT + i]; + khiter_t it = kh_put(type_map, ret->type_map, dup, &iret); + if (iret < 0) { + printf("Failed to add an intrinsic to the type map\n"); + goto failed; + } else if (iret == 0) { + if (!type_t_equal(t, kh_val(ret->type_map, it))) { + printf("Error: %s is already defined\n", dup); + free(dup); + goto failed; + } + free(dup); + } else { + ++t->nrefs; + kh_val(ret->type_map, it) = t; + } + } + break; } + case PRAGMA_MARK_SIMPLE: { + khiter_t it = kh_get(type_map, ret->type_map, string_content(tok.tokv.pragma.val)); + string_del(tok.tokv.pragma.val); + if (it == kh_end(ret->type_map)) { + printf("Invalid explicit_simple pragma: ident is not a typedef\n"); + goto failed; + } + type_t *typ0 = kh_val(ret->type_map, it); + if (typ0->typ != TYPE_STRUCT_UNION) { + printf("Invalid explicit_simple pragma: ident is not a typedef to a structure or union\n"); + goto failed; + } + typ0->val.st->explicit_simple = 1; + break; } + } + } else if (proc_token_iserror(&tok)) { + printf("Error: unexpected error token\n"); + proc_token_del(&tok); + goto failed; + } else { + enum decl_storage storage = STORAGE_NONE; + enum decl_spec spec = SPEC_NONE; + if (!parse_declaration_specifier(ret->struct_map, ret->type_map, ret->enum_map, &ret->builtins, ret->const_map, + ret->type_set, prep, &tok, &storage, &spec, typ)) { + goto failed; + } + if (spec == SPEC_NONE) continue; // Declaration was an assert, typ is unchanged + int ok; + typ = type_try_merge(typ, ret->type_set); + if ((tok.tokt != PTOK_SYM) || (tok.tokv.sym != SYM_SEMICOLON)) { + ok = parse_declarator(&(struct parse_declarator_dest_s){.f = ret}, prep, &tok, storage, typ, 1, 1, 1, 0); + } else { + ok = validate_storage_type(storage, &ret->builtins, typ, tok.tokv.sym); + } + if (!ok) { + goto failed; + } else { + // Current token is ';' (or '}' for functions), ie. end of declaration + type_del(typ); + typ = type_new(); + if (!typ) { + printf("Failed to create a type info structure\n"); + goto failed; + } + } + } + } + +success: + preproc_del(prep); + type_del(typ); + return ret; +failed: + preproc_del(prep); + if (typ) type_del(typ); + file_del(ret); + return NULL; +} diff --git a/wrapperhelper/src/parse.h b/wrapperhelper/src/parse.h new file mode 100644 index 000000000..8c7d54b30 --- /dev/null +++ b/wrapperhelper/src/parse.h @@ -0,0 +1,14 @@ +#pragma once + +#ifndef PARSE_H +#define PARSE_H + +#include + +#include "lang.h" + +void dump_prepare(const char *filename, FILE *file); +void dump_preproc(const char *filename, FILE *file); +file_t *parse_file(const char *filename, FILE *file); + +#endif // PARSE_H diff --git a/wrapperhelper/src/prepare.c b/wrapperhelper/src/prepare.c new file mode 100644 index 000000000..090da0b08 --- /dev/null +++ b/wrapperhelper/src/prepare.c @@ -0,0 +1,372 @@ +#include "prepare.h" + +#include + +struct prepare_s { + FILE *f; + int buf[4]; + int buf_len; // <= 4 (though 3 *should* be enough) + char *srcn; + enum prepare_state { + PREPST_NONE = 0, + PREPST_NL, + PREPST_HASH, + PREPST_INCL, + PREPST_DEF, + PREPST_DEFID, + } st; +}; + +prepare_t *prepare_new_file(FILE *f, const char *filename) { + prepare_t *ret = malloc(sizeof *ret); + if (!ret) { + fclose(f); + return NULL; + } + *ret = (prepare_t){ + .f = f, + .buf = {0, 0, 0}, + .buf_len = 0, + .srcn = strdup(filename), + .st = PREPST_NL, + }; + return ret; +} + +void prepare_del(prepare_t *prep) { + if (prep->f) fclose(prep->f); + if (prep->srcn) free(prep->srcn); + free(prep); +} + +static int get_char(prepare_t *src) { +start_get_char: + int c = src->buf_len ? src->buf[--src->buf_len] : getc(src->f); + src->buf_len = 0; + if (c == '\\') { + c = src->buf_len ? src->buf[--src->buf_len] : getc(src->f); + if (c == '\n') goto start_get_char; + src->buf[src->buf_len++] = c; + return '\\'; + } + return c; +} +// Do not call this more than twice in a row if the last character retrieved is '\\' +static void unget_char(prepare_t *src, int c) { + src->buf[src->buf_len++] = c; +} + +static void fill_ident(prepare_t *src, string_t *buf) { + while (1) { + int c = get_char(src); + if ((c == '_') || ((c >= '0') && (c <= '9')) || ((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z'))) { + string_add_char(buf, (char)c); + } else { + unget_char(src, c); + return; + } + } +} + +static void fill_num(prepare_t *src, string_t *buf) { + int started_exp = 0; + while (1) { + int c = get_char(src); + if ((c == '_') || (c == '.') || ((c >= '0') && (c <= '9')) || ((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) + || (started_exp && ((c == '+') || (c == '-')))) { + started_exp = (c == 'e') || (c == 'E') || (c == 'p') || (c == 'P'); + string_add_char(buf, (char)c); + } else { + unget_char(src, c); + return; + } + } +} + +static void fill_str(prepare_t *src, string_t *buf, char end_c, int can_esc) { + int has_esc = 0; + while (1) { + int c = get_char(src); + if (has_esc && (c >= 0) && (c <= 0x7F) && (c != '\n')) { + // Not technically standard compliant (should support \ooo, \x..., \u..., \U...) + // Since we don't really care about parsing the content, only the delimiters, this is good enough + string_add_char(buf, '\\'); + string_add_char(buf, (char)c); + has_esc = 0; + } else if (c == '\\') { + if (can_esc) { + has_esc = 1; + } else { + string_add_char(buf, '\\'); + } + } else if ((c >= 0) && (c <= 0x7F) && (c != end_c)) { + has_esc = 0; + string_add_char(buf, (char)c); + } else { + if (has_esc) { + // c is invalid or a '\n', or can_esc = 0 and c = end_c + string_add_char(buf, '\\'); + } + if (c != end_c) + unget_char(src, c); + return; + } + } +} + +#define BASE_NSYMS 25 +static const struct symbs_s { + char c; + enum token_sym_type_e sym; + int nnext; + const struct symbs_s *next; +} *symbs = (struct symbs_s[BASE_NSYMS]){ +#define TERM(ch, t) { .c = ch, .sym = t, .nnext = 0, .next = NULL } +#define NONTERM(ch, t, n, ...) { .c = ch, .sym = t, .nnext = n, .next = (struct symbs_s[n]){__VA_ARGS__} } + // Only '..' must have a sym > LAST_SYM; change next_token if this is not the case + NONTERM('.', SYM_DOT, 1, NONTERM('.', LAST_SYM + 1, 1, TERM('.', SYM_VARIADIC))), + TERM('{', SYM_LBRACKET), + TERM('}', SYM_RBRACKET), + TERM('[', SYM_LSQBRACKET), + TERM(']', SYM_RSQBRACKET), + TERM('(', SYM_LPAREN), + TERM(')', SYM_RPAREN), + NONTERM('#', SYM_HASH, 1, TERM('#', SYM_HASHHASH)), + TERM(';', SYM_SEMICOLON), + NONTERM(':', SYM_COLON, 1, TERM(':', SYM_COLONCOLON)), + TERM('?', SYM_QUESTION), + TERM('~', SYM_TILDE), + NONTERM('!', SYM_EXCL, 1, TERM('=', SYM_EXCLEQ)), + NONTERM('+', SYM_PLUS, 2, TERM('=', SYM_PLUSEQ), TERM('+', SYM_PLUSPLUS)), + NONTERM('-', SYM_DASH, 3, TERM('=', SYM_DASHEQ), TERM('-', SYM_DASHDASH), TERM('>', SYM_DASHGT)), + NONTERM('*', SYM_STAR, 1, TERM('=', SYM_STAREQ)), + NONTERM('/', SYM_SLASH, 1, TERM('=', SYM_SLASHEQ)), + NONTERM('%', SYM_PERCENT, 1, TERM('=', SYM_PERCENTEQ)), + NONTERM('^', SYM_HAT, 1, TERM('=', SYM_HATEQ)), + NONTERM('&', SYM_AMP, 2, TERM('=', SYM_AMPEQ), TERM('&', SYM_AMPAMP)), + NONTERM('|', SYM_PIPE, 2, TERM('=', SYM_PIPEEQ), TERM('|', SYM_PIPEPIPE)), + NONTERM('=', SYM_EQ, 1, TERM('=', SYM_EQEQ)), + NONTERM('<', SYM_LT, 2, TERM('=', SYM_LTEQ), NONTERM('<', SYM_LTLT, 1, TERM('=', SYM_LTLTEQ))), + NONTERM('>', SYM_GT, 2, TERM('=', SYM_GTEQ), NONTERM('>', SYM_GTGT, 1, TERM('=', SYM_GTGTEQ))), + TERM(',', SYM_COMMA), +#undef NONTERM +#undef TERM +}; + +preproc_token_t pre_next_token(prepare_t *src, int allow_comments) { +start_next_token: + int c = get_char(src); + if (c == EOF) { + if (src->st == PREPST_NL) { + return (preproc_token_t){ + .tokt = PPTOK_EOF, + .tokv.c = (char)c + }; + } else { + // Force newline at EOF + unget_char(src, c); + src->st = PREPST_NL; + return (preproc_token_t){ + .tokt = PPTOK_NEWLINE, + .tokv.c = (char)c + }; + } + } + + if (src->st == PREPST_INCL && (c == '<')) { + src->st = PREPST_NONE; + preproc_token_t ret; + ret.tokt = PPTOK_INCL; + ret.tokv.sisstr = 0; + ret.tokv.sstr = string_new(); + fill_str(src, ret.tokv.sstr, '>', 0); + return ret; + } + if (c == '\'') { + src->st = PREPST_NONE; + preproc_token_t ret; + ret.tokt = PPTOK_STRING; + ret.tokv.sisstr = 0; + ret.tokv.sstr = string_new_cap(1); // Usually only one character is inside a char literal + fill_str(src, ret.tokv.sstr, '\'', 1); + return ret; + } + if (c == '"') { + preproc_token_t ret; + ret.tokt = (src->st == PREPST_INCL) ? PPTOK_INCL : PPTOK_STRING; + src->st = PREPST_NONE; + ret.tokv.sisstr = 1; + ret.tokv.sstr = string_new(); + fill_str(src, ret.tokv.sstr, '"', ret.tokt == PPTOK_STRING); + return ret; + } + if ((c == ' ') || (c == '\f') || (c == '\t') || (c == '\v')) { + if (src->st == PREPST_DEFID) { + src->st = PREPST_NONE; + return (preproc_token_t){ + .tokt = PPTOK_BLANK, + .tokv.c = (char)c + }; + } else goto start_next_token; + } + if (c == '\n') { + src->st = PREPST_NL; + return (preproc_token_t){ + .tokt = PPTOK_NEWLINE, + .tokv.c = (char)c + }; + } + if ((c == '_') || ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'))) { + preproc_token_t ret; + ret.tokt = PPTOK_IDENT; + ret.tokv.str = string_new_cap(1); + string_add_char(ret.tokv.str, (char)c); + fill_ident(src, ret.tokv.str); + src->st = + ((src->st == PREPST_HASH) && (!strcmp(string_content(ret.tokv.str), "include"))) ? PREPST_INCL : + ((src->st == PREPST_HASH) && (!strcmp(string_content(ret.tokv.str), "include_next"))) ? PREPST_INCL : + ((src->st == PREPST_HASH) && (!strcmp(string_content(ret.tokv.str), "define"))) ? PREPST_DEF : + (src->st == PREPST_DEF) ? PREPST_DEFID : + PREPST_NONE; + return ret; + } + if ((c >= '0') && (c <= '9')) { + src->st = PREPST_NONE; + preproc_token_t ret; + ret.tokt = PPTOK_NUM; + ret.tokv.str = string_new_cap(1); + string_add_char(ret.tokv.str, (char)c); + fill_num(src, ret.tokv.str); + return ret; + } + if (c == '.') { + c = get_char(src); + if ((c >= '0') && (c <= '9')) { + src->st = PREPST_NONE; + preproc_token_t ret; + ret.tokt = PPTOK_NUM; + ret.tokv.str = string_new_cap(2); + string_add_char(ret.tokv.str, '.'); + string_add_char(ret.tokv.str, (char)c); + fill_num(src, ret.tokv.str); + return ret; + } else { + unget_char(src, c); + c = '.'; + } + } + if (c == '/') { + c = get_char(src); + if (c == '/') { + if (allow_comments) { + src->st = PREPST_NONE; + return (preproc_token_t){ + .tokt = PPTOK_START_LINE_COMMENT, + .tokv.c = '/' + }; + } + + do { + c = get_char(src); + } while ((c != EOF) && (c != '\n')); + if (c != EOF) { + if (src->st == PREPST_NL) + goto start_next_token; + else { + src->st = PREPST_NL; + return (preproc_token_t){ + .tokt = PPTOK_NEWLINE, + .tokv.c = (char)c + }; + } + } + + src->st = PREPST_NONE; + printf("Unfinished comment while preparing %s\n", src->srcn); + return (preproc_token_t){ + .tokt = PPTOK_INVALID, + .tokv.c = (char)c + }; + } else if (c == '*') { + c = get_char(src); + int last_star = 0; + while ((c != EOF) && (!last_star || (c != '/'))) { + last_star = c == '*'; + c = get_char(src); + } + if (c != EOF) goto start_next_token; + + src->st = PREPST_NONE; + printf("Unfinished comment while preparing %s\n", src->srcn); + return (preproc_token_t){ + .tokt = PPTOK_INVALID, + .tokv.c = (char)c + }; + } else { + unget_char(src, c); + c = '/'; + } + } + + struct symbs_s const *sym = NULL; + for (int i = 0; i < BASE_NSYMS; ++i) { + if (c == symbs[i].c) { + sym = &symbs[i]; + break; + } + } + if (sym) { + while (sym->nnext) { + c = get_char(src); + int found = 0; + for (int i = 0; i < sym->nnext; ++i) { + if (c == sym->next[i].c) { + found = 1; + sym = &sym->next[i]; + break; + } + } + if (!found) { + unget_char(src, c); + break; + } + } + if (sym->sym == LAST_SYM + 1) { + unget_char(src, sym->c); + sym = &symbs[0]; // This is where no check is made (see comment in the definition of symbs) + } + src->st = ((src->st == PREPST_NL) && (sym->sym == SYM_HASH)) ? PREPST_HASH : PREPST_NONE; + return (preproc_token_t){ + .tokt = PPTOK_SYM, + .tokv.sym = sym->sym + }; + } + + src->st = PREPST_NONE; + printf("Invalid character 0x%X (%c) while preparing %s\n", (unsigned)c, (c >= 0x20) && (c < 127) ? c : '?', src->srcn); + return (preproc_token_t){ + .tokt = PPTOK_INVALID, + .tokv.c = (char)c + }; +} +preproc_token_t pre_next_newline_token(prepare_t *src) { +start_next_token: + int c = get_char(src); + if (c == EOF) { + // Force newline at EOF + unget_char(src, c); + src->st = PREPST_NL; + return (preproc_token_t){ + .tokt = PPTOK_NEWLINE, + .tokv.c = (char)c + }; + } + if (c == '\n') { + src->st = PREPST_NL; + return (preproc_token_t){ + .tokt = PPTOK_NEWLINE, + .tokv.c = (char)c + }; + } + goto start_next_token; +} diff --git a/wrapperhelper/src/prepare.h b/wrapperhelper/src/prepare.h new file mode 100644 index 000000000..ccb57a5fc --- /dev/null +++ b/wrapperhelper/src/prepare.h @@ -0,0 +1,18 @@ +#pragma once + +#ifndef PREPARE_H +#define PREPARE_H + +#include + +#include "cstring.h" +#include "lang.h" + +typedef struct prepare_s prepare_t; + +prepare_t *prepare_new_file(FILE *f, const char *filename); // Takes ownership of f +void prepare_del(prepare_t *src); +preproc_token_t pre_next_token(prepare_t *src, int allow_comments); +preproc_token_t pre_next_newline_token(prepare_t *src); // In a comment ignore everything until the EOL or EOF + +#endif // PREPARE_H diff --git a/wrapperhelper/src/preproc.c b/wrapperhelper/src/preproc.c new file mode 100644 index 000000000..ba99f754b --- /dev/null +++ b/wrapperhelper/src/preproc.c @@ -0,0 +1,2901 @@ +// I think this file is too big for GCC to handle properly, there are curious false-positive analyzer warnings +// that didn't appear before adding preproc_eval +#include "preproc.h" + +#include +#include + +#include "cstring.h" +#include "khash.h" +#include "prepare.h" + +typedef struct mtoken_s { + enum mtoken_e { + MTOK_TOKEN, + MTOK_ARG, + MTOK_STRINGIFY, + MTOK_CONCAT, + } typ; + union { + preproc_token_t tok; + unsigned argid; + struct { struct mtoken_s *l, *r; } concat; + } val; +} mtoken_t; +KHASH_MAP_INIT_STR(argid_map, unsigned) +void argid_map_del(khash_t(argid_map) *args) { + kh_cstr_t str; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" + kh_foreach_key(args, str, free((void*)str)) +#pragma GCC diagnostic pop + kh_destroy(argid_map, args); +} +static mtoken_t *mtoken_new_token(preproc_token_t tok) { + mtoken_t *ret = malloc(sizeof *ret); + if (!ret) return NULL; + ret->typ = MTOK_TOKEN; + ret->val.tok = tok; + return ret; +} +static mtoken_t *mtoken_new_arg(unsigned argid, int as_string) { + mtoken_t *ret = malloc(sizeof *ret); + if (!ret) return NULL; + ret->typ = as_string ? MTOK_STRINGIFY : MTOK_ARG; + ret->val.argid = argid; + return ret; +} +static mtoken_t *mtoken_new_concat(mtoken_t *l, mtoken_t *r) { // Takes ownership of l and r + mtoken_t *ret = malloc(sizeof *ret); + if (!ret) return NULL; + ret->typ = MTOK_CONCAT; + ret->val.concat.l = l; + ret->val.concat.r = r; + return ret; +} +static void mtoken_del(mtoken_t **tok) { + switch ((*tok)->typ) { + case MTOK_TOKEN: + preproc_token_del(&(*tok)->val.tok); + free(*tok); + return; + + case MTOK_CONCAT: + mtoken_del(&(*tok)->val.concat.l); + mtoken_del(&(*tok)->val.concat.r); + free(*tok); + return; + + case MTOK_ARG: + case MTOK_STRINGIFY: + free(*tok); + return; + } +} + +static inline void print_macro_tok(mtoken_t *m) { + switch (m->typ) { + case MTOK_TOKEN: + printf("token type %u", m->val.tok.tokt); + if (m->val.tok.tokt == PPTOK_IDENT) printf(" IDENT '%s'", string_content(m->val.tok.tokv.str)); + if (m->val.tok.tokt == PPTOK_NUM ) printf(" NUM %s", string_content(m->val.tok.tokv.str)); + if (m->val.tok.tokt == PPTOK_SYM) printf(" SYM %s", sym2str[m->val.tok.tokv.sym]); + return; + + case MTOK_ARG: + printf("argument %u", m->val.argid); + return; + + case MTOK_CONCAT: + printf("concat {"); + print_macro_tok(m->val.concat.l); + printf("} {"); + print_macro_tok(m->val.concat.r); + printf("}"); + return; + + case MTOK_STRINGIFY: + printf("string argument %u", m->val.argid); + return; + } +} + +VECTOR_DECLARE_STATIC(mtoken, mtoken_t*) +VECTOR_IMPL_STATIC(mtoken, mtoken_del) + +typedef struct macro_s { + int is_funlike; + int has_varargs; + unsigned nargs; + VECTOR(mtoken) *toks; +} macro_t; + +KHASH_MAP_INIT_STR(macros_map, macro_t) +KHASH_SET_INIT_STR(string_set) +static void macro_del(macro_t *m) { + vector_del(mtoken, m->toks); +} +static void macros_map_del(khash_t(macros_map) *args) { + kh_cstr_t str; + macro_t *it; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" + kh_foreach_key_value_ref(args, str, it, free((void*)str); macro_del(it)) +#pragma GCC diagnostic pop + kh_destroy(macros_map, args); +} +static void macros_set_del(khash_t(string_set) *strset) { + kh_cstr_t str; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" + kh_foreach_key(strset, str, free((void*)str)) +#pragma GCC diagnostic pop + kh_destroy(string_set, strset); +} + +typedef struct ppsource_s { + enum ppsrc_e { + PPSRC_PREPARE = 0, + PPSRC_PPTOKEN, + PPSRC_PPTOKENS, // Currently, only pushed after macro expansion when the expansion is not empty + PPSRC_PTOKEN, + } srct; + union { + struct { + prepare_t *st; + char *old_dirname; + char *old_filename; + _Bool was_sys; + size_t old_pathno; + // Note: char is used for the depths, which gives a max depth of 256 #ifs before the program silently bugs + unsigned char entered_next_ok_cond; + unsigned char ok_depth, cond_depth; + } prep; + preproc_token_t pptok; + struct { + VECTOR(preproc) *toks; // Cannot contain a #if, #else, ... statement, they *must* come from a prepare_t + size_t idx; + } pptoks; + proc_token_t ptok; // Added by proc_unget_token + } srcv; +} ppsource_t; +static void ppsource_del(ppsource_t *src) { + switch (src->srct) { + case PPSRC_PREPARE: + // st may be NULL if we failed to create the prepare_t* in preproc_new_file() + if (src->srcv.prep.st) prepare_del(src->srcv.prep.st); + if (src->srcv.prep.old_dirname) free(src->srcv.prep.old_dirname); + if (src->srcv.prep.old_filename) free(src->srcv.prep.old_filename); + return; + + case PPSRC_PPTOKEN: + preproc_token_del(&src->srcv.pptok); + return; + + case PPSRC_PPTOKENS: + // Do not delete the tokens that escaped, only the remaining tokens and the vector + vector_del_free_from(preproc, src->srcv.pptoks.toks, src->srcv.pptoks.idx); + return; + + case PPSRC_PTOKEN: + proc_token_del(&src->srcv.ptok); + return; + } +} +#define PREPARE_NEW_FILE(f, fn, old_fn, old_dn, was_sys_, old_no) (ppsource_t){ .srct = PPSRC_PREPARE, \ + .srcv.prep = { .st = prepare_new_file((f), (fn)), .old_dirname = (old_dn), .old_filename = (old_fn), .was_sys = was_sys_, \ + .old_pathno = old_no, .entered_next_ok_cond = 0, .ok_depth = 0, .cond_depth = 0 } } +VECTOR_DECLARE_STATIC(ppsource, ppsource_t) +VECTOR_IMPL_STATIC(ppsource, ppsource_del) + +struct preproc_s { + VECTOR(ppsource) *prep; + enum preproc_state_e { + PPST_NONE, + PPST_NL, + } st; + khash_t(macros_map) *macros_map; + khash_t(string_set) *macros_defined, *macros_used; + char *dirname; + char *cur_file; + _Bool is_sys; + size_t cur_pathno; +}; + +void preproc_del(preproc_t *src) { + vector_del(ppsource, src->prep); + macros_set_del(src->macros_used); + macros_set_del(src->macros_defined); + macros_map_del(src->macros_map); + if (src->dirname) free(src->dirname); + if (src->cur_file) free(src->cur_file); + free(src); +} + +static preproc_token_t ppsrc_next_token(preproc_t *src) { + switch (vector_last(ppsource, src->prep).srct) { + case PPSRC_PREPARE: + return pre_next_token(vector_last(ppsource, src->prep).srcv.prep.st, 0); + + case PPSRC_PPTOKEN: { + preproc_token_t ret = vector_last(ppsource, src->prep).srcv.pptok; + vector_pop_nodel(ppsource, src->prep); + return ret; } + + case PPSRC_PPTOKENS: { + ppsource_t *tmp = &vector_last(ppsource, src->prep); + preproc_token_t ret = vector_content(preproc, tmp->srcv.pptoks.toks)[tmp->srcv.pptoks.idx++]; + if (tmp->srcv.pptoks.idx >= vector_size(preproc, tmp->srcv.pptoks.toks)) { + vector_del_freed(preproc, tmp->srcv.pptoks.toks); + vector_pop_nodel(ppsource, src->prep); + } + return ret; } + + case PPSRC_PTOKEN: + default: + return (preproc_token_t){ .tokt = PPTOK_INVALID, .tokv.c = 0 }; + } +} + +preproc_t *preproc_new_file(FILE *f, char *dirname, const char *filename) { + preproc_t *ret = malloc(sizeof *ret); + if (!ret) { + fclose(f); + return NULL; + } + ret->macros_map = kh_init(macros_map); + if (!ret->macros_map) { + fclose(f); + free(ret); + return NULL; + } + ret->macros_defined = kh_init(string_set); + if (!ret->macros_defined) { + kh_destroy(macros_map, ret->macros_map); + fclose(f); + free(ret); + return NULL; + } + ret->macros_used = kh_init(string_set); + if (!ret->macros_used) { + kh_destroy(macros_map, ret->macros_map); + kh_destroy(string_set, ret->macros_defined); + fclose(f); + free(ret); + return NULL; + } + ret->prep = vector_new_cap(ppsource, 1); + if (!ret->prep) { + kh_destroy(macros_map, ret->macros_map); + kh_destroy(string_set, ret->macros_defined); + kh_destroy(string_set, ret->macros_used); + fclose(f); + free(ret); + return NULL; + } + ret->dirname = NULL; + // ret can now be deleted by preproc_del + if (!vector_push(ppsource, ret->prep, PREPARE_NEW_FILE(f, filename, NULL, NULL, 0, 0))) { + preproc_del(ret); + return NULL; + } + if (!vector_last(ppsource, ret->prep).srcv.prep.st) { + preproc_del(ret); + return NULL; + } + ret->st = PPST_NL; + ret->is_sys = 0; + ret->dirname = dirname; + ret->cur_file = strdup(filename); + ret->cur_pathno = 0; + return ret; +} + +const char *incl_paths[] = { + "include-fixed", + "/usr/lib/gcc/x86_64-pc-linux-gnu/14.2.1/include", + "/usr/local/include", + "/usr/lib/gcc/x86_64-pc-linux-gnu/14.2.1/include-fixed", + "/usr/include", + "/usr/include/tirpc", +}; +static int try_open_dir(preproc_t *src, string_t *filename) { + size_t fnlen = string_len(filename); + size_t incl_len = src->dirname ? strlen(src->dirname) : 1; + char *fn = malloc(incl_len + fnlen + 2); + if (!fn) return 0; + if (src->dirname) { + memcpy(fn, src->dirname, incl_len); + fn[incl_len] = '/'; + } else { + fn[0] = '.'; + fn[1] = '/'; + } + strcpy(fn + incl_len + 1, string_content(filename)); + FILE *f = fopen(fn, "r"); + // printf("Trying %s: %p\n", fn, f); + int ret; + if (f) { + char *new_dirname = strchr(fn, '/') ? strndup(fn, (size_t)(strrchr(fn, '/') - fn)) : NULL; + ret = vector_push(ppsource, src->prep, PREPARE_NEW_FILE(f, fn, src->cur_file, src->dirname, src->is_sys, src->cur_pathno)); + if (ret) { + src->is_sys = 0; + src->cur_file = fn; + src->dirname = new_dirname; + src->cur_pathno = 0; + } + } else { + free(fn); + ret = 0; + } + return ret; +} +static int try_open_sys(preproc_t *src, string_t *filename, size_t array_off) { + size_t fnlen = string_len(filename); + for (; array_off < sizeof incl_paths / sizeof *incl_paths; ++array_off) { + size_t incl_len = strlen(incl_paths[array_off]); + char *fn = malloc(incl_len + fnlen + 2); + if (!fn) return 0; + memcpy(fn, incl_paths[array_off], incl_len); + fn[incl_len] = '/'; + strcpy(fn + incl_len + 1, string_content(filename)); + FILE *f = fopen(fn, "r"); + // printf("Trying %s: %p\n", fn, f); + if (f) { + char *new_dirname = strchr(fn, '/') ? strndup(fn, (size_t)(strrchr(fn, '/') - fn)) : NULL; + int ret = vector_push(ppsource, src->prep, PREPARE_NEW_FILE(f, fn, src->cur_file, src->dirname, src->is_sys, src->cur_pathno)); + if (ret) { + src->is_sys = 1; + src->cur_file = fn; + src->dirname = new_dirname; + src->cur_pathno = array_off + 1; + } + return ret; + } + free(fn); + } + return 0; +} + +static void preprocs_del(VECTOR(preproc) **p) { + if (!*p) return; + vector_del(preproc, *p); +} +VECTOR_DECLARE_STATIC(preprocs, VECTOR(preproc)*) +VECTOR_IMPL_STATIC(preprocs, preprocs_del) + +static VECTOR(preproc) *proc_do_expand(const khash_t(macros_map) *macros, const VECTOR(preproc) *toks, + khash_t(string_set) *solved_macros, khash_t(string_set) *opt_used_macros); + // Does not take any ownership, returns a vector with independent ownerships + // opt_used_macros is NULL in regular expansion, non-NULL in #if-expansions +static VECTOR(preproc) * + proc_solve_macro(const khash_t(macros_map) *macros, char *mname, const macro_t *m, VECTOR(preprocs) *margs, + khash_t(string_set) *solved_macros, khash_t(string_set) *opt_used_macros); + // Moves mname to solved_macros or frees mname, returns a vector with independent ownerships + // margs may be NULL if m->is_funlike is false + // May change margs if m->has_varargs, but takes no ownership + // opt_used_macros is NULL in regular expansion, non-NULL in #if-expansions + +static VECTOR(preproc) * +proc_solve_macro(const khash_t(macros_map) *macros, char *mname, const macro_t *m, VECTOR(preprocs) *margs, + khash_t(string_set) *solved_macros, khash_t(string_set) *opt_used_macros) { + if (m->is_funlike && !margs) { + printf("is_funlike && !margs>\n"); + free(mname); + return NULL; + } + if (m->is_funlike + && (m->nargs != vector_size(preprocs, margs)) // General case + && (!m->has_varargs || (m->nargs > vector_size(preprocs, margs))) // Variadics + && (m->nargs || ((vector_size(preprocs, margs) == 1) && (vector_size(preproc, vector_last(preprocs, margs)) == 0)))) { // Zero argument + printf("Invalid argument count for macro %s\n", mname); + free(mname); + return NULL; + } + if (m->has_varargs) { + // Change margs to have the correct __VA_ARGS__ (argument m->nargs) + if (m->nargs == vector_size(preprocs, margs)) { + // No varargs, so add an empty one + VECTOR(preproc) *marg = vector_new(preproc); + if (!marg) { + printf("Failed to create __VA_ARGS__ while expanding %s\n", mname); + free(mname); + return NULL; + } + if (!vector_push(preprocs, margs, marg)) { + printf("Failed to add __VA_ARGS__ while expanding %s\n", mname); + vector_del(preproc, marg); + free(mname); + return NULL; + } + } else if (m->nargs < vector_size(preprocs, margs) - 1) { + // Too many arguments, merge them with commas + VECTOR(preproc) *mvarg = vector_content(preprocs, margs)[m->nargs]; + size_t size0 = vector_size(preproc, mvarg); + vector_for_from(preprocs, it, margs, m->nargs + 1) { + if (!vector_push(preproc, mvarg, ((preproc_token_t){.tokt = PPTOK_SYM, .tokv.sym = SYM_COMMA}))) { + printf("Failed to add comma to __VA_ARGS__ while expanding %s\n", mname); + vector_pop_nodel_slice(preproc, mvarg, vector_size(preproc, mvarg) - size0); + free(mname); + return NULL; + } + if (!vector_push_vec(preproc, mvarg, *it)) { + printf("Failed to add extra argument to __VA_ARGS__ while expanding %s\n", mname); + vector_pop_nodel_slice(preproc, mvarg, vector_size(preproc, mvarg) - size0); + free(mname); + return NULL; + } + } + } + } + // Avoid 0-allocations + VECTOR(preproc) **margs2 = calloc(margs ? (vector_size(preprocs, margs) ? vector_size(preprocs, margs) : 1) : 1, sizeof *margs2); + if (!margs2) { + printf("Memory error while expanding %s\n", mname); + free(mname); + return NULL; + } + VECTOR(preproc) *ret = vector_new(preproc); + if (!ret) { + printf("Memory error while expanding %s\n", mname); + free(margs2); + free(mname); + return NULL; + } + + VECTOR(mtoken) *st = vector_new_cap(mtoken, vector_size(mtoken, m->toks)); + if (!st) { + printf("Memory error while expanding %s\n", mname); + vector_del(preproc, ret); + free(margs2); + free(mname); + return NULL; + } + vector_for_rev(mtoken, mtok, m->toks) { + vector_push(mtoken, st, *mtok); + } + int need_concat = 0, concat_cur = 0; + while (vector_size(mtoken, st)) { + mtoken_t *mtok = vector_last(mtoken, st); + switch (mtok->typ) { + case MTOK_CONCAT: { + vector_last(mtoken, st) = mtok->val.concat.r; + if (!vector_push(mtoken, st, mtok->val.concat.l)) { + vector_del(preproc, ret); + ret = NULL; + goto solve_done; + } + ++need_concat; + break; } + + case MTOK_TOKEN: { + int do_add = 1; + if (concat_cur == 2) { + preproc_token_t *tok1 = &vector_last(preproc, ret); // Guaranteed to exist + preproc_token_t *tok2 = &mtok->val.tok; + if (((tok1->tokt == PPTOK_IDENT) || (tok1->tokt == PPTOK_IDENT_UNEXP) || (tok1->tokt == PPTOK_NUM)) + && ((tok2->tokt == PPTOK_IDENT) || (tok2->tokt == PPTOK_IDENT_UNEXP) || (tok2->tokt == PPTOK_NUM))) { + do_add = 0; + // TODO: check if concat is possible, what behaviour should be in the case below + if (tok1->tokt == PPTOK_IDENT_UNEXP) tok1->tokt = PPTOK_IDENT; + if (!string_add_string(tok1->tokv.str, tok2->tokv.str)) { + printf("Memory error while expanding %s\n", mname); + vector_del(preproc, ret); + ret = NULL; + goto solve_done; + } + } else { + printf("Warning: unsupported concatenation between token type %u and %u while expanding %s\n", tok1->tokt, tok2->tokt, mname); + } + } + if (do_add) { + preproc_token_t tok2; + preproc_token_t *tok1 = &mtok->val.tok; + switch (tok1->tokt) { + case PPTOK_INVALID: + case PPTOK_NEWLINE: + case PPTOK_BLANK: + case PPTOK_START_LINE_COMMENT: + case PPTOK_EOF: tok2 = (preproc_token_t){.tokt = tok1->tokt, .tokv.c = tok1->tokv.c}; break; + case PPTOK_SYM: tok2 = (preproc_token_t){.tokt = tok1->tokt, .tokv.sym = tok1->tokv.sym}; break; + case PPTOK_IDENT: + case PPTOK_IDENT_UNEXP: + case PPTOK_NUM: { + string_t *dup = string_dup(tok1->tokv.str); + if (!dup) { + printf("Failed to duplicate string while expanding %s\n", mname); + vector_del(preproc, ret); + ret = NULL; + goto solve_done; + } + tok2 = (preproc_token_t){.tokt = tok1->tokt, .tokv.str = dup}; + break; } + case PPTOK_INCL: + case PPTOK_STRING: { + string_t *dup = string_dup(tok1->tokv.sstr); + if (!dup) { + printf("Failed to duplicate string while expanding %s\n", mname); + vector_del(preproc, ret); + ret = NULL; + goto solve_done; + } + tok2 = (preproc_token_t){.tokt = tok1->tokt, .tokv.sstr = dup, .tokv.sisstr = tok1->tokv.sisstr}; + break; } + } + if (!vector_push(preproc, ret, tok2)) { + preproc_token_del(&tok2); + vector_del(preproc, ret); + ret = NULL; + goto solve_done; + } + } + vector_pop_nodel(mtoken, st); + if (need_concat) { + concat_cur = 2; + --need_concat; + } else concat_cur = 0; + break; } + + case MTOK_ARG: { + VECTOR(preproc) *toks_to_add; + if (!need_concat && !concat_cur) { + if (!margs2[mtok->val.argid]) { + margs2[mtok->val.argid] = proc_do_expand(macros, vector_content(preprocs, margs)[mtok->val.argid], solved_macros, opt_used_macros); + } + toks_to_add = margs2[mtok->val.argid]; + } else { + toks_to_add = vector_content(preprocs, margs)[mtok->val.argid]; + } + size_t tta_start = 0, len = vector_size(preproc, toks_to_add); + if (len && (concat_cur == 2)) { + preproc_token_t *tok1 = &vector_last(preproc, ret); // Guaranteed to exist + preproc_token_t *tok2 = vector_begin(preproc, toks_to_add); + if (((tok1->tokt == PPTOK_IDENT) || (tok1->tokt == PPTOK_IDENT_UNEXP) || (tok1->tokt == PPTOK_NUM)) + && ((tok2->tokt == PPTOK_IDENT) || (tok2->tokt == PPTOK_IDENT_UNEXP) || (tok2->tokt == PPTOK_NUM))) { + tta_start = 1; --len; + // TODO: check if concat is possible, what behaviour should be in the case below + if ((tok2->tokt == PPTOK_IDENT_UNEXP) && (tok1->tokt == PPTOK_IDENT)) tok1->tokt = PPTOK_IDENT; + if (!string_add_string(tok1->tokv.str, tok2->tokv.str)) { + printf("Memory error while expanding %s\n", mname); + vector_del(preproc, ret); + ret = NULL; + goto solve_done; + } + } else { + printf("Warning: unsupported concatenation between token type %u and %u while expanding %s\n", tok1->tokt, tok2->tokt, mname); + } + } + if (len) { + preproc_token_t tok2; + for (preproc_token_t *tok1 = vector_begin(preproc, toks_to_add) + tta_start; tok1 != vector_end(preproc, toks_to_add); ++tok1){ + switch (tok1->tokt) { + case PPTOK_INVALID: + case PPTOK_NEWLINE: + case PPTOK_BLANK: + case PPTOK_START_LINE_COMMENT: + case PPTOK_EOF: tok2 = (preproc_token_t){.tokt = tok1->tokt, .tokv.c = tok1->tokv.c}; break; + case PPTOK_SYM: tok2 = (preproc_token_t){.tokt = tok1->tokt, .tokv.sym = tok1->tokv.sym}; break; + case PPTOK_IDENT: + case PPTOK_IDENT_UNEXP: + case PPTOK_NUM: { + string_t *dup = string_dup(tok1->tokv.str); + if (!dup) { + printf("Failed to duplicate string while expanding %s\n", mname); + vector_del(preproc, ret); + ret = NULL; + goto solve_done; + } + tok2 = (preproc_token_t){.tokt = tok1->tokt, .tokv.str = dup}; + break; } + case PPTOK_INCL: + case PPTOK_STRING: { + string_t *dup = string_dup(tok1->tokv.sstr); + if (!dup) { + printf("Failed to duplicate string while expanding %s\n", mname); + vector_del(preproc, ret); + ret = NULL; + goto solve_done; + } + tok2 = (preproc_token_t){.tokt = tok1->tokt, .tokv.sstr = dup, .tokv.sisstr = tok1->tokv.sisstr}; + break; } + } + if (!vector_push(preproc, ret, tok2)) { + preproc_token_del(&tok2); + vector_del(preproc, ret); + ret = NULL; + goto solve_done; + } + } + } + vector_pop_nodel(mtoken, st); + if (need_concat) { + concat_cur = (vector_size(preproc, toks_to_add) || (concat_cur == 2)) ? 2 : 1; + --need_concat; + } else concat_cur = 0; + break; } + + case MTOK_STRINGIFY: + // TODO: better stringifier + if (concat_cur == 2) { + printf("Warning: unsupported concatenation with strings while expanding %s\n", mname); + } + preproc_token_t tok2; + { + string_t *dup = string_new_cap(15); + if (!dup) { + printf("Failed to duplicate string while expanding %s\n", mname); + vector_del(preproc, ret); + ret = NULL; + goto solve_done; + } + string_add_char(dup, '<'); + string_add_char(dup, 'S'); + string_add_char(dup, 't'); + string_add_char(dup, 'r'); + string_add_char(dup, 'i'); + string_add_char(dup, 'n'); + string_add_char(dup, 'g'); + string_add_char(dup, 'i'); + string_add_char(dup, 'f'); + string_add_char(dup, 'y'); + string_add_char(dup, ' '); + string_add_char(dup, 'a'); + string_add_char(dup, 'r'); + string_add_char(dup, 'g'); + string_add_char(dup, '>'); + tok2 = (preproc_token_t){.tokt = PPTOK_STRING, .tokv.sstr = dup, .tokv.sisstr = 1}; + } + if (!vector_push(preproc, ret, tok2)) { + preproc_token_del(&tok2); + vector_del(preproc, ret); + ret = NULL; + goto solve_done; + } + vector_pop_nodel(mtoken, st); + if (need_concat) { + concat_cur = 2; + --need_concat; + } else concat_cur = 0; + break; + } + } + +solve_done: + // Don't call the destructors + vector_del_freed(mtoken, st); + if (margs) { + for (size_t i = 0; i < vector_size(preprocs, margs); ++i) { + if (margs2[i]) vector_del(preproc, margs2[i]); + } + } + free(margs2); + + if (ret) { + // Success, now expand the result + // First add mname to the set of expanded macros + int iret; + kh_put(string_set, solved_macros, mname, &iret); + if (iret < 0) { + printf("Memory error while expanding %s\n", mname); + vector_del(preproc, ret); + free(mname); + return NULL; + } + + // Next expand every remaining macros + vector_trim(preproc, ret); + VECTOR(preproc) *ret2 = proc_do_expand(macros, ret, solved_macros, opt_used_macros); + vector_del(preproc, ret); + ret = ret2; + if (!ret) return NULL; // There was an error, abort + + // Finally pop mname (in case we are expanding an argument) + khiter_t it = kh_get(string_set, solved_macros, mname); + if (it == kh_end(solved_macros)) { + printf("Unknown error while expanding a macro\n"); + vector_del(preproc, ret); + return NULL; + } +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" + free((void*)kh_key(solved_macros, it)); +#pragma GCC diagnostic pop + kh_del(string_set, solved_macros, it); + } + + return ret; +} +static VECTOR(preproc) *proc_do_expand(const khash_t(macros_map) *macros, const VECTOR(preproc) *toks, + khash_t(string_set) *solved_macros, khash_t(string_set) *opt_used_macros) { + VECTOR(preproc) *toks2 = vector_new_cap(preproc, vector_size(preproc, toks)); + if (!toks2) return NULL; + vector_for(preproc, tok, toks) { + preproc_token_t tok2; + switch (tok->tokt) { + case PPTOK_INVALID: + case PPTOK_NEWLINE: + case PPTOK_BLANK: + case PPTOK_START_LINE_COMMENT: + case PPTOK_EOF: tok2 = (preproc_token_t){.tokt = tok->tokt, .tokv.c = tok->tokv.c}; break; + case PPTOK_SYM: tok2 = (preproc_token_t){.tokt = tok->tokt, .tokv.sym = tok->tokv.sym}; break; + case PPTOK_IDENT: + case PPTOK_IDENT_UNEXP: + case PPTOK_NUM: { + string_t *dup = string_dup(tok->tokv.str); + if (!dup) { + printf("Failed to duplicate string during full macro expansion\n"); + vector_del(preproc, toks2); + return NULL; + } + tok2 = (preproc_token_t){.tokt = tok->tokt, .tokv.str = dup}; + break; } + case PPTOK_INCL: + case PPTOK_STRING: { + string_t *dup = string_dup(tok->tokv.sstr); + if (!dup) { + printf("Failed to duplicate string during full macro expansion\n"); + vector_del(preproc, toks2); + return NULL; + } + tok2 = (preproc_token_t){.tokt = tok->tokt, .tokv.sstr = dup, .tokv.sisstr = tok->tokv.sisstr}; + break; } + } + vector_push(preproc, toks2, tok2); // cap > size, thus this always succeed + } + + VECTOR(preproc) *ret = vector_new_cap(preproc, vector_size(preproc, toks)); + if (!ret) return NULL; + vector_for(preproc, tok, toks2) { + switch (tok->tokt) { + case PPTOK_IDENT: { + if (opt_used_macros && !strcmp(string_content(tok->tokv.str), "defined") && (vector_end(preproc, toks2) - tok >= 2)) { + // We need to support "defined ident" and "defined(ident)" + string_t *mname = NULL; + preproc_token_t *tok2; + // tok2 is the last used token, mname is the ident string + if ((tok[1].tokt == PPTOK_SYM) && (tok[1].tokv.sym == SYM_LPAREN)) { + if ((vector_end(preproc, toks2) - tok >= 4) && (tok[3].tokt == PPTOK_SYM) && (tok[3].tokv.sym == SYM_RPAREN)) { + if ((tok[2].tokt == PPTOK_IDENT) || (tok[2].tokt == PPTOK_IDENT_UNEXP)) { + tok2 = tok + 3; + mname = tok[2].tokv.str; + } + } + } else if ((tok[1].tokt == PPTOK_IDENT) || (tok[1].tokt == PPTOK_IDENT_UNEXP)) { + tok2 = tok + 1; + mname = tok[1].tokv.str; + } + if (mname) { + // Add the ident to the used_macros + int iret; + char *mname2 = strdup(string_content(mname)); + if (!mname2) { + // Abort + printf("Failed to add %s to the list of used macros (strdup returned NULL)\n", string_content(mname)); + goto expand_done; + } + kh_put(string_set, opt_used_macros, mname2, &iret); + if (iret < 0) { + // Abort + free(mname2); + printf("Failed to add %s to the list of used macros (kh_put ireturned %d)\n", string_content(mname), iret); + goto expand_done; + } else if (iret == 0) { + // Just free mname2, it was already present + free(mname2); + } + + // Value is either 0 or 1 as a PPTOK_NUM, so we need a string with capacity 1 + string_t *num_str = string_new_cap(1); + if (!num_str) { + // Abort + printf("Failed to create defined() output\n"); + goto expand_done; + } + khiter_t it = kh_get(macros_map, macros, string_content(mname)); + string_add_char(num_str, (it == kh_end(macros)) ? '0' : '1'); + if (!vector_push(preproc, ret, ((preproc_token_t){.tokt = PPTOK_NUM, .tokv.str = num_str}))) { + printf("Failed to add defined() to the output\n"); + string_del(num_str); + goto expand_done; + } + + // Done + string_del(tok->tokv.str); + string_del(mname); + tok = tok2; + break; + } + // If mname == NULL, we simply ignore it and check if there is a macro called 'defined' + } + khiter_t it = kh_get(string_set, solved_macros, string_content(tok->tokv.str)); + if (it != kh_end(solved_macros)) { + tok->tokt = PPTOK_IDENT_UNEXP; + } else { + it = kh_get(macros_map, macros, string_content(tok->tokv.str)); + if (it != kh_end(macros)) { + macro_t *m = &kh_val(macros, it); + if (m->is_funlike) { + preproc_token_t *tok2 = tok + 1; + if ((tok2 < vector_end(preproc, toks2)) && (tok2->tokt == PPTOK_SYM) && (tok2->tokv.sym == SYM_LPAREN)) { + unsigned depth = 1; + VECTOR(preprocs) *margs = vector_new(preprocs); + if (!margs) { + printf("Memory error (parsing macro use %s)\n", string_content(tok->tokv.str)); + if (margs) vector_del(preprocs, margs); + goto expand_done; + } + VECTOR(preproc) *marg = vector_new(preproc); + if (!marg) goto gather_args_err_mem; + + while (depth && (tok2 < vector_end(preproc, toks2) - 1) && (tok2->tokt != PPTOK_EOF) && (tok2->tokt != PPTOK_INVALID)) { + ++tok2; + if ((depth == 1) && (tok2->tokt == PPTOK_SYM) && (tok2->tokv.sym == SYM_COMMA)) { + // Possible optimization: emplace NULL if vector_size(marg) == 0 + // This would avoid allocating a new vector, but needs support in proc_solve_macro + vector_trim(preproc, marg); + if (!vector_push(preprocs, margs, marg)) goto gather_args_err_mem; + marg = vector_new(preproc); + if (!marg) goto gather_args_err_mem; + } else { + if (!vector_push(preproc, marg, *tok2)) goto gather_args_err_mem; + } + if ((tok2->tokt == PPTOK_SYM) && (tok2->tokv.sym == SYM_LPAREN)) ++depth; + else if ((tok2->tokt == PPTOK_SYM) && (tok2->tokv.sym == SYM_RPAREN)) --depth; + } + if (depth) { + // This may be OK ("Unfinished fun-like macro %s\n", string_content(tok->tokv.str)); + vector_del(preprocs, margs); + vector_del(preproc, marg); + goto abort_macro; + } + // margs finishes with a SYM_RPAREN token + vector_pop(preproc, marg); + vector_trim(preproc, marg); + if (!vector_push(preprocs, margs, marg)) goto gather_args_err_mem; + marg = NULL; + + if (0) { + gather_args_err_mem: + printf("Memory error (parsing macro use %s)\n", string_content(tok->tokv.str)); + if (marg) vector_del(preproc, marg); + vector_del(preprocs, margs); + goto expand_done; + } + + // OK, now expand the macro + char *mname = string_steal(tok->tokv.str); + tok = tok2; + + VECTOR(preproc) *expanded = proc_solve_macro(macros, mname, m, margs, solved_macros, opt_used_macros); + vector_del(preprocs, margs); + if (!expanded) { + // Error expanding the macro + goto expand_done; + } + if (!vector_push_vec(preproc, ret, expanded)) { + printf("Memory error (pushing expanded macro to expanded macro)\n"); + vector_del(preproc, expanded); + goto expand_done; + } + vector_del_freed(preproc, expanded); + // Done + break; + } + } else { + VECTOR(preproc) *expanded = proc_solve_macro(macros, string_steal(tok->tokv.str), m, NULL, solved_macros, opt_used_macros); + if (!expanded) { + ++tok; // Current token is already freed (string stolen) + goto expand_done; + } + if (!vector_push_vec(preproc, ret, expanded)) { + printf("Failed to extend output for full macro expansion\n"); + ++tok; // Current token is already freed (string stolen) + goto expand_done; + } + vector_del_freed(preproc, expanded); + break; + } + } + } + } + /* FALLTHROUGH */ + abort_macro: + case PPTOK_IDENT_UNEXP: + case PPTOK_INVALID: + case PPTOK_NUM: + case PPTOK_STRING: + case PPTOK_INCL: + case PPTOK_SYM: + case PPTOK_NEWLINE: + case PPTOK_BLANK: + case PPTOK_START_LINE_COMMENT: + case PPTOK_EOF: + if (!vector_push(preproc, ret, *tok)) { + printf("Failed to duplicate token during full macro expansion\n"); + goto expand_done; + } + break; + } + + if (0) { + expand_done: + vector_del_free_from(preproc, toks2, (size_t)(tok - vector_begin(preproc, toks2))); + vector_del(preproc, ret); + return NULL; + } + } + vector_del_freed(preproc, toks2); + + return ret; +} + +#define OPLVL_MUL 2 +#define OPLVL_DIV 2 +#define OPLVL_ADD 3 +#define OPLVL_SUB 3 +#define OPLVL_LSL 4 +#define OPLVL_LSR 4 +#define OPLVL_LET 5 +#define OPLVL_LEE 5 +#define OPLVL_GRT 5 +#define OPLVL_GRE 5 +#define OPLVL_EQU 6 +#define OPLVL_NEQ 6 +#define OPLVL_AAN 7 // Bitwise (Arithmetic) +#define OPLVL_XOR 8 +#define OPLVL_AOR 9 // Bitwise (Arithmetic) +#define OPLVL_BAN 10 // Boolean +#define OPLVL_BOR 11 // Boolean +#define OPTYP_MUL 1 +#define OPTYP_DIV 2 +#define OPTYP_ADD 1 +#define OPTYP_SUB 2 +#define OPTYP_LSL 1 +#define OPTYP_LSR 2 +#define OPTYP_LET 1 +#define OPTYP_LEE 2 +#define OPTYP_GRT 3 +#define OPTYP_GRE 4 +#define OPTYP_EQU 1 +#define OPTYP_NEQ 2 +#define OPTYP_AAN 1 +#define OPTYP_XOR 1 +#define OPTYP_AOR 1 +#define OPTYP_BAN 1 +#define OPTYP_BOR 1 +typedef struct preproc_eval_aux_s { + struct { + unsigned st0 : 1; + unsigned st2 : 2; + unsigned st3 : 2; + unsigned st4 : 2; + unsigned st5 : 3; + unsigned st6 : 2; + unsigned st7 : 1; + unsigned st8 : 1; + unsigned st9 : 1; + unsigned st_bool : 1; + }; + int64_t v0; + unsigned st1; + const preproc_token_t *v1; + int64_t v2; + int64_t v3; + int64_t v4; + int64_t v5; + int64_t v6; + int64_t v7; + int64_t v8; + int64_t v9; + unsigned n_colons; // Number of ':' expected (needs to skip to the end), ie number of ternary where the truth-y branch is taken +} preproc_eval_aux_t; +VECTOR_DECLARE_STATIC(ppeaux, preproc_eval_aux_t) +VECTOR_IMPL_STATIC(ppeaux, (void)) +static int64_t preproc_eval(const VECTOR(preproc) *cond, int *aux_ret) { + VECTOR(ppeaux) *stack = vector_new_cap(ppeaux, 1); + if (!stack) { + printf("Failed to allocate #if evaluation stack vector\n"); + *aux_ret = 0; + return 0; + } + if (0) { + eval_fail: + vector_del(ppeaux, stack); + *aux_ret = 0; + return 0; + } + vector_push(ppeaux, stack, (preproc_eval_aux_t){0}); // vector_cap >= 1 + int64_t acc; + vector_for(preproc, tok, cond) { + if (tok->tokt == PPTOK_NUM) { + // Evaluate token as an integer if st0 == 0, error otherwise + if (vector_last(ppeaux, stack).st0 == 0) { + num_constant_t cst; + if (!num_constant_convert(tok->tokv.str, &cst)) { + goto eval_fail; + } + switch (cst.typ) { + case NCT_INT32: acc = cst.val.i32; break; + case NCT_UINT32: acc = cst.val.u32; break; + case NCT_INT64: acc = cst.val.i64; break; + case NCT_UINT64: acc = (int64_t)cst.val.u64; break; + case NCT_FLOAT: + case NCT_DOUBLE: + case NCT_LDOUBLE: + default: + printf("Number '%s' is not a valid integer (during #if evaluation)\n", string_content(tok->tokv.str)); + goto eval_fail; + } + goto push_acc_to_st0; + } else { + printf("Invalid number during #if evaluation\n"); + goto eval_fail; + } + } else if ((tok->tokt == PPTOK_IDENT) || (tok->tokt == PPTOK_IDENT_UNEXP)) { + // Evaluate token as 0 if st0 == 0, error otherwise + if (vector_last(ppeaux, stack).st0 == 0) { + acc = 0; + goto push_acc_to_st0; + } else { + printf("Invalid ident '%s' during #if evaluation\n", string_content(tok->tokv.str)); + goto eval_fail; + } + } else if (tok->tokt == PPTOK_SYM) { + int op_lvl; + int op_typ; + switch (tok->tokv.sym) { + case SYM_TILDE: + // Unary (st0 == 0) + if (vector_last(ppeaux, stack).st0 == 0) { + // Unary, prepare lv1 then continue to the next token + if (!(vector_last(ppeaux, stack).st1++)) { + // Also update v1, since this is the first level 1 operator + vector_last(ppeaux, stack).v1 = tok; + } + goto done_partial_eval; + } else { + printf("Invalid %snary '%s' in #if expression\n", "u", sym2str[tok->tokv.sym]); + goto eval_fail; + } + case SYM_EXCL: + // Unary (st0 == 0) + if (vector_last(ppeaux, stack).st0 == 0) { + // Unary, prepare lv1 then continue to the next token + if (!(vector_last(ppeaux, stack).st1++)) { + // Also update v1, since this is the first level 1 operator + vector_last(ppeaux, stack).v1 = tok; + } + goto done_partial_eval; + } else { + printf("Invalid %snary '%s' in #if expression\n", "u", sym2str[tok->tokv.sym]); + goto eval_fail; + } + case SYM_PLUS: + // May be unary (st0 == 0) or binary (st0 != 0) + if (vector_last(ppeaux, stack).st0 == 0) { + // Unary, prepare lv1 then continue to the next token + if (!(vector_last(ppeaux, stack).st1++)) { + // Also update v1, since this is the first level 1 operator + vector_last(ppeaux, stack).v1 = tok; + } + goto done_partial_eval; + } else { + op_lvl = OPLVL_ADD; + op_typ = OPTYP_ADD; + break; + } + case SYM_DASH: + // May be unary (st0 == 0) or binary (st0 != 0) + if (vector_last(ppeaux, stack).st0 == 0) { + // Unary, prepare lv1 then continue to the next token + if (!(vector_last(ppeaux, stack).st1++)) { + // Also update v1, since this is the first level 1 operator + vector_last(ppeaux, stack).v1 = tok; + } + goto done_partial_eval; + } else { + op_lvl = OPLVL_SUB; + op_typ = OPTYP_SUB; + break; + } + case SYM_STAR: + // Binary (st0 != 0) + if (vector_last(ppeaux, stack).st0 == 0) { + printf("Invalid %snary '%s' in #if expression\n", "bi", sym2str[tok->tokv.sym]); + goto eval_fail; + } else { + op_lvl = OPLVL_MUL; + op_typ = OPTYP_MUL; + break; + } + case SYM_SLASH: + // Binary (st0 != 0) + if (vector_last(ppeaux, stack).st0 == 0) { + printf("Invalid %snary '%s' in #if expression\n", "bi", sym2str[tok->tokv.sym]); + goto eval_fail; + } else { + op_lvl = OPLVL_DIV; + op_typ = OPTYP_DIV; + break; + } + case SYM_HAT: + // Binary (st0 != 0) + if (vector_last(ppeaux, stack).st0 == 0) { + printf("Invalid %snary '%s' in #if expression\n", "bi", sym2str[tok->tokv.sym]); + goto eval_fail; + } else { + op_lvl = OPLVL_XOR; + op_typ = OPTYP_XOR; + break; + } + case SYM_AMP: + // Binary (st0 != 0) + if (vector_last(ppeaux, stack).st0 == 0) { + printf("Invalid %snary '%s' in #if expression\n", "bi", sym2str[tok->tokv.sym]); + goto eval_fail; + } else { + op_lvl = OPLVL_AAN; + op_typ = OPTYP_AAN; + break; + } + case SYM_PIPE: + // Binary (st0 != 0) + if (vector_last(ppeaux, stack).st0 == 0) { + printf("Invalid %snary '%s' in #if expression\n", "bi", sym2str[tok->tokv.sym]); + goto eval_fail; + } else { + op_lvl = OPLVL_AOR; + op_typ = OPTYP_AOR; + break; + } + case SYM_EQEQ: + // Binary (st0 != 0) + if (vector_last(ppeaux, stack).st0 == 0) { + printf("Invalid %snary '%s' in #if expression\n", "bi", sym2str[tok->tokv.sym]); + goto eval_fail; + } else { + op_lvl = OPLVL_EQU; + op_typ = OPTYP_EQU; + break; + } + case SYM_EXCLEQ: + // Binary (st0 != 0) + if (vector_last(ppeaux, stack).st0 == 0) { + printf("Invalid %snary '%s' in #if expression\n", "bi", sym2str[tok->tokv.sym]); + goto eval_fail; + } else { + op_lvl = OPLVL_NEQ; + op_typ = OPTYP_NEQ; + break; + } + case SYM_LT: + // Binary (st0 != 0) + if (vector_last(ppeaux, stack).st0 == 0) { + printf("Invalid %snary '%s' in #if expression\n", "bi", sym2str[tok->tokv.sym]); + goto eval_fail; + } else { + op_lvl = OPLVL_LET; + op_typ = OPTYP_LET; + break; + } + case SYM_GT: + // Binary (st0 != 0) + if (vector_last(ppeaux, stack).st0 == 0) { + printf("Invalid %snary '%s' in #if expression\n", "bi", sym2str[tok->tokv.sym]); + goto eval_fail; + } else { + op_lvl = OPLVL_GRT; + op_typ = OPTYP_GRT; + break; + } + case SYM_LTEQ: + // Binary (st0 != 0) + if (vector_last(ppeaux, stack).st0 == 0) { + printf("Invalid %snary '%s' in #if expression\n", "bi", sym2str[tok->tokv.sym]); + goto eval_fail; + } else { + op_lvl = OPLVL_LEE; + op_typ = OPTYP_LEE; + break; + } + case SYM_GTEQ: + // Binary (st0 != 0) + if (vector_last(ppeaux, stack).st0 == 0) { + printf("Invalid %snary '%s' in #if expression\n", "bi", sym2str[tok->tokv.sym]); + goto eval_fail; + } else { + op_lvl = OPLVL_GRE; + op_typ = OPTYP_GRE; + break; + } + case SYM_AMPAMP: + // Binary (st0 != 0) + if (vector_last(ppeaux, stack).st0 == 0) { + printf("Invalid %snary '%s' in #if expression\n", "bi", sym2str[tok->tokv.sym]); + goto eval_fail; + } else { + op_lvl = OPLVL_BAN; + op_typ = OPTYP_BAN; + break; + } + case SYM_PIPEPIPE: + // Binary (st0 != 0) + if (vector_last(ppeaux, stack).st0 == 0) { + printf("Invalid %snary '%s' in #if expression\n", "bi", sym2str[tok->tokv.sym]); + goto eval_fail; + } else { + op_lvl = OPLVL_BOR; + op_typ = OPTYP_BOR; + break; + } + case SYM_LTLT: + // Binary (st0 != 0) + if (vector_last(ppeaux, stack).st0 == 0) { + printf("Invalid %snary '%s' in #if expression\n", "bi", sym2str[tok->tokv.sym]); + goto eval_fail; + } else { + op_lvl = OPLVL_LSL; + op_typ = OPTYP_LSL; + break; + } + case SYM_GTGT: + // Binary (st0 != 0) + if (vector_last(ppeaux, stack).st0 == 0) { + printf("Invalid %snary '%s' in #if expression\n", "bi", sym2str[tok->tokv.sym]); + goto eval_fail; + } else { + op_lvl = OPLVL_LSR; + op_typ = OPTYP_LSR; + break; + } + + case SYM_LPAREN: + // May be placed anywhere a constant is expected (st0 == 0) and simply pushes a new stack + if (vector_last(ppeaux, stack).st0 == 0) { + if (!vector_push(ppeaux, stack, (preproc_eval_aux_t){0})) { + printf("Failed to push to the stack during #if evaluation\n"); + goto eval_fail; + } + goto done_partial_eval; + } else { + printf("Invalid opening parenthesis in #if expression\n"); + goto eval_fail; + } + case SYM_RPAREN: + case SYM_QUESTION: + // May be placed anywhere and simply pushes a new stack (paren) or more complex operation (ternary) + if ((tok->tokv.sym == SYM_RPAREN) && (vector_size(ppeaux, stack) == 1)) { + printf("Invalid closing parenthesis during #if evaluation\n"); + goto eval_fail; + } + if (!vector_last(ppeaux, stack).st0) { + printf("Invalid %s during #if evaluation\n", (tok->tokv.sym == SYM_RPAREN) ? "closing parenthesis" : "question mark"); + goto eval_fail; + } + + // Evaluate the top of the stack, then pop + acc = vector_last(ppeaux, stack).v0; + vector_last(ppeaux, stack).st0 = 0; + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wconversion" +#pragma GCC diagnostic ignored "-Wduplicated-cond" // For the else if (.stX) + if (vector_last(ppeaux, stack).st2 == OPTYP_MUL) { + acc = vector_last(ppeaux, stack).v2 * acc; + vector_last(ppeaux, stack).st2 = 0; + } else if (vector_last(ppeaux, stack).st2 == OPTYP_DIV) { + acc = vector_last(ppeaux, stack).v2 / acc; + vector_last(ppeaux, stack).st2 = 0; + } else if (vector_last(ppeaux, stack).st2) { + printf(" Unknown st2 %d during #if evaluation\n", vector_last(ppeaux, stack).st2); + goto eval_fail; + } + if (vector_last(ppeaux, stack).st3 == OPTYP_ADD) { + acc = vector_last(ppeaux, stack).v3 + acc; + vector_last(ppeaux, stack).st3 = 0; + } else if (vector_last(ppeaux, stack).st3 == OPTYP_SUB) { + acc = vector_last(ppeaux, stack).v3 - acc; + vector_last(ppeaux, stack).st3 = 0; + } else if (vector_last(ppeaux, stack).st3) { + printf(" Unknown st3 %d during #if evaluation\n", vector_last(ppeaux, stack).st3); + goto eval_fail; + } + if (vector_last(ppeaux, stack).st4 == OPTYP_LSL) { + acc = vector_last(ppeaux, stack).v4 << acc; + vector_last(ppeaux, stack).st4 = 0; + } else if (vector_last(ppeaux, stack).st4 == OPTYP_LSR) { + acc = vector_last(ppeaux, stack).v4 >> acc; + vector_last(ppeaux, stack).st4 = 0; + } else if (vector_last(ppeaux, stack).st4) { + printf(" Unknown st4 %d during #if evaluation\n", vector_last(ppeaux, stack).st4); + goto eval_fail; + } + if (vector_last(ppeaux, stack).st5 == OPTYP_LET) { + acc = vector_last(ppeaux, stack).v5 < acc; + vector_last(ppeaux, stack).st5 = 0; + } else if (vector_last(ppeaux, stack).st5 == OPTYP_LEE) { + acc = vector_last(ppeaux, stack).v5 <= acc; + vector_last(ppeaux, stack).st5 = 0; + } else if (vector_last(ppeaux, stack).st5 == OPTYP_GRT) { + acc = vector_last(ppeaux, stack).v5 > acc; + vector_last(ppeaux, stack).st5 = 0; + } else if (vector_last(ppeaux, stack).st5 == OPTYP_GRE) { + acc = vector_last(ppeaux, stack).v5 >= acc; + vector_last(ppeaux, stack).st5 = 0; + } else if (vector_last(ppeaux, stack).st5) { + printf(" Unknown st5 %d during #if evaluation\n", vector_last(ppeaux, stack).st5); + goto eval_fail; + } + if (vector_last(ppeaux, stack).st6 == OPTYP_EQU) { + acc = vector_last(ppeaux, stack).v6 == acc; + vector_last(ppeaux, stack).st6 = 0; + } else if (vector_last(ppeaux, stack).st6 == OPTYP_NEQ) { + acc = vector_last(ppeaux, stack).v6 != acc; + vector_last(ppeaux, stack).st6 = 0; + } else if (vector_last(ppeaux, stack).st6) { + printf(" Unknown st6 %d during #if evaluation\n", vector_last(ppeaux, stack).st6); + goto eval_fail; + } + if (vector_last(ppeaux, stack).st7 == OPTYP_AAN) { + acc = vector_last(ppeaux, stack).v7 & acc; + vector_last(ppeaux, stack).st7 = 0; + } else if (vector_last(ppeaux, stack).st7) { + printf(" Unknown st7 %d during #if evaluation\n", vector_last(ppeaux, stack).st7); + goto eval_fail; + } + if (vector_last(ppeaux, stack).st8 == OPTYP_XOR) { + acc = vector_last(ppeaux, stack).v8 ^ acc; + vector_last(ppeaux, stack).st8 = 0; + } else if (vector_last(ppeaux, stack).st8) { + printf(" Unknown st8 %d during #if evaluation\n", vector_last(ppeaux, stack).st8); + goto eval_fail; + } + if (vector_last(ppeaux, stack).st9 == OPTYP_AOR) { + acc = vector_last(ppeaux, stack).v9 | acc; + vector_last(ppeaux, stack).st9 = 0; + } else if (vector_last(ppeaux, stack).st9) { + printf(" Unknown st9 %d during #if evaluation\n", vector_last(ppeaux, stack).st9); + goto eval_fail; + } + if (vector_last(ppeaux, stack).st_bool) { + acc = !!acc; + vector_last(ppeaux, stack).st_bool = 0; + } +#pragma GCC diagnostic pop + + if (tok->tokv.sym == SYM_RPAREN) { + vector_pop(ppeaux, stack); + goto push_acc_to_st0; + } else { + eval_question_acc: + if (acc) { + // Increase n_colons + ++vector_last(ppeaux, stack).n_colons; + goto done_partial_eval; + } else { + // Skip to the corresponding colon + unsigned nquestions = 0, nparens = 0; + // Note that we don't really care about the content of the ignored part; it may be syntaxically incorrect + for (++tok; tok < vector_end(preproc, cond); ++tok) { + if ((tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_LPAREN)) { + ++nparens; + } else if ((tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_RPAREN)) { + if (nparens) --nparens; + else { + printf("Unclosed parenthesis in #if evaluation\n"); + goto eval_fail; + } + } else if (!nparens && (tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_QUESTION)) { + ++nquestions; + } else if (!nparens && (tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_COLON)) { + if (nquestions) --nquestions; + else break; + } + } + if (tok == vector_end(preproc, cond)) { + printf("Unfinished ternary operator in #if evaluation\n"); + goto eval_fail; + } + goto done_partial_eval; + } + } + case SYM_COLON: + if (vector_last(ppeaux, stack).n_colons) { + // Decrease n_colons and skip to the rparen/end of vector + if (vector_size(ppeaux, stack) == 1) { + goto done_complete_stack; // No rparen, skip to the end of the vector + } + // Skip to the next rparen; also, we skip the correct count of ':' since we don't care about those anymore + // --vector_last(ppeaux, stack).n_colons; + unsigned nparens = 0; + // Note that we don't really care about the content of the ignored part; it may be syntaxically incorrect + for (++tok; tok < vector_end(preproc, cond); ++tok) { + if ((tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_LPAREN)) { + ++nparens; + } else if ((tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_RPAREN)) { + if (nparens) --nparens; + else break; + } + } + if (tok == vector_end(preproc, cond)) { + printf("Unfinished ternary operator in #if evaluation\n"); + goto eval_fail; + } + --tok; + goto done_partial_eval; + } else { + printf("Invalid colon symbol during #if evaluation\n"); + goto eval_fail; + } + + case SYM_LBRACKET: + case SYM_RBRACKET: + case SYM_LSQBRACKET: + case SYM_RSQBRACKET: + case SYM_HASH: + case SYM_HASHHASH: + case SYM_SEMICOLON: + case SYM_COLONCOLON: + case SYM_VARIADIC: + case SYM_DOT: + case SYM_DASHGT: + case SYM_PERCENT: + case SYM_EQ: + case SYM_PLUSEQ: + case SYM_DASHEQ: + case SYM_STAREQ: + case SYM_SLASHEQ: + case SYM_PERCENTEQ: + case SYM_HATEQ: + case SYM_AMPEQ: + case SYM_PIPEEQ: + case SYM_LTLTEQ: + case SYM_GTGTEQ: + case SYM_PLUSPLUS: + case SYM_DASHDASH: + case SYM_COMMA: + default: + printf("Invalid symbol ID %u during #if evaluation\n", tok->tokv.sym); + goto eval_fail; + } + acc = vector_last(ppeaux, stack).v0; + vector_last(ppeaux, stack).st0 = 0; + + if (op_lvl < 2) { + printf(" Invalid op_lvl %d < 2 during #if evaluation\n", op_lvl); + goto eval_fail; + } + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wconversion" +#pragma GCC diagnostic ignored "-Wduplicated-cond" // For the else if (.stX) + if (vector_last(ppeaux, stack).st2 == OPTYP_MUL) { + acc = vector_last(ppeaux, stack).v2 * acc; + vector_last(ppeaux, stack).st2 = 0; + } else if (vector_last(ppeaux, stack).st2 == OPTYP_DIV) { + acc = vector_last(ppeaux, stack).v2 / acc; + vector_last(ppeaux, stack).st2 = 0; + } else if (vector_last(ppeaux, stack).st2) { + printf(" Unknown st2 %d during #if evaluation\n", vector_last(ppeaux, stack).st2); + goto eval_fail; + } + if (op_lvl == 2) { + vector_last(ppeaux, stack).st2 = op_typ; + vector_last(ppeaux, stack).v2 = acc; + goto done_partial_eval; + } + if (vector_last(ppeaux, stack).st3 == OPTYP_ADD) { + acc = vector_last(ppeaux, stack).v3 + acc; + vector_last(ppeaux, stack).st3 = 0; + } else if (vector_last(ppeaux, stack).st3 == OPTYP_SUB) { + acc = vector_last(ppeaux, stack).v3 - acc; + vector_last(ppeaux, stack).st3 = 0; + } else if (vector_last(ppeaux, stack).st3) { + printf(" Unknown st3 %d during #if evaluation\n", vector_last(ppeaux, stack).st3); + goto eval_fail; + } + if (op_lvl == 3) { + vector_last(ppeaux, stack).st3 = op_typ; + vector_last(ppeaux, stack).v3 = acc; + goto done_partial_eval; + } + if (vector_last(ppeaux, stack).st4 == OPTYP_LSL) { + acc = vector_last(ppeaux, stack).v4 << acc; + vector_last(ppeaux, stack).st4 = 0; + } else if (vector_last(ppeaux, stack).st4 == OPTYP_LSR) { + acc = vector_last(ppeaux, stack).v4 >> acc; + vector_last(ppeaux, stack).st4 = 0; + } else if (vector_last(ppeaux, stack).st4) { + printf(" Unknown st4 %d during #if evaluation\n", vector_last(ppeaux, stack).st4); + goto eval_fail; + } + if (op_lvl == 4) { + vector_last(ppeaux, stack).st4 = op_typ; + vector_last(ppeaux, stack).v4 = acc; + goto done_partial_eval; + } + if (vector_last(ppeaux, stack).st5 == OPTYP_LET) { + acc = vector_last(ppeaux, stack).v5 < acc; + vector_last(ppeaux, stack).st5 = 0; + } else if (vector_last(ppeaux, stack).st5 == OPTYP_LEE) { + acc = vector_last(ppeaux, stack).v5 <= acc; + vector_last(ppeaux, stack).st5 = 0; + } else if (vector_last(ppeaux, stack).st5 == OPTYP_GRT) { + acc = vector_last(ppeaux, stack).v5 > acc; + vector_last(ppeaux, stack).st5 = 0; + } else if (vector_last(ppeaux, stack).st5 == OPTYP_GRE) { + acc = vector_last(ppeaux, stack).v5 >= acc; + vector_last(ppeaux, stack).st5 = 0; + } else if (vector_last(ppeaux, stack).st5) { + printf(" Unknown st5 %d during #if evaluation\n", vector_last(ppeaux, stack).st5); + goto eval_fail; + } + if (op_lvl == 5) { + vector_last(ppeaux, stack).st5 = op_typ; + vector_last(ppeaux, stack).v5 = acc; + goto done_partial_eval; + } + if (vector_last(ppeaux, stack).st6 == OPTYP_EQU) { + acc = vector_last(ppeaux, stack).v6 == acc; + vector_last(ppeaux, stack).st6 = 0; + } else if (vector_last(ppeaux, stack).st6 == OPTYP_NEQ) { + acc = vector_last(ppeaux, stack).v6 != acc; + vector_last(ppeaux, stack).st6 = 0; + } else if (vector_last(ppeaux, stack).st6) { + printf(" Unknown st6 %d during #if evaluation\n", vector_last(ppeaux, stack).st6); + goto eval_fail; + } + if (op_lvl == 6) { + vector_last(ppeaux, stack).st6 = op_typ; + vector_last(ppeaux, stack).v6 = acc; + goto done_partial_eval; + } + if (vector_last(ppeaux, stack).st7 == OPTYP_AAN) { + acc = vector_last(ppeaux, stack).v7 & acc; + vector_last(ppeaux, stack).st7 = 0; + } else if (vector_last(ppeaux, stack).st7) { + printf(" Unknown st7 %d during #if evaluation\n", vector_last(ppeaux, stack).st7); + goto eval_fail; + } + if (op_lvl == 7) { + vector_last(ppeaux, stack).st7 = op_typ; + vector_last(ppeaux, stack).v7 = acc; + goto done_partial_eval; + } + if (vector_last(ppeaux, stack).st8 == OPTYP_XOR) { + acc = vector_last(ppeaux, stack).v8 ^ acc; + vector_last(ppeaux, stack).st8 = 0; + } else if (vector_last(ppeaux, stack).st8) { + printf(" Unknown st8 %d during #if evaluation\n", vector_last(ppeaux, stack).st8); + goto eval_fail; + } + if (op_lvl == 8) { + vector_last(ppeaux, stack).st8 = op_typ; + vector_last(ppeaux, stack).v8 = acc; + goto done_partial_eval; + } + if (vector_last(ppeaux, stack).st9 == OPTYP_AOR) { + acc = vector_last(ppeaux, stack).v9 | acc; + vector_last(ppeaux, stack).st9 = 0; + } else if (vector_last(ppeaux, stack).st9) { + printf(" Unknown st9 %d during #if evaluation\n", vector_last(ppeaux, stack).st9); + goto eval_fail; + } + if (op_lvl == 9) { + vector_last(ppeaux, stack).st9 = op_typ; + vector_last(ppeaux, stack).v9 = acc; + goto done_partial_eval; + } + if (op_lvl == 10) { + // We know that sym == SYM_AMPAMP, so we need to skip evaluating the remainder if it equals 0 + if (acc) goto done_partial_eval; + unsigned nparens = 0; + // 0 && y ? z : w => w + // 0 && y || z => z + // Otherwise, keep skipping to the next token + for (++tok; tok < vector_end(preproc, cond); ++tok) { + if ((tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_LPAREN)) { + ++nparens; + } else if ((tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_RPAREN)) { + if (nparens) --nparens; + else { + vector_last(ppeaux, stack).v0 = acc; + vector_last(ppeaux, stack).st0 = 1; + --tok; + goto done_partial_eval; + } + } else if (!nparens && (tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_QUESTION)) { + goto eval_question_acc; + } else if (!nparens && (tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_PIPEPIPE)) { + break; + } + } + if (tok == vector_end(preproc, cond)) { + if (!nparens) goto done_complete_acc; + else { + printf("Unclosed parenthesis in #if evaluation\n"); + goto eval_fail; + } + } + goto done_partial_eval; + } + if (op_lvl == 11) { + // We know that sym == SYM_PIPEPIPE, so we need to skip evaluating the remainder if it equals 1 + if (!acc) goto done_partial_eval; + unsigned nparens = 0; + // 0 || y ? z : w => w + // Otherwise, keep skipping to the next token + for (++tok; tok < vector_end(preproc, cond); ++tok) { + if ((tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_LPAREN)) { + ++nparens; + } else if ((tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_RPAREN)) { + if (nparens) --nparens; + else { + vector_last(ppeaux, stack).v0 = acc; + vector_last(ppeaux, stack).st0 = 1; + --tok; + goto done_partial_eval; + } + } else if (!nparens && (tok->tokt == PPTOK_SYM) && (tok->tokv.sym == SYM_QUESTION)) { + goto eval_question_acc; + } + } + if (tok == vector_end(preproc, cond)) { + if (!nparens) goto done_complete_acc; + else { + printf("Unclosed parenthesis in #if evaluation\n"); + goto eval_fail; + } + } + goto done_partial_eval; + } + // if (vector_last(ppeaux, stack).st_bool) { + // acc = !!acc; + // vector_last(ppeaux, stack).st_bool = 0; + // } +#pragma GCC diagnostic pop + printf(" Invalid op_lvl %d > 11 during #if evaluation\n", op_lvl); + goto eval_fail; + + done_partial_eval: + } else { + printf("Invalid token type %u during #if evaluation\n", tok->tokt); + goto eval_fail; + } + + // push_acc_to_st0: evaluate all held st1 operations on acc, then set st0 to 1 and v0 to acc + if (0) { + push_acc_to_st0: + while (vector_last(ppeaux, stack).st1) { + // Only symbols, so this should be safe + enum token_sym_type_e sym = vector_last(ppeaux, stack).v1[--vector_last(ppeaux, stack).st1].tokv.sym; + if (sym == SYM_PLUS) {} + else if (sym == SYM_DASH) acc = -acc; + else if (sym == SYM_EXCL) acc = !acc; + else if (sym == SYM_TILDE) acc = ~acc; + else { + printf(" Unknown level 1 unary operator sym ID %u\n", sym); + } + } + vector_last(ppeaux, stack).v0 = acc; + vector_last(ppeaux, stack).st0 = 1; + } + } + + if (vector_size(ppeaux, stack) != 1) { + printf("Opened parenthesis never closed in #if expression\n"); + goto eval_fail; + } + +done_complete_stack: + acc = vector_last(ppeaux, stack).v0; + vector_last(ppeaux, stack).st0 = 0; + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wconversion" +#pragma GCC diagnostic ignored "-Wduplicated-cond" // For the else if (.stX) + if (vector_last(ppeaux, stack).st2 == OPTYP_MUL) { + acc = vector_last(ppeaux, stack).v2 * acc; + vector_last(ppeaux, stack).st2 = 0; + } else if (vector_last(ppeaux, stack).st2 == OPTYP_DIV) { + acc = vector_last(ppeaux, stack).v2 / acc; + vector_last(ppeaux, stack).st2 = 0; + } else if (vector_last(ppeaux, stack).st2) { + printf(" Unknown st2 %d during #if evaluation\n", vector_last(ppeaux, stack).st2); + goto eval_fail; + } + if (vector_last(ppeaux, stack).st3 == OPTYP_ADD) { + acc = vector_last(ppeaux, stack).v3 + acc; + vector_last(ppeaux, stack).st3 = 0; + } else if (vector_last(ppeaux, stack).st3 == OPTYP_SUB) { + acc = vector_last(ppeaux, stack).v3 - acc; + vector_last(ppeaux, stack).st3 = 0; + } else if (vector_last(ppeaux, stack).st3) { + printf(" Unknown st3 %d during #if evaluation\n", vector_last(ppeaux, stack).st3); + goto eval_fail; + } + if (vector_last(ppeaux, stack).st4 == OPTYP_LSL) { + acc = vector_last(ppeaux, stack).v4 << acc; + vector_last(ppeaux, stack).st4 = 0; + } else if (vector_last(ppeaux, stack).st4 == OPTYP_LSR) { + acc = vector_last(ppeaux, stack).v4 >> acc; + vector_last(ppeaux, stack).st4 = 0; + } else if (vector_last(ppeaux, stack).st4) { + printf(" Unknown st4 %d during #if evaluation\n", vector_last(ppeaux, stack).st4); + goto eval_fail; + } + if (vector_last(ppeaux, stack).st5 == OPTYP_LET) { + acc = vector_last(ppeaux, stack).v5 < acc; + vector_last(ppeaux, stack).st5 = 0; + } else if (vector_last(ppeaux, stack).st5 == OPTYP_LEE) { + acc = vector_last(ppeaux, stack).v5 <= acc; + vector_last(ppeaux, stack).st5 = 0; + } else if (vector_last(ppeaux, stack).st5 == OPTYP_GRT) { + acc = vector_last(ppeaux, stack).v5 > acc; + vector_last(ppeaux, stack).st5 = 0; + } else if (vector_last(ppeaux, stack).st5 == OPTYP_GRE) { + acc = vector_last(ppeaux, stack).v5 >= acc; + vector_last(ppeaux, stack).st5 = 0; + } else if (vector_last(ppeaux, stack).st5) { + printf(" Unknown st5 %d during #if evaluation\n", vector_last(ppeaux, stack).st5); + goto eval_fail; + } + if (vector_last(ppeaux, stack).st6 == OPTYP_EQU) { + acc = vector_last(ppeaux, stack).v6 == acc; + vector_last(ppeaux, stack).st6 = 0; + } else if (vector_last(ppeaux, stack).st6 == OPTYP_NEQ) { + acc = vector_last(ppeaux, stack).v6 != acc; + vector_last(ppeaux, stack).st6 = 0; + } else if (vector_last(ppeaux, stack).st6) { + printf(" Unknown st6 %d during #if evaluation\n", vector_last(ppeaux, stack).st6); + goto eval_fail; + } + if (vector_last(ppeaux, stack).st7 == OPTYP_AAN) { + acc = vector_last(ppeaux, stack).v7 & acc; + vector_last(ppeaux, stack).st7 = 0; + } else if (vector_last(ppeaux, stack).st7) { + printf(" Unknown st7 %d during #if evaluation\n", vector_last(ppeaux, stack).st7); + goto eval_fail; + } + if (vector_last(ppeaux, stack).st8 == OPTYP_XOR) { + acc = vector_last(ppeaux, stack).v8 ^ acc; + vector_last(ppeaux, stack).st8 = 0; + } else if (vector_last(ppeaux, stack).st8) { + printf(" Unknown st8 %d during #if evaluation\n", vector_last(ppeaux, stack).st8); + goto eval_fail; + } + if (vector_last(ppeaux, stack).st9 == OPTYP_AOR) { + acc = vector_last(ppeaux, stack).v9 | acc; + vector_last(ppeaux, stack).st9 = 0; + } else if (vector_last(ppeaux, stack).st9) { + printf(" Unknown st9 %d during #if evaluation\n", vector_last(ppeaux, stack).st9); + goto eval_fail; + } + if (vector_last(ppeaux, stack).st_bool) { + acc = !!acc; + vector_last(ppeaux, stack).st_bool = 0; + } +#pragma GCC diagnostic pop + +done_complete_acc: + vector_del(ppeaux, stack); + *aux_ret = 1; + return acc; +} + +int proc_unget_token(preproc_t *src, proc_token_t *tok) { + return vector_push(ppsource, src->prep, ((ppsource_t){ .srct = PPSRC_PTOKEN, .srcv.ptok = *tok })); +} +static proc_token_t proc_next_token_aux(preproc_t *src) { + if (!vector_size(ppsource, src->prep)) { + return (proc_token_t){ .tokt = PTOK_EOF, .tokv = (union proc_token_val_u){.c = (char)EOF} }; + } + if (vector_last(ppsource, src->prep).srct == PPSRC_PTOKEN) { + proc_token_t ret = vector_last(ppsource, src->prep).srcv.ptok; + vector_pop(ppsource, src->prep); + return ret; + } +check_if_depth: + { + if (!vector_size(ppsource, src->prep)) { + return (proc_token_t){ .tokt = PTOK_EOF, .tokv = (union proc_token_val_u){.c = (char)EOF} }; + } + ppsource_t *ppsrc = &vector_last(ppsource, src->prep); + if ((ppsrc->srct == PPSRC_PREPARE) && (ppsrc->srcv.prep.ok_depth != ppsrc->srcv.prep.cond_depth)) { + // Ignore all tokens from ppsrc until: + // INVALID -------------- abort (return PTOK_INVALID) + // EOF ------------------ print an error, then pop and goto check_if_depth + // # if/elif/endif/... -- parse, execute, goto check_if_depth + // Also, track newlines to keep src->st up-to-date + + while (1) { + preproc_token_t tok = ppsrc_next_token(src); + skip_cur_token: + if (tok.tokt == PPTOK_INVALID) { + src->st = PPST_NONE; + proc_token_t ret; + ret.tokt = PTOK_INVALID; + ret.tokv = (union proc_token_val_u){.c = tok.tokv.c}; + return ret; + } else if (tok.tokt == PPTOK_NEWLINE) { + src->st = PPST_NL; + } else if (tok.tokt == PPTOK_EOF) { + printf("Error: file ended before closing all conditionals (ignoring)\n"); + vector_pop(ppsource, src->prep); + goto check_if_depth; + } else if ((tok.tokt == PPTOK_SYM) && (src->st == PPST_NL) && (tok.tokv.sym == SYM_HASH)) { + src->st = PPST_NONE; + tok = ppsrc_next_token(src); + if ((tok.tokt == PPTOK_NEWLINE) || (tok.tokt == PPTOK_EOF)) { + // Empty preprocessor command + src->st = PPST_NL; + continue; + } + if ((tok.tokt != PPTOK_IDENT) && (tok.tokt != PPTOK_IDENT_UNEXP)) goto preproc_ignore_remaining; + if (!strcmp(string_content(tok.tokv.str), "include")) goto preproc_ignore_remaining; + else if (!strcmp(string_content(tok.tokv.str), "include_next")) goto preproc_ignore_remaining; + else if (!strcmp(string_content(tok.tokv.str), "define")) goto preproc_ignore_remaining; + else if (!strcmp(string_content(tok.tokv.str), "undef")) goto preproc_ignore_remaining; + else if (!strcmp(string_content(tok.tokv.str), "error")) goto preproc_ignore_remaining; + else if (!strcmp(string_content(tok.tokv.str), "warning")) goto preproc_ignore_remaining; // C23/extension + else if (!strcmp(string_content(tok.tokv.str), "pragma")) goto preproc_ignore_remaining; + else if (!strcmp(string_content(tok.tokv.str), "line")) goto preproc_ignore_remaining; + else if (!strcmp(string_content(tok.tokv.str), "if")) { + // Increase cond_depth (we already know we are ignoring the content) + ++ppsrc->srcv.prep.cond_depth; + goto preproc_ignore_remaining; + } else if (!strcmp(string_content(tok.tokv.str), "ifdef")) { + // Increase cond_depth (we already know we are ignoring the content) + ++ppsrc->srcv.prep.cond_depth; + goto preproc_ignore_remaining; + } else if (!strcmp(string_content(tok.tokv.str), "ifndef")) { + // Increase cond_depth (we already know we are ignoring the content) + ++ppsrc->srcv.prep.cond_depth; + goto preproc_ignore_remaining; + } else if (!strcmp(string_content(tok.tokv.str), "elif")) { + if ((ppsrc->srcv.prep.ok_depth == ppsrc->srcv.prep.cond_depth - 1) && !ppsrc->srcv.prep.entered_next_ok_cond) { + string_del(tok.tokv.str); + VECTOR(preproc) *cond = vector_new(preproc); + if (!cond) { + printf("Error: failed to allocate #elif condition vector\n"); + src->st = PPST_NONE; + return (proc_token_t){ .tokt = PTOK_INVALID, .tokv = (union proc_token_val_u){.c = '\0'} }; + } + tok = ppsrc_next_token(src); + while ((tok.tokt != PPTOK_NEWLINE) && (tok.tokt != PPTOK_EOF) && (tok.tokt != PPTOK_INVALID)) { + if (!vector_push(preproc, cond, tok)) { + printf("Error: failed to add token to #elif condition vector\n"); + vector_del(preproc, cond); + src->st = PPST_NONE; + return (proc_token_t){ .tokt = PTOK_INVALID, .tokv = (union proc_token_val_u){.c = '\0'} }; + } + tok = ppsrc_next_token(src); + } + vector_trim(preproc, cond); + khash_t(string_set) *solved_macros = kh_init(string_set); + if (!solved_macros) { + printf("Error: failed to allocate #elif solved_macros set\n"); + vector_del(preproc, cond); + src->st = PPST_NONE; + return (proc_token_t){ .tokt = PTOK_INVALID, .tokv = (union proc_token_val_u){.c = '\0'} }; + } + + VECTOR(preproc) *expanded = proc_do_expand(src->macros_map, cond, solved_macros, src->macros_used); + vector_del(preproc, cond); + macros_set_del(solved_macros); + if (!expanded) { + printf("Error: failed to expand #elif condition\n"); + src->st = PPST_NONE; + return (proc_token_t){ .tokt = PTOK_INVALID, .tokv = (union proc_token_val_u){.c = '\0'} }; + } + + // Now we need to compute what is pointed by expanded, and increase cond_depth and ok_depth as needed + int st; + int64_t res = preproc_eval(expanded, &st); + vector_del(preproc, expanded); + if (!st) { + printf("Error: failed to evaluate #elif condition in (%s)\n", src->cur_file); + src->st = PPST_NONE; + return (proc_token_t){ .tokt = PTOK_INVALID, .tokv = (union proc_token_val_u){.c = '\0'} }; + } + if (res) { + vector_last(ppsource, src->prep).srcv.prep.entered_next_ok_cond = 1; + ++vector_last(ppsource, src->prep).srcv.prep.ok_depth; + if (tok.tokt == PPTOK_NEWLINE) { + src->st = PPST_NL; + goto check_next_token; + } + } else { + if (tok.tokt == PPTOK_NEWLINE) { + src->st = PPST_NL; + continue; + } + } + if (tok.tokt == PPTOK_EOF) { + printf("Error: file ended before closing all conditionals (ignoring)\n"); + vector_pop(ppsource, src->prep); + src->st = PPST_NL; // Should be redundant since TOK_NEWLINE is added before TOK_EOF if required + // EOF has an empty destructor + // Note that since we have opened the file, the previous file had ok_depth == cond_depth + goto check_next_token; + } else /* if (tok.tokt == PPTOK_INVALID) */ { + src->st = PPST_NONE; + return (proc_token_t){ .tokt = PTOK_INVALID, .tokv.c = tok.tokv.c }; + } + } else goto preproc_ignore_remaining; + } else if (!strcmp(string_content(tok.tokv.str), "elifdef")) { + string_del(tok.tokv.str); + tok = ppsrc_next_token(src); + if ((tok.tokt != PPTOK_IDENT) && (tok.tokt != PPTOK_IDENT_UNEXP)) { + printf("Invalid token type %u after '#elifdef' preprocessor command\n", tok.tokt); + goto preproc_ignore_remaining; + } + if ((ppsrc->srcv.prep.ok_depth == ppsrc->srcv.prep.cond_depth - 1) && !ppsrc->srcv.prep.entered_next_ok_cond) { + // We may enter the following block, check it + khiter_t it = kh_get(macros_map, src->macros_map, string_content(tok.tokv.str)); + if (it != kh_end(src->macros_map)) { + ppsrc->srcv.prep.entered_next_ok_cond = 1; + ++ppsrc->srcv.prep.ok_depth; + } + goto preproc_ignore_remaining; + } + } else if (!strcmp(string_content(tok.tokv.str), "elifndef")) { + string_del(tok.tokv.str); + tok = ppsrc_next_token(src); + if ((tok.tokt != PPTOK_IDENT) && (tok.tokt != PPTOK_IDENT_UNEXP)) { + printf("Invalid token type %u after '#elifdef' preprocessor command\n", tok.tokt); + goto preproc_ignore_remaining; + } + if ((ppsrc->srcv.prep.ok_depth == ppsrc->srcv.prep.cond_depth - 1) && !ppsrc->srcv.prep.entered_next_ok_cond) { + // We may enter the following block, check it + khiter_t it = kh_get(macros_map, src->macros_map, string_content(tok.tokv.str)); + if (it == kh_end(src->macros_map)) { + ppsrc->srcv.prep.entered_next_ok_cond = 1; + ++ppsrc->srcv.prep.ok_depth; + } + goto preproc_ignore_remaining; + } + } else if (!strcmp(string_content(tok.tokv.str), "else")) { + // Maybe increase ok_depth if ok_depth = cond_depth - 1; also goto check_if_depth + // Note that this (very naive) approach allows code such as: + /* #ifdef M + * ... // Not preprocessed *** Preprocessed + * #else + * ... // Preprocessed *** Not preprocessed + * #else + * ... // Not preprocessed due to unrelated code *** Not preprocessed + * #else + * ... // Preprocessed *** Not preprocessed + * #endif + */ + // Forbidding this code would require another 64-bits bitfield, which is redundant, thus not implemented. + // Also, we only need to goto check_if_depth if we actually update ok_depth + if ((ppsrc->srcv.prep.ok_depth == ppsrc->srcv.prep.cond_depth - 1) && !ppsrc->srcv.prep.entered_next_ok_cond) { + ++ppsrc->srcv.prep.ok_depth; + goto preproc_ignore_remaining_goto; + } else goto preproc_ignore_remaining; + } else if (!strcmp(string_content(tok.tokv.str), "endif")) { + // Decrease cond_depth; also goto check_if_depth + // Note that 0 <= ppsrc->srcv.prep.ok_depth < ppsrc->srcv.prep.cond_depth, so this code is OK + --ppsrc->srcv.prep.cond_depth; + goto preproc_ignore_remaining_goto; + } + + printf("Unknown ignored pp command %s (%s), skipping until EOL\n", string_content(tok.tokv.str), src->cur_file); + preproc_ignore_remaining: + while ((tok.tokt != PPTOK_NEWLINE) && (tok.tokt != PPTOK_EOF) && (tok.tokt != PPTOK_INVALID)) { + preproc_token_del(&tok); + tok = ppsrc_next_token(src); + } + if (tok.tokt == PPTOK_NEWLINE) { + src->st = PPST_NL; + } + goto skip_cur_token; + + preproc_ignore_remaining_goto: + while ((tok.tokt != PPTOK_NEWLINE) && (tok.tokt != PPTOK_EOF) && (tok.tokt != PPTOK_INVALID)) { + preproc_token_del(&tok); + tok = ppsrc_next_token(src); + } + if (tok.tokt == PPTOK_NEWLINE) { + src->st = PPST_NL; + } + goto check_if_depth; + } else { + src->st = PPST_NONE; + preproc_token_del(&tok); + } + } + } + } + proc_token_t ret; +check_next_token: + if (!vector_size(ppsource, src->prep)) { + ret.tokt = PTOK_EOF; + ret.tokv = (union proc_token_val_u){.c = (char)EOF}; + return ret; + } +start_next_token: + preproc_token_t tok = ppsrc_next_token(src); +start_cur_token: + switch (tok.tokt) { + case PPTOK_INVALID: + src->st = PPST_NONE; + ret.tokt = PTOK_INVALID; + ret.tokv = (union proc_token_val_u){.c = tok.tokv.c}; + return ret; + case PPTOK_IDENT: + src->st = PPST_NONE; + { + khiter_t it = kh_get(macros_map, src->macros_map, string_content(tok.tokv.str)); + if (it != kh_end(src->macros_map)) { + macro_t *m = &kh_val(src->macros_map, it); + int need_solve = !m->is_funlike; + VECTOR(preprocs) *margs = NULL; + if (m->is_funlike) { + preproc_token_t tok2 = ppsrc_next_token(src); + size_t nnls = 0; + while (tok2.tokt == PPTOK_NEWLINE) { + ++nnls; + tok2 = ppsrc_next_token(src); + } + if ((tok2.tokt == PPTOK_SYM) && (tok2.tokv.sym == SYM_LPAREN)) { + need_solve = 1; + margs = vector_new(preprocs); + if (!margs) goto solve_err_mem; + VECTOR(preproc) *marg = vector_new(preproc); + if (!marg) goto solve_err_mem; + while (need_solve && (tok2.tokt != PPTOK_EOF) && (tok2.tokt != PPTOK_INVALID)) { + tok2 = ppsrc_next_token(src); + if ((need_solve == 1) && (tok2.tokt == PPTOK_SYM) && (tok2.tokv.sym == SYM_COMMA)) { + // Possible optimization: emplace NULL if vector_size(marg) == 0 + // This would avoid allocating a new vector + vector_trim(preproc, marg); + if (!vector_push(preprocs, margs, marg)) { + vector_del(preproc, marg); + goto solve_err_mem; + } + marg = vector_new(preproc); + if (!marg) goto solve_err_mem; + } else { + if (!vector_push(preproc, marg, tok2)) { + vector_del(preproc, marg); + goto solve_err_mem; + } + } + if ((tok2.tokt == PPTOK_SYM) && (tok2.tokv.sym == SYM_LPAREN)) ++need_solve; + else if ((tok2.tokt == PPTOK_SYM) && (tok2.tokv.sym == SYM_RPAREN)) --need_solve; + } + if (need_solve) { + printf("Unfinished fun-like macro %s\n", string_content(tok.tokv.str)); + vector_del(preprocs, margs); + vector_del(preproc, marg); + string_del(tok.tokv.str); + src->st = PPST_NONE; + ret.tokt = PTOK_INVALID; + ret.tokv = (union proc_token_val_u){.c = tok.tokv.c}; + return ret; + } + // margs finishes with a SYM_RPAREN token + vector_pop(preproc, marg); + vector_trim(preproc, marg); + if (!vector_push(preprocs, margs, marg)) { + vector_del(preproc, marg); + goto solve_err_mem; + } + need_solve = 1; + } else { + if (!vector_reserve(ppsource, src->prep, vector_size(ppsource, src->prep) + nnls + 1)) { + printf("Memory error (undoing lookahead for macro use %s)\n", string_content(tok.tokv.str)); + string_del(tok.tokv.str); + preproc_token_del(&tok2); + src->st = PPST_NONE; + ret.tokt = PTOK_INVALID; + ret.tokv = (union proc_token_val_u){.c = '\0'}; + return ret; + } + vector_push(ppsource, src->prep, ((ppsource_t){.srct = PPSRC_PPTOKEN, .srcv.pptok = tok2})); + while (nnls--) { + vector_push( + ppsource, + src->prep, + ((ppsource_t){.srct = PPSRC_PPTOKEN, .srcv.pptok = {.tokt = PPTOK_NEWLINE, .tokv.c = '\n'}})); + } + } + } + if (need_solve) { + khash_t(string_set) *solved_macros = kh_init(string_set); + if (!solved_macros) goto solve_err_mem; + + char *mname = string_steal(tok.tokv.str); + + VECTOR(preproc) *solved = proc_solve_macro(src->macros_map, mname, m, margs, solved_macros, NULL); + if (margs) vector_del(preprocs, margs); + macros_set_del(solved_macros); + if (!solved) { + src->st = PPST_NONE; + ret.tokt = PTOK_INVALID; + ret.tokv = (union proc_token_val_u){.c = '\0'}; + return ret; + } + // If the expansion is empty, don't push it + if (vector_size(preproc, solved)) { + if (!vector_push(ppsource, src->prep, ((ppsource_t){.srct = PPSRC_PPTOKENS, .srcv.pptoks = {.idx = 0, .toks = solved}}))) { + printf("Memory error (pushing expanded macro)\n"); + vector_del(preproc, solved); + src->st = PPST_NONE; + ret.tokt = PTOK_INVALID; + ret.tokv = (union proc_token_val_u){.c = '\0'}; + return ret; + } + } else { + vector_del(preproc, solved); + } + // src->prep is not empty (either through the push or by not popping the top) + goto start_next_token; + } else { + if (margs) vector_del(preprocs, margs); + } + + if (0) { + solve_err_mem: + printf("Memory error (parsing macro use %s)\n", string_content(tok.tokv.str)); + if (margs) vector_del(preprocs, margs); + string_del(tok.tokv.str); + src->st = PPST_NONE; + ret.tokt = PTOK_INVALID; + ret.tokv = (union proc_token_val_u){.c = '\0'}; + return ret; + } + } + } + /* FALLTHROUGH */ + case PPTOK_IDENT_UNEXP: { + khiter_t it = kh_get(str2kw, str2kw, string_content(tok.tokv.str)); + if (it == kh_end(str2kw)) { + ret.tokt = PTOK_IDENT; + ret.tokv = (union proc_token_val_u){.str = tok.tokv.str}; + } else { + string_del(tok.tokv.str); + ret.tokt = PTOK_KEYWORD; + ret.tokv = (union proc_token_val_u){.kw = kh_val(str2kw, it)}; + } + return ret; } + case PPTOK_NUM: + src->st = PPST_NONE; + ret.tokt = PTOK_NUM; + ret.tokv = (union proc_token_val_u){.str = tok.tokv.str}; + return ret; + case PPTOK_STRING: + src->st = PPST_NONE; + ret.tokt = PTOK_STRING; + ret.tokv = (union proc_token_val_u){.sstr = tok.tokv.sstr, .sisstr = tok.tokv.sisstr}; + return ret; + case PPTOK_INCL: + src->st = PPST_NONE; + ret.tokt = PTOK_INVALID; + ret.tokv = (union proc_token_val_u){.c = tok.tokv.sisstr ? '<' : '"'}; + string_del(tok.tokv.sstr); + return ret; + case PPTOK_SYM: + if ((src->st == PPST_NL) && (tok.tokv.sym == SYM_HASH)) { + tok = ppsrc_next_token(src); + if ((tok.tokt == PPTOK_NEWLINE) || (tok.tokt == PPTOK_EOF)) { + // Empty preprocessor command + if (tok.tokt == PPTOK_NEWLINE) { + ret.tokt = PTOK_EOF; + ret.tokv = (union proc_token_val_u){.c = tok.tokv.c}; + return ret; + } else { + goto check_next_token; + } + } + if ((tok.tokt != PPTOK_IDENT) && (tok.tokt != PPTOK_IDENT_UNEXP)) goto preproc_hash_err; + if (!strcmp(string_content(tok.tokv.str), "include")) { + string_del(tok.tokv.str); + tok = ppsrc_next_token(src); + if (tok.tokt != PPTOK_INCL) { + printf("Invalid token type %u after '#include' preprocessor command\n", tok.tokt); + goto preproc_hash_err; + } + string_t *incl_file = tok.tokv.sstr; + int is_sys = src->is_sys || !tok.tokv.sisstr; + tok = ppsrc_next_token(src); // Token was moved + while ((tok.tokt != PPTOK_NEWLINE) && (tok.tokt != PPTOK_EOF) && (tok.tokt != PPTOK_INVALID)) { + // TODO: Print warning 'ignored token(s)' + preproc_token_del(&tok); + tok = ppsrc_next_token(src); + } + if ((is_sys || !try_open_dir(src, incl_file)) && !try_open_sys(src, incl_file, 0)) { + printf("Failed to open %s\n", string_content(incl_file)); + string_del(incl_file); + goto preproc_hash_err; + } + string_del(incl_file); + if (tok.tokt == PPTOK_NEWLINE) goto check_next_token; + else goto start_cur_token; + } else if (!strcmp(string_content(tok.tokv.str), "include_next")) { + string_del(tok.tokv.str); + tok = ppsrc_next_token(src); + if (tok.tokt != PPTOK_INCL) { + printf("Invalid token type %u after '#include_next' preprocessor command\n", tok.tokt); + goto preproc_hash_err; + } + string_t *incl_file = tok.tokv.sstr; + // Assume we only have one #include "..." path + tok = ppsrc_next_token(src); // Token was moved + while ((tok.tokt != PPTOK_NEWLINE) && (tok.tokt != PPTOK_EOF) && (tok.tokt != PPTOK_INVALID)) { + // TODO: Print warning 'ignored token(s)' + preproc_token_del(&tok); + tok = ppsrc_next_token(src); + } + // cur_pathno == 0 if cur_file was from an #include "...", otherwise idx + 1 + if (!try_open_sys(src, incl_file, src->cur_pathno)) { + printf("Failed to open %s\n", string_content(incl_file)); + string_del(incl_file); + goto preproc_hash_err; + } + string_del(incl_file); + if (tok.tokt == PPTOK_NEWLINE) goto check_next_token; + else goto start_cur_token; + } else if (!strcmp(string_content(tok.tokv.str), "define")) { + string_del(tok.tokv.str); + tok = ppsrc_next_token(src); + if ((tok.tokt != PPTOK_IDENT) && (tok.tokt != PPTOK_IDENT_UNEXP)) { + printf("Invalid token type %u after '#define' preprocessor command\n", tok.tokt); + goto preproc_hash_err; + } + string_t *defname = tok.tokv.str; + macro_t m = (macro_t){ .is_funlike = 0, .has_varargs = 0, .nargs = 0, .toks = vector_new(mtoken) }; + if (!m.toks) { + printf("Failed to allocate token vector for macro %s, returning EOF\n", string_content(defname)); + string_del(defname); // Token is now freed + ret.tokt = PTOK_EOF; + ret.tokv = (union proc_token_val_u){.c = (char)EOF}; + return ret; + } + khash_t(argid_map) *args = kh_init(argid_map); + if (!args) { + printf("Failed to allocate args map for macro %s, returning EOF\n", string_content(defname)); + string_del(defname); // Token is now freed + vector_del(mtoken, m.toks); + ret.tokt = PTOK_EOF; + ret.tokv = (union proc_token_val_u){.c = (char)EOF}; + return ret; + } + tok = ppsrc_next_token(src); + if ((tok.tokt == PPTOK_SYM) && (tok.tokv.sym == SYM_LPAREN)) { + m.is_funlike = 1; + m.nargs = 0; + tok = ppsrc_next_token(src); + int ok; + if ((tok.tokt == PPTOK_SYM) && (tok.tokv.sym == SYM_RPAREN)) { + ok = 1; + } else { + ok = 0; + while ((tok.tokt != PPTOK_NEWLINE) && (tok.tokt != PPTOK_EOF) && (tok.tokt != PPTOK_INVALID)) { + if ((tok.tokt == PPTOK_SYM) && (tok.tokv.sym == SYM_VARIADIC)) { + m.has_varargs = 1; + int kh_ret; + char *tok_str = strdup("__VA_ARGS__"); + khiter_t kh_k = kh_put(argid_map, args, tok_str, &kh_ret); // Moves the string content + if (kh_ret < 0) { // Failed to move, needs to free here + printf("Failed to push arg %s for macro %s, returning EOF\n", tok_str, string_content(defname)); + string_del(defname); + free(tok_str); + vector_del(mtoken, m.toks); + argid_map_del(args); + ret.tokt = PTOK_EOF; + ret.tokv = (union proc_token_val_u){.c = (char)EOF}; + return ret; + } + if (kh_ret == 0) { + printf("Duplicate arg %s defining macro %s\n", tok_str, string_content(defname)); + string_del(defname); + vector_del(mtoken, m.toks); + argid_map_del(args); + goto preproc_hash_err; + } + kh_val(args, kh_k) = m.nargs; + // Empty token destructor + tok = ppsrc_next_token(src); + if ((tok.tokt != PPTOK_SYM) || (tok.tokv.sym != SYM_RPAREN)) { + printf("Invalid token type %u after variadic macro arguments definition\n", tok.tokt); + string_del(defname); + vector_del(mtoken, m.toks); + argid_map_del(args); + goto preproc_hash_err; + } + ok = 1; + break; + } else if ((tok.tokt == PPTOK_IDENT) || (tok.tokt == PPTOK_IDENT_UNEXP)) { + int kh_ret; + char *tok_str = string_steal(tok.tokv.str); + khiter_t kh_k = kh_put(argid_map, args, tok_str, &kh_ret); // Moves the string content + if (kh_ret < 0) { // Failed to move, needs to free here + printf("Failed to push arg %s for macro %s, returning EOF\n", tok_str, string_content(defname)); + string_del(defname); + free(tok_str); + vector_del(mtoken, m.toks); + argid_map_del(args); + ret.tokt = PTOK_EOF; + ret.tokv = (union proc_token_val_u){.c = (char)EOF}; + return ret; + } + if (kh_ret == 0) { + printf("Duplicate arg %s defining macro %s\n", tok_str, string_content(defname)); + string_del(defname); + vector_del(mtoken, m.toks); + argid_map_del(args); + // Token was already deleted, create a fake token + tok.tokt = PPTOK_SYM; + tok.tokv.sym = LAST_SYM; + goto preproc_hash_err; + } + kh_val(args, kh_k) = m.nargs++; + // Token content is the string, which is moved to the vector + tok = ppsrc_next_token(src); + if ((tok.tokt == PPTOK_SYM) && (tok.tokv.sym == SYM_COMMA)) { + // Empty destructor + tok = ppsrc_next_token(src); + } else if ((tok.tokt == PPTOK_SYM) && (tok.tokv.sym == SYM_RPAREN)) { + // Empty destructor + ok = 1; + break; + } else if ((tok.tokt == PPTOK_SYM) && (tok.tokv.sym == SYM_VARIADIC)) { + m.has_varargs = 1; + --m.nargs; + // Empty token destructor + tok = ppsrc_next_token(src); + if ((tok.tokt != PPTOK_SYM) || (tok.tokv.sym != SYM_RPAREN)) { + printf("Invalid token type %u after variadic macro arguments definition\n", tok.tokt); + string_del(defname); + vector_del(mtoken, m.toks); + argid_map_del(args); + goto preproc_hash_err; + } + ok = 1; + break; + } else { + printf("Invalid %s type %u during variadic macro arguments definition\n", + (tok.tokt == PPTOK_SYM) ? "symbol" : "token", + (tok.tokt == PPTOK_SYM) ? tok.tokv.sym : tok.tokt); + string_del(defname); + vector_del(mtoken, m.toks); + argid_map_del(args); + goto preproc_hash_err; + } + } else { + printf("Invalid %s type %u as macro arguments definition name\n", + (tok.tokt == PPTOK_SYM) ? "symbol" : "token", (tok.tokt == PPTOK_SYM) ? tok.tokv.sym : tok.tokt); + string_del(defname); + vector_del(mtoken, m.toks); + argid_map_del(args); + goto preproc_hash_err; + } + } + } + if (!ok) { + printf("Invalid macro definition for %s\n", string_content(defname)); + string_del(defname); + vector_del(mtoken, m.toks); + argid_map_del(args); + goto preproc_hash_err; + } + // tok is ')', empty destructor + tok = ppsrc_next_token(src); + } + if (tok.tokt == PPTOK_BLANK) { + // BLANK has no destructor + tok = ppsrc_next_token(src); + } + // All tokens until INVALID/NL/EOF are macro content + int state = 0; +#define ST_CONCAT 1 +#define ST_STR 2 + while ((tok.tokt != PPTOK_NEWLINE) && (tok.tokt != PPTOK_EOF) && (tok.tokt != PPTOK_INVALID)) { + if ((tok.tokt == PPTOK_SYM) && (tok.tokv.sym == SYM_HASH)) { + if (state & ST_STR) { + printf("Warning: duplicated stringify in macro definition (defining %s)\n", string_content(defname)); + } else { + state |= ST_STR; + } + // Empty destructor + tok = ppsrc_next_token(src); + continue; + } + if ((tok.tokt == PPTOK_SYM) && (tok.tokv.sym == SYM_HASHHASH)) { + if (state & ST_CONCAT) { + printf("Warning: duplicated concatenation in macro definition (defining %s)\n", string_content(defname)); + } else if (!vector_size(mtoken, m.toks)) { + printf("Warning: invalid concatenation at start of macro definition (defining %s)\n", string_content(defname)); + } else { + state |= ST_CONCAT; + } + // Empty destructor + tok = ppsrc_next_token(src); + continue; + } + mtoken_t *mtok; + unsigned argid = -1u; + if (m.is_funlike && ((tok.tokt == PPTOK_IDENT) || (tok.tokt == PPTOK_IDENT_UNEXP))) { + khiter_t kh_k = kh_get(argid_map, args, string_content(tok.tokv.str)); + if (kh_k != kh_end(args)) { + string_del(tok.tokv.str); // Token freed + argid = kh_val(args, kh_k); + } + } + if (argid != -1u) { + mtok = mtoken_new_arg(argid, state & ST_STR); // TODO: check for != NULL + state &= ~ST_STR; + } else { + mtok = mtoken_new_token(tok); // Token moved // TODO: check for != NULL + if (state & ST_STR) { + printf("Warning: invalid stringify before token (defining %s)\n", string_content(defname)); + state &= ~ST_STR; + } + } + if (state & ST_CONCAT) { + mtoken_t *mtok2 = vector_last(mtoken, m.toks); // Guaranteed to exist before setting ST_CONCAT + mtok = mtoken_new_concat(mtok2, mtok); // TODO: check for != NULL + vector_last(mtoken, m.toks) = mtok; + state &= ~ST_CONCAT; + } else { + // TODO: check for error + vector_push(mtoken, m.toks, mtok); + } + // mtok moved to the vector + tok = ppsrc_next_token(src); + } +#undef ST_CONCAT +#undef ST_STR + argid_map_del(args); + if (tok.tokt == PPTOK_INVALID) { + // Abort + string_del(defname); + macro_del(&m); + if (tok.tokt == PPTOK_NEWLINE) goto check_next_token; + else goto start_cur_token; + } else { + // NL and EOF have empty destructors + khiter_t kh_k; + int iret; + char *mname_dup = string_steal(defname); + // if (m.is_funlike) printf("OK for %s: %u args", mname_dup, m.nargs); + // else printf("OK for %s: no arg", mname_dup); + // printf("\n"); + // vector_for(mtoken, it, m.toks) { + // printf("Macro token: "); + // print_macro_tok(*it); + // printf("\n"); + // } + kh_k = kh_put(string_set, src->macros_defined, mname_dup, &iret); + // TODO: check iret? + if (iret >= 1) { + mname_dup = strdup(mname_dup); + } + kh_k = kh_put(macros_map, src->macros_map, mname_dup, &iret); + if (iret < 0) { + // Abort + printf("Failed to remember macro %s, aborting\n", mname_dup); + free(mname_dup); + macro_del(&m); + src->st = PPST_NONE; + ret.tokt = PTOK_INVALID; + ret.tokv = (union proc_token_val_u){.c = tok.tokv.c}; + return ret; + } else if (iret == 0) { + // Ignore + // Too noisy printf("Duplicated macro %s\n", mname_dup); + free(mname_dup); + macro_del(&m); + } else { + kh_val(src->macros_map, kh_k) = m; + } + if (tok.tokt == PPTOK_NEWLINE) goto check_next_token; + else goto start_cur_token; + } + } else if (!strcmp(string_content(tok.tokv.str), "undef")) { + string_del(tok.tokv.str); + tok = ppsrc_next_token(src); + if ((tok.tokt != PPTOK_IDENT) && (tok.tokt != PPTOK_IDENT_UNEXP)) { + printf("Invalid token type %u after '#undef' preprocessor command\n", tok.tokt); + goto preproc_hash_err; + } + string_t *mname = tok.tokv.str; + tok = ppsrc_next_token(src); // Token was moved + while ((tok.tokt != PPTOK_NEWLINE) && (tok.tokt != PPTOK_EOF) && (tok.tokt != PPTOK_INVALID)) { + // TODO: Print warning 'ignored token(s)' + preproc_token_del(&tok); + tok = ppsrc_next_token(src); + } + khiter_t it = kh_get(macros_map, src->macros_map, string_content(mname)); + if (it != kh_end(src->macros_map)) { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" + free((void*)kh_key(src->macros_map, it)); +#pragma GCC diagnostic pop + macro_del(&kh_val(src->macros_map, it)); + kh_del(macros_map, src->macros_map, it); + } + string_del(mname); + if (tok.tokt == PPTOK_NEWLINE) goto check_next_token; + else goto start_cur_token; + } else if (!strcmp(string_content(tok.tokv.str), "error")) { + printf("Error: #error command \n"); + string_del(tok.tokv.str); + vector_clear(ppsource, src->prep); + goto check_next_token; // Returns EOF + } else if (!strcmp(string_content(tok.tokv.str), "warning")) { + printf("Warning: #warning command \n"); + goto preproc_hash_err; + } else if (!strcmp(string_content(tok.tokv.str), "pragma")) { + string_del(tok.tokv.str); + tok = ppsrc_next_token(src); + if ((tok.tokt != PPTOK_IDENT) && (tok.tokt != PPTOK_IDENT_UNEXP)) { + printf("Unknown pragma directive, skipping until EOL\n"); + goto preproc_hash_err; + } else if (!strcmp(string_content(tok.tokv.str), "wrappers")) { + string_del(tok.tokv.str); + tok = ppsrc_next_token(src); + if ((tok.tokt != PPTOK_IDENT) && (tok.tokt != PPTOK_IDENT_UNEXP)) { + printf("Unknown pragma wrappers directive, skipping until EOL\n"); + goto preproc_hash_err; + } else if (!strcmp(string_content(tok.tokv.str), "allow_ints_ext")) { + while ((tok.tokt != PPTOK_NEWLINE) && (tok.tokt != PPTOK_EOF) && (tok.tokt != PPTOK_INVALID)) { + preproc_token_del(&tok); + tok = ppsrc_next_token(src); + } + if (tok.tokt == PPTOK_INVALID) goto start_cur_token; + else { + ret.tokt = PTOK_PRAGMA; + ret.tokv.pragma.typ = PRAGMA_ALLOW_INTS; + return ret; + } + } else if (!strcmp(string_content(tok.tokv.str), "explicit_simple")) { + string_del(tok.tokv.str); + tok = ppsrc_next_token(src); + if ((tok.tokt != PPTOK_IDENT) && (tok.tokt != PPTOK_IDENT_UNEXP)) { + printf("Invalid pragma wrappers explicit_simple directive, skipping until EOL\n"); + goto preproc_hash_err; + } + string_t *id = tok.tokv.str; + tok = ppsrc_next_token(src); + while ((tok.tokt != PPTOK_NEWLINE) && (tok.tokt != PPTOK_EOF) && (tok.tokt != PPTOK_INVALID)) { + preproc_token_del(&tok); + tok = ppsrc_next_token(src); + } + if (tok.tokt == PPTOK_INVALID) { + string_del(id); + goto start_cur_token; + } else { + ret.tokt = PTOK_PRAGMA; + ret.tokv.pragma.typ = PRAGMA_MARK_SIMPLE; + ret.tokv.pragma.val = id; + return ret; + } + } else { + printf("Unknown pragma wrappers directive '%s', skipping until EOL\n", string_content(tok.tokv.str)); + goto preproc_hash_err; + } + } else { + printf("Unknown pragma directive '%s', skipping until EOL\n", string_content(tok.tokv.str)); + goto preproc_hash_err; + } + } else if (!strcmp(string_content(tok.tokv.str), "if")) { + if (vector_last(ppsource, src->prep).srct != PPSRC_PREPARE) { + printf("Error: invalid #if source type %u\n", vector_last(ppsource, src->prep).srct); + src->st = PPST_NONE; + ret.tokt = PTOK_INVALID; + ret.tokv = (union proc_token_val_u){.c = '\0'}; + return ret; + } + string_del(tok.tokv.str); + VECTOR(preproc) *cond = vector_new(preproc); + if (!cond) { + printf("Error: failed to allocate #if condition vector\n"); + src->st = PPST_NONE; + ret.tokt = PTOK_INVALID; + ret.tokv = (union proc_token_val_u){.c = '\0'}; + return ret; + } + tok = ppsrc_next_token(src); + while ((tok.tokt != PPTOK_NEWLINE) && (tok.tokt != PPTOK_EOF) && (tok.tokt != PPTOK_INVALID)) { + if (!vector_push(preproc, cond, tok)) { + printf("Error: failed to add token to #if condition vector\n"); + vector_del(preproc, cond); + src->st = PPST_NONE; + ret.tokt = PTOK_INVALID; + ret.tokv = (union proc_token_val_u){.c = '\0'}; + return ret; + } + tok = ppsrc_next_token(src); + } + vector_trim(preproc, cond); + khash_t(string_set) *solved_macros = kh_init(string_set); + if (!solved_macros) { + printf("Error: failed to allocate #if solved_macros set\n"); + vector_del(preproc, cond); + src->st = PPST_NONE; + ret.tokt = PTOK_INVALID; + ret.tokv = (union proc_token_val_u){.c = '\0'}; + return ret; + } + + VECTOR(preproc) *expanded = proc_do_expand(src->macros_map, cond, solved_macros, src->macros_used); + vector_del(preproc, cond); + macros_set_del(solved_macros); + if (!expanded) { + printf("Error: failed to expand #if condition\n"); + src->st = PPST_NONE; + ret.tokt = PTOK_INVALID; + ret.tokv = (union proc_token_val_u){.c = '\0'}; + return ret; + } + + // Now we need to compute what is pointed by expanded, and increase cond_depth and ok_depth as needed + int st; + int64_t res = preproc_eval(expanded, &st); + vector_del(preproc, expanded); + if (!st) { + printf("Error: failed to evaluate #if condition (%s)\n", src->cur_file); + src->st = PPST_NONE; + ret.tokt = PTOK_INVALID; + ret.tokv = (union proc_token_val_u){.c = '\0'}; + return ret; + } + ++vector_last(ppsource, src->prep).srcv.prep.cond_depth; + if (res) { + vector_last(ppsource, src->prep).srcv.prep.entered_next_ok_cond = 1; + ++vector_last(ppsource, src->prep).srcv.prep.ok_depth; + if (tok.tokt == PPTOK_NEWLINE) goto check_next_token; + else goto start_cur_token; + } else { + vector_last(ppsource, src->prep).srcv.prep.entered_next_ok_cond = 0; + if (tok.tokt == PPTOK_NEWLINE) goto check_if_depth; + else goto start_cur_token; // Returns immediately + } + } else if (!strcmp(string_content(tok.tokv.str), "ifdef")) { + if (vector_last(ppsource, src->prep).srct != PPSRC_PREPARE) { + printf("Error: invalid #ifdef source type %u\n", vector_last(ppsource, src->prep).srct); + goto preproc_hash_err; + } + string_del(tok.tokv.str); + tok = ppsrc_next_token(src); + if ((tok.tokt != PPTOK_IDENT) && (tok.tokt != PPTOK_IDENT_UNEXP)) { + printf("Invalid token type %u after '#ifdef' preprocessor command\n", tok.tokt); + goto preproc_hash_err; + } + khiter_t it = kh_get(macros_map, src->macros_map, string_content(tok.tokv.str)); + int iret; + char *mname = string_steal(tok.tokv.str); + kh_put(string_set, src->macros_used, mname, &iret); + if (iret <= 0) { + free(mname); + } + tok.tokt = PPTOK_SYM; + tok.tokv.sym = LAST_SYM; + // We don't care about iret(?) + // TODO: check iret, error if needed + ++vector_last(ppsource, src->prep).srcv.prep.cond_depth; + if (it != kh_end(src->macros_map)) { + vector_last(ppsource, src->prep).srcv.prep.entered_next_ok_cond = 1; + ++vector_last(ppsource, src->prep).srcv.prep.ok_depth; + goto preproc_hash_err; + } else { + vector_last(ppsource, src->prep).srcv.prep.entered_next_ok_cond = 0; + goto preproc_hash_err_goto; + } + } else if (!strcmp(string_content(tok.tokv.str), "ifndef")) { + if (vector_last(ppsource, src->prep).srct != PPSRC_PREPARE) { + printf("Error: invalid #ifndef source type %u\n", vector_last(ppsource, src->prep).srct); + goto preproc_hash_err; + } + string_del(tok.tokv.str); + tok = ppsrc_next_token(src); + if ((tok.tokt != PPTOK_IDENT) && (tok.tokt != PPTOK_IDENT_UNEXP)) { + printf("Invalid token type %u after '#ifndef' preprocessor command\n", tok.tokt); + goto preproc_hash_err; + } + khiter_t it = kh_get(macros_map, src->macros_map, string_content(tok.tokv.str)); + int iret; + char *mname = string_steal(tok.tokv.str); + kh_put(string_set, src->macros_used, mname, &iret); + if (iret <= 0) { + free(mname); + } + tok.tokt = PPTOK_SYM; + tok.tokv.sym = LAST_SYM; + // We don't care about iret(?) + // TODO: check iret, error if needed + ++vector_last(ppsource, src->prep).srcv.prep.cond_depth; + if (it == kh_end(src->macros_map)) { + vector_last(ppsource, src->prep).srcv.prep.entered_next_ok_cond = 1; + ++vector_last(ppsource, src->prep).srcv.prep.ok_depth; + goto preproc_hash_err; + } else { + vector_last(ppsource, src->prep).srcv.prep.entered_next_ok_cond = 0; + goto preproc_hash_err_goto; + } + } else if (!strcmp(string_content(tok.tokv.str), "elif")) { + // We are already in an #if or #elif (or #else) with a match, so we need to leave it and go to the very top + if (vector_last(ppsource, src->prep).srct != PPSRC_PREPARE) { + printf("Error: invalid #elif source type %u\n", vector_last(ppsource, src->prep).srct); + goto preproc_hash_err; + } + if (vector_last(ppsource, src->prep).srcv.prep.ok_depth) { + --vector_last(ppsource, src->prep).srcv.prep.ok_depth; + goto preproc_hash_err_goto; + } else { + printf("Warning: unexpected #elif preprocessor command\n"); + goto preproc_hash_err; + } + } else if (!strcmp(string_content(tok.tokv.str), "elifdef")) { + // We are already in an #if or #elif (or #else) with a match, so we need to leave it and go to the very top + if (vector_last(ppsource, src->prep).srct != PPSRC_PREPARE) { + printf("Error: invalid #elifdef source type %u\n", vector_last(ppsource, src->prep).srct); + goto preproc_hash_err; + } + string_del(tok.tokv.str); + tok = ppsrc_next_token(src); + int iret; + char *mname = string_steal(tok.tokv.str); + kh_put(string_set, src->macros_used, mname, &iret); + if (iret <= 0) { + free(mname); + } + tok.tokt = PPTOK_SYM; + tok.tokv.sym = LAST_SYM; + // We don't care about iret(?) + // TODO: check iret, error if needed + if (vector_last(ppsource, src->prep).srcv.prep.ok_depth) { + --vector_last(ppsource, src->prep).srcv.prep.ok_depth; + goto preproc_hash_err_goto; + } else { + printf("Warning: unexpected #elifdef preprocessor command\n"); + goto preproc_hash_err; + } + } else if (!strcmp(string_content(tok.tokv.str), "elifndef")) { + // We are already in an #if or #elif (or #else) with a match, so we need to leave it and go to the very top + if (vector_last(ppsource, src->prep).srct != PPSRC_PREPARE) { + printf("Error: invalid #elifndef source type %u\n", vector_last(ppsource, src->prep).srct); + goto preproc_hash_err; + } + string_del(tok.tokv.str); + tok = ppsrc_next_token(src); + int iret; + char *mname = string_steal(tok.tokv.str); + kh_put(string_set, src->macros_used, mname, &iret); + if (iret <= 0) { + free(mname); + } + tok.tokt = PPTOK_SYM; + tok.tokv.sym = LAST_SYM; + // We don't care about iret(?) + // TODO: check iret, error if needed + if (vector_last(ppsource, src->prep).srcv.prep.ok_depth) { + --vector_last(ppsource, src->prep).srcv.prep.ok_depth; + goto preproc_hash_err_goto; + } else { + printf("Warning: unexpected #elifndef preprocessor command\n"); + goto preproc_hash_err; + } + } else if (!strcmp(string_content(tok.tokv.str), "else")) { + // We are already in an #if or #elif (or #else) with a match, so we need to leave it and go to the very top + if (vector_last(ppsource, src->prep).srct != PPSRC_PREPARE) { + printf("Error: invalid #else source type %u\n", vector_last(ppsource, src->prep).srct); + goto preproc_hash_err; + } + if (vector_last(ppsource, src->prep).srcv.prep.ok_depth) { + --vector_last(ppsource, src->prep).srcv.prep.ok_depth; + goto preproc_hash_err_goto; + } else { + printf("Warning: unexpected #else preprocessor command\n"); + goto preproc_hash_err; + } + } else if (!strcmp(string_content(tok.tokv.str), "endif")) { + if (vector_last(ppsource, src->prep).srct != PPSRC_PREPARE) { + printf("Error: invalid #endif source type %u\n", vector_last(ppsource, src->prep).srct); + goto preproc_hash_err; + } + if (vector_last(ppsource, src->prep).srcv.prep.ok_depth) { + --vector_last(ppsource, src->prep).srcv.prep.ok_depth; + --vector_last(ppsource, src->prep).srcv.prep.cond_depth; + } else { + printf("Warning: unexpected #endif preprocessor command\n"); + } + goto preproc_hash_err; + } + + printf("Unknown pp command %s (%s), skipping until EOL\n", string_content(tok.tokv.str), src->cur_file); + preproc_hash_err: + while ((tok.tokt != PPTOK_NEWLINE) && (tok.tokt != PPTOK_EOF) && (tok.tokt != PPTOK_INVALID)) { + preproc_token_del(&tok); + tok = ppsrc_next_token(src); + } + if (tok.tokt == PPTOK_NEWLINE) goto check_next_token; + else goto start_cur_token; + + preproc_hash_err_goto: + while ((tok.tokt != PPTOK_NEWLINE) && (tok.tokt != PPTOK_EOF) && (tok.tokt != PPTOK_INVALID)) { + preproc_token_del(&tok); + tok = ppsrc_next_token(src); + } + if (tok.tokt == PPTOK_NEWLINE) goto check_if_depth; + else goto start_cur_token; // Returns immediately + } + src->st = PPST_NONE; + ret.tokt = PTOK_SYM; + ret.tokv = (union proc_token_val_u){.sym = tok.tokv.sym}; + return ret; + case PPTOK_NEWLINE: + src->st = PPST_NL; + goto check_next_token; + case PPTOK_BLANK: + src->st = PPST_NONE; + ret.tokt = PTOK_INVALID; + ret.tokv = (union proc_token_val_u){.c = tok.tokv.c}; + return ret; + case PPTOK_START_LINE_COMMENT: + src->st = PPST_NONE; + ret.tokt = PTOK_INVALID; + ret.tokv = (union proc_token_val_u){.c = tok.tokv.c}; + return ret; + case PPTOK_EOF: + if ((vector_last(ppsource, src->prep).srct == PPSRC_PREPARE) && vector_last(ppsource, src->prep).srcv.prep.cond_depth) { + printf("Error: file ended before closing all conditionals (ignoring)\n"); + } + if (vector_last(ppsource, src->prep).srct == PPSRC_PREPARE) { + if (src->dirname) free(src->dirname); + if (src->cur_file) free(src->cur_file); + src->dirname = vector_last(ppsource, src->prep).srcv.prep.old_dirname; + src->cur_file = vector_last(ppsource, src->prep).srcv.prep.old_filename; + src->is_sys = vector_last(ppsource, src->prep).srcv.prep.was_sys; + src->cur_pathno = vector_last(ppsource, src->prep).srcv.prep.old_pathno; + vector_last(ppsource, src->prep).srcv.prep.old_dirname = NULL; + vector_last(ppsource, src->prep).srcv.prep.old_filename = NULL; + } + vector_pop(ppsource, src->prep); + src->st = PPST_NL; // Should be redundant since TOK_NEWLINE is added before TOK_EOF if required + // EOF has an empty destructor + // Note that since we have opened the file, the previous file also had ok_depth == cond_depth + goto check_next_token; + + default: + printf("Unknown next pp token type %u, sending EOF\n", tok.tokt); + ret.tokt = PTOK_EOF; + ret.tokv = (union proc_token_val_u){.c = (char)EOF}; + return ret; + } +} +proc_token_t proc_next_token(preproc_t *src) { + proc_token_t ret = proc_next_token_aux(src); + if ((ret.tokt == PTOK_STRING) && ret.tokv.sisstr) { + while (1) { + proc_token_t ret2 = proc_next_token_aux(src); + if ((ret.tokt == PTOK_STRING) && ret.tokv.sisstr) { + if (!string_add_string(ret.tokv.sstr, ret2.tokv.sstr)) { + printf("Error: failed to concatenate adjacent strings\n"); + string_del(ret.tokv.sstr); + string_del(ret2.tokv.sstr); + src->st = PPST_NONE; + ret.tokt = PTOK_INVALID; + ret.tokv = (union proc_token_val_u){.c = '\0'}; + return ret; + } + string_del(ret2.tokv.sstr); + } else { + if (!proc_unget_token(src, &ret2)) { + printf("Error: failed to unget token next to string token\n"); + string_del(ret.tokv.sstr); + proc_token_del(&ret2); + src->st = PPST_NONE; + ret.tokt = PTOK_INVALID; + ret.tokv = (union proc_token_val_u){.c = '\0'}; + return ret; + } + return ret; + } + } + } else return ret; +} diff --git a/wrapperhelper/src/preproc.h b/wrapperhelper/src/preproc.h new file mode 100644 index 000000000..3ba2e06cb --- /dev/null +++ b/wrapperhelper/src/preproc.h @@ -0,0 +1,17 @@ +#pragma once + +#ifndef PREPROC_H +#define PREPROC_H + +#include + +#include "lang.h" + +typedef struct preproc_s preproc_t; + +preproc_t *preproc_new_file(FILE *f, char *dirname, const char *filename); // Takes ownership of f and dirname +proc_token_t proc_next_token(preproc_t *src); +int proc_unget_token(preproc_t *src, proc_token_t *tok); +void preproc_del(preproc_t *src); + +#endif // PREPROC_H diff --git a/wrapperhelper/src/vector.c b/wrapperhelper/src/vector.c new file mode 100644 index 000000000..2cccf09d3 --- /dev/null +++ b/wrapperhelper/src/vector.c @@ -0,0 +1,105 @@ +#include "vector.h" + +VECTOR_IMPL(voidp, (void)) +VECTOR_IMPL(char, (void)) +VECTOR_IMPL(charp, (void)) +static void stringp_del(string_t **s) { return string_del(*s); } +VECTOR_IMPL(string, stringp_del) + +VECTOR(voidp) *vector_new_impl(void) { + VECTOR(voidp) *ret = malloc(sizeof(*ret)); + if (!ret) return NULL; + ret->vsize = ret->vcap = 0; ret->content_v = NULL; + return ret; +} + +VECTOR(voidp) *vector_new_cap_impl(size_t elem_size, size_t cap) { + VECTOR(voidp) *ret = malloc(sizeof(*ret)); + if (!ret) return NULL; + cap = (cap < VECTOR_MIN_CAP) ? VECTOR_MIN_CAP : cap * 2; + ret->content_v = malloc(cap * elem_size); + if (!ret->content_v) { + free(ret); + return NULL; + } + ret->vcap = cap; + ret->vsize = 0; + return ret; +} + +int vector_reserve_impl(VECTOR(voidp) *v, size_t elem_size, size_t cap) { + size_t new_cap = (cap < VECTOR_MIN_CAP) ? VECTOR_MIN_CAP : cap; + if (new_cap <= v->vcap) return 1; + + void *new_content_v = realloc(v->content_v, elem_size * new_cap); + if (!new_content_v) return 0; + v->content_v = new_content_v; + v->vcap = new_cap; + return 1; +} + +int vector_trim_impl(VECTOR(voidp) *v, size_t elem_size) { + if (v->vsize) { + void *new_content_v = realloc(v->content_v, elem_size * v->vsize); + if (!new_content_v) return 0; + v->content_v = new_content_v; + v->vcap = v->vsize; + return 1; + } else { + free(v->content_v); + v->content_v = NULL; + v->vcap = 0; + return 1; + } +} + +void vector_common_pop_impl(VECTOR(voidp) *v, size_t elem_size) { + if (--v->vsize < v->vcap / 4) { + size_t new_cap = (v->vcap / 2 < VECTOR_MIN_CAP) ? VECTOR_MIN_CAP : v->vcap / 2; + if (new_cap == v->vcap) return; + void *new_content_v = realloc(v->content_v, elem_size * new_cap); + if (!new_content_v) return; // We don't really care if the realloc fails, we just need to not update anything + v->content_v = new_content_v; + v->vcap = new_cap; + } +} + +void vector_common_popn_impl(VECTOR(voidp) *v, size_t n, size_t elem_size) { + if (n > v->vsize) n = v->vsize; + v->vsize -= n; + if (v->vsize < v->vcap / 4) { + size_t new_cap = v->vcap / 2; + while (v->vsize < new_cap / 4) new_cap /= 2; + new_cap = (new_cap < VECTOR_MIN_CAP) ? VECTOR_MIN_CAP : new_cap; + if (new_cap == v->vcap) return; + void *new_content_v = realloc(v->content_v, elem_size * new_cap); + if (!new_content_v) return; // We don't really care if the realloc fails, we just need to not update anything + v->content_v = new_content_v; + v->vcap = new_cap; + } +} + +void vector_common_clear_impl(VECTOR(voidp) *v) { + if (!v->content_v) return; + free(v->content_v); + v->vsize = v->vcap = 0; v->content_v = NULL; +} + +int vector_push_vec_impl(VECTOR(voidp) *v1, VECTOR(voidp) *v2, size_t start, size_t len, size_t elem_size) { + if (start >= v2->vsize) return 1; + if (start + len > v2->vsize) len = v2->vsize - start; + if (!len) return 1; + if (v1->vsize + len > v1->vcap) { + size_t new_cap = (v1->vcap < VECTOR_MIN_CAP) ? VECTOR_MIN_CAP : v1->vcap * 2; + while (v1->vsize + len > new_cap) { + new_cap = new_cap * 2; + } + void *new_content_v = realloc(v1->content_v, elem_size * new_cap); + if (!new_content_v) return 0; + v1->content_v = new_content_v; + v1->vcap = new_cap; + } + memcpy((char*)v1->content_v + elem_size * v1->vsize, (char*)v2->content_v + elem_size * start, elem_size * len); + v1->vsize += len; + return 1; +} diff --git a/wrapperhelper/src/vector.h b/wrapperhelper/src/vector.h new file mode 100644 index 000000000..70319cc25 --- /dev/null +++ b/wrapperhelper/src/vector.h @@ -0,0 +1,213 @@ +#pragma once + +#ifndef VECTOR_H +#define VECTOR_H + +#include +#include +#include +#include + +#include "cstring.h" + +// Note: do not use with empty types; simply use ints in this case. + +/** Thread-unsafe vector implementation + * USAGE: + * ====== + * VECTOR_DECLARE ----------- Declare a new vector type. Takes the name and the element type. + * VECTOR_IMPL -------------- Implements required functions for a vector type. Takes the name and the destructor of a pointer to the element. + * VECTOR_DECLARE_STATIC ---- Declare a new vector type with static implementation. Takes the name and the element type. + * VECTOR_IMPL_STATIC ------- Implements required functions for a vector type. Takes the name and the destructor of a pointer to the element. + * Functions are declared static. + * VECTOR_DEL --------------- Macro that takes the vector name and gives a function taking a vector and deletes it. + * VECTOR ------------------- The vector type. Takes the name. + * VECTOR_ELEM -------------- The element type. Takes the name. + * vector_new --------------- Creates a new vector. Takes the name. + * vector_new_cap ----------- Creates a new vector with a given capacity. Takes the name and the capacity. + * vector_reserve ----------- Ensures a vector has at least a given capacity. Takes the name, the vector and the capacity. May not reduce the vector capacity. + * vector_trim -------------- Ensures a vector has a capacity equal to its size. Takes the name, the vector. May reduce the vector capacity. + * vector_del --------------- Frees a vector. Takes the name and the vector. Destructs the content of the vector. + * vector_steal ------------- Frees a vector and returns the content. Takes the name and the vector. May reduce the vector capacity. + * vector_del_freed --------- Frees a vector without freeing the content. Takes the name and the vector. Does not interact with the content of the vector. + * vector_del_free_from ----- Frees a vector without freeing all of the content. Takes the name, the vector and the first element to free. + * Destroys part of the content of the vector. + * vector_push -------------- Push a new element. Takes the name, the vector and the new element. Does not fail if enough capacity remains. + * vector_push_vec ---------- Push a vector of new elements. Takes the name, the vector and the new element vector. Does not fail if enough capacity remains. + * vector_push_vec_slice ---- Push a slice of a vector of elements. Takes the name, the vector the new element vector, start and size. + * Does not fail if enough capacity remains. + * vector_pop --------------- Pops the last element. Takes the name and the vector. May reduce the vector capacity. + * vector_pop_nodel --------- Pops the last element without freeing it. Takes the name and the vector. May reduce the vector capacity. + * vector_pop_slice --------- Pops the last N element. Takes the name, the vector and the number of elements to remove. + * vector_pop_nodel_slice --- Pops the last N element without freeing them. Takes the name, the vector and the number of elements to remove. + * May reduce the vector capacity. + * vector_clear ------------- Remove every element. Takes the name and the vector. + * vector_clear_nodel ------- Remove every element without freeing them. Takes the name and the vector. + * vector_remove ------------ Removes an element. Takes the name, the vector and the element number. May reduce the vector capacity. + * vector_size -------------- Vector size (number of elements). Takes the name and the vector. + * vector_cap --------------- Vector capacity (number of elements). Takes the name and the vector. + * vector_content ----------- Pointer to the vector content. Takes the name and the vector. + * vector_begin ------------- Start of the vector content. Takes the name and the vector. + * vector_end --------------- End of the vector content. Points to unmanaged memory. Takes the name and the vector. + * vector_last -------------- Last element of the vector. Points to invalid memory if size is zero. Takes the name and the vector. + * vector_for --------------- Iterate over the elements of a vector. This is a for loop. Takes the name, the iterator name and the vector. + * + * VECTOR_DEL(name)(v) is equivalent to vector_del(name, v). + * Predefined vectors: string (string_t*), char (char), charp (char*), voidp (void*) + * + * EXAMPLE: + * ======== + * Header myvec.h: + * --------------- +// ... +VECTOR_DECLARE(myvec, my_elem_t*) +// ... + + * Source myvec.c: + * --------------- +// ... +VECTOR_IMPL(myvec) +// ... + + * Source main.c: + * ------------------- +// ... +extern my_elem_t elems[2]; +int main() { + VECTOR(myvec) *vec = vector_new_cap(myvec, 2); + vector_push(myvec, vec, &elems[0]); + vector_push(myvec, vec, &elems[1]); + vector_for (myvec, it, vec) { + printf("We have an element: %s\n", it->elem_name); + } + vector_del(myvec, vec); +} + */ + +#define VECTOR(name) vector_##name##_t +#define VECTOR_ELEM(name) vector_##name##_elem + +#define VECTOR_MIN_CAP 8 + +#define VECTOR_DECLARE_(name, t, pre) \ + typedef struct vector_##name##_s { \ + size_t vsize; \ + size_t vcap; \ + union { \ + t *content; \ + void *content_v; \ + }; \ + } VECTOR(name); \ + typedef t VECTOR_ELEM(name); \ + pre VECTOR_ELEM(name) *vector_steal_##name(VECTOR(name) *v); \ + pre void vector_pop_del_##name(VECTOR(name) *v); \ + pre void vector_popn_del_##name(VECTOR(name) *v, size_t idx); \ + pre void vector_clear_del_##name(VECTOR(name) *v); \ + pre void vector_del_from_##name(VECTOR(name) *v, size_t idx); \ + pre int vector_push_elem_##name(VECTOR(name) *v, VECTOR_ELEM(name) elem); +#define VECTOR_DECLARE(name, t) VECTOR_DECLARE_(name, t,) +#define VECTOR_DECLARE_STATIC(name, t) VECTOR_DECLARE_(name, t, static) + +VECTOR_DECLARE(voidp, void*) + +VECTOR(voidp) *vector_new_impl(void); +VECTOR(voidp) *vector_new_cap_impl(size_t elem_size, size_t cap); +int vector_reserve_impl(VECTOR(voidp) *v, size_t elem_size, size_t cap); +int vector_trim_impl(VECTOR(voidp) *v, size_t elem_size); +void vector_common_pop_impl(VECTOR(voidp) *v, size_t elem_size); +void vector_common_popn_impl(VECTOR(voidp) *v, size_t n, size_t elem_size); +void vector_common_clear_impl(VECTOR(voidp) *v); +int vector_push_vec_impl(VECTOR(voidp) *v1, VECTOR(voidp) *v2, size_t start, size_t len, size_t elem_size); + +#define vector_new(name) (VECTOR(name)*)vector_new_impl() +#define vector_new_cap(name, cap) (VECTOR(name)*)vector_new_cap_impl(sizeof(VECTOR_ELEM(name)), (cap)) +#define vector_del(name, v) vector_del_from_##name((v), 0) +#define vector_del_freed(name, v) vector_del_from_##name((v), vector_size(name, (v))) +#define vector_del_free_from(name, v, i) vector_del_from_##name((v), (i)) +#define VECTOR_DEL(name) vector_del_##name +#define vector_steal(name, v) vector_steal_##name((v)) +#define vector_reserve(name, v, cap) vector_reserve_impl((VECTOR(voidp)*)(v), sizeof(VECTOR_ELEM(name)), (cap)) +#define vector_trim(name, v) vector_trim_impl((VECTOR(voidp)*)(v), sizeof(VECTOR_ELEM(name))) +#define vector_push(name, v, e) vector_push_elem_##name((v), (e)) +#define vector_push_vec(name, v1, v2) vector_push_vec_impl((VECTOR(voidp)*)(v1), (VECTOR(voidp)*)(v2), 0, vector_size(name, (v2)), sizeof(VECTOR_ELEM(name))) +#define vector_push_vec_slice(name, v1, v2, s, l) vector_push_vec_impl((VECTOR(voidp)*)(v1), (VECTOR(voidp)*)(v2), (s), (l), sizeof(VECTOR_ELEM(name))) +#define vector_pop(name, v) vector_pop_del_##name((v)) +#define vector_pop_slice(name, v, n) vector_popn_del_##name((VECTOR(voidp)*)(v), (n)) +#define vector_pop_nodel(name, v) vector_common_pop_impl((VECTOR(voidp)*)(v), sizeof(VECTOR_ELEM(name))) +#define vector_pop_nodel_slice(name, v, n) vector_common_popn_impl((VECTOR(voidp)*)(v), (n), sizeof(VECTOR_ELEM(name))) +#define vector_clear(name, v) vector_clear_del_##name((v)) +#define vector_clear_nodel(name, v) vector_common_clear_impl((VECTOR(voidp)*)(v)) +#define vector_remove(name, v, i) vector_remove_##name((v), (i)) + +#define vector_size(name, v) ((v)->vsize) +#define vector_cap(name, v) ((v)->vcap) +#define vector_content(name, v) ((v)->content) +#define vector_begin(name, v) ((v)->content) +#define vector_end(name, v) ((v)->content + (v)->vsize) +#define vector_last(name, v) ((v)->content[(v)->vsize - 1]) +#define vector_for(name, itname, v) \ + for (VECTOR_ELEM(name) *itname = vector_begin(name, (v)); itname < vector_end(name, (v)); ++itname) +#define vector_for_from(name, itname, v, i) \ + for (VECTOR_ELEM(name) *itname = vector_begin(name, (v)) + (i); itname < vector_end(name, (v)); ++itname) +#define vector_for_rev(name, itname, v) \ + for (VECTOR_ELEM(name) *itname = (v)->content ? vector_end(name, (v)) - 1 : NULL; (v)->content && (itname >= vector_begin(name, (v))); --itname) + +#define VECTOR_IMPL_(name, dtor, pre) \ + pre VECTOR_ELEM(name) *vector_steal_##name(VECTOR(name) *v) { \ + vector_trim(name, v); \ + VECTOR_ELEM(name) *ret = v->content; \ + free(v); \ + return ret; \ + } \ + \ + pre void vector_pop_del_##name(VECTOR(name) *v) { \ + if (v->vsize) { \ + dtor(&vector_last(name, v)); \ + vector_common_pop_impl((VECTOR(voidp)*)v, sizeof(VECTOR_ELEM(name))); \ + } \ + } \ + pre void vector_popn_del_##name(VECTOR(name) *v, size_t n) { \ + if (v->vsize > n) n = v->vsize; \ + vector_for_from(name, it, v, v->vsize - n) { dtor(it); } \ + vector_common_popn_impl((VECTOR(voidp)*)v, n, sizeof(VECTOR_ELEM(name))); \ + } \ + \ + pre void vector_remove_##name(VECTOR(name) *v, size_t i) { \ + dtor(v->content + i); \ + memmove(v->content + i, v->content + i + 1, (v->vsize - i - 1) * sizeof(VECTOR_ELEM(name))); \ + vector_common_pop_impl((VECTOR(voidp)*)v, sizeof(VECTOR_ELEM(name))); \ + } \ + \ + pre void vector_clear_del_##name(VECTOR(name) *v) { \ + if (!v->content) return; \ + vector_for(name, it, v) { dtor(it); } \ + free(v->content); \ + v->content = NULL; \ + v->vcap = v->vsize = 0; \ + } \ + \ + pre void vector_del_from_##name(VECTOR(name) *v, size_t idx) { \ + vector_for_from(name, it, v, idx) { dtor(it); } \ + if (v->content) free(v->content); \ + free(v); \ + } \ + \ + pre int vector_push_elem_##name(VECTOR(name) *v, VECTOR_ELEM(name) elem) { \ + if (v->vsize >= v->vcap) { \ + size_t new_cap = (v->vcap < VECTOR_MIN_CAP) ? VECTOR_MIN_CAP : v->vcap * 2; \ + VECTOR_ELEM(name) *new_content = realloc(v->content, sizeof(VECTOR_ELEM(name)) * new_cap); \ + if (!new_content) return 0; \ + v->content = new_content; \ + v->vcap = new_cap; \ + } \ + v->content[v->vsize++] = elem; \ + return 1; \ + } +#define VECTOR_IMPL(name, dtor) VECTOR_IMPL_(name, dtor,) +#define VECTOR_IMPL_STATIC(name, dtor) VECTOR_IMPL_(name, dtor, static inline) + +VECTOR_DECLARE(char, char) +VECTOR_DECLARE(charp, char*) +VECTOR_DECLARE(string, string_t*) + +#endif // VECTOR_H diff --git a/wrapperhelper/utils.h b/wrapperhelper/utils.h deleted file mode 100644 index 06acbb729..000000000 --- a/wrapperhelper/utils.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - -static const clang::Type* StripTypedef(clang::QualType type) { - if (type->isTypedefNameType()) { - return StripTypedef(type->getAs()->getDecl()->getUnderlyingType()); - } else { - return type.getTypePtr(); - } -} - -// FIXME: Need to support other triple except default target triple -static std::string GetDeclHeaderFile(clang::ASTContext& Ctx, clang::Decl* Decl) { - const auto& SourceManager = Ctx.getSourceManager(); - const clang::FileID FileID = SourceManager.getFileID(Decl->getBeginLoc()); - const clang::FileEntry *FileEntry = SourceManager.getFileEntryForID(FileID); - if (FileEntry) { - return FileEntry->getName().str(); - } - return ""; -} \ No newline at end of file