mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 20:11:17 +00:00
1451 lines
30 KiB
C
1451 lines
30 KiB
C
#pragma once
|
|
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
|
// a copy of which can be found in the LICENSE file.
|
|
|
|
#include "../utils/common.h"
|
|
#include "../utils/errors.h"
|
|
#include "../utils/lib.h"
|
|
#include "../build/build_options.h"
|
|
#include "compiler.h"
|
|
#include "enums.h"
|
|
#include "target.h"
|
|
|
|
typedef uint32_t SourceLoc;
|
|
#define INVALID_LOC UINT32_MAX
|
|
#define INVALID_RANGE ((SourceRange){ .loc = UINT32_MAX })
|
|
#define EMPTY_TOKEN ((Token) { .string = NULL })
|
|
#define MAX_LOCALS 0xFFFF
|
|
#define MAX_SCOPE_DEPTH 0xFF
|
|
#define MAX_PATH 1024
|
|
#define MAX_DEFERS 0xFFFF
|
|
#define MAX_MACRO_NESTING 1024
|
|
#define MAX_FUNCTION_SIGNATURE_SIZE 2048
|
|
#define MAX_PARAMS 512
|
|
#define MAX_ERRORS 0xFFFF
|
|
#define MAX_ALIGNMENT (1U << 29U)
|
|
|
|
typedef struct _Ast Ast;
|
|
typedef struct _Decl Decl;
|
|
typedef struct _TypeInfo TypeInfo;
|
|
typedef struct _Expr Expr;
|
|
typedef struct _Module Module;
|
|
typedef struct _Type Type;
|
|
|
|
typedef struct _BigInt
|
|
{
|
|
unsigned digit_count;
|
|
bool is_negative;
|
|
union {
|
|
uint64_t digit;
|
|
uint64_t *digits;
|
|
};
|
|
} BigInt;
|
|
|
|
|
|
typedef struct
|
|
{
|
|
union
|
|
{
|
|
long double f;
|
|
BigInt i;
|
|
bool b;
|
|
struct
|
|
{
|
|
char* chars;
|
|
int len;
|
|
} string;
|
|
Decl *enum_constant;
|
|
Decl *error_constant;
|
|
};
|
|
// Valid type kinds:
|
|
// bool, ints, floats, string
|
|
TypeKind kind;
|
|
} ExprConst;
|
|
|
|
typedef struct
|
|
{
|
|
SourceLoc loc;
|
|
SourceLoc end_loc;
|
|
} SourceRange;
|
|
|
|
typedef struct
|
|
{
|
|
Ast *start;
|
|
Ast *end;
|
|
} DeferList;
|
|
|
|
typedef struct
|
|
{
|
|
SourceRange span;
|
|
TokenType type : 8;
|
|
union
|
|
{
|
|
const char *string;
|
|
const char* start;
|
|
};
|
|
} Token;
|
|
|
|
typedef struct _Diagnostics
|
|
{
|
|
bool panic_mode;
|
|
unsigned errors;
|
|
unsigned warnings;
|
|
} Diagnostics;
|
|
|
|
typedef struct
|
|
{
|
|
const char *key;
|
|
void *value;
|
|
} SEntry;
|
|
|
|
typedef struct
|
|
{
|
|
uint32_t count;
|
|
uint32_t capacity;
|
|
SEntry *entries;
|
|
} STable;
|
|
|
|
|
|
typedef struct
|
|
{
|
|
const char *contents;
|
|
char *name;
|
|
char *dir_path;
|
|
const char *full_path;
|
|
SourceLoc start_id;
|
|
SourceLoc end_id;
|
|
SourceLoc *lines;
|
|
SourceLoc current_line_start;
|
|
} File;
|
|
|
|
typedef struct
|
|
{
|
|
File *file;
|
|
uint32_t line;
|
|
uint32_t col;
|
|
SourceLoc loc;
|
|
const char *start;
|
|
} SourcePosition;
|
|
|
|
typedef struct _Path
|
|
{
|
|
SourceRange span;
|
|
const char *module;
|
|
uint32_t len;
|
|
} Path;
|
|
|
|
typedef enum
|
|
{
|
|
DESIGNATED_IDENT,
|
|
DESIGNATED_SUBSCRIPT,
|
|
} DesignatedPathKind;
|
|
|
|
typedef struct _DesignatedPath
|
|
{
|
|
DesignatedPathKind kind;
|
|
struct _DesignatedPath *sub_path;
|
|
Type *type;
|
|
union
|
|
{
|
|
unsigned index;
|
|
Expr *index_expr;
|
|
};
|
|
} DesignatedPath;
|
|
|
|
typedef struct
|
|
{
|
|
unsigned char bitsize;
|
|
unsigned char bytesize;
|
|
unsigned char abi_alignment;
|
|
unsigned char pref_alignment;
|
|
} TypeBuiltin;
|
|
|
|
typedef struct
|
|
{
|
|
Token name_loc;
|
|
Path *path;
|
|
} TypeUnresolved;
|
|
|
|
typedef struct
|
|
{
|
|
Type *base;
|
|
size_t len;
|
|
} TypeArray;
|
|
|
|
typedef struct
|
|
{
|
|
struct _FunctionSignature *signature;
|
|
} TypeFunc;
|
|
|
|
struct _Type
|
|
{
|
|
TypeKind type_kind : 8;
|
|
Type *canonical;
|
|
const char *name;
|
|
Type **type_cache;
|
|
void *backend_type;
|
|
void *backend_typeid;
|
|
void *backend_debug_type;
|
|
union
|
|
{
|
|
// Error, Struct, Union, Typedef
|
|
Decl *decl;
|
|
// int, float, bool
|
|
TypeBuiltin builtin;
|
|
// Type[], Type[*], Type[123]
|
|
TypeArray array;
|
|
// func Type1(Type2, Type3, ...) throws Err1, Err2, ...
|
|
TypeFunc func;
|
|
// Type*
|
|
Type *pointer;
|
|
};
|
|
};
|
|
|
|
struct _TypeInfo
|
|
{
|
|
ResolveStatus resolve_status : 2;
|
|
Type *type;
|
|
TypeInfoKind kind;
|
|
SourceRange span;
|
|
union
|
|
{
|
|
TypeUnresolved unresolved;
|
|
Expr *unresolved_type_expr;
|
|
struct
|
|
{
|
|
TypeInfo *base;
|
|
Expr *len;
|
|
} array;
|
|
TypeInfo *pointer;
|
|
};
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
Path *path;
|
|
Token name;
|
|
union
|
|
{
|
|
Expr *expr;
|
|
uint32_t alignment;
|
|
};
|
|
} Attr;
|
|
|
|
typedef struct
|
|
{
|
|
Decl **error_constants;
|
|
void *start_value;
|
|
} ErrorDecl;
|
|
|
|
typedef struct
|
|
{
|
|
Path *path;
|
|
Token symbol;
|
|
bool aliased;
|
|
} ImportDecl;
|
|
|
|
typedef struct
|
|
{
|
|
uint32_t abi_alignment;
|
|
uint32_t id;
|
|
uint64_t size;
|
|
Decl **members;
|
|
} StructDecl;
|
|
|
|
|
|
typedef struct _VarDecl
|
|
{
|
|
unsigned id : 16;
|
|
VarDeclKind kind : 3;
|
|
TypeInfo *type_info;
|
|
union
|
|
{
|
|
Expr *init_expr;
|
|
Decl *parent;
|
|
};
|
|
void *backend_debug_ref;
|
|
} VarDecl;
|
|
|
|
|
|
typedef struct
|
|
{
|
|
Expr *expr;
|
|
Decl **then;
|
|
Decl *elif;
|
|
} CtIfDecl;
|
|
|
|
|
|
typedef struct
|
|
{
|
|
Expr *expr;
|
|
Expr **args;
|
|
uint64_t ordinal;
|
|
} EnumConstantDecl;
|
|
|
|
typedef struct
|
|
{
|
|
Decl *parent;
|
|
uint32_t value;
|
|
} ErrorConstantDecl;
|
|
|
|
typedef struct
|
|
{
|
|
Decl** values;
|
|
Decl** parameters;
|
|
TypeInfo *type_info;
|
|
} EnumDecl;
|
|
|
|
typedef enum
|
|
{
|
|
ERROR_RETURN_NONE = 0,
|
|
ERROR_RETURN_ONE = 1,
|
|
ERROR_RETURN_MANY = 2,
|
|
ERROR_RETURN_ANY = 3,
|
|
} ErrorReturn;
|
|
|
|
typedef struct _FunctionSignature
|
|
{
|
|
CallConvention convention : 4;
|
|
bool variadic : 1;
|
|
bool has_default : 1;
|
|
bool throw_any : 1;
|
|
bool return_param : 1;
|
|
ErrorReturn error_return : 4;
|
|
TypeInfo *rtype;
|
|
Decl** params;
|
|
TypeInfo** throws;
|
|
const char *mangled_signature;
|
|
} FunctionSignature;
|
|
|
|
typedef struct
|
|
{
|
|
Decl **vars;
|
|
} FuncAnnotations;
|
|
|
|
typedef struct
|
|
{
|
|
struct
|
|
{
|
|
bool attr_weak : 1;
|
|
bool attr_noreturn : 1;
|
|
bool attr_inline : 1;
|
|
bool attr_noinline : 1;
|
|
bool attr_cname : 1;
|
|
bool attr_stdcall : 1;
|
|
};
|
|
|
|
TypeInfo *type_parent;
|
|
FunctionSignature function_signature;
|
|
Ast *body;
|
|
FuncAnnotations *annotations;
|
|
Decl **locals;
|
|
Ast **labels;
|
|
} FuncDecl;
|
|
|
|
typedef struct
|
|
{
|
|
AttributeDomain domains;
|
|
FunctionSignature attr_signature;
|
|
} AttrDecl;
|
|
|
|
typedef struct
|
|
{
|
|
Decl *import;
|
|
} AliasDecl;
|
|
|
|
typedef struct
|
|
{
|
|
bool is_func : 1;
|
|
union
|
|
{
|
|
FunctionSignature function_signature;
|
|
TypeInfo *type_info;
|
|
};
|
|
} TypedefDecl;
|
|
|
|
typedef struct
|
|
{
|
|
Decl **parameters;
|
|
TypeInfo *rtype; // May be null!
|
|
struct _Ast *body;
|
|
} MacroDecl;
|
|
|
|
typedef struct
|
|
{
|
|
struct _Ast **cases;
|
|
Token *parameters;
|
|
TypeInfo *rtype; // May be null!
|
|
Path *path; // For redefinition
|
|
} GenericDecl;
|
|
|
|
|
|
typedef struct _Decl
|
|
{
|
|
const char *name;
|
|
SourceRange name_span;
|
|
SourceRange span;
|
|
const char *external_name;
|
|
DeclKind decl_kind : 6;
|
|
Visibility visibility : 2;
|
|
ResolveStatus resolve_status : 2;
|
|
bool is_packed : 1;
|
|
void *ref;
|
|
const char *cname;
|
|
uint32_t alignment;
|
|
const char *section;
|
|
/* bool is_exported : 1;
|
|
bool is_used : 1;
|
|
bool is_used_public : 1;
|
|
bool has_cname : 1;
|
|
uint32_t alignment : 5;
|
|
union
|
|
{
|
|
uint32_t offset;
|
|
uint32_t counter;
|
|
};
|
|
uint32_t size;*/
|
|
Module *module;
|
|
Attr** attributes;
|
|
Type *type;
|
|
union
|
|
{
|
|
struct
|
|
{
|
|
union
|
|
{
|
|
Decl* parent_struct;
|
|
Decl** method_functions;
|
|
};
|
|
union
|
|
{
|
|
ErrorDecl error;
|
|
StructDecl strukt;
|
|
EnumDecl enums;
|
|
};
|
|
};
|
|
ErrorConstantDecl error_constant;
|
|
ImportDecl import;
|
|
VarDecl var;
|
|
EnumConstantDecl enum_constant;
|
|
FuncDecl func;
|
|
AttrDecl attr;
|
|
TypedefDecl typedef_decl;
|
|
MacroDecl macro_decl;
|
|
GenericDecl generic_decl;
|
|
CtIfDecl ct_if_decl;
|
|
CtIfDecl ct_elif_decl;
|
|
Decl** ct_else_decl;
|
|
Expr *incr_array_decl;
|
|
TypeInfo *throws;
|
|
};
|
|
} Decl;
|
|
|
|
typedef enum
|
|
{
|
|
TRY_EXPR_ELSE_EXPR,
|
|
TRY_EXPR_ELSE_JUMP,
|
|
TRY_EXPR,
|
|
TRY_STMT,
|
|
} TryType;
|
|
|
|
typedef struct
|
|
{
|
|
TryType type;
|
|
union
|
|
{
|
|
Expr *expr;
|
|
Ast *stmt;
|
|
};
|
|
union
|
|
{
|
|
Expr *else_expr;
|
|
Ast *else_stmt;
|
|
};
|
|
void *jump_target;
|
|
} ExprTry;
|
|
|
|
typedef struct
|
|
{
|
|
TypeInfo *type;
|
|
union
|
|
{
|
|
Token name;
|
|
Decl *method;
|
|
};
|
|
} ExprTypeAccess;
|
|
|
|
typedef struct
|
|
{
|
|
TypeInfo *type;
|
|
Expr *init_expr;
|
|
} ExprStructValue;
|
|
|
|
typedef struct
|
|
{
|
|
Expr *cond;
|
|
Expr *then_expr; // May be null for elvis!
|
|
Expr *else_expr;
|
|
} ExprTernary;
|
|
|
|
typedef struct
|
|
{
|
|
Expr *left;
|
|
Expr *right;
|
|
BinaryOp operator;
|
|
} ExprBinary;
|
|
|
|
typedef struct
|
|
{
|
|
Expr* expr;
|
|
UnaryOp operator;
|
|
} ExprUnary;
|
|
|
|
typedef struct
|
|
{
|
|
Expr* expr;
|
|
PostUnaryOp operator;
|
|
} ExprPostUnary;
|
|
|
|
|
|
typedef enum
|
|
{
|
|
CATCH_TRY_ELSE,
|
|
CATCH_REGULAR,
|
|
CATCH_RETURN_ANY,
|
|
CATCH_RETURN_MANY,
|
|
CATCH_RETURN_ONE
|
|
} CatchKind;
|
|
|
|
typedef struct
|
|
{
|
|
CatchKind kind;
|
|
union
|
|
{
|
|
Expr *try_else;
|
|
Ast *catch;
|
|
Decl *error;
|
|
};
|
|
Ast *defer;
|
|
} CatchInfo;
|
|
|
|
typedef struct
|
|
{
|
|
bool is_completely_handled;
|
|
Ast *defer;
|
|
CatchInfo *catches;
|
|
} ThrowInfo;
|
|
|
|
typedef struct
|
|
{
|
|
bool is_struct_function : 1;
|
|
bool is_pointer_call : 1;
|
|
Expr *function;
|
|
Expr **arguments;
|
|
ThrowInfo *throw_info;
|
|
} ExprCall;
|
|
|
|
typedef struct
|
|
{
|
|
Expr *expr;
|
|
Expr *index;
|
|
} ExprSubscript;
|
|
|
|
typedef struct
|
|
{
|
|
Expr *parent;
|
|
union
|
|
{
|
|
Token sub_element;
|
|
Decl *ref;
|
|
};
|
|
} ExprAccess;
|
|
|
|
typedef struct
|
|
{
|
|
Path *path;
|
|
const char *identifier;
|
|
bool is_ref;
|
|
Decl *decl;
|
|
} ExprIdentifier;
|
|
|
|
|
|
typedef struct
|
|
{
|
|
CastKind kind;
|
|
Expr *expr;
|
|
TypeInfo *type_info;
|
|
union
|
|
{
|
|
size_t truncated_size;
|
|
};
|
|
} ExprCast;
|
|
|
|
typedef struct
|
|
{
|
|
Expr *expr;
|
|
DeferList defers;
|
|
} ExprScope;
|
|
|
|
typedef struct
|
|
{
|
|
Ast **stmts;
|
|
} ExprFuncBlock;
|
|
|
|
typedef struct
|
|
{
|
|
Expr *left;
|
|
Expr *right;
|
|
} ExprRange;
|
|
|
|
typedef enum
|
|
{
|
|
INITIALIZER_UNKNOWN,
|
|
INITIALIZER_ZERO,
|
|
INITIALIZER_DESIGNATED,
|
|
INITIALIZER_NORMAL
|
|
} InitializerType;
|
|
|
|
typedef struct
|
|
{
|
|
InitializerType init_type;
|
|
Expr** initializer_expr;
|
|
} ExprInitializer;
|
|
|
|
typedef struct
|
|
{
|
|
Expr *initializer;
|
|
TypeInfo *type_info;
|
|
} ExprCompoundLiteral;
|
|
|
|
typedef struct
|
|
{
|
|
DesignatedPath *path;
|
|
Expr* value;
|
|
} ExprDesignatedInit;
|
|
|
|
struct _Expr
|
|
{
|
|
ExprKind expr_kind : 8;
|
|
ResolveStatus resolve_status : 3;
|
|
SourceRange span;
|
|
Type *type;
|
|
union {
|
|
ExprDesignatedInit designated_init_expr;
|
|
Expr *group_expr;
|
|
ExprCast cast_expr;
|
|
Expr *typeof_expr;
|
|
ExprConst const_expr;
|
|
ExprRange range_expr;
|
|
ExprStructValue struct_value_expr;
|
|
ExprTypeAccess type_access;
|
|
ExprTry try_expr;
|
|
Expr* macro_expr;
|
|
ExprBinary binary_expr;
|
|
ExprTernary ternary_expr;
|
|
ExprUnary unary_expr;
|
|
ExprPostUnary post_expr;
|
|
ExprCall call_expr;
|
|
ExprSubscript subscript_expr;
|
|
ExprAccess access_expr;
|
|
ExprIdentifier identifier_expr;
|
|
TypeInfo *typeid_expr;
|
|
ExprInitializer expr_initializer;
|
|
ExprCompoundLiteral expr_compound_literal;
|
|
Expr** expression_list;
|
|
ExprScope expr_scope;
|
|
ExprFuncBlock expr_block;
|
|
};
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
|
|
} AstAttribute;
|
|
|
|
|
|
typedef struct
|
|
{
|
|
struct _Ast **stmts;
|
|
DeferList defer_list;
|
|
} AstCompoundStmt;
|
|
|
|
typedef struct
|
|
{
|
|
const char *name;
|
|
uint16_t last_goto;
|
|
bool is_used : 1;
|
|
Ast *defer;
|
|
struct _Ast *in_defer;
|
|
void *backend_value;
|
|
} AstLabelStmt;
|
|
|
|
typedef struct
|
|
{
|
|
Expr *expr; // May be NULL
|
|
Ast *defer;
|
|
} AstReturnStmt;
|
|
|
|
typedef struct
|
|
{
|
|
Ast *decl;
|
|
Ast *cond;
|
|
Ast *body;
|
|
} AstWhileStmt;
|
|
|
|
typedef struct
|
|
{
|
|
Expr *expr;
|
|
Ast *body;
|
|
DeferList expr_defer;
|
|
DeferList body_defer;
|
|
} AstDoStmt;
|
|
|
|
typedef struct
|
|
{
|
|
Ast *decl;
|
|
Ast *cond;
|
|
Ast *then_body;
|
|
Ast *else_body;
|
|
} AstIfStmt;
|
|
|
|
|
|
typedef struct
|
|
{
|
|
Expr *expr; // NULL == DEFAULT
|
|
Ast *body;
|
|
void *backend_value;
|
|
} AstCaseStmt;
|
|
|
|
|
|
typedef struct
|
|
{
|
|
Ast *decl;
|
|
Ast *cond;
|
|
Ast **cases;
|
|
} AstSwitchStmt;
|
|
|
|
typedef struct
|
|
{
|
|
Ast *init;
|
|
Expr *cond;
|
|
Expr *incr;
|
|
Ast *body;
|
|
} AstForStmt;
|
|
|
|
|
|
|
|
typedef struct
|
|
{
|
|
const char *label_name;
|
|
Ast *label;
|
|
DeferList defer;
|
|
union
|
|
{
|
|
struct _Ast *in_defer;
|
|
};
|
|
} AstGotoStmt;
|
|
|
|
typedef struct _AstDeferStmt
|
|
{
|
|
bool emit_boolean : 1;
|
|
Ast *body; // Compound statement
|
|
Ast *prev_defer;
|
|
void *bool_var;
|
|
} AstDeferStmt;
|
|
|
|
typedef struct _AstCatchStmt
|
|
{
|
|
Decl *error_param;
|
|
struct _Ast *body;
|
|
void *block;
|
|
} AstCatchStmt;
|
|
|
|
typedef struct _AstCtIfStmt
|
|
{
|
|
Expr *expr;
|
|
struct _Ast *then;
|
|
struct _Ast *elif;
|
|
} AstCtIfStmt;
|
|
|
|
|
|
typedef struct _AstGenericCaseStmt
|
|
{
|
|
TypeInfo **types;
|
|
struct _Ast *body;
|
|
} AstGenericCaseStmt;
|
|
|
|
typedef struct
|
|
{
|
|
Ast *stmt;
|
|
DeferList defers;
|
|
} AstScopedStmt;
|
|
|
|
typedef struct
|
|
{
|
|
Expr *cond;
|
|
Ast **body;
|
|
} AstCtSwitchStmt;
|
|
|
|
typedef struct
|
|
{
|
|
TypeInfo **types;
|
|
Ast *body;
|
|
} AstCtCaseStmt;
|
|
|
|
typedef struct
|
|
{
|
|
Token index;
|
|
Token value;
|
|
Expr *expr;
|
|
Ast *body;
|
|
} AstCtForStmt;
|
|
|
|
typedef struct
|
|
{
|
|
DeferList defers;
|
|
} AstContinueBreakStmt;
|
|
|
|
typedef struct
|
|
{
|
|
Ast *prev;
|
|
DeferList defers;
|
|
} AstNextStmt;
|
|
|
|
typedef struct
|
|
{
|
|
Expr *throw_value;
|
|
Ast *defer;
|
|
} AstThrowStmt;
|
|
|
|
typedef struct
|
|
{
|
|
Token name;
|
|
Token constraints;
|
|
Token token;
|
|
} AsmOperand;
|
|
|
|
typedef struct
|
|
{
|
|
bool is_volatile : 1;
|
|
bool is_inline : 1;
|
|
bool is_goto : 1;
|
|
Expr *asm_template;
|
|
AsmOperand** output_operands;
|
|
AsmOperand** input_operands;
|
|
AsmOperand** clobbers;
|
|
Token* labels;
|
|
} AstAsmStmt;
|
|
|
|
typedef struct _Ast
|
|
{
|
|
SourceRange span;
|
|
AstKind ast_kind : 8;
|
|
union
|
|
{
|
|
AstAttribute attribute;
|
|
AstAsmStmt asm_stmt;
|
|
AstCompoundStmt compound_stmt;
|
|
Decl *declare_stmt;
|
|
Expr *expr_stmt;
|
|
AstThrowStmt throw_stmt;
|
|
Ast *volatile_stmt;
|
|
AstLabelStmt label_stmt;
|
|
AstReturnStmt return_stmt;
|
|
AstWhileStmt while_stmt;
|
|
AstDoStmt do_stmt;
|
|
AstIfStmt if_stmt;
|
|
AstDeferStmt defer_stmt;
|
|
AstSwitchStmt switch_stmt;
|
|
AstCaseStmt case_stmt;
|
|
AstCtSwitchStmt ct_switch_stmt;
|
|
AstCtCaseStmt ct_case_stmt;
|
|
AstContinueBreakStmt continue_stmt;
|
|
AstContinueBreakStmt break_stmt;
|
|
Ast* ct_default_stmt;
|
|
AstNextStmt next_stmt;
|
|
AstCatchStmt catch_stmt;
|
|
AstGotoStmt goto_stmt;
|
|
AstForStmt for_stmt;
|
|
AstCtIfStmt ct_if_stmt;
|
|
AstCtIfStmt ct_elif_stmt;
|
|
Ast *ct_else_stmt;
|
|
AstCtForStmt ct_for_stmt;
|
|
AstGenericCaseStmt generic_case_stmt;
|
|
Ast *generic_default_stmt;
|
|
Ast** decl_expr_stmt;
|
|
AstScopedStmt scoped_stmt;
|
|
};
|
|
} Ast;
|
|
|
|
|
|
typedef struct _Module
|
|
{
|
|
Path *name;
|
|
|
|
bool is_external;
|
|
bool is_c_library;
|
|
bool is_exported;
|
|
|
|
Ast **files; // Asts
|
|
|
|
Decl** functions;
|
|
STable struct_functions;
|
|
STable symbols;
|
|
STable public_symbols;
|
|
Module **sub_modules;
|
|
} Module;
|
|
|
|
|
|
typedef struct _DynamicScope
|
|
{
|
|
ScopeFlags flags;
|
|
ScopeFlags flags_created;
|
|
unsigned throws;
|
|
Decl **local_decl_start;
|
|
DeferList defers;
|
|
ExitType exit;
|
|
} DynamicScope;
|
|
|
|
|
|
typedef struct
|
|
{
|
|
const char *file_begin;
|
|
const char *lexing_start;
|
|
const char *current;
|
|
uint16_t source_file;
|
|
File *current_file;
|
|
SourceLoc last_in_range;
|
|
Token tok;
|
|
Token next_tok;
|
|
} Lexer;
|
|
|
|
typedef enum
|
|
{
|
|
THROW_TYPE_CALL_ANY,
|
|
THROW_TYPE_CALL_THROW_MANY,
|
|
THROW_TYPE_CALL_THROW_ONE,
|
|
} ThrowType;
|
|
|
|
typedef struct
|
|
{
|
|
SourceRange span;
|
|
ThrowType kind : 4;
|
|
ThrowInfo *throw_info;
|
|
TypeInfo **throws;
|
|
} Throw;
|
|
|
|
typedef struct _Context
|
|
{
|
|
BuildTarget *target;
|
|
Path *module_name;
|
|
Token* module_parameters;
|
|
File* file;
|
|
Decl** imports;
|
|
Decl *specified_imports;
|
|
Module *module;
|
|
STable local_symbols;
|
|
Decl **enums;
|
|
Decl **error_types;
|
|
Decl **types;
|
|
Decl **functions;
|
|
Decl **struct_functions;
|
|
Decl **vars;
|
|
Decl **ct_ifs;
|
|
Ast **defers;
|
|
Decl *active_function_for_analysis;
|
|
Decl *active_type_for_analysis;
|
|
Decl **last_local;
|
|
Ast **labels;
|
|
Ast **gotos;
|
|
Token *comments;
|
|
Token *lead_comment;
|
|
Token *trailing_comment;
|
|
Token *next_lead_comment;
|
|
DynamicScope *current_scope;
|
|
struct
|
|
{
|
|
Type *expected_block_type;
|
|
Ast **returns;
|
|
// Reusable returns cache.
|
|
Ast **returns_cache;
|
|
};
|
|
Decl *evaluating_macro;
|
|
// Error handling
|
|
struct
|
|
{
|
|
Ast **throw;
|
|
Throw *error_calls;
|
|
int try_nesting;
|
|
};
|
|
Type *rtype;
|
|
int in_volatile_section;
|
|
struct
|
|
{
|
|
bool in_macro_call : 1;
|
|
bool in_macro : 1;
|
|
int macro_counter;
|
|
int macro_nesting;
|
|
Expr *first_macro_call;
|
|
};
|
|
Decl *locals[MAX_LOCALS];
|
|
DynamicScope scopes[MAX_SCOPE_DEPTH];
|
|
char path_scratch[MAX_PATH];
|
|
struct {
|
|
STable external_symbols;
|
|
Decl **external_symbol_list;
|
|
};
|
|
STable scratch_table;
|
|
Lexer lexer;
|
|
SourceLoc prev_tok_end;
|
|
Token tok;
|
|
Token next_tok;
|
|
struct
|
|
{
|
|
bool in_lookahead;
|
|
const char *current;
|
|
const char *start;
|
|
Token tok;
|
|
Token next_tok;
|
|
Token *lead_comment;
|
|
Token *trailing_comment;
|
|
Token *next_lead_comment;
|
|
} stored;
|
|
} Context;
|
|
|
|
typedef struct
|
|
{
|
|
STable modules;
|
|
STable global_symbols;
|
|
STable qualified_symbols;
|
|
Type **type;
|
|
} Compiler;
|
|
|
|
extern Compiler compiler;
|
|
extern Ast *poisoned_ast;
|
|
extern Decl *poisoned_decl;
|
|
extern Expr *poisoned_expr;
|
|
extern Type *poisoned_type;
|
|
extern TypeInfo *poisoned_type_info;
|
|
extern Diagnostics diagnostics;
|
|
|
|
|
|
extern Type *type_bool, *type_void, *type_string, *type_voidptr;
|
|
extern Type *type_float, *type_double;
|
|
extern Type *type_char, *type_short, *type_int, *type_long, *type_isize;
|
|
extern Type *type_byte, *type_ushort, *type_uint, *type_ulong, *type_usize;
|
|
extern Type *type_compint, *type_compfloat;
|
|
extern Type *type_c_short, *type_c_int, *type_c_long, *type_c_longlong;
|
|
extern Type *type_c_ushort, *type_c_uint, *type_c_ulong, *type_c_ulonglong;
|
|
extern Type *type_typeid, *type_error_union, *type_error_base;
|
|
|
|
extern const char *attribute_list[NUMBER_OF_ATTRIBUTES];
|
|
|
|
extern const char *main_kw;
|
|
|
|
#define AST_NEW_TOKEN(_kind, _token) new_ast(_kind, _token.span)
|
|
#define AST_NEW(_kind, _loc) new_ast(_kind, _loc)
|
|
|
|
static inline bool ast_ok(Ast *ast) { return ast == NULL || ast->ast_kind != AST_POISONED; }
|
|
static inline bool ast_poison(Ast *ast) { ast->ast_kind = AST_POISONED; return false; }
|
|
|
|
static inline Ast *new_ast(AstKind kind, SourceRange range)
|
|
{
|
|
Ast *ast = malloc_arena(sizeof(Ast));
|
|
memset(ast, 0, sizeof(Ast));
|
|
ast->span = range;
|
|
ast->ast_kind = kind;
|
|
return ast;
|
|
}
|
|
|
|
static inline Ast *extend_ast(Ast *ast, Token token)
|
|
{
|
|
ast->span.end_loc = token.span.end_loc;
|
|
return ast;
|
|
}
|
|
|
|
static inline Ast *extend_ast_with_prev_token(Context *context, Ast *ast)
|
|
{
|
|
ast->span.end_loc = context->prev_tok_end;
|
|
return ast;
|
|
}
|
|
|
|
|
|
|
|
void builtin_setup(Target *target);
|
|
|
|
static inline bool builtin_may_negate(Type *canonical)
|
|
{
|
|
assert(canonical->canonical == canonical);
|
|
switch (canonical->type_kind)
|
|
{
|
|
case TYPE_FXX:
|
|
case TYPE_F32:
|
|
case TYPE_F64:
|
|
case TYPE_I8:
|
|
case TYPE_I16:
|
|
case TYPE_I32:
|
|
case TYPE_I64:
|
|
case TYPE_IXX:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static inline bool builtin_may_bit_negate(Type *canonical)
|
|
{
|
|
assert(canonical->canonical == canonical);
|
|
switch (canonical->type_kind)
|
|
{
|
|
case TYPE_BOOL:
|
|
case TYPE_I8:
|
|
case TYPE_I16:
|
|
case TYPE_I32:
|
|
case TYPE_I64:
|
|
case TYPE_IXX:
|
|
case TYPE_U8:
|
|
case TYPE_U16:
|
|
case TYPE_U32:
|
|
case TYPE_U64:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
bool cast_implicit(Expr *expr, Type *to_type);
|
|
bool cast(Expr *expr, Type *to_type, CastType cast_type);
|
|
bool cast_binary_arithmetic(Expr *left, Expr *right, const char *action);
|
|
CastKind cast_to_bool_kind(Type *type);
|
|
bool cast_to_runtime(Expr *expr);
|
|
void cast_to_smallest_runtime(Expr *expr);
|
|
|
|
void llvm_codegen(Context *context);
|
|
void llvm_codegen_setup();
|
|
|
|
|
|
bool sema_analyse_expr_of_required_type(Context *context, Type *to, Expr *expr);
|
|
bool sema_analyse_expr(Context *context, Type *to, Expr *expr);
|
|
bool sema_analyse_decl(Context *context, Decl *decl);
|
|
|
|
void compiler_add_type(Type *type);
|
|
Decl *compiler_find_symbol(Token token);
|
|
Module *compiler_find_or_create_module(Path *module_name);
|
|
void compiler_register_public_symbol(Decl *decl);
|
|
|
|
Context *context_create(File *file, BuildTarget *target);
|
|
void context_register_global_decl(Context *context, Decl *decl);
|
|
void context_register_external_symbol(Context *context, Decl *decl);
|
|
bool context_add_import(Context *context, Path *path, Token symbol, Token alias);
|
|
bool context_set_module_from_filename(Context *context);
|
|
bool context_set_module(Context *context, Path *path, Token *generic_parameters);
|
|
void context_print_ast(Context *context, FILE *file);
|
|
|
|
#pragma mark --- Decl functions
|
|
|
|
Decl *decl_new(DeclKind decl_kind, Token name, Visibility visibility);
|
|
Decl *decl_new_with_type(Token name, DeclKind decl_type, Visibility visibility);
|
|
Decl *decl_new_var(Token name, TypeInfo *type, VarDeclKind kind, Visibility visibility);
|
|
void decl_set_external_name(Decl *decl);
|
|
const char *decl_var_to_string(VarDeclKind kind);
|
|
|
|
static inline bool decl_ok(Decl *decl) { return decl->decl_kind != DECL_POISONED; }
|
|
static inline bool decl_poison(Decl *decl) { decl->decl_kind = DECL_POISONED; decl->resolve_status = RESOLVE_DONE; return false; }
|
|
static inline bool decl_is_struct_type(Decl *decl) { return decl->decl_kind == DECL_UNION || decl->decl_kind == DECL_STRUCT; }
|
|
static inline DeclKind decl_from_token(TokenType type)
|
|
{
|
|
if (type == TOKEN_STRUCT) return DECL_STRUCT;
|
|
if (type == TOKEN_UNION) return DECL_UNION;
|
|
UNREACHABLE
|
|
}
|
|
|
|
#pragma mark --- Diag functions
|
|
|
|
void diag_reset(void);
|
|
void diag_error_range(SourceRange span, const char *message, ...);
|
|
void diag_verror_range(SourceRange span, const char *message, va_list args);
|
|
|
|
|
|
#define EXPR_NEW_EXPR(_kind, _expr) expr_new(_kind, _expr->span)
|
|
#define EXPR_NEW_TOKEN(_kind, _tok) expr_new(_kind, _tok.span)
|
|
Expr *expr_new(ExprKind kind, SourceRange start);
|
|
static inline bool expr_ok(Expr *expr) { return expr == NULL || expr->expr_kind != EXPR_POISONED; }
|
|
static inline bool expr_poison(Expr *expr) { expr->expr_kind = EXPR_POISONED; expr->resolve_status = RESOLVE_DONE; return false; }
|
|
static inline void expr_replace(Expr *expr, Expr *replacement)
|
|
{
|
|
SourceRange loc = expr->span;
|
|
*expr = *replacement;
|
|
expr->span = loc;
|
|
}
|
|
void expr_const_set_int(ExprConst *expr, uint64_t v, TypeKind kind);
|
|
void expr_const_set_float(ExprConst *expr, long double d, TypeKind kind);
|
|
void expr_const_set_bool(ExprConst *expr, bool b);
|
|
void expr_const_set_nil(ExprConst *expr);
|
|
void expr_const_fprint(FILE *__restrict file, ExprConst *expr);
|
|
bool expr_const_int_overflowed(const ExprConst *expr);
|
|
bool expr_const_compare(const ExprConst *left, const ExprConst *right, BinaryOp op);
|
|
const char *expr_const_to_error_string(const ExprConst *expr);
|
|
|
|
void fprint_decl(FILE *file, Decl *dec);
|
|
void fprint_type_info_recursive(FILE *file, TypeInfo *type_info, int indent);
|
|
void fprint_expr_recursive(FILE *file, Expr *expr, int indent);
|
|
|
|
|
|
#pragma mark --- Lexer functions
|
|
|
|
Token lexer_scan_token(Lexer *lexer);
|
|
Token lexer_scan_ident_test(Lexer *lexer, const char *scan);
|
|
void lexer_init_for_test(Lexer *lexer, const char *text, size_t len);
|
|
void lexer_init_with_file(Lexer *lexer, File *file);
|
|
File* lexer_current_file(Lexer *lexer);
|
|
|
|
|
|
typedef enum
|
|
{
|
|
MODULE_SYMBOL_SEARCH_EXTERNAL,
|
|
MODULE_SYMBOL_SEARCH_PARENT,
|
|
MODULE_SYMBOL_SEARCH_THIS
|
|
} ModuleSymbolSearch;
|
|
|
|
Decl *module_find_symbol(Module *module, const char *symbol, ModuleSymbolSearch search);
|
|
|
|
void parse_file(Context *context);
|
|
Path *path_create_from_string(const char *string, size_t len, SourceRange span);
|
|
Path *path_find_parent_path(Path *path);
|
|
|
|
const char *resolve_status_to_string(ResolveStatus status);
|
|
|
|
#define SEMA_TOKEN_ERROR(_tok, ...) sema_error_range(_tok.span, __VA_ARGS__)
|
|
#define SEMA_ERROR(_node, ...) sema_error_range(_node->span, __VA_ARGS__)
|
|
#define SEMA_PREV(_node, ...) sema_prev_at_range(_node->span, __VA_ARGS__)
|
|
void sema_analysis_pass_process_imports(Context *context);
|
|
void sema_analysis_pass_conditional_compilation(Context *context);
|
|
void sema_analysis_pass_decls(Context *context);
|
|
|
|
bool sema_add_local(Context *context, Decl *decl);
|
|
bool sema_analyse_statement(Context *context, Ast *statement);
|
|
Decl *sema_resolve_symbol(Context *context, const char *symbol, Path *path, Decl **ambiguous_other_decl);
|
|
bool sema_resolve_type_info(Context *context, TypeInfo *type_info);
|
|
bool sema_resolve_type_shallow(Context *context, TypeInfo *type_info);
|
|
void sema_error_at(SourceLoc loc, const char *message, ...);
|
|
void sema_error_range(SourceRange range, const char *message, ...);
|
|
void sema_verror_at(SourceLoc loc, const char *message, va_list args);
|
|
void sema_verror_range(SourceRange range, const char *message, va_list args);
|
|
void sema_error(Context *context, const char *message, ...);
|
|
void sema_prev_at_range(SourceRange span, const char *message, ...);
|
|
void sema_prev_at(SourceLoc loc, const char *message, ...);
|
|
void sema_shadow_error(Decl *decl, Decl *old);
|
|
|
|
File *source_file_load(const char *filename, bool *already_loaded);
|
|
File *source_file_from_position(SourceLoc loc);
|
|
void source_file_append_line_end(File *file, SourceLoc loc);
|
|
SourcePosition source_file_find_position_in_file(File *file, SourceLoc loc);
|
|
SourcePosition source_file_find_position(SourceLoc loc);
|
|
SourceRange source_range_from_ranges(SourceRange first, SourceRange last);
|
|
#define RANGE_EXTEND_PREV(x) (x)->span.end_loc = context->prev_tok_end
|
|
|
|
static inline unsigned source_range_len(SourceRange range) { return range.end_loc - range.loc; }
|
|
|
|
void stable_init(STable *table, uint32_t initial_size);
|
|
void *stable_set(STable *table, const char *key, void *value);
|
|
void *stable_get(STable *table, const char *key);
|
|
void *stable_delete(STable *table, const char *key);
|
|
void stable_clear(STable *table);
|
|
|
|
const char *symtab_add(const char *symbol, uint32_t len, uint32_t fnv1hash, TokenType *type);
|
|
|
|
void target_setup();
|
|
int target_alloca_addr_space();
|
|
void *target_data_layout();
|
|
void *target_machine();
|
|
void *target_target();
|
|
|
|
bool throw_completely_caught(TypeInfo *throw, CatchInfo *catches);
|
|
static inline Throw throw_new_union(SourceRange range, ThrowType type, ThrowInfo *info)
|
|
{
|
|
return (Throw) { .kind = THROW_TYPE_CALL_ANY, .span = range, .throw_info = info };
|
|
}
|
|
static inline Throw throw_new(SourceRange range, ThrowType type, ThrowInfo *info, TypeInfo **throws)
|
|
{
|
|
return (Throw) { .kind = type, .span = range, .throw_info = info, .throws = throws };
|
|
}
|
|
|
|
#define TOKEN_MAX_LENGTH 0xFFFF
|
|
#define TOK2VARSTR(_token) _token.span.length, _token.start
|
|
bool token_is_type(TokenType type);
|
|
bool token_is_symbol(TokenType type);
|
|
const char *token_type_to_string(TokenType type);
|
|
static inline Token wrap(const char *string)
|
|
{
|
|
return (Token) { .span = INVALID_RANGE, .type = TOKEN_IDENT, .string = string };
|
|
}
|
|
|
|
Type *type_get_ptr(Type *ptr_type);
|
|
Type *type_get_subarray(Type *arr_type);
|
|
Type *type_get_vararray(Type *arr_type);
|
|
Type *type_get_meta(Type *meta_type);
|
|
Type *type_get_indexed_type(Type *type);
|
|
Type *type_get_array(Type *arr_type, uint64_t len);
|
|
Type *type_signed_int_by_bitsize(unsigned bytesize);
|
|
Type *type_unsigned_int_by_bitsize(unsigned bytesize);
|
|
bool type_is_subtype(Type *type, Type *possible_subtype);
|
|
Type *type_find_common_ancestor(Type *left, Type *right);
|
|
const char *type_to_error_string(Type *type);
|
|
size_t type_size(Type *canonical);
|
|
unsigned int type_abi_alignment(Type *canonical);
|
|
void type_append_signature_name(Type *type, char *dst, size_t *offset);
|
|
Type *type_find_max_type(Type *type, Type *other);
|
|
|
|
static inline bool type_is_builtin(TypeKind kind) { return kind >= TYPE_VOID && kind <= TYPE_FXX; }
|
|
static inline bool type_kind_is_signed(TypeKind kind) { return kind >= TYPE_I8 && kind <= TYPE_I64; }
|
|
static inline bool type_kind_is_unsigned(TypeKind kind) { return kind >= TYPE_U8 && kind <= TYPE_U64; }
|
|
static inline bool type_kind_is_any_integer(TypeKind kind) { return kind >= TYPE_I8 && kind <= TYPE_IXX; }
|
|
static inline bool type_is_signed(Type *type) { return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_I64; }
|
|
static inline bool type_is_unsigned(Type *type) { return type->type_kind >= TYPE_U8 && type->type_kind <= TYPE_U64; }
|
|
static inline bool type_ok(Type *type) { return !type || type->type_kind != TYPE_POISONED; }
|
|
static inline bool type_info_ok(TypeInfo *type_info) { return !type_info || type_info->kind != TYPE_INFO_POISON; }
|
|
bool type_may_have_method_functions(Type *type);
|
|
|
|
static inline Type *type_reduced(Type *type)
|
|
{
|
|
Type *canonical = type->canonical;
|
|
if (canonical->type_kind == TYPE_ENUM) return canonical->decl->enums.type_info->type->canonical;
|
|
if (canonical->type_kind == TYPE_ERROR) return type_error_base;
|
|
return canonical;
|
|
}
|
|
|
|
static inline bool type_is_structlike(Type *type)
|
|
{
|
|
assert(type->canonical == type);
|
|
switch (type->type_kind)
|
|
{
|
|
case TYPE_UNION:
|
|
case TYPE_STRUCT:
|
|
return true;
|
|
default:
|
|
return false;
|
|
|
|
}
|
|
}
|
|
|
|
static inline Type *type_reduced_from_expr(Expr *expr)
|
|
{
|
|
return type_reduced(expr->type);
|
|
}
|
|
|
|
|
|
static inline bool type_is_integer(Type *type)
|
|
{
|
|
assert(type == type->canonical);
|
|
return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_U64;
|
|
}
|
|
|
|
static inline bool type_is_any_integer(Type *type)
|
|
{
|
|
assert(type == type->canonical);
|
|
return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_IXX;
|
|
}
|
|
|
|
static inline bool type_is_signed_integer(Type *type)
|
|
{
|
|
assert(type == type->canonical);
|
|
return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_I64;
|
|
}
|
|
|
|
static inline bool type_is_unsigned_integer(Type *type)
|
|
{
|
|
assert(type == type->canonical);
|
|
return type->type_kind >= TYPE_U8 && type->type_kind <= TYPE_U64;
|
|
}
|
|
|
|
static inline bool type_info_poison(TypeInfo *type)
|
|
{
|
|
type->type = poisoned_type;
|
|
type->resolve_status = RESOLVE_DONE;
|
|
return false;
|
|
}
|
|
|
|
static inline bool type_is_float(Type *type)
|
|
{
|
|
assert(type == type->canonical);
|
|
return type->type_kind >= TYPE_F32 && type->type_kind <= TYPE_FXX;
|
|
}
|
|
|
|
static inline TypeInfo *type_info_new(TypeInfoKind kind, SourceRange range)
|
|
{
|
|
TypeInfo *type_info = malloc_arena(sizeof(TypeInfo));
|
|
memset(type_info, 0, sizeof(TypeInfo));
|
|
type_info->kind = kind;
|
|
type_info->span = range;
|
|
type_info->resolve_status = RESOLVE_NOT_DONE;
|
|
return type_info;
|
|
}
|
|
|
|
static inline TypeInfo *type_info_new_base(Type *type, SourceRange range)
|
|
{
|
|
TypeInfo *type_info = malloc_arena(sizeof(TypeInfo));
|
|
memset(type_info, 0, sizeof(TypeInfo));
|
|
type_info->kind = TYPE_INFO_IDENTIFIER;
|
|
type_info->resolve_status = RESOLVE_DONE;
|
|
type_info->type = type;
|
|
type_info->span = range;
|
|
return type_info;
|
|
}
|
|
|
|
static inline Type *type_new(TypeKind kind, const char *name)
|
|
{
|
|
Type *type = malloc_arena(sizeof(Type));
|
|
memset(type, 0, sizeof(Type));
|
|
type->type_kind = kind;
|
|
type->name = name;
|
|
compiler_add_type(type);
|
|
return type;
|
|
}
|
|
|
|
|
|
static inline bool type_convert_will_trunc(Type *destination, Type *source)
|
|
{
|
|
assert(type_is_builtin(destination->canonical->type_kind));
|
|
assert(type_is_builtin(source->canonical->type_kind));
|
|
return (unsigned)destination->canonical->builtin.bitsize < (unsigned)source->canonical->builtin.bitsize;
|
|
}
|
|
|
|
static inline bool type_is_numeric(Type *type)
|
|
{
|
|
assert(type == type->canonical);
|
|
return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_FXX;
|
|
}
|
|
|
|
|
|
#define TYPE_MODULE_UNRESOLVED(_module, _name) ({ Type *__type = type_new(TYPE_USER_DEFINED); \
|
|
__type->name_loc = _name; __type->unresolved.module = _module; __type; })
|
|
#define TYPE_UNRESOLVED(_name) ({ TypeInfo *__type = type_new(TYPE_USER_DEFINED); __type->name_loc = _name; __type; })
|
|
|
|
UnaryOp unaryop_from_token(TokenType type);
|
|
TokenType unaryop_to_token(UnaryOp type);
|
|
PostUnaryOp post_unaryop_from_token(TokenType type);
|
|
TokenType postunaryop_to_token(PostUnaryOp type);
|
|
BinaryOp binaryop_from_token(TokenType type);
|
|
BinaryOp binaryop_assign_base_op(BinaryOp assign_binary_op);
|
|
TokenType binaryop_to_token(BinaryOp type);
|
|
|
|
|
|
Decl *struct_find_name(Decl *decl, const char* name);
|
|
|
|
|
|
static inline const char* struct_union_name_from_token(TokenType type)
|
|
{
|
|
return type == TOKEN_STRUCT ? "struct" : "union";
|
|
}
|
|
|
|
|
|
void advance(Context *context);
|
|
|
|
// Useful sanity check function.
|
|
static inline void advance_and_verify(Context *context, TokenType token_type)
|
|
{
|
|
assert(context->tok.type == token_type);
|
|
advance(context);
|
|
} |