From c895bed5e6506180d2325efaad601901b94912e0 Mon Sep 17 00:00:00 2001
From: iskolbin <iskolbin@gmail.com>
Date: Thu, 3 Feb 2022 16:56:00 +0300
Subject: [PATCH] Added defines to parser (#2269)

* added defines to parser

* added value and description for defines in parser

* parser: fixed lua defines output, fixed makefile for raygui, added LONG type for defines

* parser defines: remove postfix for LONG/FLOAT, support for hexadecimal consts, convert hex ints to decimal for JSON output

* removed defines from raylib_apis
---
 parser/Makefile        |   3 +-
 parser/raylib_parser.c | 210 ++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 208 insertions(+), 5 deletions(-)

diff --git a/parser/Makefile b/parser/Makefile
index b9197e20..9446e282 100644
--- a/parser/Makefile
+++ b/parser/Makefile
@@ -20,8 +20,9 @@ parse:
 	./raylib_parser -i ../src/raymath.h -o raymath_api.$(EXTENSION) -f $(FORMAT) -d RMAPI
 	./raylib_parser -i ../src/extras/easings.h -o easings_api.$(EXTENSION) -f $(FORMAT) -d EASEDEF
 	./raylib_parser -i ../src/extras/physac.h -o physac_api.$(EXTENSION) -f $(FORMAT) -d PHYSACDEF
-	./raylib_parser -i ../src/extras/raygui.h -o raygui_api.$(EXTENSION) -f $(FORMAT) -d RAYGUIDEF
+	./raylib_parser -i ../src/extras/raygui.h -o raygui_api.$(EXTENSION) -f $(FORMAT) -d RAYGUIAPI
 	./raylib_parser -i ../src/extras/rmem.h -o rmem_api.$(EXTENSION) -f $(FORMAT) -d RMEMAPI
+	./raylib_parser -i ../src/rlgl.h -o rlgl_api.$(EXTENSION) -f $(FORMAT) -d RLAPI
 
 clean:
 	rm -f raylib_parser *.json *.txt *.xml *.lua
diff --git a/parser/raylib_parser.c b/parser/raylib_parser.c
index ed534299..68c893f4 100644
--- a/parser/raylib_parser.c
+++ b/parser/raylib_parser.c
@@ -2,12 +2,13 @@
 
     raylib API parser
 
-    This parser scans raylib.h to get API information about structs, enums and functions.
+    This parser scans raylib.h to get API information about structs, enums, functions and defines.
     All data is divided into pieces, usually as strings. The following types are used for data:
 
      - struct FunctionInfo
      - struct StructInfo
      - struct EnumInfo
+     - struct DefInfo
 
     CONSTRAINTS:
 
@@ -62,10 +63,12 @@
 #include <stdlib.h>             // Required for: malloc(), calloc(), realloc(), free(), atoi(), strtol()
 #include <stdio.h>              // Required for: printf(), fopen(), fseek(), ftell(), fread(), fclose()
 #include <stdbool.h>            // Required for: bool
+#include <ctype.h>              // Required for: isdigit()
 
 #define MAX_FUNCS_TO_PARSE       512    // Maximum number of functions to parse
 #define MAX_STRUCTS_TO_PARSE      64    // Maximum number of structures to parse
 #define MAX_ENUMS_TO_PARSE        64    // Maximum number of enums to parse
+#define MAX_DEFINES_TO_PARSE    2048    // Maximum number of defines to parse
 
 #define MAX_LINE_LENGTH          512    // Maximum length of one line (including comments)
 #define MAX_STRUCT_LINE_LENGTH  2048    // Maximum length of one struct (multiple lines)
@@ -108,6 +111,18 @@ typedef struct EnumInfo {
     char valueDesc[MAX_ENUM_VALUES][128];   // Value description
 } EnumInfo;
 
+// Type of parsed define
+typedef enum { UNKNOWN = 0, MACRO, GUARD, INT, LONG, FLOAT, DOUBLE, CHAR, STRING, COLOR } DefineType;
+
+// Define info data
+typedef struct DefineInfo {
+    char name[64];    // Define name
+    DefineType type;  // Define type
+		char value[256];  // Define value
+    char desc[128];   // Define description
+    bool isHex;       // Define is hex number (for types INT, LONG)
+} DefineInfo;
+
 // Output format for parsed data
 typedef enum { DEFAULT = 0, JSON, XML, LUA } OutputFormat;
 
@@ -117,9 +132,11 @@ typedef enum { DEFAULT = 0, JSON, XML, LUA } OutputFormat;
 static int funcCount = 0;
 static int structCount = 0;
 static int enumCount = 0;
+static int defineCount = 0;
 static FunctionInfo *funcs = NULL;
 static StructInfo *structs = NULL;
 static EnumInfo *enums = NULL;
+static DefineInfo *defines = NULL;
 static char apiDefine[32] = "RLAPI\0";
 
 // Command line variables
@@ -143,6 +160,8 @@ static char *EscapeBackslashes(char *text);                 // Replace '\' by "\
 
 static void ExportParsedData(const char *fileName, int format); // Export parsed data in desired format
 
+static const char *StrDefineType(DefineType type);          // Get string of define type
+
 //------------------------------------------------------------------------------------
 // Program main entry point
 //------------------------------------------------------------------------------------
@@ -169,6 +188,9 @@ int main(int argc, char* argv[])
     // Enums lines pointers, selected from buffer "lines"
     int *enumLines = (int *)malloc(MAX_ENUMS_TO_PARSE*sizeof(int));
 
+    // Defines lines pointers, selected from buffer "lines"
+    int *defineLines = (int *)malloc(MAX_DEFINES_TO_PARSE*sizeof(int));
+
     // Prepare required lines for parsing
     //--------------------------------------------------------------------------------------------------
 
@@ -228,7 +250,22 @@ int main(int argc, char* argv[])
         }
     }
 
