|
27 | 27 | #include <cstdlib> |
28 | 28 | #include <cstring> |
29 | 29 | #include <ctime> |
| 30 | +#include <deque> |
30 | 31 | #include <fstream> |
31 | 32 | #include <iostream> |
32 | 33 | #include <istream> |
| 34 | +#include <iterator> |
33 | 35 | #include <limits> |
34 | 36 | #include <list> |
35 | 37 | #include <map> |
| 38 | +#include <memory> |
36 | 39 | #include <set> |
37 | 40 | #include <sstream> |
38 | 41 | #include <stack> |
@@ -1852,7 +1855,10 @@ namespace simplecpp { |
1852 | 1855 | tok->location = loc; |
1853 | 1856 | } |
1854 | 1857 |
|
| 1858 | + class HideSetExpander; |
| 1859 | + |
1855 | 1860 | class Macro { |
| 1861 | + friend class HideSetExpander; |
1856 | 1862 | public: |
1857 | 1863 | explicit Macro(std::vector<std::string> &f) : nameTokDef(nullptr), valueToken(nullptr), endToken(nullptr), files(f), tokenListDefine(f), variadic(false), variadicOpt(false), valueDefinedInCode_(false) {} |
1858 | 1864 |
|
@@ -2937,6 +2943,199 @@ namespace simplecpp { |
2937 | 2943 | }; |
2938 | 2944 | } |
2939 | 2945 |
|
| 2946 | +namespace simplecpp { |
| 2947 | + /** |
| 2948 | + * Macro expansion with hide sets (Prosser's algorithm, used by DUI::hideSetExpansion). |
| 2949 | + * Each token has a hide set: the macros that it must not be expanded into. |
| 2950 | + * The result of an expansion is pushed back onto the input and scanned again, so a |
| 2951 | + * function-like macro whose arguments follow the expansion in the source is found by |
| 2952 | + * normal rescanning. |
| 2953 | + * Work in progress: #, ##, __VA_OPT__ and "defined" in a replacement list are not handled yet. |
| 2954 | + */ |
| 2955 | + class HideSetExpander { |
| 2956 | + public: |
| 2957 | + HideSetExpander(const MacroMap ¯os, std::vector<std::string> &files) |
| 2958 | + : macros(macros), files(files) {} |
| 2959 | + |
| 2960 | + HideSetExpander(const HideSetExpander&) = delete; |
| 2961 | + HideSetExpander &operator=(const HideSetExpander&) = delete; |
| 2962 | + |
| 2963 | + /** |
| 2964 | + * Expand the macro at rawtok. |
| 2965 | + * @param output destination tokenlist |
| 2966 | + * @param rawtok the macro name in the source |
| 2967 | + * @return the first source token after the expansion |
| 2968 | + * @throws Macro::Error if the macro can not be expanded |
| 2969 | + */ |
| 2970 | + const Token *expand(TokenList &output, const Token *rawtok) { |
| 2971 | + loc = rawtok->location; |
| 2972 | + Input input(rawtok->next); |
| 2973 | + input.pending.push_back(new Token(*rawtok)); |
| 2974 | + expandInput(output, input); |
| 2975 | + return input.raw; |
| 2976 | + } |
| 2977 | + |
| 2978 | + private: |
| 2979 | + /** The input of an expansion: tokens to scan again, then the source */ |
| 2980 | + struct Input { |
| 2981 | + explicit Input(const Token *raw) : raw(raw) {} |
| 2982 | + Input(const Input&) = delete; |
| 2983 | + Input &operator=(const Input&) = delete; |
| 2984 | + ~Input() { |
| 2985 | + for (const Token * const tok : pending) |
| 2986 | + delete tok; |
| 2987 | + } |
| 2988 | + |
| 2989 | + /** The next token, read from the source if there are no pending tokens; nullptr at the end */ |
| 2990 | + Token *read() { |
| 2991 | + if (!pending.empty()) { |
| 2992 | + std::unique_ptr<Token> tok(pending.front()); |
| 2993 | + pending.pop_front(); |
| 2994 | + return tok.release(); |
| 2995 | + } |
| 2996 | + if (!raw) |
| 2997 | + return nullptr; |
| 2998 | + if (raw->op == '#' && !sameline(raw->previous, raw)) |
| 2999 | + throw Macro::invalidDirectiveAsMacroParameter(raw->location); |
| 3000 | + const Token * const tok = raw; |
| 3001 | + raw = raw->next; |
| 3002 | + return new Token(*tok); |
| 3003 | + } |
| 3004 | + |
| 3005 | + /** The next token, without reading it */ |
| 3006 | + const Token *peek() const { |
| 3007 | + return pending.empty() ? raw : pending.front(); |
| 3008 | + } |
| 3009 | + |
| 3010 | + /** Put tokens in front of the pending tokens */ |
| 3011 | + void pushFront(TokenList &tokens) { |
| 3012 | + std::deque<Token *> front; |
| 3013 | + while (tokens.front()) { |
| 3014 | + front.push_back(new Token(*tokens.front())); |
| 3015 | + tokens.deleteToken(tokens.front()); |
| 3016 | + } |
| 3017 | + pending.insert(pending.begin(), front.begin(), front.end()); |
| 3018 | + } |
| 3019 | + |
| 3020 | + /** tokens to scan again, the next one is at the front */ |
| 3021 | + std::deque<Token *> pending; |
| 3022 | + /** next token in the source, it is only read for the arguments of a macro call */ |
| 3023 | + const Token *raw; |
| 3024 | + }; |
| 3025 | + |
| 3026 | + static Macro::Error unsupported(const Location &loc, const std::string &what) { |
| 3027 | + return {loc, what + " is not supported by the hide-set expansion yet"}; |
| 3028 | + } |
| 3029 | + |
| 3030 | + /** Expand the pending tokens of the input. Source tokens are only read for macro arguments. */ |
| 3031 | + void expandInput(TokenList &output, Input &input) { |
| 3032 | + while (!input.pending.empty()) { |
| 3033 | + Token * const tok = input.read(); |
| 3034 | + const MacroMap::const_iterator it = tok->name ? macros.find(tok->str()) : macros.end(); |
| 3035 | + if (it == macros.end() || tok->isInHideSet(it->first)) { |
| 3036 | + output.push_back(tok); |
| 3037 | + continue; |
| 3038 | + } |
| 3039 | + const Macro ¯o = it->second; |
| 3040 | + |
| 3041 | + if (macro.functionLike()) { |
| 3042 | + const Token * const next = input.peek(); |
| 3043 | + if (!next || next->op != '(') { |
| 3044 | + output.push_back(tok); |
| 3045 | + continue; |
| 3046 | + } |
| 3047 | + } |
| 3048 | + |
| 3049 | + macro.usageList.emplace_back(loc); |
| 3050 | + if (macro.expandBuiltin(output, loc, tok)) { |
| 3051 | + delete tok; |
| 3052 | + continue; |
| 3053 | + } |
| 3054 | + |
| 3055 | + std::set<TokenString> hideSet; |
| 3056 | + std::vector<TokenList> arguments; |
| 3057 | + if (macro.functionLike()) { |
| 3058 | + TokenList call(files); |
| 3059 | + call.push_back(tok); |
| 3060 | + readCall(call, input); |
| 3061 | + const std::vector<const Token *> delimiters = macro.getMacroParameters(call.cfront(), false); |
| 3062 | + // the hide set of the name and the ) have the macro calls that this call |
| 3063 | + // was produced by, the arguments are not affected by them |
| 3064 | + const std::set<TokenString> &hs1 = call.cfront()->hideSet(); |
| 3065 | + const std::set<TokenString> &hs2 = delimiters.back()->hideSet(); |
| 3066 | + std::set_intersection(hs1.begin(), hs1.end(), hs2.begin(), hs2.end(), std::inserter(hideSet, hideSet.begin())); |
| 3067 | + arguments = expandArguments(macro, delimiters); |
| 3068 | + } else { |
| 3069 | + hideSet = tok->hideSet(); |
| 3070 | + delete tok; |
| 3071 | + } |
| 3072 | + hideSet.insert(macro.name()); |
| 3073 | + |
| 3074 | + TokenList result(files); |
| 3075 | + substitute(result, macro, arguments, hideSet); |
| 3076 | + input.pushFront(result); |
| 3077 | + } |
| 3078 | + } |
| 3079 | + |
| 3080 | + /** Read "( ... )" after the macro name in call */ |
| 3081 | + static void readCall(TokenList &call, Input &input) { |
| 3082 | + int par = 0; |
| 3083 | + while (Token * const tok = input.read()) { |
| 3084 | + call.push_back(tok); |
| 3085 | + if (tok->op == '(') |
| 3086 | + ++par; |
| 3087 | + else if (tok->op == ')' && --par == 0) |
| 3088 | + return; |
| 3089 | + } |
| 3090 | + } |
| 3091 | + |
| 3092 | + /** Each argument of the call, fully macro expanded */ |
| 3093 | + std::vector<TokenList> expandArguments(const Macro ¯o, const std::vector<const Token *> &delimiters) { |
| 3094 | + std::vector<TokenList> arguments; |
| 3095 | + for (std::size_t i = 0; i < macro.args.size(); ++i) { |
| 3096 | + arguments.emplace_back(files); |
| 3097 | + if (i + 1U >= delimiters.size()) |
| 3098 | + continue; // empty variadic argument |
| 3099 | + Input input(nullptr); |
| 3100 | + for (const Token *tok = delimiters[i]->next; tok != delimiters[i + 1U]; tok = tok->next) |
| 3101 | + input.pending.push_back(new Token(*tok)); |
| 3102 | + expandInput(arguments.back(), input); |
| 3103 | + } |
| 3104 | + return arguments; |
| 3105 | + } |
| 3106 | + |
| 3107 | + /** The replacement list of the macro, with the parameters replaced by the arguments */ |
| 3108 | + void substitute(TokenList &result, const Macro ¯o, const std::vector<TokenList> &arguments, const std::set<TokenString> &hideSet) const { |
| 3109 | + if (macro.variadicOpt) |
| 3110 | + throw unsupported(loc, "__VA_OPT__"); |
| 3111 | + for (const Token *tok = macro.valueToken; tok != macro.endToken; tok = tok->next) { |
| 3112 | + if (tok->op == '#') |
| 3113 | + throw unsupported(loc, "# and ##"); |
| 3114 | + if (tok->str() == DEFINED) |
| 3115 | + throw unsupported(loc, "defined"); |
| 3116 | + const unsigned int argnr = tok->name ? macro.getArgNum(tok->str()) : ~0U; |
| 3117 | + if (argnr < arguments.size()) { |
| 3118 | + for (const Token *argtok = arguments[argnr].cfront(); argtok; argtok = argtok->next) |
| 3119 | + result.push_back(new Token(*argtok)); |
| 3120 | + } else { |
| 3121 | + result.push_back(new Token(tok->str(), loc, tok->whitespaceahead)); |
| 3122 | + result.back()->macro = macro.name(); |
| 3123 | + } |
| 3124 | + } |
| 3125 | + for (Token *tok = result.front(); tok; tok = tok->next) { |
| 3126 | + tok->location = loc; |
| 3127 | + for (const TokenString &name : hideSet) |
| 3128 | + tok->addToHideSet(name); |
| 3129 | + } |
| 3130 | + } |
| 3131 | + |
| 3132 | + const MacroMap ¯os; |
| 3133 | + std::vector<std::string> &files; |
| 3134 | + /** location of the macro name in the source, all tokens of the expansion get it */ |
| 3135 | + Location loc; |
| 3136 | + }; |
| 3137 | +} |
| 3138 | + |
2940 | 3139 | namespace simplecpp { |
2941 | 3140 |
|
2942 | 3141 | #ifdef __CYGWIN__ |
@@ -3865,7 +4064,12 @@ static bool preprocessToken(simplecpp::TokenList &output, const simplecpp::Token |
3865 | 4064 | if (it != macros.end()) { |
3866 | 4065 | simplecpp::TokenList value(files); |
3867 | 4066 | try { |
3868 | | - tok1 = it->second.expand(value, tok, macros, files, outputList); |
| 4067 | + if (dui.hideSetExpansion) { |
| 4068 | + simplecpp::HideSetExpander expander(macros, files); |
| 4069 | + tok1 = expander.expand(value, tok); |
| 4070 | + } else { |
| 4071 | + tok1 = it->second.expand(value, tok, macros, files, outputList); |
| 4072 | + } |
3869 | 4073 | } catch (const simplecpp::Macro::invalidDirectiveAsMacroParameter &err) { |
3870 | 4074 | if (outputList) { |
3871 | 4075 | simplecpp::Output out{ |
|
0 commit comments