summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore5
-rw-r--r--tools/minicargo/build.cpp83
-rw-r--r--tools/minicargo/debug.h43
-rw-r--r--tools/minicargo/helpers.h93
-rw-r--r--tools/minicargo/main.cpp42
-rw-r--r--tools/minicargo/manifest.cpp290
-rw-r--r--tools/minicargo/manifest.h117
-rw-r--r--tools/minicargo/toml.cpp323
-rw-r--r--tools/minicargo/toml.h127
-rw-r--r--vsproject/minicargo/minicargo.vcxproj127
-rw-r--r--vsproject/minicargo/minicargo.vcxproj.filters45
-rw-r--r--vsproject/mrustc.sln12
12 files changed, 1299 insertions, 8 deletions
diff --git a/.gitignore b/.gitignore
index cd4fb87a..4488f48c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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