summaryrefslogtreecommitdiff
path: root/tools/minicargo/cfg.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/minicargo/cfg.cpp')
-rw-r--r--tools/minicargo/cfg.cpp262
1 files changed, 262 insertions, 0 deletions
diff --git a/tools/minicargo/cfg.cpp b/tools/minicargo/cfg.cpp
new file mode 100644
index 00000000..e8c732d8
--- /dev/null
+++ b/tools/minicargo/cfg.cpp
@@ -0,0 +1,262 @@
+/*
+ * mrustc "minicargo" (minimal cargo clone)
+ * - By John Hodge (Mutabah)
+ *
+ * cfg.cpp
+ * - Handling of target configuration (in manifest nodes)
+ */
+#include <iostream> // cerr
+#include "debug.h"
+#include <cassert>
+#include <algorithm>
+#include <string>
+#include <cstring>
+#include "cfg.hpp"
+
+// TODO: Extract this from the target at runtime (by invoking the compiler on the passed target)
+#ifdef _WIN32
+//# define TARGET_NAME "i586-windows-msvc"
+# define CFG_UNIX false
+# define CFG_WINDOWS true
+#elif defined(__NetBSD__)
+//# define TARGET_NAME "x86_64-unknown-netbsd"
+# define CFG_UNIX true
+# define CFG_WINDOWS false
+#else
+//# define TARGET_NAME "x86_64-unknown-linux-gnu"
+# define CFG_UNIX true
+# define CFG_WINDOWS false
+#endif
+
+class CfgParseLexer
+{
+public:
+ class Tok
+ {
+ friend class CfgParseLexer;
+ public:
+ enum Type {
+ EndOfStream,
+ Operator,
+ Ident,
+ String,
+ };
+ private:
+ Type m_ty;
+ const char* s;
+ const char* e;
+ ::std::string m_val;
+ Tok():
+ m_ty(EndOfStream), s(nullptr),e(nullptr)
+ {
+ }
+ Tok(const char* s):
+ m_ty(Operator), s(s), e(s+1), m_val()
+ {
+ }
+ Tok(const char* s, const char* e):
+ m_ty(Ident), s(s), e(e), m_val()
+ {
+ }
+ Tok(const char* s, const char* e, ::std::string val):
+ m_ty(String), s(s), e(e), m_val(::std::move(val))
+ {
+ }
+ public:
+ bool operator==(char c) const {
+ return (m_ty == Operator && *s == c);
+ }
+ bool operator!=(char c) const { return !(*this == c); }
+ bool operator==(const char* v) const {
+ return strlen(v) == static_cast<unsigned>(e - s) && memcmp(s, v, e-s) == 0;
+ }
+ bool operator!=(const char* v) const { return !(*this == v); }
+
+ const Type ty() const { return m_ty; }
+ const ::std::string& str() const {
+ return m_val;
+ }
+ ::std::string to_string() const {
+ return ::std::string(s, e);
+ }
+ };
+private:
+ const char* m_pos;
+ Tok m_cur;
+
+public:
+ CfgParseLexer(const char* s):
+ m_pos(s),
+ m_cur(nullptr,nullptr)
+ {
+ consume();
+ }
+ const Tok& cur() const {
+ return m_cur;
+ }
+
+ Tok consume() {
+ auto rv = m_cur;
+ m_cur = get_next();
+ //::std::cout << "consume: " << rv.to_string() << " => " << m_cur.to_string() << ::std::endl;
+ return rv;
+ }
+ bool consume_if(char c) {
+ if( cur() == c ) {
+ consume();
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+ bool consume_if(const char* s) {
+ if( cur() == s ) {
+ consume();
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+private:
+ Tok get_next();
+};
+
+struct CfgChecker
+{
+ const char* target_env;
+ const char* target_os;
+ const char* target_arch;
+
+ bool check_cfg(CfgParseLexer& p) const;
+};
+
+CfgChecker gCfgChecker {
+ (CFG_WINDOWS ? "msvc" : "gnu"),
+ (CFG_WINDOWS ? "windows" : "linux"),
+ "x86"
+ };
+
+CfgParseLexer::Tok CfgParseLexer::get_next()
+{
+ while(*m_pos == ' ')
+ m_pos ++;
+ if(*m_pos == 0)
+ return Tok();
+ switch(*m_pos)
+ {
+ case '(': case ')':
+ case ',': case '=':
+ return Tok(m_pos++);
+ case '"': {
+ ::std::string str;
+ auto s = m_pos;
+ m_pos ++;
+ while( *m_pos != '"' )
+ {
+ if( *m_pos == '\\' )
+ {
+ TODO("Escape sequences in cfg parser");
+ }
+ str += *m_pos;
+ m_pos ++;
+ }
+ m_pos ++;
+ return Tok(s, m_pos, str); }
+ default:
+ if( isalnum(*m_pos) || *m_pos == '_' )
+ {
+ auto s = m_pos;
+ while(isalnum(*m_pos) || *m_pos == '_')
+ m_pos ++;
+ return Tok(s, m_pos);
+ }
+ else
+ {
+ throw ::std::runtime_error(format("Unexpected character in cfg() - ", *m_pos));
+ }
+ }
+}
+
+bool Cfg_Check(const char* cfg_string)
+{
+ CfgParseLexer p { cfg_string + 4 };
+
+ if( gCfgChecker.target_os == nullptr )
+ {
+ // TODO: If the checker isn't initialised, invoke the compiler and ask it to dump the current target
+ // - It's pre-initialised above currently
+ }
+
+ bool success = gCfgChecker.check_cfg(p);
+ if( !p.consume_if(")") )
+ throw ::std::runtime_error(format("Expected ')' after cfg condition - got", p.cur().to_string()));
+ return success;
+}
+
+bool CfgChecker::check_cfg(CfgParseLexer& p) const
+{
+ auto name = p.consume();
+ if( name.ty() != CfgParseLexer::Tok::Ident )
+ throw ::std::runtime_error("Expected an identifier");
+ // Combinators
+ if( p.consume_if('(') ) {
+ bool rv;
+ if( false ) {
+ }
+ else if( name == "not" ) {
+ rv = !check_cfg(p);
+ }
+ else if( name == "all" ) {
+ rv = true;
+ do
+ {
+ rv &= check_cfg(p);
+ } while(p.consume_if(','));
+ }
+ else {
+ TODO("Unknown fragment in cfg - " << name.to_string());
+ }
+ if( !p.consume_if(')') )
+ throw ::std::runtime_error("Expected ')' after combinator content");
+ return rv;
+ }
+ // Values
+ else if( p.consume_if('=') ) {
+ auto t = p.consume();
+ if( t.ty() != CfgParseLexer::Tok::String )
+ throw ::std::runtime_error("Expected a string after `=`");
+ const auto& val = t.str();
+
+ if( false ) {
+ }
+ else if( name == "target_env" )
+ return val == this->target_env;
+ else if( name == "target_os" )
+ return val == this->target_os;
+ else if( name == "target_arch" )
+ return val == this->target_arch;
+ else {
+ TODO("Unknown fragment in cfg - " << name.to_string());
+ }
+ }
+ // Flags
+ else {
+ if( false ) {
+ }
+ else if( name == "unix" ) {
+ return CFG_UNIX;
+ }
+ else if( name == "windows" ) {
+ return CFG_WINDOWS;
+ }
+ else if( name == "stage0" ) {
+ return false;
+ }
+ else {
+ TODO("Unknown fragment in cfg - " << name.to_string());
+ }
+ }
+ throw ::std::runtime_error("Hit end of check_cfg");
+}