/* toc's types. Note that although these types are in the public domain, the code which uses them (i.e. most of the rest of toc) is not. This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to */ /* NOTE: make sure you edit copy.c and cgen_recurse_subexprs/types when you make a change to expression-related types or type-related types in this file! */ typedef long double Floating; /* OPTIM: Switch to double, but make sure floating-point literals are right */ #if __STDC_VERSION__ >= 199901 #define LONGLONG_AVAILABLE 1 typedef long long longlong; #else #define LONGLONG_AVAILABLE 0 typedef long longlong; #endif #if __STDC_VERSION__ < 201112 /* try to find the type with the strictest alignment */ typedef union { long double floating; void *ptr; longlong integer; void (*fn_ptr)(void); } MaxAlign; #else typedef max_align_t MaxAlign; #endif typedef uint8_t U8; #define U8_MAX UINT8_MAX typedef uint16_t U16; #define U16_MAX UINT16_MAX typedef uint32_t U32; #define U32_MAX UINT32_MAX typedef uint64_t U64; #define U64_MAX UINT64_MAX #define U8_FMT "%" PRIu8 #define U16_FMT "%" PRIu16 #define U32_FMT "%" PRIu32 #define U64_FMT "%" PRIu64 #if __STDC_VERSION__ >= 199901 #include #elif defined __cplusplus #else typedef U8 bool; #define false ((bool)0) #define true ((bool)1) #endif #if defined __GNUC__ && !defined NO_WARN_UNUSED_RESULT #define Status bool __attribute__((warn_unused_result)) #else #define Status bool #endif typedef int8_t I8; #define I8_MAX INT8_MAX typedef int16_t I16; #define I16_MAX INT16_MAX typedef int32_t I32; #define I32_MAX INT32_MAX typedef int64_t I64; #define I64_MAX INT64_MAX #define I8_FMT "%" PRId8 #define I16_FMT "%" PRId16 #define I32_FMT "%" PRId32 #define I64_FMT "%" PRId64 /* NOTE: if you change these, make sure you change hash_tables.c */ typedef float F32; typedef double F64; #define F32_MANT_DIG FLT_MANT_DIG #define F32_DIG FLT_DIG #define F64_MANT_DIG DBL_MANT_DIG #define F64_DIG DBL_DIG #define F32_FMT "%.16f" #define F64_FMT "%.16f" typedef U32 IdentID; /* identifier ID for cgen (anonymous variables). not to be confused with IdentTree.id */ /* for keeping track of whence something came */ #ifdef TOC_DEBUG #define SOURCE_LOCATION char *src_file; int src_line; #define SOURCE_LOCATION_PARAMS char *src_file, int src_line, #define DEBUG_UNDERSCORE(x) x##_ #else #define SOURCE_LOCATION #define SOURCE_LOCATION_PARAMS #define DEBUG_UNDERSCORE(x) x #endif typedef struct ErrCtx { bool enabled; bool color_enabled; bool have_errored; struct Location *instance_stack; /* stack of locations which generate the instances we're dealing with */ } ErrCtx; typedef struct Page { struct Page *next; size_t used; /* number MaxAligns used, not bytes */ MaxAlign data[]; } Page; typedef struct Allocator { Page *first; Page *last; } Allocator; typedef struct ArrBlock { void *data; size_t n; /* number of things in this block so far */ } ArrBlock; /* initialize to 0 */ typedef struct HashTable { void *data; bool *occupied; U64 n; U64 cap; } HashTable; typedef struct Slice { I64 n; void *data; } Slice; typedef union Value { U8 u8; U16 u16; U32 u32; U64 u64; I8 i8; I16 i16; I32 i32; I64 i64; bool boolv; char charv; float f32; double f64; struct FnExpr *fn; void *arr; void *ptr; void *struc; union Value *tuple; Slice slice; struct Type *type; struct Namespace *nms; struct VarArg *varargs; /* dynamic array */ } Value; typedef struct VarArg { struct Type *type; Value val; } VarArg; typedef enum { IDECL_NONE, IDECL_DECL, IDECL_EXPR } IdentDeclKind; typedef struct IdentSlot { char *str; size_t len; /* where this identifier was declared */ IdentDeclKind decl_kind; union { struct Declaration *decl; struct Expression *decl_expr; }; struct Identifiers *idents; struct Namespace *nms; /* only exists after typing, and only for namespace-level declarations (i.e. not local variables) */ SOURCE_LOCATION } IdentSlot; typedef struct StrHashTableSlot { const char *str; size_t len; MaxAlign data[]; } StrHashTableSlot; typedef StrHashTableSlot *StrHashTableSlotPtr; typedef struct StrHashTable { StrHashTableSlot **slots; Allocator *allocr; U32 rand_seed; size_t data_size; size_t nentries; /* # of filled slots */ } StrHashTable; typedef IdentSlot *Identifier; typedef IdentSlot *IdentSlotPtr; typedef struct Identifiers { StrHashTable table; U32 rseed; struct Block *scope; /* NULL for file scope */ } Identifiers; typedef enum { TOKEN_KW, TOKEN_IDENT, TOKEN_DIRECT, TOKEN_LITERAL_NUM, TOKEN_LITERAL_CHAR, TOKEN_LITERAL_STR, TOKEN_EOF } TokenKind; typedef enum { DIRECT_C, DIRECT_SIZEOF, DIRECT_ALIGNOF, DIRECT_EXPORT, DIRECT_FOREIGN, DIRECT_BUILTIN, DIRECT_INCLUDE, DIRECT_FORCE, DIRECT_IF, DIRECT_ERROR, DIRECT_WARN, DIRECT_INFO, DIRECT_COUNT } Directive; static const char *directives[DIRECT_COUNT] = {"C", "sizeof", "alignof", "export", "foreign", "builtin", "include", "force", "if", "error", "warn", "info"}; typedef enum { KW_SEMICOLON, KW_COLON, KW_COMMA, KW_LPAREN, KW_RPAREN, KW_LBRACE, KW_RBRACE, KW_LSQUARE, KW_RSQUARE, KW_EQ_EQ, KW_PLUS_EQ, KW_MINUS_EQ, KW_ASTERISK_EQ, KW_SLASH_EQ, KW_PERCENT_EQ, KW_NE, KW_LE, KW_LT, KW_GE, KW_GT, KW_PLUS, KW_MINUS, KW_ASTERISK, KW_EXCLAMATION, KW_AMPERSAND, KW_SLASH, KW_PERCENT, KW_DOTDOT, KW_DOT, KW_EQ, KW_LAST_SYMBOL = KW_EQ, /* last one entirely consisting of symbols */ KW_IF, KW_ELIF, KW_ELSE, KW_WHILE, KW_FOR, KW_RETURN, KW_BREAK, KW_CONTINUE, KW_FN, KW_AS, KW_NEW, KW_DEL, KW_STRUCT, KW_INT, KW_I8, KW_I16, KW_I32, KW_I64, KW_U8, KW_U16, KW_U32, KW_U64, KW_FLOAT, KW_F32, KW_F64, KW_TYPE, KW_NAMESPACE, KW_CHAR, KW_BOOL, KW_TRUE, KW_FALSE, KW_NMS, KW_TYPEOF, KW_COUNT } Keyword; static const char *const keywords[KW_COUNT] = {";", ":", ",", "(", ")", "{", "}", "[", "]", "==", "+=", "-=", "*=", "/=", "%=", "!=", "<=", "<", ">=", ">", "+", "-", "*", "!", "&", "/", "%", "..", ".", "=", "if", "elif", "else", "while", "for", "return", "break", "continue", "fn", "as", "new", "del", "struct", "int", "i8", "i16", "i32", "i64", "u8", "u16", "u32", "u64", "float", "f32", "f64", "Type", "Namespace", "char", "bool", "true", "false", "nms", "typeof"}; typedef enum { NUM_LITERAL_INT, NUM_LITERAL_FLOAT } NumLiteralKind; typedef struct NumLiteral { NumLiteralKind kind; union { U64 intval; Floating floatval; }; } NumLiteral; typedef struct StrLiteral { char *str; size_t len; } StrLiteral; typedef struct { U32 line; U32 start; /* index in ctx->str */ U32 end; /* exclusive */ } SourcePos; /* NOTE: Location is typedef'd in util/err.c */ typedef struct Token { TokenKind kind; SourcePos pos; union { Keyword kw; Directive direct; char *ident; NumLiteral num; char chr; StrLiteral str; }; } Token; typedef struct { ErrCtx *ctx; const char *filename; char *contents; } File; typedef struct Location { File *file; /* if start is NULL, simple_location will be used. */ Token *start; union { Token *end; /* Exclusive */ struct { U32 line; /* if 0, this is a null location */ } simple_location; }; } Location; typedef struct Tokenizer { Allocator *allocr; Token *tokens; File *file; char *s; /* string being parsed */ ErrCtx *err_ctx; U32 line; Token *token; /* token currently being processed */ Identifiers *globals; } Tokenizer; typedef enum { TYPE_UNKNOWN, TYPE_VOID, TYPE_BUILTIN, TYPE_FN, TYPE_TUPLE, TYPE_ARR, TYPE_PTR, TYPE_SLICE, TYPE_EXPR, /* just use this expression as the type. this kind of type doesn't exist after resolving. */ TYPE_STRUCT #define TYPE_COUNT (TYPE_STRUCT+1) } TypeKind; typedef enum { BUILTIN_I8, BUILTIN_I16, BUILTIN_I32, BUILTIN_I64, BUILTIN_U8, BUILTIN_U16, BUILTIN_U32, BUILTIN_U64, BUILTIN_F32, BUILTIN_F64, BUILTIN_CHAR, BUILTIN_BOOL, BUILTIN_TYPE, BUILTIN_VARARGS, BUILTIN_NMS } BuiltinType; typedef U8 Constness; #define CONSTNESS_NO ((Constness)0) #define CONSTNESS_SEMI ((Constness)1) #define CONSTNESS_YES ((Constness)2) typedef struct FnType { struct Type *types; /* dynamic array [0] = ret_type, [1:] = param_types */ Constness *constness; /* [i] = constness of param #i. iff no parameters are constant, this is NULL. don't use it as a dynamic array, because eventually it might not be. */ } FnType; enum { TYPE_IS_FLEXIBLE = 0x01, TYPE_IS_RESOLVED = 0x02, }; typedef U8 TypeFlags; typedef struct Type { struct Expression *was_expr; /* if non-NULL, indicates that this type used to be an expression (TYPE_EXPR) */ TypeKind kind; TypeFlags flags; union { BuiltinType builtin; FnType fn; struct Type *tuple; struct { struct Type *of; union { U64 n; /* after resolving */ struct Expression *n_expr; /* before resolving */ }; } arr; struct Type *ptr; struct Type *slice; struct StructDef *struc; /* multiple resolved types can refer to the same struct */ struct Expression *expr; }; } Type; /* field of a struct */ typedef struct Field { Location where; Identifier name; Type type; size_t offset; /* offset during compile time */ } Field; enum { BLOCK_IS_FN = 0x01, BLOCK_IS_NMS = 0x02, BLOCK_FINDING_TYPES = 0x04, BLOCK_FOUND_TYPES = 0x08, BLOCK_IS_LOOP = 0x10 /* can we break/continue in this block? */ }; typedef U8 BlockFlags; typedef struct Block { BlockFlags flags; Location where; Identifiers idents; struct Statement *stmts; struct Expression *ret_expr; /* the return expression of this block, e.g. {foo(); 3} => 3 NULL for no expression. */ struct Block *parent; } Block; enum { STRUCT_DEF_FOUND_OFFSETS = 0x01, STRUCT_DEF_FINDING_OFFSETS = 0x02, STRUCT_DEF_CGEN_DECLARED = 0x04, STRUCT_DEF_CGEN_DEFINED = 0x08, STRUCT_DEF_RESOLVED = 0x10 }; typedef struct StructDef { Field *fields; struct Declaration *constants; Location where; U8 flags; Block scope; /* to make sure that parameters and constants live somewhere. fields are not kept here. */ union { HashTable instances; struct { size_t size; /* size of this struct during compile time */ size_t align; U64 instance_id; /* ID of instance */ }; }; Identifier name; struct Declaration *params; struct { /* if name is NULL, use this */ IdentID id; } c; } StructDef; typedef enum { EXPR_LITERAL_FLOAT, EXPR_LITERAL_INT, EXPR_LITERAL_STR, EXPR_LITERAL_BOOL, EXPR_LITERAL_CHAR, EXPR_IDENT, /* variable or constant */ EXPR_BINARY_OP, EXPR_UNARY_OP, EXPR_IF, EXPR_WHILE, EXPR_FOR, EXPR_FN, EXPR_CAST, EXPR_NEW, EXPR_CALL, EXPR_BLOCK, EXPR_TUPLE, EXPR_C, EXPR_BUILTIN, EXPR_SLICE, EXPR_TYPE, EXPR_NMS, /* a value (it's useful to have this). right now they don't work with cgen_set_tuple (as of yet, that is unneeded) */ EXPR_VAL } ExprKind; typedef enum { UNARY_MINUS, UNARY_ADDRESS, /* &x */ UNARY_DEREF, /* *x */ UNARY_NOT, /* !x */ UNARY_DEL, /* del x */ UNARY_TYPEOF, /* typeof x */ UNARY_LEN, /* x.len ; replaces BINARY_DOT len when typing */ UNARY_DSIZEOF, UNARY_DALIGNOF } UnaryOp; typedef enum { BINARY_SET, /* e.g. x = y */ BINARY_ADD, BINARY_SUB, BINARY_MUL, BINARY_DIV, BINARY_MOD, BINARY_SET_ADD, /* e.g. x += y */ BINARY_SET_SUB, BINARY_SET_MUL, BINARY_SET_DIV, BINARY_SET_MOD, BINARY_GT, BINARY_LT, BINARY_GE, BINARY_LE, BINARY_EQ, BINARY_NE, BINARY_AT_INDEX, /* e.g. x[i] */ BINARY_DOT } BinaryOp; typedef struct CallExpr { struct Expression *fn; union { struct Argument *args; /* before typing */ struct Expression *arg_exprs; /* after typing */ }; struct Instance *instance; /* NULL = ordinary function, no compile time args */ } CallExpr; enum { IF_STATIC = 0x01 }; typedef struct IfExpr { U8 flags; struct Expression *cond; /* NULL = this is an else */ struct Expression *next_elif; /* next elif/else of this statement */ Block body; } IfExpr; typedef struct WhileExpr { struct Expression *cond; Block body; } WhileExpr; enum { FOR_IS_RANGE = 0x01, FOR_ANNOTATED_TYPE = 0x02, }; typedef struct ForExpr { U8 flags; Type type; /* uninitialized unless typed or flags & FOR_ANNOTATED_TYPE */ Identifier index; /* NULL = no index */ Identifier value; /* NULL = no value */ Block body; union { struct { struct Expression *from; /* can't be null */ struct Expression *to; /* can be null */ union { /* (either) can be null */ struct Expression *step; /* before typing */ Value *stepval; /* after typing */ }; } range; struct Expression *of; }; Value **val_stack; /* see Declaration for comments */ } ForExpr; enum { FN_EXPR_FOREIGN = 0x01, FN_EXPR_EXPORT = 0x02, /* set by sdecls_cgen.c */ FN_EXPR_HAS_VARARGS = 0x04 }; typedef enum { CTYPE_NONE = 0x00, CTYPE_CHAR = 0x01, CTYPE_SHORT = 0x02, CTYPE_INT = 0x03, CTYPE_LONG = 0x04, CTYPE_LONGLONG = 0x05, CTYPE_SIGNED_CHAR = 0x06, CTYPE_UNSIGNED = 0x08, CTYPE_UNSIGNED_CHAR = CTYPE_UNSIGNED|CTYPE_CHAR, CTYPE_UNSIGNED_SHORT = CTYPE_UNSIGNED|CTYPE_SHORT, CTYPE_UNSIGNED_INT = CTYPE_UNSIGNED|CTYPE_INT, CTYPE_UNSIGNED_LONG = CTYPE_UNSIGNED|CTYPE_LONG, CTYPE_UNSIGNED_LONGLONG = CTYPE_UNSIGNED|CTYPE_LONGLONG, /* things that can't be unsigned */ CTYPE_PTR = 0x10, CTYPE_FLOAT, CTYPE_DOUBLE, CTYPE_SIZE_T, CTYPE_VARARGS } CTypeKind; typedef struct { CTypeKind kind; char *points_to; /* if kind == CTYPE_PTR, ident string of C type which it points to */ } CType; typedef struct FnExpr { Location where; union { struct { struct Declaration *params; /* declarations of the parameters to this function */ struct Declaration *ret_decls; /* array of decls, if this has named return values. otherwise, NULL */ U64 instance_id; Type ret_type; Block body; }; struct { Type type; /* type of this function */ CType *ctypes; /* ctypes[i] = CTYPE_NONE if this isn't a ctype, or the specified CType. don't use this as a dynamic array. */ union { const char *name; struct Expression *name_expr; /* before typing */ }; union { const char *lib; struct Expression *lib_expr; }; void (*fn_ptr)(); } foreign; }; HashTable *instances; /* for fns with constant parameters. the key is a tuple where the first element is a u64 value whose ith bit (1<