basic support for builtins
[m6w6/ext-psi] / src / builtin.c
diff --git a/src/builtin.c b/src/builtin.c
new file mode 100644 (file)
index 0000000..e4e2e9b
--- /dev/null
@@ -0,0 +1,180 @@
+/*******************************************************************************
+ Copyright (c) 2018, Michael Wallner <mike@php.net>.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+     * Redistributions of source code must retain the above copyright notice,
+       this list of conditions and the following disclaimer.
+     * Redistributions in binary form must reproduce the above copyright
+       notice, this list of conditions and the following disclaimer in the
+       documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*******************************************************************************/
+
+#include "php_psi_stdinc.h"
+
+#include "php_psi.h"
+#include "builtin.h"
+#include "parser.h"
+#include "cpp.h"
+
+#include <stdarg.h>
+
+HashTable psi_builtins;
+
+static bool has_include(struct psi_cpp *cpp, struct psi_token *target, struct psi_plist **args, struct psi_plist **res);
+static bool has_include_next(struct psi_cpp *cpp, struct psi_token *target, struct psi_plist **args, struct psi_plist **res);
+static bool builtin_constant_p(struct psi_cpp *cpp, struct psi_token *target, struct psi_plist **args, struct psi_plist **res);
+static bool COUNTER__(struct psi_cpp *cpp, struct psi_token *target, struct psi_plist **args, struct psi_plist **res);
+
+static inline struct psi_plist *builtin_sig(token_t typ, ...)
+{
+       struct psi_plist *sig;
+       size_t n = 0;
+       va_list args;
+
+       if (typ == (token_t) - 1) {
+               return NULL;
+       }
+
+       sig  = psi_plist_init((psi_plist_dtor) psi_token_free);
+       va_start(args, typ);
+       while (typ) {
+               char a = 'a' + n++;
+               struct psi_token *arg;
+
+               arg = psi_token_init(typ, &a, 1, 0, 0, zend_empty_string);
+               sig = psi_plist_add(sig, &arg);
+               typ = va_arg(args, token_t);
+       }
+       va_end(args);
+
+       return sig;
+}
+
+static void free_builtin(zval *p)
+{
+       struct psi_builtin *b = Z_PTR_P(p);
+
+       if (b) {
+               zend_string_release(b->name);
+               psi_cpp_macro_decl_free(&b->decl);
+               pefree(b, 1);
+       }
+}
+
+PHP_MINIT_FUNCTION(psi_builtin)
+{
+#define PSI_BUILTIN(builtin, ...) do { \
+       struct psi_builtin entry; \
+       struct psi_plist *sig = builtin_sig(__VA_ARGS__, 0); \
+       struct psi_cpp_macro_decl *decl = psi_cpp_macro_decl_init(sig, NULL, NULL); \
+       decl->token = psi_token_init(PSI_T_NAME, "__" #builtin, sizeof("__" #builtin)-1, \
+               0, 0, zend_empty_string); \
+       entry.name = zend_string_copy(decl->token->text); \
+       entry.func = &builtin; \
+       entry.decl = decl; \
+       zend_hash_add_mem(&psi_builtins, entry.name, &entry, sizeof(entry)); \
+} while(0)
+
+       zend_hash_init(&psi_builtins, 0, NULL, free_builtin, 1);
+       PSI_BUILTIN(has_include, PSI_T_CPP_HEADER);
+       PSI_BUILTIN(has_include_next, PSI_T_CPP_HEADER);
+       PSI_BUILTIN(builtin_constant_p, PSI_T_NAME);
+       PSI_BUILTIN(COUNTER__, -1);
+
+       return SUCCESS;
+}
+
+PHP_MSHUTDOWN_FUNCTION(psi_builtin)
+{
+       zend_hash_destroy(&psi_builtins);
+       return SUCCESS;
+}
+
+bool psi_builtin_exists(zend_string *name)
+{
+       return zend_hash_exists(&psi_builtins, name);
+}
+
+struct psi_builtin *psi_builtin_get(zend_string *name)
+{
+       return zend_hash_find_ptr(&psi_builtins, name);
+}
+
+static bool has_include(struct psi_cpp *cpp, struct psi_token *target,
+               struct psi_plist **args, struct psi_plist **res)
+{
+       struct psi_plist *arg = args[0];
+       struct psi_token *tok;
+
+       switch (psi_plist_count(arg)) {
+       case 1:
+               if (psi_plist_get(arg, 0, &tok)) {
+                       const char *cpp_search = cpp->search;
+                       bool has = psi_cpp_has_include(cpp, tok, 0, NULL);
+                       cpp->search = cpp_search;
+                       return has;
+               }
+               /* no break */
+       default:
+               cpp->parser->error(PSI_DATA(cpp->parser), target, PSI_WARNING,
+                               "Erroneous usage of builtin __%s", __FUNCTION__);
+       }
+       return false;
+}
+
+static bool has_include_next(struct psi_cpp *cpp, struct psi_token *target,
+               struct psi_plist **args, struct psi_plist **res)
+{
+       struct psi_plist *arg = args[0];
+       struct psi_token *tok;
+
+       switch (psi_plist_count(arg)) {
+       case 1:
+               if (psi_plist_get(arg, 0, &tok)) {
+                       const char *cpp_search = cpp->search;
+                       bool has = psi_cpp_has_include(cpp, tok, PSI_CPP_INCLUDE_NEXT, NULL);
+                       cpp->search = cpp_search;
+                       return has;
+               }
+               /* no break */
+       default:
+               cpp->parser->error(PSI_DATA(cpp->parser), target, PSI_WARNING,
+                               "Erroneous usage of builtin __%s", __FUNCTION__);
+       }
+       return false;
+}
+
+static bool builtin_constant_p(struct psi_cpp *cpp, struct psi_token *target,
+               struct psi_plist **args, struct psi_plist **res_ptr)
+{
+       /* we want functions, not macros for e.g. htonl() */
+       return false;
+}
+
+static bool COUNTER__(struct psi_cpp *cpp, struct psi_token *target,
+               struct psi_plist **args, struct psi_plist **res)
+{
+       struct psi_token *tok;
+       char buf[0x20];
+       size_t len = sprintf(buf, "%u", cpp->counter++);
+
+       tok = psi_token_init(PSI_T_NUMBER, buf, len, target->col, target->line, target->file);
+       *res = psi_plist_init((psi_plist_dtor) psi_token_free);
+       *res = psi_plist_add(*res, &tok);
+
+       return true;
+}