diff options
author | John Hodge <tpg@mutabah.net> | 2017-08-19 17:37:07 +0800 |
---|---|---|
committer | John Hodge <tpg@mutabah.net> | 2017-08-19 17:39:08 +0800 |
commit | a3ca810d74c09235cbd501f606ed1b979691aba3 (patch) | |
tree | 50b94b73cd49849c7bab9bde821ba41d8c9d3f62 | |
parent | 42f772e01d1cae1fa774bb0b670670dbefa813ea (diff) | |
download | mrust-a3ca810d74c09235cbd501f606ed1b979691aba3.tar.gz |
minicargo - Draft implementation, spawns mrustc on windows
-rw-r--r-- | .gitignore | 5 | ||||
-rw-r--r-- | tools/minicargo/build.cpp | 83 | ||||
-rw-r--r-- | tools/minicargo/debug.h | 43 | ||||
-rw-r--r-- | tools/minicargo/helpers.h | 93 | ||||
-rw-r--r-- | tools/minicargo/main.cpp | 42 | ||||
-rw-r--r-- | tools/minicargo/manifest.cpp | 290 | ||||
-rw-r--r-- | tools/minicargo/manifest.h | 117 | ||||
-rw-r--r-- | tools/minicargo/toml.cpp | 323 | ||||
-rw-r--r-- | tools/minicargo/toml.h | 127 | ||||
-rw-r--r-- | vsproject/minicargo/minicargo.vcxproj | 127 | ||||
-rw-r--r-- | vsproject/minicargo/minicargo.vcxproj.filters | 45 | ||||
-rw-r--r-- | vsproject/mrustc.sln | 12 |
12 files changed, 1299 insertions, 8 deletions
@@ -19,6 +19,8 @@ /vsproject/x64 /vsproject/tu_test/Win32 /vsproject/tu_test/x64 +/vsproject/minicargo/Win32 +/vsproject/minicargo/x64 /vsproject/packages /vsproject/*.sdf /vsproject/*.VC.db @@ -26,6 +28,9 @@ /vsproject/*.opendb /vsproject/*.user +/vsproject/*/*.opendb +/vsproject/*/*.user + /run_rustc/output /gitstats diff --git a/tools/minicargo/build.cpp b/tools/minicargo/build.cpp new file mode 100644 index 00000000..a8032760 --- /dev/null +++ b/tools/minicargo/build.cpp @@ -0,0 +1,83 @@ +/* + */ +#include "manifest.h" +#include <vector> +#include <algorithm> + +struct BuildList +{ + struct BuildEnt { + const PackageManifest* package; + unsigned level; + }; + ::std::vector<BuildEnt> m_list; + + void add_package(const PackageManifest& p, unsigned level); + void sort_list(); + + struct Iter { + const BuildList& l; + size_t i; + + const PackageManifest& operator*() const { + return *this->l.m_list[this->l.m_list.size() - this->i - 1].package; + } + void operator++() { + this->i++; + } + bool operator!=(const Iter& x) const { + return this->i != x.i; + } + Iter begin() const { + return *this; + } + Iter end() { + return Iter{ this->l, this->l.m_list.size() }; + } + }; + + Iter iter() const { + return Iter { *this, 0 }; + } +}; + +void MiniCargo_Build(const PackageManifest& manifest) +{ + BuildList list; + // Generate sorted dependency list + for (const auto& dep : manifest.dependencies()) + { + list.add_package(dep.get_package(), 1); + } + + + // Build dependencies + for(const auto& p : list.iter()) + { + p.build_lib(); + } + + manifest.build_lib(); + // TODO: If the manifest doesn't have a library, build the binary +} + +void BuildList::add_package(const PackageManifest& p, unsigned level) +{ + for(auto& ent : m_list) + { + if(ent.package == &p) + { + ent.level = level; + return ; + } + } + m_list.push_back({ &p, level }); + for (const auto& dep : p.dependencies()) + { + add_package(dep.get_package(), level+1); + } +} +void BuildList::sort_list() +{ + ::std::sort(m_list.begin(), m_list.end(), [](const auto& a, const auto& b){ return a.level < b.level; }); +} diff --git a/tools/minicargo/debug.h b/tools/minicargo/debug.h new file mode 100644 index 00000000..5cc338e3 --- /dev/null +++ b/tools/minicargo/debug.h @@ -0,0 +1,43 @@ +#pragma once + +#include <functional> +#include <vector> +#include <sstream> + +extern void Debug_Print(::std::function<void(::std::ostream& os)> cb); + +#define DEBUG(fmt) do { Debug_Print([&](auto& os){ os << "DEBUG: " << fmt; }); } while(0) +#define TODO(fmt) do { Debug_Print([&](auto& os){ os << "DEBUG: " << fmt; }); abort(); } while(0) + +namespace { + static void format_to_stream(::std::ostream& os) { + } + template<typename T, typename... A> + static void format_to_stream(::std::ostream& os, const T& v, const A&... a) { + os << v; + format_to_stream(os, a...); + } +} + +template<typename ...T> +::std::string format(const T&... v) +{ + ::std::stringstream ss; + format_to_stream(ss, v...); + return ss.str(); +} + +template<typename T> +::std::ostream& operator<<(::std::ostream& os, const ::std::vector<T>& v) +{ + bool first = true; + for(const auto& e : v) + { + if(!first) + os << ","; + os << e; + first = false; + } + return os; +} + diff --git a/tools/minicargo/helpers.h b/tools/minicargo/helpers.h new file mode 100644 index 00000000..d811f21a --- /dev/null +++ b/tools/minicargo/helpers.h @@ -0,0 +1,93 @@ +#pragma once + +#include <string> + +namespace helpers { + + +/// Path helper class (because I don't want to include boost) +class path +{ +#ifdef _WIN32 + const char SEP = '\\'; +#else + const char SEP = '/'; +#endif + + ::std::string m_str; + + path() + { + } +public: + path(const ::std::string& s): + path(s.c_str()) + { + } + path(const char* s): + m_str(s) + { + // 1. Normalise path separators to the system specified separator + for(size_t i = 0; i < m_str.size(); i ++) + { + if( m_str[i] == '/' || m_str[i] == '\\' ) + m_str[i] = SEP; + } + + // 2. Remove any trailing separators + if( !m_str.empty() ) + { + while(!m_str.empty() && m_str.back() == SEP ) + m_str.pop_back(); + if(m_str.empty()) + { + m_str.push_back(SEP); + } + } + else + { + throw ::std::runtime_error("Empty path being constructed"); + } + } + + path operator/(const path& p) const + { + if(p.m_str[0] == '/') + throw ::std::runtime_error("Appending an absolute path to another path"); + return *this / p.m_str.c_str(); + } + path operator/(const char* o) const + { + auto rv = *this; + rv.m_str.push_back(SEP); + rv.m_str.append(o); + return rv; + } + + path parent() const + { + auto pos = m_str.find_last_of(SEP); + if(pos == ::std::string::npos) + { + return *this; + } + else + { + path rv; + rv.m_str = m_str.substr(0, pos); + return rv; + } + } + + operator ::std::string() const + { + return m_str; + } + + friend ::std::ostream& operator<<(::std::ostream& os, const path& p) + { + return os << p.m_str; + } +}; + +} // namespace helpers
\ No newline at end of file diff --git a/tools/minicargo/main.cpp b/tools/minicargo/main.cpp index 89097928..b1e21c69 100644 --- a/tools/minicargo/main.cpp +++ b/tools/minicargo/main.cpp @@ -4,6 +4,13 @@ * main.cpp * - Entrypoint */ +#include <iostream> +#include <cstring> // strcmp +#include "debug.h" +#include "manifest.h" +#include "helpers.h" + +extern void MiniCargo_Build(const PackageManifest& manifest); struct ProgramOptions { @@ -16,6 +23,7 @@ struct ProgramOptions const char* output_directory = nullptr; int parse(int argc, const char* argv[]); + void usage() const; }; int main(int argc, const char* argv[]) @@ -26,9 +34,18 @@ int main(int argc, const char* argv[]) } // 1. Load the Cargo.toml file from the passed directory + auto m = PackageManifest::load_from_toml( ::helpers::path(opts.directory ? opts.directory : ".") / "Cargo.toml" ); + // 2. Recursively load dependency manifests - // 3. Generate makefile for all dependencies + for(const auto& dep : m.dependencies()) + { + throw "TODO: Deps"; + } + // 3. Build dependency tree + MiniCargo_Build(m); + + throw ""; return 0; } @@ -44,17 +61,14 @@ int ProgramOptions::parse(int argc, const char* argv[]) if( !this->directory ) { this->directory = arg; } - else if( !this->outfile ) { - this->outfile = arg; - } else { } } - else if( argv[1] != '-' ) + else if( arg[1] != '-' ) { // Short arguments } - else if( argv[1] == '\0' ) + else if( arg[1] == '\0' ) { all_free = true; } @@ -74,7 +88,7 @@ int ProgramOptions::parse(int argc, const char* argv[]) } } - if( !this->directory || !this->outfile ) + if( !this->directory /*|| !this->outfile*/ ) { usage(); exit(1); @@ -83,3 +97,17 @@ int ProgramOptions::parse(int argc, const char* argv[]) return 0; } +void ProgramOptions::usage() const +{ + ::std::cerr + << "Usage: minicargo <package dir>" << ::std::endl + ; +} + + + +void Debug_Print(::std::function<void(::std::ostream& os)> cb) +{ + cb(::std::cout); + ::std::cout << ::std::endl; +} diff --git a/tools/minicargo/manifest.cpp b/tools/minicargo/manifest.cpp new file mode 100644 index 00000000..69660702 --- /dev/null +++ b/tools/minicargo/manifest.cpp @@ -0,0 +1,290 @@ +/* + */ +#include "manifest.h" +#include "toml.h" +#include "debug.h" +#include "helpers.h" +#include <cassert> +#include <algorithm> +#include <sstream> +#ifdef _WIN32 +#include <Windows.h> +#endif + +static ::std::vector<::std::shared_ptr<PackageManifest>> g_loaded_manifests; + +PackageManifest::PackageManifest() +{ +} + +namespace +{ + void target_edit_from_kv(PackageTarget& target, TomlKeyValue& kv, unsigned base_idx); +} + +PackageManifest PackageManifest::load_from_toml(const ::std::string& path) +{ + PackageManifest rv; + rv.m_manmifest_path = path; + + TomlFile toml_file(path); + + for(auto key_val : toml_file) + { + assert(key_val.path.size() > 0); + const auto& section = key_val.path[0]; + if( section == "package" ) + { + assert(key_val.path.size() > 1); + const auto& key = key_val.path[1]; + if(key == "authors") + { + // TODO: Use the `authors` key + } + else if( key == "name" ) + { + if(rv.m_name != "" ) + { + // TODO: Warn/error + throw ::std::runtime_error("Package name set twice"); + } + rv.m_name = key_val.value.as_string(); + if(rv.m_name == "") + { + // TODO: Error + throw ::std::runtime_error("Package name cannot be empty"); + } + } + else if( key == "version" ) + { + rv.m_version = PackageVersion::from_string(key_val.value.as_string()); + } + else + { + // Unknown value in `package` + throw ::std::runtime_error("Unknown key `" + key + "` in [package]"); + } + } + else if( section == "lib" ) + { + // TODO: Parse information related to use as a library + // 1. Find (and add if needed) the `lib` descriptor + auto it = ::std::find_if(rv.m_targets.begin(), rv.m_targets.end(), [](const auto& x){ return x.m_type == PackageTarget::Type::Lib; }); + if(it == rv.m_targets.end()) + it = rv.m_targets.insert(it, PackageTarget { PackageTarget::Type::Lib }); + + target_edit_from_kv(*it, key_val, 1); + } + else if( section == "bin" ) + { + assert(key_val.path.size() > 1); + unsigned idx = ::std::stoi( key_val.path[1] ); + + auto it = ::std::find_if(rv.m_targets.begin(), rv.m_targets.end(), [&idx](const auto& x) { return x.m_type == PackageTarget::Type::Bin && idx-- == 0; }); + if (it == rv.m_targets.end()) + it = rv.m_targets.insert(it, PackageTarget{ PackageTarget::Type::Bin }); + + target_edit_from_kv(*it, key_val, 2); + } + else if (section == "test") + { + assert(key_val.path.size() > 1); + unsigned idx = ::std::stoi(key_val.path[1]); + + auto it = ::std::find_if(rv.m_targets.begin(), rv.m_targets.end(), [&idx](const auto& x) { return x.m_type == PackageTarget::Type::Test && idx-- == 0; }); + if (it == rv.m_targets.end()) + it = rv.m_targets.insert(it, PackageTarget{ PackageTarget::Type::Test }); + + target_edit_from_kv(*it, key_val, 2); + } + else if( section == "dependencies" ) + { + assert(key_val.path.size() > 1); + + const auto& depname = key_val.path[1]; + + // Find/create dependency descriptor + auto it = ::std::find_if(rv.m_dependencies.begin(), rv.m_dependencies.end(), [&](const auto& x) { return x.m_name == depname; }); + bool was_added = (it == rv.m_dependencies.end()); + if (it == rv.m_dependencies.end()) + { + it = rv.m_dependencies.insert(it, PackageRef{ depname }); + } + auto& ref = *it; + + if( key_val.path.size() == 2 ) + { + // Shorthand, picks a version from the package repository + if(!was_added) + { + throw ::std::runtime_error(::format("ERROR: Duplicate dependency `", depname, "`")); + } + + const auto& version_spec_str = key_val.value.as_string(); + ref.m_version = PackageVersionSpec::from_string(version_spec_str); + } + else + { + + // (part of a) Full dependency specification + const auto& attr = key_val.path[2]; + if( attr == "path" ) + { + // Set path specification of the named depenency + ref.m_path = key_val.value.as_string(); + } + else if (attr == "git") + { + // Load from git repo. + TODO("Support git dependencies"); + } + else if (attr == "branch") + { + // Specify git branch + TODO("Support git dependencies (branch)"); + } + else if( attr == "version") + { + assert(key_val.path.size() == 3); + // Parse version specifier + ref.m_version = PackageVersionSpec::from_string(key_val.value.as_string()); + } + else + { + // TODO: Error + throw ::std::runtime_error(::format("ERROR: Unkown depencency attribute `", attr, "` on dependency `", depname, "`")); + } + } + } + else if( section == "patch" ) + { + //const auto& repo = key_val.path[1]; + } + else + { + // Unknown manifest section + } + } + + return rv; +} + +namespace +{ + void target_edit_from_kv(PackageTarget& target, TomlKeyValue& kv, unsigned base_idx) + { + const auto& key = kv.path[base_idx]; + if(key == "name") + { + assert(kv.path.size() == base_idx+1); + target.m_name = kv.value.as_string(); + } + else if(key == "path") + { + assert(kv.path.size() == base_idx + 1); + target.m_path = kv.value.as_string(); + } + else if(key == "test") + { + assert(kv.path.size() == base_idx + 1); + target.m_enable_test = kv.value.as_bool(); + } + else if (key == "doctest") + { + assert(kv.path.size() == base_idx + 1); + target.m_enable_doctest = kv.value.as_bool(); + } + else if (key == "bench") + { + assert(kv.path.size() == base_idx + 1); + target.m_enable_bench = kv.value.as_bool(); + } + else if (key == "doc") + { + assert(kv.path.size() == base_idx + 1); + target.m_enable_doc = kv.value.as_bool(); + } + else if (key == "plugin") + { + assert(kv.path.size() == base_idx + 1); + target.m_is_plugin = kv.value.as_bool(); + } + else if (key == "proc-macro") + { + assert(kv.path.size() == base_idx + 1); + target.m_is_proc_macro = kv.value.as_bool(); + } + else if (key == "harness") + { + assert(kv.path.size() == base_idx + 1); + target.m_is_own_harness = kv.value.as_bool(); + } + else + { + throw ::std::runtime_error( ::format("TODO: Handle target option `", key, "`") ); + } + } +} + + +void PackageManifest::build_lib() const +{ + auto it = ::std::find_if(m_targets.begin(), m_targets.end(), [](const auto& x) { return x.m_type == PackageTarget::Type::Lib; }); + if (it == m_targets.end()) + { + throw ::std::runtime_error(::format("Package ", m_name, " doesn't have a library")); + } + ::std::vector<::std::string> args; + args.push_back( ::helpers::path(m_manmifest_path).parent() / ::helpers::path(it->m_path) ); + args.push_back("--crate-name"); args.push_back(it->m_name); + args.push_back("--crate-type"); args.push_back("rlib"); + args.push_back("-o"); args.push_back( ::helpers::path("output") / ::format("lib", it->m_name, ".hir") ); +#ifdef _WIN32 + ::std::stringstream cmdline; + cmdline << "mrustc.exe"; + for(const auto& arg : args) + cmdline << " " << arg; + DEBUG("Calling " << cmdline.str()); + + STARTUPINFO si = {0}; + PROCESS_INFORMATION pi; + CreateProcessA("x64\\Release\\mrustc.exe", (LPSTR)cmdline.str().c_str(), NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi); + WaitForSingleObject(pi.hProcess, INFINITE); + DWORD status = 1; + GetExitCodeProcess(pi.hProcess, &status); + if(status != 0) + { + DEBUG("Compiler exited with non-zero exit status " << status); + throw ""; + } +#elif defined(__posix__) + //spawn(); +#else +#endif +} + +const PackageManifest& PackageRef::get_package() const +{ + throw ""; +} + +PackageVersion PackageVersion::from_string(const ::std::string& s) +{ + PackageVersion rv; + ::std::istringstream iss { s }; + iss >> rv.major; + iss.get(); + iss >> rv.minor; + if( iss.get() != EOF ) + { + iss >> rv.patch; + } + return rv; +} + +PackageVersionSpec PackageVersionSpec::from_string(const ::std::string& s) +{ + PackageVersionSpec rv; + throw ""; + return rv; +}
\ No newline at end of file diff --git a/tools/minicargo/manifest.h b/tools/minicargo/manifest.h new file mode 100644 index 00000000..7bbcfa9c --- /dev/null +++ b/tools/minicargo/manifest.h @@ -0,0 +1,117 @@ +#pragma once + +#include <string> +#include <vector> +#include <memory> + +class PackageManifest; + +struct PackageVersion +{ + unsigned major; + unsigned minor; + unsigned patch; + + static PackageVersion from_string(const ::std::string& s); +}; +struct PackageVersionSpec +{ + struct Bound + { + enum class Type + { + Compatible, + Equal, + Less, + }; + + Type ty; + PackageVersion ver; + }; + ::std::vector<Bound> m_bounds; + + // TODO: Just upper and lower? + static PackageVersionSpec from_string(const ::std::string& s); +}; + +class PackageRef +{ + friend class PackageManifest; + ::std::string m_name; + PackageVersionSpec m_version; + + ::std::string m_path; + ::std::shared_ptr<PackageManifest> m_manifest; + + PackageRef(const ::std::string& n) : + m_name(n) + { + } + +public: + const PackageManifest& get_package() const; +}; + +struct PackageTarget +{ + enum class Type + { + Lib, + Bin, + Test, + Bench, + Example, + }; + + Type m_type; + ::std::string m_name; + ::std::string m_path; + bool m_enable_test = true; + bool m_enable_doctest = true; + bool m_enable_bench = true; + bool m_enable_doc = true; + bool m_is_plugin = false; + bool m_is_proc_macro = false; + bool m_is_own_harness = false; + + PackageTarget(Type ty): + m_type(ty) + { + switch(ty) + { + case Type::Lib: + m_path = "src/lib.rs"; + break; + case Type::Bin: + m_path = "src/main.rs"; + break; + default: + break; + } + } +}; + +class PackageManifest +{ + ::std::string m_manmifest_path; + + ::std::string m_name; + PackageVersion m_version; + + ::std::vector<PackageRef> m_dependencies; + + ::std::vector<PackageTarget> m_targets; + + struct BuildScript + { + }; + + PackageManifest(); +public: + static PackageManifest load_from_toml(const ::std::string& path); + void build_lib() const; + + const ::std::vector<PackageRef>& dependencies() const { + return m_dependencies; + } +}; diff --git a/tools/minicargo/toml.cpp b/tools/minicargo/toml.cpp new file mode 100644 index 00000000..7d4b0685 --- /dev/null +++ b/tools/minicargo/toml.cpp @@ -0,0 +1,323 @@ +/* + * A very bad streaming TOML parser + */ +#include "toml.h" +#include "debug.h" +#include <cassert> +#include <string> + + +struct Token +{ + enum class Type + { + Eof, + SquareOpen, + SquareClose, + BraceOpen, + BraceClose, + Assign, + Newline, + Comma, + Dot, + + Ident, + String, + }; + + Type m_type; + ::std::string m_data; + + static Token lex_from(::std::ifstream& is); + static Token lex_from_inner(::std::ifstream& is); + + const ::std::string& as_string() const { + assert(m_type == Type::Ident || m_type == Type::String); + return m_data; + } + + friend ::std::ostream& operator<<(::std::ostream& os, const Token& x) { + switch(x.m_type) + { + case Type::Eof: os << "Eof"; break; + case Type::SquareOpen: os << "SquareOpen"; break; + case Type::SquareClose: os << "SquareClose"; break; + case Type::BraceOpen: os << "BraceOpen"; break; + case Type::BraceClose: os << "BraceClose"; break; + case Type::Assign: os << "Assign"; break; + case Type::Newline: os << "Newline"; break; + case Type::Comma: os << "Comma"; break; + case Type::Dot: os << "Dot"; break; + case Type::Ident: os << "Ident(" << x.m_data << ")"; break; + case Type::String: os << "String(" << x.m_data << ")"; break; + } + return os; + } +}; + +TomlFile::TomlFile(const ::std::string& filename): + m_if(filename) +{ + if( !m_if.is_open() ) { + throw ::std::runtime_error("Unable to open file '" + filename + "'"); + } +} +TomlFileIter TomlFile::begin() +{ + TomlFileIter rv { *this }; + ++rv; + return rv; +} +TomlFileIter TomlFile::end() +{ + return TomlFileIter { *this }; +} + +TomlKeyValue TomlFile::get_next_value() +{ + auto t = Token::lex_from(m_if); + + if(m_current_composite.empty()) + { + while( t.m_type == Token::Type::Newline ) + t = Token::lex_from(m_if); + + // Expect '[', a string, or an identifier + switch(t.m_type) + { + case Token::Type::Eof: + // Empty return indicates the end of the list + return TomlKeyValue {}; + case Token::Type::SquareOpen: + m_current_block.clear(); + do + { + t = Token::lex_from(m_if); + bool is_array = false; + if(t.m_type == Token::Type::SquareOpen) + { + is_array = true; + t = Token::lex_from(m_if); + } + assert(t.m_type == Token::Type::Ident || t.m_type == Token::Type::String); + m_current_block.push_back(t.as_string()); + if(is_array) + { + m_current_block.push_back(::format(m_array_counts[t.as_string()]++)); + t = Token::lex_from(m_if); + assert(t.m_type == Token::Type::SquareClose); + } + + t = Token::lex_from(m_if); + } while(t.m_type == Token::Type::Dot); + if( t.m_type != Token::Type::SquareClose ) + { + throw ""; + } + t = Token::lex_from(m_if); + if (t.m_type != Token::Type::Newline) + { + throw ""; + } + DEBUG("Start block " << m_current_block); + // TODO: Are empty sections allowed? + //goto recurse; + + t = Token::lex_from(m_if); + break ; + default: + break; + } + } + else + { + // Expect a string or an identifier + if( t.m_type == Token::Type::Eof ) + { + // EOF isn't allowed here + throw ""; + } + } + switch (t.m_type) + { + case Token::Type::String: + case Token::Type::Ident: + break; + default: + throw ""; + } + ::std::string key_name = t.as_string(); + t = Token::lex_from(m_if); + + if(t.m_type != Token::Type::Assign) + throw ""; + t = Token::lex_from(m_if); + + TomlKeyValue rv; + switch(t.m_type) + { + case Token::Type::String: + rv.path = m_current_block; + rv.path.insert(rv.path.begin(), m_current_composite.begin(), m_current_composite.end()); + rv.path.push_back(key_name); + + rv.value = TomlValue { t.m_data }; + break; + case Token::Type::SquareOpen: + rv.path = m_current_block; + rv.path.insert(rv.path.begin(), m_current_composite.begin(), m_current_composite.end()); + rv.path.push_back(key_name); + + rv.value.m_type = TomlValue::Type::List; + while( (t = Token::lex_from(m_if)).m_type != Token::Type::SquareClose ) + { + // TODO: Recurse parse a value + switch(t.m_type) + { + case Token::Type::String: + rv.value.m_sub_values.push_back(TomlValue { t.as_string() }); + break; + default: + throw ""; + } + + t = Token::lex_from(m_if); + if(t.m_type != Token::Type::Comma) + break; + } + if(t.m_type != Token::Type::SquareClose) + throw ""; + break; + case Token::Type::BraceOpen: + m_current_composite.push_back(key_name); + DEBUG("Enter composite block " << m_current_block << ", " << m_current_composite); + // Recurse to restart parse + return get_next_value(); + case Token::Type::Ident: + if( t.m_data == "true" ) + { + rv.path = m_current_block; + rv.path.insert(rv.path.begin(), m_current_composite.begin(), m_current_composite.end()); + rv.path.push_back(key_name); + rv.value = TomlValue { true }; + } + else if( t.m_data == "false" ) + { + rv.path = m_current_block; + rv.path.insert(rv.path.begin(), m_current_composite.begin(), m_current_composite.end()); + rv.path.push_back(key_name); + + rv.value = TomlValue { false }; + } + else + { + throw ""; + } + break; + default: + throw ""; + } + + t = Token::lex_from(m_if); + while (!m_current_composite.empty() && t.m_type == Token::Type::BraceClose) + { + DEBUG("Leave composite block " << m_current_block << ", " << m_current_composite); + m_current_composite.pop_back(); + t = Token::lex_from(m_if); + } + if( m_current_composite.empty() ) + { + // TODO: Allow EOF? + if(t.m_type != Token::Type::Newline) + throw ""; + } + else + { + if( t.m_type != Token::Type::Comma ) + throw ""; + } + return rv; +} + +Token Token::lex_from(::std::ifstream& is) +{ + auto rv = Token::lex_from_inner(is); + DEBUG("lex_from: " << rv); + return rv; +} +Token Token::lex_from_inner(::std::ifstream& is) +{ + int c; + do + { + c = is.get(); + } while( c != EOF && c != '\n' && isspace(c) ); + + ::std::string str; + switch(c) + { + case EOF: return Token { Type::Eof }; + case '[': return Token { Type::SquareOpen }; + case ']': return Token { Type::SquareClose }; + case '{': return Token { Type::BraceOpen }; + case '}': return Token { Type::BraceClose }; + case ',': return Token { Type::Comma }; + case '.': return Token { Type::Dot }; + case '=': return Token { Type::Assign }; + case '\n': return Token { Type::Newline }; + case '#': + while(c != '\n') + { + c = is.get(); + if(c == EOF) + return Token { Type::Eof }; + } + return Token { Type::Newline }; + case '\'': + c = is.get(); + while (c != '\'') + { + if (c == EOF) + throw ""; + if (c == '\\') + { + // TODO: Escaped strings + throw ""; + } + str += (char)c; + c = is.get(); + } + return Token { Type::String, str }; + case '"': + c = is.get(); + while(c != '"') + { + if (c == EOF) + throw ""; + if (c == '\\') + { + // TODO: Escaped strings + throw ""; + } + str += (char)c; + c = is.get(); + } + return Token { Type::String, str }; + default: + if(isalpha(c)) + { + // Identifier + while(isalnum(c) || c == '-') + { + str += (char)c; + c = is.get(); + } + is.putback(c); + return Token { Type::Ident, str }; + } + else + { + throw ""; + } + } +}
\ No newline at end of file diff --git a/tools/minicargo/toml.h b/tools/minicargo/toml.h new file mode 100644 index 00000000..1c8f90d5 --- /dev/null +++ b/tools/minicargo/toml.h @@ -0,0 +1,127 @@ +#pragma once + +#include <fstream> +#include <vector> +#include <unordered_map> + +class TomlFileIter; +struct TomlKeyValue; + +class TomlFile +{ + /// Input file stream + ::std::ifstream m_if; + + /// Name of the current `[]` block + ::std::vector<::std::string> m_current_block; + + /// Path suffix of the current composite (none if empty) + ::std::vector<::std::string> m_current_composite; + + /// Index of the next array field (if zero, not parsing an array) + unsigned int m_next_array_index; + + /// Next indexes if top-level defined arrays (e.g. `[[foo]]`) + ::std::unordered_map<::std::string,unsigned> m_array_counts; + +public: + TomlFile(const ::std::string& filename); + + TomlFileIter begin(); + TomlFileIter end(); + + TomlKeyValue get_next_value(); +}; + +struct TomlValue +{ + enum class Type + { + Boolean, + String, + Integer, + List, + }; + struct TypeError: + public ::std::exception + { + Type have; + Type exp; + + TypeError(Type h, Type e): + have(h), + exp(e) + { + } + + const char* what() const override { + return "toml type error"; + } + }; + + Type m_type; + uint64_t m_int_value; + ::std::string m_str_value; + ::std::vector<TomlValue> m_sub_values; + + TomlValue(): + m_type(Type::String) + { + } + TomlValue(::std::string s): + m_type( Type::String ), + m_str_value(::std::move(s)) + { + } + TomlValue(bool v) : + m_type(Type::Boolean), + m_int_value(v ? 1 : 0) + { + } + + const ::std::string& as_string() const { + if( m_type != Type::String ) { + throw TypeError { m_type, Type::String }; + } + return m_str_value; + } + bool as_bool() const { + if(m_type != Type::Boolean) { + throw TypeError { m_type, Type::Boolean }; + } + return m_int_value != 0; + } +}; + +struct TomlKeyValue +{ + ::std::vector<::std::string> path; + TomlValue value; +}; + +class TomlFileIter +{ + friend class TomlFile; + TomlFile& m_reader; + TomlKeyValue m_cur_value; + + TomlFileIter(TomlFile& tf): + m_reader(tf) + { + + } + +public: + TomlKeyValue operator*() const + { + return m_cur_value; + } + void operator++() + { + m_cur_value = m_reader.get_next_value(); + } + bool operator!=(const TomlFileIter& x) const + { + return m_cur_value.path != x.m_cur_value.path; + } +}; diff --git a/vsproject/minicargo/minicargo.vcxproj b/vsproject/minicargo/minicargo.vcxproj new file mode 100644 index 00000000..9c82ff56 --- /dev/null +++ b/vsproject/minicargo/minicargo.vcxproj @@ -0,0 +1,127 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{15F3D38B-14FF-4872-805D-6D9C52920842}</ProjectGuid> + <RootNamespace>minicargo</RootNamespace> + <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v140</PlatformToolset> + <CharacterSet>MultiByte</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v140</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>MultiByte</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v140</PlatformToolset> + <CharacterSet>MultiByte</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v140</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>MultiByte</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="Shared"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup /> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <SDLCheck>true</SDLCheck> + </ClCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <SDLCheck>true</SDLCheck> + </ClCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <SDLCheck>true</SDLCheck> + </ClCompile> + <Link> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <SDLCheck>true</SDLCheck> + </ClCompile> + <Link> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="..\..\tools\minicargo\build.cpp" /> + <ClCompile Include="..\..\tools\minicargo\main.cpp" /> + <ClCompile Include="..\..\tools\minicargo\manifest.cpp" /> + <ClCompile Include="..\..\tools\minicargo\toml.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\..\tools\minicargo\debug.h" /> + <ClInclude Include="..\..\tools\minicargo\helpers.h" /> + <ClInclude Include="..\..\tools\minicargo\manifest.h" /> + <ClInclude Include="..\..\tools\minicargo\toml.h" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/vsproject/minicargo/minicargo.vcxproj.filters b/vsproject/minicargo/minicargo.vcxproj.filters new file mode 100644 index 00000000..b21142bb --- /dev/null +++ b/vsproject/minicargo/minicargo.vcxproj.filters @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\..\tools\minicargo\main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\tools\minicargo\build.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\tools\minicargo\manifest.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\tools\minicargo\toml.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\..\tools\minicargo\helpers.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\tools\minicargo\manifest.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\tools\minicargo\toml.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\tools\minicargo\debug.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/vsproject/mrustc.sln b/vsproject/mrustc.sln index a74f29e3..3aebce2e 100644 --- a/vsproject/mrustc.sln +++ b/vsproject/mrustc.sln @@ -1,12 +1,14 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.24720.0 +VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mrustc", "mrustc.vcxproj", "{12AA9964-C1BD-406A-9545-43EE63230EBE}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tu_test", "tu_test\tu_test.vcxproj", "{F0A80ABB-A11A-492C-B5FC-E26C29A988D8}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "minicargo", "minicargo\minicargo.vcxproj", "{15F3D38B-14FF-4872-805D-6D9C52920842}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -31,6 +33,14 @@ Global {F0A80ABB-A11A-492C-B5FC-E26C29A988D8}.Release|x64.Build.0 = Release|x64 {F0A80ABB-A11A-492C-B5FC-E26C29A988D8}.Release|x86.ActiveCfg = Release|Win32 {F0A80ABB-A11A-492C-B5FC-E26C29A988D8}.Release|x86.Build.0 = Release|Win32 + {15F3D38B-14FF-4872-805D-6D9C52920842}.Debug|x64.ActiveCfg = Debug|x64 + {15F3D38B-14FF-4872-805D-6D9C52920842}.Debug|x64.Build.0 = Debug|x64 + {15F3D38B-14FF-4872-805D-6D9C52920842}.Debug|x86.ActiveCfg = Debug|Win32 + {15F3D38B-14FF-4872-805D-6D9C52920842}.Debug|x86.Build.0 = Debug|Win32 + {15F3D38B-14FF-4872-805D-6D9C52920842}.Release|x64.ActiveCfg = Release|x64 + {15F3D38B-14FF-4872-805D-6D9C52920842}.Release|x64.Build.0 = Release|x64 + {15F3D38B-14FF-4872-805D-6D9C52920842}.Release|x86.ActiveCfg = Release|Win32 + {15F3D38B-14FF-4872-805D-6D9C52920842}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE |