summaryrefslogtreecommitdiffhomepage
path: root/parser/raylib_parser.c
diff options
context:
space:
mode:
authorRay <[email protected]>2021-06-13 16:34:51 +0200
committerRay <[email protected]>2021-06-13 16:34:51 +0200
commitd875891a3c2621ab40733ca3569eca9e054a6506 (patch)
treed7568f28238e7ef38c75b7efc5a57a6face50eb6 /parser/raylib_parser.c
parent3e68059556f628a172292ec99389fe2fbd820716 (diff)
downloadraylib-d875891a3c2621ab40733ca3569eca9e054a6506.tar.gz
raylib-d875891a3c2621ab40733ca3569eca9e054a6506.zip
Improved raylib parser to export data as XML
- Improved command-line - Added XML support - Added sample outputs: DEFAULT, JSON, XML - Reviewed LoadFileText() bug - Removed trailing spaces
Diffstat (limited to 'parser/raylib_parser.c')
-rw-r--r--parser/raylib_parser.c698
1 files changed, 440 insertions, 258 deletions
diff --git a/parser/raylib_parser.c b/parser/raylib_parser.c
index f2f3109f..e38dde67 100644
--- a/parser/raylib_parser.c
+++ b/parser/raylib_parser.c
@@ -1,35 +1,35 @@
/**********************************************************************************************
- raylib parser - raylib header parser
+ raylib API parser
+
+ This parser scans raylib.h to get API information about structs, enums and functions.
+ All data is divided into pieces, usually as strings. The following types are used for data:
- This parser scans raylib.h to get information about structs, enums and functions.
- All data is separated into parts, usually as strings. The following types are used for data:
-
- struct FunctionInfo
- struct StructInfo
- struct EnumInfo
- CONSTRAINTS:
-
- This parser is specifically designed to work with raylib.h, so, it has some constraints:
-
+ CONSTRAINTS:
+
+ This parser is specifically designed to work with raylib.h, so, it has some constraints:
+
- Functions are expected as a single line with the following structure:
<retType> <name>(<paramType[0]> <paramName[0]>, <paramType[1]> <paramName[1]>); <desc>
Be careful with functions broken into several lines, it breaks the process!
-
+
- Structures are expected as several lines with the following form:
-
+
<desc>
typedef struct <name> {
<fieldType[0]> <fieldName[0]>; <fieldDesc[0]>
<fieldType[1]> <fieldName[1]>; <fieldDesc[1]>
<fieldType[2]> <fieldName[2]>; <fieldDesc[2]>
} <name>;
-
+
- Enums are expected as several lines with the following form:
-
+
<desc>
typedef enum {
<valueName[0]> = <valueInteger[0]>, <valueDesc[0]>
@@ -37,14 +37,16 @@
<valueName[2]>, <valueDesc[2]>
<valueName[3]> <valueDesc[3]>
} <name>;
-
- NOTE: Multiple options are supported:
+
+ NOTE: Multiple options are supported for enums:
- If value is not provided, (<valueInteger[i -1]> + 1) is assigned
- Value description can be provided or not
-
- This parser could work with other C header files if mentioned constraints are followed.
-
- This parser does not require <string.h> library, all data is parsed directly from char buffers.
+
+ OTHER NOTES:
+
+ - This parser could work with other C header files if mentioned constraints are followed.
+
+ - This parser does not require <string.h> library, all data is parsed directly from char buffers.
LICENSE: zlib/libpng
@@ -55,6 +57,8 @@
**********************************************************************************************/
+#define _CRT_SECURE_NO_WARNINGS
+
#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
@@ -66,8 +70,6 @@
#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)
-enum OutputFormat { PlainText, JSON }; // Which format the header information should be in
-
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
@@ -79,6 +81,7 @@ typedef struct FunctionInfo {
int paramCount; // Number of function parameters
char paramType[12][32]; // Parameters type (max: 12 parameters)
char paramName[12][32]; // Parameters name (max: 12 parameters)
+ char paramDesc[12][8]; // Parameters description (max: 12 parameters)
} FunctionInfo;
// Struct info data
@@ -101,59 +104,70 @@ typedef struct EnumInfo {
char valueDesc[128][64]; // Value description (max: 128 values)
} EnumInfo;
+// Output format for parsed data
+typedef enum { DEFAULT = 0, JSON, XML } OutputFormat;
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+static int funcCount = 0;
+static int structCount = 0;
+static int enumCount = 0;
+static FunctionInfo *funcs = NULL;
+static StructInfo *structs = NULL;
+static EnumInfo *enums = NULL;
+
+// Command line variables
+static char inFileName[512] = { 0 }; // Input file name (required in case of provided through CLI)
+static char outFileName[512] = { 0 }; // Output file name (required for file save/export)
+static int outputFormat = DEFAULT;
+
//----------------------------------------------------------------------------------
// Module Functions Declaration
//----------------------------------------------------------------------------------
-char *LoadFileText(const char *fileName, int *length);
-char **GetTextLines(const char *buffer, int length, int *linesCount);
-void GetDataTypeAndName(const char *typeName, int typeNameLen, char *type, char *name);
-bool IsTextEqual(const char *text1, const char *text2, unsigned int count);
-void MemoryCopy(void *dest, const void *src, unsigned int count);
-char* CharReplace(char* text, char search, char replace);
-
-// Main entry point
+static void ShowCommandLineInfo(void); // Show command line usage info
+static void ProcessCommandLine(int argc, char *argv[]); // Process command line input
+
+static char *LoadFileText(const char *fileName, int *length);
+static char **GetTextLines(const char *buffer, int length, int *linesCount);
+static void GetDataTypeAndName(const char *typeName, int typeNameLen, char *type, char *name);
+static unsigned int TextLength(const char *text); // Get text length in bytes, check for \0 character
+static bool IsTextEqual(const char *text1, const char *text2, unsigned int count);
+static void MemoryCopy(void *dest, const void *src, unsigned int count);
+static char* CharReplace(char* text, char search, char replace);
+
+static void ExportParsedData(const char *fileName, int format); // Export parsed data in desired format
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
int main(int argc, char* argv[])
{
- // Help
- if (argv[1] != NULL && IsTextEqual(argv[1], "--help", 6)) {
- printf("Usage:\n");
- printf(" raylib_parser [--json]\n");
- return 0;
- }
+ if (argc > 1) ProcessCommandLine(argc, argv);
- // Allow changing the output format.
- int outputFormat = 0;
- if (argv[1] != NULL && IsTextEqual(argv[1], "--json", 6)) {
- outputFormat = JSON;
- }
+ if (inFileName[0] == '\0') MemoryCopy(inFileName, "../src/raylib.h\0", 16);
int length = 0;
- char *buffer = LoadFileText("../src/raylib.h", &length);
-
+ char *buffer = LoadFileText(inFileName, &length);
+
// Preprocess buffer to get separate lines
// NOTE: GetTextLines() also removes leading spaces/tabs
int linesCount = 0;
char **lines = GetTextLines(buffer, length, &linesCount);
-
- // Print buffer lines
- //for (int i = 0; i < linesCount; i++) printf("_%s_\n", lines[i]);
-
+
// Function lines pointers, selected from buffer "lines"
- int funcCount = 0;
char **funcLines = (char **)malloc(MAX_FUNCS_TO_PARSE*sizeof(char *));
// Structs data (multiple lines), selected from "buffer"
- int structCount = 0;
char **structLines = (char **)malloc(MAX_STRUCTS_TO_PARSE*sizeof(char *));
for (int i = 0; i < MAX_STRUCTS_TO_PARSE; i++) structLines[i] = (char *)calloc(MAX_STRUCT_LINE_LENGTH, sizeof(char));
-
+
// Enums lines pointers, selected from buffer "lines"
- int enumCount = 0;
int *enumLines = (int *)malloc(MAX_ENUMS_TO_PARSE*sizeof(int));
-
+
// Prepare required lines for parsing
//--------------------------------------------------------------------------------------------------
-
+
// Read function lines
for (int i = 0; i < linesCount; i++)
{
@@ -165,9 +179,6 @@ int main(int argc, char* argv[])
funcCount++;
}
}
-
- // Print function lines
- //for (int i = 0; i < funcCount; i++) printf("%s\n", funcLines[i]);
// Read structs data (multiple lines, read directly from buffer)
// TODO: Parse structs data from "lines" instead of "buffer" -> Easier to get struct definition
@@ -179,9 +190,9 @@ int main(int argc, char* argv[])
{
int j = 0;
bool validStruct = false;
-
- // WARNING: Typedefs between types: typedef Vector4 Quaternion;
-
+
+ // WARNING: Typedefs between types: typedef Vector4 Quaternion;
+
for (int c = 0; c < 128; c++)
{
if (buffer[i + c] == '{')
@@ -191,13 +202,13 @@ int main(int argc, char* argv[])
}
else if (buffer[i + c] == ';')
{
- // Not valid struct:
+ // Not valid struct:
// i.e typedef struct rAudioBuffer rAudioBuffer; -> Typedef and forward declaration
i += c;
break;
}
}
-
+
if (validStruct)
{
while (buffer[i + j] != '}')
@@ -223,7 +234,7 @@ int main(int argc, char* argv[])
}
}
}
-
+
// Read enum lines
for (int i = 0; i < linesCount; i++)
{
@@ -236,17 +247,17 @@ int main(int argc, char* argv[])
enumCount++;
}
}
-
+
// At this point we have all raylib structs, enums, functions lines data to start parsing
-
+
free(buffer); // Unload text buffer
// Parsing raylib data
//--------------------------------------------------------------------------------------------------
// Structs info data
- StructInfo *structs = (StructInfo *)calloc(MAX_STRUCTS_TO_PARSE, sizeof(StructInfo));
-
+ structs = (StructInfo *)calloc(MAX_STRUCTS_TO_PARSE, sizeof(StructInfo));
+
for (int i = 0; i < structCount; i++)
{
int structLineOffset = 0;
@@ -254,52 +265,52 @@ int main(int argc, char* argv[])
// Get struct name: typedef struct name {
for (int c = 15; c < 64 + 15; c++)
{
- if (structLines[i][c] == '{')
+ if (structLines[i][c] == '{')
{
structLineOffset = c + 2;
-
+
MemoryCopy(structs[i].name, &structLines[i][15], c - 15 - 1);
break;
}
}
-
+
// Get struct fields and count them -> fields finish with ;
int j = 0;
while (structLines[i][structLineOffset + j] != '}')
{
// WARNING: Some structs have empty spaces and comments -> OK, processed
-
+
int fieldStart = 0;
if ((structLines[i][structLineOffset + j] != ' ') && (structLines[i][structLineOffset + j] != '\n')) fieldStart = structLineOffset + j;
-
+
if (fieldStart != 0)
{
// Scan one field line
int c = 0;
int fieldEndPos = 0;
char fieldLine[256] = { 0 };
-
+
while (structLines[i][structLineOffset + j] != '\n')
{
if (structLines[i][structLineOffset + j] == ';') fieldEndPos = c;
fieldLine[c] = structLines[i][structLineOffset + j];
c++; j++;
}
-
+
if (fieldLine[0] != '/') // Field line is not a comment
{
//printf("Struct field: %s_\n", fieldLine); // OK!
-
+
// Get struct field type and name
GetDataTypeAndName(fieldLine, fieldEndPos, structs[i].fieldType[structs[i].fieldCount], structs[i].fieldName[structs[i].fieldCount]);
-
+
// Get the field description
// We start skipping spaces in front of description comment
int descStart = fieldEndPos;
while ((fieldLine[descStart] != '/') && (fieldLine[descStart] != '\0')) descStart++;
-
+
int k = 0;
- while ((fieldLine[descStart + k] != '\0') && (fieldLine[descStart + k] != '\n'))
+ while ((fieldLine[descStart + k] != '\0') && (fieldLine[descStart + k] != '\n'))
{
structs[i].fieldDesc[structs[i].fieldCount][k] = fieldLine[descStart + k];
k++;
@@ -313,17 +324,17 @@ int main(int argc, char* argv[])
}
}
-
+
for (int i = 0; i < MAX_STRUCTS_TO_PARSE; i++) free(structLines[i]);
free(structLines);
-
+
// Enum info data
- EnumInfo *enums = (EnumInfo *)calloc(MAX_ENUMS_TO_PARSE, sizeof(EnumInfo));
-
+ enums = (EnumInfo *)calloc(MAX_ENUMS_TO_PARSE, sizeof(EnumInfo));
+
for (int i = 0; i < enumCount; i++)
{
// TODO: Get enum description from lines[enumLines[i] - 1]
-
+
for (int j = 1; j < 256; j++) // Maximum number of lines following enum first line
{
char *linePtr = lines[enumLines[i] + j];
@@ -336,20 +347,20 @@ int main(int argc, char* argv[])
//ENUM_VALUE_NAME = 99
//ENUM_VALUE_NAME = 99,
//ENUM_VALUE_NAME = 0x00000040, // Value description
-
+
// We start reading the value name
int c = 0;
- while ((linePtr[c] != ',') &&
- (linePtr[c] != ' ') &&
+ while ((linePtr[c] != ',') &&
+ (linePtr[c] != ' ') &&
(linePtr[c] != '=') &&
(linePtr[c] != '\0')) { enums[i].valueName[enums[i].valueCount][c] = linePtr[c]; c++; }
- // After the name we can have:
+ // After the name we can have:
// '=' -> value is provided
// ',' -> value is equal to previous + 1, there could be a description if not '\0'
// ' ' -> value is equal to previous + 1, there could be a description if not '\0'
// '\0' -> value is equal to previous + 1
-
+
// Let's start checking if the line is not finished
if ((linePtr[c] != ',') && (linePtr[c] != '\0'))
{
@@ -357,7 +368,7 @@ int main(int argc, char* argv[])
// '=' -> value is provided
// ' ' -> value is equal to previous + 1, there could be a description if not '\0'
bool foundValue = false;
- while (linePtr[c] != '\0')
+ while (linePtr[c] != '\0')
{
if (linePtr[c] == '=') { foundValue = true; break; }
c++;
@@ -367,64 +378,64 @@ int main(int argc, char* argv[])
{
if (linePtr[c + 1] == ' ') c += 2;
else c++;
-
+
// Parse integer value
int n = 0;
char integer[16] = { 0 };
-
- while ((linePtr[c] != ',') && (linePtr[c] != ' ') && (linePtr[c] != '\0'))
+
+ while ((linePtr[c] != ',') && (linePtr[c] != ' ') && (linePtr[c] != '\0'))
{
integer[n] = linePtr[c];
c++; n++;
}
-
+
if (integer[1] == 'x') enums[i].valueInteger[enums[i].valueCount] = (int)strtol(integer, NULL, 16);
else enums[i].valueInteger[enums[i].valueCount] = atoi(integer);
}
else enums[i].valueInteger[enums[i].valueCount] = (enums[i].valueInteger[enums[i].valueCount - 1] + 1);
-
+
// TODO: Parse value description if any
}
else enums[i].valueInteger[enums[i].valueCount] = (enums[i].valueInteger[enums[i].valueCount - 1] + 1);
enums[i].valueCount++;
}
- else if (linePtr[0] == '}')
+ else if (linePtr[0] == '}')
{
// Get enum name from typedef
int c = 0;
while (linePtr[2 + c] != ';') { enums[i].name[c] = linePtr[2 + c]; c++; }
-
+
break; // Enum ended, break for() loop
}
}
}
-
+
// Functions info data
- FunctionInfo *funcs = (FunctionInfo *)calloc(MAX_FUNCS_TO_PARSE, sizeof(FunctionInfo));
-
+ funcs = (FunctionInfo *)calloc(MAX_FUNCS_TO_PARSE, sizeof(FunctionInfo));
+
for (int i = 0; i < funcCount; i++)
{
int funcParamsStart = 0;
int funcEnd = 0;
-
+
// Get return type and function name from func line
for (int c = 0; (c < MAX_LINE_LENGTH) && (funcLines[i][c] != '\n'); c++)
{
if (funcLines[i][c] == '(') // Starts function parameters
{
funcParamsStart = c + 1;
-
+
// At this point we have function return type and function name
char funcRetTypeName[128] = { 0 };
int funcRetTypeNameLen = c - 6; // Substract "RLAPI "
MemoryCopy(funcRetTypeName, &funcLines[i][6], funcRetTypeNameLen);
-
+
GetDataTypeAndName(funcRetTypeName, funcRetTypeNameLen, funcs[i].retType, funcs[i].name);
break;
}
}
-
+
// Get parameters from func line
for (int c = funcParamsStart; c < MAX_LINE_LENGTH; c++)
{
@@ -434,9 +445,9 @@ int main(int argc, char* argv[])
char funcParamTypeName[128] = { 0 };
int funcParamTypeNameLen = c - funcParamsStart;
MemoryCopy(funcParamTypeName, &funcLines[i][funcParamsStart], funcParamTypeNameLen);
-
+
GetDataTypeAndName(funcParamTypeName, funcParamTypeNameLen, funcs[i].paramType[funcs[i].paramCount], funcs[i].paramName[funcs[i].paramCount]);
-
+
funcParamsStart = c + 1;
if (funcLines[i][c + 1] == ' ') funcParamsStart += 1;
funcs[i].paramCount++; // Move to next parameter
@@ -444,170 +455,54 @@ int main(int argc, char* argv[])
else if (funcLines[i][c] == ')')
{
funcEnd = c + 2;
-
+
// Check if previous word is void
if ((funcLines[i][c - 4] == 'v') && (funcLines[i][c - 3] == 'o') && (funcLines[i][c - 2] == 'i') && (funcLines[i][c - 1] == 'd')) break;
-
+
// Get parameter type + name, extract info
char funcParamTypeName[128] = { 0 };
int funcParamTypeNameLen = c - funcParamsStart;
MemoryCopy(funcParamTypeName, &funcLines[i][funcParamsStart], funcParamTypeNameLen);
-
+
GetDataTypeAndName(funcParamTypeName, funcParamTypeNameLen, funcs[i].paramType[funcs[i].paramCount], funcs[i].paramName[funcs[i].paramCount]);
funcs[i].paramCount++; // Move to next parameter
break;
}
}
-
+
// Get function description
for (int c = funcEnd; c < MAX_LINE_LENGTH; c++)
{
- if (funcLines[i][c] == '/')
+ if (funcLines[i][c] == '/')
{
MemoryCopy(funcs[i].desc, &funcLines[i][c], 127); // WARNING: Size could be too long for funcLines[i][c]?
break;
}
}
}
-
- for (int i = 0; i < linesCount; i++) free(lines[i]);
+
+ //for (int i = 0; i < linesCount; i++) free(lines[i]);
free(lines);
free(funcLines);
-
+
// At this point, all raylib data has been parsed!
//-----------------------------------------------------------------------------------------
// 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
-
- // Print structs info
- switch (outputFormat)
- {
- case PlainText: {
- printf("\nStructures found: %i\n\n", structCount);
- for (int i = 0; i < structCount; i++)
- {
- printf("Struct %02i: %s (%i fields)\n", i + 1, structs[i].name, structs[i].fieldCount);
- //printf("Description: %s\n", structs[i].desc);
- for (int f = 0; f < structs[i].fieldCount; f++) printf(" Fields %i: %s %s %s\n", f + 1, structs[i].fieldType[f], structs[i].fieldName[f], structs[i].fieldDesc[f]);
- }
- // Print enums info
- printf("\nEnums found: %i\n\n", enumCount);
- for (int i = 0; i < enumCount; i++)
- {
- printf("Enum %02i: %s (%i values)\n", i + 1, enums[i].name, enums[i].valueCount);
- //printf("Description: %s\n", enums[i].desc);
- for (int e = 0; e < enums[i].valueCount; e++) printf(" Value %s: %i\n", enums[i].valueName[e], enums[i].valueInteger[e]);
- }
+ // Process input file to output
+ if (outFileName[0] == '\0') MemoryCopy(outFileName, "raylib_api.txt\0", 15);
- // Print function info
- printf("\nFunctions found: %i\n\n", funcCount);
- for (int i = 0; i < funcCount; i++)
- {
- printf("Function %03i: %s() (%i input parameters)\n", i + 1, funcs[i].name, funcs[i].paramCount);
- printf(" Description: %s\n", funcs[i].desc);
- printf(" Return type: %s\n", funcs[i].retType);
- for (int p = 0; p < funcs[i].paramCount; p++) printf(" Param %i: %s (type: %s)\n", p + 1, funcs[i].paramName[p], funcs[i].paramType[p]);
- if (funcs[i].paramCount == 0) printf(" No input parameters\n");
- }
- } break;
- case JSON: {
- printf("{\n");
- printf(" \"structs\": [\n");
- for (int i = 0; i < structCount; i++)
- {
- printf(" {\n");
- printf(" \"name\": \"%s\",\n", structs[i].name);
- printf(" \"description\": \"%s\",\n", structs[i].desc);
- printf(" \"fields\": [\n");
- for (int f = 0; f < structs[i].fieldCount; f++)
- {
- printf(" {\n");
- printf(" \"name\": \"%s\",\n", structs[i].fieldName[f]),
- printf(" \"type\": \"%s\",\n", structs[i].fieldType[f]),
- printf(" \"description\": \"%s\"\n", structs[i].fieldDesc[f] + 3),
- printf(" }");
- if (f < structs[i].fieldCount - 1)
- printf(",\n");
- else
- printf("\n");
- }
- printf(" ]\n");
- printf(" }");
- if (i < structCount - 1)
- printf(",\n");
- else
- printf("\n");
- }
- printf(" ],\n");
-
- // Print enums info
- printf(" \"enums\": [\n");
- for (int i = 0; i < enumCount; i++)
- {
- printf(" {\n");
- printf(" \"name\": \"%s\",\n", enums[i].name);
- printf(" \"description\": \"%s\",\n", enums[i].desc + 3);
- printf(" \"values\": [\n");
- for (int e = 0; e < enums[i].valueCount; e++)
- {
- printf(" {\n");
- printf(" \"name\": \"%s\",\n", enums[i].valueName[e]),
- printf(" \"value\": %i,\n", enums[i].valueInteger[e]),
- printf(" \"description\": \"%s\"\n", enums[i].valueDesc[e] + 3),
- printf(" }");
- if (e < enums[i].valueCount - 1)
- printf(",\n");
- else
- printf("\n");
- }
- printf(" ]\n");
- printf(" }");
- if (i < enumCount - 1)
- printf(",\n");
- else
- printf("\n");
- }
- printf(" ],\n");
-
- // Print function info
- printf(" \"functions\": [\n");
- for (int i = 0; i < funcCount; i++)
- {
- printf(" {\n");
- printf(" \"name\": \"%s\",\n", funcs[i].name);
- printf(" \"description\": \"%s\",\n", CharReplace(funcs[i].desc, '\\', ' ') + 3);
- printf(" \"returnType\": \"%s\"", funcs[i].retType);
+ printf("\nInput file: %s", inFileName);
+ printf("\nOutput file: %s", outFileName);
+ if (outputFormat == DEFAULT) printf("\nOutput format: DEFAULT\n\n");
+ else if (outputFormat == JSON) printf("\nOutput format: JSON\n\n");
+ else if (outputFormat == XML) printf("\nOutput format: XML\n\n");
- if (funcs[i].paramCount == 0)
- printf("\n");
- else
- {
- printf(",\n \"params\": {\n");
- for (int p = 0; p < funcs[i].paramCount; p++)
- {
- printf(" \"%s\": \"%s\"", funcs[i].paramName[p], funcs[i].paramType[p]);
- if (p < funcs[i].paramCount - 1)
- printf(",\n");
- else
- printf("\n");
- }
- printf(" }\n");
- }
- printf(" }");
+ ExportParsedData(outFileName, outputFormat);
- if (i < funcCount - 1)
- printf(",\n");
- else
- printf("\n");
- }
- printf(" ]\n");
- printf("}\n");
- } break;
- }
-
free(funcs);
free(structs);
free(enums);
@@ -616,10 +511,84 @@ int main(int argc, char* argv[])
//----------------------------------------------------------------------------------
// Module Functions Definition
//----------------------------------------------------------------------------------
+// Show command line usage info
+static void ShowCommandLineInfo(void)
+{
+ printf("\n//////////////////////////////////////////////////////////////////////////////////\n");
+ printf("// //\n");
+ printf("// raylib API parser //\n");
+ printf("// //\n");
+ printf("// more info and bugs-report: github.com/raysan5/raylib/parser //\n");
+ printf("// //\n");
+ printf("// Copyright (c) 2021 Ramon Santamaria (@raysan5) //\n");
+ printf("// //\n");
+ printf("//////////////////////////////////////////////////////////////////////////////////\n\n");
+
+ printf("USAGE:\n\n");
+ printf(" > raylib_parser [--help] [--input <filename.h>] [--output <filename.ext>] [--format <type>]\n");
+
+ printf("\nOPTIONS:\n\n");
+ printf(" -h, --help : Show tool version and command line usage help\n\n");
+ printf(" -i, --input <filename.h> : Define input header file to parse.\n");
+ printf(" NOTE: If not specified, defaults to: raylib.h\n\n");
+ printf(" -o, --output <filename.ext> : Define output file and format.\n");
+ printf(" Supported extensions: .txt, .json, .xml, .h\n");
+ printf(" NOTE: If not specified, defaults to: raylib_api.txt\n\n");
+ printf(" -f, --format <type> : Define output format for parser data.\n");
+ printf(" Supported types: DEFAULT, JSON, XML\n\n");
+
+ printf("\nEXAMPLES:\n\n");
+ printf(" > raylib_parser --input raylib.h --output api.json\n");
+ printf(" Process <raylib.h> to generate <api.json>\n\n");
+ printf(" > raylib_parser --output raylib_data.info --format XML\n");
+ printf(" Process <raylib.h> to generate <raylib_data.info> as XML text data\n\n");
+}
+
+// Process command line arguments
+static void ProcessCommandLine(int argc, char *argv[])
+{
+ for (int i = 1; i < argc; i++)
+ {
+ if (IsTextEqual(argv[i], "-h", 2) || IsTextEqual(argv[i], "--help", 6))
+ {
+ // Show info
+ ShowCommandLineInfo();
+ }
+ else if (IsTextEqual(argv[i], "-i", 2) || IsTextEqual(argv[i], "--input", 7))
+ {
+ // Check for valid argument and valid file extension
+ if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
+ {
+ MemoryCopy(inFileName, argv[i + 1], TextLength(argv[i + 1])); // Read input filename
+ i++;
+ }
+ else printf("WARNING: No input file provided\n");
+ }
+ else if (IsTextEqual(argv[i], "-o", 2) || IsTextEqual(argv[i], "--output", 8))
+ {
+ if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
+ {
+ MemoryCopy(outFileName, argv[i + 1], TextLength(argv[i + 1])); // Read output filename
+ i++;
+ }
+ else printf("WARNING: No output file provided\n");
+ }
+ else if (IsTextEqual(argv[i], "-f", 2) || IsTextEqual(argv[i], "--format", 8))
+ {
+ if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
+ {
+ if (IsTextEqual(argv[i + 1], "DEFAULT\0", 8)) outputFormat = DEFAULT;
+ else if (IsTextEqual(argv[i + 1], "JSON\0", 5)) outputFormat = JSON;
+ else if (IsTextEqual(argv[i + 1], "XML\0", 4)) outputFormat = XML;
+ }
+ else printf("WARNING: No format parameters provided\n");
+ }
+ }
+}
// Load text data from file, returns a '\0' terminated string
// NOTE: text chars array should be freed manually
-char *LoadFileText(const char *fileName, int *length)
+static char *LoadFileText(const char *fileName, int *length)
{
char *text = NULL;
@@ -638,14 +607,17 @@ char *LoadFileText(const char *fileName, int *length)
if (size > 0)
{
- *length = size;
-
text = (char *)calloc((size + 1), sizeof(char));
unsigned int count = (unsigned int)fread(text, sizeof(char), size, file);
// WARNING: \r\n is converted to \n on reading, so,
// read bytes count gets reduced by the number of lines
- if (count < (unsigned int)size) text = realloc(text, count + 1);
+ if (count < (unsigned int)size)
+ {
+ text = realloc(text, count + 1);
+ *length = count;
+ }
+ else *length = size;
// Zero-terminate the string
text[count] = '\0';
@@ -659,47 +631,45 @@ char *LoadFileText(const char *fileName, int *length)
}
// Get all lines from a text buffer (expecting lines ending with '\n')
-char **GetTextLines(const char *buffer, int length, int *linesCount)
+static char **GetTextLines(const char *buffer, int length, int *linesCount)
{
- //#define MAX_LINE_LENGTH 512
-
// Get the number of lines in the text
int count = 0;
for (int i = 0; i < length; i++) if (buffer[i] == '\n') count++;
- //printf("Number of text lines in buffer: %i\n", count);
-
+ printf("Number of text lines in buffer: %i\n", count);
+
// Allocate as many pointers as lines
char **lines = (char **)malloc(count*sizeof(char **));
-
+
char *bufferPtr = (char *)buffer;
-
+
for (int i = 0; (i < count) || (bufferPtr[0] != '\0'); i++)
{
lines[i] = (char *)calloc(MAX_LINE_LENGTH, sizeof(char));
-
+
// Remove line leading spaces
// Find last index of space/tab character
int index = 0;
while ((bufferPtr[index] == ' ') || (bufferPtr[index] == '\t')) index++;
int j = 0;
- while (bufferPtr[index + j] != '\n')
+ while (bufferPtr[index + j] != '\n')
{
lines[i][j] = bufferPtr[index + j];
j++;
}
-
+
bufferPtr += (index + j + 1);
}
-
+
*linesCount = count;
return lines;
}
// Get data type and name from a string containing both
// NOTE: Useful to parse function parameters and struct fields
-void GetDataTypeAndName(const char *typeName, int typeNameLen, char *type, char *name)
+static void GetDataTypeAndName(const char *typeName, int typeNameLen, char *type, char *name)
{
for (int k = typeNameLen; k > 0; k--)
{
@@ -719,8 +689,18 @@ void GetDataTypeAndName(const char *typeName, int typeNameLen, char *type, char
}
}
+// Get text length in bytes, check for \0 character
+static unsigned int TextLength(const char *text)
+{
+ unsigned int length = 0;
+
+ if (text != NULL) while (*text++) length++;
+
+ return length;
+}
+
// Custom memcpy() to avoid <string.h>
-void MemoryCopy(void *dest, const void *src, unsigned int count)
+static void MemoryCopy(void *dest, const void *src, unsigned int count)
{
char *srcPtr = (char *)src;
char *destPtr = (char *)dest;
@@ -729,13 +709,13 @@ void MemoryCopy(void *dest, const void *src, unsigned int count)
}
// Compare two text strings, requires number of characters to compare
-bool IsTextEqual(const char *text1, const char *text2, unsigned int count)
+static bool IsTextEqual(const char *text1, const char *text2, unsigned int count)
{
bool result = true;
-
- for (unsigned int i = 0; i < count; i++)
+
+ for (unsigned int i = 0; i < count; i++)
{
- if (text1[i] != text2[i])
+ if (text1[i] != text2[i])
{
result = false;
break;
@@ -746,7 +726,7 @@ bool IsTextEqual(const char *text1, const char *text2, unsigned int count)
}
// Search and replace a character within a string.
-char* CharReplace(char* text, char search, char replace)
+static char* CharReplace(char* text, char search, char replace)
{
for (int i = 0; text[i] != '\0'; i++)
if (text[i] == search)
@@ -758,7 +738,7 @@ char* CharReplace(char* text, char search, char replace)
// Replace text string
// REQUIRES: strlen(), strstr(), strncpy(), strcpy() -> TODO: Replace by custom implementations!
// WARNING: Returned buffer must be freed by the user (if return != NULL)
-char *TextReplace(char *text, const char *replace, const char *by)
+static char *TextReplace(char *text, const char *replace, const char *by)
{
// Sanity checks and initialization
if (!text || !replace || !by) return NULL;
@@ -804,4 +784,206 @@ char *TextReplace(char *text, const char *replace, const char *by)
return result;
}
-*/ \ No newline at end of file
+*/
+
+// Export parsed data in desired format
+static void ExportParsedData(const char *fileName, int format)
+{
+ FILE *outFile = fopen(fileName, "wt");
+
+ switch (format)
+ {
+ case DEFAULT:
+ {
+ // Print structs info
+ fprintf(outFile, "\nStructures found: %i\n\n", structCount);
+ for (int i = 0; i < structCount; i++)
+ {
+ fprintf(outFile, "Struct %02i: %s (%i fields)\n", i + 1, structs[i].name, structs[i].fieldCount);
+ fprintf(outFile, " Name: %s\n", structs[i].name);
+ fprintf(outFile, " Description: %s\n", structs[i].desc + 3);
+ for (int f = 0; f < structs[i].fieldCount; f++) fprintf(outFile, " Field[%i]: %s %s %s\n", f + 1, structs[i].fieldType[f], structs[i].fieldName[f], structs[i].fieldDesc[f]);
+ }
+
+ // Print enums info
+ fprintf(outFile, "\nEnums found: %i\n\n", enumCount);
+ for (int i = 0; i < enumCount; i++)
+ {
+ 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);
+ for (int e = 0; e < enums[i].valueCount; e++) fprintf(outFile, " Value[%s]: %i\n", enums[i].valueName[e], enums[i].valueInteger[e]);
+ }
+
+ // Print functions info
+ fprintf(outFile, "\nFunctions found: %i\n\n", funcCount);
+ for (int i = 0; i < funcCount; i++)
+ {
+ fprintf(outFile, "Function %03i: %s() (%i input parameters)\n", i + 1, funcs[i].name, funcs[i].paramCount);
+ fprintf(outFile, " Name: %s\n", funcs[i].name);
+ fprintf(outFile, " Return type: %s\n", funcs[i].retType);
+ fprintf(outFile, " Description: %s\n", funcs[i].desc + 3);
+ 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");
+ }
+ } break;
+ case JSON:
+ {
+ fprintf(outFile, "{\n");
+
+ // Print structs info
+ fprintf(outFile, " \"structs\": [\n");
+ for (int i = 0; i < structCount; i++)
+ {
+ fprintf(outFile, " {\n");
+ fprintf(outFile, " \"name\": \"%s\",\n", structs[i].name);
+ fprintf(outFile, " \"description\": \"%s\",\n", structs[i].desc);
+ fprintf(outFile, " \"fields\": [\n");
+ for (int f = 0; f < structs[i].fieldCount; f++)
+ {
+ fprintf(outFile, " {\n");
+ fprintf(outFile, " \"name\": \"%s\",\n", structs[i].fieldName[f]),
+ fprintf(outFile, " \"type\": \"%s\",\n", structs[i].fieldType[f]),
+ fprintf(outFile, " \"description\": \"%s\"\n", structs[i].fieldDesc[f] + 3),
+ fprintf(outFile, " }");
+ if (f < structs[i].fieldCount - 1) fprintf(outFile, ",\n");
+ else fprintf(outFile, "\n");
+ }
+ fprintf(outFile, " ]\n");
+ fprintf(outFile, " }");
+ if (i < structCount - 1) fprintf(outFile, ",\n");
+ else fprintf(outFile, "\n");
+ }
+ fprintf(outFile, " ],\n");
+
+ // Print enums info
+ fprintf(outFile, " \"enums\": [\n");
+ for (int i = 0; i < enumCount; i++)
+ {
+ fprintf(outFile, " {\n");
+ fprintf(outFile, " \"name\": \"%s\",\n", enums[i].name);
+ fprintf(outFile, " \"description\": \"%s\",\n", enums[i].desc + 3);
+ fprintf(outFile, " \"values\": [\n");
+ for (int e = 0; e < enums[i].valueCount; e++)
+ {
+ fprintf(outFile, " {\n");
+ fprintf(outFile, " \"name\": \"%s\",\n", enums[i].valueName[e]),
+ fprintf(outFile, " \"value\": %i,\n", enums[i].valueInteger[e]),
+ fprintf(outFile, " \"description\": \"%s\"\n", enums[i].valueDesc[e] + 3),
+ fprintf(outFile, " }");
+ if (e < enums[i].valueCount - 1) fprintf(outFile, ",\n");
+ else fprintf(outFile, "\n");
+ }
+ fprintf(outFile, " ]\n");
+ fprintf(outFile, " }");
+ if (i < enumCount - 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++)
+ {
+ fprintf(outFile, " {\n");
+ fprintf(outFile, " \"name\": \"%s\",\n", funcs[i].name);
+ fprintf(outFile, " \"description\": \"%s\",\n", CharReplace(funcs[i].desc, '\\', ' ') + 3);
+ fprintf(outFile, " \"returnType\": \"%s\"", funcs[i].retType);
+
+ if (funcs[i].paramCount == 0) fprintf(outFile, "\n");
+ else
+ {
+ fprintf(outFile, ",\n \"params\": {\n");
+ for (int p = 0; p < funcs[i].paramCount; p++)
+ {
+ fprintf(outFile, " \"%s\": \"%s\"", funcs[i].paramName[p], funcs[i].paramType[p]);
+ if (p < funcs[i].paramCount - 1) fprintf(outFile, ",\n");
+ else fprintf(outFile, "\n");
+ }
+ fprintf(outFile, " }\n");
+ }
+ fprintf(outFile, " }");
+
+ if (i < funcCount - 1) fprintf(outFile, ",\n");
+ else fprintf(outFile, "\n");
+ }
+ fprintf(outFile, " ]\n");
+ fprintf(outFile, "}\n");
+ } break;
+ case XML:
+ {
+ // XML format to export data:
+ /*
+ <?xml version="1.0" encoding="Windows-1252" ?>
+ <raylibAPI>
+ <Structs count="">
+ <Struct name="" fieldCount="" desc="">
+ <Field type="" name="" desc="">
+ <Field type="" name="" desc="">
+ </Struct>
+ <Structs>
+ <Enums count="">
+ <Enum name="" valueCount="" desc="">
+ <Value name="" integer="" desc="">
+ <Value name="" integer="" desc="">
+ </Enum>
+ </Enums>
+ <Functions count="">
+ <Function name="" retType="" paramCount="" desc="">
+ <Param type="" name="" desc="" />
+ <Param type="" name="" desc="" />
+ </Function>
+ </Functions>
+ </raylibAPI>
+ */
+
+ fprintf(outFile, "<?xml version=\"1.0\" encoding=\"Windows-1252\" ?>\n");
+ fprintf(outFile, "<raylibAPI>\n");
+
+ // Print structs info
+ fprintf(outFile, " <Structs count=\"%i\">\n", structCount);
+ for (int i = 0; i < structCount; i++)
+ {
+ fprintf(outFile, " <Struct name=\"%s\" fieldCount=\"%i\" desc=\"%s\">\n", structs[i].name, structs[i].fieldCount, structs[i].desc + 3);
+ for (int f = 0; f < structs[i].fieldCount; f++)
+ {
+ fprintf(outFile, " <Field type=\"%s\" name=\"%s\" desc=\"%s\" />\n", structs[i].fieldType[f], structs[i].fieldName[f], structs[i].fieldDesc[f] + 3);
+ }
+ fprintf(outFile, " </Struct>\n");
+ }
+ fprintf(outFile, " </Structs>\n");
+
+ // Print enums info
+ fprintf(outFile, " <Enums count=\"%i\">\n", enumCount);
+ for (int i = 0; i < enumCount; i++)
+ {
+ fprintf(outFile, " <Enum name=\"%s\" valueCount=\"%i\" desc=\"%s\">\n", enums[i].name, enums[i].valueCount, enums[i].desc + 3);
+ for (int v = 0; v < enums[i].valueCount; v++)
+ {
+ fprintf(outFile, " <Value name=\"%s\" integer=\"%i\" desc=\"%s\" />\n", enums[i].valueName[v], enums[i].valueInteger[v], enums[i].valueDesc[v] + 3);
+ }
+ fprintf(outFile, " </Enum>\n");
+ }
+ fprintf(outFile, " </Enums>\n");
+
+ // Print functions info
+ fprintf(outFile, " <Functions count=\"%i\">\n", funcCount);
+ for (int i = 0; i < funcCount; i++)
+ {
+ fprintf(outFile, " <Function name=\"%s\" retType=\"%s\" paramCount=\"%i\" desc=\"%s\">\n", funcs[i].name, funcs[i].retType, funcs[i].paramCount, funcs[i].desc + 3);
+ for (int p = 0; p < funcs[i].paramCount; p++)
+ {
+ fprintf(outFile, " <Param type=\"%s\" name=\"%s\" desc=\"%s\" />\n", funcs[i].paramType[p], funcs[i].paramName[p], funcs[i].paramDesc[p] + 3);
+ }
+ fprintf(outFile, " </Function>\n");
+ }
+ fprintf(outFile, " </Functions>\n");
+
+ fprintf(outFile, "</raylibAPI>\n");
+
+ } break;
+ default: break;
+ }
+
+ fclose(outFile);
+}