6844 lines
238 KiB
C
6844 lines
238 KiB
C
#ifndef CCB_H
|
|
#define CCB_H
|
|
|
|
#ifdef _ZCC
|
|
//#define __func__ "TODO: function name"
|
|
#endif
|
|
|
|
#define CCB_X_OBJC
|
|
#define CCB_X_OBJX
|
|
|
|
#include <stdio.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stddef.h>
|
|
|
|
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 <list>
|
|
*/
|
|
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 <ccb_type_t> 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 <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <setjmp.h>
|
|
#include <ctype.h>
|
|
#include <limits.h>
|
|
|
|
#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 <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#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
|