#pragma once #include #include #include #ifndef CL_API_FUNCTION #ifdef __cplusplus #define CL_API_FUNCTION extern "C" #else #define CL_API_FUNCTION #endif #endif #ifndef CL_INLINE #ifndef _MSC_VER #ifdef __cplusplus #define CL_INLINE inline #else #define CL_INLINE #endif #else #define CL_INLINE __forceinline #endif #endif #ifndef CL_Allocator struct MA_Arena; #define CL_Allocator MA_Arena * #endif #ifndef AND_CL_STRING_TERMINATE_ON_NEW_LINE #define AND_CL_STRING_TERMINATE_ON_NEW_LINE &&*T->stream != '\n' #endif typedef enum CL_Kind { CL_EOF, CL_MUL, CL_DIV, CL_MOD, CL_LEFTSHIFT, CL_RIGHTSHIFT, CL_ADD, CL_SUB, CL_EQUALS, CL_LESSERTHEN, CL_GREATERTHEN, CL_LESSERTHEN_OR_EQUAL, CL_GREATERTHEN_OR_EQUAL, CL_NOTEQUALS, CL_BITAND, CL_BITOR, CL_BITXOR, CL_AND, CL_OR, CL_NEG, CL_NOT, CL_DECREMENT, CL_INCREMENT, CL_POSTDECREMENT, CL_POSTINCREMENT, CL_ASSIGN, CL_DIVASSIGN, CL_MULASSIGN, CL_MODASSIGN, CL_SUBASSIGN, CL_ADDASSIGN, CL_ANDASSIGN, CL_ORASSIGN, CL_XORASSIGN, CL_LEFTSHIFTASSIGN, CL_RIGHTSHIFTASSIGN, CL_OPENPAREN, CL_CLOSEPAREN, CL_OPENBRACE, CL_CLOSEBRACE, CL_OPENBRACKET, CL_CLOSEBRACKET, CL_COMMA, CL_MACRO_CONCAT, CL_PREPROC_STRINGIFY, CL_QUESTION, CL_THREEDOTS, CL_SEMICOLON, CL_DOT, CL_COLON, CL_TAG, CL_ARROW, CL_EXPRSIZEOF, CL_DOCCOMMENT, CL_COMMENT, CL_IDENTIFIER, CL_STRINGLIT, CL_CHARLIT, CL_ERROR, CL_FLOAT, CL_INT, CL_PREPROC_NULL, CL_PREPROC_DEFINE, CL_PREPROC_IFDEF, CL_PREPROC_IFNDEF, CL_PREPROC_INCLUDE, CL_PREPROC_ENDIF, CL_PREPROC_IF, CL_PREPROC_PRAGMA, CL_PREPROC_ERROR, CL_PREPROC_ELSE, CL_PREPROC_ELIF, CL_PREPROC_UNDEF, CL_KEYWORD_VOID, CL_KEYWORD_INT, CL_KEYWORD_CHAR, CL_KEYWORD_UNSIGNED, CL_KEYWORD_SIGNED, CL_KEYWORD_LONG, CL_KEYWORD_SHORT, CL_KEYWORD_DOUBLE, CL_KEYWORD_FLOAT, CL_KEYWORD__BOOL, CL_KEYWORD__COMPLEX, CL_KEYWORD__IMAGINARY, CL_KEYWORD_STATIC, CL_KEYWORD_AUTO, CL_KEYWORD_CONST, CL_KEYWORD_EXTERN, CL_KEYWORD_INLINE, CL_KEYWORD_REGISTER, CL_KEYWORD_RESTRICT, CL_KEYWORD_VOLATILE, CL_KEYWORD__THREAD_LOCAL, CL_KEYWORD__ATOMIC, CL_KEYWORD__NORETURN, CL_KEYWORD_STRUCT, CL_KEYWORD_UNION, CL_KEYWORD_ENUM, CL_KEYWORD_TYPEDEF, CL_KEYWORD_DEFAULT, CL_KEYWORD_BREAK, CL_KEYWORD_RETURN, CL_KEYWORD_SWITCH, CL_KEYWORD_IF, CL_KEYWORD_ELSE, CL_KEYWORD_FOR, CL_KEYWORD_WHILE, CL_KEYWORD_CASE, CL_KEYWORD_CONTINUE, CL_KEYWORD_DO, CL_KEYWORD_GOTO, CL_KEYWORD_SIZEOF, CL_KEYWORD__ALIGNAS, CL_KEYWORD__ALIGNOF, CL_KEYWORD__STATIC_ASSERT, CL_KEYWORD__GENERIC, CL_COUNT, } CL_Kind; typedef enum CL_Fix { CL_FIX_NONE, CL_SUFFIX_U, CL_SUFFIX_UL, CL_SUFFIX_ULL, CL_SUFFIX_L, CL_SUFFIX_LL, CL_SUFFIX_F, CL_SUFFIX_FL, CL_PREFIX_U8, CL_PREFIX_U16, CL_PREFIX_U32, CL_PREFIX_L, } CL_Fix; typedef struct CL_Token CL_Token; struct CL_Token { CL_Kind kind; CL_Fix fix; bool is_hex : 1; bool is_inside_macro : 1; bool is_system_include : 1; bool is_there_whitespace_before_token : 1; uint32_t id; int len; char *str; // Not storing line_begin like I would normally cause the user could // override the line and file information using directives. // On error need to do search if I want nice error context. int line, column; char *file; union { double f64; uint64_t u64; char *intern; char *string_literal; struct CL_Message *error; }; }; typedef struct CL_Message CL_Message; struct CL_Message { CL_Message *next; char *string; CL_Token token; }; typedef struct CL_Lexer CL_Lexer; struct CL_Lexer { CL_Message *first_message; CL_Message *last_message; int errors; char *stream; char *stream_begin; int line; int column; char *file; bool inside_of_macro; // filters bool skip_comments : 1; bool skip_macros : 1; bool select_includes : 1; bool select_comments : 1; bool select_macros : 1; CL_Allocator arena; }; typedef struct CL_SearchPaths CL_SearchPaths; struct CL_SearchPaths { char **include_path; int include_path_count; char **system_include_path; int system_include_path_count; char *file_begin_to_ignore; }; CL_API_FUNCTION CL_Token CL_Next(CL_Lexer *T); CL_API_FUNCTION CL_Lexer CL_Begin(CL_Allocator arena, char *stream, char *filename); CL_API_FUNCTION char *CL_ResolveFilepath(CL_Allocator arena, CL_SearchPaths *search_paths, char *filename, char *parent_file, bool is_system_include); CL_API_FUNCTION void CL_StringifyMessage(char *buff, int buff_size, CL_Message *msg); CL_API_FUNCTION void CL_Stringify(char *buff, int buff_size, CL_Token *token); CL_API_FUNCTION void CL_SetTokenLength(CL_Lexer *T, CL_Token *token); CL_API_FUNCTION void CL_ParseCharLiteral(CL_Lexer *T, CL_Token *token); CL_API_FUNCTION void CL_ParseString(CL_Lexer *T, CL_Token *token); CL_API_FUNCTION void CL_IsIdentifierKeyword(CL_Token *token); CL_API_FUNCTION void CL_LexMacroInclude(CL_Lexer *T, CL_Token *token); CL_API_FUNCTION bool CL_LexMacro(CL_Lexer *T, CL_Token *token); CL_API_FUNCTION void CL_PrepareToken(CL_Lexer *T, CL_Token *token, bool skipped_space); CL_API_FUNCTION void CL_DefaultTokenize(CL_Lexer *T, CL_Token *token); CL_API_FUNCTION bool CL_EatWhitespace(CL_Lexer *T); CL_API_FUNCTION void CL_TryToFinalizeToken(CL_Lexer *T, CL_Token *token); CL_API_FUNCTION void CL_InitNextToken(CL_Lexer *T, CL_Token *token); CL_INLINE int CL_StringLength(char *string) { int len = 0; while (*string++ != 0) len++; return len; } CL_INLINE bool CL_StringsAreEqual(char *a, int64_t alen, const char *b, int64_t blen) { if (alen != blen) return false; for (int i = 0; i < alen; i += 1) { if (a[i] != b[i]) return false; } return true; } CL_INLINE bool CL_IsIdentifier(CL_Token *token, char *str) { int str_len = CL_StringLength(str); bool result = token->kind == CL_IDENTIFIER && CL_StringsAreEqual(token->str, token->len, str, str_len); return result; } CL_INLINE bool CL_IsAssign(CL_Kind op) { bool result = op >= CL_ASSIGN && op <= CL_RIGHTSHIFTASSIGN; return result; } CL_INLINE bool CL_IsKeywordType(CL_Kind op) { bool result = op >= CL_KEYWORD_VOID && op <= CL_KEYWORD__IMAGINARY; return result; } CL_INLINE bool CL_IsKeywordTypeOrSpec(CL_Kind op) { bool result = op >= CL_KEYWORD_VOID && op <= CL_KEYWORD_TYPEDEF; return result; } CL_INLINE bool CL_IsMacro(CL_Kind kind) { bool result = kind >= CL_PREPROC_DEFINE && kind <= CL_PREPROC_UNDEF; return result; } CL_INLINE bool CL_IsKeyword(CL_Kind kind) { bool result = kind >= CL_KEYWORD_VOID && kind <= CL_KEYWORD__GENERIC; return result; } CL_INLINE bool CL_IsKeywordOrIdent(CL_Kind kind) { bool result = CL_IsKeyword(kind) || kind == CL_IDENTIFIER; return result; }