#ifndef CCB_H #define CCB_H #ifdef _ZCC //#define __func__ "TODO: function name" #endif #define CCB_X_OBJC #define CCB_X_OBJX #include #include #include #include typedef struct ccb ccb_t; typedef struct ccb_pos ccb_pos_t; /* * Type: ccb_list_t * A standard double-linked list (NOTE: Moved to the top since it's needed in ccb_t) */ typedef struct ccb_list_s ccb_list_t; struct ccb_pos { int line, col; // These record the line of compiler input int uline, ucol; // "User" line/column (within the file that the preprocessed input claims to be from) char* ufile; // "User" filename (the file that the preprocessed input claims to be from) }; struct ccb { FILE* input; FILE* output; bool dump_ast; bool silent; bool include_code; bool include_data; ccb_pos_t pos; //int line, col; // These record the line of compiler input //int uline, ucol; // "User" line/column (within the file that the preprocessed input claims to be from) //char* ufile; // "User" filename (the file that the preprocessed input claims to be from) int iline, icol; // Previous increment of line/column (subtracted on an ungetc equivalent!) const char* func_name; // This is used for the __func__ builtin int func_callconv; int default_callconv; const char* mod_name; // Module name, used for determining name of initialisation function or other module-specific symbols ccb_list_t* mod_initstmts; // If used, the list of init statements in the module initialisation function to add to ccb_list_t* declarednames; // If used, the list of predeclared names ccb_list_t* knownexterns; // If used, the list of known extern symbols long bsscount; // Can be used by the backend if it needs to manually track & generate BSS padding/alignment (which is seemingly the case for NASM...) const char* oop_classname; bool oop_ismeta; const char* sym_prefix; }; /* * Type: ccb_string_t * A type capable of representing a self-resizing string with * O(1) strlen. */ typedef struct ccb_string_s ccb_string_t; /* * Function: ccb_string_create * Create a string object */ ccb_string_t* ccb_string_create(void); /* * Function: ccb_string_buffer * Return the raw buffer of a string object */ char* ccb_string_buffer(ccb_string_t* string); /* * Function: ccb_string_cat * Append a character to a string object */ void ccb_string_cat(ccb_string_t* string, char ch); void ccb_string_catcstr(ccb_string_t* string, const char* str); void ccb_string_catint(ccb_string_t* string, long long i); /* * Function: ccb_string_catf * Append a formatted string to a string object */ void ccb_string_catf(ccb_string_t* string, const char* fmt, ...); /* * Function: ccb_string_quote * Escape a string's quotes */ char* ccb_string_quote(char* p); // C/GNU-style char* ccb_string_quote_fasm(char* p, char quotechar); // FASM/Intel-style (closes and reopens quotes, adding special chars by number) /* * Macro: CCB_SENTINEL_LIST * Initialize an empty list in place */ /*#define CCB_SENTINEL_LIST ((ccb_list_t) { \ .length = 0, \ .head = NULL, \ .tail = NULL \ })*/ /* * Type: ccb_list_iterator_t * A type capable of representing an itrator for a */ typedef struct ccb_list_iterator_s ccb_list_iterator_t; /* * Function: ccb_list_create * Creates a new list */ ccb_list_t* ccb_list_create(void); /* * Function: ccb_list_push * Push an element onto a list */ void ccb_list_push(ccb_list_t* list, void* element); /* * Function: ccb_list_pop * Pop an element from a list */ void* ccb_list_pop(ccb_list_t* list); /* * Function: ccb_list_length * Used to retrieve length of a list object */ int ccb_list_length(ccb_list_t* list); /* * Function: ccb_list_shift * Like a ccb_list_pop but shift from the head (instead of the tail) */ void* ccb_list_shift(ccb_list_t* list); /* * Function: ccb_list_reverse * Reverse the contents of a list */ ccb_list_t* ccb_list_reverse(ccb_list_t* list); /* * Function: ccb_list_iterator * Create an iterator for a given list object */ ccb_list_iterator_t* ccb_list_iterator(ccb_list_t* list); /* * Function: ccb_list_iterator_next * Increment the list iterator while returning the given element */ void* ccb_list_iterator_next(ccb_list_iterator_t* iter); /* * Function: ccb_list_iterator_end * Test if the iterator is at the end of the list */ bool ccb_list_iterator_end(ccb_list_iterator_t* iter); /* * Function: ccb_list_tail * Get the last element in a list */ void* ccb_list_tail(ccb_list_t* list); typedef struct ccb_list_node_s ccb_list_node_t; struct ccb_list_s { int length; ccb_list_node_t* head; ccb_list_node_t* tail; }; /* * Type: ccb_table_t * A key value associative table */ typedef struct ccb_table_s ccb_table_t; struct ccb_table_s { ccb_list_t* list; ccb_table_t* parent; }; /* * Function: ccb_table_create * Creates a ccb_table_t object */ void* ccb_table_create(void* parent); /* * Funciton: ccb_table_find * Searches for a given value in the table based on the * key associated with it. */ void* ccb_table_find(ccb_table_t* table, const char* key); /* * Function: ccb_table_insert * Inserts a value for the given key as an entry in the * table. */ void ccb_table_insert(ccb_table_t* table, char* key, void* value); /* * Function: ccb_table_parent * Returns the parent opaque object for the given table to * be used as the argument to a new table. */ void* ccb_table_parent(ccb_table_t* table); /* * Function: ccb_table_values * Generates a list of all the values in the table, useful for * iterating over the values. */ ccb_list_t* ccb_table_values(ccb_table_t* table); /* * Function: ccb_table_keys * Generate a list of all the keys in the table, useful for * iteration over the keys. */ ccb_list_t* ccb_table_keys(ccb_table_t* table); /* * Macro: CCB_SENTINEL_TABLE * Initialize an empty table in place */ /*#define CCB_SENTINEL_TABLE ((ccb_table_t) { \ .list = ccb_list_empty(ccb), //&CCB_SENTINEL_LIST \ .parent = NULL \ })*/ ccb_table_t* ccb_table_empty(); #define CCB_MIN(A, B) (((A) < (B)) ? (A) : (B)) #define CCB_MAX(A, B) (((A) > (B)) ? (A) : (B)) /* * Function: ccb_memory_allocate * Allocate some memory */ void* ccb_memory_allocate(size_t bytes); int ccb_strcasecmp(const char* s1, const char* s2); int ccb_strncasecmp(const char* s1, const char* s2, size_t n); /* * Type: ccb_lexer_token_type_t * Type to describe a tokens type. * * Remarks: * Implemented as a typedef of an enumeration, ccb_lexer_token_t * is used to describe the current lexer token. The following * tokens exist (as constants). * * Tokens: * CCB_LEXER_TOKEN_IDENTIFIER - Identifier * CCB_LEXER_TOKEN_PUNCT - Language punctuation * CCB_LEXER_TOKEN_CHAR - Character literal * CCB_LEXER_TOKEN_STRING - String literal * CCB_LEXER_TOKEN_NUMBER - Number (of any type) * CCB_LEXER_TOKEN_EQUAL - Equal * CCB_LEXER_TOKEN_LEQUAL - Lesser-or-equal * CCB_LEXER_TOKEN_GEQUAL - Greater-or-equal * CCB_LEXER_TOKEN_NEQUAL - Not-equal * CCB_LEXER_TOKEN_INCREMENT - Pre/post increment * CCB_LEXER_TOKEN_DECREMENT - Pre/post decrement * CCB_LEXER_TOKEN_ARROW - Pointer arrow `->` * CCB_LEXER_TOKEN_LSHIFT - Left shift * CCB_LEXER_TOKEN_RSHIFT - Right shift * CCB_LEXER_TOKEN_COMPOUND_ADD - Compound-assignment addition * CCB_LEXER_TOKEN_COMPOUND_SUB - Compound-assignment subtraction * CCB_LEXER_TOKEN_COMPOUND_MUL - Compound-assignment multiplication * CCB_LEXER_TOKEN_COMPOUND_DIV - Compound-assignment division * CCB_LEXER_TOKEN_COMPOUND_MOD - Compound-assignment moduluas * CCB_LEXER_TOKEN_COMPOUND_OR - Compound-assignment bit-or * CCB_LEXER_TOKEN_COMPOUND_XOR - Compound-assignment bit-xor * CCB_LEXER_TOKEN_COMPOUND_LSHIFT - Compound-assignment left-shift * LEXER_TOKEN_COMPOUND_RSHIFt - Compound-assignment right-shift * CCB_LEXER_TOKEN_AND - Logical and * CCB_LEXER_TOKEN_OR - Logical or */ enum ccb_lexer_token_enum { CCB_LEXER_TOKEN_IDENTIFIER, CCB_LEXER_TOKEN_PUNCT, CCB_LEXER_TOKEN_CHAR, CCB_LEXER_TOKEN_STRING, CCB_LEXER_TOKEN_NUMBER, CCB_LEXER_TOKEN_EQUAL = 0x200, CCB_LEXER_TOKEN_LEQUAL, CCB_LEXER_TOKEN_GEQUAL, CCB_LEXER_TOKEN_NEQUAL, CCB_LEXER_TOKEN_INCREMENT, CCB_LEXER_TOKEN_DECREMENT, CCB_LEXER_TOKEN_ARROW, CCB_LEXER_TOKEN_LSHIFT, CCB_LEXER_TOKEN_RSHIFT, CCB_LEXER_TOKEN_COMPOUND_ADD, CCB_LEXER_TOKEN_COMPOUND_SUB, CCB_LEXER_TOKEN_COMPOUND_MUL, CCB_LEXER_TOKEN_COMPOUND_DIV, CCB_LEXER_TOKEN_COMPOUND_MOD, CCB_LEXER_TOKEN_COMPOUND_AND, CCB_LEXER_TOKEN_COMPOUND_OR, CCB_LEXER_TOKEN_COMPOUND_XOR, CCB_LEXER_TOKEN_COMPOUND_LSHIFT, CCB_LEXER_TOKEN_COMPOUND_RSHIFT, CCB_LEXER_TOKEN_AND, CCB_LEXER_TOKEN_OR }; typedef enum ccb_lexer_token_enum ccb_lexer_token_type_t; /* * Class: ccb_lexer_token_t * Describes a token in the token stream */ typedef struct { /* * Variable: type * The token type */ ccb_lexer_token_type_t type; union { long integer; int punct; char* string; char character; }; } ccb_lexer_token_t; /* * Function: ccb_lexer_islong * Checks for a given string if it's a long-integer-literal. * * Parameters: * string - The string to check * * Remarks: * Returns `true` if the string is a long-literal, * `false` otherwise. */ bool ccb_lexer_islong(ccb_t* ccb, char* string); /* * Function: ccb_lexer_isint * Checks for a given string if it's a int-integer-literal. * * Parameters: * string - The string to check * * Remarks: * Returns `true` if the string is a int-literal, * `false` otherwise. */ bool ccb_lexer_isint(ccb_t* ccb, char* string); /* * Function: ccb_lexer_isfloat * Checks for a given string if it's a floating-point-literal. * * Parameters: * string - The string to check * * Remarks: * Returns `true` if the string is floating-point-literal, * `false` otherwise. */ bool ccb_lexer_isfloat(ccb_t* ccb, char* string); /* * Function: ccb_lexer_ispunct * Checks if a given token is language punctuation and matches. * * Parameters: * token - The token to test * c - The punction to test if matches * * Remarks: * Returns `true` if the given token is language punctuation and * matches *c*. */ bool ccb_lexer_ispunct(ccb_t* ccb, ccb_lexer_token_t* token, int c); /* * Function: ccb_lexer_unget * Undo the given token in the token stream. * * Parameters: * token - The token to unget */ void ccb_lexer_unget(ccb_t* ccb, ccb_lexer_token_t* token); /* * Function: ccb_lexer_next * Get the next token in the token stream. * * Returns: * The next token in the token stream or NULL * on failure or EOF. */ ccb_lexer_token_t* ccb_lexer_next(ccb_t* ccb); /* * Function: ccb_lexer_peek * Look at the next token without advancing the stream. * * Returns: * The next token without advancing the token stream or NULL on failure * or EOF. * * Remarks: * The function will peek ahead to see the next token in the stream * without advancing the lexer state. */ ccb_lexer_token_t* ccb_lexer_peek(ccb_t* ccb); /* * Function: ccb_lexer_tokenstr * Convert a token to a human-readable representation * * Parameters: * token - The token to convert * * Returns: * A string representation of the token or NULL on failure. */ char* ccb_lexer_tokenstr(ccb_t* ccb, ccb_lexer_token_t* token); typedef struct ccb_ast_s ccb_ast_t; /* * Type: ccb_ast_type_t * The type of ast node * * Constants: * * CCB_AST_TYPE_LITERAL - Literal * CCB_AST_TYPE_STRING - String literal * CCB_AST_TYPE_VAR_LOCAL - Local variable * CCB_AST_TYPE_VAR_GLOBAL - Global variable * CCB_AST_TYPE_CALL - Direct function call * CCB_AST_TYPE_PTRCALL - Indirect function call * CCB_AST_TYPE_FUNCTION - Function * CCB_AST_TYPE_PROTOTYPE - Prototype * CCB_AST_TYPE_DECLARATION - Declaration * CCB_AST_TYPE_INITIALIZER - Initializer * CCB_AST_TYPE_STRUCT - Structure * CCB_AST_TYPE_ADDRESS - Address of operation * CCB_AST_TYPE_DEREFERENCE - Pointer dereference * CCB_AST_TYPE_EXPRESSION_TERNARY - Ternary expression * CCB_AST_TYPE_EXPRESSION_CAST - Type cast expression * CCB_AST_TYPE_STATEMENT_IF - If statement * CCB_AST_TYPE_STATEMENT_FOR - For statement * CCB_AST_TYPE_STATEMENT_WHILE - While statement * CCB_AST_TYPE_STATEMENT_DO - Do statement * CCB_AST_TYPE_STATEMENT_SWITCH - Switch statement * CCB_AST_TYPE_STATEMENT_CASE - Switch statement case * CCB_AST_TYPE_STATEMENT_DEFAULT - Switch statement default case * CCB_AST_TYPE_STATEMENT_RETURN - Return statement * CCB_AST_TYPE_STATEMENT_BREAK - Break statement * CCB_AST_TYPE_STATEMENT_CONTINUE - Continue statement * CCB_AST_TYPE_STATEMENT_COMPOUND - Compound statement * CCB_AST_TYPE_STATEMENT_GOTO - Goto statement * CCB_AST_TYPE_STATEMENT_LABEL - Goto statement label * CCB_AST_TYPE_POST_INCREMENT - Post increment operation * CCB_AST_TYPE_POST_DECREMENT - Post decrement operation * CCB_AST_TYPE_PRE_INCREMENT - Pre increment operation * CCB_AST_TYPE_PRE_DECREMENT - Pre decrement operation * CCB_AST_TYPE_LSHIFT - Left shift operation * CCB_AST_TYPE_RSHIFT - Right shift operation * CCB_AST_TYPE_EQUAL - Equality condition * CCB_AST_TYPE_GEQUAL - Greater-or-equal condition * CCB_AST_TYPE_LEQUAL - Less-or-equal condition * CCB_AST_TYPE_NEQUAL - Not-equal condition * CCB_AST_TYPE_AND - Logical-and operation * CCB_AST_TYPE_OR - Logical-or operation */ typedef enum { CCB_AST_TYPE_LITERAL = 0x100, CCB_AST_TYPE_STRING, CCB_AST_TYPE_VAR_LOCAL, CCB_AST_TYPE_VAR_GLOBAL, CCB_AST_TYPE_CALL, CCB_AST_TYPE_PTRCALL, CCB_AST_TYPE_FUNCTION, CCB_AST_TYPE_PROTOTYPE, CCB_AST_TYPE_DECLARATION, CCB_AST_TYPE_INITIALIZER, CCB_AST_TYPE_STRUCT, CCB_AST_TYPE_ADDRESS, CCB_AST_TYPE_DEREFERENCE, CCB_AST_TYPE_EXPRESSION_TERNARY, CCB_AST_TYPE_EXPRESSION_CAST, CCB_AST_TYPE_EXPRESSION_COMMA, CCB_AST_TYPE_STATEMENT_IF, CCB_AST_TYPE_STATEMENT_FOR, CCB_AST_TYPE_STATEMENT_WHILE, CCB_AST_TYPE_STATEMENT_DO, CCB_AST_TYPE_STATEMENT_SWITCH, CCB_AST_TYPE_STATEMENT_CASE, CCB_AST_TYPE_STATEMENT_DEFAULT, CCB_AST_TYPE_STATEMENT_RETURN, CCB_AST_TYPE_STATEMENT_BREAK, CCB_AST_TYPE_STATEMENT_CONTINUE, CCB_AST_TYPE_STATEMENT_COMPOUND, CCB_AST_TYPE_STATEMENT_GOTO, CCB_AST_TYPE_STATEMENT_LABEL, CCB_AST_TYPE_STATEMENT_ASM, CCB_AST_TYPE_POST_INCREMENT, CCB_AST_TYPE_POST_DECREMENT, CCB_AST_TYPE_PRE_INCREMENT, CCB_AST_TYPE_PRE_DECREMENT, CCB_AST_TYPE_LSHIFT, CCB_AST_TYPE_RSHIFT, CCB_AST_TYPE_EQUAL, CCB_AST_TYPE_GEQUAL, CCB_AST_TYPE_LEQUAL, CCB_AST_TYPE_NEQUAL, CCB_AST_TYPE_AND, CCB_AST_TYPE_OR } ccb_ast_type_t; /* * Type: ccb_type_t * Type describing the ast type. * * Constants: * * CCB_TYPE_VOID - void * CCB_TYPE_CHAR - char * CCB_TYPE_SHORT - short * CCB_TYPE_INT - int * CCB_TYPE_LONG - long * CCB_TYPE_LLONG - long long * CCB_TYPE_DOUBLE - double * CCB_TYPE_LDOUBLE - long double * CCB_TYPE_ARRAY - array (also contains a ccb_type_t for base type) * CCB_TYPE_POINTER - pointer (also contains a ccb_type_t for base type) * CCB_TYPE_STRUCTURE - structure (user defined) * CCB_TYPE_FUNCTION - function (user defined) * TYPE_CECL - used by the parser for dealing with declarations */ typedef enum { CCB_TYPE_VOID, CCB_TYPE_CHAR, CCB_TYPE_SHORT, CCB_TYPE_INT, CCB_TYPE_LONG, CCB_TYPE_LLONG, CCB_TYPE_FLOAT, CCB_TYPE_DOUBLE, CCB_TYPE_LDOUBLE, CCB_TYPE_ARRAY, CCB_TYPE_POINTER, CCB_TYPE_STRUCTURE, CCB_TYPE_FUNCTION, CCB_TYPE_CDECL #ifdef CCB_X_OBJC , CCB_TYPE_ID #endif } ccb_type_t; /* * Type: ccb_ast_data_type_t * Type describing the indice into `ast_data_table` * * Constants: * * CCB_AST_DATA_VOID - void * CCB_AST_DATA_LONG - long * CCB_AST_DATA_LLONG - long long * CCB_AST_DATA_INT - int * CCB_AST_DATA_SHORT - short * CCB_AST_DATA_CHAR - char * CCB_AST_DATA_FLOAT - float * CCB_AST_DATA_DOUBLE - double * CCB_AST_DATA_LDOUBLE - long double * CCB_AST_DATA_UINT - unsigned int * CCB_AST_DATA_ULONG - unsigned long * CCB_AST_DATA_ULLONG - unsigned long long * CCB_AST_DATA_FUNCTION - function (current) */ typedef enum { CCB_AST_DATA_VOID, CCB_AST_DATA_LONG, CCB_AST_DATA_LLONG, CCB_AST_DATA_INT, CCB_AST_DATA_SHORT, CCB_AST_DATA_CHAR, CCB_AST_DATA_FLOAT, CCB_AST_DATA_DOUBLE, CCB_AST_DATA_LDOUBLE, CCB_AST_DATA_UINT, CCB_AST_DATA_ULONG, CCB_AST_DATA_ULLONG, CCB_AST_DATA_FUNCTION, #ifdef CCB_X_OBJC CCB_AST_DATA_ID, #endif CCB_AST_DATA_COUNT } ccb_ast_data_type_t; /* * Type: ccb_cdecl_t * Describes type of declarations * * Constants: * * CCB_CDECL_BODY - function body * CCB_CDECL_PARAMETER - parameters (with name) * CCB_CDECL_OBJCPARAMETER - objc parameter (type in brackets then name) * CCB_CDECL_TYPEONLY - parameters (without name) * CCB_CDECL_CAST - cast */ typedef enum { CCB_CDECL_BODY = 1, CCB_CDECL_PARAMETER, #ifdef CCB_X_OBJC CCB_CDECL_OBJCPARAMETER, #endif CCB_CDECL_TYPEONLY, CCB_CDECL_CAST } ccb_cdecl_t; /* * Type: ccb_storage_t * Describes the storage class for a given variable * * Constants: * * CCB_STORAGE_TYPEDEF - typedef to another type * CCB_STORAGE_EXTERN - external linkage * CCB_STORAGE_STATIC - static storage * CCB_STORAGE_AUTO - automatic storage (implicit) * CCB_STORAGE_REGISTER - make use of register for storage */ typedef enum { CCB_STORAGE_TYPEDEF = 1, CCB_STORAGE_EXTERN, CCB_STORAGE_STATIC, CCB_STORAGE_AUTO, CCB_STORAGE_REGISTER } ccb_storage_t; /* * Struct: ccb_data_type_t * A structure that describes a data type. */ typedef struct ccb_data_type_s ccb_data_type_t; struct ccb_data_type_s { /* * Variable: type * The type of the data type. * * See Constants for a list of * valid constant values. */ ccb_type_t type; /* * Variable: type * The size of the given data data */ int size; /* * Variable: sign * Describes if the type is signed or unsigned. * * Contains `true` when signed, otherwise `false. */ bool sign; /* * Variable: isstatic * True if when static (global only) */ bool isstatic; /* * Variable: length * Instances of the data type. * * When used as a base-type, i.e not an array; this will be * 1, otherwise it will be the length of the array, or -1 * if the size of the array is unknown. */ int length; /* * Variable: pointer * Pointer to pointer type if pointer * * When the variable is a pointer type, this will point to another * data type that describes the base type of the pointer, NULL other- * wise. */ ccb_data_type_t* pointer; /* structure */ struct { /* Variable: classname * Name of the declared class, or null (NOTE: This may be dynamically resolved). */ const char* classname; /* Variable: supername * Name of the declared superclass (NOTE: This may be dynamically resolved). */ const char* supername; /* Variable: supertype * Pointer to the superclass type, if defined. */ //ccb_data_type_t* supertype; /* Variable: imethods * Table of instance methods, if defined. */ ccb_table_t* imethods; /* Variable: cmethods * Table of class methods, if defined. */ ccb_table_t* cmethods; /* * Variable: fields * Pointer to a table of fields (if structure) */ ccb_table_t* fields; /* * Variable: offset * Offset of the given field in a structure (if a structure base type) */ int offset; /* * Variable: isstruct * If we're dealing with a structure this will be true, false * otherwise. */ bool isstruct; }; /* function */ struct { /* * Variable: returntype * Pointer to a data type which describes the return type * of the function (if a function) */ ccb_data_type_t* returntype; /* * Variable: parameters * Pointer to a list of parameters for a function. */ ccb_list_t* parameters; /* * Variable: hasdots * Describes if the given function is variable-argument. * * Contains the value `true` when the function has * three dots `...` in it's prototype, otherwise `false`. */ bool hasdots; int callconv; bool isnaked; }; }; /* * Struct: ccb_ast_string_t * The *CCB_AST_TYPE_STRING* ast node. */ typedef struct { /* * Variable: data * String contents */ char* data; /* * Variable: label * Name of the label associated with the string. */ char* label; } ccb_ast_string_t; /* * Struct: ccb_ast_variable_t * The *CCB_AST_TYPE_VAR_LOCAL* and *CCB_AST_TYPE_VAR_GLOBAL* ast node. */ typedef struct { /* * Variable: name * Name of the variable */ char* name; /* * Variable: off * Offset of the variable on the stack. */ int off; /* * Variable: label * Name of the label associated with the variable. */ char* label; /* * Variable: init * Compound literal list for initialization */ ccb_list_t* init; /* * Variable: isclassobj * This is set to true for global variables representing class objects, so that initialisation can be done at send-time. */ bool isclassobj; } ccb_ast_variable_t; /* * Struct ccb_ast_function_call_t * Function call * * Remarks: * Not associated with any node. Instead describes the * data associated with a function call for *ccb_ast_function_t* */ typedef struct { /* * Variable: args * Pointer to a list of arguments for a function call */ ccb_list_t* args; /* * Variable: paramtypes * Pointer to a list of parameter types for the function call. */ ccb_list_t* paramtypes; /* * Variable: callable * Expression resulting in callable value (function pointer) for a PTRCALL. */ ccb_ast_t* callable; } ccb_ast_function_call_t; /* * Struct: ccb_ast_function_t * The *CCB_AST_TYPE_FUNCTION* ast node. */ typedef struct { /* * Variable: name * The function name */ char* name; /* * Variable: callconv * Records any non-default calling convention for a call */ int callconv; /* * Variable: isnaked * Set to true if the function prologue is not to be emitted (only used for * functions implemented primarily in assembly language). */ //bool isnaked; /* * Variable: call * Data associated with a function call. */ ccb_ast_function_call_t call; /* * Variable: params * Pointer to a list of parameters. */ ccb_list_t* params; /* * Variable: locals * Pointer to a list of locals. */ ccb_list_t* locals; /* * Variable: body * Pointer to an ast node which describes the body. * * Remarks: * A body is usually composed of a serise of ast nodes, * typically a compound expression, but could also contain * nested compound expressions. Think of this as a pointer * to the head of the beginning of a serise of basic-blocks * which are the forming of the function body. */ ccb_ast_t* body; } ccb_ast_function_t; /* * Struct: ccb_ast_unary_t * Represents a unary operation in the AST tree */ typedef struct { /* * Variable: operand * Pointer to the operand the unary operation is to * be performed on. */ ccb_ast_t* operand; } ccb_ast_unary_t; /* * Struct: ccb_ast_decl_t * Represents a declaration in the AST tree */ typedef struct { /* * Variable: var * Pointer to the variable node associated with the * declaration. */ ccb_ast_t* var; /* * Variable: init * When the declaration includes an initialization this points * to a initlization list. */ ccb_list_t* init; } ccb_ast_decl_t; /* * Struct: ccb_ast_ifthen_t * Represents a if-then node in the AST tree. * * Remarks: * Describes a two-branch gaurded by conditional test node in the AST * tree for implementing ternary expressions and if statements. */ typedef struct { /* * Variable: cond * The condition node */ ccb_ast_t* cond; /* * Variable: then * Basic block for truth path in branch */ ccb_ast_t* then; /* * Variable: last * Basic block for false path in branch */ ccb_ast_t* last; } ccb_ast_ifthen_t; /* * Struct: ccb_ast_for_t * Represents a for-loop node in the AST tree. * * Remarks: * Standard for loop with precondition / initilization expression, * conditionally testsed, and post step / expression, ergo * for(init; cond; step) body; */ typedef struct { /* Variable: init */ ccb_ast_t* init; /* Variable: cond */ ccb_ast_t* cond; /* Variable: step */ ccb_ast_t* step; /* Variable: body */ ccb_ast_t* body; } ccb_ast_for_t; /* * Struct: ccb_ast_init_t * Represents an initializer in the AST tree. * * Remarks: * Represents array initializer lists, as well as aggregate initializer * lists for structure, enum and union. Also represents a designated * initializer for a structure. */ typedef struct { /* Variable: value */ ccb_ast_t* value; /* Variable: offset */ int offset; /* Variable: type */ ccb_data_type_t* type; } ccb_ast_init_t; /* * Struct: ccb_ast_switch_t * Represents a switch statement in the AST tree. */ typedef struct { /* Variable: expr */ ccb_ast_t* expr; /* Variable: body */ ccb_ast_t* body; } ccb_ast_switch_t; /* * Struct: ccb_ast_goto_t * Represents a goto statement (or label) in the AST tree. */ typedef struct { /* * Variable: label * When not used as a goto statement, describes the name of a label * that may be 'gone to' with 'goto' */ char* label; /* * Variable: where * Where to go (label wise) for a goto statement. */ char* where; } ccb_ast_goto_t; /* * Struct: ccb_ast_asm_t * Represents an asm statement in the AST tree. */ typedef struct { /* * Variable: code * Assembler code to output. */ ccb_list_t* code; } ccb_ast_asm_t; /* * Struct: ccb_ast_t * The monolthic ast tree and node at the same time. * * Remarks: * The ast tree is just a doubly-linked list of ast nodes which are * capable of being all the possible ast nodes at once. This is * acomplished with a rather large union of all ast nodes. The only * thing that declares what a node actually is, is the nodes type * member. This is beneficial to keeping the complexity of the AST * tree down, while also keeping memory footprint low. One more * interesting aspect of this is the ability to have the AST tree * nodes (next, prev), which make up the doubly-linked list part * of the same union, giving us a free way to terminate the tree * without using additional space to determine it. */ struct ccb_ast_s { int type; ccb_data_type_t* ctype; int return_callconv; // A bit of a hack, used for checking the calling convention for a return statement ccb_pos_t pos; union { int casevalue; long long integer; char character; ccb_ast_string_t string; ccb_ast_variable_t variable; ccb_ast_function_t function; ccb_ast_unary_t unary; ccb_ast_decl_t decl; ccb_ast_ifthen_t ifstmt; ccb_ast_for_t forstmt; ccb_ast_switch_t switchstmt; ccb_ast_t* returnstmt; ccb_list_t* compound; ccb_ast_init_t init; ccb_ast_goto_t gotostmt; ccb_ast_asm_t asmstmt; struct { ccb_ast_t* left; ccb_ast_t* right; }; struct { ccb_ast_t* structure; char* field; ccb_data_type_t* fieldtype; }; struct { double value; char* label; } floating; }; }; extern ccb_data_type_t* ccb_ast_data_table[CCB_AST_DATA_COUNT]; extern ccb_list_t* ccb_ast_floats; extern ccb_list_t* ccb_ast_strings; extern ccb_list_t* ccb_ast_locals; extern ccb_list_t* ccb_ast_gotos; extern ccb_table_t* ccb_ast_globalenv; extern ccb_table_t* ccb_ast_localenv; extern ccb_table_t* ccb_ast_structures; extern ccb_table_t* ccb_ast_unions; extern ccb_table_t* ccb_ast_labels; /* * Function: ccb_util_init * Initialises any structures required for lists or other utilities. */ void ccb_util_init(ccb_t* ccb); /* * Function: ccb_ast_init * Initialises the AST's internal structures according to arch_xxx results. */ void ccb_ast_init(ccb_t* ccb); /* * Function: ccb_ast_structure_reference * Creates an structure reference of a given type for a given field * * Parameters: * type - The type of the field for reference * structure - The structure that contains said field to be referenced * name - The name of the field in that structure to reference * * Returns: * An ast node referencing that field in that paticular structure on * success, otherwise NULL. */ ccb_ast_t* ccb_ast_structure_reference(ccb_t* ccb, ccb_data_type_t* type, ccb_ast_t* structure, char* name); /* * Function: ccb_ast_structure_field * Copies a given field data type and changes it offset * * Parameters: * type - Pointer to the structure field data type * offset - The offset of the copied data type in the structure * * Returns: * A copy of the structure fields data type with the supplied * offset on success, NULL otherwise. */ ccb_data_type_t* ccb_ast_structure_field(ccb_t* ccb, ccb_data_type_t* type, int offset); /* * Function: ccb_ast_structure_new * Creates a structure data type * * Parameters; * field - A table of ccb_data_type_t fields for the structure * size - The size of the structure * isstruct - true if structure, false if structure-like * * Returns: * A new structure data type with the specified fields and size on * success, NULL otherwise. */ ccb_data_type_t* ccb_ast_structure_new(ccb_t* ccb, ccb_table_t* fields, int size, bool isstruct); ccb_ast_t* ccb_ast_new_unary(ccb_t* ccb, int type, ccb_data_type_t* data, ccb_ast_t* operand); ccb_ast_t* ccb_ast_new_binary(ccb_t* ccb, int type, ccb_ast_t* left, ccb_ast_t* right); ccb_ast_t* ccb_ast_new_comma(ccb_t* ccb, int type, ccb_ast_t* left, ccb_ast_t* right); ccb_ast_t* ccb_ast_new_integer(ccb_t* ccb, ccb_data_type_t* type, long long int value); ccb_ast_t* ccb_ast_new_floating(ccb_t* ccb, ccb_data_type_t*, double value); ccb_ast_t* ccb_ast_new_char(ccb_t* ccb, char value); ccb_ast_t* ccb_ast_new_string(ccb_t* ccb, char* value); ccb_ast_t* ccb_ast_new_label(ccb_t* ccb, char*); char* ccb_ast_label(ccb_t* ccb); ccb_ast_t* ccb_ast_declaration(ccb_t* ccb, ccb_ast_t* var, ccb_list_t* init); ccb_ast_t* ccb_ast_variable_local(ccb_t* ccb, ccb_data_type_t* type, char* name); ccb_ast_t* ccb_ast_variable_global(ccb_t* ccb, ccb_data_type_t* type, char* name); ccb_ast_t* ccb_ast_call(ccb_t* ccb, ccb_data_type_t* type, char* name, ccb_list_t* args, ccb_list_t* paramtypes, int callconv); ccb_ast_t* ccb_ast_ptrcall(ccb_t* ccb, ccb_data_type_t* type, char* name, ccb_ast_t* callable, ccb_list_t* args, ccb_list_t* paramtypes, int callconv); ccb_ast_t* ccb_ast_function(ccb_t* ccb, ccb_data_type_t* type, char* name, ccb_list_t* params, ccb_ast_t* body, ccb_list_t* locals); ccb_ast_t* ccb_ast_initializer(ccb_t* ccb, ccb_ast_t*, ccb_data_type_t*, int); ccb_ast_t* ccb_ast_if(ccb_t* ccb, ccb_ast_t* cond, ccb_ast_t* then, ccb_ast_t* last); ccb_ast_t* ccb_ast_for(ccb_t* ccb, ccb_ast_t* init, ccb_ast_t* cond, ccb_ast_t* step, ccb_ast_t* body); ccb_ast_t* ccb_ast_while(ccb_t* ccb, ccb_ast_t* cond, ccb_ast_t* body); ccb_ast_t* ccb_ast_do(ccb_t* ccb, ccb_ast_t* cond, ccb_ast_t* body); ccb_ast_t* ccb_ast_return(ccb_t* ccb, int callconv, ccb_data_type_t* returntype, ccb_ast_t* val); ccb_ast_t* ccb_ast_compound(ccb_t* ccb, ccb_list_t* statements); ccb_ast_t* ccb_ast_ternary(ccb_t* ccb, ccb_data_type_t* type, ccb_ast_t* cond, ccb_ast_t* then, ccb_ast_t* last); ccb_ast_t* ccb_ast_switch(ccb_t* ccb, ccb_ast_t* expr, ccb_ast_t* body); ccb_ast_t* ccb_ast_case(ccb_t* ccb, int value); ccb_ast_t* ccb_ast_goto(ccb_t* ccb, char*); ccb_ast_t* ccb_ast_asm(ccb_t* ccb, ccb_list_t*); ccb_ast_t* ccb_ast_make(ccb_t* ccb, int type); ccb_data_type_t* ccb_ast_prototype(ccb_t* ccb, ccb_data_type_t* returntype, ccb_list_t* paramtypes, bool dots); ccb_data_type_t* ccb_ast_pointer(ccb_t* ccb, ccb_data_type_t* type); ccb_data_type_t* ccb_ast_array(ccb_t* ccb, ccb_data_type_t* type, int size); ccb_data_type_t* ccb_ast_array_convert(ccb_t* ccb, ccb_data_type_t* ast); ccb_data_type_t* ccb_ast_result_type(ccb_t* ccb, int op, ccb_data_type_t* a, ccb_data_type_t* b); const char* ccb_ast_type_string(ccb_t* ccb, ccb_data_type_t* type); bool ccb_ast_type_integer(ccb_t* ccb, ccb_data_type_t* type); bool ccb_ast_type_floating(ccb_t* ccb, ccb_data_type_t* type); ccb_data_type_t* ccb_ast_type_copy(ccb_t* ccb, ccb_data_type_t* type); ccb_data_type_t* ccb_ast_type_copy_incomplete(ccb_t* ccb, ccb_data_type_t* type); ccb_data_type_t* ccb_ast_type_create(ccb_t* ccb, ccb_type_t type, bool sign); ccb_data_type_t* ccb_ast_type_stub(ccb_t* ccb); char* ccb_ast_string(ccb_t* ccb, ccb_ast_t* ast); /* NOTE: The ccb_target interface will probably be reworked eventually to allow multiple targets * to be managed easier. This will probably involve simplifying or generalising some features * and having the code generators operate via callback functions. Right now all targets are * implemented in the same backend, which has various options. */ void ccb_target_init(ccb_t* ccb); // Mostly/fully implemented but only partly tested, 64-bit mode only #define CCB_ARCH_FAMILY_X86 1 // Not implemented yet #define CCB_ARCH_FAMILY_ARM 2 // Partly implemented #define CCB_ARCH_FAMILY_RISCV 3 // Mostly implemented but mostly untested, just generates a virtual instruction set #define CCB_ARCH_FAMILY_GENERIC 4 // Partly implemented, aimed at working with an earlier version of my CPU core #define CCB_ARCH_FAMILY_GEN1 5 int ccb_target_family(ccb_t* ccb); size_t ccb_target_type_size_char(ccb_t* ccb); size_t ccb_target_type_size_short(ccb_t* ccb); size_t ccb_target_type_size_int(ccb_t* ccb); size_t ccb_target_type_size_long(ccb_t* ccb); size_t ccb_target_type_size_llong(ccb_t* ccb); size_t ccb_target_type_size_float(ccb_t* ccb); size_t ccb_target_type_size_double(ccb_t* ccb); size_t ccb_target_type_size_ldouble(ccb_t* ccb); size_t ccb_target_type_size_pointer(ccb_t* ccb); /* * Function: ccb_target_alignment() * The default alignment of structure elements (padding) for the given * architecture / ABI */ size_t ccb_target_alignment(ccb_t* ccb); /* * Function: ccb_target_callregisters() * The maximum number of registers to place a direct or indirect * function call for the given architecture / ABI, after which stack * space will be used. */ int ccb_target_callregisters(ccb_t* ccb); /* * Function: ccb_target_callregister() * The name of the register used to store the direct argument at the given index (which must be >= 0 and < ccb_target_callregisters()). */ const char* ccb_target_callregister(ccb_t* ccb, int idx); const char* ccb_target_r0(ccb_t* ccb); const char* ccb_target_r1(ccb_t* ccb); const char* ccb_target_sp(ccb_t* ccb); const char* ccb_target_bp(ccb_t* ccb); #define CCB_TARGET_ASMFMT_GAS 1 #define CCB_TARGET_ASMFMT_FASM 2 #define CCB_TARGET_ASMFMT_NASM 3 #define CCB_TARGET_ASMFMT_RAW 4 int ccb_target_asmfmt(ccb_t* ccb); #define CCB_TARGET_BINFMT_ELF 1 #define CCB_TARGET_BINFMT_FLAT 2 int ccb_target_binfmt(ccb_t* ccb); #define CCB_TARGET_CALLCONV_STANDARD 1 #define CCB_TARGET_CALLCONV_WINDOWS 2 int ccb_target_callconv(ccb_t* ccb); /* * Function: ccb_compile_error * Write compiler error diagnostic to stderr, formatted * * Parameters: * fmt - Standard format specification string * ... - Additional variable arguments * * Remarks: * This function does not return, it kills execution via * exit(1); */ void ccb_compile_error_impl(ccb_t* ccb);//, const char* fmt, ...); #define ccb_compile_error(ccb,...) \ do{fprintf(stderr, "ERROR: "); fprintf(stderr,__VA_ARGS__);fprintf(stderr,"\n");ccb_compile_error_impl(ccb);}while(0); /* * Function: ccb_compile_warn * Same as ccb_compile_error but doesn't exit */ void ccb_compile_warn_impl(ccb_t* ccb);//, const char* fmt, ...); #define ccb_compile_warn(ccb,...) \ do{fprintf(stderr, "WARNING: "); fprintf(stderr,__VA_ARGS__);fprintf(stderr,"\n");ccb_compile_warn_impl(ccb);}while(0); /* TODO: eliminate */ ccb_list_t* ccb_parse_run(ccb_t* ccb); int ccb_parse_evaluate(ccb_t* ccb, ccb_ast_t* ast); void ccb_target_gen_data_section(ccb_t* ccb); void ccb_target_gen_function(ccb_t* ccb, ccb_ast_t* function); #ifdef CCB_IMPLEMENTATION #include #include #include #include #include #include #include #define CCB_MEMORY (1024ULL*1024*1024*2) /*0x800000*/ static unsigned char* ccb_memory_pool = NULL; static size_t ccb_memory_next; static void ccb_memory_cleanup(void) { free(ccb_memory_pool); } void* ccb_memory_allocate(size_t bytes) { void* value; //fprintf(stderr, "ccb_memory_allocate: %ld\n", bytes); while ((bytes % 16) != 0) { bytes++; } if (!ccb_memory_pool) { //fprintf(stderr, "ccb_memory_allocate: B %ld\n", CCB_MEMORY); ccb_memory_pool = calloc(CCB_MEMORY,1); ccb_memory_next = 0; //fprintf(stderr, "ccb_memory_create: C %lx\n", ccb_memory_pool); //TODO: atexit(ccb_memory_cleanup); } if (ccb_memory_next + bytes >= CCB_MEMORY) { fprintf(stderr, "FATAL: Out of memory, need to increase compiler reserved memory size!\n"); ((char*)NULL)[0] = 0; // Trigger debugger exit(-1); } /* TODO: This doesn't self-compile properly yet (it's accepted but results in zero/error) value = &(ccb_memory_pool[ccb_memory_next]); */ value = ccb_memory_pool + ccb_memory_next; ccb_memory_next += bytes; //fprintf(stderr, "ccb_memory_allocate: %lx\n", value); return value; } void ccb_util_init(ccb_t* ccb) { } struct ccb_string_s { char* buffer; int allocated; int length; }; static void ccb_string_reallocate(ccb_string_t* string) { int size = string->allocated * 2; char* buffer = ccb_memory_allocate(size); strcpy(buffer, string->buffer); string->buffer = buffer; string->allocated = size; } void ccb_string_catcstr(ccb_string_t* string, const char* str) { size_t i = 0; while (str[i] != 0) { ccb_string_cat(string, str[i]); i++; } } /* ** LTOA.C ** ** Converts a long integer to a string. ** ** Copyright 1988-90 by Robert B. Stout dba MicroFirm ** ** Released to public domain, 1991 ** ** Parameters: 1 - number to be converted ** 2 - buffer in which to build the converted string ** 3 - number base to use for conversion ** ** Returns: A character pointer to the converted string if ** successful, a NULL pointer if the number base specified ** is out of range. */ #include #include #define BUFSIZE (sizeof(long) * 8 + 1) char *ltoa(long N, char *str, int base) { /* register */ int i = 2; long uarg; char *tail; char *head = str; char buf[BUFSIZE]; if (36 < base || 2 > base) base = 10; /* can only use 0-9, A-Z */ tail = buf + (BUFSIZE - 1); /* last character position */ *tail-- = '\0'; if (10 == base && N < 0L) { *head++ = '-'; uarg = -N; } else uarg = N; if (uarg) { for (i = 1; uarg; ++i) { ///*register*/ ldiv_t r; int rrem = uarg % base;//ldiv(uarg, base); *tail-- = (char)(rrem + ((9L < rrem) ? ('A' - 10L) : '0')); uarg = uarg / base; } } else *tail-- = '0'; memcpy(head, ++tail, i); return str; } void ccb_string_catint(ccb_string_t* string, long long i) { const char* x = calloc(100, 1); ccb_string_catcstr(string, ltoa(i, x, 10)); free(x); } void #ifdef _ZCC __classic_call #endif ccb_string_catf(ccb_string_t* string, const char* fmt, ...) { //fprintf(stderr, "Catting f '%s'...\n", fmt); va_list va; for (;;) { int left = string->allocated - string->length; int write; va_start(va, fmt); write = vsnprintf((string->buffer) + (string->length), left, fmt, va); va_end(va); if (left <= write) { ccb_string_reallocate(string); continue; } string->length += write; //fprintf(stderr, "Done catting f...\n"); return; } } ccb_string_t* ccb_string_create(void) { ccb_string_t* string = ccb_memory_allocate(sizeof(ccb_string_t)); string->buffer = ccb_memory_allocate(8); string->allocated = 8; string->length = 0; string->buffer[0] = '\0'; return string; } char* ccb_string_buffer(ccb_string_t* string) { return string->buffer; } void ccb_string_cat(ccb_string_t* string, char ch) { if (string->allocated == (string->length + 1)) ccb_string_reallocate(string); string->buffer[string->length++] = ch; string->buffer[string->length] = '\0'; } char* ccb_string_quote(char* p) { //printf("Trying to handle quotes of '%s'\n", p); ccb_string_t* string = ccb_string_create(); while (*p) { if (*p == '\"' || *p == '\\') { //ccb_string_catf(string, "\\%c", *p); ccb_string_cat(string, '\\'); ccb_string_cat(string, *p); } else if (*p == '\n') { //ccb_string_catf(string, "\\n"); ccb_string_catcstr(string, "\\n"); } else { ccb_string_cat(string, *p); } p++; } return string->buffer; } char* ccb_string_quote_fasm(char* p, char quotechar) { ccb_string_t* string = ccb_string_create(); while (*p) { if (*p == '\"' || *p == '\'' || *p == '\\' || *p == '\n') { //ccb_string_catf(string, "%c, %d, %c", quotechar, (int)*p, quotechar); ccb_string_cat(string, quotechar); ccb_string_catcstr(string, ", "); ccb_string_catint(string, (int)*p); ccb_string_catcstr(string, ", "); ccb_string_cat(string, quotechar); } else { ccb_string_cat(string, *p); } p++; } return string->buffer; } struct ccb_list_node_s { void* element; ccb_list_node_t* next; ccb_list_node_t* prev; }; struct ccb_list_iterator_s { ccb_list_node_t* pointer; }; ccb_list_t* ccb_list_create(void) { ccb_list_t* list = ccb_memory_allocate(sizeof(ccb_list_t)); list->length = 0; list->head = NULL; list->tail = NULL; return list; } void* ccb_list_node_create(void* element) { ccb_list_node_t* node = ccb_memory_allocate(sizeof(ccb_list_node_t)); node->element = element; node->next = NULL; node->prev = NULL; return node; } void ccb_list_push(ccb_list_t* list, void* element) { ccb_list_node_t* node = ccb_list_node_create(element); if (!list->head) list->head = node; else { list->tail->next = node; node->prev = list->tail; } list->tail = node; list->length++; } void* ccb_list_pop(ccb_list_t* list) { if (!list->head) return NULL; void* element = list->tail->element; list->tail = list->tail->prev; if (list->tail) list->tail->next = NULL; else list->head = NULL; list->length--; return element; } void* ccb_list_shift(ccb_list_t* list) { if (!list->head) return NULL; void* element = list->head->element; list->head = list->head->next; if (list->head) list->head->prev = NULL; else list->tail = NULL; list->length--; return element; } int ccb_list_length(ccb_list_t* list) { return list->length; } ccb_list_iterator_t* ccb_list_iterator(ccb_list_t* list) { ccb_list_iterator_t* iter = ccb_memory_allocate(sizeof(ccb_list_iterator_t)); if (iter == NULL) { fprintf(stderr, "ICE: %s (out of memory?)", __func__); exit(-1); } if (list == NULL) { // TODO: Added by Zak but not sure if a call with NULL indicates an error? iter->pointer = NULL; } else { iter->pointer = list->head; } return iter; } void* ccb_list_iterator_next(ccb_list_iterator_t* iter) { void* ret; if (!iter->pointer) return NULL; ret = iter->pointer->element; iter->pointer = iter->pointer->next; return ret; } bool ccb_list_iterator_end(ccb_list_iterator_t* iter) { return !iter->pointer; } static void ccb_list_shiftify(ccb_list_t* list, void* element) { ccb_list_node_t* node = ccb_list_node_create(element); node->next = list->head; if (list->head) list->head->prev = node; list->head = node; if (!list->tail) list->tail = node; list->length++; } ccb_list_t* ccb_list_reverse(ccb_list_t* list) { ccb_list_t* ret = ccb_list_create(); for (ccb_list_iterator_t* it = ccb_list_iterator(list); !ccb_list_iterator_end(it); ) ccb_list_shiftify(ret, ccb_list_iterator_next(it)); return ret; } void* ccb_list_tail(ccb_list_t* list) { if (!list->head) return NULL; ccb_list_node_t* node = list->head; while (node->next) node = node->next; return node->element; } typedef struct { char* key; void* value; } ccb_table_entry_t; void* ccb_table_create(void* parent) { ccb_table_t* table = ccb_memory_allocate(sizeof(ccb_table_t)); table->list = ccb_list_create(); table->parent = parent; return table; } ccb_table_t* ccb_table_empty() { return ccb_table_create(NULL); } void* ccb_table_find(ccb_table_t* table, const char* key) { for (; table; table = table->parent) { for (ccb_list_iterator_t* it = ccb_list_iterator(table->list); !ccb_list_iterator_end(it); ) { ccb_table_entry_t* entry = ccb_list_iterator_next(it); if (!strcmp(key, entry->key)) return entry->value; } } return NULL; } void ccb_table_insert(ccb_table_t* table, char* key, void* value) { ccb_table_entry_t* entry = ccb_memory_allocate(sizeof(ccb_table_entry_t)); entry->key = key; entry->value = value; ccb_list_push(table->list, entry); } void* ccb_table_parent(ccb_table_t* table) { return table->parent; } ccb_list_t* ccb_table_values(ccb_table_t* table) { ccb_list_t* list = ccb_list_create(); for (; table; table = table->parent) for (ccb_list_iterator_t* it = ccb_list_iterator(table->list); !ccb_list_iterator_end(it); ) ccb_list_push(list, ((ccb_table_entry_t*)ccb_list_iterator_next(it))->value); return list; } ccb_list_t* ccb_table_keys(ccb_table_t* table) { ccb_list_t* list = ccb_list_create(); for (; table; table = table->parent) for (ccb_list_iterator_t* it = ccb_list_iterator(table->list); !ccb_list_iterator_end(it); ) ccb_list_push(list, ((ccb_table_entry_t*)ccb_list_iterator_next(it))->key); return list; } int ccb_strcasecmp(const char* s1, const char* s2) { const unsigned char* u1 = (const unsigned char*)s1; const unsigned char* u2 = (const unsigned char*)s2; while (tolower(*u1) == tolower(*u2++)) if (*u1++ == '\0') return 0; return tolower(*u1) - tolower(*--u2); } int ccb_strncasecmp(const char* s1, const char* s2, size_t n) { const unsigned char* u1 = (const unsigned char*)s1; const unsigned char* u2 = (const unsigned char*)s2; if (!n) return 0; do { if (tolower(*u1) != tolower(*u2++)) return tolower(*u1) - tolower(*--u2); if (*u1++ == '\0') break; } while (--n != 0); return 0; } static int ccb_input_getc(ccb_t* ccb) { int result = getc(ccb->input); switch (result) { case '\n': ccb->pos.line++; ccb->pos.uline++; //fprintf(stderr, "@line %d\n", ccb->line); ccb->icol = ccb->pos.ucol; ccb->pos.col = 1; ccb->pos.ucol = 1; break; default: ccb->pos.col++; ccb->pos.ucol++; } return result; } static void ccb_input_ungetc(ccb_t* ccb, int charcode) { switch (charcode) { case '\n': ccb->pos.line--; ccb->pos.uline--; ccb->pos.col = ccb->icol; ccb->pos.ucol = ccb->icol; // TODO... break; default: ccb->pos.col--; // XXX: I should've tested, this seemed to be going in the wrong direction! ccb->pos.ucol--; // XXX: I should've tested, this seemed to be going in the wrong direction! } ungetc(charcode, ccb->input); } static ccb_list_t* ccb_lexer_buffer = NULL; ///*&CCB_SENTINEL_LIST*/ccb_list_create(); static ccb_lexer_token_t* ccb_lexer_token_copy(ccb_t* ccb, ccb_lexer_token_t* token) { return memcpy(calloc(1,sizeof(ccb_lexer_token_t)), token, sizeof(ccb_lexer_token_t)); } static ccb_lexer_token_t* ccb_lexer_identifier(ccb_t* ccb, ccb_string_t* str) { ccb_lexer_token_t result = {}; result.type = CCB_LEXER_TOKEN_IDENTIFIER; result.string = ccb_string_buffer(str); return ccb_lexer_token_copy(ccb, &result); /*return ccb_lexer_token_copy(ccb, &(ccb_lexer_token_t){ .type = CCB_LEXER_TOKEN_IDENTIFIER, .string = ccb_string_buffer(str) });*/ } static ccb_lexer_token_t* ccb_lexer_strtok(ccb_t* ccb, ccb_string_t* str) { ccb_lexer_token_t result= {}; result.type = CCB_LEXER_TOKEN_STRING; result.string = ccb_string_buffer(str); return ccb_lexer_token_copy(ccb, &result); /*return ccb_lexer_token_copy(ccb, &(ccb_lexer_token_t){ .type = CCB_LEXER_TOKEN_STRING, .string = ccb_string_buffer(str) });*/ } static ccb_lexer_token_t* ccb_lexer_punct(ccb_t* ccb, int punct) { ccb_lexer_token_t tmp = {}; tmp.type = CCB_LEXER_TOKEN_PUNCT; tmp.punct = punct; return ccb_lexer_token_copy(ccb, &tmp); } static ccb_lexer_token_t* ccb_lexer_number(ccb_t* ccb, char* string) { ccb_lexer_token_t tmp = {}; tmp.type = CCB_LEXER_TOKEN_NUMBER; tmp.string = string; return ccb_lexer_token_copy(ccb, &tmp); } static ccb_lexer_token_t* ccb_lexer_char(ccb_t* ccb, char value) { ccb_lexer_token_t tmp = {}; tmp.type = CCB_LEXER_TOKEN_CHAR; tmp.character = value; return ccb_lexer_token_copy(ccb,&tmp); /*return ccb_lexer_token_copy(ccb, &(ccb_lexer_token_t){ .type = CCB_LEXER_TOKEN_CHAR, .character = value });*/ } static void ccb_lexer_skip_comment_line(ccb_t* ccb) { for (;;) { int c = ccb_input_getc(ccb); //fputc(c, stderr); if (c == '\n' || c == EOF) return; } } static char* ccb_lexer_consume_line(ccb_t* ccb, int* mode) { char* buffer = calloc(100,1); // TODO: Use compiler MM if (mode != NULL) *mode = 0; int n = 0; int onlywhitespace = 1; for (;;) { int c = ccb_input_getc(ccb); //fprintf(stderr, "Got char '%c' ows=%d mdptr=%p\n", c, onlywhitespace, mode); if (c == '\n' || c == EOF) { goto done; } else if (onlywhitespace && mode != NULL && c == '{') { //fprintf(stderr, "Setting mode -1\n"); *mode = 1; onlywhitespace = 0; continue; } else if (mode != NULL && c == '}') { if (*mode == 1) { *mode = 0; // single-line with braces treated as single line } else { *mode = -1; // Signal last line of section } goto done; } if (!isspace(c) && c != '\r') { onlywhitespace = 0; } buffer[n] = (char) c; n++; if (n >= 99) { ccb_compile_error(ccb, "Assembler line too long (allows at most 99 chars)"); } } done: if (onlywhitespace) { free(buffer); return ""; } // TODO: strdup and free from a large buffer? return buffer; } static void ccb_lexer_skip_comment_block(ccb_t* ccb) { enum { comment_outside, comment_astrick } state = comment_outside; for (;;) { int c = ccb_input_getc(ccb); if (c == '*') state = comment_astrick; else if (state == comment_astrick && c == '/') return; else state = comment_outside; } } static int ccb_lexer_skip(ccb_t* ccb) { int c; while ((c = ccb_input_getc(ccb)) != EOF) { if (isspace(c) || c == '\n' || c == '\r') { //fprintf(stderr, "Yo got space/newline\n"); continue; } //fprintf(stderr, "Not a space/newline: '%c' 0x%x", c, c); ccb_input_ungetc(ccb, c); return c; } return EOF; } static ccb_lexer_token_t* ccb_lexer_read_number(ccb_t* ccb, int c) { ccb_string_t* string = ccb_string_create(); ccb_string_cat(string, c); for (;;) { int p = ccb_input_getc(ccb); if (!isdigit(p) && !isalpha(p) && p != '.') { ccb_input_ungetc(ccb, p); return ccb_lexer_number(ccb, ccb_string_buffer(string)); } ccb_string_cat(string, p); } return NULL; } static bool ccb_lexer_read_character_octal_brace(ccb_t* ccb, int c, int* r) { if ('0' <= c && c <= '7') { *r = (*r << 3) | (c - '0'); return true; } return false; } static int ccb_lexer_read_character_octal(ccb_t* ccb, int c) { int r = c - '0'; if (ccb_lexer_read_character_octal_brace(ccb, (c = ccb_input_getc(ccb)), &r)) { if (!ccb_lexer_read_character_octal_brace(ccb, (c = ccb_input_getc(ccb)), &r)) ccb_input_ungetc(ccb, c); } else ccb_input_ungetc(ccb, c); return r; } static int ccb_lexer_read_character_hexadecimal(ccb_t* ccb) { int c = ccb_input_getc(ccb); int r = 0; if (!isxdigit(c)) ccb_compile_error(ccb, "malformatted hexadecimal character"); for (;; c = ccb_input_getc(ccb)) { switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': r = (r << 4) | (c - '0'); continue; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': r = (r << 4) | (c - 'a' + 10); continue; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': r = (r << 4) | (c - 'A' + 10); continue;// Fix by Zak - was minusing 'f'!? default: ccb_input_ungetc(ccb, c); return r; } } return -1; } static int ccb_lexer_read_character_escaped(ccb_t* ccb) { int c = ccb_input_getc(ccb); switch (c) { case '\'': return '\''; case '"': return '"'; case '?': return '?'; case '\\': return '\\'; case 'a': return '\a'; case 'b': return '\b'; case 'f': return '\f'; case 'n': return '\n'; case 'r': return '\r'; case 't': return '\t'; case 'v': return '\v'; case 'e': return '\033'; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': return ccb_lexer_read_character_octal(ccb, c); case 'x': return ccb_lexer_read_character_hexadecimal(ccb); case EOF: ccb_compile_error(ccb, "malformatted escape sequence"); default: return c; } } static ccb_lexer_token_t* ccb_lexer_read_character(ccb_t* ccb) { int c = ccb_input_getc(ccb); int r = (c == '\\') ? ccb_lexer_read_character_escaped(ccb) : c; if (ccb_input_getc(ccb) != '\'') ccb_compile_error(ccb, "unterminated character"); return ccb_lexer_char(ccb, (char)r); } static ccb_lexer_token_t* ccb_lexer_read_string(ccb_t* ccb, int allowcontinued) { ccb_string_t* string = ccb_string_create(); for (;;) { int c = ccb_input_getc(ccb); if (c == EOF) ccb_compile_error(ccb, "Expected termination for string literal"); if (c == '"') { /* This used to break the loop as soon as a '"' is found, but now it checks in * case it's followed by another string part. In that case the processing just * continues as though there was no break inbetween the strings. * * The new behaviour still has to be disabled in some cases, like if we're reading * a string on a #pragma or line-number line, in which case we don't want to go * skipping whitespace at the end or it'll read past the intended line and cause * havoc! */ if (!allowcontinued) { break; } int d = ccb_lexer_skip(ccb); //int d = ccb_input_getc(ccb); if (d != '"') { /* If it isn't followed by a string, then we unget the other token's character * we just read and break the loop knowing our string is completely ended! * NOTE: We don't actually have to getc/ungetc since the skip function does it for us. */ //ccb_input_ungetc(ccb, d); break; } else { /* If the skip function did find a '"' then we read/discard it and continue the loop, * WITHOUT processing the '"' character as part of the string contents! */ ccb_input_getc(ccb); continue; } } if (c == '\\') c = ccb_lexer_read_character_escaped(ccb); ccb_string_cat(string, c); } return ccb_lexer_strtok(ccb, string); } static ccb_lexer_token_t* ccb_lexer_read_identifier(ccb_t* ccb, int c1) { ccb_string_t* string = ccb_string_create(); ccb_string_cat(string, (char)c1); for (;;) { int c2 = ccb_input_getc(ccb); if (isalnum(c2) || c2 == '_' || c2 == '$') { ccb_string_cat(string, c2); } else { ccb_input_ungetc(ccb, c2); return ccb_lexer_identifier(ccb, string); } } return NULL; } static ccb_lexer_token_t* ccb_lexer_read_reclassify_one(ccb_t* ccb, int expect1, int a, int e) { int c = ccb_input_getc(ccb); if (c == expect1) return ccb_lexer_punct(ccb, a); ccb_input_ungetc(ccb, c); return ccb_lexer_punct(ccb, e); } static ccb_lexer_token_t* ccb_lexer_read_reclassify_two(ccb_t* ccb, int expect1, int a, int expect2, int b, int e) { int c = ccb_input_getc(ccb); if (c == expect1) return ccb_lexer_punct(ccb, a); if (c == expect2) return ccb_lexer_punct(ccb, b); ccb_input_ungetc(ccb, c); return ccb_lexer_punct(ccb, e); } static ccb_lexer_token_t* ccb_lexer_read_token(ccb_t* ccb); // TODO: Predeclaration required for self-compile static ccb_lexer_token_t* ccb_lexer_read_token_impl(ccb_t* ccb) { int c; if (ccb_lexer_skip(ccb) == EOF) { //printf("ccb_lexer_read_token_impl: Got EOF\n"); return NULL; } c = ccb_input_getc(ccb); //printf("ccb_lexer_read_token_impl: Got '%c'\n", c); switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return ccb_lexer_read_number(ccb, c); case '"': return ccb_lexer_read_string(ccb, 1); case '\'': return ccb_lexer_read_character(ccb); case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': /*not L*/case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case '$': case '_': return ccb_lexer_read_identifier(ccb, c); case 'L': switch ((c = ccb_input_getc(ccb))) { case '"': return ccb_lexer_read_string(ccb, 1); case '\'': return ccb_lexer_read_character(ccb); } ccb_input_ungetc(ccb, c); return ccb_lexer_read_identifier(ccb, 'L'); case '#': //fprintf(stderr, "WARNING: Skipping #-line (at input %d)\n", ccb->line); // TODO: Track line numbers reported by preprocessor c = ccb_input_getc(ccb); if (c == ' ') { c = ccb_input_getc(ccb); if (c >= '0' && c <= '9') { ccb_lexer_token_t* n = ccb_lexer_read_number(ccb, c); //fprintf(stderr, "REPORTING LINE %d\n", atoi(n->string)); ccb->pos.uline = atoi(n->string) - 1; // Apparently it's the number of the NEXT line c = ccb_input_getc(ccb); if (c == ' ') { c = ccb_input_getc(ccb); if (c == '"') { ccb_lexer_token_t* f = ccb_lexer_read_string(ccb, 0); //fprintf(stderr, "REPORTING FILE '%s'\n", f->string); ccb->pos.ufile = f->string; } else { ccb_input_ungetc(ccb, c); } } else { ccb_input_ungetc(ccb, c); } } else { ccb_input_ungetc(ccb, c); } } else { ccb_input_ungetc(ccb, c); } ccb_lexer_skip_comment_line(ccb); //ccb->ucol = 1; return ccb_lexer_read_token(ccb); case '/': switch ((c = ccb_input_getc(ccb))) { case '/': ccb_lexer_skip_comment_line(ccb); return ccb_lexer_read_token(ccb); case '*': ccb_lexer_skip_comment_block(ccb); return ccb_lexer_read_token(ccb); } if (c == '=') return ccb_lexer_punct(ccb, CCB_LEXER_TOKEN_COMPOUND_DIV); ccb_input_ungetc(ccb, c); return ccb_lexer_punct(ccb, '/'); case '(': case ')': case ',': case ';': case '[': case ']': case '{': case '}': case '?': case ':': case '~': #ifdef CCB_X_OBJC case '@': #endif return ccb_lexer_punct(ccb, c); case '+': return ccb_lexer_read_reclassify_two(ccb, '+', CCB_LEXER_TOKEN_INCREMENT, '=', CCB_LEXER_TOKEN_COMPOUND_ADD, '+'); case '&': return ccb_lexer_read_reclassify_two(ccb, '&', CCB_LEXER_TOKEN_AND, '=', CCB_LEXER_TOKEN_COMPOUND_AND, '&'); case '|': return ccb_lexer_read_reclassify_two(ccb, '|', CCB_LEXER_TOKEN_OR, '=', CCB_LEXER_TOKEN_COMPOUND_OR, '|'); case '*': return ccb_lexer_read_reclassify_one(ccb, '=', CCB_LEXER_TOKEN_COMPOUND_MUL, '*'); case '%': return ccb_lexer_read_reclassify_one(ccb, '=', CCB_LEXER_TOKEN_COMPOUND_MOD, '%'); case '=': return ccb_lexer_read_reclassify_one(ccb, '=', CCB_LEXER_TOKEN_EQUAL, '='); case '!': return ccb_lexer_read_reclassify_one(ccb, '=', CCB_LEXER_TOKEN_NEQUAL, '!'); case '^': return ccb_lexer_read_reclassify_one(ccb, '=', CCB_LEXER_TOKEN_COMPOUND_XOR, '^'); case '-': switch ((c = ccb_input_getc(ccb))) { case '-': return ccb_lexer_punct(ccb, CCB_LEXER_TOKEN_DECREMENT); case '>': return ccb_lexer_punct(ccb, CCB_LEXER_TOKEN_ARROW); case '=': return ccb_lexer_punct(ccb, CCB_LEXER_TOKEN_COMPOUND_SUB); default: break; } ccb_input_ungetc(ccb, c); return ccb_lexer_punct(ccb, '-'); case '<': if ((c = ccb_input_getc(ccb)) == '=') return ccb_lexer_punct(ccb, CCB_LEXER_TOKEN_LEQUAL); if (c == '<') return ccb_lexer_read_reclassify_one(ccb, '=', CCB_LEXER_TOKEN_COMPOUND_LSHIFT, CCB_LEXER_TOKEN_LSHIFT); ccb_input_ungetc(ccb, c); return ccb_lexer_punct(ccb, '<'); case '>': if ((c = ccb_input_getc(ccb)) == '=') return ccb_lexer_punct(ccb, CCB_LEXER_TOKEN_GEQUAL); if (c == '>') return ccb_lexer_read_reclassify_one(ccb, '=', CCB_LEXER_TOKEN_COMPOUND_RSHIFT, CCB_LEXER_TOKEN_RSHIFT); ccb_input_ungetc(ccb, c); return ccb_lexer_punct(ccb, '>'); case '.': c = ccb_input_getc(ccb); if (c == '.') { ccb_string_t* str = ccb_string_create(); //ccb_string_catf(str, "..%c", ccb_input_getc(ccb)); ccb_string_catcstr(str, ".."); ccb_string_cat(str, ccb_input_getc(ccb)); return ccb_lexer_identifier(ccb, str); } ccb_input_ungetc(ccb, c); return ccb_lexer_punct(ccb, '.'); case EOF: return NULL; default: ccb_compile_error(ccb, "Unexpected character: `%c` (0x%x)", c, c); } return NULL; } static ccb_lexer_token_t* ccb_lexer_read_token(ccb_t* ccb) { ccb_lexer_token_t* result = ccb_lexer_read_token_impl(ccb); //printf("Got token type %d\n", (result == NULL ? -1 : result->type)); return result; } bool ccb_lexer_ispunct(ccb_t* ccb, ccb_lexer_token_t* token, int c) { //printf("Checking '%c' against '%c'\n", c, token->punct); bool result = (token != NULL) && (token->type == CCB_LEXER_TOKEN_PUNCT) && (token->punct == c); //printf("Got %s\n", result ? "true" : "false"); return result; } void ccb_lexer_unget(ccb_t* ccb, ccb_lexer_token_t* token) { if (!token) return; ccb_list_push(ccb_lexer_buffer, token); } ccb_lexer_token_t* ccb_lexer_next(ccb_t* ccb) { if (ccb_list_length(ccb_lexer_buffer) > 0) return ccb_list_pop(ccb_lexer_buffer); return ccb_lexer_read_token(ccb); } ccb_lexer_token_t* ccb_lexer_peek(ccb_t* ccb) { ccb_lexer_token_t* token = ccb_lexer_next(ccb); ccb_lexer_unget(ccb, token); return token; } char* ccb_lexer_tokenstr(ccb_t* ccb, ccb_lexer_token_t* token) { ccb_string_t* string = ccb_string_create(); if (!token) return "(null)"; switch (token->type) { case CCB_LEXER_TOKEN_PUNCT: if (token->punct == CCB_LEXER_TOKEN_EQUAL) { ccb_string_catf(string, "=="); return ccb_string_buffer(string); } case CCB_LEXER_TOKEN_CHAR: ccb_string_cat(string, token->character); return ccb_string_buffer(string); case CCB_LEXER_TOKEN_NUMBER: //ccb_string_catf(string, "%d", token->integer); ccb_string_catint(string, token->integer); return ccb_string_buffer(string); case CCB_LEXER_TOKEN_STRING: ccb_string_catf(string, "\"%s\"", token->string); return ccb_string_buffer(string); case CCB_LEXER_TOKEN_IDENTIFIER: return token->string; default: break; } ccb_compile_error(ccb, "Internal error: unexpected token"); return NULL; } ccb_data_type_t* ccb_ast_data_table[CCB_AST_DATA_COUNT];// = { //&(ccb_data_type_t) { CCB_TYPE_VOID, 0, true }, /* void */ //&(ccb_data_type_t) { CCB_TYPE_LONG, -1, true }, /* long */ //&(ccb_data_type_t) { CCB_TYPE_LLONG, -1, true }, /* long long */ //&(ccb_data_type_t) { CCB_TYPE_INT, -1, true }, /* int */ //&(ccb_data_type_t) { CCB_TYPE_SHORT, -1, true }, /* short */ //&(ccb_data_type_t) { CCB_TYPE_CHAR, -1, true }, /* char */ //&(ccb_data_type_t) { CCB_TYPE_FLOAT, -1, true }, /* float */ //&(ccb_data_type_t) { CCB_TYPE_DOUBLE, -1, true }, /* double */ //&(ccb_data_type_t) { CCB_TYPE_LDOUBLE, -1, true }, /* long double */ //&(ccb_data_type_t) { CCB_TYPE_LONG, -1, false }, /* unsigned long */ //&(ccb_data_type_t) { CCB_TYPE_LLONG, -1, false }, /* unsigned long long */ //#ifdef CCB_X_OBJC // TODO: Fix or remove this? //& (ccb_data_type_t) { CCB_TYPE_ID, -1, false }, /* objc identifier */ //#endif //}; ccb_data_type_t* ccb_ast_data_function = NULL; ccb_list_t* ccb_ast_locals = NULL; ccb_list_t* ccb_ast_gotos = NULL; ccb_list_t* ccb_ast_floats = NULL; ///*&CCB_SENTINEL_LIST*/ccb_list_create(); ccb_list_t* ccb_ast_strings = NULL; ///*&CCB_SENTINEL_LIST*/ccb_list_create(); ccb_table_t* ccb_ast_labels = NULL; ccb_table_t* ccb_ast_globalenv = NULL; ///*&CCB_SENTINEL_TABLE*/ccb_table_empty(); ccb_table_t* ccb_ast_localenv = NULL; ///*&CCB_SENTINEL_TABLE*/ccb_table_empty(); ccb_table_t* ccb_ast_structures = NULL; ///*&CCB_SENTINEL_TABLE*/ccb_table_empty(); ccb_table_t* ccb_ast_unions = NULL;// /*&CCB_SENTINEL_TABLE*/ccb_table_empty(); ccb_table_t* ccb_parse_typedefs = NULL; ///*&CCB_SENTINEL_TABLE*/ccb_table_empty(); void ccb_ast_init_t_(ccb_t* ccb, int idx, int typ, int siz, bool sig) { ccb_ast_data_table[idx] = ccb_memory_allocate(sizeof(ccb_data_type_t)); ccb_ast_data_table[idx]->type = typ; ccb_ast_data_table[idx]->size = siz; ccb_ast_data_table[idx]->sign = sig; } void ccb_ast_init(ccb_t* ccb) { //printf("Doint AST init...\n"); ccb_ast_init_t_(ccb, CCB_AST_DATA_VOID, CCB_TYPE_VOID, 0, true ); /* void */ ccb_ast_init_t_(ccb, CCB_AST_DATA_LONG, CCB_TYPE_LONG, -1, true ); /* long */ ccb_ast_init_t_(ccb, CCB_AST_DATA_LLONG, CCB_TYPE_LLONG, -1, true ); /* long long */ ccb_ast_init_t_(ccb, CCB_AST_DATA_INT, CCB_TYPE_INT, -1, true ); /* int */ ccb_ast_init_t_(ccb, CCB_AST_DATA_SHORT, CCB_TYPE_SHORT, -1, true ); /* short */ ccb_ast_init_t_(ccb, CCB_AST_DATA_CHAR, CCB_TYPE_CHAR, -1, true ); /* char */ ccb_ast_init_t_(ccb, CCB_AST_DATA_FLOAT, CCB_TYPE_FLOAT, -1, true ); /* float */ ccb_ast_init_t_(ccb, CCB_AST_DATA_DOUBLE, CCB_TYPE_DOUBLE, -1, true ); /* double */ ccb_ast_init_t_(ccb, CCB_AST_DATA_LDOUBLE, CCB_TYPE_LDOUBLE, -1, true ); /* long double */ ccb_ast_init_t_(ccb, CCB_AST_DATA_UINT, CCB_TYPE_INT, -1, false ); /* unsigned int */ ccb_ast_init_t_(ccb, CCB_AST_DATA_ULONG, CCB_TYPE_LONG, -1, false ); /* unsigned long */ ccb_ast_init_t_(ccb, CCB_AST_DATA_ULLONG, CCB_TYPE_LLONG, -1, false ); /* unsigned long long */ //printf("Doint AST init...\n"); //#ifdef CCB_X_OBJC // TODO: Fix or remove this? //ccb_ast_init_t_(ccb, 11, CCB_TYPE_ID, -1, false ); /* objc identifier */ //ccb_ast_init_t_(ccb, 0, CCB_TYPE_VOID, 0, true ); /* void */ //ccb_ast_init_t_(ccb, 1, CCB_TYPE_LONG, -1, true ); /* long */ //ccb_ast_init_t_(ccb, 2, CCB_TYPE_LLONG, -1, true ); /* long long */ //ccb_ast_init_t_(ccb, 3, CCB_TYPE_INT, -1, true ); /* int */ //ccb_ast_init_t_(ccb, 4, CCB_TYPE_SHORT, -1, true ); /* short */ //ccb_ast_init_t_(ccb, 5, CCB_TYPE_CHAR, -1, true ); /* char */ //ccb_ast_init_t_(ccb, 6, CCB_TYPE_FLOAT, -1, true ); /* float */ //ccb_ast_init_t_(ccb, 7, CCB_TYPE_DOUBLE, -1, true ); /* double */ //ccb_ast_init_t_(ccb, 8, CCB_TYPE_LDOUBLE, -1, true ); /* long double */ //ccb_ast_init_t_(ccb, 9, CCB_TYPE_LONG, -1, false ); /* unsigned long */ //ccb_ast_init_t_(ccb, 10, CCB_TYPE_LLONG, -1, false ); /* unsigned long long */ //printf("Doint AST init...\n"); //#ifdef CCB_X_OBJC // TODO: Fix or remove this? //ccb_ast_init_t_(ccb, 11, CCB_TYPE_ID, -1, false ); /* objc identifier */ //#endif //printf("Doint AST init...\n"); ccb_lexer_buffer = /*&CCB_SENTINEL_LIST*/ccb_list_create(); ccb_ast_floats = /*&CCB_SENTINEL_LIST*/ccb_list_create(); ccb_ast_strings = /*&CCB_SENTINEL_LIST*/ccb_list_create(); ccb_ast_globalenv = /*&CCB_SENTINEL_TABLE*/ccb_table_empty(); ccb_ast_structures = /*&CCB_SENTINEL_TABLE*/ccb_table_empty(); ccb_ast_unions = /*&CCB_SENTINEL_TABLE*/ccb_table_empty(); ccb_parse_typedefs = /*&CCB_SENTINEL_TABLE*/ccb_table_empty(); ccb_ast_data_table[CCB_AST_DATA_VOID]->size = 0; ccb_ast_data_table[CCB_AST_DATA_CHAR]->size = ccb_target_type_size_char(ccb); ccb_ast_data_table[CCB_AST_DATA_SHORT]->size = ccb_target_type_size_short(ccb); ccb_ast_data_table[CCB_AST_DATA_INT]->size = ccb_target_type_size_int(ccb); ccb_ast_data_table[CCB_AST_DATA_LONG]->size = ccb_target_type_size_long(ccb); ccb_ast_data_table[CCB_AST_DATA_LLONG]->size = ccb_target_type_size_llong(ccb); ccb_ast_data_table[CCB_AST_DATA_UINT]->size = ccb_target_type_size_int(ccb); ccb_ast_data_table[CCB_AST_DATA_ULONG]->size = ccb_target_type_size_long(ccb); ccb_ast_data_table[CCB_AST_DATA_ULLONG]->size = ccb_target_type_size_llong(ccb); ccb_ast_data_table[CCB_AST_DATA_FLOAT]->size = ccb_target_type_size_float(ccb); ccb_ast_data_table[CCB_AST_DATA_DOUBLE]->size = ccb_target_type_size_double(ccb); ccb_ast_data_table[CCB_AST_DATA_LDOUBLE]->size = ccb_target_type_size_ldouble(ccb); //ccb_ast_data_table[CCB_AST_DATA_ID]->size = ccb_target_type_size_pointer(ccb); //printf("Doint AST init...\n"); ccb_ast_data_table[CCB_AST_DATA_ID] = ccb_ast_pointer(ccb, ccb_ast_data_table[CCB_TYPE_VOID]); //printf("Done?"); } static ccb_data_type_t* ccb_ast_result_type_impl(ccb_t* ccb, jmp_buf* jmpbuf, char op, ccb_data_type_t* a, ccb_data_type_t* b); // TODO: Predeclaration required for self-compile static ccb_data_type_t* ccb_ast_result_type_impl(ccb_t* ccb, jmp_buf* jmpbuf, char op, ccb_data_type_t* a, ccb_data_type_t* b) { if (a->type > b->type) { ccb_data_type_t* t = a; a = b; b = t; } //printf("Getting result type of %d and %d...\n", a->type, b->type); if (b->type == CCB_TYPE_POINTER) { if (op == '=') return a; if (op != '+' && op != '-') goto error; if (a->type == CCB_TYPE_POINTER) { return ccb_ast_data_table[CCB_AST_DATA_LONG]; } if (!ccb_ast_type_integer(ccb, a)) goto error; return b; } switch (a->type) { case CCB_TYPE_VOID: goto error; case CCB_TYPE_CHAR: case CCB_TYPE_SHORT: case CCB_TYPE_INT: switch (b->type) { case CCB_TYPE_CHAR: case CCB_TYPE_SHORT: case CCB_TYPE_INT: return ccb_ast_data_table[CCB_AST_DATA_INT]; case CCB_TYPE_LONG: case CCB_TYPE_LLONG: return ccb_ast_data_table[CCB_AST_DATA_LONG]; case CCB_TYPE_FLOAT: case CCB_TYPE_DOUBLE: case CCB_TYPE_LDOUBLE: return ccb_ast_data_table[CCB_AST_DATA_DOUBLE]; case CCB_TYPE_ARRAY: case CCB_TYPE_POINTER: case CCB_TYPE_FUNCTION: return b; default: break; } ccb_compile_error(ccb, "Internal error: ast_result_type %d", b->type); case CCB_TYPE_LONG: case CCB_TYPE_LLONG: switch (b->type) { case CCB_TYPE_LONG: case CCB_TYPE_LLONG: return ccb_ast_data_table[CCB_AST_DATA_LONG]; case CCB_TYPE_FLOAT: case CCB_TYPE_DOUBLE: case CCB_TYPE_LDOUBLE: return ccb_ast_data_table[CCB_AST_DATA_DOUBLE]; case CCB_TYPE_ARRAY: case CCB_TYPE_POINTER: return b; default: break; } ccb_compile_error(ccb, "Internal error: ast_result_type (3)"); case CCB_TYPE_FLOAT: if (b->type == CCB_TYPE_FLOAT || b->type == CCB_TYPE_DOUBLE || b->type == CCB_TYPE_LDOUBLE) return ccb_ast_data_table[CCB_AST_DATA_DOUBLE]; goto error; case CCB_TYPE_DOUBLE: case CCB_TYPE_LDOUBLE: if (b->type == CCB_TYPE_DOUBLE || b->type == CCB_TYPE_LDOUBLE) return ccb_ast_data_table[CCB_AST_DATA_DOUBLE]; goto error; //case CCB_TYPE_POINTER: case CCB_TYPE_ARRAY: if (b->type != CCB_TYPE_ARRAY) goto error; return ccb_ast_result_type_impl(ccb, jmpbuf, op, a->pointer, b->pointer); case CCB_TYPE_STRUCTURE: if (b->type != CCB_TYPE_STRUCTURE || b->size != a->size) { goto error; } return a; default: ccb_compile_error(ccb, "ICE ast_result_type_impl %d", a->type); } error: longjmp(*jmpbuf, 1); return NULL; } ccb_data_type_t* ccb_ast_result_type(ccb_t* ccb, int op, ccb_data_type_t* a, ccb_data_type_t* b) { switch (op) { case '!': case '~': case '<': case '>': case '&': case '%': case CCB_AST_TYPE_EQUAL: case CCB_AST_TYPE_GEQUAL: case CCB_AST_TYPE_LEQUAL: case CCB_AST_TYPE_NEQUAL: case CCB_AST_TYPE_AND: case CCB_AST_TYPE_OR: return ccb_ast_data_table[CCB_AST_DATA_INT]; case CCB_AST_TYPE_LSHIFT: case CCB_AST_TYPE_RSHIFT: return a; } jmp_buf jmpbuf; if (setjmp(jmpbuf) == 0) { return ccb_ast_result_type_impl(ccb, &jmpbuf, op, ccb_ast_array_convert(ccb, a), ccb_ast_array_convert(ccb, b) ); } ccb_compile_error(ccb, "incompatible operands `%s' and `%s' in `%c` operation", &(ccb_ast_type_string(ccb, a)[0]), &(ccb_ast_type_string(ccb, b)[0]), op ); return NULL; } ccb_ast_t* ccb_ast_copy(ccb_t* ccb, ccb_ast_t* ast) { ccb_ast_t* copy = ccb_memory_allocate(sizeof(ccb_ast_t)); if (copy == NULL) { fprintf(stderr, "FATAL ERROR: Out of memory!\n"); exit(-1); } //*copy = *ast; memcpy(copy, ast, sizeof(ccb_ast_t)); return copy; } ccb_ast_t* ccb_ast_structure_reference(ccb_t* ccb, ccb_data_type_t* type, ccb_ast_t* structure, char* name) { ccb_ast_t tmp = {}; tmp.type = CCB_AST_TYPE_STRUCT; tmp.ctype = type; tmp.structure = structure; tmp.field = name; return ccb_ast_copy(ccb, &tmp); } ccb_ast_t* ccb_ast_new_unary(ccb_t* ccb, int type, ccb_data_type_t* data, ccb_ast_t* operand) { ccb_ast_t tmp = {}; tmp.type = type; tmp.ctype = data; tmp.unary.operand = operand; if (operand == NULL) { ((char*)NULL)[0] = 0; // Trigger debugger } return ccb_ast_copy(ccb, &tmp); /* TODO: This doesn't self-compile yet due to the nested ".unary.operand" return ccb_ast_copy(ccb, &(ccb_ast_t) { .type = type, .ctype = data, .unary.operand = operand }); */ } ccb_ast_t* ccb_ast_new_binary(ccb_t* ccb, int type, ccb_ast_t* left, ccb_ast_t* right) { ccb_ast_t tmp = {}; tmp.type = type; /* TODO? I'll disable this until I understand the intended "function pointer" syntax. if (right->type == CCB_AST_TYPE_FUNCTION) { // Special support for assigning a function pointer without the & right = ccb_ast_new_unary(ccb, CCB_AST_TYPE_ADDRESS, ccb_ast_pointer(ccb, right->ctype), right); }*/ tmp.ctype = ccb_ast_result_type(ccb, type, left->ctype, right->ctype); ccb_ast_t* ast = ccb_ast_copy(ccb, &tmp); if (type != '=' && ccb_ast_array_convert(ccb, left->ctype)->type != CCB_TYPE_POINTER && ccb_ast_array_convert(ccb, right->ctype)->type == CCB_TYPE_POINTER) { ast->left = right; ast->right = left; } else if ((type == '+' || type == '-') && ccb_ast_array_convert(ccb, left->ctype)->type == CCB_TYPE_POINTER && ccb_ast_array_convert(ccb, right->ctype)->type == CCB_TYPE_POINTER) { ast->left = left; ast->right = right; return ccb_ast_new_binary(ccb, '/', ast, ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_LONG], left->ctype->pointer->size)); } else { ast->left = left; ast->right = right; } return ast; } /* Conceptually like a binary operator, except a bit simpler. Note that comma expressions * have nothing to do with the commas used as separators elsewhere in the language. */ ccb_ast_t* ccb_ast_new_comma(ccb_t* ccb, int type, ccb_ast_t* left, ccb_ast_t* right) { ccb_ast_t tmp = {}; tmp.type = type; tmp.left = left; tmp.right = right; tmp.ctype = right->ctype; ccb_ast_t* ast = ccb_ast_copy(ccb, &tmp); return ast; } ccb_ast_t* ccb_ast_new_integer(ccb_t* ccb, ccb_data_type_t* type, long long int value) { ccb_ast_t tmp = {}; tmp.type = CCB_AST_TYPE_LITERAL; tmp.ctype = type; tmp.integer = value; return ccb_ast_copy(ccb, &tmp); } ccb_ast_t* ccb_ast_new_floating(ccb_t* ccb, ccb_data_type_t* type, double value) { ccb_ast_t tmp = {}; tmp.type = CCB_AST_TYPE_LITERAL; tmp.ctype = type; tmp.floating.value = value; ccb_ast_t* ast = ccb_ast_copy(ccb, &tmp); /* TODO: This doesn't compile yet due to nested .floating.value... ccb_ast_t* ast = ccb_ast_copy(ccb, &(ccb_ast_t){ .type = CCB_AST_TYPE_LITERAL, .ctype = type, .floating.value = value }); */ ccb_list_push(ccb_ast_floats, ast); return ast; } ccb_ast_t* ccb_ast_new_string(ccb_t* ccb, char* value) { ccb_ast_t tmp = {}; tmp.type = CCB_AST_TYPE_STRING; tmp.ctype = ccb_ast_array(ccb, ccb_ast_data_table[CCB_AST_DATA_CHAR], strlen(value) + 1); tmp.string.data = value; tmp.string.label = ccb_ast_label(ccb); return ccb_ast_copy(ccb, &tmp); /* TODO: This doesn't self-compile yet due to nested fields .string.data/.string.label return ccb_ast_copy(ccb, &(ccb_ast_t) { .type = CCB_AST_TYPE_STRING, .ctype = ccb_ast_array(ccb, ccb_ast_data_table[CCB_AST_DATA_CHAR], strlen(value) + 1), .string.data = value, .string.label = ccb_ast_label(ccb) }); */ } ccb_ast_t* ccb_ast_variable_local(ccb_t* ccb, ccb_data_type_t* type, char* name) { ccb_ast_t tmp = {}; tmp.type = CCB_AST_TYPE_VAR_LOCAL; tmp.ctype = type; tmp.variable.name = name; ccb_ast_t* ast = ccb_ast_copy(ccb, &tmp); /* TODO: This doesn't self-compile due to nested .variable.name... ccb_ast_t* ast = ccb_ast_copy(ccb, &(ccb_ast_t){ .type = CCB_AST_TYPE_VAR_LOCAL, .ctype = type, .variable.name = name }); */ if (ccb_ast_localenv) ccb_table_insert(ccb_ast_localenv, name, ast); if (ccb_ast_locals) ccb_list_push(ccb_ast_locals, ast); return ast; } /* Applies prefix to standard variable names. */ static void ccb_ast_fixglobal(ccb_t* ccb, ccb_ast_t* ast) { if (strlen(ccb->sym_prefix) != 0) { char* tmp = calloc(strlen(ccb->sym_prefix) + strlen(ast->variable.label) + 1, 1); strcat(tmp, ccb->sym_prefix); strcat(tmp, ast->variable.label); ast->variable.label = tmp; } } ccb_ast_t* ccb_ast_variable_global(ccb_t* ccb, ccb_data_type_t* type, char* name) { ccb_ast_t tmp = {}; tmp.type = CCB_AST_TYPE_VAR_GLOBAL; tmp.ctype = type; tmp.variable.name = name; tmp.variable.label = name; ccb_ast_fixglobal(ccb, &tmp); ccb_ast_t* ast = ccb_ast_copy(ccb, &tmp); /* TODO: Doesn't compile due to nested .variable.name ccb_ast_t* ast = ccb_ast_copy(ccb, &(ccb_ast_t){ .type = CCB_AST_TYPE_VAR_GLOBAL, .ctype = type, .variable.name = name, .variable.label = name }); */ ccb_table_insert(ccb_ast_globalenv, name, ast); return ast; } /* Applies prefix to standard function names. */ static void ccb_ast_fixfunction(ccb_t* ccb, ccb_ast_t* ast) { if (ast->function.callconv == 0 /*&& strlen(ccb->sym_prefix) != 0*/) { char* tmp = calloc(strlen(ccb->sym_prefix) + strlen(ast->function.name) + 1, 1); strcat(tmp, ccb->sym_prefix); strcat(tmp, ast->function.name); ast->function.name = tmp; } } ccb_ast_t* ccb_ast_call(ccb_t* ccb, ccb_data_type_t* type, char* name, ccb_list_t* arguments, ccb_list_t* parametertypes, int callconv) { ccb_ast_t tmp = {}; tmp.type = CCB_AST_TYPE_CALL; tmp.ctype = type; tmp.function.call.paramtypes = parametertypes; tmp.function.call.args = arguments; tmp.function.name = name; tmp.function.callconv = callconv; ccb_ast_fixfunction(ccb, &tmp); return ccb_ast_copy(ccb, &tmp); /* TODO: This doesn't self-compile yet due to nested fields return ccb_ast_copy(ccb, &(ccb_ast_t) { .type = CCB_AST_TYPE_CALL, .ctype = type, .function.call.paramtypes = parametertypes, .function.call.args = arguments, .function.name = name });*/ } ccb_ast_t* ccb_ast_ptrcall(ccb_t* ccb, ccb_data_type_t* type, char* name, ccb_ast_t* callable, ccb_list_t* arguments, ccb_list_t* parametertypes, int callconv) { ccb_ast_t tmp = {}; tmp.type = CCB_AST_TYPE_PTRCALL; tmp.ctype = type; tmp.function.call.paramtypes = parametertypes; tmp.function.call.args = arguments; tmp.function.name = name; tmp.function.call.callable = callable; tmp.function.callconv = callconv; ccb_ast_fixfunction(ccb, &tmp); return ccb_ast_copy(ccb, &tmp); /* TODO: This doesn't self-compile yet due to nested fields return ccb_ast_copy(ccb, &(ccb_ast_t) { .type = CCB_AST_TYPE_PTRCALL, .ctype = type, .function.call.paramtypes = parametertypes, .function.call.args = arguments, .function.name = name, .function.call.callable = callable });*/ } void ccb_ast_initpos(ccb_t* ccb, ccb_ast_t* ast) { ast->pos.line = ccb->pos.line; ast->pos.col = ccb->pos.col; ast->pos.uline = ccb->pos.uline; ast->pos.ucol = ccb->pos.ucol; ast->pos.ufile = ccb->pos.ufile; } void ccb_ast_setpos(ccb_t* ccb, ccb_ast_t* ast) { ccb->pos.line = ast->pos.line; ccb->pos.col = ast->pos.col; ccb->pos.uline = ast->pos.uline; ccb->pos.ucol = ast->pos.ucol; ccb->pos.ufile = ast->pos.ufile; } ccb_ast_t* ccb_ast_function(ccb_t* ccb, ccb_data_type_t* ret, char* name, ccb_list_t* params, ccb_ast_t* body, ccb_list_t* locals) { ccb_ast_t tmp = {}; ccb_ast_initpos(ccb, &tmp); tmp.type = CCB_AST_TYPE_FUNCTION; tmp.ctype = ret; tmp.function.params = params; tmp.function.locals = locals; tmp.function.name = name; tmp.function.body = body; ccb_ast_fixfunction(ccb, &tmp); return ccb_ast_copy(ccb, &tmp); /* TODO: This doesn't self-compile yet due to nested fields return ccb_ast_copy(ccb, &(ccb_ast_t) { .type = CCB_AST_TYPE_FUNCTION, .ctype = ret, .function.name = name, .function.params = params, .function.locals = locals, .function.body = body });*/ } ccb_ast_t* ccb_ast_declaration(ccb_t* ccb, ccb_ast_t* var, ccb_list_t* init) { ccb_ast_t tmp = {}; tmp.type = CCB_AST_TYPE_DECLARATION; tmp.ctype = NULL; tmp.decl.var = var; tmp.decl.init = init; return ccb_ast_copy(ccb, &tmp); /* TODO: This doesn't self-compile yet due to nested fields return ccb_ast_copy(ccb, &(ccb_ast_t) { .type = CCB_AST_TYPE_DECLARATION, .ctype = NULL, .decl.var = var, .decl.init = init, });*/ } ccb_ast_t* ccb_ast_initializer(ccb_t* ccb, ccb_ast_t* value, ccb_data_type_t* to, int offset) { ccb_ast_t tmp = {}; tmp.type = CCB_AST_TYPE_INITIALIZER; tmp.init.value = value; tmp.init.offset = offset; tmp.init.type = to; return ccb_ast_copy(ccb, &tmp); /* TODO: This doesn't self-compile yet due to nested fields return ccb_ast_copy(ccb, &(ccb_ast_t){ .type = CCB_AST_TYPE_INITIALIZER, .init.value = value, .init.offset = offset, .init.type = to });*/ } ccb_ast_t* ccb_ast_ternary(ccb_t* ccb, ccb_data_type_t* type, ccb_ast_t* cond, ccb_ast_t* then, ccb_ast_t* last) { ccb_ast_t tmp = {}; tmp.type = CCB_AST_TYPE_EXPRESSION_TERNARY; tmp.ctype = type; tmp.ifstmt.cond = cond; tmp.ifstmt.then = then; tmp.ifstmt.last = last; return ccb_ast_copy(ccb, &tmp); /* TODO: This doesn't self-compile yet due to nested fields return ccb_ast_copy(ccb, &(ccb_ast_t){ .type = CCB_AST_TYPE_EXPRESSION_TERNARY, .ctype = type, .ifstmt.cond = cond, .ifstmt.then = then, .ifstmt.last = last });*/ } static ccb_ast_t* ccb_ast_for_intermediate(ccb_t* ccb, int type, ccb_ast_t* init, ccb_ast_t* cond, ccb_ast_t* step, ccb_ast_t* body) { ccb_ast_t tmp = {}; tmp.type = type; tmp.ctype = NULL; tmp.forstmt.init = init; tmp.forstmt.cond = cond; tmp.forstmt.step = step; tmp.forstmt.body = body; return ccb_ast_copy(ccb, &tmp); /* TODO: This doesn't self-compile yet due to nested fields return ccb_ast_copy(ccb, &(ccb_ast_t){ .type = type, .ctype = NULL, .forstmt.init = init, .forstmt.cond = cond, .forstmt.step = step, .forstmt.body = body });*/ } ccb_ast_t* ccb_ast_switch(ccb_t* ccb, ccb_ast_t* expr, ccb_ast_t* body) { ccb_ast_t tmp = {}; tmp.type = CCB_AST_TYPE_STATEMENT_SWITCH; tmp.switchstmt.expr = expr; tmp.switchstmt.body = body; return ccb_ast_copy(ccb, &tmp); /* TODO: This doesn't self-compile yet due to nested fields return ccb_ast_copy(ccb, &(ccb_ast_t){ .type = CCB_AST_TYPE_STATEMENT_SWITCH, .switchstmt.expr = expr, .switchstmt.body = body });*/ } ccb_ast_t* ccb_ast_case(ccb_t* ccb, int value) { ccb_ast_t tmp = {}; tmp.type = CCB_AST_TYPE_STATEMENT_CASE; tmp.casevalue = value; return ccb_ast_copy(ccb, &tmp); } ccb_ast_t* ccb_ast_make(ccb_t* ccb, int type) { ccb_ast_t tmp = {}; tmp.type = type; return ccb_ast_copy(ccb, &tmp); } ccb_ast_t* ccb_ast_if(ccb_t* ccb, ccb_ast_t* cond, ccb_ast_t* then, ccb_ast_t* last) { ccb_ast_t tmp = {}; tmp.type = CCB_AST_TYPE_STATEMENT_IF; tmp.ctype = NULL; tmp.ifstmt.cond = cond; tmp.ifstmt.then = then; tmp.ifstmt.last = last; return ccb_ast_copy(ccb, &tmp); /* TODO: This doesn't self-compile yet due to nested fields return ccb_ast_copy(ccb, &(ccb_ast_t){ .type = CCB_AST_TYPE_STATEMENT_IF, .ctype = NULL, .ifstmt.cond = cond, .ifstmt.then = then, .ifstmt.last = last });*/ } ccb_ast_t* ccb_ast_for(ccb_t* ccb, ccb_ast_t* init, ccb_ast_t* cond, ccb_ast_t* step, ccb_ast_t* body) { return ccb_ast_for_intermediate(ccb, CCB_AST_TYPE_STATEMENT_FOR, init, cond, step, body); } ccb_ast_t* ccb_ast_while(ccb_t* ccb, ccb_ast_t* cond, ccb_ast_t* body) { return ccb_ast_for_intermediate(ccb, CCB_AST_TYPE_STATEMENT_WHILE, NULL, cond, NULL, body); } ccb_ast_t* ccb_ast_do(ccb_t* ccb, ccb_ast_t* cond, ccb_ast_t* body) { return ccb_ast_for_intermediate(ccb, CCB_AST_TYPE_STATEMENT_DO, NULL, cond, NULL, body); } ccb_ast_t* ccb_ast_goto(ccb_t* ccb, char* label) { ccb_ast_t tmp = {}; tmp.type = CCB_AST_TYPE_STATEMENT_GOTO; tmp.gotostmt.label = label; tmp.gotostmt.where = NULL; return ccb_ast_copy(ccb, &tmp); /* TODO: This doesn't self-compile yet due to nested fields return ccb_ast_copy(ccb, &(ccb_ast_t){ .type = CCB_AST_TYPE_STATEMENT_GOTO, .gotostmt.label = label, .gotostmt.where = NULL });*/ } ccb_ast_t* ccb_ast_asm(ccb_t* ccb, ccb_list_t* code) { ccb_ast_t tmp = {}; tmp.type = CCB_AST_TYPE_STATEMENT_ASM; tmp.asmstmt.code = code; return ccb_ast_copy(ccb, &tmp); } ccb_ast_t* ccb_ast_new_label(ccb_t* ccb, char* label) { ccb_ast_t tmp = {}; tmp.type = CCB_AST_TYPE_STATEMENT_LABEL; tmp.gotostmt.label = label; tmp.gotostmt.where = NULL; return ccb_ast_copy(ccb, &tmp); /* TODO: This doesn't self-compile yet due to nested fields return ccb_ast_copy(ccb, &(ccb_ast_t){ .type = CCB_AST_TYPE_STATEMENT_LABEL, .gotostmt.label = label, .gotostmt.where = NULL });*/ } ccb_ast_t* ccb_ast_return(ccb_t* ccb, int callconv, ccb_data_type_t* returntype, ccb_ast_t* value) { ccb_ast_t tmp = {}; tmp.type = CCB_AST_TYPE_STATEMENT_RETURN; tmp.ctype = returntype; tmp.returnstmt = value; tmp.return_callconv = callconv; return ccb_ast_copy(ccb, &tmp); /* return ccb_ast_copy(ccb, &(ccb_ast_t){ .type = CCB_AST_TYPE_STATEMENT_RETURN, .ctype = returntype, .returnstmt = value });*/ } ccb_ast_t* ccb_ast_compound(ccb_t* ccb, ccb_list_t* statements) { ccb_ast_t tmp = {}; tmp.type = CCB_AST_TYPE_STATEMENT_COMPOUND; tmp.ctype = NULL; tmp.compound = statements; return ccb_ast_copy(ccb, &tmp); /* return ccb_ast_copy(ccb, &(ccb_ast_t){ .type = CCB_AST_TYPE_STATEMENT_COMPOUND, .ctype = NULL, .compound = statements });*/ } ccb_data_type_t* ccb_ast_structure_field(ccb_t* ccb, ccb_data_type_t* type, int offset) { ccb_data_type_t* field = ccb_ast_type_copy(ccb, type); field->offset = offset; return field; } ccb_data_type_t* ccb_ast_structure_new(ccb_t* ccb, ccb_table_t* fields, int size, bool isstruct) { ccb_data_type_t tmp = {}; tmp.type = CCB_TYPE_STRUCTURE; tmp.size = size; tmp.fields = fields; tmp.isstruct = isstruct; return ccb_ast_type_copy(ccb, &tmp/*(ccb_data_type_t) { .type = CCB_TYPE_STRUCTURE, .size = size, .fields = fields, .isstruct = isstruct }*/); } #ifdef _WIN32 #define getpid _getpid #endif int ccb_ast_label_index = 0; char* ccb_ast_label(ccb_t* ccb) { //static int index = 0; ccb_string_t* string = ccb_string_create(); if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_GENERIC || ccb_target_family(ccb) == CCB_ARCH_FAMILY_GEN1 || (ccb_target_family(ccb) == CCB_ARCH_FAMILY_X86 && ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_RAW)) { ccb_string_catf(string, "L%d_%d", getpid(), ccb_ast_label_index++); // TODO: Fix this and/or the FASM code above to use a better unique base at least } else if (ccb_target_family(ccb) == CCB_ARCH_FAMILY_X86) { if (ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_FASM) { if (ccb_target_binfmt(ccb) == CCB_TARGET_BINFMT_FLAT) { //ccb_string_catf(string, "L%d_%d", getpid(), ccb_ast_label_index++); // NOTE: getpid() isn't a good way to get a random number, but is enough to be unlikely to mix up with another run's local data ccb_string_catcstr(string, "L"); ccb_string_catint(string, getpid()); ccb_string_catcstr(string, "_"); ccb_string_catint(string, ccb_ast_label_index++); } else { //ccb_string_catf(string, "L%d", ccb_ast_label_index++); ccb_string_catcstr(string, "L"); ccb_string_catint(string, ccb_ast_label_index++); } } else if (ccb_target_asmfmt(ccb) == CCB_TARGET_ASMFMT_NASM) { if (ccb_target_binfmt(ccb) == CCB_TARGET_BINFMT_FLAT) { //ccb_string_catf(string, "L%d_%d", getpid(), ccb_ast_label_index++); // NOTE: getpid() isn't a good way to get a random number, but is enough to be unlikely to mix up with another run's local data ccb_string_catcstr(string, "L"); ccb_string_catint(string, getpid()); ccb_string_catcstr(string, "_"); ccb_string_catint(string, ccb_ast_label_index++); } else { //ccb_string_catf(string, "L%d", ccb_ast_label_index++); ccb_string_catcstr(string, "L"); ccb_string_catint(string, ccb_ast_label_index++); } } else { //ccb_string_catf(string, ".L%d", ccb_ast_label_index++); ccb_string_catcstr(string, ".L"); ccb_string_catint(string, ccb_ast_label_index++); } } else { //ccb_string_catf(string, ".L%d", ccb_ast_label_index++); ccb_string_catcstr(string, ".L"); ccb_string_catint(string, ccb_ast_label_index++); } return ccb_string_buffer(string); } bool ccb_ast_type_integer(ccb_t* ccb, ccb_data_type_t* type) { return type->type == CCB_TYPE_CHAR || type->type == CCB_TYPE_SHORT || type->type == CCB_TYPE_INT || type->type == CCB_TYPE_LONG || type->type == CCB_TYPE_LLONG; } bool ccb_ast_type_floating(ccb_t* ccb, ccb_data_type_t* type) { return type->type == CCB_TYPE_FLOAT || type->type == CCB_TYPE_DOUBLE || type->type == CCB_TYPE_LDOUBLE; } ccb_data_type_t* ccb_ast_type_copy(ccb_t* ccb, ccb_data_type_t* type) { return memcpy(ccb_memory_allocate(sizeof(ccb_data_type_t)), type, sizeof(ccb_data_type_t)); } ccb_data_type_t* ccb_ast_type_copy_incomplete(ccb_t* ccb, ccb_data_type_t* type) { if (!type) return NULL; return (type->length == -1) ? ccb_ast_type_copy(ccb, type) : type; } ccb_data_type_t* ccb_ast_type_create(ccb_t* ccb, ccb_type_t type, bool sign) { ccb_data_type_t* t = ccb_memory_allocate(sizeof(ccb_data_type_t)); t->type = type; t->sign = sign; switch (type) { case CCB_TYPE_VOID: t->size = 0; break; case CCB_TYPE_CHAR: t->size = ccb_target_type_size_char(ccb); break; case CCB_TYPE_SHORT: t->size = ccb_target_type_size_short(ccb); break; case CCB_TYPE_INT: t->size = ccb_target_type_size_int(ccb); break; case CCB_TYPE_LONG: t->size = ccb_target_type_size_long(ccb); break; case CCB_TYPE_LLONG: t->size = ccb_target_type_size_llong(ccb); break; case CCB_TYPE_FLOAT: t->size = ccb_target_type_size_float(ccb); break; case CCB_TYPE_DOUBLE: t->size = ccb_target_type_size_double(ccb); break; case CCB_TYPE_LDOUBLE: t->size = ccb_target_type_size_ldouble(ccb); break; default: ccb_compile_error(ccb, "ICE"); } return t; } ccb_data_type_t* ccb_ast_type_stub(ccb_t* ccb) { ccb_data_type_t tmp = {}; tmp.type = CCB_TYPE_CDECL; tmp.size = 0; return ccb_ast_copy(ccb, &tmp); /*return ccb_ast_type_copy(ccb, &(ccb_data_type_t) { .type = CCB_TYPE_CDECL, .size = 0 });*/ } ccb_data_type_t* ccb_ast_prototype(ccb_t* ccb, ccb_data_type_t* returntype, ccb_list_t* paramtypes, bool dots) { ccb_data_type_t tmp = {}; tmp.type = CCB_TYPE_FUNCTION; tmp.returntype = returntype; tmp.parameters = paramtypes; tmp.hasdots = dots; return ccb_ast_copy(ccb, &tmp); /*return ccb_ast_type_copy(ccb, &(ccb_data_type_t){ .type = CCB_TYPE_FUNCTION, .returntype = returntype, .parameters = paramtypes, .hasdots = dots });*/ } ccb_data_type_t* ccb_ast_array(ccb_t* ccb, ccb_data_type_t* type, int length) { ccb_data_type_t tmp = {}; tmp.type = CCB_TYPE_ARRAY; tmp.pointer = type; tmp.size = ((length < 0) ? -1 : (type->size * length)); /* TODO: This requires more brackets on self-compilation */ tmp.length = length; return ccb_ast_type_copy(ccb, &tmp/*(ccb_data_type_t){ .type = CCB_TYPE_ARRAY, .pointer = type, .size = ((length < 0) ? -1 : type->size * length), .length = length }*/); } ccb_data_type_t* ccb_ast_array_convert(ccb_t* ccb, ccb_data_type_t* type) { if (type->type != CCB_TYPE_ARRAY) return type; return ccb_ast_pointer(ccb, type->pointer); } ccb_data_type_t* ccb_ast_pointer(ccb_t* ccb, ccb_data_type_t* type) { ccb_data_type_t tmp = {}; tmp.type = CCB_TYPE_POINTER; tmp.pointer = type; tmp.size = ccb_target_type_size_pointer(ccb); return ccb_ast_type_copy(ccb, &tmp/*(ccb_data_type_t){ .type = CCB_TYPE_POINTER, .pointer = type, .size = ccb_target_type_size_pointer(ccb) }*/); } const char* ccb_ast_type_string(ccb_t* ccb, ccb_data_type_t* type) { ccb_string_t* string; switch (type->type) { case CCB_TYPE_VOID: return "void"; case CCB_TYPE_INT: return "int"; case CCB_TYPE_CHAR: return "char"; case CCB_TYPE_LONG: return "long"; case CCB_TYPE_LLONG: return "long long"; //TODO... case CCB_TYPE_ULLONG: return "unsigned long long"; case CCB_TYPE_SHORT: return "short"; case CCB_TYPE_FLOAT: return "float"; case CCB_TYPE_DOUBLE: return "double"; case CCB_TYPE_LDOUBLE: return "long double"; #ifdef CCB_X_OBJC case CCB_TYPE_ID: return "id"; #endif case CCB_TYPE_FUNCTION: string = ccb_string_create(); ccb_string_cat(string, '('); for (ccb_list_iterator_t* it = ccb_list_iterator(type->parameters); !ccb_list_iterator_end(it); ) { ccb_data_type_t* next = ccb_list_iterator_next(it); ccb_string_catf(string, "%s", ccb_ast_type_string(ccb, next)); if (!ccb_list_iterator_end(it)) ccb_string_cat(string, ','); } ccb_string_catf(string, ") -> %s", ccb_ast_type_string(ccb, type->returntype)); return ccb_string_buffer(string); case CCB_TYPE_POINTER: string = ccb_string_create(); ccb_string_catf(string, "%s*", ccb_ast_type_string(ccb, type->pointer)); return ccb_string_buffer(string); case CCB_TYPE_ARRAY: string = ccb_string_create(); /*ccb_string_catf( string, "%s[%d]", ccb_ast_type_string(ccb, type->pointer), type->length );*/ ccb_string_catcstr(string, ccb_ast_type_string(ccb, type->pointer)); ccb_string_cat(string, '['); ccb_string_catint(string, type->length); ccb_string_cat(string, ']'); return ccb_string_buffer(string); case CCB_TYPE_STRUCTURE: string = ccb_string_create(); ccb_string_catcstr(string, "(struct..."); //for (ccb_list_iterator_t* it = ccb_list_iterator(ccb_table_values(type->fields)); !ccb_list_iterator_end(it); ) // ccb_string_catf(string, " (%s)", ccb_ast_type_string(ccb, ccb_list_iterator_next(it))); ccb_string_cat(string, ')'); return ccb_string_buffer(string); default: break; } return NULL; } static void ccb_ast_string_unary(ccb_t* ccb, ccb_string_t* string, const char* op, ccb_ast_t* ast) { ccb_string_catf(string, "(%s %s)", op, ccb_ast_string(ccb, ast->unary.operand)); } static void ccb_ast_string_binary(ccb_t* ccb, ccb_string_t* string, const char* op, ccb_ast_t* ast) { ccb_string_catf(string, "(%s %s %s)", op, ccb_ast_string(ccb, ast->left), ccb_ast_string(ccb, ast->right)); } static void ccb_ast_string_initialization_declaration(ccb_t* ccb, ccb_string_t* string, ccb_list_t* initlist) { for (ccb_list_iterator_t* it = ccb_list_iterator(initlist); !ccb_list_iterator_end(it); ) { ccb_ast_t* init = ccb_list_iterator_next(it); ccb_string_catf(string, "%s", ccb_ast_string(ccb, init)); if (!ccb_list_iterator_end(it)) ccb_string_cat(string, ' '); } } static void ccb_ast_string_impl(ccb_t* ccb, ccb_string_t* string, ccb_ast_t* ast); // TODO: Predeclaration required for self-compile! static void ccb_ast_string_impl(ccb_t* ccb, ccb_string_t* string, ccb_ast_t* ast) { char* left; char* right; if (!ast) { ccb_string_catf(string, "(null)"); return; } switch (ast->type) { case CCB_AST_TYPE_LITERAL: switch (ast->ctype->type) { case CCB_TYPE_INT: case CCB_TYPE_SHORT: //ccb_string_catf(string, "%d", ast->integer); ccb_string_catint(string, ast->integer); break; case CCB_TYPE_FLOAT: case CCB_TYPE_DOUBLE: ccb_string_catf(string, "%f", ast->floating.value); break; case CCB_TYPE_LONG: ccb_string_catf(string, "%ldL", ast->integer); break; case CCB_TYPE_CHAR: if (ast->integer == '\n') ccb_string_catf(string, "'\n'"); else if (ast->integer == '\\') ccb_string_catf(string, "'\\\\'"); else if (ast->integer == '\0') ccb_string_catf(string, "'\\0'"); else ccb_string_catf(string, "'%c'", ast->integer); break; default: ccb_compile_error(ccb, "Internal error: ast_string_impl"); break; } break; case CCB_AST_TYPE_STRING: ccb_string_catf(string, "\"%s\"", ccb_string_quote(ast->string.data)); break; case CCB_AST_TYPE_VAR_LOCAL: ccb_string_catf(string, "%s", ast->variable.name); if (ast->variable.init) { ccb_string_cat(string, '('); ccb_ast_string_initialization_declaration(ccb, string, ast->variable.init); ccb_string_cat(string, ')'); } break; case CCB_AST_TYPE_VAR_GLOBAL: ccb_string_catf(string, "%s", ast->variable.name); break; case CCB_AST_TYPE_CALL: ccb_string_catf(string, "(%s)%s(", ccb_ast_type_string(ccb, ast->ctype), ast->function.name); for (ccb_list_iterator_t* it = ccb_list_iterator(ast->function.call.args); !ccb_list_iterator_end(it); ) { ccb_string_catf(string, "%s", ccb_ast_string(ccb, ccb_list_iterator_next(it))); if (!ccb_list_iterator_end(it)) ccb_string_cat(string, ','); } ccb_string_cat(string, ')'); break; case CCB_AST_TYPE_FUNCTION: ccb_string_catf(string, "(%s)%s(", ccb_ast_type_string(ccb, ast->ctype), ast->function.name); for (ccb_list_iterator_t* it = ccb_list_iterator(ast->function.params); !ccb_list_iterator_end(it); ) { ccb_ast_t* param = ccb_list_iterator_next(it); ccb_string_catf(string, "%s %s", ccb_ast_type_string(ccb, param->ctype), ccb_ast_string(ccb, param)); if (!ccb_list_iterator_end(it)) ccb_string_cat(string, ','); } ccb_string_cat(string, ')'); ccb_ast_string_impl(ccb, string, ast->function.body); break; case CCB_AST_TYPE_DECLARATION: ccb_string_catf(string, "(decl %s %s", ccb_ast_type_string(ccb, ast->decl.var->ctype), ast->decl.var->variable.name ); ccb_ast_string_initialization_declaration(ccb, string, ast->decl.init); ccb_string_cat(string, ')'); break; case CCB_AST_TYPE_INITIALIZER: ccb_string_catf(string, "%s@%d", ccb_ast_string(ccb, ast->init.value), ast->init.offset); break; case CCB_AST_TYPE_STATEMENT_COMPOUND: ccb_string_cat(string, '{'); for (ccb_list_iterator_t* it = ccb_list_iterator(ast->compound); !ccb_list_iterator_end(it); ) { ccb_ast_string_impl(ccb, string, ccb_list_iterator_next(it)); ccb_string_cat(string, ';'); } ccb_string_cat(string, '}'); break; case CCB_AST_TYPE_STRUCT: ccb_ast_string_impl(ccb, string, ast->structure); ccb_string_cat(string, '.'); ccb_string_catf(string, ast->field); break; case CCB_AST_TYPE_EXPRESSION_TERNARY: ccb_string_catf(string, "(? %s %s %s)", ccb_ast_string(ccb, ast->ifstmt.cond), ccb_ast_string(ccb, ast->ifstmt.then), ccb_ast_string(ccb, ast->ifstmt.last) ); break; case CCB_AST_TYPE_STATEMENT_IF: ccb_string_catf(string, "(if %s %s", ccb_ast_string(ccb, ast->ifstmt.cond), ccb_ast_string(ccb, ast->ifstmt.then)); if (ast->ifstmt.last) ccb_string_catf(string, " %s", ccb_ast_string(ccb, ast->ifstmt.last)); ccb_string_cat(string, ')'); break; case CCB_AST_TYPE_STATEMENT_FOR: ccb_string_catf(string, "(for %s %s %s %s)", ccb_ast_string(ccb, ast->forstmt.init), ccb_ast_string(ccb, ast->forstmt.cond), ccb_ast_string(ccb, ast->forstmt.step), ccb_ast_string(ccb, ast->forstmt.body) ); break; case CCB_AST_TYPE_STATEMENT_WHILE: ccb_string_catf(string, "(while %s %s)", ccb_ast_string(ccb, ast->forstmt.cond), ccb_ast_string(ccb, ast->forstmt.body) ); break; case CCB_AST_TYPE_STATEMENT_DO: ccb_string_catf(string, "(do %s %s)", ccb_ast_string(ccb, ast->forstmt.cond), ccb_ast_string(ccb, ast->forstmt.body) ); break; case CCB_AST_TYPE_STATEMENT_RETURN: ccb_string_catf(string, "(return %s)", ccb_ast_string(ccb, ast->returnstmt)); break; case CCB_AST_TYPE_ADDRESS: ccb_ast_string_unary(ccb, string, "&", ast); break; case CCB_AST_TYPE_DEREFERENCE: ccb_ast_string_unary(ccb, string, "*", ast); break; case CCB_LEXER_TOKEN_INCREMENT: ccb_ast_string_unary(ccb, string, "++", ast); break; case CCB_LEXER_TOKEN_DECREMENT: ccb_ast_string_unary(ccb, string, "--", ast); break; case '!': ccb_ast_string_unary(ccb, string, "!", ast); break; case '&': ccb_ast_string_binary(ccb, string, "&", ast); break; case '|': ccb_ast_string_binary(ccb, string, "|", ast); break; case CCB_LEXER_TOKEN_AND: ccb_ast_string_binary(ccb, string, "&&", ast); break; case CCB_LEXER_TOKEN_OR: ccb_ast_string_binary(ccb, string, "||", ast); break; case CCB_LEXER_TOKEN_GEQUAL: ccb_ast_string_binary(ccb, string, ">=", ast); break; case CCB_LEXER_TOKEN_LEQUAL: ccb_ast_string_binary(ccb, string, "<=", ast); break; case CCB_LEXER_TOKEN_NEQUAL: ccb_ast_string_binary(ccb, string, "!=", ast); break; case CCB_AST_TYPE_EXPRESSION_CAST: ccb_string_catf(string, "((%s) -> (%s) %s)", ccb_ast_type_string(ccb, ast->unary.operand->ctype), ccb_ast_type_string(ccb, ast->ctype), ccb_ast_string(ccb, ast->unary.operand) ); break; case CCB_AST_TYPE_STATEMENT_CASE: ccb_string_catcstr(string, "(todo: case)"); break; default: fprintf(stderr, "Unknown node type %d (0x100+%d)\n", ast->type, ast->type - 0x100); left = ccb_ast_string(ccb, ast->left); right = ccb_ast_string(ccb, ast->right); if (ast->type == CCB_LEXER_TOKEN_EQUAL) ccb_string_catf(string, "(== %s %s)", left, right); else ccb_string_catf(string, "(%c %s %s)", ast->type, left, right); break; } } char* ccb_ast_string(ccb_t* ccb, ccb_ast_t* ast) { if (ast == NULL) { return "?NULL?"; } ccb_string_t* string = ccb_string_create(); ccb_ast_string_impl(ccb, string, ast); return ccb_string_buffer(string); } static ccb_ast_t* ccb_parse_expression(ccb_t* ccb); static ccb_ast_t* ccb_parse_expression_withcomma(ccb_t* ccb); static ccb_ast_t* ccb_parse_expression_unary(ccb_t* ccb); static ccb_ast_t* ccb_parse_expression_intermediate(ccb_t* ccb, int); static ccb_ast_t* ccb_parse_statement_compound(ccb_t* ccb); static void ccb_parse_statement_declaration(ccb_t* ccb, ccb_list_t*); static ccb_ast_t* ccb_parse_statement(ccb_t* ccb); static ccb_data_type_t* ccb_parse_declaration_specification(ccb_t* ccb, ccb_storage_t*); static ccb_list_t* ccb_parse_initializer_declaration(ccb_t* ccb, ccb_data_type_t* type); static ccb_data_type_t* ccb_parse_declarator(ccb_t* ccb, char**, ccb_data_type_t*, ccb_list_t*, ccb_cdecl_t); static void ccb_parse_declaration(ccb_t* ccb, ccb_list_t*, ccb_ast_t* (*)(ccb_t*, ccb_data_type_t*, char*)); static void ccb_parse_function_parameter(ccb_t* ccb, ccb_data_type_t**, char**, bool); static ccb_data_type_t* ccb_parse_function_parameters(ccb_t* ccb, ccb_list_t*, ccb_data_type_t*); static bool ccb_parse_type_check(ccb_t* ccb, ccb_lexer_token_t* token); static void ccb_parse_semantic_lvalue(ccb_t* ccb, ccb_ast_t* ast, bool allowfunc) { switch (ast->type) { case CCB_AST_TYPE_FUNCTION: if (allowfunc) { return; } break; case CCB_AST_TYPE_VAR_LOCAL: case CCB_AST_TYPE_VAR_GLOBAL: case CCB_AST_TYPE_DEREFERENCE: case CCB_AST_TYPE_STRUCT: return; } ccb_compile_error(ccb, "expected lvalue, `%s' isn't a valid lvalue", ccb_ast_string(ccb, ast)); } static void ccb_parse_semantic_notvoid(ccb_t* ccb, ccb_data_type_t* type) { if (type->type == CCB_TYPE_VOID) ccb_compile_error(ccb, "void not allowed in expression"); } static void ccb_parse_semantic_integer(ccb_t* ccb, ccb_ast_t* node) { if (!ccb_ast_type_integer(ccb, node->ctype)) ccb_compile_error(ccb, "expected integer type, `%s' isn't a valid integer type", ccb_ast_string(ccb, node)); } static bool ccb_parse_semantic_rightassoc(ccb_t* ccb, ccb_lexer_token_t* token) { return (token->punct == '='); } static void ccb_parse_expect(ccb_t* ccb, char punct) { ccb_lexer_token_t* token = ccb_lexer_next(ccb); if (!ccb_lexer_ispunct(ccb, token, punct)) ccb_compile_error(ccb, "expected `%c`, got %s instead", punct, ccb_lexer_tokenstr(ccb, token)); } static bool ccb_parse_identifier_check(ccb_t* ccb, ccb_lexer_token_t* token, const char* identifier) { return token->type == CCB_LEXER_TOKEN_IDENTIFIER && !strcmp(token->string, identifier); } int ccb_parse_evaluate(ccb_t* ccb, ccb_ast_t* ast) { switch (ast->type) { case CCB_AST_TYPE_LITERAL: if (ccb_ast_type_integer(ccb, ast->ctype)) return ast->integer; // TODO: Should this return a 64-bit value?? //fprintf(stderr, "NOTE: Type->type is %d\n", ast->ctype->type); ccb_compile_error(ccb, "not a valid integer constant expression `%s'", ccb_ast_string(ccb, ast)); break; case '+': return ccb_parse_evaluate(ccb, ast->left) + ccb_parse_evaluate(ccb, ast->right); case '-': return ccb_parse_evaluate(ccb, ast->left) - ccb_parse_evaluate(ccb, ast->right); case '*': return ccb_parse_evaluate(ccb, ast->left) * ccb_parse_evaluate(ccb, ast->right); case '/': return ccb_parse_evaluate(ccb, ast->left) / ccb_parse_evaluate(ccb, ast->right); case '<': return ccb_parse_evaluate(ccb, ast->left) < ccb_parse_evaluate(ccb, ast->right); case '>': return ccb_parse_evaluate(ccb, ast->left) > ccb_parse_evaluate(ccb, ast->right); case '^': return ccb_parse_evaluate(ccb, ast->left) ^ ccb_parse_evaluate(ccb, ast->right); case '%': return ccb_parse_evaluate(ccb, ast->left) % ccb_parse_evaluate(ccb, ast->right); case CCB_LEXER_TOKEN_AND: return ccb_parse_evaluate(ccb, ast->left) && ccb_parse_evaluate(ccb, ast->right); case CCB_LEXER_TOKEN_OR: return ccb_parse_evaluate(ccb, ast->left) || ccb_parse_evaluate(ccb, ast->right); case CCB_AST_TYPE_EQUAL /*CCB_LEXER_TOKEN_EQUAL*/: return ccb_parse_evaluate(ccb, ast->left) == ccb_parse_evaluate(ccb, ast->right); case CCB_LEXER_TOKEN_LEQUAL: return ccb_parse_evaluate(ccb, ast->left) <= ccb_parse_evaluate(ccb, ast->right); case CCB_LEXER_TOKEN_GEQUAL: return ccb_parse_evaluate(ccb, ast->left) >= ccb_parse_evaluate(ccb, ast->right); case CCB_LEXER_TOKEN_NEQUAL: return ccb_parse_evaluate(ccb, ast->left) != ccb_parse_evaluate(ccb, ast->right); case CCB_AST_TYPE_LSHIFT /* CCB_LEXER_TOKEN_LSHIFT*/: return ccb_parse_evaluate(ccb, ast->left) << ccb_parse_evaluate(ccb, ast->right); case CCB_LEXER_TOKEN_RSHIFT: return ccb_parse_evaluate(ccb, ast->left) >> ccb_parse_evaluate(ccb, ast->right); // TODO sign differences case CCB_AST_TYPE_EXPRESSION_TERNARY: if(ccb_parse_evaluate(ccb, ast->ifstmt.cond) != 0) { return ccb_parse_evaluate(ccb, ast->ifstmt.then); } else { return ccb_parse_evaluate(ccb, ast->ifstmt.last); } /* Deal with unary operations differently */ case '!': return !ccb_parse_evaluate(ccb, ast->unary.operand); case '~': return ~ccb_parse_evaluate(ccb, ast->unary.operand); case CCB_AST_TYPE_EXPRESSION_CAST: return ccb_parse_evaluate(ccb, ast->unary.operand); default: ccb_compile_error(ccb, "not a valid integer constant expression `%s'", ccb_ast_string(ccb, ast)); } return -1; } static int ccb_parse_operator_priority(ccb_t* ccb, ccb_lexer_token_t* token) { switch (token->punct) { case '[': case '.': case CCB_LEXER_TOKEN_ARROW: return 1; case CCB_LEXER_TOKEN_INCREMENT: case CCB_LEXER_TOKEN_DECREMENT: return 2; case '*': case '/': case '%': return 3; case '+': case '-': return 4; case CCB_LEXER_TOKEN_LSHIFT: case CCB_LEXER_TOKEN_RSHIFT: return 5; case '<': case '>': return 6; case CCB_LEXER_TOKEN_EQUAL: case CCB_LEXER_TOKEN_GEQUAL: case CCB_LEXER_TOKEN_LEQUAL: case CCB_LEXER_TOKEN_NEQUAL: return 7; case '&': return 8; case '^': return 9; case '|': return 10; case CCB_LEXER_TOKEN_AND: return 11; case CCB_LEXER_TOKEN_OR: return 12; case '?': return 13; case '=': case CCB_LEXER_TOKEN_COMPOUND_ADD: case CCB_LEXER_TOKEN_COMPOUND_AND: case CCB_LEXER_TOKEN_COMPOUND_DIV: case CCB_LEXER_TOKEN_COMPOUND_LSHIFT: case CCB_LEXER_TOKEN_COMPOUND_MOD: case CCB_LEXER_TOKEN_COMPOUND_MUL: case CCB_LEXER_TOKEN_COMPOUND_OR: case CCB_LEXER_TOKEN_COMPOUND_RSHIFT: case CCB_LEXER_TOKEN_COMPOUND_SUB: case CCB_LEXER_TOKEN_COMPOUND_XOR: return 14; } return -1; } static ccb_list_t* ccb_parse_parameter_types(ccb_t* ccb, ccb_list_t* parameters) { ccb_list_t* list = ccb_list_create(); for (ccb_list_iterator_t* it = ccb_list_iterator(parameters); !ccb_list_iterator_end(it); ) ccb_list_push(list, ((ccb_ast_t*)ccb_list_iterator_next(it))->ctype); return list; } static void ccb_parse_function_typecheck(ccb_t* ccb, const char* name, ccb_list_t* parameters, ccb_list_t* arguments) { fprintf(stderr, "Parsing function '%s'\n", name); if (ccb_list_length(arguments) < ccb_list_length(parameters)) ccb_compile_error(ccb, "too few arguments for function `%s'", name); ccb_list_iterator_t *jt = ccb_list_iterator(arguments); // TODO: Secondary variables not handled properly in "for" for (ccb_list_iterator_t* it = ccb_list_iterator(parameters); !ccb_list_iterator_end(jt); ) { ccb_data_type_t* parameter = ccb_list_iterator_next(it); ccb_data_type_t* argument = ccb_list_iterator_next(jt); if (parameter) { //printf("Comparing formal params\n"); ccb_ast_result_type(ccb, '=', parameter, argument); } else { //printf("Comparing to default int\n"); ccb_ast_result_type(ccb, '=', argument, ccb_ast_data_table[CCB_AST_DATA_INT]); } } } static ccb_ast_t* ccb_parse_finish_call(ccb_t* ccb, char* name, ccb_list_t* args) { //fprintf(stderr, "TESTING: '%s'\n", name); ccb_ast_t* func = ccb_table_find(ccb_ast_localenv, name); bool isptr = false; if (func) { ccb_data_type_t* declaration = func->ctype; if (declaration->type == CCB_TYPE_POINTER) { if (declaration->callconv != 0) { fprintf(stderr, "NOTE: Ignoring non-default calling convention %d in indirect call\n", declaration->callconv); } declaration = declaration->pointer; isptr = true; } if (declaration->type != CCB_TYPE_FUNCTION) ccb_compile_error(ccb, "expected a function name, `%s' isn't a function", name); ccb_parse_function_typecheck(ccb, name, declaration->parameters, ccb_parse_parameter_types(ccb, args)); if (declaration->callconv != 0) { fprintf(stderr, "NOTE: Ignoring non-default calling convention %d in normal call\n", declaration->callconv); } if (isptr) { return ccb_ast_ptrcall(ccb, declaration->returntype, name, func, args, declaration->parameters, func->ctype->callconv); } else { return ccb_ast_call(ccb, declaration->returntype, name, args, declaration->parameters, func->ctype->callconv); } } if (strlen(name) >= 6 && !strcmp(name+(strlen(name)-6), "__init")) { // Lazily ignore warnings for undeclared __init functions (these won't take any parameters anyway...) } else { ccb_compile_warn(ccb, "function `%s' is not declared, integer return type implied", name); } return ccb_ast_call(ccb, ccb_ast_data_table[CCB_AST_DATA_INT], name, args, ccb_list_create(), 0 /* default callconv */); } static char* ccb_parse_runtime_fname(ccb_t* ccb, char* name) { /* In the future this function might convert internal names to library names * according to user preferences (e.g. for calling into an obj-c runtime for * @"strings" instead of calling the default function). */ return name; } static ccb_ast_t* ccb_parse_runtime_call(ccb_t* ccb, char* name, ccb_list_t* list) { return ccb_parse_finish_call(ccb, ccb_parse_runtime_fname(ccb, name), list); } static ccb_ast_t* ccb_parse_runtime_call_0(ccb_t* ccb, char* name) { ccb_list_t* list = ccb_list_create(); return ccb_parse_runtime_call(ccb, name, list); } static ccb_ast_t* ccb_parse_runtime_call_1(ccb_t* ccb, char* name, ccb_ast_t* arg1) { ccb_list_t* list = ccb_list_create(); ccb_list_push(list, arg1); return ccb_parse_runtime_call(ccb, name, list); } static ccb_ast_t* ccb_parse_runtime_call_2(ccb_t* ccb, char* name, ccb_ast_t* arg1, ccb_ast_t* arg2) { ccb_list_t* list = ccb_list_create(); ccb_list_push(list, arg1); ccb_list_push(list, arg2); return ccb_parse_runtime_call(ccb, name, list); } static ccb_ast_t* ccb_parse_runtime_call_3(ccb_t* ccb, char* name, ccb_ast_t* arg1, ccb_ast_t* arg2, ccb_ast_t* arg3) { ccb_list_t* list = ccb_list_create(); ccb_list_push(list, arg1); ccb_list_push(list, arg2); ccb_list_push(list, arg3); return ccb_parse_runtime_call(ccb, name, list); } static ccb_ast_t* ccb_parse_runtime_call_4(ccb_t* ccb, char* name, ccb_ast_t* arg1, ccb_ast_t* arg2, ccb_ast_t* arg3, ccb_ast_t* arg4) { ccb_list_t* list = ccb_list_create(); ccb_list_push(list, arg1); ccb_list_push(list, arg2); ccb_list_push(list, arg3); ccb_list_push(list, arg4); return ccb_parse_runtime_call(ccb, name, list); } /* TODO... static ccb_ast_t* ccb_parse_runtime_call_5(ccb_t* ccb, char* name, ccb_ast_t* arg1, ccb_ast_t* arg2, ccb_ast_t* arg3, ccb_ast_t* arg4, ccb_ast_t* arg5) { ccb_list_t* list = ccb_list_create(); ccb_list_push(list, arg1); ccb_list_push(list, arg2); ccb_list_push(list, arg3); ccb_list_push(list, arg4); ccb_list_push(list, arg5); return ccb_parse_runtime_call(ccb, name, list); } static ccb_ast_t* ccb_parse_runtime_call_6(ccb_t* ccb, char* name, ccb_ast_t* arg1, ccb_ast_t* arg2, ccb_ast_t* arg3, ccb_ast_t* arg4, ccb_ast_t* arg5, ccb_ast_t* arg6) { ccb_list_t* list = ccb_list_create(); ccb_list_push(list, arg1); ccb_list_push(list, arg2); ccb_list_push(list, arg3); ccb_list_push(list, arg4); ccb_list_push(list, arg5); ccb_list_push(list, arg6); return ccb_parse_runtime_call(ccb, name, list); } */ static ccb_ast_t* ccb_parse_function_call(ccb_t* ccb, char* name) { ccb_list_t* list = ccb_list_create(); for (;;) { ccb_lexer_token_t* token = ccb_lexer_next(ccb); if (ccb_lexer_ispunct(ccb, token, ')')) break; ccb_lexer_unget(ccb, token); ccb_list_push(list, ccb_parse_expression(ccb)); token = ccb_lexer_next(ccb); if (ccb_lexer_ispunct(ccb, token, ')')) break; if (!ccb_lexer_ispunct(ccb, token, ',')) ccb_compile_error(ccb, "unexpected token `%s'", ccb_lexer_tokenstr(ccb, token)); } return ccb_parse_finish_call(ccb, name, list); } static int ccb_parse_checkstart(ccb_t* ccb, const char* start, char* name) { int i; for (i = 0; start[i] != 0; i++) { if (name[i] != start[i]) { return 0; } } return 1; } static const char* ccb_argbuiltin_buffername(ccb_t* ccb); static ccb_ast_t* ccb_parse_generic(ccb_t* ccb, char* name) { ccb_ast_t* var = NULL; ccb_lexer_token_t* token = ccb_lexer_next(ccb); if (ccb_lexer_ispunct(ccb, token, '(')) return ccb_parse_function_call(ccb, name); ccb_lexer_unget(ccb, token); if (!(var = ccb_table_find(ccb_ast_localenv, name))) { /*char* gname = calloc(strlen(ccb->sym_prefix) + strlen(name) + 1, 1); strcat(gname, ccb->sym_prefix); strcat(gname, name);*/ if (!(var = ccb_table_find(ccb_ast_globalenv, name))) { // TODO: Check if this causes any unwanted side-effects (added by Zak to handle enums in some situations) /* __func__ (to get the function name as a string constant) and some similar builtins have been added * as simple fallbacks to variable resolution. */ if (!strcmp("__func__", name)) { var = ccb_ast_new_string(ccb, ccb->func_name == NULL ? "(NULL)" : ccb->func_name); ccb_list_push(ccb_ast_strings, var); return var; } else if (!strcmp("__builtin_func_callconv", name)) { /* NOTE: At time of writing these values will not be reliable except (hopefully) for checking whether * the __classic_call convention (#101) is used. This can be helpful to implement type checks when defining * vararg macros. */ return ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_LONG], ccb->func_callconv); } else if (!strcmp("__builtin_default_callconv", name)) { return ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_LONG], ccb->default_callconv); } /* Undefined __builtin_argX aliases can still be used, but are replaced with references to a special variable to indicate that they're undefined. */ if (ccb_parse_checkstart(ccb, "__builtin_arg", name) && ccb_argbuiltin_buffername(ccb) != NULL) { return ccb_parse_generic(ccb, ccb_argbuiltin_buffername(ccb)); } ccb_compile_error(ccb, "undefined variable `%s'", name); } } return var; } #ifdef CCB_X_OBJC ccb_data_type_t* ccb_parse_objx_methodtype(ccb_t* ccb, ccb_data_type_t* cltype, int isclass, const char* sel) { if (isclass && cltype != NULL) { return ccb_table_find(cltype->cmethods, sel); } else if (cltype != NULL) { return ccb_table_find(cltype->imethods, sel); } else { return NULL; } } ccb_ast_t* ccb_parse_objc_useinitstub(ccb_t* ccb, ccb_ast_t* expr); static ccb_ast_t* ccb_parse_objc_message(ccb_t* ccb) { int isclass = 0; const char* clname = NULL; ccb_data_type_t* cltype = NULL; /* On entry, the '[' token has already been read (but not the target expression). */ ccb_ast_t* target; ccb_lexer_token_t* token = NULL; bool issuper = false; token = ccb_lexer_next(ccb); if (token->type == CCB_LEXER_TOKEN_IDENTIFIER && !strcmp("super", token->string)) { target = ccb_parse_generic(ccb, "self"); issuper = true; if (ccb->oop_ismeta) { cltype = ccb_table_find(ccb_parse_typedefs, ccb->oop_classname); isclass = 1; } } else { ccb_lexer_unget(ccb, token); target = ccb_parse_expression(ccb); } //printf("[] Got AST type 0x100+%d\n", target->type - 0x100); if (target->type == CCB_AST_TYPE_VAR_GLOBAL && ccb_table_find(ccb_parse_typedefs, target->variable.name) != NULL) { // TODO: Actually check if it's a class or not... if (target->variable.isclassobj) { //printf("Sending to a class!\n"); clname = target->variable.name; cltype = ccb_table_find(ccb_parse_typedefs, clname); target = ccb_parse_objc_useinitstub(ccb, target); isclass = 1; } } else if (target->type == CCB_AST_TYPE_VAR_LOCAL && !strcmp("self", target->variable.name)) { //printf("Got self call...\n"); if (ccb->oop_ismeta) { //printf("Using type '%s'\n", ccb->oop_classname); cltype = ccb_table_find(ccb_parse_typedefs, ccb->oop_classname); isclass = 1; } } char* selector = NULL; ccb_ast_t* selast = NULL; ccb_ast_t* lookupast = NULL; ccb_data_type_t* rtype = ccb_ast_data_table[CCB_AST_DATA_ID]; token = ccb_lexer_next(ccb); if (token->type != CCB_LEXER_TOKEN_IDENTIFIER) { ccb_compile_error(ccb, "unexpected token `%s'", ccb_lexer_tokenstr(ccb, token)); } selector = token->string; int maxargs = 4; int nargs = 0; ccb_ast_t* args[4]; ccb_list_t* list = ccb_list_create(); ccb_list_t* tlist = ccb_list_create(); ccb_list_push(list, target); ccb_list_push(tlist, ccb_ast_data_table[CCB_AST_DATA_ID]); token = ccb_lexer_next(ccb); if (ccb_lexer_ispunct(ccb, token, ':')) { char* oldsel = selector; selector = calloc(strlen(selector) + 150, 1); if (selector == NULL) { ccb_compile_error(ccb, "Out of memory?"); return NULL; // Unreachable } strcat(selector, oldsel); strcat(selector, ":"); bool hasargs = true; while (hasargs) { /* Append argument */ if (nargs >= maxargs) { ccb_compile_error(ccb, "Too many arguments", NULL); } ccb_ast_t* a = ccb_parse_expression(ccb); args[nargs] = a; nargs++; ccb_list_push(list, a); ccb_list_push(tlist, a->ctype); /* Check if followed by anotherPart: */ token = ccb_lexer_next(ccb); if (token->type == CCB_LEXER_TOKEN_IDENTIFIER) { strcat(selector, token->string); ccb_parse_expect(ccb, ':'); strcat(selector, ":"); } else { ccb_lexer_unget(ccb, token); token = NULL; hasargs = false; } } //ccb_compile_error(ccb, "messages with arguments are not yet supported", NULL); } else { ccb_lexer_unget(ccb, token); token = NULL; } ccb_parse_expect(ccb, ']'); selast = ccb_ast_new_string(ccb, selector); ccb_list_push(ccb_ast_strings, selast); selast = ccb_parse_runtime_call_2(ccb, "__oop_literal_selector", selast, ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_INT], strlen(selector))); if (issuper) { lookupast = ccb_parse_runtime_call_3(ccb, "__oop_superfunc", ccb_parse_generic(ccb, ccb->oop_classname), ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_INT], ccb->oop_ismeta?1:0), selast); } else { lookupast = ccb_parse_runtime_call_2(ccb, "__oop_methodfunc", target, selast); } ccb_data_type_t* mtype = NULL; if (!isclass) { if (target->ctype->type == CCB_TYPE_POINTER && target->ctype->pointer->imethods != NULL) { cltype = target->ctype->pointer; } } mtype = ccb_parse_objx_methodtype(ccb, cltype, isclass, selector); if (mtype == NULL) { ccb_compile_warn(ccb, "No declaration can be found for %s method '%s', assuming 'id' return type", isclass ? "class" : "instance", selector); return ccb_ast_ptrcall(ccb, rtype, "[...]", lookupast, list, tlist, 0);//101); } else { return ccb_ast_ptrcall(ccb, mtype->returntype, "[...]", lookupast, list, mtype->parameters, 0);//101); } /*switch (nargs) { case 0: return ccb_parse_runtime_call_2(ccb, "__oop_send0", target, selast); case 1: return ccb_parse_runtime_call_3(ccb, "__oop_send1", target, selast, args[0]); case 2: return ccb_parse_runtime_call_4(ccb, "__oop_send2", target, selast, args[0], args[1]); / * TODO... case 3: return ccb_parse_runtime_call_5(ccb, "__oop_send3", target, selast, args[0], args[1], args[2]); case 4: return ccb_parse_runtime_call_6(ccb, "__oop_send4", target, selast, args[0], args[1], args[2], args[3]); * / default: ccb_compile_error(ccb, "Language Extension Error: Unsupported number of parameters to message send", NULL); }*/ } #endif #ifdef CCB_X_OBJC static ccb_ast_t* ccb_parse_objc_selector(ccb_t* ccb) { /* On entry, the '@' and 'selector' have been read already (but not the '('). */ ccb_lexer_token_t* token = NULL; char* selector = NULL; char* tmpstr = NULL; ccb_ast_t* selast = NULL; ccb_parse_expect(ccb, '('); token = ccb_lexer_next(ccb); if (token->type != CCB_LEXER_TOKEN_IDENTIFIER) { ccb_compile_error(ccb, "unexpected token `%s'", ccb_lexer_tokenstr(ccb, token)); } selector = calloc(strlen(token->string) + 100, 1); strcat(selector, token->string); token = ccb_lexer_next(ccb); if (ccb_lexer_ispunct(ccb, token, ':')) { tmpstr = selector; selector = strcat(tmpstr, ":"); //free(tmpstr); XXX Wrong place? while ((token = ccb_lexer_next(ccb))->type == CCB_LEXER_TOKEN_IDENTIFIER) { tmpstr = selector; selector = strcat(tmpstr, token->string); //free(tmpstr); ccb_parse_expect(ccb, ':'); tmpstr = selector; selector = strcat(tmpstr, ":"); //free(tmpstr); } } ccb_lexer_unget(ccb, token); token = NULL; ccb_parse_expect(ccb, ')'); selast = ccb_ast_new_string(ccb, selector); ccb_list_push(ccb_ast_strings, selast); return ccb_parse_runtime_call_2(ccb, "__oop_literal_selector", selast, ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_INT], strlen(selector))); } #endif static ccb_ast_t* ccb_parse_number_integer(ccb_t* ccb, const char* string) { const char* p = string; int base = 10; if (!ccb_strncasecmp(string, "0x", 2)) { base = 16; p++; p++; } else if (string[0] == '0' && string[1] != '\0') { base = 8; p++; } while (isxdigit(*p)) { if (base == 10 && isalpha(*p)) ccb_compile_error(ccb, "invalid character in decimal literal"); if (base == 8 && !('0' <= *p && *p <= '7')) ccb_compile_error(ccb, "invalid character in octal literal"); p++; } if (!ccb_strcasecmp(p, "u")) return ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_UINT], strtol(string, NULL, base)); if (!ccb_strcasecmp(p, "l")) return ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_LONG], strtol(string, NULL, base)); if (!ccb_strcasecmp(p, "ul") || !ccb_strcasecmp(p, "lu")) return ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_ULONG], strtoul(string, NULL, base)); if (!ccb_strcasecmp(p, "ll")) return ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_LLONG], strtoll(string, NULL, base)); if (!ccb_strcasecmp(p, "ull") || !ccb_strcasecmp(p, "llu")) return ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_ULLONG], strtoull(string, NULL, base)); if (*p != '\0') ccb_compile_error(ccb, "invalid suffix for literal"); // TODO: Was initially using "long long"/strtoll but decided to opt for long just in case that was causing issues // I don't think that was the problem (long long always == long?) so I changed it back, but left a todo just in case. long long value = strtoll(string, NULL, base); return (/*(value & ~(long)UINT_MAX) != 0*/value != (long long) (int) value) ? ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_LONG], value) : ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_INT], value); } static ccb_ast_t* ccb_parse_number_floating(ccb_t* ccb, const char* string) { const char* p = string; char* end; while (p[1]) p++; ccb_ast_t* ast; if (*p == 'l' || *p == 'L') ast = ccb_ast_new_floating(ccb, ccb_ast_data_table[CCB_AST_DATA_LDOUBLE], strtold(string, &end)); else if (*p == 'f' || *p == 'F') ast = ccb_ast_new_floating(ccb, ccb_ast_data_table[CCB_AST_DATA_FLOAT], strtof(string, &end)); else { ast = ccb_ast_new_floating(ccb, ccb_ast_data_table[CCB_AST_DATA_DOUBLE], strtod(string, &end)); p++; } if (end != p) ccb_compile_error(ccb, "malformatted float literal"); return ast; } static ccb_ast_t* ccb_parse_number(ccb_t* ccb, const char* string) { /* Fix to detect hex before checking if the number has an "e". */ if (string[0] == '0' && string[1] == 'x') { return ccb_parse_number_integer(ccb, string); } return strpbrk(string, ".pe") ? ccb_parse_number_floating(ccb, string) : ccb_parse_number_integer(ccb, string); } static ccb_data_type_t* ccb_parse_sizeoflike_type(ccb_t* ccb, const char* debugname, bool typename, bool allowvoid); #ifdef CCB_X_OBJX static ccb_ast_t* ccb_parse_objx_typeof(ccb_t* ccb, ccb_data_type_t* t); // TODO: Predeclaration required for self-compile static ccb_ast_t* ccb_parse_objx_typeof(ccb_t* ccb, ccb_data_type_t* t) { char* name = NULL; bool isflt = false; switch (t->type) { case CCB_TYPE_VOID: return ccb_parse_runtime_call_0(ccb, "__oop_typeof_void"); case CCB_TYPE_CHAR: name = "char"; break; case CCB_TYPE_SHORT: name = "short"; break; case CCB_TYPE_INT: name = "int"; break; case CCB_TYPE_LONG: name = "long"; break; case CCB_TYPE_LLONG: name = "long long"; break; case CCB_TYPE_FLOAT: name = "float"; isflt = true; break; case CCB_TYPE_DOUBLE: name = "double"; isflt = true; break; case CCB_TYPE_LDOUBLE: name = "long double"; isflt = true; break; case CCB_TYPE_ARRAY: return ccb_parse_runtime_call_2(ccb, "__oop_typeof_array", ccb_parse_objx_typeof(ccb, t->pointer), ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_LONG], t->length)); case CCB_TYPE_POINTER: return ccb_parse_runtime_call_1(ccb, "__oop_typeof_pointer", ccb_parse_objx_typeof(ccb, t->pointer)); case CCB_TYPE_STRUCTURE: if (t->classname != NULL) { ccb_ast_t* strast = ccb_ast_new_string(ccb, t->classname); ccb_list_push(ccb_ast_strings, strast); return ccb_parse_runtime_call_1(ccb, "__oop_typeof_dynamic", strast); } ccb_compile_error(ccb, "can't get @typeof structures yet"); case CCB_TYPE_FUNCTION: { //ccb_compile_error(ccb, "can't get @typeof functions yet"); ccb_ast_t* params; if (t->parameters == NULL || ccb_list_length(t->parameters) == 0) { params = ccb_parse_runtime_call_1(ccb, "__oop_typeof_params_create", ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_LONG], 0)); } else { params = ccb_parse_runtime_call_1(ccb, "__oop_typeof_params_create", ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_LONG], ccb_list_length(t->parameters))); ccb_list_iterator_t* iter = ccb_list_iterator(t->parameters); while (!ccb_list_iterator_end(iter)) { params = ccb_parse_runtime_call_2(ccb, "__oop_typeof_params_with", params, ccb_parse_objx_typeof(ccb, ccb_list_iterator_next(iter))); } } return ccb_parse_runtime_call_3(ccb, "__oop_typeof_function", ccb_parse_objx_typeof(ccb, t->returntype), params, ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_LONG], t->hasdots ? true : t->sign)); } default: ccb_compile_error(ccb, "bad type code (%d)", t->type); } // If the switch fell through to here then it must be a simple integer or float type // The name and isflt variables should have been set during or before the switch statement. ccb_ast_t* strast = ccb_ast_new_string(ccb, name); ccb_list_push(ccb_ast_strings, strast); return ccb_parse_runtime_call_4(ccb, isflt ? "__oop_typeof_float" : "__oop_typeof_integer", ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_LONG], t->size * 8), ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_LONG], isflt ? true : t->sign), strast, ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_LONG], strlen(name))); } #endif static ccb_ast_t* ccb_parse_pedanticbox(ccb_t* ccb, ccb_ast_t* v) { ccb_ast_t* t = ccb_parse_objx_typeof(ccb, v->ctype); switch (v->ctype->type) { case CCB_TYPE_VOID: return ccb_parse_runtime_call_1(ccb, "__oop_box_void", t); case CCB_TYPE_CHAR: case CCB_TYPE_SHORT: case CCB_TYPE_INT: case CCB_TYPE_LONG: case CCB_TYPE_LLONG: return ccb_parse_runtime_call_2(ccb, "__oop_box_i", t, v); case CCB_TYPE_FLOAT: case CCB_TYPE_DOUBLE: case CCB_TYPE_LDOUBLE: return ccb_parse_runtime_call_2(ccb, "__oop_box_f", t, v); case CCB_TYPE_ARRAY: case CCB_TYPE_POINTER: case CCB_TYPE_FUNCTION: return ccb_parse_runtime_call_2(ccb, "__oop_box_p", t, v); case CCB_TYPE_STRUCTURE: ccb_compile_error(ccb, "can't fully box structures yet"); default: ccb_compile_error(ccb, "bad type code (%d)", t->type); } } static ccb_ast_t* ccb_parse_expression_primary(ccb_t* ccb) { ccb_lexer_token_t* token; ccb_ast_t* ast; //fprintf(stderr, "TESTING\n"); if (!(token = ccb_lexer_next(ccb))) return NULL; #ifdef CCB_X_OBJC bool isobjc = false; ccb_lexer_token_t* attoken = NULL; if (token->type == CCB_LEXER_TOKEN_PUNCT && token->character == '@') { isobjc = true; attoken = token; if (!(token = ccb_lexer_next(ccb))) { ccb_lexer_unget(ccb, attoken); } //ccb_compile_error(ccb, "Got objc literal"); } #endif switch (token->type) { case CCB_LEXER_TOKEN_IDENTIFIER: #ifdef CCB_X_OBJC if (isobjc) { if (strcmp(token->string, "selector") == 0) { return ccb_parse_objc_selector(ccb); #ifdef CCB_X_OBJX } else if (strcmp(token->string, "typeof") == 0) { return ccb_parse_objx_typeof(ccb, ccb_parse_sizeoflike_type(ccb, "@typeof", false, true)); #endif } else { ccb_lexer_unget(ccb, attoken); ccb_lexer_unget(ccb, token); return NULL; } } #endif return ccb_parse_generic(ccb, token->string); case CCB_LEXER_TOKEN_NUMBER: #ifdef CCB_X_OBJC if (isobjc) { ast = ccb_parse_number(ccb, token->string); if (ast->type == CCB_TYPE_FLOAT || ast->type == CCB_TYPE_DOUBLE || ast->type == CCB_TYPE_LDOUBLE) { return ccb_parse_runtime_call_1(ccb, "__oop_literal_float", ast); } else { return ccb_parse_runtime_call_1(ccb, "__oop_literal_integer", ast); } } #endif return ccb_parse_number(ccb, token->string); case CCB_LEXER_TOKEN_CHAR: #ifdef CCB_X_OBJC if (isobjc) { return ccb_parse_runtime_call_1(ccb, "__oop_literal_character", ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_CHAR], token->character)); } #endif return ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_CHAR], token->character); case CCB_LEXER_TOKEN_STRING: ast = ccb_ast_new_string(ccb, token->string); ccb_list_push(ccb_ast_strings, ast); #ifdef CCB_X_OBJC if (isobjc) { return ccb_parse_runtime_call_2(ccb, "__oop_literal_stringn", ast, ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_INT], strlen(token->string))); } #endif return ast; case CCB_LEXER_TOKEN_PUNCT: #ifdef CCB_X_OBJC if (token->character == '[') { if (isobjc) { ccb_ast_t* prevlist = ccb_parse_runtime_call_0(ccb, "__oop_literal_emptylist"); ccb_ast_t* tmp; while ((tmp = ccb_parse_expression(ccb)) != NULL) { tmp = ccb_parse_pedanticbox(ccb, tmp); prevlist = ccb_parse_runtime_call_2(ccb, "__oop_literal_listwith", prevlist, tmp); if (ccb_lexer_ispunct(ccb, ccb_lexer_peek(ccb), ',')) { ccb_lexer_next(ccb); // Discard the comma } } ccb_parse_expect(ccb, ']'); return prevlist; } else { return ccb_parse_objc_message(ccb); } } else { ccb_lexer_unget(ccb, attoken); ccb_lexer_unget(ccb, token); return NULL; } #else ccb_lexer_unget(ccb, token); return NULL; #endif default: break; } ccb_compile_error(ccb, "Internal error: parse_expression_primary"); return NULL; } static ccb_ast_t* ccb_parse_expression_subscript(ccb_t* ccb, ccb_ast_t* ast) { ccb_ast_t* subscript = ccb_parse_expression(ccb); ccb_parse_expect(ccb, ']'); ccb_ast_t* node = ccb_ast_new_binary(ccb, '+', ast, subscript); return ccb_ast_new_unary(ccb, CCB_AST_TYPE_DEREFERENCE, node->ctype->pointer, node); } static ccb_data_type_t* ccb_parse_sizeoflike_type(ccb_t* ccb, const char* debugname, bool typename, bool allowvoid) { ccb_lexer_token_t* token = ccb_lexer_next(ccb); if (typename && ccb_parse_type_check(ccb, token)) { ccb_lexer_unget(ccb, token); ccb_data_type_t* type; ccb_parse_function_parameter(ccb, &type, NULL, true); return type; } if (ccb_lexer_ispunct(ccb, token, '(')) { ccb_data_type_t* next = ccb_parse_sizeoflike_type(ccb, debugname, true, allowvoid); ccb_parse_expect(ccb, ')'); token = ccb_lexer_next(ccb); if (ccb_lexer_ispunct(ccb, token, '{')) { ccb_parse_initializer_declaration(ccb, next); ccb_parse_expect(ccb, '}'); } else { ccb_lexer_unget(ccb, token); } return next; } ccb_lexer_unget(ccb, token); ccb_ast_t* expression = ccb_parse_expression_unary(ccb); if (!allowvoid && expression->ctype->size == 0) ccb_compile_error(ccb, "%s(void) illegal", debugname); return expression->ctype; } static ccb_ast_t* ccb_parse_expression_compound_literal(ccb_t* ccb, ccb_data_type_t* type) { char* name = ccb_ast_label(ccb); ccb_list_t* list = ccb_parse_initializer_declaration(ccb, type); ccb_parse_expect(ccb, '}'); ccb_ast_t* node = ccb_ast_variable_local(ccb, type, name); node->variable.init = list; return node; } static ccb_ast_t* ccb_parse_expression_unary_cast(ccb_t* ccb) { ccb_data_type_t* basetype = ccb_parse_declaration_specification(ccb, NULL); ccb_data_type_t* casttype = ccb_parse_declarator(ccb, NULL, basetype, NULL, CCB_CDECL_CAST); ccb_parse_expect(ccb, ')'); ccb_lexer_token_t* token = ccb_lexer_next(ccb); if (ccb_lexer_ispunct(ccb, token, '{')) return ccb_parse_expression_compound_literal(ccb, casttype); ccb_lexer_unget(ccb, token); ccb_ast_t* expression = ccb_parse_expression_intermediate(ccb, 2); //ccb_parse_expression_unary(ccb); return ccb_ast_new_unary(ccb, CCB_AST_TYPE_EXPRESSION_CAST, casttype, expression); } static int ccb_parse_expression_unary_innerisfloat(ccb_t* ccb, ccb_data_type_t* t) { switch (t->type) { case CCB_TYPE_FLOAT: case CCB_TYPE_DOUBLE: case CCB_TYPE_LDOUBLE: return 1; default: return 0; } } static int ccb_parse_expression_unary_innerispointer(ccb_t* ccb, ccb_data_type_t* t) { switch (t->type) { case CCB_TYPE_POINTER: case CCB_TYPE_FUNCTION: case CCB_TYPE_ID: return 1; default: return 0; } } static int ccb_parse_expression_unary_innerisint(ccb_t* ccb, ccb_data_type_t* t) { switch (t->type) { case CCB_TYPE_CHAR: case CCB_TYPE_SHORT: case CCB_TYPE_INT: case CCB_TYPE_LONG: case CCB_TYPE_LLONG: return 1; default: return 0; } } static int ccb_parse_expression_unary_innerissigned(ccb_t* ccb, ccb_data_type_t* t) { switch (t->type) { case CCB_TYPE_FLOAT: case CCB_TYPE_DOUBLE: case CCB_TYPE_LDOUBLE: return 1; case CCB_TYPE_CHAR: case CCB_TYPE_SHORT: case CCB_TYPE_INT: case CCB_TYPE_LONG: case CCB_TYPE_LLONG: return t->sign ? 1 : 0; default: return 0; } } static ccb_ast_t* ccb_parse_expression_unary(ccb_t* ccb) { ccb_lexer_token_t* token = ccb_lexer_next(ccb); if (!token) ccb_compile_error(ccb, "unexpected end of input"); if (ccb_parse_identifier_check(ccb, token, "sizeof")) { return ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_LONG], ccb_parse_sizeoflike_type(ccb, "sizeof", false, false)->size); } /* These builtins can be thought of as periphery versions of "sizeof", which instead of size just give basic information for debugging macros (). */ if (ccb_parse_identifier_check(ccb, token, "__builtin_isfloat")) { return ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_LONG], ccb_parse_expression_unary_innerisfloat(ccb, ccb_parse_sizeoflike_type(ccb, "__builtin_isfloat", false, false))); } if (ccb_parse_identifier_check(ccb, token, "__builtin_isint")) { return ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_LONG], ccb_parse_expression_unary_innerisint(ccb, ccb_parse_sizeoflike_type(ccb, "__builtin_isint", false, false))); } if (ccb_parse_identifier_check(ccb, token, "__builtin_ispointer")) { return ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_LONG], ccb_parse_expression_unary_innerispointer(ccb, ccb_parse_sizeoflike_type(ccb, "__builtin_ispointer", false, false))); } if (ccb_parse_identifier_check(ccb, token, "__builtin_issigned")) { return ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_LONG], ccb_parse_expression_unary_innerisint(ccb, ccb_parse_sizeoflike_type(ccb, "__builtin_issigned", false, false))); } if (ccb_lexer_ispunct(ccb, token, '(')) { if (ccb_parse_type_check(ccb, ccb_lexer_peek(ccb))) return ccb_parse_expression_unary_cast(ccb); ccb_ast_t* next = ccb_parse_expression(ccb); ccb_parse_expect(ccb, ')'); // TODO: Check for complex function PTRCALL here or somewhere else? return next; } if (ccb_lexer_ispunct(ccb, token, '&')) { ccb_ast_t* operand = ccb_parse_expression_intermediate(ccb, 3); if (operand->type == CCB_AST_TYPE_DEREFERENCE) { return operand->unary.operand; } ccb_parse_semantic_lvalue(ccb, operand, true); return ccb_ast_new_unary(ccb, CCB_AST_TYPE_ADDRESS, ccb_ast_pointer(ccb, operand->ctype), operand); } if (ccb_lexer_ispunct(ccb, token, '!')) { ccb_ast_t* operand = ccb_parse_expression_intermediate(ccb, 3); return ccb_ast_new_unary(ccb, '!', ccb_ast_data_table[CCB_AST_DATA_INT], operand); } if (ccb_lexer_ispunct(ccb, token, '-')) { ccb_ast_t* ast = ccb_parse_expression_intermediate(ccb, 3); return ccb_ast_new_binary(ccb, '-', ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_INT], 0), ast); } if (ccb_lexer_ispunct(ccb, token, '~')) { ccb_ast_t* ast = ccb_parse_expression_intermediate(ccb, 3); if (!ccb_ast_type_integer(ccb, ast->ctype)) ccb_compile_error(ccb, "invalid expression `%s'", ccb_ast_string(ccb, ast)); return ccb_ast_new_unary(ccb, '~', ast->ctype, ast); } if (ccb_lexer_ispunct(ccb, token, '*')) { ccb_ast_t* operand = ccb_parse_expression_intermediate(ccb, 3); switch (operand->type) { case CCB_AST_TYPE_VAR_LOCAL: case CCB_AST_TYPE_VAR_GLOBAL: default: //operand = ccb_ast_new_binary(ccb, '+', operand, ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_INT], 0)); //default: break; //ccb_compile_error(ccb, "invalid expression `%s'", ccb_ast_string(ccb, operand)); } ccb_data_type_t* type = ccb_ast_array_convert(ccb, operand->ctype); if (type->type != CCB_TYPE_POINTER) ccb_compile_error(ccb, "expected pointer type, `%s' isn't pointer type", ccb_ast_string(ccb, operand)); return ccb_ast_new_unary(ccb, CCB_AST_TYPE_DEREFERENCE, operand->ctype->pointer, operand); } if (ccb_lexer_ispunct(ccb, token, CCB_LEXER_TOKEN_INCREMENT) || ccb_lexer_ispunct(ccb, token, CCB_LEXER_TOKEN_DECREMENT)) { ccb_ast_t* next = ccb_parse_expression_intermediate(ccb, 3); ccb_parse_semantic_lvalue(ccb, next, false); int operand = ccb_lexer_ispunct(ccb, token, CCB_LEXER_TOKEN_INCREMENT) ? CCB_AST_TYPE_PRE_INCREMENT : CCB_AST_TYPE_PRE_DECREMENT; return ccb_ast_new_unary(ccb, operand, next->ctype, next); } #ifdef CCB_X_OBJC // @expressions may be supported in the future. Only raw literals (@"foo", @123, etc.) are supported right now //if (ccb_lexer_ispunct(ccb, token, '@')) { // ccb_ast_t *ast = ccb_parse_expression_intermediate(3); // return ccb_ast_new_unary('@', ast->ctype, ast); //} #endif ccb_lexer_unget(ccb, token); return ccb_parse_expression_primary(ccb); } static ccb_ast_t* ccb_parse_expression_condition(ccb_t* ccb, ccb_ast_t* condition) { ccb_ast_t* then = ccb_parse_expression(ccb); ccb_parse_expect(ccb, ':'); ccb_ast_t* last = ccb_parse_expression(ccb); // TODO: This should probably use a similar mechanism as with binops to check result type return ccb_ast_ternary(ccb, then->ctype, condition, then, last); } static ccb_ast_t* ccb_parse_structure_field(ccb_t* ccb, ccb_ast_t* structure) { if (structure->ctype->type != CCB_TYPE_STRUCTURE) ccb_compile_error(ccb, "expected structure type, `%s' isn't structure type", ccb_ast_string(ccb, structure)); ccb_lexer_token_t* name = ccb_lexer_next(ccb); if (name->type != CCB_LEXER_TOKEN_IDENTIFIER) ccb_compile_error(ccb, "expected field name, got `%s' instead", ccb_lexer_tokenstr(ccb, name)); ccb_data_type_t* field = ccb_table_find(structure->ctype->fields, name->string); if (!field) ccb_compile_error(ccb, "structure has no such field `%s'", ccb_lexer_tokenstr(ccb, name)); return ccb_ast_structure_reference(ccb, field, structure, name->string); } static int ccb_parse_operation_compound_operator(ccb_t* ccb, ccb_lexer_token_t* token) { if (token->type != CCB_LEXER_TOKEN_PUNCT) return 0; switch (token->punct) { case CCB_LEXER_TOKEN_COMPOUND_RSHIFT: return CCB_LEXER_TOKEN_RSHIFT; case CCB_LEXER_TOKEN_COMPOUND_LSHIFT: return CCB_LEXER_TOKEN_LSHIFT; case CCB_LEXER_TOKEN_COMPOUND_ADD: return '+'; case CCB_LEXER_TOKEN_COMPOUND_AND: return '&'; case CCB_LEXER_TOKEN_COMPOUND_DIV: return '/'; case CCB_LEXER_TOKEN_COMPOUND_MOD: return '%'; case CCB_LEXER_TOKEN_COMPOUND_MUL: return '*'; case CCB_LEXER_TOKEN_COMPOUND_OR: return '|'; case CCB_LEXER_TOKEN_COMPOUND_SUB: return '-'; case CCB_LEXER_TOKEN_COMPOUND_XOR: return '^'; default: return 0; } return -1; } static int ccb_parse_operation_reclassify(ccb_t* ccb, int punct) { switch (punct) { case CCB_LEXER_TOKEN_LSHIFT: return CCB_AST_TYPE_LSHIFT; case CCB_LEXER_TOKEN_RSHIFT: return CCB_AST_TYPE_RSHIFT; case CCB_LEXER_TOKEN_EQUAL: return CCB_AST_TYPE_EQUAL; case CCB_LEXER_TOKEN_GEQUAL: return CCB_AST_TYPE_GEQUAL; case CCB_LEXER_TOKEN_LEQUAL: return CCB_AST_TYPE_LEQUAL; case CCB_LEXER_TOKEN_NEQUAL: return CCB_AST_TYPE_NEQUAL; case CCB_LEXER_TOKEN_AND: return CCB_AST_TYPE_AND; case CCB_LEXER_TOKEN_OR: return CCB_AST_TYPE_OR; default: break; } return punct; } static bool ccb_parse_operation_integer_check(ccb_t* ccb, int operation) { return operation == '^' || operation == '%' || operation == CCB_LEXER_TOKEN_LSHIFT || operation == CCB_LEXER_TOKEN_RSHIFT; } static ccb_ast_t* ccb_parse_expression_intermediate(ccb_t* ccb, int precision) { ccb_ast_t* ast; ccb_ast_t* next; if (!(ast = ccb_parse_expression_unary(ccb))) return NULL; for (;;) { ccb_lexer_token_t* token = ccb_lexer_next(ccb); if (token->type != CCB_LEXER_TOKEN_PUNCT) { ccb_lexer_unget(ccb, token); return ast; } int pri = ccb_parse_operator_priority(ccb, token); if (pri < 0 || precision <= pri) { ccb_lexer_unget(ccb, token); return ast; } if (ccb_lexer_ispunct(ccb, token, '?')) { ast = ccb_parse_expression_condition(ccb, ast); continue; } if (ccb_lexer_ispunct(ccb, token, '.')) { ast = ccb_parse_structure_field(ccb, ast); continue; } if (ccb_lexer_ispunct(ccb, token, CCB_LEXER_TOKEN_ARROW)) { if (ast->ctype->type != CCB_TYPE_POINTER) ccb_compile_error(ccb, "Not a valid pointer type: %s", ccb_ast_string(ccb, ast)); ast = ccb_ast_new_unary(ccb, CCB_AST_TYPE_DEREFERENCE, ast->ctype->pointer, ast); ast = ccb_parse_structure_field(ccb, ast); continue; } if (ccb_lexer_ispunct(ccb, token, '[')) { ast = ccb_parse_expression_subscript(ccb, ast); continue; } if (ccb_lexer_ispunct(ccb, token, CCB_LEXER_TOKEN_INCREMENT) || ccb_lexer_ispunct(ccb, token, CCB_LEXER_TOKEN_DECREMENT)) { ccb_parse_semantic_lvalue(ccb, ast, false); int operand = ccb_lexer_ispunct(ccb, token, CCB_LEXER_TOKEN_INCREMENT) ? CCB_AST_TYPE_POST_INCREMENT : CCB_AST_TYPE_POST_DECREMENT; ast = ccb_ast_new_unary(ccb, operand, ast->ctype, ast); continue; } int compound = ccb_parse_operation_compound_operator(ccb, token); if (ccb_lexer_ispunct(ccb, token, '=') || compound) ccb_parse_semantic_lvalue(ccb, ast, false); next = ccb_parse_expression_intermediate(ccb, pri + !!ccb_parse_semantic_rightassoc(ccb, token)); if (!next) ccb_compile_error(ccb, "Internal error: parse_expression_intermediate (next)"); int operation = compound ? compound : token->punct; int op = ccb_parse_operation_reclassify(ccb, operation); if (ccb_parse_operation_integer_check(ccb, op)) { ccb_parse_semantic_integer(ccb, ast); ccb_parse_semantic_integer(ccb, next); } if (compound) ast = ccb_ast_new_binary(ccb, '=', ast, ccb_ast_new_binary(ccb, op, ast, next)); else ast = ccb_ast_new_binary(ccb, op, ast, next); } return NULL; } static ccb_ast_t* ccb_parse_expression(ccb_t* ccb) { return ccb_parse_expression_intermediate(ccb, 16); } static ccb_ast_t* ccb_parse_expression_withcomma(ccb_t* ccb) { ccb_ast_t* lhs = ccb_parse_expression(ccb); if (lhs == NULL) { return NULL; } ccb_lexer_token_t* t; /* Might as well use the comma operator to implement itself just for demonstration! */ while (t = ccb_lexer_peek(ccb), ccb_lexer_ispunct(ccb, t, ',')) { ccb_lexer_next(ccb); // Discard the comma ccb_ast_t* rhs = ccb_parse_expression(ccb); if (rhs == NULL) { ccb_compile_error(ccb, "Expected expression following comma"); return NULL; } lhs = ccb_ast_new_comma(ccb, CCB_AST_TYPE_EXPRESSION_COMMA, lhs, rhs); } return lhs; } static bool ccb_parse_type_check(ccb_t* ccb, ccb_lexer_token_t* token) { if (token->type != CCB_LEXER_TOKEN_IDENTIFIER) return false; static const char* keywords[] = { "char", "short", "int", "long", "float", "double", "struct", "union", "signed", "unsigned", "enum", "void", "typedef", "extern", "static", "auto", "register", "const", "volatile", "inline", "restrict"/*, "__asm", "__naked"*/ #ifdef CCB_X_OBJC , "id" #endif }; for (int i = 0; i < sizeof(keywords) / sizeof(const char* /* NOTE: keywords[0] doesn't self-compile */); i++) if (!strcmp(keywords[i], token->string)) return true; if (ccb_table_find(ccb_parse_typedefs, token->string)) return true; return false; } /* struct / union */ static char* ccb_parse_memory_tag(ccb_t* ccb) { ccb_lexer_token_t* token = ccb_lexer_next(ccb); if (token->type == CCB_LEXER_TOKEN_IDENTIFIER) return token->string; ccb_lexer_unget(ccb, token); return NULL; } static int ccb_parse_memory_fields_padding(ccb_t* ccb, int offset, int size) { size = CCB_MIN(size, (int)ccb_target_alignment(ccb)); return (offset % size == 0) ? 0 : size - offset % size; } static void ccb_parse_memory_fields_squash(ccb_t* ccb, ccb_table_t* table, ccb_data_type_t* unnamed, int offset) { for (ccb_list_iterator_t* it = ccb_list_iterator(ccb_table_keys(unnamed->fields)); !ccb_list_iterator_end(it); ) { char* name = ccb_list_iterator_next(it); ccb_data_type_t* type = ccb_ast_type_copy(ccb, ccb_table_find(unnamed->fields, name)); type->offset += offset; ccb_table_insert(table, name, type); } } static ccb_table_t* ccb_parse_memory_fields(ccb_t* ccb, int* rsize, bool isstruct) { ccb_lexer_token_t* token = ccb_lexer_next(ccb); if (!ccb_lexer_ispunct(ccb, token, '{')) { ccb_lexer_unget(ccb, token); return NULL; } int offset = *rsize;// TODO: Check that this is always initialised correctly int maxsize = 0; ccb_table_t* table = ccb_table_create(NULL); for (;;) { if (!ccb_parse_type_check(ccb, ccb_lexer_peek(ccb))) break; ccb_data_type_t* basetype = ccb_parse_declaration_specification(ccb, NULL); if (basetype->type == CCB_TYPE_STRUCTURE && ccb_lexer_ispunct(ccb, ccb_lexer_peek(ccb), ';')) { ccb_lexer_next(ccb); /* Skip */ ccb_parse_memory_fields_squash(ccb, table, basetype, offset); if (isstruct) offset += basetype->size; else maxsize = CCB_MAX(maxsize, basetype->size); continue; } for (;;) { char* name; ccb_data_type_t* fieldtype = ccb_parse_declarator(ccb, &name, basetype, NULL, CCB_CDECL_PARAMETER); ccb_parse_semantic_notvoid(ccb, fieldtype); if (isstruct) { //if (fieldtype->length < 1) {ccb_compile_error(ccb, "Field type length is not set (internal error)");} //if (fieldtype->type == CCB_TYPE_ARRAY) { // fprintf(stderr, "### SPECIAL HANDLING OF FIELD '%s' size %d length %d ptrsize %d\n", name, fieldtype->size, fieldtype->length, fieldtype->pointer->size); // offset += ccb_parse_memory_fields_padding(ccb, offset, fieldtype->size / (fieldtype->length < 1 ? 1 : fieldtype->length)); //, fieldtype->pointer->size); //} else { offset += ccb_parse_memory_fields_padding(ccb, offset, fieldtype->size /* / (fieldtype->length < 1 ? 1 : fieldtype->length)*/); //} fieldtype = ccb_ast_structure_field(ccb, fieldtype, offset); offset += fieldtype->size; } else { maxsize = CCB_MAX(maxsize, fieldtype->size); fieldtype = ccb_ast_structure_field(ccb, fieldtype, 0); } ccb_table_insert(table, name, fieldtype); token = ccb_lexer_next(ccb); if (ccb_lexer_ispunct(ccb, token, ',')) continue; ccb_lexer_unget(ccb, token); ccb_parse_expect(ccb, ';'); break; } } ccb_parse_expect(ccb, '}'); *rsize = isstruct ? offset : maxsize; return table; } static ccb_data_type_t* ccb_parse_tag_definition(ccb_t* ccb, ccb_table_t* table, bool isstruct, int initialsize, char* pretag) { char* tag = ((pretag == NULL) ? ccb_parse_memory_tag(ccb) : pretag); int size = initialsize; ccb_table_t* fields = ((size < 0) ? NULL : ccb_parse_memory_fields(ccb, &size, isstruct)); ccb_data_type_t* r; if (tag) { if (!(r = ccb_table_find(table, tag))) { r = ccb_ast_structure_new(ccb, NULL, 0, isstruct); ccb_table_insert(table, tag, r); } } else { r = ccb_ast_structure_new(ccb, NULL, 0, isstruct); if (tag) ccb_table_insert(table, tag, r); } if (r && !fields) return r; if (r && fields) { r->fields = fields; r->size = size; /*while (isstruct && r->size % ccb_target_alignment(ccb) != 0) { r->size++; }*/ return r; } return r; } /* enum */ static ccb_data_type_t* ccb_parse_enumeration(ccb_t* ccb) { ccb_lexer_token_t* token = ccb_lexer_next(ccb); if (token->type == CCB_LEXER_TOKEN_IDENTIFIER) token = ccb_lexer_next(ccb); if (!ccb_lexer_ispunct(ccb, token, '{')) { ccb_lexer_unget(ccb, token); return ccb_ast_data_table[CCB_AST_DATA_INT]; } int accumulate = 0; for (;;) { token = ccb_lexer_next(ccb); if (ccb_lexer_ispunct(ccb, token, '}')) break; if (token->type != CCB_LEXER_TOKEN_IDENTIFIER) ccb_compile_error(ccb, "NOPE"); char* name = token->string; token = ccb_lexer_next(ccb); if (ccb_lexer_ispunct(ccb, token, '=')) accumulate = ccb_parse_evaluate(ccb, ccb_parse_expression(ccb)); else ccb_lexer_unget(ccb, token); ccb_ast_t* constval = ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_INT], accumulate++); ccb_table_insert(ccb_ast_localenv ? ccb_ast_localenv : ccb_ast_globalenv, name, constval); //fprintf(stderr, "INSERTED '%s' into %s\n", name, ccb_ast_localenv ? "locals" : "globals"); token = ccb_lexer_next(ccb); if (ccb_lexer_ispunct(ccb, token, ',')) continue; if (ccb_lexer_ispunct(ccb, token, '}')) break; ccb_compile_error(ccb, "NOPE!"); } return ccb_ast_data_table[CCB_AST_DATA_INT]; } /* initializer */ static void ccb_parse_assign_string(ccb_t* ccb, ccb_list_t* init, ccb_data_type_t* type, char* p, int offset) { if (type->length == -1) type->length = type->size = strlen(p) + 1; int i = 0; for (; i < type->length && *p; i++) { ccb_list_push( init, ccb_ast_initializer(ccb, ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_CHAR], *p++), ccb_ast_data_table[CCB_AST_DATA_CHAR], offset + i ) ); } for (; i < type->length; i++) { ccb_list_push( init, ccb_ast_initializer(ccb, ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_CHAR], 0), ccb_ast_data_table[CCB_AST_DATA_CHAR], offset + i ) ); } } static bool ccb_parse_brace_maybe(ccb_t* ccb) { ccb_lexer_token_t* token = ccb_lexer_next(ccb); if (ccb_lexer_ispunct(ccb, token, '{')) return true; ccb_lexer_unget(ccb, token); return false; } static void ccb_parse_commaskip_maybe(ccb_t* ccb) { ccb_lexer_token_t* token = ccb_lexer_next(ccb); if (!ccb_lexer_ispunct(ccb, token, ',')) ccb_lexer_unget(ccb, token); } static void ccb_parse_brace_skipto(ccb_t* ccb) { for (;;) { ccb_lexer_token_t* token = ccb_lexer_next(ccb); if (ccb_lexer_ispunct(ccb, token, '}')) return; if (ccb_lexer_ispunct(ccb, token, '.')) { ccb_lexer_next(ccb); ccb_parse_expect(ccb, '='); } else { ccb_lexer_unget(ccb, token); } ccb_ast_t* ignore = ccb_parse_expression_intermediate(ccb, 3); if (!ignore) return; /* TODO aggregate warning */ token = ccb_lexer_next(ccb); if (!ccb_lexer_ispunct(ccb, token, ',')) ccb_lexer_unget(ccb, token); } } static ccb_ast_t* ccb_parse_zero(ccb_t* ccb, ccb_data_type_t* type) { return ccb_ast_type_floating(ccb, type) ? ccb_ast_new_floating(ccb, ccb_ast_data_table[CCB_AST_DATA_DOUBLE], 0.0) : ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_INT], 0); } static void ccb_parse_initializer_list(ccb_t* ccb, ccb_list_t* init, ccb_data_type_t* type, int offset); static void ccb_parse_initializer_element(ccb_t* ccb, ccb_list_t* init, ccb_data_type_t* type, int offset) { if (type == NULL) { ccb_compile_error(ccb, "ICE: %s (NULL type?)", __func__); } if (type->type == CCB_TYPE_ARRAY || type->type == CCB_TYPE_STRUCTURE) ccb_parse_initializer_list(ccb, init, type, offset); else { ccb_ast_t* expression = ccb_parse_expression_intermediate(ccb, 3); if (expression == NULL) { ccb_compile_error(ccb, "ICE: %s (NULL expression?)", __func__); } ccb_ast_result_type(ccb, '=', type, expression->ctype); ccb_list_push(init, ccb_ast_initializer(ccb, expression, type, offset)); } } static void ccb_parse_initializer_zero(ccb_t* ccb, ccb_list_t* init, ccb_data_type_t* type, int offset); // TODO: Predeclaration required for self-compile static void ccb_parse_initializer_zero(ccb_t* ccb, ccb_list_t* init, ccb_data_type_t* type, int offset) { if (type->type == CCB_TYPE_STRUCTURE) { ccb_list_iterator_t* it = ccb_list_iterator(ccb_table_keys(type->fields)); while (!ccb_list_iterator_end(it)) { char* fieldname = ccb_list_iterator_next(it); ccb_data_type_t* fieldtype = ccb_table_find(type->fields, fieldname); ccb_parse_initializer_zero(ccb, init, fieldtype, offset + fieldtype->offset); if (!type->isstruct) return; } return; } if (type->type == CCB_TYPE_ARRAY) { for (int i = 0; i < type->length; i++) ccb_parse_initializer_zero(ccb, init, type->pointer, offset + i * type->pointer->size); return; } ccb_list_push(init, ccb_ast_initializer(ccb, ccb_parse_zero(ccb, type), type, offset)); } static void ccb_parse_initializer_structure(ccb_t* ccb, ccb_list_t* init, ccb_data_type_t* type, int offset) { bool brace = ccb_parse_brace_maybe(ccb); ccb_list_iterator_t* it = ccb_list_iterator(ccb_table_keys(type->fields)); ccb_table_t* wrote = ccb_table_create(NULL); for (;;) { ccb_lexer_token_t* token = ccb_lexer_next(ccb); if (ccb_lexer_ispunct(ccb, token, '}')) { if (!brace) ccb_lexer_unget(ccb, token); goto complete; } char* fieldname; ccb_data_type_t* fieldtype; if (ccb_lexer_ispunct(ccb, token, '.')) { if (!(token = ccb_lexer_next(ccb)) || token->type != CCB_LEXER_TOKEN_IDENTIFIER) ccb_compile_error(ccb, "invalid designated initializer"); fieldname = token->string; if (!(fieldtype = ccb_table_find(type->fields, fieldname))) ccb_compile_error(ccb, "field doesn't exist in designated initializer"); ccb_parse_expect(ccb, '='); it = ccb_list_iterator(ccb_table_keys(type->fields)); while (!ccb_list_iterator_end(it)) if (!strcmp(fieldname, ccb_list_iterator_next(it))) break; } else { ccb_lexer_unget(ccb, token); if (ccb_list_iterator_end(it)) break; fieldname = ccb_list_iterator_next(it); fieldtype = ccb_table_find(type->fields, fieldname); } if (ccb_table_find(wrote, fieldname)) ccb_compile_error(ccb, "field initialized twice in designated initializer"); ccb_table_insert(wrote, fieldname, (void*)1); ccb_parse_initializer_element(ccb, init, fieldtype, offset + fieldtype->offset); ccb_parse_commaskip_maybe(ccb); if (!type->isstruct) break; } if (brace) ccb_parse_brace_skipto(ccb); complete: it = ccb_list_iterator(ccb_table_keys(type->fields)); while (!ccb_list_iterator_end(it)) { char* fieldname = ccb_list_iterator_next(it); if (ccb_table_find(wrote, fieldname)) continue; ccb_data_type_t* fieldtype = ccb_table_find(type->fields, fieldname); ccb_parse_initializer_zero(ccb, init, fieldtype, offset + fieldtype->offset); } } static void ccb_parse_initializer_array(ccb_t* ccb, ccb_list_t* init, ccb_data_type_t* type, int offset) { bool brace = ccb_parse_brace_maybe(ccb); int size = type->pointer->size; int i; for (i = 0; type->length == -1 || i < type->length; i++) { ccb_lexer_token_t* token = ccb_lexer_next(ccb); if (ccb_lexer_ispunct(ccb, token, '}')) { if (!brace) ccb_lexer_unget(ccb, token); goto complete; } ccb_lexer_unget(ccb, token); ccb_parse_initializer_element(ccb, init, type->pointer, offset + size * i); ccb_parse_commaskip_maybe(ccb); } if (brace) ccb_parse_brace_skipto(ccb); complete: if (type->length == -1) { type->length = i; type->size = size * i; } int testn1 = -1; printf("Type reports length as %ld, -1 is %ld i is %d\n", type->length, -1, i); int itl = type->length; /*if (((int) type->length) == -1) { return; // TODO: This is required due to condition below failing in self-compilation! }*/ /*for (; i < type->length; i++)*/ while(itl != (int)-1 && i < itl) { //printf("Parsing zero %d\n", i); ccb_parse_initializer_zero(ccb, init, type->pointer, offset + size * i); i++; } } static void ccb_parse_initializer_list(ccb_t* ccb, ccb_list_t* init, ccb_data_type_t* type, int offset) { ccb_lexer_token_t* token = ccb_lexer_next(ccb); if (type->type == CCB_TYPE_ARRAY && type->pointer->type == CCB_TYPE_CHAR) { if (token->type == CCB_LEXER_TOKEN_STRING) { ccb_parse_assign_string(ccb, init, type, token->string, offset); return; } if (ccb_lexer_ispunct(ccb, token, '{') && ccb_lexer_peek(ccb)->type == CCB_LEXER_TOKEN_STRING) { ccb_parse_assign_string(ccb, init, type, token->string, offset); ccb_parse_expect(ccb, '}'); return; } } ccb_lexer_unget(ccb, token); if (type->type == CCB_TYPE_ARRAY) ccb_parse_initializer_array(ccb, init, type, offset); else if (type->type == CCB_TYPE_STRUCTURE) ccb_parse_initializer_structure(ccb, init, type, offset); else ccb_compile_error(ccb, "ICE"); } static ccb_list_t* ccb_parse_initializer_declaration(ccb_t* ccb, ccb_data_type_t* type) { ccb_list_t* list = ccb_list_create(); if (type->type == CCB_TYPE_ARRAY || type->type == CCB_TYPE_STRUCTURE) ccb_parse_initializer_list(ccb, list, type, 0); else ccb_list_push(list, ccb_ast_initializer(ccb, ccb_parse_expression(ccb), type, 0)); return list; } /* declarator */ static ccb_data_type_t* ccb_parse_declaration_specification(ccb_t* ccb, ccb_storage_t* rstorage) { ccb_storage_t storage = 0; ccb_lexer_token_t* token = ccb_lexer_peek(ccb); if (!token || token->type != CCB_LEXER_TOKEN_IDENTIFIER) ccb_compile_error(ccb, "internal error in declaration specification parsing"); /* * large declaration specification state machine: * There is six pieces of state to the following state machine * for dealing with all the permutations of declaration * specification. * * 1: The type, most common of course, this is the "base type" * of the declaration. * * 2: The size, in C, types are also size specifiers on types, * e.g short int, long int, long long int, act as 'sizes'. * Short and long are not technically types, people who use * them as is without a type associated with them (like unsigned) * concludes implicit int. There is no situation where a size * specifier would couple with anything but an int type. It * should be noted that there has to be an "unsized" state for * types on their own. * * 3: The Signness/signature, for knowing if the declaration is * signed or unsigned. This isn't actually a boolean state because * there needs to be an unsignness state since the char type is * allowed to have it's signeness implementation-defined. * * 4: constantness * self explanatory * 5: vollatileness * self explanatory * 6: inlineness * self explanatory * * 7: user (can include redundant partial specification), e.g * typedef unsigned int foo; signed foo; <--- what to do? * this also includes enums, unions, and structures. */ // TODO: Get rid of these enums (unclear if they apply locally or globally) enum { kvoid = 1, kchar, kint, kfloat, kdouble #ifdef CCB_X_OBJC , kid #endif } type = 0; enum { kunsize, kshort, klong, kllong } size = kunsize; enum { ksigned = 1, kunsigned } signature = 0; #ifdef FUCC_USEUNUSED bool __attribute__((unused)) kconst = false; bool __attribute__((unused)) kvolatile = false; bool __attribute__((unused)) kinline = false; #else bool kconst = false; bool kvolatile = false; bool kinline = false; #endif ccb_data_type_t* user = NULL; ccb_data_type_t* find = NULL; #define ccb_set_uncheck(STATE, VALUE) \ do { \ STATE = VALUE; \ } while (0) #define ccb_set_check(STATE, VALUE) \ do { \ if (STATE != 0) { \ goto state_machine_error; \ } \ ccb_set_uncheck(STATE, VALUE); \ } while (0) #define ccb_set_state(STATE, VALUE) \ do { \ ccb_set_check(STATE, VALUE); \ switch (size) { \ case kshort: \ if (type != 0 && type != kint) \ goto state_machine_error; \ break; \ case klong: \ if (type != 0 && type != kint && type != kdouble) \ goto state_machine_error; \ break; \ default: \ break; \ } \ if (signature != 0) { \ switch (type) { \ case kvoid: \ case kfloat: \ case kdouble: \ goto state_machine_error; \ break; \ default: \ break; \ } \ } \ if (user && (type != 0 || size != 0 || signature != 0)) \ goto state_machine_error; \ } while (0) #define ccb_set_class(VALUE) \ do { \ ccb_set_check(storage, VALUE); \ } while (0) #define ccb_state_machine_try(THING) \ if (!strcmp(token->string, THING)) for (;;) { token = ccb_lexer_next(ccb); if (!token) ccb_compile_error(ccb, "type specification with unexpected ending"); if (token->type != CCB_LEXER_TOKEN_IDENTIFIER) { ccb_lexer_unget(ccb, token); break; } ccb_state_machine_try("const") kconst = true; else ccb_state_machine_try("volatile") kvolatile = true; else ccb_state_machine_try("inline") kinline = true; else ccb_state_machine_try("typedef") ccb_set_class(CCB_STORAGE_TYPEDEF); else ccb_state_machine_try("extern") ccb_set_class(CCB_STORAGE_EXTERN); else ccb_state_machine_try("static") ccb_set_class(CCB_STORAGE_STATIC); else ccb_state_machine_try("auto") ccb_set_class(CCB_STORAGE_AUTO); else ccb_state_machine_try("register") ccb_set_class(CCB_STORAGE_REGISTER); else ccb_state_machine_try("void") ccb_set_state(type, kvoid); else ccb_state_machine_try("char") ccb_set_state(type, kchar); else ccb_state_machine_try("int") ccb_set_state(type, kint); else ccb_state_machine_try("float") ccb_set_state(type, kfloat); else ccb_state_machine_try("double") ccb_set_state(type, kdouble); #ifdef CCB_X_OBJC else ccb_state_machine_try("id") ccb_set_state(type, kid); #endif else ccb_state_machine_try("signed") ccb_set_state(signature, ksigned); else ccb_state_machine_try("unsigned") ccb_set_state(signature, kunsigned); else ccb_state_machine_try("short") ccb_set_state(size, kshort); else ccb_state_machine_try("struct") ccb_set_state(user, ccb_parse_tag_definition(ccb, ccb_ast_structures, true, 0, NULL)); else ccb_state_machine_try("union") ccb_set_state(user, ccb_parse_tag_definition(ccb, ccb_ast_unions, false, 0, NULL)); else ccb_state_machine_try("enum") ccb_set_state(user, ccb_parse_enumeration(ccb)); else ccb_state_machine_try("long") { switch (size) { case kunsize: ccb_set_state(size, klong); break; case klong: ccb_set_uncheck(size, kllong); break; default: goto state_machine_error; } } else if ((find = ccb_table_find(ccb_parse_typedefs, token->string))) { ccb_set_state(user, find); } else { ccb_lexer_unget(ccb, token); break; } #undef ccb_set_check #undef ccb_set_class #undef ccb_set_state #undef ccb_set_uncheck } if (rstorage) *rstorage = storage; if (user) return user; switch (type) { case kvoid: return ccb_ast_data_table[CCB_AST_DATA_VOID]; case kchar: return ccb_ast_type_create(ccb, CCB_TYPE_CHAR, signature != kunsigned); case kfloat: return ccb_ast_type_create(ccb, CCB_TYPE_FLOAT, false); case kdouble: return ccb_ast_type_create(ccb, (size == klong) ? CCB_TYPE_LDOUBLE : CCB_TYPE_DOUBLE, false ); #ifdef CCB_X_OBJC case kid: return ccb_ast_data_table[CCB_AST_DATA_ID]; #endif default: break; } switch (size) { case kshort: return ccb_ast_type_create(ccb, CCB_TYPE_SHORT, signature != kunsigned); case klong: return ccb_ast_type_create(ccb, CCB_TYPE_LONG, signature != kunsigned); case kllong: return ccb_ast_type_create(ccb, CCB_TYPE_LLONG, signature != kunsigned); default: /* * You also need to deal with implicit int given the right * conditions of the state machine. */ return ccb_ast_type_create(ccb, CCB_TYPE_INT, signature != kunsigned); } ccb_compile_error(ccb, "State machine error (BAD)"); state_machine_error: ccb_compile_error(ccb, "State machine error (GOOD)"); return NULL; } static ccb_data_type_t* ccb_parse_array_dimensions_intermediate(ccb_t* ccb, ccb_data_type_t* basetype); // TODO: Predeclaration required for self-compilation static ccb_data_type_t* ccb_parse_array_dimensions_intermediate(ccb_t* ccb, ccb_data_type_t* basetype) { ccb_lexer_token_t* token = ccb_lexer_next(ccb); if (!ccb_lexer_ispunct(ccb, token, '[')) { ccb_lexer_unget(ccb, token); return NULL; } int dimension = -1; if (!ccb_lexer_ispunct(ccb, ccb_lexer_peek(ccb), ']')) dimension = ccb_parse_evaluate(ccb, ccb_parse_expression(ccb)); ccb_parse_expect(ccb, ']'); ccb_data_type_t* next = ccb_parse_array_dimensions_intermediate(ccb, basetype); if (next) { if (next->length == -1 && dimension == -1) ccb_compile_error(ccb, "Internal error: parse_array_dimensions_intermediate (2)"); return ccb_ast_array(ccb, next, dimension); } return ccb_ast_array(ccb, basetype, dimension); } static ccb_data_type_t* ccb_parse_array_dimensions(ccb_t* ccb, ccb_data_type_t* basetype) { ccb_data_type_t* data = ccb_parse_array_dimensions_intermediate(ccb, basetype); return (data) ? data : basetype; } static void ccb_parse_function_parameter(ccb_t* ccb, ccb_data_type_t** rtype, char** name, bool next) { ccb_data_type_t* basetype; ccb_storage_t storage; basetype = ccb_parse_declaration_specification(ccb, &storage); basetype = ccb_parse_declarator(ccb, name, basetype, NULL, next ? CCB_CDECL_TYPEONLY : CCB_CDECL_PARAMETER); *rtype = ccb_parse_array_dimensions(ccb, basetype); } static ccb_ast_t* ccb_parse_statement_if(ccb_t* ccb) { ccb_lexer_token_t* token; ccb_ast_t* cond; ccb_ast_t* then; ccb_ast_t* last; ccb_parse_expect(ccb, '('); cond = ccb_parse_expression_withcomma(ccb); ccb_parse_expect(ccb, ')'); then = ccb_parse_statement(ccb); token = ccb_lexer_next(ccb); if (!token || token->type != CCB_LEXER_TOKEN_IDENTIFIER || strcmp(token->string, "else")) { ccb_lexer_unget(ccb, token); return ccb_ast_if(ccb, cond, then, NULL); } last = ccb_parse_statement(ccb); return ccb_ast_if(ccb, cond, then, last); } static ccb_ast_t* ccb_parse_statement_declaration_semicolon(ccb_t* ccb) { ccb_lexer_token_t* token = ccb_lexer_next(ccb); if (ccb_lexer_ispunct(ccb, token, ';')) return NULL; ccb_lexer_unget(ccb, token); ccb_list_t* list = ccb_list_create(); ccb_parse_statement_declaration(ccb, list); return ccb_list_shift(list); } static ccb_ast_t* ccb_parse_expression_semicolon(ccb_t* ccb) { ccb_lexer_token_t* token = ccb_lexer_next(ccb); if (ccb_lexer_ispunct(ccb, token, ';')) return NULL; ccb_lexer_unget(ccb, token); ccb_ast_t* read = ccb_parse_expression_withcomma(ccb); ccb_parse_expect(ccb, ';'); return read; } static ccb_ast_t* ccb_parse_statement_for(ccb_t* ccb) { ccb_parse_expect(ccb, '('); ccb_ast_localenv = ccb_table_create(ccb_ast_localenv); ccb_ast_t* init = ccb_parse_statement_declaration_semicolon(ccb); ccb_ast_t* cond = ccb_parse_expression_semicolon(ccb); ccb_ast_t* step = ccb_lexer_ispunct(ccb, ccb_lexer_peek(ccb), ')') ? NULL : ccb_parse_expression_withcomma(ccb); ccb_parse_expect(ccb, ')'); ccb_ast_t* body = ccb_parse_statement(ccb); ccb_ast_localenv = ccb_table_parent(ccb_ast_localenv); return ccb_ast_for(ccb, init, cond, step, body); } static ccb_ast_t* ccb_parse_statement_while(ccb_t* ccb) { ccb_parse_expect(ccb, '('); ccb_ast_t* cond = ccb_parse_expression_withcomma(ccb); ccb_parse_expect(ccb, ')'); ccb_ast_t* body = ccb_parse_statement(ccb); return ccb_ast_while(ccb, cond, body); } static ccb_ast_t* ccb_parse_statement_do(ccb_t* ccb) { ccb_ast_t* body = ccb_parse_statement(ccb); ccb_lexer_token_t* token = ccb_lexer_next(ccb); if (!ccb_parse_identifier_check(ccb, token, "while")) ccb_compile_error(ccb, "expected while for do"); ccb_parse_expect(ccb, '('); ccb_ast_t* cond = ccb_parse_expression_withcomma(ccb); ccb_parse_expect(ccb, ')'); ccb_parse_expect(ccb, ';'); return ccb_ast_do(ccb, cond, body); } static ccb_ast_t* ccb_parse_statement_break(ccb_t* ccb) { ccb_parse_expect(ccb, ';'); return ccb_ast_make(ccb, CCB_AST_TYPE_STATEMENT_BREAK); } static ccb_ast_t* ccb_parse_statement_continue(ccb_t* ccb) { ccb_parse_expect(ccb, ';'); return ccb_ast_make(ccb, CCB_AST_TYPE_STATEMENT_CONTINUE); } static ccb_ast_t* ccb_parse_statement_case(ccb_t* ccb) { int value = ccb_parse_evaluate(ccb, ccb_parse_expression(ccb)); ccb_parse_expect(ccb, ':'); return ccb_ast_case(ccb, value); } static ccb_ast_t* ccb_parse_statement_switch(ccb_t* ccb) { ccb_parse_expect(ccb, '('); ccb_ast_t* expression = ccb_parse_expression_withcomma(ccb); /* TODO lvalueness test propogate?*/ ccb_parse_expect(ccb, ')'); /* Some special handling is added for switch statements leading directly into some case/s with no {...} */ ccb_lexer_token_t* token = ccb_lexer_next(ccb); bool wascase = ccb_parse_identifier_check(ccb, token, "case"); if (wascase) { /* If we go straight into some case/s, then we basically create an implicit compound statement assembling * those cases and the following statement. The main difference between the result and a normal switch * using a compound statement is that we don't create a new scope for the statement. */ ccb_list_t* statements = ccb_list_create(); while (wascase) { ccb_ast_t* casestmt = ccb_parse_statement_case(ccb); ccb_list_push(statements, casestmt); token = ccb_lexer_next(ccb); wascase = ccb_parse_identifier_check(ccb, token, "case"); if (!wascase) { ccb_lexer_unget(ccb, token); ccb_ast_t* stmt = ccb_parse_statement(ccb); ccb_list_push(statements, stmt); return ccb_ast_switch(ccb, expression, ccb_ast_compound(ccb, statements)); } } } else { /* Otherwise we just unget the (non-"case") token and parse a normal/compound statement. */ ccb_lexer_unget(ccb, token); ccb_ast_t* body = ccb_parse_statement(ccb); return ccb_ast_switch(ccb, expression, body); } } static ccb_ast_t* ccb_parse_statement_default(ccb_t* ccb) { ccb_parse_expect(ccb, ':'); return ccb_ast_make(ccb, CCB_AST_TYPE_STATEMENT_DEFAULT); } static ccb_ast_t* ccb_parse_statement_return(ccb_t* ccb) { if (ccb->func_callconv != 0) { ccb_compile_warn(ccb, "Returning from non-standard calling convention %d", ccb->func_callconv); } ccb_ast_t* val = ccb_parse_expression(ccb); ccb_parse_expect(ccb, ';'); return ccb_ast_return(ccb, ccb->func_callconv, ccb_ast_data_table[CCB_AST_DATA_FUNCTION]->returntype, val); } static ccb_ast_t* ccb_parse_statement_goto(ccb_t* ccb) { ccb_lexer_token_t* token = ccb_lexer_next(ccb); if (!token || token->type != CCB_LEXER_TOKEN_IDENTIFIER) ccb_compile_error(ccb, "expected identifier in goto statement"); ccb_parse_expect(ccb, ';'); ccb_ast_t* node = ccb_ast_goto(ccb, token->string); ccb_list_push(ccb_ast_gotos, node); return node; } static ccb_ast_t* ccb_parse_statement_asm(ccb_t* ccb) { int mode = 0; ccb_list_t* l = ccb_list_create(); char* line = ccb_lexer_consume_line(ccb, &mode); fprintf(stderr, "MODE %d CODE: %s\n", mode, line); ccb_list_push(l, line); if (mode == 0) { // No action, just a single asm line } else if (mode == 1) { // Line starts with '{' // Runs until mode == -1 ('}' line) or prints error if not 0 or 1 do { line = ccb_lexer_consume_line(ccb, &mode); fprintf(stderr, "MODE %d CODE: %s\n", mode, line); ccb_list_push(l, line); } while (mode == 0); if (mode != -1) { ccb_compile_error(ccb, "Invalid asm line (likely stray '{'?)"); } } else { ccb_compile_error(ccb, "Invalid asm line (likely stray '}')?"); } ccb_ast_t* node = ccb_ast_asm(ccb, l); return node; } static void ccb_parse_label_backfill(ccb_t* ccb) { for (ccb_list_iterator_t* it = ccb_list_iterator(ccb_ast_gotos); !ccb_list_iterator_end(it); ) { ccb_ast_t* source = ccb_list_iterator_next(it); char* label = source->gotostmt.label; ccb_ast_t* destination = ccb_table_find(ccb_ast_labels, label); if (!destination) ccb_compile_error(ccb, "undefined label: %s", label); if (destination->gotostmt.where) source->gotostmt.where = destination->gotostmt.where; else source->gotostmt.where = destination->gotostmt.where = ccb_ast_label(ccb); //fprintf(stderr, "Gave goto '%s' the target label '%s'.\n", source->gotostmt.label, source->gotostmt.where); } } static ccb_ast_t* ccb_parse_label(ccb_t* ccb, ccb_lexer_token_t* token) { ccb_parse_expect(ccb, ':'); char* label = token->string; ccb_ast_t* node = ccb_ast_new_label(ccb, label); if (ccb_table_find(ccb_ast_labels, label)) ccb_compile_error(ccb, "duplicate label: %s", label); ccb_table_insert(ccb_ast_labels, label, node); return node; } #ifdef CCB_X_OBJC static ccb_ast_t* ccb_parse_statement_objc_throw(ccb_t* ccb) { ccb_ast_t* val = ccb_parse_expression(ccb); ccb_parse_expect(ccb, ';'); return ccb_parse_runtime_call_1(ccb, "__oop_throw", val); } #endif static ccb_ast_t* ccb_parse_statement(ccb_t* ccb) { //__ccb_checkstack(); ccb_lexer_token_t* token = ccb_lexer_next(ccb); ccb_ast_t* ast; /*fputs("TESTA\n", stderr); fprintf(stderr, "TESTING\n");*/ if (ccb_lexer_ispunct(ccb, token, '{')) return ccb_parse_statement_compound(ccb); #ifdef CCB_X_OBJC if (ccb_lexer_ispunct(ccb, token, '@')) { ccb_lexer_token_t* attoken = token; token = ccb_lexer_next(ccb); if (ccb_parse_identifier_check(ccb, token, "throw")) return ccb_parse_statement_objc_throw(ccb); ccb_lexer_unget(ccb, token); } #endif if (ccb_parse_identifier_check(ccb, token, "if")) return ccb_parse_statement_if(ccb); if (ccb_parse_identifier_check(ccb, token, "for")) return ccb_parse_statement_for(ccb); if (ccb_parse_identifier_check(ccb, token, "while")) return ccb_parse_statement_while(ccb); if (ccb_parse_identifier_check(ccb, token, "do")) return ccb_parse_statement_do(ccb); if (ccb_parse_identifier_check(ccb, token, "return")) return ccb_parse_statement_return(ccb); if (ccb_parse_identifier_check(ccb, token, "switch")) return ccb_parse_statement_switch(ccb); if (ccb_parse_identifier_check(ccb, token, "case")) return ccb_parse_statement_case(ccb); if (ccb_parse_identifier_check(ccb, token, "default")) return ccb_parse_statement_default(ccb); if (ccb_parse_identifier_check(ccb, token, "break")) return ccb_parse_statement_break(ccb); if (ccb_parse_identifier_check(ccb, token, "continue")) return ccb_parse_statement_continue(ccb); if (ccb_parse_identifier_check(ccb, token, "goto")) return ccb_parse_statement_goto(ccb); if (ccb_parse_identifier_check(ccb, token, "__asm")) return ccb_parse_statement_asm(ccb); if (token->type == CCB_LEXER_TOKEN_IDENTIFIER && ccb_lexer_ispunct(ccb, ccb_lexer_peek(ccb), ':')) return ccb_parse_label(ccb, token); ccb_lexer_unget(ccb, token); ast = ccb_parse_expression_withcomma(ccb); ccb_parse_expect(ccb, ';'); return ast; } int __ccb_checkstack(){ #ifdef _ZCC_X64 int* a = NULL; /* Should be unaligned. */ int* b = NULL; /* Should be aligned. */ if ((((long long) &b) % 16) != 0) { fputs("ERROR: Stack unaligned in caller!\n", stderr); /* Should trigger an error or loop. */ while (a[0]) { // ... } } else { fputs("Stack is ok\n", stderr); } #endif return 0; } int __ccb_ignore(int x) { return x; } int __ccb_ignore2(int x, int y) { return x; } static void ccb_parse_statement_declaration(ccb_t* ccb, ccb_list_t* list) { /*fprintf(stderr, "TEST2\n"); __ccb_checkstack(); __ccb_ignore(__ccb_checkstack()); __ccb_ignore2(33, __ccb_checkstack()); __ccb_ignore2(__ccb_checkstack(), 99); fprintf(stderr, "TESTING\n");*/ ccb_lexer_token_t* token = ccb_lexer_peek(ccb); //int test = 1; if (!token) ccb_compile_error(ccb, "statement declaration with unexpected ending"); if (ccb_parse_type_check(ccb, token)) ccb_parse_declaration(ccb, list, &ccb_ast_variable_local); // TODO: Should compiler allow function pointers w/o the "&" like gcc does? else ccb_list_push(list, ccb_parse_statement(ccb)); } static ccb_ast_t* ccb_parse_statement_compound(ccb_t* ccb) { /*__ccb_checkstack(); fprintf(stderr, "TESTING\n");*/ ccb_ast_localenv = ccb_table_create(ccb_ast_localenv); ccb_list_t* statements = ccb_list_create(); for (;;) { /* Check for end before checking for inner statements, in order to handle {} without any statements. */ ccb_lexer_token_t* token = ccb_lexer_next(ccb); if (ccb_lexer_ispunct(ccb, token, '}')) break; ccb_lexer_unget(ccb, token); ccb_parse_statement_declaration(ccb, statements); } ccb_ast_localenv = ccb_table_parent(ccb_ast_localenv); return ccb_ast_compound(ccb, statements); } static int ccb_argbuiltin_max(ccb_t* ccb) { return 16; // This might be configurable in the future. } static const char* ccb_argbuiltin(ccb_t* ccb, int argnum) { switch (argnum) { case 0: return "__builtin_arg0"; case 1: return "__builtin_arg1"; case 2: return "__builtin_arg2"; case 3: return "__builtin_arg3"; case 4: return "__builtin_arg4"; case 5: return "__builtin_arg5"; case 6: return "__builtin_arg6"; case 7: return "__builtin_arg7"; case 8: return "__builtin_arg8"; case 9: return "__builtin_arg9"; case 10: return "__builtin_arg10"; case 11: return "__builtin_arg11"; case 12: return "__builtin_arg12"; case 13: return "__builtin_arg13"; case 14: return "__builtin_arg14"; case 15: return "__builtin_arg15"; default: return NULL; } } /* Returns the name of the buffer referenced by undefined argbuiltins. * If non-NULL, then this must be defined anywhere __builtin_arg0 and similar are used. * If it's NULL, then undefined __builtin_args are simply never defined. */ static const char* ccb_argbuiltin_buffername(ccb_t* ccb) { return NULL; } static ccb_data_type_t* ccb_parse_function_parameters(ccb_t* ccb, ccb_list_t* paramvars, ccb_data_type_t* returntype) { bool typeonly = !paramvars; ccb_list_t* paramtypes = ccb_list_create(); ccb_lexer_token_t* token = ccb_lexer_next(ccb); ccb_lexer_token_t* next = ccb_lexer_next(ccb); if (ccb_parse_identifier_check(ccb, token, "void") && ccb_lexer_ispunct(ccb, next, ')')) return ccb_ast_prototype(ccb, returntype, paramtypes, false); ccb_lexer_unget(ccb, next); if (ccb_lexer_ispunct(ccb, token, ')')) return ccb_ast_prototype(ccb, returntype, paramtypes, true); ccb_lexer_unget(ccb, token); int paramcount; for (paramcount = 0;; paramcount++) { token = ccb_lexer_next(ccb); if (ccb_parse_identifier_check(ccb, token, "...")) { if (ccb_list_length(paramtypes) == 0) ccb_compile_error(ccb, "ICE: %s (0)", __func__); ccb_parse_expect(ccb, ')'); return ccb_ast_prototype(ccb, returntype, paramtypes, true); } else { ccb_lexer_unget(ccb, token); } ccb_data_type_t* ptype; char* name; ccb_parse_function_parameter(ccb, &ptype, &name, typeonly); ccb_parse_semantic_notvoid(ccb, ptype); if (ptype->type == CCB_TYPE_ARRAY) ptype = ccb_ast_pointer(ccb, ptype->pointer); ccb_list_push(paramtypes, ptype); if (!typeonly) { void* vardef = ccb_ast_variable_local(ccb, ptype, name); ccb_list_push(paramvars, vardef); const char* builtinalias = ccb_argbuiltin(ccb, paramcount); if (builtinalias != NULL /*paramcount == 0*/) { /* Create a __builtin alias for the first argument. * This is useful for iterating the arguments via macros, i.e. for a va_start macro to see where the last argument is relative to the first argument. * In this implementation, it will conveniently be undefined unless a first argument is actually defined! Undefined __builtin_arg variables can be automatically simulated at lookup. */ ccb_table_insert(ccb_ast_localenv, builtinalias /*"__builtin_arg0"*/, vardef); } } ccb_lexer_token_t* token = ccb_lexer_next(ccb); if (ccb_lexer_ispunct(ccb, token, ')')) return ccb_ast_prototype(ccb, returntype, paramtypes, false); if (!ccb_lexer_ispunct(ccb, token, ',')) ccb_compile_error(ccb, "ICE: %s (2)", __func__); } /* Add any additional builtins. These are now mostly handled at-lookup. */ //const char* buffername = ccb_argbuiltin_buffername(ccb); //if (buffername != NULL) { // int aliascount; // for (aliascount = paramcount; aliascount < ccb_argbuiltin_max(ccb); aliascount++) { // const char* builtinalias = ccb_argbuiltin(ccb, aliascount); // ccb_table_insert(ccb_ast_localenv, builtinalias /*"__builtin_arg0"*/, bufferdef); // } //} } static ccb_ast_t* ccb_parse_function_definition(ccb_t* ccb, ccb_data_type_t* functype, char* name, ccb_list_t* parameters, int asmstmt) { ccb_ast_localenv = ccb_table_create(ccb_ast_localenv); ccb_ast_locals = ccb_list_create(); ccb_ast_data_table[CCB_AST_DATA_FUNCTION] = functype; ccb_ast_t* body; if (asmstmt) { //ccb_lexer_next(ccb); body = ccb_parse_statement_asm(ccb); } else { body = ccb_parse_statement_compound(ccb); } ccb_ast_t* r = ccb_ast_function(ccb, functype, name, parameters, body, ccb_ast_locals); ccb_table_insert(ccb_ast_globalenv, name, r); ccb_ast_data_table[CCB_AST_DATA_FUNCTION] = NULL; ccb_ast_localenv = NULL; ccb_ast_locals = NULL; return r; } static bool ccb_parse_function_definition_check(ccb_t* ccb) { ccb_list_t* buffer = ccb_list_create(); int nests = 0; bool paren = false; bool ready = true; for (;;) { ccb_lexer_token_t* token = ccb_lexer_next(ccb); ccb_list_push(buffer, token); //printf("Checking token... %d %s %s\n", nests, paren? "paren":"no-paren", ready?"ready":"no-ready"); if (!token) ccb_compile_error(ccb, "function definition with unexpected ending"); if (nests == 0 && paren && (ccb_lexer_ispunct(ccb, token, '{') || ccb_parse_identifier_check(ccb, token, "__asm"))) break; if (nests == 0 && (ccb_lexer_ispunct(ccb, token, ';') || ccb_lexer_ispunct(ccb, token, ',') || ccb_lexer_ispunct(ccb, token, '='))) { ready = false; break; } if (ccb_lexer_ispunct(ccb, token, '(')) nests++; if (ccb_lexer_ispunct(ccb, token, ')')) { if (nests == 0) ccb_compile_error(ccb, "unmatches parenthesis"); paren = true; nests--; } } while (ccb_list_length(buffer) > 0) ccb_lexer_unget(ccb, ccb_list_pop(buffer)); return ready; } static ccb_ast_t* ccb_parse_function_definition_intermediate(ccb_t* ccb) { //fprintf(stderr,"TESTING\n"); ccb_data_type_t* basetype; char* name; ccb_list_t* parameters = ccb_list_create(); basetype = ccb_parse_declaration_specification(ccb, NULL); ccb_ast_localenv = ccb_table_create(ccb_ast_globalenv); ccb_ast_labels = ccb_table_create(NULL); ccb_ast_gotos = ccb_list_create(); int oldcallconv = ccb->func_callconv; ccb->func_callconv = ccb->default_callconv; ccb_data_type_t* functype = ccb_parse_declarator(ccb, &name, basetype, parameters, CCB_CDECL_BODY); if (functype->isnaked) ccb_compile_warn(ccb, "Got naked function"); /* The ccb->func_name field is used for the __func__ builtin. */ char* oldname = ccb->func_name; ccb->func_name = name; //ccb->func_callconv = functype->callconv; int asmstmt; if (ccb_parse_identifier_check(ccb, ccb_lexer_peek(ccb), "__asm")) { ccb_lexer_next(ccb); asmstmt = 1; } else { asmstmt = 0; ccb_parse_expect(ccb, '{'); } ccb_ast_t* value = ccb_parse_function_definition(ccb, functype, name, parameters, asmstmt); ccb_parse_label_backfill(ccb); ccb_ast_localenv = NULL; ccb->func_callconv = oldcallconv; ccb->func_name = oldname; return value; } /* TODO: Predeclaration is required to self-compile recursive functions. */ static ccb_data_type_t* ccb_parse_declarator_direct_restage(ccb_t* ccb, ccb_data_type_t* basetype, ccb_list_t* parameters); static ccb_data_type_t* ccb_parse_declarator_direct_restage(ccb_t* ccb, ccb_data_type_t* basetype, ccb_list_t* parameters) { ccb_lexer_token_t* token = ccb_lexer_next(ccb); if (ccb_lexer_ispunct(ccb, token, '[')) { int length; token = ccb_lexer_next(ccb); if (ccb_lexer_ispunct(ccb, token, ']')) length = -1; else { ccb_lexer_unget(ccb, token); length = ccb_parse_evaluate(ccb, ccb_parse_expression(ccb)); ccb_parse_expect(ccb, ']'); } ccb_data_type_t* type = ccb_parse_declarator_direct_restage(ccb, basetype, parameters); if (type->type == CCB_TYPE_FUNCTION) ccb_compile_error(ccb, "array of functions"); return ccb_ast_array(ccb, type, length); } if (ccb_lexer_ispunct(ccb, token, '(')) { if (basetype->type == CCB_TYPE_FUNCTION) ccb_compile_error(ccb, "function returning function"); if (basetype->type == CCB_TYPE_ARRAY) ccb_compile_error(ccb, "function returning array"); return ccb_parse_function_parameters(ccb, parameters, basetype); } ccb_lexer_unget(ccb, token); return basetype; } static void ccb_parse_qualifiers_skip(ccb_t* ccb) { for (;;) { ccb_lexer_token_t* token = ccb_lexer_next(ccb); if (ccb_parse_identifier_check(ccb, token, "const") || ccb_parse_identifier_check(ccb, token, "volatile") || ccb_parse_identifier_check(ccb, token, "restrict")) { continue; } ccb_lexer_unget(ccb, token); return; } } // TODO: Predeclaration is required for recursive functions static ccb_data_type_t* ccb_parse_declarator_direct(ccb_t* ccb, char** rname, ccb_data_type_t* basetype, ccb_list_t* parameters, ccb_cdecl_t context); static ccb_data_type_t* ccb_parse_declarator_direct(ccb_t* ccb, char** rname, ccb_data_type_t* basetype, ccb_list_t* parameters, ccb_cdecl_t context) { ccb_lexer_token_t* token = ccb_lexer_next(ccb); ccb_lexer_token_t* next = ccb_lexer_peek(ccb); bool hascallconv = false; bool isnaked = false; int callconv = 0; if (ccb_lexer_ispunct(ccb, token, '(') && !ccb_parse_type_check(ccb, next) && !ccb_lexer_ispunct(ccb, next, ')')) { if (ccb_parse_identifier_check(ccb, next, "__cdecl")) { fprintf(stderr, "NOTE: Parsed a __cdecl but this may be ignored\n"); // TODO.. hascallconv = true; callconv = 100; next = ccb_lexer_next(ccb); } else if (ccb_parse_identifier_check(ccb, next, "__classic_call")) { fprintf(stderr, "NOTE: Parsed a __classic_call but this may be ignored\n"); // TODO.. hascallconv = true; callconv = 101; next = ccb_lexer_next(ccb); } if (ccb_parse_identifier_check(ccb, next, "__naked")) { fprintf(stderr, "NOTE: Parsed a __naked\n"); // TODO.. isnaked = true; next = ccb_lexer_next(ccb); } ccb_data_type_t* stub = ccb_ast_type_stub(ccb); ccb_data_type_t* type = ccb_parse_declarator_direct(ccb, rname, stub, parameters, context); //fprintf(stderr, "AAA\n"); ccb_parse_expect(ccb, ')'); //fprintf(stderr, "AAB\n"); // TODO: This doesn't compile yet: *stub = *ccb_parse_declarator_direct_restage(ccb, basetype, parameters); ccb_data_type_t* tmp = ccb_parse_declarator_direct_restage(ccb, basetype, parameters); memcpy(stub, tmp, sizeof(ccb_data_type_t)); if (hascallconv) { type->callconv = callconv; ccb->func_callconv = callconv; ccb_compile_warn(ccb, "Set non-standard calling convention"); } else { type->callconv = ccb->default_callconv; } type->isnaked = isnaked; return type; } if (ccb_lexer_ispunct(ccb, token, '*')) { ccb_parse_qualifiers_skip(ccb); ccb_data_type_t* stub = ccb_ast_type_stub(ccb); ccb_data_type_t* type = ccb_parse_declarator_direct(ccb, rname, stub, parameters, context); // TODO: This doesn't compile yet: *stub = *ccb_ast_pointer(ccb, basetype); ccb_data_type_t* tmp = ccb_ast_pointer(ccb, basetype);; memcpy(stub, tmp, sizeof(ccb_data_type_t)); // TODO: Do I need to check callconv here too? return type; } #ifdef CCB_X_OBJC if (context == CCB_CDECL_OBJCPARAMETER) { if (ccb_lexer_ispunct(ccb, token, ')')) { token = ccb_lexer_next(ccb); } else { ccb_compile_error(ccb, "expected `)' to finish objc parameter type"); } } #endif if (ccb_parse_identifier_check(ccb, token, "__cdecl")) { fprintf(stderr, "NOTE: Parsed a __cdecl but this may be ignored\n"); // TODO.. hascallconv = true; callconv = 100; token = ccb_lexer_next(ccb); } else if (ccb_parse_identifier_check(ccb, token, "__classic_call")) { fprintf(stderr, "NOTE: Parsed a __classic_call but this may be ignored\n"); // TODO.. hascallconv = true; callconv = 101; token = ccb_lexer_next(ccb); } if (ccb_parse_identifier_check(ccb, token, "__naked")) { fprintf(stderr, "NOTE: Parsed a __naked\n"); // TODO.. isnaked = true; token = ccb_lexer_next(ccb); } if (token->type == CCB_LEXER_TOKEN_IDENTIFIER) { if (context == CCB_CDECL_CAST || rname == NULL) ccb_compile_error(ccb, "wasn't expecting identifier `%s'", ccb_lexer_tokenstr(ccb, token)); *rname = token->string; ccb_data_type_t* type = ccb_parse_declarator_direct_restage(ccb, basetype, parameters); if (hascallconv) { type->callconv = callconv; ccb->func_callconv = callconv; ccb_compile_warn(ccb, "Set non-standard calling convention"); } else { type->callconv = ccb->default_callconv; } type->isnaked = isnaked; return type; } #ifdef CCB_X_OBJC if (context == CCB_CDECL_BODY || context == CCB_CDECL_PARAMETER || context == CCB_CDECL_OBJCPARAMETER) #else if (context == CCB_CDECL_BODY || context == CCB_CDECL_PARAMETER) #endif ccb_compile_error(ccb, "expected identifier, `(` or `*` for declarator"); ccb_lexer_unget(ccb, token); ccb_data_type_t* result = ccb_parse_declarator_direct_restage(ccb, basetype, parameters); if (hascallconv) { result->callconv = callconv; ccb->func_callconv = callconv; ccb_compile_warn(ccb, "Set non-standard calling convention"); } else { result->callconv = ccb->default_callconv; // If no calling convention is specified, use the default } result->isnaked = isnaked; return result; } static void ccb_parse_array_fix(ccb_t* ccb, ccb_data_type_t* type); // TODO: Predeclaration required for self-compile static void ccb_parse_array_fix(ccb_t* ccb, ccb_data_type_t* type) { if (type->type == CCB_TYPE_ARRAY) { ccb_parse_array_fix(ccb, type->pointer); type->size = type->length * type->pointer->size; } else if (type->type == CCB_TYPE_POINTER) { ccb_parse_array_fix(ccb, type->pointer); } else if (type->type == CCB_TYPE_FUNCTION) { ccb_parse_array_fix(ccb, type->returntype); } } static ccb_data_type_t* ccb_parse_declarator(ccb_t* ccb, char** rname, ccb_data_type_t* basetype, ccb_list_t* parameters, ccb_cdecl_t context) { ccb_data_type_t* type = ccb_parse_declarator_direct(ccb, rname, basetype, parameters, context); #ifdef CCB_X_OBJC if (context != CCB_CDECL_OBJCPARAMETER) #endif ccb_parse_array_fix(ccb, type); return type; } void ccb_extrastage_setpredeclared(ccb_t* ccb, const char* name); void ccb_extrastage_setknownextern(ccb_t* ccb, const char* name); static void ccb_parse_declaration(ccb_t* ccb, ccb_list_t* list, ccb_ast_t* (*make)(ccb_t* ccb, ccb_data_type_t*, char*)) { ccb_storage_t storage; ccb_data_type_t* basetype = ccb_parse_declaration_specification(ccb, &storage); ccb_lexer_token_t* token = ccb_lexer_next(ccb); if (ccb_lexer_ispunct(ccb, token, ';')) return; ccb_lexer_unget(ccb, token); for (;;) { char* name = NULL; ccb_data_type_t* type = ccb_parse_declarator(ccb, &name, ccb_ast_type_copy_incomplete(ccb, basetype), NULL, CCB_CDECL_BODY); if (storage == CCB_STORAGE_STATIC) type->isstatic = true; token = ccb_lexer_next(ccb); if (ccb_lexer_ispunct(ccb, token, '=')) { if (storage == CCB_STORAGE_TYPEDEF) ccb_compile_error(ccb, "invalid use of typedef"); ccb_parse_semantic_notvoid(ccb, type); ccb_ast_t* var = make(ccb, type, name); ccb_list_push(list, ccb_ast_declaration(ccb, var, ccb_parse_initializer_declaration(ccb, var->ctype))); token = ccb_lexer_next(ccb); /* NOTE: This should override isknownextern, in the event that something is declared as extern and then declared again. */ ccb_extrastage_setpredeclared(ccb, name); } else if (storage == CCB_STORAGE_TYPEDEF) { ccb_table_insert(ccb_parse_typedefs, name, type); } else if (type->type == CCB_TYPE_FUNCTION) { make(ccb, type, name); } else { ccb_ast_t* var = make(ccb, type, name); if (storage == CCB_STORAGE_EXTERN) { ccb_extrastage_setknownextern(ccb, name); } else { /* NOTE: This should override isknownextern, in the event that something is declared as extern and then declared again. */ ccb_extrastage_setpredeclared(ccb, name); ccb_list_push(list, ccb_ast_declaration(ccb, var, NULL)); } } if (ccb_lexer_ispunct(ccb, token, ';')) return; if (!ccb_lexer_ispunct(ccb, token, ',')) ccb_compile_error(ccb, "Confused!"); } } #ifdef CCB_X_OBJC bool ccb_parse_objc_definition_check(ccb_t* ccb) { ccb_lexer_token_t* token = ccb_lexer_next(ccb); if (ccb_lexer_ispunct(ccb, token, '@')) { token = ccb_lexer_next(ccb); if (token->type != CCB_LEXER_TOKEN_IDENTIFIER) { ccb_compile_error(ccb, "unexpected token following @: `%s'", ccb_lexer_tokenstr(ccb, token)); } if (strcmp(token->string, "interface") == 0) { ccb_lexer_unget(ccb, token); return true; } else if (strcmp(token->string, "implementation") == 0) { ccb_lexer_unget(ccb, token); return true; } else if (strcmp(token->string, "class") == 0) { ccb_lexer_unget(ccb, token); return true; } else if (strcmp(token->string, "protocol") == 0) { ccb_compile_error(ccb, "protocols aren't supported yet"); return false; // Unreachable } else { ccb_compile_error(ccb, "unexpected identifier following @: `%s'", token->string); return false; // Unreachable } } else { ccb_lexer_unget(ccb, token); return false; } } #endif #ifdef CCB_X_OBJC /* Parses method (definition and/or implementation) and returns it or NULL when finished. */ ccb_ast_t* ccb_parse_objc_method(ccb_t* ccb, char* classname, bool isimpl, ccb_list_t* initstmts) { //TODO: ccb->func_callconv = 101; ccb_lexer_token_t* token = ccb_lexer_next(ccb); // Should be either + or - if this is a method bool ismeta = false; char* selector = NULL; char* abiname = NULL; char* tmp = NULL; bool moreargs = false; if (ccb_lexer_ispunct(ccb, token, '+')) { ismeta = true; ccb->oop_ismeta = true; } else if (ccb_lexer_ispunct(ccb, token, '-')) { ismeta = false; ccb->oop_ismeta = false; } else { ccb_lexer_unget(ccb, token); return NULL; } if (isimpl) { ccb_ast_localenv = ccb_table_create(ccb_ast_globalenv); ccb_ast_labels = ccb_table_create(NULL); ccb_ast_gotos = ccb_list_create(); } token = ccb_lexer_next(ccb); ccb_data_type_t* rettype = NULL; if (ccb_lexer_ispunct(ccb, token, '(')) { ccb_data_type_t* basetype = ccb_parse_declaration_specification(ccb, NULL); rettype = ccb_parse_declarator(ccb, NULL, basetype, NULL, CCB_CDECL_TYPEONLY); ccb_parse_expect(ccb, ')'); token = ccb_lexer_next(ccb); } if (token->type != CCB_LEXER_TOKEN_IDENTIFIER) { ccb_compile_error(ccb, "unexpected token following `%s': `%s'", ismeta ? "+" : "-", ccb_lexer_tokenstr(ccb, token)); } selector = calloc(strlen(token->string) + 1000, 1); // TODO: A more appropriate limit abiname = calloc(strlen(token->string) + 1000, 1); // TODO: A more appropriate limit strcat(selector, token->string);// _strdup(token->string); strcat(abiname, classname); strcat(abiname, "__");// _strdup(token->string); if (ismeta) { strcat(abiname, "meta__"); } else { strcat(abiname, "instance__"); } strcat(abiname, token->string);// _strdup(token->string); ccb_list_t* paramtypes = ccb_list_create(); ccb_list_t* paramvars = ccb_list_create(); ccb_list_push(paramtypes, ccb_ast_data_table[CCB_AST_DATA_ID]); ccb_list_push(paramvars, ccb_ast_variable_local(ccb, ismeta ? ccb_ast_data_table[CCB_AST_DATA_ID] : ccb_ast_pointer(ccb, ccb_table_find(ccb_parse_typedefs, ccb->oop_classname)), "self")); if (ccb_lexer_ispunct(ccb, token = ccb_lexer_next(ccb), ':')) { moreargs = true; do { tmp = selector; selector = strcat(tmp, ":"); strcat(abiname, "_"); //free(tmp); XXX wrong place? token = ccb_lexer_next(ccb); char* argname; if (ccb_lexer_ispunct(ccb, token, '(')) { ccb_data_type_t* basetype = ccb_parse_declaration_specification(ccb, NULL); ccb_data_type_t* casttype = ccb_parse_declarator(ccb, &argname, basetype, NULL, CCB_CDECL_OBJCPARAMETER); ccb_list_push(paramtypes, casttype); ccb_list_push(paramvars, ccb_ast_variable_local(ccb, casttype, argname)); } else { ccb_lexer_unget(ccb, token); token = NULL; } token = ccb_lexer_next(ccb); if (token->type == CCB_LEXER_TOKEN_IDENTIFIER) { tmp = selector; selector = strcat(tmp, token->string); strcat(abiname, token->string); //free(tmp); ccb_parse_expect(ccb, ':'); moreargs = true; } else { ccb_lexer_unget(ccb, token); token = NULL; moreargs = false; } } while (moreargs); } else { ccb_lexer_unget(ccb, token); } // ccb_data_type_t* ccb_ast_prototype(ccb_t* ccb, ccb_data_type_t* returntype, ccb_list_t* paramtypes, bool dots); ccb_data_type_t* functype = ccb_ast_prototype(ccb, rettype, paramtypes, false); //TODO: functype->callconv = 101; //if (isimpl) { ccb_ast_localenv = ccb_table_create(ccb_ast_localenv); ccb_ast_locals = ccb_list_create(); ccb_ast_data_table[CCB_AST_DATA_FUNCTION] = functype; /*if (!ccb_lexer_ispunct(ccb, token, '{')) { ccb_compile_error(ccb, "Expected {...} for method implementation"); }*/ //ccb_lexer_next(ccb); //fprintf(stderr, "Parsing body...\n"); ccb_ast_t* body = ccb_parse_statement(ccb);//_compound(ccb); //fprintf(stderr, "Done parsing body...\n"); ccb_ast_t* r = ccb_ast_function(ccb, functype, abiname, paramvars, body, ccb_ast_locals); //TODO: r->function.callconv = 101; ccb_table_insert(ccb_ast_globalenv, abiname, r); ccb_ast_t* selstr = ccb_ast_new_string(ccb, selector); ccb_list_push(ccb_ast_strings, selstr); if (isimpl) { ccb_list_push(initstmts, ccb_parse_runtime_call_3(ccb, ismeta ? "__oop_addmetamethod" : "__oop_addinstancemethod", ccb_ast_variable_global(ccb, ccb_ast_data_table[CCB_AST_DATA_ID], classname), selstr, ccb_ast_new_unary(ccb, CCB_AST_TYPE_ADDRESS, ccb_ast_pointer(ccb, r->ctype), r)));//ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_INT], 1234))); } ccb_data_type_t* cltype = ccb_table_find(ccb_parse_typedefs, classname); if (cltype == NULL) { ccb_compile_error(ccb, "Can't find class '%s' (is it defined?)", classname); } if (ismeta) { ccb_table_insert(cltype->cmethods, selector, functype); } else { ccb_table_insert(cltype->imethods, selector, functype); } /* NOTE: This is a bit of a hack, but the objx_addinstancemethod/objx_addmetamethod call is * added to the initialisation stub function BEFORE we leave the context of the function we're adding. * This is so that objx_addinstancemethod/objx_addmetamethod gets looked up properly (otherwise, we'd * have to re-enter the init stub context instead, but the effect would be the same). */ ccb_ast_data_table[CCB_AST_DATA_FUNCTION] = NULL; ccb_ast_localenv = NULL; ccb_ast_locals = NULL; ccb->func_callconv = ccb->default_callconv; ccb->oop_ismeta = false; if (!isimpl) { return (void*)(size_t)1; // XXX hack... TODO wtf was I doing here?? } return r; //} //else { // ccb_parse_expect(ccb, ';'); //} return (void*)(size_t)1; // XXX hack... } ccb_ast_t* ccb_parse_objc_useinitstub(ccb_t* ccb, ccb_ast_t* expr) { char* abiname = calloc(1000, 1); // TODO: A more appropriate limit strcat(abiname, expr->variable.name); strcat(abiname, "__init"); ccb_ast_t* initfunc = ccb_table_find(ccb_ast_globalenv, abiname); if (initfunc == NULL) { ccb_compile_error(ccb, "Can't find initialisation function '%s'", abiname); } ccb_ast_t* funcptrexpr = ccb_ast_new_unary(ccb, CCB_AST_TYPE_ADDRESS, ccb_ast_pointer(ccb, initfunc->ctype), initfunc); return ccb_parse_runtime_call_2(ccb, "__oop_getclass", ccb_ast_new_unary(ccb, CCB_AST_TYPE_ADDRESS, ccb_ast_pointer(ccb, ccb_ast_data_table[CCB_AST_DATA_ID]), expr), funcptrexpr); } ccb_ast_t* ccb_parse_objc_mkinitstub(ccb_t* ccb, char* classname, ccb_list_t* stmts) { //bool isimpl = true; //ccb_lexer_token_t* token = ccb_lexer_next(ccb); // Should be either + or - if this is a method //bool ismeta = false; //char* selector = NULL; char* abiname = NULL; //char* tmp = NULL; //bool moreargs = false; ccb_data_type_t* typ = ccb_table_find(ccb_parse_typedefs, classname); const char* supname = ""; int siz = -1; if (typ == NULL) { ccb_compile_warn(ccb, "No corresponding @interface for '%s'", classname); } else { supname = typ->supername; siz = typ->size; //if (supname == NULL) { //supname = ""; //} } ccb_ast_localenv = ccb_table_create(ccb_ast_globalenv); ccb_ast_labels = ccb_table_create(NULL); ccb_ast_gotos = ccb_list_create(); ccb_data_type_t* rettype = ccb_ast_data_table[CCB_AST_DATA_VOID]; abiname = calloc(1000, 1); // TODO: A more appropriate limit strcat(abiname, classname); strcat(abiname, "__init");// _strdup(token->string); ccb_list_t* paramtypes = ccb_list_create(); ccb_list_t* paramvars = ccb_list_create(); //ccb_list_push(paramtypes, ccb_ast_data_table[CCB_AST_DATA_INT]); //ccb_list_push(paramvars, ccb_ast_variable_local(ccb, ccb_ast_data_table[CCB_AST_DATA_INT], "self")); // ccb_data_type_t* ccb_ast_prototype(ccb_t* ccb, ccb_data_type_t* returntype, ccb_list_t* paramtypes, bool dots); ccb_data_type_t* functype = ccb_ast_prototype(ccb, rettype, paramtypes, false); ccb_ast_localenv = ccb_table_create(ccb_ast_localenv); ccb_ast_locals = ccb_list_create(); ccb_ast_data_table[CCB_AST_DATA_FUNCTION] = functype; ccb_ast_t* body = ccb_ast_compound(ccb, (stmts == NULL) ? ccb_list_create() : stmts); ccb_ast_t* namestr = ccb_ast_new_string(ccb, classname); ccb_list_push(ccb_ast_strings, namestr); //ccb_ast_t* superstr = ccb_ast_new_string(ccb, supname); //ccb_list_push(ccb_ast_strings, superstr); if (stmts != NULL) { ccb_list_push(stmts, ccb_parse_runtime_call_4(ccb, "__oop_addclass", ccb_ast_new_unary(ccb, CCB_AST_TYPE_ADDRESS, ccb_ast_pointer(ccb, ccb_ast_data_table[CCB_AST_DATA_ID]), ccb_ast_variable_global(ccb, ccb_ast_data_table[CCB_AST_DATA_ID], classname)), namestr, ((supname == NULL) ? ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_LLONG], 0) : ccb_parse_objc_useinitstub(ccb, ccb_ast_variable_global(ccb, ccb_ast_data_table[CCB_AST_DATA_ID], supname))),//superstr, ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_INT], siz))); // Instance size } ccb_ast_t* r = ccb_ast_function(ccb, functype, abiname, paramvars, body, ccb_ast_locals); ccb_table_insert(ccb_ast_globalenv, abiname, r); ccb_ast_data_table[CCB_AST_DATA_FUNCTION] = NULL; ccb_ast_localenv = NULL; ccb_ast_locals = NULL; /* Once we've got the ABI name, we can add a call to it within the module initialisation. */ if (stmts != NULL) { if(ccb->mod_initstmts != NULL) { ccb_list_push(ccb->mod_initstmts, ccb_parse_runtime_call_0(ccb, abiname)); } else { fprintf(stderr, "WARNING: Classes are implemented here but with no module name for initialisation!\n"); } } return r; } ccb_ast_t* ccb_parse_objc_mkmodinitstub(ccb_t* ccb, char* modname, ccb_list_t* stmts) { //bool isimpl = true; //ccb_lexer_token_t* token = ccb_lexer_next(ccb); // Should be either + or - if this is a method //bool ismeta = false; //char* selector = NULL; char* abiname = NULL; //char* tmp = NULL; //bool moreargs = false; ccb_ast_localenv = ccb_table_create(ccb_ast_globalenv); ccb_ast_labels = ccb_table_create(NULL); ccb_ast_gotos = ccb_list_create(); ccb_data_type_t* rettype = ccb_ast_data_table[CCB_AST_DATA_VOID]; abiname = calloc(1000, 1); // TODO: A more appropriate limit strcat(abiname, "__module__"); strcat(abiname, modname); strcat(abiname, "__init");// _strdup(token->string); ccb_list_t* paramtypes = ccb_list_create(); ccb_list_t* paramvars = ccb_list_create(); //ccb_list_push(paramtypes, ccb_ast_data_table[CCB_AST_DATA_INT]); //ccb_list_push(paramvars, ccb_ast_variable_local(ccb, ccb_ast_data_table[CCB_AST_DATA_INT], "self")); // ccb_data_type_t* ccb_ast_prototype(ccb_t* ccb, ccb_data_type_t* returntype, ccb_list_t* paramtypes, bool dots); ccb_data_type_t* functype = ccb_ast_prototype(ccb, rettype, paramtypes, false); ccb_ast_localenv = ccb_table_create(ccb_ast_localenv); ccb_ast_locals = ccb_list_create(); ccb_ast_data_table[CCB_AST_DATA_FUNCTION] = functype; ccb_ast_t* body = ccb_ast_compound(ccb, (stmts == NULL) ? ccb_list_create() : stmts); //ccb_ast_t* namestr = ccb_ast_new_string(ccb, classname); //ccb_list_push(ccb_ast_strings, namestr); //ccb_ast_t* superstr = ccb_ast_new_string(ccb, supname); //ccb_list_push(ccb_ast_strings, superstr); /* if (stmts != NULL) { ccb_list_push(stmts, ccb_parse_runtime_call_4(ccb, "__oop_addclass", ccb_ast_new_unary(ccb, CCB_AST_TYPE_ADDRESS, ccb_ast_pointer(ccb, ccb_ast_data_table[CCB_AST_DATA_ID]), ccb_ast_variable_global(ccb, ccb_ast_data_table[CCB_AST_DATA_ID], classname)), namestr, ((supname == NULL) ? ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_LLONG], 0) : ccb_parse_objc_useinitstub(ccb, ccb_ast_variable_global(ccb, ccb_ast_data_table[CCB_AST_DATA_ID], supname))),//superstr, ccb_ast_new_integer(ccb, ccb_ast_data_table[CCB_AST_DATA_INT], siz))); // Instance size } */ ccb_ast_t* r = ccb_ast_function(ccb, functype, abiname, paramvars, body, ccb_ast_locals); ccb_table_insert(ccb_ast_globalenv, abiname, r); ccb_ast_data_table[CCB_AST_DATA_FUNCTION] = NULL; ccb_ast_localenv = NULL; ccb_ast_locals = NULL; return r; } #endif #ifdef CCB_X_OBJC void ccb_parse_objc_interface(ccb_t* ccb, ccb_list_t* list, char* name) { ccb->oop_classname = name; ccb_lexer_token_t* token = ccb_lexer_next(ccb); char* supname = NULL; ccb_data_type_t* suptype = NULL; int supsize = 0; ccb_ast_t* method = NULL; if (ccb_lexer_ispunct(ccb, token, ':')) { token = ccb_lexer_next(ccb); if (token->type == CCB_LEXER_TOKEN_IDENTIFIER) { supname = token->string; suptype = ccb_table_find(ccb_parse_typedefs, supname); if (suptype == NULL) { ccb_compile_error("Undefined base class '%s'", supname); } else { supsize = suptype->size; while ((supsize % ccb_target_type_size_pointer(ccb)) != 0) { supsize++; } } } else { ccb_compile_error(ccb, "unexpected token following `:': `%s'", ccb_lexer_tokenstr(ccb, token)); } } else { ccb_lexer_unget(ccb, token); token = NULL; } /* Before we actually begin parsing the class, which initially happens by parsing the "struct"-like part, * we first create a dummy typedef. * That is, an "@interface Foo {...}" translates into a "typedef struct Foo Foo", followed by the "struct Foo {...}" * (the main difference for the struct part being that a flag is set to say that it's a class). */ /* The first call to ccb_parse_tag_definition(...) is given a -1 initial size, indicating that it isn't to * attempt to read any fields yet. */ ccb_data_type_t* foo = ccb_parse_tag_definition(ccb, ccb_ast_structures, true, -1, name); foo->classname = name; foo->supername = supname; ccb_table_insert(ccb_parse_typedefs, name, foo); foo = ccb_parse_tag_definition(ccb, ccb_ast_structures, true, supsize, name); foo->classname = name; foo->supername = supname; if (suptype == NULL) { foo->cmethods = ccb_table_create(NULL); foo->imethods = ccb_table_create(NULL); } else { foo->cmethods = ccb_table_create(suptype->cmethods); foo->imethods = ccb_table_create(suptype->imethods); } ccb_table_insert(ccb_parse_typedefs, name, foo); //ccb_ast_data_table[CCB_AST_DATA_INT]); while ((method = ccb_parse_objc_method(ccb, name, false, NULL)) != NULL) { //ccb_list_push(list, method); //ccb_compile_warn(ccb, "ignoring @interface method (TODO)"); } //ccb_compile_error(ccb, "TODO: @interface"); ccb_parse_expect(ccb, '@'); token = ccb_lexer_next(ccb); if (token->type != CCB_LEXER_TOKEN_IDENTIFIER || strcmp(token->string, "end") != 0) { ccb_compile_error(ccb, "unexpected token following `@': `%s'", ccb_lexer_tokenstr(ccb, token)); } ccb_ast_t* globvar = ccb_ast_variable_global(ccb, ccb_ast_data_table[CCB_AST_DATA_ID], name); globvar->variable.isclassobj = true; //ccb_list_t* initstmts = ccb_list_create(); ccb_parse_objc_mkinitstub(ccb, name, NULL); //initstmts); ccb->oop_classname = NULL; } #endif #ifdef CCB_X_OBJC void ccb_parse_objc_implementation(ccb_t* ccb, ccb_list_t* list, char* name) { ccb->oop_classname = name; ccb_lexer_token_t* token = NULL; ccb_ast_t* method = NULL; ccb_list_t* initstmts = ccb_list_create(); ccb_ast_t* initstub = ccb_parse_objc_mkinitstub(ccb, name, initstmts); ccb_list_push(list, initstub); while ((method = ccb_parse_objc_method(ccb, name, true, initstmts)) != NULL) { ccb_list_push(list, method); //ccb_compile_warn(ccb, "ignoring @implementation method (TODO)"); } ccb_parse_expect(ccb, '@'); token = ccb_lexer_next(ccb); if (token->type != CCB_LEXER_TOKEN_IDENTIFIER || strcmp(token->string, "end") != 0) { ccb_compile_error(ccb, "unexpected token following `@': `%s'", ccb_lexer_tokenstr(ccb, token)); } // Has already been declared like an extern in the @interface section, but the class object is declared as a global here ccb_ast_t* globvar = ccb_ast_variable_global(ccb, ccb_ast_data_table[CCB_AST_DATA_ID], name); globvar->variable.isclassobj = true; ccb_list_push(list, ccb_ast_declaration(ccb, globvar, ccb_list_create())); ccb->oop_classname = NULL; //ccb_compile_error(ccb, "TODO: @implementation"); } #endif #ifdef CCB_X_OBJC void ccb_parse_objc_forwardclass(ccb_t* ccb, ccb_list_t* list, char* name) { ccb_parse_expect(ccb, ';'); ccb_table_insert(ccb_parse_typedefs, name, ccb_ast_data_table[CCB_AST_DATA_ID]); ccb_compile_warn(ccb, "TODO: Finish @class"); //ccb_list_push(list, ccb_ast_declaration(ccb, ccb_ast_variable_global(ccb, ccb_ast_data_table[CCB_AST_DATA_ID], name), ccb_list_create())); } #endif #ifdef CCB_X_OBJC void ccb_parse_objc_definition(ccb_t* ccb, ccb_list_t* list) { // The '@' token has already been read (but the 'interface'/'implementation'/... and name tokens haven't) ccb_lexer_token_t* ttoken = ccb_lexer_next(ccb); ccb_lexer_token_t* ntoken = NULL; if (ttoken->type == CCB_LEXER_TOKEN_IDENTIFIER) { ntoken = ccb_lexer_next(ccb); if (ntoken->type != CCB_LEXER_TOKEN_IDENTIFIER) { ccb_compile_error(ccb, "unexpected token following @%s: `%s'", ttoken->string, ccb_lexer_tokenstr(ccb, ntoken)); } if (strcmp(ttoken->string, "interface") == 0) { ccb_parse_objc_interface(ccb, list, ntoken->string); return; } else if (strcmp(ttoken->string, "implementation") == 0) { ccb_parse_objc_implementation(ccb, list, ntoken->string); return; } else if (strcmp(ttoken->string, "class") == 0) { ccb_parse_objc_forwardclass(ccb, list, ntoken->string); return; } ccb_lexer_unget(ccb, ntoken); } ccb_compile_error(ccb, "TODO: @%s", ccb_lexer_tokenstr(ccb, ttoken)); } #endif int ccb_extrastage_ispredeclared(ccb_t* ccb, const char* name) { if (ccb->declarednames != NULL) { for (ccb_list_iterator_t* it = ccb_list_iterator(ccb->declarednames); !ccb_list_iterator_end(it); ) { const char* entry = ccb_list_iterator_next(it); if (!strcmp(name, entry)) return 1; } } return 0; } void ccb_extrastage_setpredeclared(ccb_t* ccb, const char* name) { if (!ccb_extrastage_ispredeclared(ccb, name)) { if (ccb->declarednames == NULL) { ccb->declarednames = ccb_list_create(); } ccb_list_push(ccb->declarednames, name); } } int ccb_extrastage_isknownextern(ccb_t* ccb, const char* name) { if (ccb->knownexterns != NULL) { for (ccb_list_iterator_t* it = ccb_list_iterator(ccb->knownexterns); !ccb_list_iterator_end(it); ) { const char* entry = ccb_list_iterator_next(it); if (!strcmp(name, entry)) return 1; } } return 0; } void ccb_extrastage_setknownextern(ccb_t* ccb, const char* name) { if (!ccb_extrastage_isknownextern(ccb, name)) { if (ccb->knownexterns == NULL) { ccb->knownexterns = ccb_list_create(); } ccb_list_push(ccb->knownexterns, name); } } ccb_list_t* ccb_parse_run(ccb_t* ccb) { ccb_list_t* list = ccb_list_create(); /* If we're going to use OOP initialisations (to load classes etc.) then we need to create * an initialisation function, so that each class init can be invoked systematically. The * alternative (used by GCC?) is to use linker tricks to concatenate any initialisation stuff, * but this is problematic for many reasons (firstly you're stuck with one linker, and secondly * it's more difficult to debug than just using some normal functions). This way, each logical * module (OOP source file) has a single initialisation function, which calls in defined order * any class initialisations. */ if (ccb->mod_name != NULL) { ccb_list_t* initstmts = ccb_list_create(); ccb_ast_t* initstub = ccb_parse_objc_mkmodinitstub(ccb, ccb->mod_name, initstmts); ccb_list_push(list, initstub); ccb->mod_initstmts = initstmts; } for (;;) { if (!ccb_lexer_peek(ccb)) return list; #ifdef CCB_X_OBJC if (ccb_parse_objc_definition_check(ccb)) { ccb_parse_objc_definition(ccb, list); } else #endif if (ccb_parse_function_definition_check(ccb)) ccb_list_push(list, ccb_parse_function_definition_intermediate(ccb)); else ccb_parse_declaration(ccb, list, &ccb_ast_variable_global); } return NULL; } /* From ifdef of CCB_IMPLEMENTATION: */ #endif /* From ifndef at top of file: */ #endif