summaryrefslogtreecommitdiff
path: root/src/expand/cfg.cpp
blob: 431933f02c24b9a9f565354993eaa2195871ec9d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132

#include <synext.hpp>
#include <parse/tokentree.hpp>
#include <parse/lex.hpp>
#include <parse/common.hpp>
#include "cfg.hpp"

#include <map>
#include <set>

::std::map< ::std::string, ::std::string>   g_cfg_values;
::std::set< ::std::string >   g_cfg_flags;

void Cfg_SetFlag(::std::string name) {
    g_cfg_flags.insert( mv$(name) );
}
void Cfg_SetValue(::std::string name, ::std::string val) {
    g_cfg_values.insert( ::std::make_pair(mv$(name), mv$(val)) );
}

bool check_cfg(Span sp, const ::AST::MetaItem& mi) {
    
    if( mi.has_sub_items() ) {
        // Must be `any`/`not`/`all`
        if( mi.name() == "any" || mi.name() == "cfg" ) {
            for(const auto& si : mi.items()) {
                if( check_cfg(sp, si) )
                    return true;
            }
            return false;
        }
        else if( mi.name() == "not" ) {
            if( mi.items().size() != 1 )
                ERROR(sp, E0000, "cfg(not()) with != 1 argument");
            return !check_cfg(sp, mi.items()[0]);
        }
        else if( mi.name() == "all" ) {
            for(const auto& si : mi.items()) {
                if( ! check_cfg(sp, si) )
                    return false;
            }
            return true;
        }
        else {
            // oops
            ERROR(sp, E0000, "Unknown cfg() function - " << mi.name());
        }
    }
    else if( mi.has_string() ) {
        // Equaliy
        auto it = g_cfg_values.find(mi.name());
        if( it != g_cfg_values.end() )
        {
            DEBUG(""<<mi.name()<<": '"<<it->second<<"' == '"<<mi.string()<<"'");
            return it->second == mi.string();
        }
        else
        {
            WARNING(sp, W0000, "Unknown cfg() param '" << mi.name() << "'");
            return false;
        }
    }
    else {
        // Flag
        auto it = g_cfg_flags.find(mi.name());
        return (it != g_cfg_flags.end());
    }
    BUG(sp, "Fell off the end of check_cfg");
}

class CCfgExpander:
    public ExpandProcMacro
{
    bool    expand_early() const override { return true; }
    
    ::std::unique_ptr<TokenStream> expand(Span sp, const ::AST::Crate& crate, const ::std::string& ident, const TokenTree& tt, AST::Module& mod) override
    {
        if( ident != "" ) {
            ERROR(sp, E0000, "cfg! doesn't take an identifier");
        }
        
        auto lex = TTStreamO(tt);
        auto attrs = Parse_MetaItem(lex);
        DEBUG("cfg!() - " << attrs);
        
        if( check_cfg(sp, attrs) ) {
            return box$( TTStreamO(TokenTree(TOK_RWORD_TRUE )) );
        }
        else {
            return box$( TTStreamO(TokenTree(TOK_RWORD_FALSE)) );
        }
    }
};


class CCfgHandler:
    public ExpandDecorator
{
    AttrStage   stage() const override { return AttrStage::EarlyPre; }
    
    
    void handle(const AST::MetaItem& mi, ::AST::Crate& crate, AST::MacroInvocation& mac) const override {
        DEBUG("#[cfg] mac! - " << mi);
        if( check_cfg(mac.span(), mi) ) {
            // Leave as is
        }
        else {
            mac.clear();
        }
    }
    void handle(const AST::MetaItem& mi, ::AST::Crate& crate, const AST::Path& path, AST::Module& mod, AST::Item&i) const override {
        DEBUG("#[cfg] item - " << mi);
        if( check_cfg(Span(), mi) ) {
            // Leave
        }
        else {
            i = AST::Item::make_None({});
        }
    }
    void handle(const AST::MetaItem& mi, ::AST::Crate& crate, ::std::unique_ptr<AST::ExprNode>& expr) const override {
        DEBUG("#[cfg] expr - " << mi);
        if( check_cfg(Span(expr->get_pos()), mi) ) {
            // Leave
        }
        else {
            expr.reset();
        }
    }
};

STATIC_MACRO("cfg", CCfgExpander);
STATIC_DECORATOR("cfg", CCfgHandler);