+
+
+static inline bool psi_cpp_level_skipped(struct psi_cpp *cpp)
+{
+ return cpp->skip == cpp->level;
+}
+
+static inline void psi_cpp_level_skip(struct psi_cpp *cpp)
+{
+ assert(!cpp->skip);
+ cpp->skip = cpp->level;
+}
+
+static inline void psi_cpp_level_unskip(struct psi_cpp *cpp)
+{
+ if (psi_cpp_level_skipped(cpp)) {
+ cpp->skip = 0;
+ }
+}
+
+static inline bool psi_cpp_level_masked(struct psi_cpp *cpp)
+{
+ return cpp->seen & (1 << cpp->level);
+}
+
+static inline void psi_cpp_level_mask(struct psi_cpp *cpp)
+{
+ assert(!psi_cpp_level_masked(cpp));
+ cpp->seen |= (1 << cpp->level);
+}
+
+static inline void psi_cpp_level_unmask(struct psi_cpp *cpp)
+{
+ cpp->seen &= ~(1 << cpp->level);
+}
+
+void psi_cpp_exp_exec(struct psi_cpp_exp *exp, struct psi_cpp *cpp, struct psi_data *D)
+{
+ PSI_DEBUG_PRINT(D, "PSI: CPP EVAL < %s (level=%u, skip=%u)\n",
+ exp->token->text, cpp->level, cpp->skip);
+
+#if PSI_CPP_DEBUG
+ psi_cpp_exp_dump(2, exp);
+#endif
+
+ switch (exp->type) {
+ case PSI_T_ERROR:
+ if (!cpp->skip) {
+ D->error(D, exp->token, PSI_ERROR, "%s",
+ exp->data.tok ? exp->data.tok->text : "");
+ }
+ break;
+ case PSI_T_WARNING:
+ if (!cpp->skip) {
+ D->error(D, exp->token, PSI_WARNING, "%s",
+ exp->data.tok ? exp->data.tok->text : "");
+ }
+ break;
+ case PSI_T_UNDEF:
+ if (!cpp->skip) {
+ psi_cpp_undef(cpp, exp->data.tok);
+ }
+ break;
+ case PSI_T_DEFINE:
+ if (!cpp->skip) {
+ psi_cpp_define(cpp, exp->data.decl);
+ /* FIXME: copy */
+ exp->data.decl = NULL;
+ }
+ break;
+ case PSI_T_IFDEF:
+ ++cpp->level;
+ if (!cpp->skip) {
+ if (psi_cpp_defined(cpp, exp->data.tok)) {
+ psi_cpp_level_mask(cpp);
+ } else {
+ psi_cpp_level_skip(cpp);
+ }
+ }
+ break;
+ case PSI_T_IFNDEF:
+ ++cpp->level;
+ if (!cpp->skip) {
+ if (psi_cpp_defined(cpp, exp->data.tok)) {
+ psi_cpp_level_skip(cpp);
+ } else {
+ psi_cpp_level_mask(cpp);
+ }
+ }
+ break;
+ case PSI_T_IF:
+ ++cpp->level;
+ if (!cpp->skip) {
+ if (psi_cpp_if(cpp, exp)) {
+ psi_cpp_level_mask(cpp);
+ } else {
+ psi_cpp_level_skip(cpp);
+ }
+ }
+ break;
+ case PSI_T_ENDIF:
+ if (!cpp->level) {
+ D->error(D, exp->token, PSI_WARNING, "Ingoring lone #endif");
+ } else {
+ psi_cpp_level_unskip(cpp);
+ psi_cpp_level_unmask(cpp);
+ --cpp->level;
+ }
+ break;
+ case PSI_T_ELSE:
+ /* FIXME: catch "else" after "else" */
+ if (!cpp->level) {
+ D->error(D, exp->token, PSI_WARNING, "Ingoring lone #else");
+ } else if (psi_cpp_level_skipped(cpp) && !psi_cpp_level_masked(cpp)) {
+ /*
+ * if skip is set on this level and the level has
+ * not been masked yet, then unskip and mask this level
+ */
+ psi_cpp_level_unskip(cpp);
+ psi_cpp_level_mask(cpp);
+ } else if (!cpp->skip && psi_cpp_level_masked(cpp)) {
+ /*
+ * previous block masked this level
+ */
+ psi_cpp_level_skip(cpp);
+ } else {
+ assert(cpp->skip <= cpp->level);
+ }
+ break;
+ case PSI_T_ELIF:
+ if (!cpp->level) {
+ D->error(D, exp->token, PSI_WARNING, "Ingoring lone #elif");
+ } else if (psi_cpp_level_skipped(cpp) && !psi_cpp_level_masked(cpp)) {
+ /*
+ * if skip is set on this level and the level has
+ * not been masked yet, then unskip and mask this
+ * level, if the condition evals truthy
+ */
+ if (psi_cpp_if(cpp, exp)) {
+ psi_cpp_level_unskip(cpp);
+ psi_cpp_level_mask(cpp);
+ }
+ } else if (!cpp->skip && psi_cpp_level_masked(cpp)) {
+ /*
+ * previous block masked this level
+ */
+ psi_cpp_level_skip(cpp);
+ } else {
+ assert(cpp->skip <= cpp->level);
+ }
+ break;
+ case PSI_T_INCLUDE:
+ if (!cpp->skip) {
+ if (!psi_cpp_include(cpp, exp->data.tok, PSI_CPP_INCLUDE)) {
+ D->error(D, exp->token, PSI_WARNING, "Failed to include %s", exp->data.tok->text);
+ }
+ }
+ break;
+ case PSI_T_INCLUDE_NEXT:
+ if (!cpp->skip) {
+ if (!psi_cpp_include(cpp, exp->data.tok, PSI_CPP_INCLUDE_NEXT)) {
+ D->error(D, exp->token, PSI_WARNING, "Failed to include %s", exp->data.tok->text);
+ }
+ }
+ break;
+ case PSI_T_IMPORT:
+ if (!cpp->skip) {
+ if (!psi_cpp_include(cpp, exp->data.tok, PSI_CPP_INCLUDE_ONCE)) {
+ D->error(D, exp->token, PSI_WARNING, "Failed to include %s", exp->data.tok->text);
+ }
+ }
+ break;
+ case PSI_T_PRAGMA_ONCE:
+ if (!cpp->skip) {
+ zend_hash_str_add_empty_element(&cpp->once, exp->token->file, strlen(exp->token->file));
+ }
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ PSI_DEBUG_PRINT(D, "PSI: CPP EVAL > %s (level=%u, skip=%u)\n",
+ exp->token->text, cpp->level, cpp->skip);
+}