-    // At this point we have all raylib structs, enums, functions lines data to start parsing
+    // Read const lines
+    for (int i = 0; i < linesCount; i++)
+    {
+        int j = 0;
+        while (lines[i][j] == ' ' || lines[i][j] == '\t') j++; // skip spaces and tabs in the begining
+        // Read define line
+        if (IsTextEqual(lines[i]+j, "#define ", 8))
+        {
+            // Keep the line position in the array of lines,
+            // so, we can scan that position and following lines
+            defineLines[defineCount] = i;
+            defineCount++;
+        }
+    }
+
+    // At this point we have all raylib structs, enums, functions, defines lines data to start parsing
 
     free(buffer);       // Unload text buffer
 
@@ -402,9 +439,94 @@ int main(int argc, char* argv[])
             }
         }
     }
-
     free(enumLines);
 
+    // Define info data
+    defines = (DefineInfo *)calloc(MAX_DEFINES_TO_PARSE, sizeof(DefineInfo));
+    int defineIndex = 0;
+
+    for (int i = 0; i < defineCount; i++)
+    {
+        char *linePtr = lines[defineLines[i]];
+        int j = 0;
+
+        while (linePtr[j] == ' ' || linePtr[j] == '\t') j++; // Skip spaces and tabs in the begining
+        j += 8;                                              // Skip "#define "
+        while (linePtr[j] == ' ' || linePtr[j] == '\t') j++; // Skip spaces and tabs after "#define "
+
+        // Extract name
+        int defineNameStart = j;
+        while (linePtr[j] != ' ' && linePtr[j] != '\t' && linePtr[j] != '\0') j++;
+        int defineNameEnd = j-1;
+
+        // Skip duplicates
+        int nameLen = defineNameEnd - defineNameStart + 1;
+        bool isDuplicate = false;
+        for (int k = 0; k < defineIndex; k++) {
+            if (nameLen == TextLength(defines[k].name) && IsTextEqual(defines[k].name, linePtr + defineNameStart, nameLen)) {
+                isDuplicate = true;
+                break;
+            }
+        }
+        if (isDuplicate) continue;
+
+        MemoryCopy(defines[defineIndex].name, linePtr + defineNameStart, nameLen);
+
+        // Determine type
+        if (linePtr[defineNameEnd] == ')') defines[defineIndex].type = MACRO;
+
+        while (linePtr[j] == ' ' || linePtr[j] == '\t') j++; // Skip spaces and tabs after name
+
+        int defineValueStart = j;
+        if (linePtr[j] == '\0' || linePtr == "/") defines[defineIndex].type = GUARD;
+        if (linePtr[j] == '"') defines[defineIndex].type = STRING;
+        else if (linePtr[j] == '\'') defines[defineIndex].type = CHAR;
+        else if (IsTextEqual(linePtr+j, "CLITERAL(Color)", 15)) defines[defineIndex].type = COLOR;
+        else if (isdigit(linePtr[j])) { // Parsing numbers
+            bool isFloat = false, isNumber = true, isHex = false;
+            while (linePtr[j] != ' ' && linePtr[j] != '\t' && linePtr[j] != '\0') {
+                char ch = linePtr[j];
+                if (ch == '.') isFloat = true;
+                if (ch == 'x') isHex = true;
+                if (!(isdigit(ch)||(ch >= 'a' && ch <= 'f')||(ch >= 'A' && ch <= 'F')||ch=='x'||ch=='L'||ch=='.'||ch=='+'||ch=='-')) isNumber = false;
+                j++;
+            }
+            if (isNumber) {
+                if (isFloat) {
+                    defines[defineIndex].type = linePtr[j-1] == 'f' ? FLOAT : DOUBLE;
+                } else {
+                    defines[defineIndex].type = linePtr[j-1] == 'L' ? LONG : INT;
+                    defines[defineIndex].isHex = isHex;
+                }
+            }
+        }
+
+        // Extracting value
+        while (linePtr[j] != '\\' && linePtr[j] != '\0' && !(linePtr[j] == '/' && linePtr[j+1] == '/')) j++;
+        int defineValueEnd = j-1;
+        while (linePtr[defineValueEnd] == ' ' || linePtr[defineValueEnd] == '\t') defineValueEnd--; // Remove trailing spaces and tabs
+        if (defines[defineIndex].type == LONG || defines[defineIndex].type == FLOAT) defineValueEnd--; // Remove number postfix
+        int valueLen = defineValueEnd - defineValueStart + 1;
+        if (valueLen > 255) valueLen = 255;
+
+        if (valueLen > 0) MemoryCopy(defines[defineIndex].value, linePtr + defineValueStart, valueLen);
+
+        // Extracting description
+        if (linePtr[j] == '/') {
+            int commentStart = j;
+            while (linePtr[j] != '\\' && linePtr[j] != '\0') j++;
+            int commentEnd = j-1;
+            int commentLen = commentEnd - commentStart + 1;
+            if (commentLen > 127) commentLen = 127;
+
+            MemoryCopy(defines[defineIndex].desc, linePtr + commentStart, commentLen);
+        }
+
+        defineIndex++;
+    }
+    defineCount = defineIndex;
+    free(defineLines);
+
     // Functions info data
     funcs = (FunctionInfo *)calloc(MAX_FUNCS_TO_PARSE, sizeof(FunctionInfo));
 
@@ -486,6 +608,7 @@ int main(int argc, char* argv[])
     // structs[] -> We have all the structs decomposed into pieces for further analysis
     // enums[]   -> We have all the enums decomposed into pieces for further analysis
     // funcs[]   -> We have all the functions decomposed into pieces for further analysis
+    // defines[] -> We have all the defines decomposed into pieces for further analysis
 
     // Process input file to output
     if (outFileName[0] == '\0') MemoryCopy(outFileName, "raylib_api.txt\0", 15);
@@ -759,6 +882,25 @@ static char *EscapeBackslashes(char *text)
     return buffer;
 }
 
+// Get string of define type
+static const char *StrDefineType(DefineType type)
+{
+    switch (type)
+    {
+        case UNKNOWN: return "UNKNOWN";
+        case GUARD:   return "GUARD";
+        case MACRO:   return "MACRO";
+        case INT:     return "INT";
+        case LONG:    return "LONG";
+        case FLOAT:   return "FLOAT";
+        case DOUBLE:  return "DOUBLE";
+        case CHAR:    return "CHAR";
+        case STRING:  return "STRING";
+        case COLOR:   return "COLOR";
+    }
+    return "";
+}
+
 /*
 // Replace text string
 // REQUIRES: strlen(), strstr(), strncpy(), strcpy() -> TODO: Replace by custom implementations!
@@ -836,7 +978,7 @@ static void ExportParsedData(const char *fileName, int format)
             {
                 fprintf(outFile, "Enum %02i: %s (%i values)\n", i + 1, enums[i].name, enums[i].valueCount);
                 fprintf(outFile, "  Name: %s\n", enums[i].name);
-                fprintf(outFile, " Description: %s\n", enums[i].desc + 3);
+                fprintf(outFile, "  Description: %s\n", enums[i].desc + 3);
                 for (int e = 0; e < enums[i].valueCount; e++) fprintf(outFile, "  Value[%s]: %i\n", enums[i].valueName[e], enums[i].valueInteger[e]);
             }
 
@@ -851,6 +993,16 @@ static void ExportParsedData(const char *fileName, int format)
                 for (int p = 0; p < funcs[i].paramCount; p++) fprintf(outFile, "  Param[%i]: %s (type: %s)\n", p + 1, funcs[i].paramName[p], funcs[i].paramType[p]);
                 if (funcs[i].paramCount == 0) fprintf(outFile, "  No input parameters\n");
             }
+
+            fprintf(outFile, "\nDefines found: %i\n\n", defineCount);
+            for (int i = 0; i < defineCount; i++)
+            {
+                fprintf(outFile, "Define %03i: %s\n", i + 1, defines[i].name);
+                fprintf(outFile, "  Name: %s\n", defines[i].name);
+                fprintf(outFile, "  Type: %s\n", StrDefineType(defines[i].type));
+								fprintf(outFile, "  Value: %s\n", defines[i].value);
+								fprintf(outFile, "  Description: %s\n", defines[i].desc + 3);
+            }
         } break;
         case LUA:
         {
@@ -906,6 +1058,26 @@ static void ExportParsedData(const char *fileName, int format)
             }
             fprintf(outFile, "  },\n");
 
+            // Print defines info
+            fprintf(outFile, "  defines = {\n");
+            for (int i = 0; i < defineCount; i++)
+            {
+                fprintf(outFile, "    {\n");
+                fprintf(outFile, "      name = \"%s\",\n", defines[i].name);
+                fprintf(outFile, "      type = \"%s\",\n", StrDefineType(defines[i].type));
+                if (defines[i].type == INT || defines[i].type == LONG || defines[i].type == FLOAT || defines[i].type == DOUBLE || defines[i].type == STRING) {
+                    fprintf(outFile, "      value = %s,\n", defines[i].value);
+                } else {
+                    fprintf(outFile, "      value = \"%s\",\n", defines[i].value);
+                }
+								fprintf(outFile, "      description = \"%s\"\n", defines[i].desc + 3);
+                fprintf(outFile, "    }");
+
+                if (i < defineCount - 1) fprintf(outFile, ",\n");
+                else fprintf(outFile, "\n");
+            }
+            fprintf(outFile, "  },\n");
+
             // Print functions info
             fprintf(outFile, "  functions = {\n");
             for (int i = 0; i < funcCount; i++)
@@ -989,6 +1161,28 @@ static void ExportParsedData(const char *fileName, int format)
             }
             fprintf(outFile, "  ],\n");
 
+            // Print defines info
+            fprintf(outFile, "  \"defines\": [\n");
+            for (int i = 0; i < defineCount; i++)
+            {
+                fprintf(outFile, "    {\n");
+                fprintf(outFile, "      \"name\": \"%s\",\n", defines[i].name);
+                fprintf(outFile, "      \"type\": \"%s\",\n", StrDefineType(defines[i].type));
+                if (defines[i].isHex) { // INT or LONG
+                    fprintf(outFile, "      \"value\": %ld,\n", strtol(defines[i].value, NULL, 16));
+                } else if (defines[i].type == INT || defines[i].type == LONG || defines[i].type == FLOAT || defines[i].type == DOUBLE || defines[i].type == STRING) {
+                    fprintf(outFile, "      \"value\": %s,\n", defines[i].value);
+                } else {
+                    fprintf(outFile, "      \"value\": \"%s\",\n", defines[i].value);
+                }
+								fprintf(outFile, "      \"description\": \"%s\"\n", defines[i].desc + 3);
+                fprintf(outFile, "    }");
+
+                if (i < defineCount - 1) fprintf(outFile, ",\n");
+                else fprintf(outFile, "\n");
+            }
+            fprintf(outFile, "  ],\n");
+
             // Print functions info
             fprintf(outFile, "  \"functions\": [\n");
             for (int i = 0; i < funcCount; i++)
@@ -1077,6 +1271,14 @@ static void ExportParsedData(const char *fileName, int format)
             }
             fprintf(outFile, "    </Enums>\n");
 
+            // Print defines info
+            fprintf(outFile, "    <Defines count=\"%i\">\n", defineCount);
+            for (int i = 0; i < defineCount; i++)
+            {
+                fprintf(outFile, "        <Define name=\"%s\" type=\"%s\" value=\"%s\" desc=\"%s\" />\n", defines[i].name, StrDefineType(defines[i].type), defines[i].value, defines[i].desc);
+            }
+            fprintf(outFile, "    </Defines>\n");
+
             // Print functions info
             fprintf(outFile, "    <Functions count=\"%i\">\n", funcCount);
             for (int i = 0; i < funcCount; i++)