@echo off
-set SOURCE_FILES=src/onyx.c src/onyxastnodes.c src/onyxbuiltins.c src/onyxchecker.c src/onyxclone.c src/onyxdoc.c src/onyxentities.c src/onyxerrors.c src/onyxlex.c src/onyxparser.c src/onyxsymres.c src/onyxtypes.c src/onyxutils.c src/onyxwasm.c
+set SOURCE_FILES=src/onyx.c src/astnodes.c src/builtins.c src/checker.c src/clone.c src/doc.c src/entities.c src/errors.c src/lex.c src/parser.c src/symres.c src/types.c src/utils.c src/wasm.c
if "%1" == "1" (
set FLAGS=/O2 /MT /Z7
cl.exe %FLAGS% /I include /std:c17 /Tc %SOURCE_FILES% /link /DEBUG /OUT:onyx.exe /incremental:no /opt:ref /subsystem:console
-del *.obj > NUL 2> NUL
\ No newline at end of file
+del *.obj > NUL 2> NUL
[ "$1" = "libs_only" ] && exit 0
-C_FILES="onyx onyxastnodes onyxbuiltins onyxchecker onyxclone onyxdoc onyxentities onyxerrors onyxlex onyxparser onyxsymres onyxtypes onyxutils onyxwasm"
+C_FILES="onyx astnodes builtins checker clone doc entities errors lex parser symres types utils wasm"
CC='gcc'
WARNINGS='-Wimplicit -Wmisleading-indentation -Wparentheses -Wsequence-point -Wreturn-type -Wshift-negative-value -Wunused-but-set-parameter -Wunused-but-set-variable -Wunused-function -Wunused-label -Wmaybe-uninitialized -Wsign-compare -Wstrict-overflow -Wduplicated-branches -Wduplicated-cond -Wtrigraphs -Waddress -Wlogical-op'
println(x); // Doesn't work
}
+[ ] Memory reservation type checking doesn't work how I think it should. This errors for example:
+
+ x: i64 = 1;
+
[X] Initialization statements on control statements should be better. For example, this should
be possible:
--- /dev/null
+#ifndef ONYXASTNODES_H
+#define ONYXASTNODES_H
+
+#include "lex.h"
+#include "types.h"
+
+#define AST_NODES \
+ NODE(Node) \
+ NODE(Typed) \
+ \
+ NODE(NamedValue) \
+ NODE(BinaryOp) \
+ NODE(UnaryOp) \
+ NODE(NumLit) \
+ NODE(StrLit) \
+ NODE(Local) \
+ NODE(Call) \
+ NODE(Argument) \
+ NODE(AddressOf) \
+ NODE(Dereference) \
+ NODE(Subscript) \
+ NODE(FieldAccess) \
+ NODE(UnaryFieldAccess) \
+ NODE(SizeOf) \
+ NODE(AlignOf) \
+ NODE(FileContents) \
+ NODE(StructLiteral) \
+ NODE(ArrayLiteral) \
+ NODE(RangeLiteral) \
+ NODE(Compound) \
+ NODE(IfExpression) \
+ NODE(DoBlock) \
+ \
+ NODE(DirectiveSolidify) \
+ NODE(DirectiveError) \
+ NODE(DirectiveAddOverload) \
+ NODE(DirectiveOperator) \
+ NODE(DirectiveExport) \
+ NODE(DirectiveDefined) \
+ \
+ NODE(Return) \
+ NODE(Jump) \
+ NODE(Use) \
+ \
+ NODE(Block) \
+ NODE(IfWhile) \
+ NODE(For) \
+ NODE(Defer) \
+ NODE(SwitchCase) \
+ NODE(Switch) \
+ \
+ NODE(Type) \
+ NODE(BasicType) \
+ NODE(PointerType) \
+ NODE(FunctionType) \
+ NODE(ArrayType) \
+ NODE(SliceType) \
+ NODE(DynArrType) \
+ NODE(VarArgType) \
+ NODE(StructType) \
+ NODE(StructMember) \
+ NODE(PolyStructType) \
+ NODE(PolyStructParam) \
+ NODE(PolyCallType) \
+ NODE(EnumType) \
+ NODE(EnumValue) \
+ NODE(TypeAlias) \
+ NODE(TypeRawAlias) \
+ NODE(CompoundType) \
+ NODE(TypeOf) \
+ \
+ NODE(Binding) \
+ NODE(Alias) \
+ NODE(MemRes) \
+ NODE(Include) \
+ NODE(UsePackage) \
+ NODE(Global) \
+ NODE(Param) \
+ NODE(Function) \
+ NODE(OverloadedFunction) \
+ \
+ NODE(PolyParam) \
+ NODE(PolySolution) \
+ NODE(SolidifiedFunction) \
+ NODE(PolyProc) \
+ \
+ NODE(Note) \
+ NODE(CallSite) \
+ \
+ NODE(CodeBlock) \
+ NODE(DirectiveInsert) \
+ NODE(Macro) \
+ \
+ NODE(Package)
+
+#define NODE(name) typedef struct Ast ## name Ast ## name;
+AST_NODES
+#undef NODE
+
+typedef struct Package Package;
+
+typedef struct Scope {
+ u64 id;
+ struct Scope *parent;
+ OnyxFilePos created_at;
+ bh_table(AstNode *) symbols;
+} Scope;
+
+
+typedef enum AstKind {
+ Ast_Kind_Error,
+ Ast_Kind_Package,
+ Ast_Kind_Load_File,
+ Ast_Kind_Load_Path,
+ Ast_Kind_Memres,
+
+ Ast_Kind_Binding,
+ Ast_Kind_Alias,
+ Ast_Kind_Function,
+ Ast_Kind_Overloaded_Function,
+ Ast_Kind_Polymorphic_Proc,
+ Ast_Kind_Block,
+ Ast_Kind_Local,
+ Ast_Kind_Global,
+ Ast_Kind_Symbol,
+
+ Ast_Kind_Unary_Op,
+ Ast_Kind_Binary_Op,
+
+ Ast_Kind_Compound,
+ Ast_Kind_Named_Value,
+
+ Ast_Kind_Type_Start,
+ Ast_Kind_Type,
+ Ast_Kind_Basic_Type,
+ Ast_Kind_Pointer_Type,
+ Ast_Kind_Function_Type,
+ Ast_Kind_Array_Type,
+ Ast_Kind_Slice_Type,
+ Ast_Kind_DynArr_Type,
+ Ast_Kind_VarArg_Type,
+ Ast_Kind_Struct_Type,
+ Ast_Kind_Poly_Struct_Type,
+ Ast_Kind_Poly_Call_Type,
+ Ast_Kind_Enum_Type,
+ Ast_Kind_Type_Alias,
+ Ast_Kind_Type_Raw_Alias,
+ Ast_Kind_Type_Compound,
+ Ast_Kind_Typeof,
+ Ast_Kind_Type_End,
+
+ Ast_Kind_Struct_Member,
+ Ast_Kind_Enum_Value,
+
+ Ast_Kind_NumLit,
+ Ast_Kind_StrLit,
+ Ast_Kind_Param,
+ Ast_Kind_Argument,
+ Ast_Kind_Call,
+ Ast_Kind_Intrinsic_Call,
+ Ast_Kind_Return,
+ Ast_Kind_Address_Of,
+ Ast_Kind_Dereference,
+ Ast_Kind_Subscript,
+ Ast_Kind_Slice,
+ Ast_Kind_Field_Access,
+ Ast_Kind_Unary_Field_Access,
+ Ast_Kind_Pipe,
+ Ast_Kind_Method_Call,
+ Ast_Kind_Range_Literal,
+ Ast_Kind_Size_Of,
+ Ast_Kind_Align_Of,
+ Ast_Kind_File_Contents,
+ Ast_Kind_Struct_Literal,
+ Ast_Kind_Array_Literal,
+ Ast_Kind_If_Expression,
+
+ Ast_Kind_If,
+ Ast_Kind_For,
+ Ast_Kind_While,
+ Ast_Kind_Jump,
+ Ast_Kind_Use,
+ Ast_Kind_Defer,
+ Ast_Kind_Switch,
+ Ast_Kind_Switch_Case,
+
+ Ast_Kind_Directive_Solidify,
+ Ast_Kind_Static_If,
+ Ast_Kind_Directive_Error,
+ Ast_Kind_Directive_Add_Overload,
+ Ast_Kind_Directive_Operator,
+ Ast_Kind_Directive_Export,
+ Ast_Kind_Directive_Defined,
+ Ast_Kind_Call_Site,
+
+ Ast_Kind_Code_Block,
+ Ast_Kind_Directive_Insert,
+ Ast_Kind_Macro,
+ Ast_Kind_Do_Block,
+
+ Ast_Kind_Note,
+
+ Ast_Kind_Count
+} AstKind;
+
+// NOTE: Some of these flags will overlap since there are
+// only 32-bits of flags to play with
+typedef enum AstFlags {
+ // Top-level flags
+ Ast_Flag_Exported = BH_BIT(1),
+ Ast_Flag_Foreign = BH_BIT(2),
+ Ast_Flag_Const = BH_BIT(3),
+ Ast_Flag_Comptime = BH_BIT(4),
+ Ast_Flag_Private_Package = BH_BIT(5),
+ Ast_Flag_Private_File = BH_BIT(6),
+
+ // Global flags
+ Ast_Flag_Global_Stack_Top = BH_BIT(7),
+
+ // Function flags
+ Ast_Flag_Already_Checked = BH_BIT(8),
+ Ast_Flag_Intrinsic = BH_BIT(10),
+ Ast_Flag_Function_Used = BH_BIT(11),
+
+ // Expression flags
+ Ast_Flag_Expr_Ignored = BH_BIT(13),
+ Ast_Flag_Param_Use = BH_BIT(14), // Unneeded, just use a bool on AstParam
+ Ast_Flag_Address_Taken = BH_BIT(15),
+
+ // Type flags
+ Ast_Flag_Type_Is_Resolved = BH_BIT(16),
+
+ // Enum flags
+ Ast_Flag_Enum_Is_Flags = BH_BIT(17), // Unneeded, just use a bool on AstEnum
+
+ // Struct flags
+ Ast_Flag_Struct_Is_Union = BH_BIT(18), // Unneeded, just a usea bool on AstStruct
+
+ Ast_Flag_No_Clone = BH_BIT(19),
+
+ Ast_Flag_Cannot_Take_Addr = BH_BIT(20),
+
+ Ast_Flag_Struct_Mem_Used = BH_BIT(21), // Unneeded, just a usea bool on AstStructMember
+
+ // HACK: NullProcHack
+ Ast_Flag_Proc_Is_Null = BH_BIT(22),
+
+ Ast_Flag_From_Polymorphism = BH_BIT(23),
+
+ Ast_Flag_Incomplete_Body = BH_BIT(24),
+
+ Ast_Flag_Array_Literal_Typed = BH_BIT(25),
+
+ Ast_Flag_Has_Been_Symres = BH_BIT(26),
+
+ Ast_Flag_Has_Been_Checked = BH_BIT(27),
+
+ Ast_Flag_Static_If_Resolved = BH_BIT(28),
+
+ // :TEMPORARY
+ Ast_Flag_Params_Introduced = BH_BIT(29),
+} AstFlags;
+
+typedef enum UnaryOp {
+ Unary_Op_Negate,
+ Unary_Op_Not,
+ Unary_Op_Bitwise_Not,
+ Unary_Op_Cast,
+ Unary_Op_Auto_Cast,
+} UnaryOp;
+
+typedef enum BinaryOp {
+ Binary_Op_Add = 0,
+ Binary_Op_Minus = 1,
+ Binary_Op_Multiply = 2,
+ Binary_Op_Divide = 3,
+ Binary_Op_Modulus = 4,
+
+ Binary_Op_Equal = 5,
+ Binary_Op_Not_Equal = 6,
+ Binary_Op_Less = 7,
+ Binary_Op_Less_Equal = 8,
+ Binary_Op_Greater = 9,
+ Binary_Op_Greater_Equal = 10,
+
+ Binary_Op_And = 11,
+ Binary_Op_Or = 12,
+ Binary_Op_Xor = 13,
+ Binary_Op_Shl = 14,
+ Binary_Op_Shr = 15,
+ Binary_Op_Sar = 16,
+
+ Binary_Op_Bool_And = 17,
+ Binary_Op_Bool_Or = 18,
+
+ Binary_Op_Assign_Start = 19,
+ Binary_Op_Assign = 20,
+ Binary_Op_Assign_Add = 21,
+ Binary_Op_Assign_Minus = 22,
+ Binary_Op_Assign_Multiply = 23,
+ Binary_Op_Assign_Divide = 24,
+ Binary_Op_Assign_Modulus = 25,
+ Binary_Op_Assign_And = 26,
+ Binary_Op_Assign_Or = 27,
+ Binary_Op_Assign_Xor = 28,
+ Binary_Op_Assign_Shl = 29,
+ Binary_Op_Assign_Shr = 30,
+ Binary_Op_Assign_Sar = 31,
+ Binary_Op_Assign_End = 32,
+
+ Binary_Op_Pipe = 33,
+ Binary_Op_Range = 34,
+ Binary_Op_Method_Call = 35,
+
+ Binary_Op_Subscript = 36,
+
+ Binary_Op_Count
+} BinaryOp;
+
+extern const char *binaryop_string[Binary_Op_Count];
+
+typedef enum OnyxIntrinsic {
+ ONYX_INTRINSIC_UNDEFINED,
+
+ ONYX_INTRINSIC_MEMORY_SIZE, ONYX_INTRINSIC_MEMORY_GROW,
+ ONYX_INTRINSIC_MEMORY_COPY, ONYX_INTRINSIC_MEMORY_FILL,
+
+ ONYX_INTRINSIC_INITIALIZE, ONYX_INTRINSIC_ZERO_VALUE,
+
+ ONYX_INTRINSIC_I32_CLZ, ONYX_INTRINSIC_I32_CTZ, ONYX_INTRINSIC_I32_POPCNT,
+ ONYX_INTRINSIC_I32_AND, ONYX_INTRINSIC_I32_OR, ONYX_INTRINSIC_I32_XOR,
+ ONYX_INTRINSIC_I32_SHL, ONYX_INTRINSIC_I32_SLR, ONYX_INTRINSIC_I32_SAR,
+ ONYX_INTRINSIC_I32_ROTL, ONYX_INTRINSIC_I32_ROTR,
+
+ ONYX_INTRINSIC_I64_CLZ, ONYX_INTRINSIC_I64_CTZ, ONYX_INTRINSIC_I64_POPCNT,
+ ONYX_INTRINSIC_I64_AND, ONYX_INTRINSIC_I64_OR, ONYX_INTRINSIC_I64_XOR,
+ ONYX_INTRINSIC_I64_SHL, ONYX_INTRINSIC_I64_SLR, ONYX_INTRINSIC_I64_SAR,
+ ONYX_INTRINSIC_I64_ROTL, ONYX_INTRINSIC_I64_ROTR,
+
+ ONYX_INTRINSIC_F32_ABS, ONYX_INTRINSIC_F32_SQRT,
+ ONYX_INTRINSIC_F32_CEIL, ONYX_INTRINSIC_F32_FLOOR,
+ ONYX_INTRINSIC_F32_TRUNC, ONYX_INTRINSIC_F32_NEAREST,
+ ONYX_INTRINSIC_F32_MIN, ONYX_INTRINSIC_F32_MAX,
+ ONYX_INTRINSIC_F32_COPYSIGN,
+
+ ONYX_INTRINSIC_F64_ABS, ONYX_INTRINSIC_F64_SQRT,
+ ONYX_INTRINSIC_F64_CEIL, ONYX_INTRINSIC_F64_FLOOR,
+ ONYX_INTRINSIC_F64_TRUNC, ONYX_INTRINSIC_F64_NEAREST,
+ ONYX_INTRINSIC_F64_MIN, ONYX_INTRINSIC_F64_MAX,
+ ONYX_INTRINSIC_F64_COPYSIGN,
+
+
+
+ ONYX_INTRINSIC_V128_CONST,
+ ONYX_INTRINSIC_I8X16_CONST, ONYX_INTRINSIC_I16X8_CONST,
+ ONYX_INTRINSIC_I32X4_CONST, ONYX_INTRINSIC_I64X2_CONST,
+ ONYX_INTRINSIC_F32X4_CONST, ONYX_INTRINSIC_F64X2_CONST,
+ ONYX_INTRINSIC_I8X16_SHUFFLE,
+
+ ONYX_INTRINSIC_I8X16_EXTRACT_LANE_S, ONYX_INTRINSIC_I8X16_EXTRACT_LANE_U, ONYX_INTRINSIC_I8X16_REPLACE_LANE,
+ ONYX_INTRINSIC_I16X8_EXTRACT_LANE_S, ONYX_INTRINSIC_I16X8_EXTRACT_LANE_U, ONYX_INTRINSIC_I16X8_REPLACE_LANE,
+ ONYX_INTRINSIC_I32X4_EXTRACT_LANE, ONYX_INTRINSIC_I32X4_REPLACE_LANE,
+ ONYX_INTRINSIC_I64X2_EXTRACT_LANE, ONYX_INTRINSIC_I64X2_REPLACE_LANE,
+ ONYX_INTRINSIC_F32X4_EXTRACT_LANE, ONYX_INTRINSIC_F32X4_REPLACE_LANE,
+ ONYX_INTRINSIC_F64X2_EXTRACT_LANE, ONYX_INTRINSIC_F64X2_REPLACE_LANE,
+
+ ONYX_INTRINSIC_I8X16_SWIZZLE,
+ ONYX_INTRINSIC_I8X16_SPLAT, ONYX_INTRINSIC_I16X8_SPLAT,
+ ONYX_INTRINSIC_I32X4_SPLAT, ONYX_INTRINSIC_I64X2_SPLAT,
+ ONYX_INTRINSIC_F32X4_SPLAT, ONYX_INTRINSIC_F64X2_SPLAT,
+
+ ONYX_INTRINSIC_I8X16_EQ, ONYX_INTRINSIC_I8X16_NEQ,
+ ONYX_INTRINSIC_I8X16_LT_S, ONYX_INTRINSIC_I8X16_LT_U,
+ ONYX_INTRINSIC_I8X16_GT_S, ONYX_INTRINSIC_I8X16_GT_U,
+ ONYX_INTRINSIC_I8X16_LE_S, ONYX_INTRINSIC_I8X16_LE_U,
+ ONYX_INTRINSIC_I8X16_GE_S, ONYX_INTRINSIC_I8X16_GE_U,
+
+ ONYX_INTRINSIC_I16X8_EQ, ONYX_INTRINSIC_I16X8_NEQ,
+ ONYX_INTRINSIC_I16X8_LT_S, ONYX_INTRINSIC_I16X8_LT_U,
+ ONYX_INTRINSIC_I16X8_GT_S, ONYX_INTRINSIC_I16X8_GT_U,
+ ONYX_INTRINSIC_I16X8_LE_S, ONYX_INTRINSIC_I16X8_LE_U,
+ ONYX_INTRINSIC_I16X8_GE_S, ONYX_INTRINSIC_I16X8_GE_U,
+
+ ONYX_INTRINSIC_I32X4_EQ, ONYX_INTRINSIC_I32X4_NEQ,
+ ONYX_INTRINSIC_I32X4_LT_S, ONYX_INTRINSIC_I32X4_LT_U,
+ ONYX_INTRINSIC_I32X4_GT_S, ONYX_INTRINSIC_I32X4_GT_U,
+ ONYX_INTRINSIC_I32X4_LE_S, ONYX_INTRINSIC_I32X4_LE_U,
+ ONYX_INTRINSIC_I32X4_GE_S, ONYX_INTRINSIC_I32X4_GE_U,
+
+ ONYX_INTRINSIC_F32X4_EQ, ONYX_INTRINSIC_F32X4_NEQ,
+ ONYX_INTRINSIC_F32X4_LT, ONYX_INTRINSIC_F32X4_GT,
+ ONYX_INTRINSIC_F32X4_LE, ONYX_INTRINSIC_F32X4_GE,
+
+ ONYX_INTRINSIC_F64X2_EQ, ONYX_INTRINSIC_F64X2_NEQ,
+ ONYX_INTRINSIC_F64X2_LT, ONYX_INTRINSIC_F64X2_GT,
+ ONYX_INTRINSIC_F64X2_LE, ONYX_INTRINSIC_F64X2_GE,
+
+ ONYX_INTRINSIC_V128_NOT, ONYX_INTRINSIC_V128_AND, ONYX_INTRINSIC_V128_ANDNOT,
+ ONYX_INTRINSIC_V128_OR, ONYX_INTRINSIC_V128_XOR, ONYX_INTRINSIC_V128_BITSELECT,
+
+ ONYX_INTRINSIC_I8X16_ABS, ONYX_INTRINSIC_I8X16_NEG,
+ ONYX_INTRINSIC_I8X16_ANY_TRUE, ONYX_INTRINSIC_I8X16_ALL_TRUE,
+ ONYX_INTRINSIC_I8X16_BITMASK,
+ ONYX_INTRINSIC_I8X16_NARROW_I16X8_S, ONYX_INTRINSIC_I8X16_NARROW_I16X8_U,
+ ONYX_INTRINSIC_I8X16_SHL, ONYX_INTRINSIC_I8X16_SHR_S, ONYX_INTRINSIC_I8X16_SHR_U,
+ ONYX_INTRINSIC_I8X16_ADD, ONYX_INTRINSIC_I8X16_ADD_SAT_S, ONYX_INTRINSIC_I8X16_ADD_SAT_U,
+ ONYX_INTRINSIC_I8X16_SUB, ONYX_INTRINSIC_I8X16_SUB_SAT_S, ONYX_INTRINSIC_I8X16_SUB_SAT_U,
+ ONYX_INTRINSIC_I8X16_MIN_S, ONYX_INTRINSIC_I8X16_MIN_U,
+ ONYX_INTRINSIC_I8X16_MAX_S, ONYX_INTRINSIC_I8X16_MAX_U,
+ ONYX_INTRINSIC_I8X16_AVGR_U,
+
+ ONYX_INTRINSIC_I16X8_ABS, ONYX_INTRINSIC_I16X8_NEG,
+ ONYX_INTRINSIC_I16X8_ANY_TRUE, ONYX_INTRINSIC_I16X8_ALL_TRUE,
+ ONYX_INTRINSIC_I16X8_BITMASK,
+ ONYX_INTRINSIC_I16X8_NARROW_I32X4_S, ONYX_INTRINSIC_I16X8_NARROW_I32X4_U,
+ ONYX_INTRINSIC_I16X8_WIDEN_LOW_I8X16_S, ONYX_INTRINSIC_I16X8_WIDEN_HIGH_I8X16_S,
+ ONYX_INTRINSIC_I16X8_WIDEN_LOW_I8X16_U, ONYX_INTRINSIC_I16X8_WIDEN_HIGH_I8X16_U,
+ ONYX_INTRINSIC_I16X8_SHL, ONYX_INTRINSIC_I16X8_SHR_S, ONYX_INTRINSIC_I16X8_SHR_U,
+ ONYX_INTRINSIC_I16X8_ADD, ONYX_INTRINSIC_I16X8_ADD_SAT_S, ONYX_INTRINSIC_I16X8_ADD_SAT_U,
+ ONYX_INTRINSIC_I16X8_SUB, ONYX_INTRINSIC_I16X8_SUB_SAT_S, ONYX_INTRINSIC_I16X8_SUB_SAT_U,
+ ONYX_INTRINSIC_I16X8_MUL,
+ ONYX_INTRINSIC_I16X8_MIN_S, ONYX_INTRINSIC_I16X8_MIN_U,
+ ONYX_INTRINSIC_I16X8_MAX_S, ONYX_INTRINSIC_I16X8_MAX_U,
+ ONYX_INTRINSIC_I16X8_AVGR_U,
+
+ ONYX_INTRINSIC_I32X4_ABS, ONYX_INTRINSIC_I32X4_NEG,
+ ONYX_INTRINSIC_I32X4_ANY_TRUE, ONYX_INTRINSIC_I32X4_ALL_TRUE,
+ ONYX_INTRINSIC_I32X4_BITMASK,
+ ONYX_INTRINSIC_I32X4_WIDEN_LOW_I16X8_S, ONYX_INTRINSIC_I32X4_WIDEN_HIGH_I16X8_S,
+ ONYX_INTRINSIC_I32X4_WIDEN_LOW_I16X8_U, ONYX_INTRINSIC_I32X4_WIDEN_HIGH_I16X8_U,
+ ONYX_INTRINSIC_I32X4_SHL, ONYX_INTRINSIC_I32X4_SHR_S, ONYX_INTRINSIC_I32X4_SHR_U,
+ ONYX_INTRINSIC_I32X4_ADD, ONYX_INTRINSIC_I32X4_SUB, ONYX_INTRINSIC_I32X4_MUL,
+ ONYX_INTRINSIC_I32X4_MIN_S, ONYX_INTRINSIC_I32X4_MIN_U,
+ ONYX_INTRINSIC_I32X4_MAX_S, ONYX_INTRINSIC_I32X4_MAX_U,
+
+ ONYX_INTRINSIC_I64X2_NEG, ONYX_INTRINSIC_I64X2_SHL,
+ ONYX_INTRINSIC_I64X2_SHR_S, ONYX_INTRINSIC_I64X2_SHR_U,
+ ONYX_INTRINSIC_I64X2_ADD, ONYX_INTRINSIC_I64X2_SUB, ONYX_INTRINSIC_I64X2_MUL,
+
+ ONYX_INTRINSIC_F32X4_ABS, ONYX_INTRINSIC_F32X4_NEG, ONYX_INTRINSIC_F32X4_SQRT,
+ ONYX_INTRINSIC_F32X4_ADD, ONYX_INTRINSIC_F32X4_SUB,
+ ONYX_INTRINSIC_F32X4_MUL, ONYX_INTRINSIC_F32X4_DIV,
+ ONYX_INTRINSIC_F32X4_MIN, ONYX_INTRINSIC_F32X4_MAX,
+
+ ONYX_INTRINSIC_F64X2_ABS, ONYX_INTRINSIC_F64X2_NEG, ONYX_INTRINSIC_F64X2_SQRT,
+ ONYX_INTRINSIC_F64X2_ADD, ONYX_INTRINSIC_F64X2_SUB,
+ ONYX_INTRINSIC_F64X2_MUL, ONYX_INTRINSIC_F64X2_DIV,
+ ONYX_INTRINSIC_F64X2_MIN, ONYX_INTRINSIC_F64X2_MAX,
+
+ ONYX_INTRINSIC_I32X4_TRUNC_SAT_F32X4_S,
+ ONYX_INTRINSIC_I32X4_TRUNC_SAT_F32X4_U,
+ ONYX_INTRINSIC_F32X4_CONVERT_I32X4_S,
+ ONYX_INTRINSIC_F32X4_CONVERT_I32X4_U,
+} OnyxIntrinsic;
+
+typedef enum CallingConvention {
+ CC_Undefined,
+ CC_Return_Wasm,
+ CC_Return_Stack
+} CallingConvention;
+
+typedef enum JumpType {
+ Jump_Type_Break,
+ Jump_Type_Continue,
+ Jump_Type_Fallthrough,
+ Jump_Type_Return,
+
+ Jump_Type_Count,
+} JumpType;
+
+typedef enum ForLoopType {
+ For_Loop_Invalid,
+ For_Loop_Range,
+ For_Loop_Array,
+ For_Loop_Slice,
+ For_Loop_DynArr,
+ For_Loop_Iterator,
+} ForLoopType;
+
+typedef enum ParamPassType {
+ Param_Pass_Invalid,
+ Param_Pass_By_Value,
+ Param_Pass_By_VarArg,
+ Param_Pass_By_Implicit_Pointer,
+} ParamPassType;
+
+typedef enum VarArgKind {
+ VA_Kind_Not_VA,
+ VA_Kind_Typed,
+ VA_Kind_Any,
+ VA_Kind_Untyped,
+} VarArgKind;
+
+typedef enum BlockRule {
+ Block_Rule_New_Scope = BH_BIT(1),
+ Block_Rule_Emit_Instructions = BH_BIT(2),
+ Block_Rule_Clear_Defer = BH_BIT(3),
+ Block_Rule_Override_Return = BH_BIT(4),
+
+ Block_Rule_Normal = Block_Rule_New_Scope | Block_Rule_Clear_Defer | Block_Rule_Emit_Instructions,
+ Block_Rule_Macro = Block_Rule_New_Scope,
+ Block_Rule_Code_Block = Block_Rule_New_Scope,
+ Block_Rule_Do_Block = Block_Rule_New_Scope | Block_Rule_Clear_Defer | Block_Rule_Override_Return | Block_Rule_Emit_Instructions,
+} BlockRule;
+
+typedef struct Arguments Arguments;
+struct Arguments {
+ bh_arr(AstTyped *) values;
+ bh_arr(AstNamedValue *) named_values;
+};
+
+
+// Base Nodes
+#define AstNode_base \
+ AstKind kind; \
+ u32 flags; \
+ OnyxToken *token; \
+ struct Entity* entity; \
+ AstNode *next
+struct AstNode { AstNode_base; };
+
+// NOTE: 'type_node' is filled out by the parser.
+// For a type such as '^^i32', the tree would look something like
+//
+// Typed Thing -> AstPointerType -> AstPointerType -> AstNode (symbol node)
+//
+// The symbol node will be filled out during symbol resolution.
+// It will end up pointing to an AstBasicType that corresponds to
+// the underlying type.
+//
+// 'type' is filled out afterwards. If it is NULL, the Type* is built
+// using the type_node. This can then be used to typecheck this node.
+#define AstTyped_base \
+ AstNode_base; \
+ AstType *type_node; \
+ Type *type
+struct AstTyped { AstTyped_base; };
+
+// Expression Nodes
+struct AstNamedValue { AstTyped_base; AstTyped* value; };
+struct AstBinaryOp { AstTyped_base; BinaryOp operation; AstTyped *left, *right; };
+struct AstUnaryOp { AstTyped_base; UnaryOp operation; AstTyped *expr; };
+struct AstNumLit { AstTyped_base; union { i32 i; i64 l; f32 f; f64 d; } value; };
+struct AstStrLit { AstTyped_base; u64 addr; u64 length; };
+struct AstLocal { AstTyped_base; };
+struct AstArgument { AstTyped_base; AstTyped *value; VarArgKind va_kind; b32 is_baked : 1; };
+struct AstAddressOf { AstTyped_base; AstTyped *expr; };
+struct AstDereference { AstTyped_base; AstTyped *expr; };
+struct AstSizeOf { AstTyped_base; AstType *so_ast_type; Type *so_type; u64 size; };
+struct AstAlignOf { AstTyped_base; AstType *ao_ast_type; Type *ao_type; u64 alignment; };
+struct AstSubscript {
+ AstTyped_base;
+ BinaryOp __unused_operation; // This will be set to Binary_Op_Subscript
+ AstTyped *addr;
+ AstTyped *expr;
+ u64 elem_size;
+};
+struct AstFieldAccess {
+ AstTyped_base;
+ AstTyped *expr;
+ u32 offset;
+ u32 idx;
+ char* field; // If token is null, defer to field
+};
+struct AstFileContents {
+ AstTyped_base;
+
+ OnyxToken *filename_token;
+ char *filename; // The parsed file name, with '\' sequences removed and resolved to a particular file if possible.
+
+ u32 addr, size;
+};
+struct AstUnaryFieldAccess {
+ AstTyped_base;
+
+ // "token" represents the field. This does not need an "offset" or an "index"
+ // because this node is meant to be replaced.
+};
+struct AstStructLiteral {
+ AstTyped_base;
+
+ AstTyped *stnode;
+
+ Arguments args;
+};
+struct AstArrayLiteral {
+ AstTyped_base;
+
+ AstTyped *atnode;
+
+ bh_arr(AstTyped *) values;
+};
+struct AstRangeLiteral {
+ AstTyped_base;
+
+ // HACK: Currently, range literals are parsed as binary operators, which means
+ // the first sizeof(AstBinaryOp) bytes of this structure must match that of
+ // AstBinaryOp, which means I need this dummy field here.
+ // - brendanfh 2020/12/23
+ BinaryOp __unused_operation;
+ AstTyped *low, *high;
+
+ // Currently, there is no way to specify this in the grammar, but it is set
+ // to be the initial value of the `step` member of the range structure in
+ // core/builtin.onyx.
+ // - brendanfh 2020/12/23
+ AstTyped *step;
+};
+struct AstCall {
+ AstTyped_base;
+
+ Arguments original_args;
+ Arguments args;
+
+ union {
+ AstTyped *callee;
+ OnyxIntrinsic intrinsic;
+ };
+
+ VarArgKind va_kind;
+};
+struct AstCompound {
+ AstTyped_base;
+
+ bh_arr(AstTyped *) exprs;
+};
+struct AstIfExpression {
+ AstTyped_base;
+
+ AstTyped* cond;
+ AstTyped* true_expr;
+ AstTyped* false_expr;
+};
+struct AstDoBlock {
+ AstTyped_base;
+
+ AstBlock* block;
+};
+
+struct AstDirectiveSolidify {
+ AstTyped_base;
+
+ AstPolyProc* poly_proc;
+ bh_arr(AstPolySolution) known_polyvars;
+
+ AstNode* resolved_proc;
+};
+
+// Intruction Node
+struct AstReturn { AstNode_base; AstTyped* expr; };
+struct AstJump { AstNode_base; JumpType jump; u32 count; };
+
+typedef struct QualifiedUse {
+ OnyxToken* symbol_name;
+ OnyxToken* as_name;
+} QualifiedUse;
+struct AstUse {
+ AstNode_base;
+
+ AstTyped* expr;
+ bh_arr(QualifiedUse) only;
+};
+
+// Structure Nodes
+struct AstBlock {
+ AstNode_base;
+
+ AstNode *body;
+
+ Scope *scope;
+ Scope *binding_scope;
+ BlockRule rules;
+};
+struct AstDefer { AstNode_base; AstNode *stmt; };
+struct AstFor {
+ AstNode_base;
+
+ // NOTE: Stores the iteration variable
+ Scope *scope;
+
+ // NOTE: Local defining the iteration variable
+ AstLocal* var;
+
+ // NOTE: This can be any expression, but it is checked that
+ // it is of a type that we know how to iterate over.
+ AstTyped* iter;
+ ForLoopType loop_type;
+
+ AstBlock *stmt;
+
+ // ROBUSTNESS: This should be able to be set by a compile time variable at some point.
+ // But for now, this will do.
+ b32 by_pointer : 1;
+};
+struct AstIfWhile {
+ AstNode_base;
+
+ Scope *scope;
+ AstNode* initialization;
+
+ AstTyped *cond;
+
+ AstBlock *true_stmt;
+ AstBlock *false_stmt;
+
+ // Used by Static_If
+ bh_arr(struct Entity *) true_entities;
+ bh_arr(struct Entity *) false_entities;
+};
+typedef struct AstIfWhile AstIf;
+typedef struct AstIfWhile AstWhile;
+
+struct AstSwitchCase {
+ // NOTE: All expressions that end up in this block
+ bh_arr(AstTyped *) values;
+
+ AstBlock *block;
+};
+struct AstSwitch {
+ AstNode_base;
+
+ Scope *scope;
+ AstNode* initialization;
+
+ AstTyped *expr;
+
+ bh_arr(AstSwitchCase) cases;
+ AstBlock *default_case;
+
+ // NOTE: This is a mapping from the compile time known case value
+ // to a pointer to the block that it is associated with.
+ bh_imap case_map;
+ u64 min_case, max_case;
+};
+
+// Type Nodes
+// NOTE: This node is very similar to an AstNode, just
+// without the 'next' member. This is because types
+// can't be in expressions so a 'next' thing
+// doesn't make sense.
+#define AstType_base \
+ AstKind kind; \
+ u32 flags; \
+ OnyxToken* token; \
+ struct Entity* entity; \
+ u64 type_id; \
+ void* __unused; \
+ Type* type
+struct AstType { AstType_base; };
+
+struct AstBasicType { AstType_base; Type* basic_type; };
+struct AstPointerType { AstType_base; AstType* elem; };
+struct AstFunctionType { AstType_base; AstType* return_type; u64 param_count; AstType* params[]; };
+struct AstArrayType { AstType_base; AstType* elem; AstTyped *count_expr; };
+struct AstSliceType { AstType_base; AstType* elem; };
+struct AstDynArrType { AstType_base; AstType* elem; };
+struct AstVarArgType { AstType_base; AstType* elem; };
+struct AstStructType {
+ AstType_base;
+ char *name;
+
+ bh_arr(AstStructMember *) members;
+
+ u32 min_alignment, min_size;
+
+ // NOTE: Used to cache the actual type, since building
+ // a struct type is kind of complicated and should
+ // only happen once.
+ Type *stcache;
+
+ // NOTE: Used to store statically bound expressions in the struct.
+ Scope* scope;
+
+ struct Entity* entity_type;
+ struct Entity* entity_defaults;
+
+ b32 stcache_is_valid : 1;
+};
+struct AstStructMember {
+ AstTyped_base;
+ AstTyped* initial_value;
+};
+struct AstPolyStructParam {
+ AstTyped_base;
+};
+struct AstPolyStructType {
+ AstType_base;
+ char *name;
+
+ Scope *scope;
+ bh_arr(AstPolyStructParam) poly_params;
+ bh_table(AstStructType *) concrete_structs;
+
+ AstStructType* base_struct;
+};
+struct AstPolyCallType {
+ AstType_base;
+
+ AstType* callee;
+
+ // NOTE: These nodes can be either AstTypes, or AstTyped expressions.
+ bh_arr(AstNode *) params;
+};
+struct AstEnumType {
+ AstType_base;
+ char *name;
+ Scope *scope;
+
+ AstType *backing;
+ Type *backing_type;
+
+ bh_arr(AstEnumValue *) values;
+
+ // NOTE: Used to cache the actual type for the same reason as above.
+ Type *etcache;
+};
+struct AstEnumValue { AstTyped_base; AstNumLit* value; };
+struct AstTypeAlias { AstType_base; AstType* to; };
+struct AstTypeRawAlias { AstType_base; Type* to; };
+struct AstCompoundType {
+ AstType_base;
+
+ bh_arr(AstType *) types;
+};
+struct AstTypeOf {
+ AstType_base;
+
+ AstTyped* expr;
+ Type* resolved_type;
+};
+
+// Top level nodes
+struct AstBinding { AstTyped_base; AstNode* node; };
+struct AstAlias { AstTyped_base; AstTyped* alias; };
+struct AstMemRes { AstTyped_base; u64 addr; AstTyped *initial_value; };
+struct AstInclude { AstNode_base; char* name; };
+struct AstGlobal {
+ AstTyped_base;
+
+ OnyxToken* name;
+
+ OnyxToken* foreign_module;
+ OnyxToken* foreign_name;
+};
+struct AstParam {
+ // HACK CLEANUP: This does not need to have a local buried inside of it.
+ // Convert this to be AstTyped_base and pull the ParamPassType from AstLocal
+ // to here. - brendanfh 2020/09/18
+ AstLocal *local;
+ AstTyped *default_value;
+
+ VarArgKind vararg_kind;
+ b32 use_processed : 1;
+};
+struct AstFunction {
+ AstTyped_base;
+
+ Scope *scope;
+
+ bh_arr(AstParam) params;
+ AstType* return_type;
+
+ AstBlock *body;
+
+ OnyxToken* name;
+
+
+ // NOTE: This is NULL, unless this function was generated from a polymorphic
+ // procedure call. Then it is set to the token of the call node.
+ OnyxToken* generated_from;
+
+ // NOTE: This is NULL, unless this function is used in a "#export" directive.
+ // It is undefined which name it will have if there are multiple export directives
+ // for this particular function.
+ OnyxToken* exported_name;
+
+ union {
+ OnyxToken* intrinsic_name;
+
+ // NOTE: Used when the function is declared as foreign
+ struct {
+ OnyxToken* foreign_module;
+ OnyxToken* foreign_name;
+ };
+ };
+
+ struct Entity* entity_header;
+ struct Entity* entity_body;
+};
+
+typedef struct OverloadOption OverloadOption;
+struct OverloadOption {
+ // This is u64 because padding will make it that anyway.
+ // Consider: would there be any practical benefit to having the precedence setting
+ // be a compile-time known value? as opposed to a hardcoded value?
+ u64 precedence;
+
+ AstTyped* option;
+};
+
+struct AstOverloadedFunction {
+ AstTyped_base;
+
+ bh_arr(OverloadOption) overloads;
+
+ // CLEANUP: This is unused right now, but should be used to cache
+ // the complete set of overloads that can be used by an overloaded
+ // function.
+ bh_imap all_overloads;
+};
+
+struct AstPackage {
+ AstTyped_base;
+
+ // Allocated in the ast arena
+ char * package_name;
+
+ // NOTE: Symbol nodes
+ bh_arr(OnyxToken *) path;
+
+ Package* package;
+};
+
+//
+// Polymorphic procedures
+//
+
+typedef enum PolyParamKind {
+ // Dont love these names
+ PPK_Undefined,
+ PPK_Poly_Type,
+ PPK_Baked_Value,
+} PolyParamKind;
+
+typedef enum PolySolutionKind {
+ PSK_Undefined,
+ PSK_Type,
+ PSK_Value,
+} PolySolutionKind;
+
+struct AstPolyParam {
+ PolyParamKind kind;
+
+ // The parameter index where the polymorphic variable occurs.
+ u32 idx;
+
+ // The symbol node that represents the polymorphic variable.
+ AstNode* poly_sym;
+
+ // The type expression that contains `poly_sym` in it somewhere.
+ // Matching polymorphic variables does a parallel tree traversal
+ // to find the pairing of the actual type and polymorphic variable
+ // symbol.
+ AstType* type_expr;
+
+ // Used for baked values. The expected type of the parameter.
+ Type* type;
+};
+
+struct AstPolySolution {
+ PolySolutionKind kind;
+ AstNode* poly_sym;
+
+ union {
+ struct {
+ // If `type` is null, it is filled in with this type.
+ AstType* ast_type;
+ Type* type;
+ };
+
+ AstTyped* value;
+ };
+};
+
+struct AstSolidifiedFunction {
+ AstFunction* func;
+ Scope* poly_scope;
+
+ b32 header_complete: 1;
+};
+
+struct AstPolyProc {
+ // While the "type" of a polymorphic procedure will never be set, it is necessary
+ // for contexts where it used in an expression.
+ AstTyped_base;
+
+ Scope *poly_scope;
+ bh_arr(AstPolyParam) poly_params;
+
+ bh_arr(AstPolySolution) known_slns;
+
+ AstFunction* base_func;
+ bh_table(AstSolidifiedFunction) concrete_funcs;
+};
+
+
+struct AstDirectiveError {
+ AstNode_base;
+
+ OnyxToken* error_msg;
+};
+
+struct AstDirectiveAddOverload {
+ AstNode_base;
+
+ // NOTE: used by the #add_overload directive. Initially set to a symbol,
+ // then resolved to an overloaded function.
+ AstNode *overloaded_function;
+
+ // See note in OverloadOption. This could be refactored into an OverloadOption?
+ u64 precedence;
+ AstTyped *overload;
+};
+
+struct AstDirectiveOperator {
+ AstNode_base;
+
+ BinaryOp operator;
+ AstTyped *overload;
+};
+
+struct AstDirectiveExport {
+ AstNode_base;
+
+ OnyxToken* export_name;
+ AstTyped* export;
+};
+
+struct AstDirectiveDefined {
+ AstTyped_base;
+ AstTyped *expr;
+
+ b32 is_defined: 1;
+};
+
+struct AstNote {
+ AstNode_base;
+};
+
+struct AstCallSite {
+ AstTyped_base;
+
+ OnyxToken* callsite_token;
+
+ AstStrLit* filename;
+ AstNumLit* line;
+ AstNumLit* column;
+};
+
+// Represents a "pastable" block of code.
+struct AstCodeBlock {
+ AstTyped_base;
+
+ AstNode *code;
+};
+
+struct AstDirectiveInsert {
+ AstNode_base;
+
+ AstTyped *code_expr;
+};
+
+struct AstMacro {
+ AstTyped_base;
+
+ AstTyped* body;
+};
+
+typedef enum EntityState {
+ Entity_State_Error,
+
+ Entity_State_Parse_Builtin,
+ Entity_State_Introduce_Symbols,
+ Entity_State_Parse,
+ Entity_State_Resolve_Symbols,
+ Entity_State_Check_Types,
+ Entity_State_Code_Gen,
+ Entity_State_Finalized,
+
+ Entity_State_Count,
+} EntityState;
+
+extern const char* entity_state_strings[Entity_State_Count];
+
+// NOTE: An Entity represents something will need to be
+// processed later down the pipeline.
+typedef enum EntityType {
+ Entity_Type_Unknown,
+
+ Entity_Type_Error,
+ Entity_Type_Note,
+ Entity_Type_Load_Path,
+ Entity_Type_Load_File,
+ Entity_Type_Binding,
+ Entity_Type_Use_Package,
+ Entity_Type_Static_If,
+ Entity_Type_String_Literal,
+ Entity_Type_File_Contents,
+ Entity_Type_Enum,
+ Entity_Type_Type_Alias,
+ Entity_Type_Memory_Reservation_Type,
+ Entity_Type_Use,
+ Entity_Type_Polymorphic_Proc,
+ Entity_Type_Macro,
+ Entity_Type_Foreign_Function_Header,
+ Entity_Type_Foreign_Global_Header,
+ Entity_Type_Function_Header,
+ Entity_Type_Global_Header,
+ Entity_Type_Process_Directive,
+ Entity_Type_Struct_Member_Default,
+ Entity_Type_Memory_Reservation,
+ Entity_Type_Expression,
+ Entity_Type_Global,
+ Entity_Type_Overloaded_Function,
+ Entity_Type_Function,
+
+ Entity_Type_Count,
+} EntityType;
+
+extern const char* entity_type_strings[Entity_Type_Count];
+
+typedef struct Entity {
+ u32 id;
+
+ EntityType type;
+ EntityState state;
+
+ // TODO: Document this!
+ u32 macro_attempts;
+ u32 micro_attempts;
+
+ b32 entered_in_queue : 1;
+
+ Package *package;
+ Scope *scope;
+
+ // TODO: This is incomplete. Add proper cycle detection and halting.
+ // struct Entity *waiting_on;
+
+ union {
+ AstDirectiveError *error;
+ AstInclude *include;
+ AstBinding *binding;
+ AstIf *static_if;
+ AstFunction *function;
+ AstOverloadedFunction *overloaded_function;
+ AstGlobal *global;
+ AstTyped *expr;
+ AstStrLit *strlit;
+ AstFileContents *file_contents;
+ AstType *type_alias;
+ AstEnumType *enum_type;
+ AstMemRes *mem_res;
+ AstPolyProc *poly_proc;
+ AstMacro *macro;
+ AstUse *use;
+ };
+} Entity;
+
+typedef struct EntityHeap {
+ bh_arena entity_arena;
+ bh_arr(Entity *) entities;
+ i32 next_id;
+
+ i32 state_count[Entity_State_Count];
+ i32 type_count[Entity_Type_Count];
+
+ i32 all_count[Entity_State_Count][Entity_Type_Count];
+} EntityHeap;
+
+void entity_heap_init(EntityHeap* entities);
+void entity_heap_insert_existing(EntityHeap* entities, Entity* e);
+Entity* entity_heap_insert(EntityHeap* entities, Entity e);
+Entity* entity_heap_top(EntityHeap* entities);
+void entity_heap_change_top(EntityHeap* entities, Entity* new_top);
+void entity_heap_remove_top(EntityHeap* entities);
+
+// If target_arr is null, the entities will be placed directly in the heap.
+void add_entities_for_node(bh_arr(Entity *)* target_arr, AstNode* node, Scope* scope, Package* package);
+
+b32 entity_bring_to_state(Entity* ent, EntityState state);
+void symres_entity(Entity* ent);
+void check_entity(Entity* ent);
+void emit_entity(Entity* ent);
+
+struct Package {
+ char *name;
+
+ Scope *scope;
+ Scope *private_scope;
+
+ // NOTE: This tracks all of the 'use package' statements of this package throughout
+ // the code base. This is used when a static if clears and new symbols are introduced.
+ // 'use package' statements have to be reevaluated to pull in the new symbols.
+ bh_arr(Entity *) use_package_entities;
+};
+
+typedef enum CompileAction CompileAction;
+enum CompileAction {
+ ONYX_COMPILE_ACTION_COMPILE,
+ ONYX_COMPILE_ACTION_DOCUMENT,
+ ONYX_COMPILE_ACTION_PRINT_HELP,
+};
+
+
+// ROBUSTNESS: The Runtime definitions here must match those in build_opts.onyx!!
+typedef enum Runtime Runtime;
+enum Runtime {
+ Runtime_Unknown = 0,
+ Runtime_Wasi = 1,
+ Runtime_Js = 2,
+ Runtime_Custom = 3,
+};
+
+
+typedef struct CompileOptions CompileOptions;
+struct CompileOptions {
+ bh_allocator allocator;
+ CompileAction action;
+
+ u32 verbose_output : 2;
+ b32 fun_output : 1;
+ b32 print_function_mappings : 1;
+ b32 print_static_if_results : 1;
+ b32 print_notes : 1;
+ b32 no_colors : 1;
+ b32 no_file_contents : 1;
+
+ b32 use_post_mvp_features : 1;
+
+ Runtime runtime;
+
+ bh_arr(const char *) included_folders;
+ bh_arr(const char *) files;
+ const char* target_file;
+ const char* documentation_file;
+};
+
+typedef struct Context Context;
+struct Context {
+ bh_table(Package *) packages;
+ EntityHeap entities;
+
+ Scope *global_scope;
+
+ CompileOptions* options;
+
+ bh_arena ast_arena;
+ bh_allocator token_alloc, ast_alloc;
+
+ bh_arr(bh_file_contents) loaded_files;
+
+ // NOTE: This is defined in onyxwasm.h
+ struct OnyxWasmModule* wasm_module;
+
+ b32 cycle_detected : 1;
+};
+
+extern Context context;
+
+// NOTE: Basic internal types constructed in the parser
+extern AstBasicType basic_type_void;
+extern AstBasicType basic_type_bool;
+extern AstBasicType basic_type_i8;
+extern AstBasicType basic_type_u8;
+extern AstBasicType basic_type_i16;
+extern AstBasicType basic_type_u16;
+extern AstBasicType basic_type_i32;
+extern AstBasicType basic_type_u32;
+extern AstBasicType basic_type_i64;
+extern AstBasicType basic_type_u64;
+extern AstBasicType basic_type_f32;
+extern AstBasicType basic_type_f64;
+extern AstBasicType basic_type_rawptr;
+extern AstBasicType basic_type_type_expr; // :TypeExprHack
+
+extern AstBasicType basic_type_int_unsized;
+extern AstBasicType basic_type_float_unsized;
+
+extern AstBasicType basic_type_i8x16;
+extern AstBasicType basic_type_i16x8;
+extern AstBasicType basic_type_i32x4;
+extern AstBasicType basic_type_i64x2;
+extern AstBasicType basic_type_f32x4;
+extern AstBasicType basic_type_f64x2;
+extern AstBasicType basic_type_v128;
+
+// HACK
+// :AutoReturnType
+extern Type type_auto_return;
+extern AstBasicType basic_type_auto_return;
+
+extern OnyxToken builtin_package_token;
+extern AstNumLit builtin_heap_start;
+extern AstGlobal builtin_stack_top;
+extern AstType *builtin_string_type;
+extern AstType *builtin_range_type;
+extern Type *builtin_range_type_type;
+extern AstType *builtin_vararg_type;
+extern Type *builtin_vararg_type_type;
+extern AstTyped *builtin_context_variable;
+extern AstType *builtin_allocator_type;
+extern AstType *builtin_iterator_type;
+extern AstType *builtin_callsite_type;
+extern AstType *builtin_any_type;
+extern AstType *builtin_code_type;
+extern AstTyped *type_table_node;
+
+typedef struct BuiltinSymbol {
+ char* package;
+ char* sym;
+ AstNode* node;
+} BuiltinSymbol;
+
+extern const BuiltinSymbol builtin_symbols[];
+
+typedef struct IntrinsicMap {
+ char* name;
+ OnyxIntrinsic intrinsic;
+} IntrinsicMap;
+
+extern bh_table(OnyxIntrinsic) intrinsic_table;
+
+extern bh_arr(OverloadOption) operator_overloads[Binary_Op_Count];
+
+void initialize_builtins(bh_allocator a);
+void introduce_build_options(bh_allocator a);
+
+
+// NOTE: Useful not inlined functions
+AstTyped* ast_reduce(bh_allocator a, AstTyped* node);
+AstNode* ast_clone(bh_allocator a, void* n);
+AstFunction* clone_function_header(bh_allocator a, AstFunction* func);
+void clone_function_body(bh_allocator a, AstFunction* dest, AstFunction* source);
+
+void promote_numlit_to_larger(AstNumLit* num);
+b32 convert_numlit_to_type(AstNumLit* num, Type* type);
+
+b32 type_check_or_auto_cast(AstTyped** pnode, Type* type);
+Type* resolve_expression_type(AstTyped* node);
+i64 get_expression_integer_value(AstTyped* node);
+
+b32 cast_is_legal(Type* from_, Type* to_, char** err_msg);
+char* get_function_name(AstFunction* func);
+
+AstNode* strip_aliases(AstNode* node);
+
+AstNumLit* make_bool_literal(bh_allocator, b32 b);
+AstNumLit* make_int_literal(bh_allocator a, i64 value);
+AstNumLit* make_float_literal(bh_allocator a, f64 value);
+AstRangeLiteral* make_range_literal(bh_allocator a, AstTyped* low, AstTyped* high);
+AstBinaryOp* make_binary_op(bh_allocator a, BinaryOp operation, AstTyped* left, AstTyped* right);
+AstArgument* make_argument(bh_allocator a, AstTyped* value);
+AstFieldAccess* make_field_access(bh_allocator a, AstTyped* node, char* field);
+AstAddressOf* make_address_of(bh_allocator a, AstTyped* node);
+AstLocal* make_local(bh_allocator a, OnyxToken* token, AstType* type_node);
+AstNode* make_symbol(bh_allocator a, OnyxToken* sym);
+
+void arguments_initialize(Arguments* args);
+b32 fill_in_arguments(Arguments* args, AstNode* provider, char** err_msg);
+void arguments_ensure_length(Arguments* args, u32 count);
+void arguments_copy(Arguments* dest, Arguments* src);
+void arguments_clone(Arguments* dest, Arguments* src);
+void arguments_deep_clone(bh_allocator a, Arguments* dest, Arguments* src);
+void arguments_remove_baked(Arguments* args);
+
+// GROSS: Using void* to avoid having to cast everything.
+const char* node_get_type_name(void* node);
+
+b32 static_if_resolution(AstIf* static_if);
+
+typedef enum PolyProcLookupMethod {
+ PPLM_By_Arguments,
+ PPLM_By_Function_Type,
+} PolyProcLookupMethod;
+AstFunction* polymorphic_proc_lookup(AstPolyProc* pp, PolyProcLookupMethod pp_lookup, ptr actual, OnyxToken* tkn);
+AstFunction* polymorphic_proc_solidify(AstPolyProc* pp, bh_arr(AstPolySolution) slns, OnyxToken* tkn);
+AstNode* polymorphic_proc_try_solidify(AstPolyProc* pp, bh_arr(AstPolySolution) slns, OnyxToken* tkn);
+AstFunction* polymorphic_proc_build_only_header(AstPolyProc* pp, PolyProcLookupMethod pp_lookup, ptr actual);
+
+void add_overload_option(bh_arr(OverloadOption)* poverloads, u64 precedence, AstTyped* overload);
+AstTyped* find_matching_overload_by_arguments(bh_arr(OverloadOption) overloads, Arguments* args, b32* should_yield);
+AstTyped* find_matching_overload_by_type(bh_arr(OverloadOption) overloads, Type* type);
+void report_unable_to_match_overload(AstCall* call);
+
+void expand_macro(AstCall** pcall, AstFunction* template);
+AstFunction* macro_resolve_header(AstMacro* macro, Arguments* args, OnyxToken* callsite);
+
+AstStructType* polymorphic_struct_lookup(AstPolyStructType* ps_type, bh_arr(AstPolySolution) slns, OnyxFilePos pos);
+
+// NOTE: Useful inlined functions
+static inline b32 is_lval(AstNode* node) {
+ if ((node->kind == Ast_Kind_Local)
+ || (node->kind == Ast_Kind_Param)
+ || (node->kind == Ast_Kind_Global)
+ || (node->kind == Ast_Kind_Dereference)
+ || (node->kind == Ast_Kind_Subscript)
+ || (node->kind == Ast_Kind_Field_Access)
+ || (node->kind == Ast_Kind_Memres))
+ return 1;
+
+ if (node->kind == Ast_Kind_Compound) {
+ b32 all_lval = 1;
+ bh_arr_each(AstTyped *, expr, ((AstCompound *) node)->exprs) all_lval = all_lval && is_lval((AstNode *) *expr);
+ return all_lval;
+ }
+
+ return 0;
+}
+
+static inline b32 binop_is_assignment(BinaryOp binop) {
+ return (binop >= Binary_Op_Assign_Start && binop <= Binary_Op_Assign_End);
+}
+
+static inline b32 binop_is_compare(BinaryOp binop) {
+ return (binop >= Binary_Op_Equal && binop <= Binary_Op_Greater_Equal);
+}
+
+static inline b32 node_is_type(AstNode* node) {
+ node = strip_aliases(node);
+
+ return (node->kind > Ast_Kind_Type_Start) && (node->kind < Ast_Kind_Type_End);
+}
+
+static inline b32 node_is_auto_cast(AstNode* node) {
+ return (node->kind == Ast_Kind_Unary_Op) && (((AstUnaryOp *) node)->operation == Unary_Op_Auto_Cast);
+}
+
+static inline CallingConvention type_function_get_cc(Type* type) {
+ if (type == NULL) return CC_Undefined;
+ if (type->kind != Type_Kind_Function) return CC_Undefined;
+ if (type_is_compound(type->Function.return_type)) return CC_Return_Stack;
+ return CC_Return_Wasm;
+}
+
+static inline ParamPassType type_get_param_pass(Type* type) {
+ if (type_is_structlike_strict(type) && !type_structlike_is_simple(type)) return Param_Pass_By_Implicit_Pointer;
+ return Param_Pass_By_Value;
+}
+
+static inline AstFunction* get_function_from_node(AstNode* node) {
+ if (node->kind == Ast_Kind_Function) return (AstFunction *) node;
+ if (node->kind == Ast_Kind_Polymorphic_Proc) return ((AstPolyProc *) node)->base_func;
+ if (node->kind == Ast_Kind_Macro) return get_function_from_node((AstNode*) ((AstMacro *) node)->body);
+ return NULL;
+}
+
+#endif // #ifndef ONYXASTNODES_H
--- /dev/null
+#ifndef ONYXDOC_H
+#define ONYXDOC_H
+
+#include "bh.h"
+#include "astnodes.h"
+
+typedef enum DocFormat {
+ Doc_Format_Human,
+ Doc_Format_Tags,
+ Doc_Format_Html,
+} DocFormat;
+
+typedef struct DocEntry {
+ OnyxFilePos pos;
+ char* sym; // Unused by doc generator
+ char* def;
+ char* additional;
+} DocEntry;
+
+typedef struct DocPackage {
+ const char* name;
+
+ bh_arr(DocEntry) public_entries;
+ bh_arr(DocEntry) private_entries;
+} DocPackage;
+
+typedef struct OnyxDocumentation {
+ bh_arena doc_arena;
+
+ DocFormat format;
+
+ bh_arr(DocPackage) package_docs;
+} OnyxDocumentation;
+
+OnyxDocumentation onyx_docs_generate();
+void onyx_docs_emit(OnyxDocumentation* doc, const char* filename);
+
+#endif
--- /dev/null
+#ifndef ONYXERRORS_H
+#define ONYXERRORS_H
+
+#include "bh.h"
+#include "lex.h"
+
+#include <stdarg.h>
+
+typedef struct OnyxError {
+ OnyxFilePos pos;
+ char *text;
+} OnyxError;
+
+typedef struct OnyxErrors {
+ bh_arena msg_arena;
+ bh_allocator msg_alloc;
+
+ // NOTE: Pointer to a array of all the loaded files.
+ bh_arr(bh_file_contents)* file_contents;
+
+ bh_arr(OnyxError) errors;
+} OnyxErrors;
+
+extern OnyxErrors msgs;
+
+void onyx_errors_init(bh_arr(bh_file_contents)* files);
+void onyx_report_error(OnyxFilePos pos, char * format, ...);
+void onyx_report_warning(OnyxFilePos pos, char* format, ...);
+void onyx_errors_print();
+b32 onyx_has_errors();
+void onyx_clear_errors();
+
+#endif
--- /dev/null
+#ifndef ONYXLEX_H
+#define ONYXLEX_H
+
+#include "bh.h"
+
+// NOTE: Used for global statistics
+extern u64 lexer_lines_processed;
+extern u64 lexer_tokens_processed;
+
+typedef enum TokenType {
+ Token_Type_Ascii_End = 256,
+ Token_Type_Unknown = 256,
+ Token_Type_End_Stream = 257,
+
+ Token_Type_Comment = 258,
+
+ Token_Type_Keyword_Package,
+ Token_Type_Keyword_Struct,
+ Token_Type_Keyword_Enum,
+ Token_Type_Keyword_Use,
+ Token_Type_Keyword_If,
+ Token_Type_Keyword_Else,
+ Token_Type_Keyword_Elseif,
+ Token_Type_Keyword_Return,
+ Token_Type_Keyword_Global,
+ Token_Type_Keyword_Proc,
+ Token_Type_Keyword_As,
+ Token_Type_Keyword_Cast,
+ Token_Type_Keyword_While,
+ Token_Type_Keyword_For,
+ Token_Type_Keyword_Break,
+ Token_Type_Keyword_Continue,
+ Token_Type_Keyword_Sizeof,
+ Token_Type_Keyword_Alignof,
+ Token_Type_Keyword_Typeof,
+ Token_Type_Keyword_Defer,
+ Token_Type_Keyword_Do,
+ Token_Type_Keyword_Case,
+ Token_Type_Keyword_Switch,
+ Token_Type_Keyword_Fallthrough,
+ Token_Type_Keyword_Macro,
+
+ Token_Type_Right_Arrow,
+ Token_Type_Left_Arrow,
+ Token_Type_Empty_Block,
+ Token_Type_Pipe,
+
+ Token_Type_Greater_Equal,
+ Token_Type_Less_Equal,
+ Token_Type_Equal_Equal,
+ Token_Type_Not_Equal,
+ Token_Type_Plus_Equal,
+ Token_Type_Minus_Equal,
+ Token_Type_Star_Equal,
+ Token_Type_Fslash_Equal,
+ Token_Type_Percent_Equal,
+ Token_Type_And_Equal,
+ Token_Type_Or_Equal,
+ Token_Type_Xor_Equal,
+ Token_Type_And_And,
+ Token_Type_Or_Or,
+ Token_Type_Shift_Left,
+ Token_Type_Shift_Right,
+ Token_Type_Shift_Arith_Right,
+ Token_Type_Shl_Equal,
+ Token_Type_Shr_Equal,
+ Token_Type_Sar_Equal,
+
+ Token_Type_Dot_Dot,
+ Token_Type_Tilde_Tilde,
+
+ Token_Type_Symbol,
+ Token_Type_Literal_String,
+ Token_Type_Literal_Integer,
+ Token_Type_Literal_Float,
+ Token_Type_Literal_True,
+ Token_Type_Literal_False,
+
+ Token_Type_Note,
+
+ Token_Type_Count,
+} TokenType;
+
+typedef struct OnyxFilePos {
+ const char* filename;
+ char* line_start;
+ u32 line;
+
+ // NOTE: This assumes that no line is no longer than 2^16 chars
+ u16 column, length;
+} OnyxFilePos;
+
+typedef struct OnyxToken {
+ TokenType type;
+ i32 length;
+ char* text;
+ OnyxFilePos pos;
+} OnyxToken;
+
+typedef struct OnyxTokenizer {
+ char *start, *curr, *end;
+
+ const char* filename;
+
+ char* line_start;
+ u64 line_number;
+
+ bh_arr(OnyxToken) tokens;
+} OnyxTokenizer;
+
+const char* token_name(TokenType tkn_type);
+void token_toggle_end(OnyxToken* tkn);
+OnyxToken* onyx_get_token(OnyxTokenizer* tokenizer);
+OnyxTokenizer onyx_tokenizer_create(bh_allocator allocator, bh_file_contents *fc);
+void onyx_tokenizer_free(OnyxTokenizer* tokenizer);
+void onyx_lex_tokens(OnyxTokenizer* tokenizer);
+
+b32 token_equals(OnyxToken* tkn1, OnyxToken* tkn2);
+b32 token_text_equals(OnyxToken* tkn, char* text);
+
+#endif
+++ /dev/null
-#ifndef ONYXASTNODES_H
-#define ONYXASTNODES_H
-
-#include "onyxlex.h"
-#include "onyxtypes.h"
-
-#define AST_NODES \
- NODE(Node) \
- NODE(Typed) \
- \
- NODE(NamedValue) \
- NODE(BinaryOp) \
- NODE(UnaryOp) \
- NODE(NumLit) \
- NODE(StrLit) \
- NODE(Local) \
- NODE(Call) \
- NODE(Argument) \
- NODE(AddressOf) \
- NODE(Dereference) \
- NODE(Subscript) \
- NODE(FieldAccess) \
- NODE(UnaryFieldAccess) \
- NODE(SizeOf) \
- NODE(AlignOf) \
- NODE(FileContents) \
- NODE(StructLiteral) \
- NODE(ArrayLiteral) \
- NODE(RangeLiteral) \
- NODE(Compound) \
- NODE(IfExpression) \
- NODE(DoBlock) \
- \
- NODE(DirectiveSolidify) \
- NODE(DirectiveError) \
- NODE(DirectiveAddOverload) \
- NODE(DirectiveOperator) \
- NODE(DirectiveExport) \
- NODE(DirectiveDefined) \
- \
- NODE(Return) \
- NODE(Jump) \
- NODE(Use) \
- \
- NODE(Block) \
- NODE(IfWhile) \
- NODE(For) \
- NODE(Defer) \
- NODE(SwitchCase) \
- NODE(Switch) \
- \
- NODE(Type) \
- NODE(BasicType) \
- NODE(PointerType) \
- NODE(FunctionType) \
- NODE(ArrayType) \
- NODE(SliceType) \
- NODE(DynArrType) \
- NODE(VarArgType) \
- NODE(StructType) \
- NODE(StructMember) \
- NODE(PolyStructType) \
- NODE(PolyStructParam) \
- NODE(PolyCallType) \
- NODE(EnumType) \
- NODE(EnumValue) \
- NODE(TypeAlias) \
- NODE(TypeRawAlias) \
- NODE(CompoundType) \
- NODE(TypeOf) \
- \
- NODE(Binding) \
- NODE(Alias) \
- NODE(MemRes) \
- NODE(Include) \
- NODE(UsePackage) \
- NODE(Global) \
- NODE(Param) \
- NODE(Function) \
- NODE(OverloadedFunction) \
- \
- NODE(PolyParam) \
- NODE(PolySolution) \
- NODE(SolidifiedFunction) \
- NODE(PolyProc) \
- \
- NODE(Note) \
- NODE(CallSite) \
- \
- NODE(CodeBlock) \
- NODE(DirectiveInsert) \
- NODE(Macro) \
- \
- NODE(Package)
-
-#define NODE(name) typedef struct Ast ## name Ast ## name;
-AST_NODES
-#undef NODE
-
-typedef struct Package Package;
-
-typedef struct Scope {
- u64 id;
- struct Scope *parent;
- OnyxFilePos created_at;
- bh_table(AstNode *) symbols;
-} Scope;
-
-
-typedef enum AstKind {
- Ast_Kind_Error,
- Ast_Kind_Package,
- Ast_Kind_Load_File,
- Ast_Kind_Load_Path,
- Ast_Kind_Memres,
-
- Ast_Kind_Binding,
- Ast_Kind_Alias,
- Ast_Kind_Function,
- Ast_Kind_Overloaded_Function,
- Ast_Kind_Polymorphic_Proc,
- Ast_Kind_Block,
- Ast_Kind_Local,
- Ast_Kind_Global,
- Ast_Kind_Symbol,
-
- Ast_Kind_Unary_Op,
- Ast_Kind_Binary_Op,
-
- Ast_Kind_Compound,
- Ast_Kind_Named_Value,
-
- Ast_Kind_Type_Start,
- Ast_Kind_Type,
- Ast_Kind_Basic_Type,
- Ast_Kind_Pointer_Type,
- Ast_Kind_Function_Type,
- Ast_Kind_Array_Type,
- Ast_Kind_Slice_Type,
- Ast_Kind_DynArr_Type,
- Ast_Kind_VarArg_Type,
- Ast_Kind_Struct_Type,
- Ast_Kind_Poly_Struct_Type,
- Ast_Kind_Poly_Call_Type,
- Ast_Kind_Enum_Type,
- Ast_Kind_Type_Alias,
- Ast_Kind_Type_Raw_Alias,
- Ast_Kind_Type_Compound,
- Ast_Kind_Typeof,
- Ast_Kind_Type_End,
-
- Ast_Kind_Struct_Member,
- Ast_Kind_Enum_Value,
-
- Ast_Kind_NumLit,
- Ast_Kind_StrLit,
- Ast_Kind_Param,
- Ast_Kind_Argument,
- Ast_Kind_Call,
- Ast_Kind_Intrinsic_Call,
- Ast_Kind_Return,
- Ast_Kind_Address_Of,
- Ast_Kind_Dereference,
- Ast_Kind_Subscript,
- Ast_Kind_Slice,
- Ast_Kind_Field_Access,
- Ast_Kind_Unary_Field_Access,
- Ast_Kind_Pipe,
- Ast_Kind_Method_Call,
- Ast_Kind_Range_Literal,
- Ast_Kind_Size_Of,
- Ast_Kind_Align_Of,
- Ast_Kind_File_Contents,
- Ast_Kind_Struct_Literal,
- Ast_Kind_Array_Literal,
- Ast_Kind_If_Expression,
-
- Ast_Kind_If,
- Ast_Kind_For,
- Ast_Kind_While,
- Ast_Kind_Jump,
- Ast_Kind_Use,
- Ast_Kind_Defer,
- Ast_Kind_Switch,
- Ast_Kind_Switch_Case,
-
- Ast_Kind_Directive_Solidify,
- Ast_Kind_Static_If,
- Ast_Kind_Directive_Error,
- Ast_Kind_Directive_Add_Overload,
- Ast_Kind_Directive_Operator,
- Ast_Kind_Directive_Export,
- Ast_Kind_Directive_Defined,
- Ast_Kind_Call_Site,
-
- Ast_Kind_Code_Block,
- Ast_Kind_Directive_Insert,
- Ast_Kind_Macro,
- Ast_Kind_Do_Block,
-
- Ast_Kind_Note,
-
- Ast_Kind_Count
-} AstKind;
-
-// NOTE: Some of these flags will overlap since there are
-// only 32-bits of flags to play with
-typedef enum AstFlags {
- // Top-level flags
- Ast_Flag_Exported = BH_BIT(1),
- Ast_Flag_Foreign = BH_BIT(2),
- Ast_Flag_Const = BH_BIT(3),
- Ast_Flag_Comptime = BH_BIT(4),
- Ast_Flag_Private_Package = BH_BIT(5),
- Ast_Flag_Private_File = BH_BIT(6),
-
- // Global flags
- Ast_Flag_Global_Stack_Top = BH_BIT(7),
-
- // Function flags
- Ast_Flag_Already_Checked = BH_BIT(8),
- Ast_Flag_Intrinsic = BH_BIT(10),
- Ast_Flag_Function_Used = BH_BIT(11),
-
- // Expression flags
- Ast_Flag_Expr_Ignored = BH_BIT(13),
- Ast_Flag_Param_Use = BH_BIT(14), // Unneeded, just use a bool on AstParam
- Ast_Flag_Address_Taken = BH_BIT(15),
-
- // Type flags
- Ast_Flag_Type_Is_Resolved = BH_BIT(16),
-
- // Enum flags
- Ast_Flag_Enum_Is_Flags = BH_BIT(17), // Unneeded, just use a bool on AstEnum
-
- // Struct flags
- Ast_Flag_Struct_Is_Union = BH_BIT(18), // Unneeded, just a usea bool on AstStruct
-
- Ast_Flag_No_Clone = BH_BIT(19),
-
- Ast_Flag_Cannot_Take_Addr = BH_BIT(20),
-
- Ast_Flag_Struct_Mem_Used = BH_BIT(21), // Unneeded, just a usea bool on AstStructMember
-
- // HACK: NullProcHack
- Ast_Flag_Proc_Is_Null = BH_BIT(22),
-
- Ast_Flag_From_Polymorphism = BH_BIT(23),
-
- Ast_Flag_Incomplete_Body = BH_BIT(24),
-
- Ast_Flag_Array_Literal_Typed = BH_BIT(25),
-
- Ast_Flag_Has_Been_Symres = BH_BIT(26),
-
- Ast_Flag_Has_Been_Checked = BH_BIT(27),
-
- Ast_Flag_Static_If_Resolved = BH_BIT(28),
-
- // :TEMPORARY
- Ast_Flag_Params_Introduced = BH_BIT(29),
-} AstFlags;
-
-typedef enum UnaryOp {
- Unary_Op_Negate,
- Unary_Op_Not,
- Unary_Op_Bitwise_Not,
- Unary_Op_Cast,
- Unary_Op_Auto_Cast,
-} UnaryOp;
-
-typedef enum BinaryOp {
- Binary_Op_Add = 0,
- Binary_Op_Minus = 1,
- Binary_Op_Multiply = 2,
- Binary_Op_Divide = 3,
- Binary_Op_Modulus = 4,
-
- Binary_Op_Equal = 5,
- Binary_Op_Not_Equal = 6,
- Binary_Op_Less = 7,
- Binary_Op_Less_Equal = 8,
- Binary_Op_Greater = 9,
- Binary_Op_Greater_Equal = 10,
-
- Binary_Op_And = 11,
- Binary_Op_Or = 12,
- Binary_Op_Xor = 13,
- Binary_Op_Shl = 14,
- Binary_Op_Shr = 15,
- Binary_Op_Sar = 16,
-
- Binary_Op_Bool_And = 17,
- Binary_Op_Bool_Or = 18,
-
- Binary_Op_Assign_Start = 19,
- Binary_Op_Assign = 20,
- Binary_Op_Assign_Add = 21,
- Binary_Op_Assign_Minus = 22,
- Binary_Op_Assign_Multiply = 23,
- Binary_Op_Assign_Divide = 24,
- Binary_Op_Assign_Modulus = 25,
- Binary_Op_Assign_And = 26,
- Binary_Op_Assign_Or = 27,
- Binary_Op_Assign_Xor = 28,
- Binary_Op_Assign_Shl = 29,
- Binary_Op_Assign_Shr = 30,
- Binary_Op_Assign_Sar = 31,
- Binary_Op_Assign_End = 32,
-
- Binary_Op_Pipe = 33,
- Binary_Op_Range = 34,
- Binary_Op_Method_Call = 35,
-
- Binary_Op_Subscript = 36,
-
- Binary_Op_Count
-} BinaryOp;
-
-extern const char *binaryop_string[Binary_Op_Count];
-
-typedef enum OnyxIntrinsic {
- ONYX_INTRINSIC_UNDEFINED,
-
- ONYX_INTRINSIC_MEMORY_SIZE, ONYX_INTRINSIC_MEMORY_GROW,
- ONYX_INTRINSIC_MEMORY_COPY, ONYX_INTRINSIC_MEMORY_FILL,
-
- ONYX_INTRINSIC_INITIALIZE, ONYX_INTRINSIC_ZERO_VALUE,
-
- ONYX_INTRINSIC_I32_CLZ, ONYX_INTRINSIC_I32_CTZ, ONYX_INTRINSIC_I32_POPCNT,
- ONYX_INTRINSIC_I32_AND, ONYX_INTRINSIC_I32_OR, ONYX_INTRINSIC_I32_XOR,
- ONYX_INTRINSIC_I32_SHL, ONYX_INTRINSIC_I32_SLR, ONYX_INTRINSIC_I32_SAR,
- ONYX_INTRINSIC_I32_ROTL, ONYX_INTRINSIC_I32_ROTR,
-
- ONYX_INTRINSIC_I64_CLZ, ONYX_INTRINSIC_I64_CTZ, ONYX_INTRINSIC_I64_POPCNT,
- ONYX_INTRINSIC_I64_AND, ONYX_INTRINSIC_I64_OR, ONYX_INTRINSIC_I64_XOR,
- ONYX_INTRINSIC_I64_SHL, ONYX_INTRINSIC_I64_SLR, ONYX_INTRINSIC_I64_SAR,
- ONYX_INTRINSIC_I64_ROTL, ONYX_INTRINSIC_I64_ROTR,
-
- ONYX_INTRINSIC_F32_ABS, ONYX_INTRINSIC_F32_SQRT,
- ONYX_INTRINSIC_F32_CEIL, ONYX_INTRINSIC_F32_FLOOR,
- ONYX_INTRINSIC_F32_TRUNC, ONYX_INTRINSIC_F32_NEAREST,
- ONYX_INTRINSIC_F32_MIN, ONYX_INTRINSIC_F32_MAX,
- ONYX_INTRINSIC_F32_COPYSIGN,
-
- ONYX_INTRINSIC_F64_ABS, ONYX_INTRINSIC_F64_SQRT,
- ONYX_INTRINSIC_F64_CEIL, ONYX_INTRINSIC_F64_FLOOR,
- ONYX_INTRINSIC_F64_TRUNC, ONYX_INTRINSIC_F64_NEAREST,
- ONYX_INTRINSIC_F64_MIN, ONYX_INTRINSIC_F64_MAX,
- ONYX_INTRINSIC_F64_COPYSIGN,
-
-
-
- ONYX_INTRINSIC_V128_CONST,
- ONYX_INTRINSIC_I8X16_CONST, ONYX_INTRINSIC_I16X8_CONST,
- ONYX_INTRINSIC_I32X4_CONST, ONYX_INTRINSIC_I64X2_CONST,
- ONYX_INTRINSIC_F32X4_CONST, ONYX_INTRINSIC_F64X2_CONST,
- ONYX_INTRINSIC_I8X16_SHUFFLE,
-
- ONYX_INTRINSIC_I8X16_EXTRACT_LANE_S, ONYX_INTRINSIC_I8X16_EXTRACT_LANE_U, ONYX_INTRINSIC_I8X16_REPLACE_LANE,
- ONYX_INTRINSIC_I16X8_EXTRACT_LANE_S, ONYX_INTRINSIC_I16X8_EXTRACT_LANE_U, ONYX_INTRINSIC_I16X8_REPLACE_LANE,
- ONYX_INTRINSIC_I32X4_EXTRACT_LANE, ONYX_INTRINSIC_I32X4_REPLACE_LANE,
- ONYX_INTRINSIC_I64X2_EXTRACT_LANE, ONYX_INTRINSIC_I64X2_REPLACE_LANE,
- ONYX_INTRINSIC_F32X4_EXTRACT_LANE, ONYX_INTRINSIC_F32X4_REPLACE_LANE,
- ONYX_INTRINSIC_F64X2_EXTRACT_LANE, ONYX_INTRINSIC_F64X2_REPLACE_LANE,
-
- ONYX_INTRINSIC_I8X16_SWIZZLE,
- ONYX_INTRINSIC_I8X16_SPLAT, ONYX_INTRINSIC_I16X8_SPLAT,
- ONYX_INTRINSIC_I32X4_SPLAT, ONYX_INTRINSIC_I64X2_SPLAT,
- ONYX_INTRINSIC_F32X4_SPLAT, ONYX_INTRINSIC_F64X2_SPLAT,
-
- ONYX_INTRINSIC_I8X16_EQ, ONYX_INTRINSIC_I8X16_NEQ,
- ONYX_INTRINSIC_I8X16_LT_S, ONYX_INTRINSIC_I8X16_LT_U,
- ONYX_INTRINSIC_I8X16_GT_S, ONYX_INTRINSIC_I8X16_GT_U,
- ONYX_INTRINSIC_I8X16_LE_S, ONYX_INTRINSIC_I8X16_LE_U,
- ONYX_INTRINSIC_I8X16_GE_S, ONYX_INTRINSIC_I8X16_GE_U,
-
- ONYX_INTRINSIC_I16X8_EQ, ONYX_INTRINSIC_I16X8_NEQ,
- ONYX_INTRINSIC_I16X8_LT_S, ONYX_INTRINSIC_I16X8_LT_U,
- ONYX_INTRINSIC_I16X8_GT_S, ONYX_INTRINSIC_I16X8_GT_U,
- ONYX_INTRINSIC_I16X8_LE_S, ONYX_INTRINSIC_I16X8_LE_U,
- ONYX_INTRINSIC_I16X8_GE_S, ONYX_INTRINSIC_I16X8_GE_U,
-
- ONYX_INTRINSIC_I32X4_EQ, ONYX_INTRINSIC_I32X4_NEQ,
- ONYX_INTRINSIC_I32X4_LT_S, ONYX_INTRINSIC_I32X4_LT_U,
- ONYX_INTRINSIC_I32X4_GT_S, ONYX_INTRINSIC_I32X4_GT_U,
- ONYX_INTRINSIC_I32X4_LE_S, ONYX_INTRINSIC_I32X4_LE_U,
- ONYX_INTRINSIC_I32X4_GE_S, ONYX_INTRINSIC_I32X4_GE_U,
-
- ONYX_INTRINSIC_F32X4_EQ, ONYX_INTRINSIC_F32X4_NEQ,
- ONYX_INTRINSIC_F32X4_LT, ONYX_INTRINSIC_F32X4_GT,
- ONYX_INTRINSIC_F32X4_LE, ONYX_INTRINSIC_F32X4_GE,
-
- ONYX_INTRINSIC_F64X2_EQ, ONYX_INTRINSIC_F64X2_NEQ,
- ONYX_INTRINSIC_F64X2_LT, ONYX_INTRINSIC_F64X2_GT,
- ONYX_INTRINSIC_F64X2_LE, ONYX_INTRINSIC_F64X2_GE,
-
- ONYX_INTRINSIC_V128_NOT, ONYX_INTRINSIC_V128_AND, ONYX_INTRINSIC_V128_ANDNOT,
- ONYX_INTRINSIC_V128_OR, ONYX_INTRINSIC_V128_XOR, ONYX_INTRINSIC_V128_BITSELECT,
-
- ONYX_INTRINSIC_I8X16_ABS, ONYX_INTRINSIC_I8X16_NEG,
- ONYX_INTRINSIC_I8X16_ANY_TRUE, ONYX_INTRINSIC_I8X16_ALL_TRUE,
- ONYX_INTRINSIC_I8X16_BITMASK,
- ONYX_INTRINSIC_I8X16_NARROW_I16X8_S, ONYX_INTRINSIC_I8X16_NARROW_I16X8_U,
- ONYX_INTRINSIC_I8X16_SHL, ONYX_INTRINSIC_I8X16_SHR_S, ONYX_INTRINSIC_I8X16_SHR_U,
- ONYX_INTRINSIC_I8X16_ADD, ONYX_INTRINSIC_I8X16_ADD_SAT_S, ONYX_INTRINSIC_I8X16_ADD_SAT_U,
- ONYX_INTRINSIC_I8X16_SUB, ONYX_INTRINSIC_I8X16_SUB_SAT_S, ONYX_INTRINSIC_I8X16_SUB_SAT_U,
- ONYX_INTRINSIC_I8X16_MIN_S, ONYX_INTRINSIC_I8X16_MIN_U,
- ONYX_INTRINSIC_I8X16_MAX_S, ONYX_INTRINSIC_I8X16_MAX_U,
- ONYX_INTRINSIC_I8X16_AVGR_U,
-
- ONYX_INTRINSIC_I16X8_ABS, ONYX_INTRINSIC_I16X8_NEG,
- ONYX_INTRINSIC_I16X8_ANY_TRUE, ONYX_INTRINSIC_I16X8_ALL_TRUE,
- ONYX_INTRINSIC_I16X8_BITMASK,
- ONYX_INTRINSIC_I16X8_NARROW_I32X4_S, ONYX_INTRINSIC_I16X8_NARROW_I32X4_U,
- ONYX_INTRINSIC_I16X8_WIDEN_LOW_I8X16_S, ONYX_INTRINSIC_I16X8_WIDEN_HIGH_I8X16_S,
- ONYX_INTRINSIC_I16X8_WIDEN_LOW_I8X16_U, ONYX_INTRINSIC_I16X8_WIDEN_HIGH_I8X16_U,
- ONYX_INTRINSIC_I16X8_SHL, ONYX_INTRINSIC_I16X8_SHR_S, ONYX_INTRINSIC_I16X8_SHR_U,
- ONYX_INTRINSIC_I16X8_ADD, ONYX_INTRINSIC_I16X8_ADD_SAT_S, ONYX_INTRINSIC_I16X8_ADD_SAT_U,
- ONYX_INTRINSIC_I16X8_SUB, ONYX_INTRINSIC_I16X8_SUB_SAT_S, ONYX_INTRINSIC_I16X8_SUB_SAT_U,
- ONYX_INTRINSIC_I16X8_MUL,
- ONYX_INTRINSIC_I16X8_MIN_S, ONYX_INTRINSIC_I16X8_MIN_U,
- ONYX_INTRINSIC_I16X8_MAX_S, ONYX_INTRINSIC_I16X8_MAX_U,
- ONYX_INTRINSIC_I16X8_AVGR_U,
-
- ONYX_INTRINSIC_I32X4_ABS, ONYX_INTRINSIC_I32X4_NEG,
- ONYX_INTRINSIC_I32X4_ANY_TRUE, ONYX_INTRINSIC_I32X4_ALL_TRUE,
- ONYX_INTRINSIC_I32X4_BITMASK,
- ONYX_INTRINSIC_I32X4_WIDEN_LOW_I16X8_S, ONYX_INTRINSIC_I32X4_WIDEN_HIGH_I16X8_S,
- ONYX_INTRINSIC_I32X4_WIDEN_LOW_I16X8_U, ONYX_INTRINSIC_I32X4_WIDEN_HIGH_I16X8_U,
- ONYX_INTRINSIC_I32X4_SHL, ONYX_INTRINSIC_I32X4_SHR_S, ONYX_INTRINSIC_I32X4_SHR_U,
- ONYX_INTRINSIC_I32X4_ADD, ONYX_INTRINSIC_I32X4_SUB, ONYX_INTRINSIC_I32X4_MUL,
- ONYX_INTRINSIC_I32X4_MIN_S, ONYX_INTRINSIC_I32X4_MIN_U,
- ONYX_INTRINSIC_I32X4_MAX_S, ONYX_INTRINSIC_I32X4_MAX_U,
-
- ONYX_INTRINSIC_I64X2_NEG, ONYX_INTRINSIC_I64X2_SHL,
- ONYX_INTRINSIC_I64X2_SHR_S, ONYX_INTRINSIC_I64X2_SHR_U,
- ONYX_INTRINSIC_I64X2_ADD, ONYX_INTRINSIC_I64X2_SUB, ONYX_INTRINSIC_I64X2_MUL,
-
- ONYX_INTRINSIC_F32X4_ABS, ONYX_INTRINSIC_F32X4_NEG, ONYX_INTRINSIC_F32X4_SQRT,
- ONYX_INTRINSIC_F32X4_ADD, ONYX_INTRINSIC_F32X4_SUB,
- ONYX_INTRINSIC_F32X4_MUL, ONYX_INTRINSIC_F32X4_DIV,
- ONYX_INTRINSIC_F32X4_MIN, ONYX_INTRINSIC_F32X4_MAX,
-
- ONYX_INTRINSIC_F64X2_ABS, ONYX_INTRINSIC_F64X2_NEG, ONYX_INTRINSIC_F64X2_SQRT,
- ONYX_INTRINSIC_F64X2_ADD, ONYX_INTRINSIC_F64X2_SUB,
- ONYX_INTRINSIC_F64X2_MUL, ONYX_INTRINSIC_F64X2_DIV,
- ONYX_INTRINSIC_F64X2_MIN, ONYX_INTRINSIC_F64X2_MAX,
-
- ONYX_INTRINSIC_I32X4_TRUNC_SAT_F32X4_S,
- ONYX_INTRINSIC_I32X4_TRUNC_SAT_F32X4_U,
- ONYX_INTRINSIC_F32X4_CONVERT_I32X4_S,
- ONYX_INTRINSIC_F32X4_CONVERT_I32X4_U,
-} OnyxIntrinsic;
-
-typedef enum CallingConvention {
- CC_Undefined,
- CC_Return_Wasm,
- CC_Return_Stack
-} CallingConvention;
-
-typedef enum JumpType {
- Jump_Type_Break,
- Jump_Type_Continue,
- Jump_Type_Fallthrough,
- Jump_Type_Return,
-
- Jump_Type_Count,
-} JumpType;
-
-typedef enum ForLoopType {
- For_Loop_Invalid,
- For_Loop_Range,
- For_Loop_Array,
- For_Loop_Slice,
- For_Loop_DynArr,
- For_Loop_Iterator,
-} ForLoopType;
-
-typedef enum ParamPassType {
- Param_Pass_Invalid,
- Param_Pass_By_Value,
- Param_Pass_By_VarArg,
- Param_Pass_By_Implicit_Pointer,
-} ParamPassType;
-
-typedef enum VarArgKind {
- VA_Kind_Not_VA,
- VA_Kind_Typed,
- VA_Kind_Any,
- VA_Kind_Untyped,
-} VarArgKind;
-
-typedef enum BlockRule {
- Block_Rule_New_Scope = BH_BIT(1),
- Block_Rule_Emit_Instructions = BH_BIT(2),
- Block_Rule_Clear_Defer = BH_BIT(3),
- Block_Rule_Override_Return = BH_BIT(4),
-
- Block_Rule_Normal = Block_Rule_New_Scope | Block_Rule_Clear_Defer | Block_Rule_Emit_Instructions,
- Block_Rule_Macro = Block_Rule_New_Scope,
- Block_Rule_Code_Block = Block_Rule_New_Scope,
- Block_Rule_Do_Block = Block_Rule_New_Scope | Block_Rule_Clear_Defer | Block_Rule_Override_Return | Block_Rule_Emit_Instructions,
-} BlockRule;
-
-typedef struct Arguments Arguments;
-struct Arguments {
- bh_arr(AstTyped *) values;
- bh_arr(AstNamedValue *) named_values;
-};
-
-
-// Base Nodes
-#define AstNode_base \
- AstKind kind; \
- u32 flags; \
- OnyxToken *token; \
- struct Entity* entity; \
- AstNode *next
-struct AstNode { AstNode_base; };
-
-// NOTE: 'type_node' is filled out by the parser.
-// For a type such as '^^i32', the tree would look something like
-//
-// Typed Thing -> AstPointerType -> AstPointerType -> AstNode (symbol node)
-//
-// The symbol node will be filled out during symbol resolution.
-// It will end up pointing to an AstBasicType that corresponds to
-// the underlying type.
-//
-// 'type' is filled out afterwards. If it is NULL, the Type* is built
-// using the type_node. This can then be used to typecheck this node.
-#define AstTyped_base \
- AstNode_base; \
- AstType *type_node; \
- Type *type
-struct AstTyped { AstTyped_base; };
-
-// Expression Nodes
-struct AstNamedValue { AstTyped_base; AstTyped* value; };
-struct AstBinaryOp { AstTyped_base; BinaryOp operation; AstTyped *left, *right; };
-struct AstUnaryOp { AstTyped_base; UnaryOp operation; AstTyped *expr; };
-struct AstNumLit { AstTyped_base; union { i32 i; i64 l; f32 f; f64 d; } value; };
-struct AstStrLit { AstTyped_base; u64 addr; u64 length; };
-struct AstLocal { AstTyped_base; };
-struct AstArgument { AstTyped_base; AstTyped *value; VarArgKind va_kind; b32 is_baked : 1; };
-struct AstAddressOf { AstTyped_base; AstTyped *expr; };
-struct AstDereference { AstTyped_base; AstTyped *expr; };
-struct AstSizeOf { AstTyped_base; AstType *so_ast_type; Type *so_type; u64 size; };
-struct AstAlignOf { AstTyped_base; AstType *ao_ast_type; Type *ao_type; u64 alignment; };
-struct AstSubscript {
- AstTyped_base;
- BinaryOp __unused_operation; // This will be set to Binary_Op_Subscript
- AstTyped *addr;
- AstTyped *expr;
- u64 elem_size;
-};
-struct AstFieldAccess {
- AstTyped_base;
- AstTyped *expr;
- u32 offset;
- u32 idx;
- char* field; // If token is null, defer to field
-};
-struct AstFileContents {
- AstTyped_base;
-
- OnyxToken *filename_token;
- char *filename; // The parsed file name, with '\' sequences removed and resolved to a particular file if possible.
-
- u32 addr, size;
-};
-struct AstUnaryFieldAccess {
- AstTyped_base;
-
- // "token" represents the field. This does not need an "offset" or an "index"
- // because this node is meant to be replaced.
-};
-struct AstStructLiteral {
- AstTyped_base;
-
- AstTyped *stnode;
-
- Arguments args;
-};
-struct AstArrayLiteral {
- AstTyped_base;
-
- AstTyped *atnode;
-
- bh_arr(AstTyped *) values;
-};
-struct AstRangeLiteral {
- AstTyped_base;
-
- // HACK: Currently, range literals are parsed as binary operators, which means
- // the first sizeof(AstBinaryOp) bytes of this structure must match that of
- // AstBinaryOp, which means I need this dummy field here.
- // - brendanfh 2020/12/23
- BinaryOp __unused_operation;
- AstTyped *low, *high;
-
- // Currently, there is no way to specify this in the grammar, but it is set
- // to be the initial value of the `step` member of the range structure in
- // core/builtin.onyx.
- // - brendanfh 2020/12/23
- AstTyped *step;
-};
-struct AstCall {
- AstTyped_base;
-
- Arguments original_args;
- Arguments args;
-
- union {
- AstTyped *callee;
- OnyxIntrinsic intrinsic;
- };
-
- VarArgKind va_kind;
-};
-struct AstCompound {
- AstTyped_base;
-
- bh_arr(AstTyped *) exprs;
-};
-struct AstIfExpression {
- AstTyped_base;
-
- AstTyped* cond;
- AstTyped* true_expr;
- AstTyped* false_expr;
-};
-struct AstDoBlock {
- AstTyped_base;
-
- AstBlock* block;
-};
-
-struct AstDirectiveSolidify {
- AstTyped_base;
-
- AstPolyProc* poly_proc;
- bh_arr(AstPolySolution) known_polyvars;
-
- AstNode* resolved_proc;
-};
-
-// Intruction Node
-struct AstReturn { AstNode_base; AstTyped* expr; };
-struct AstJump { AstNode_base; JumpType jump; u32 count; };
-
-typedef struct QualifiedUse {
- OnyxToken* symbol_name;
- OnyxToken* as_name;
-} QualifiedUse;
-struct AstUse {
- AstNode_base;
-
- AstTyped* expr;
- bh_arr(QualifiedUse) only;
-};
-
-// Structure Nodes
-struct AstBlock {
- AstNode_base;
-
- AstNode *body;
-
- Scope *scope;
- Scope *binding_scope;
- BlockRule rules;
-};
-struct AstDefer { AstNode_base; AstNode *stmt; };
-struct AstFor {
- AstNode_base;
-
- // NOTE: Stores the iteration variable
- Scope *scope;
-
- // NOTE: Local defining the iteration variable
- AstLocal* var;
-
- // NOTE: This can be any expression, but it is checked that
- // it is of a type that we know how to iterate over.
- AstTyped* iter;
- ForLoopType loop_type;
-
- AstBlock *stmt;
-
- // ROBUSTNESS: This should be able to be set by a compile time variable at some point.
- // But for now, this will do.
- b32 by_pointer : 1;
-};
-struct AstIfWhile {
- AstNode_base;
-
- Scope *scope;
- AstNode* initialization;
-
- AstTyped *cond;
-
- AstBlock *true_stmt;
- AstBlock *false_stmt;
-
- // Used by Static_If
- bh_arr(struct Entity *) true_entities;
- bh_arr(struct Entity *) false_entities;
-};
-typedef struct AstIfWhile AstIf;
-typedef struct AstIfWhile AstWhile;
-
-struct AstSwitchCase {
- // NOTE: All expressions that end up in this block
- bh_arr(AstTyped *) values;
-
- AstBlock *block;
-};
-struct AstSwitch {
- AstNode_base;
-
- Scope *scope;
- AstNode* initialization;
-
- AstTyped *expr;
-
- bh_arr(AstSwitchCase) cases;
- AstBlock *default_case;
-
- // NOTE: This is a mapping from the compile time known case value
- // to a pointer to the block that it is associated with.
- bh_imap case_map;
- u64 min_case, max_case;
-};
-
-// Type Nodes
-// NOTE: This node is very similar to an AstNode, just
-// without the 'next' member. This is because types
-// can't be in expressions so a 'next' thing
-// doesn't make sense.
-#define AstType_base \
- AstKind kind; \
- u32 flags; \
- OnyxToken* token; \
- struct Entity* entity; \
- u64 type_id; \
- void* __unused; \
- Type* type
-struct AstType { AstType_base; };
-
-struct AstBasicType { AstType_base; Type* basic_type; };
-struct AstPointerType { AstType_base; AstType* elem; };
-struct AstFunctionType { AstType_base; AstType* return_type; u64 param_count; AstType* params[]; };
-struct AstArrayType { AstType_base; AstType* elem; AstTyped *count_expr; };
-struct AstSliceType { AstType_base; AstType* elem; };
-struct AstDynArrType { AstType_base; AstType* elem; };
-struct AstVarArgType { AstType_base; AstType* elem; };
-struct AstStructType {
- AstType_base;
- char *name;
-
- bh_arr(AstStructMember *) members;
-
- u32 min_alignment, min_size;
-
- // NOTE: Used to cache the actual type, since building
- // a struct type is kind of complicated and should
- // only happen once.
- Type *stcache;
-
- // NOTE: Used to store statically bound expressions in the struct.
- Scope* scope;
-
- struct Entity* entity_type;
- struct Entity* entity_defaults;
-
- b32 stcache_is_valid : 1;
-};
-struct AstStructMember {
- AstTyped_base;
- AstTyped* initial_value;
-};
-struct AstPolyStructParam {
- AstTyped_base;
-};
-struct AstPolyStructType {
- AstType_base;
- char *name;
-
- Scope *scope;
- bh_arr(AstPolyStructParam) poly_params;
- bh_table(AstStructType *) concrete_structs;
-
- AstStructType* base_struct;
-};
-struct AstPolyCallType {
- AstType_base;
-
- AstType* callee;
-
- // NOTE: These nodes can be either AstTypes, or AstTyped expressions.
- bh_arr(AstNode *) params;
-};
-struct AstEnumType {
- AstType_base;
- char *name;
- Scope *scope;
-
- AstType *backing;
- Type *backing_type;
-
- bh_arr(AstEnumValue *) values;
-
- // NOTE: Used to cache the actual type for the same reason as above.
- Type *etcache;
-};
-struct AstEnumValue { AstTyped_base; AstNumLit* value; };
-struct AstTypeAlias { AstType_base; AstType* to; };
-struct AstTypeRawAlias { AstType_base; Type* to; };
-struct AstCompoundType {
- AstType_base;
-
- bh_arr(AstType *) types;
-};
-struct AstTypeOf {
- AstType_base;
-
- AstTyped* expr;
- Type* resolved_type;
-};
-
-// Top level nodes
-struct AstBinding { AstTyped_base; AstNode* node; };
-struct AstAlias { AstTyped_base; AstTyped* alias; };
-struct AstMemRes { AstTyped_base; u64 addr; AstTyped *initial_value; };
-struct AstInclude { AstNode_base; char* name; };
-struct AstGlobal {
- AstTyped_base;
-
- OnyxToken* name;
-
- OnyxToken* foreign_module;
- OnyxToken* foreign_name;
-};
-struct AstParam {
- // HACK CLEANUP: This does not need to have a local buried inside of it.
- // Convert this to be AstTyped_base and pull the ParamPassType from AstLocal
- // to here. - brendanfh 2020/09/18
- AstLocal *local;
- AstTyped *default_value;
-
- VarArgKind vararg_kind;
- b32 use_processed : 1;
-};
-struct AstFunction {
- AstTyped_base;
-
- Scope *scope;
-
- bh_arr(AstParam) params;
- AstType* return_type;
-
- AstBlock *body;
-
- OnyxToken* name;
-
-
- // NOTE: This is NULL, unless this function was generated from a polymorphic
- // procedure call. Then it is set to the token of the call node.
- OnyxToken* generated_from;
-
- // NOTE: This is NULL, unless this function is used in a "#export" directive.
- // It is undefined which name it will have if there are multiple export directives
- // for this particular function.
- OnyxToken* exported_name;
-
- union {
- OnyxToken* intrinsic_name;
-
- // NOTE: Used when the function is declared as foreign
- struct {
- OnyxToken* foreign_module;
- OnyxToken* foreign_name;
- };
- };
-
- struct Entity* entity_header;
- struct Entity* entity_body;
-};
-
-typedef struct OverloadOption OverloadOption;
-struct OverloadOption {
- // This is u64 because padding will make it that anyway.
- // Consider: would there be any practical benefit to having the precedence setting
- // be a compile-time known value? as opposed to a hardcoded value?
- u64 precedence;
-
- AstTyped* option;
-};
-
-struct AstOverloadedFunction {
- AstTyped_base;
-
- bh_arr(OverloadOption) overloads;
-
- // CLEANUP: This is unused right now, but should be used to cache
- // the complete set of overloads that can be used by an overloaded
- // function.
- bh_imap all_overloads;
-};
-
-struct AstPackage {
- AstTyped_base;
-
- // Allocated in the ast arena
- char * package_name;
-
- // NOTE: Symbol nodes
- bh_arr(OnyxToken *) path;
-
- Package* package;
-};
-
-//
-// Polymorphic procedures
-//
-
-typedef enum PolyParamKind {
- // Dont love these names
- PPK_Undefined,
- PPK_Poly_Type,
- PPK_Baked_Value,
-} PolyParamKind;
-
-typedef enum PolySolutionKind {
- PSK_Undefined,
- PSK_Type,
- PSK_Value,
-} PolySolutionKind;
-
-struct AstPolyParam {
- PolyParamKind kind;
-
- // The parameter index where the polymorphic variable occurs.
- u32 idx;
-
- // The symbol node that represents the polymorphic variable.
- AstNode* poly_sym;
-
- // The type expression that contains `poly_sym` in it somewhere.
- // Matching polymorphic variables does a parallel tree traversal
- // to find the pairing of the actual type and polymorphic variable
- // symbol.
- AstType* type_expr;
-
- // Used for baked values. The expected type of the parameter.
- Type* type;
-};
-
-struct AstPolySolution {
- PolySolutionKind kind;
- AstNode* poly_sym;
-
- union {
- struct {
- // If `type` is null, it is filled in with this type.
- AstType* ast_type;
- Type* type;
- };
-
- AstTyped* value;
- };
-};
-
-struct AstSolidifiedFunction {
- AstFunction* func;
- Scope* poly_scope;
-
- b32 header_complete: 1;
-};
-
-struct AstPolyProc {
- // While the "type" of a polymorphic procedure will never be set, it is necessary
- // for contexts where it used in an expression.
- AstTyped_base;
-
- Scope *poly_scope;
- bh_arr(AstPolyParam) poly_params;
-
- bh_arr(AstPolySolution) known_slns;
-
- AstFunction* base_func;
- bh_table(AstSolidifiedFunction) concrete_funcs;
-};
-
-
-struct AstDirectiveError {
- AstNode_base;
-
- OnyxToken* error_msg;
-};
-
-struct AstDirectiveAddOverload {
- AstNode_base;
-
- // NOTE: used by the #add_overload directive. Initially set to a symbol,
- // then resolved to an overloaded function.
- AstNode *overloaded_function;
-
- // See note in OverloadOption. This could be refactored into an OverloadOption?
- u64 precedence;
- AstTyped *overload;
-};
-
-struct AstDirectiveOperator {
- AstNode_base;
-
- BinaryOp operator;
- AstTyped *overload;
-};
-
-struct AstDirectiveExport {
- AstNode_base;
-
- OnyxToken* export_name;
- AstTyped* export;
-};
-
-struct AstDirectiveDefined {
- AstTyped_base;
- AstTyped *expr;
-
- b32 is_defined: 1;
-};
-
-struct AstNote {
- AstNode_base;
-};
-
-struct AstCallSite {
- AstTyped_base;
-
- OnyxToken* callsite_token;
-
- AstStrLit* filename;
- AstNumLit* line;
- AstNumLit* column;
-};
-
-// Represents a "pastable" block of code.
-struct AstCodeBlock {
- AstTyped_base;
-
- AstNode *code;
-};
-
-struct AstDirectiveInsert {
- AstNode_base;
-
- AstTyped *code_expr;
-};
-
-struct AstMacro {
- AstTyped_base;
-
- AstTyped* body;
-};
-
-typedef enum EntityState {
- Entity_State_Error,
-
- Entity_State_Parse_Builtin,
- Entity_State_Introduce_Symbols,
- Entity_State_Parse,
- Entity_State_Resolve_Symbols,
- Entity_State_Check_Types,
- Entity_State_Code_Gen,
- Entity_State_Finalized,
-
- Entity_State_Count,
-} EntityState;
-
-extern const char* entity_state_strings[Entity_State_Count];
-
-// NOTE: An Entity represents something will need to be
-// processed later down the pipeline.
-typedef enum EntityType {
- Entity_Type_Unknown,
-
- Entity_Type_Error,
- Entity_Type_Note,
- Entity_Type_Load_Path,
- Entity_Type_Load_File,
- Entity_Type_Binding,
- Entity_Type_Use_Package,
- Entity_Type_Static_If,
- Entity_Type_String_Literal,
- Entity_Type_File_Contents,
- Entity_Type_Enum,
- Entity_Type_Type_Alias,
- Entity_Type_Memory_Reservation_Type,
- Entity_Type_Use,
- Entity_Type_Polymorphic_Proc,
- Entity_Type_Macro,
- Entity_Type_Foreign_Function_Header,
- Entity_Type_Foreign_Global_Header,
- Entity_Type_Function_Header,
- Entity_Type_Global_Header,
- Entity_Type_Process_Directive,
- Entity_Type_Struct_Member_Default,
- Entity_Type_Memory_Reservation,
- Entity_Type_Expression,
- Entity_Type_Global,
- Entity_Type_Overloaded_Function,
- Entity_Type_Function,
-
- Entity_Type_Count,
-} EntityType;
-
-extern const char* entity_type_strings[Entity_Type_Count];
-
-typedef struct Entity {
- u32 id;
-
- EntityType type;
- EntityState state;
-
- // TODO: Document this!
- u32 macro_attempts;
- u32 micro_attempts;
-
- b32 entered_in_queue : 1;
-
- Package *package;
- Scope *scope;
-
- // TODO: This is incomplete. Add proper cycle detection and halting.
- // struct Entity *waiting_on;
-
- union {
- AstDirectiveError *error;
- AstInclude *include;
- AstBinding *binding;
- AstIf *static_if;
- AstFunction *function;
- AstOverloadedFunction *overloaded_function;
- AstGlobal *global;
- AstTyped *expr;
- AstStrLit *strlit;
- AstFileContents *file_contents;
- AstType *type_alias;
- AstEnumType *enum_type;
- AstMemRes *mem_res;
- AstPolyProc *poly_proc;
- AstMacro *macro;
- AstUse *use;
- };
-} Entity;
-
-typedef struct EntityHeap {
- bh_arena entity_arena;
- bh_arr(Entity *) entities;
- i32 next_id;
-
- i32 state_count[Entity_State_Count];
- i32 type_count[Entity_Type_Count];
-
- i32 all_count[Entity_State_Count][Entity_Type_Count];
-} EntityHeap;
-
-void entity_heap_init(EntityHeap* entities);
-void entity_heap_insert_existing(EntityHeap* entities, Entity* e);
-Entity* entity_heap_insert(EntityHeap* entities, Entity e);
-Entity* entity_heap_top(EntityHeap* entities);
-void entity_heap_change_top(EntityHeap* entities, Entity* new_top);
-void entity_heap_remove_top(EntityHeap* entities);
-
-// If target_arr is null, the entities will be placed directly in the heap.
-void add_entities_for_node(bh_arr(Entity *)* target_arr, AstNode* node, Scope* scope, Package* package);
-
-b32 entity_bring_to_state(Entity* ent, EntityState state);
-void symres_entity(Entity* ent);
-void check_entity(Entity* ent);
-void emit_entity(Entity* ent);
-
-struct Package {
- char *name;
-
- Scope *scope;
- Scope *private_scope;
-
- // NOTE: This tracks all of the 'use package' statements of this package throughout
- // the code base. This is used when a static if clears and new symbols are introduced.
- // 'use package' statements have to be reevaluated to pull in the new symbols.
- bh_arr(Entity *) use_package_entities;
-};
-
-typedef enum CompileAction CompileAction;
-enum CompileAction {
- ONYX_COMPILE_ACTION_COMPILE,
- ONYX_COMPILE_ACTION_DOCUMENT,
- ONYX_COMPILE_ACTION_PRINT_HELP,
-};
-
-
-// ROBUSTNESS: The Runtime definitions here must match those in build_opts.onyx!!
-typedef enum Runtime Runtime;
-enum Runtime {
- Runtime_Unknown = 0,
- Runtime_Wasi = 1,
- Runtime_Js = 2,
- Runtime_Custom = 3,
-};
-
-
-typedef struct CompileOptions CompileOptions;
-struct CompileOptions {
- bh_allocator allocator;
- CompileAction action;
-
- u32 verbose_output : 2;
- b32 fun_output : 1;
- b32 print_function_mappings : 1;
- b32 print_static_if_results : 1;
- b32 print_notes : 1;
- b32 no_colors : 1;
- b32 no_file_contents : 1;
-
- b32 use_post_mvp_features : 1;
-
- Runtime runtime;
-
- bh_arr(const char *) included_folders;
- bh_arr(const char *) files;
- const char* target_file;
- const char* documentation_file;
-};
-
-typedef struct Context Context;
-struct Context {
- bh_table(Package *) packages;
- EntityHeap entities;
-
- Scope *global_scope;
-
- CompileOptions* options;
-
- bh_arena ast_arena;
- bh_allocator token_alloc, ast_alloc;
-
- bh_arr(bh_file_contents) loaded_files;
-
- // NOTE: This is defined in onyxwasm.h
- struct OnyxWasmModule* wasm_module;
-
- b32 cycle_detected : 1;
-};
-
-extern Context context;
-
-// NOTE: Basic internal types constructed in the parser
-extern AstBasicType basic_type_void;
-extern AstBasicType basic_type_bool;
-extern AstBasicType basic_type_i8;
-extern AstBasicType basic_type_u8;
-extern AstBasicType basic_type_i16;
-extern AstBasicType basic_type_u16;
-extern AstBasicType basic_type_i32;
-extern AstBasicType basic_type_u32;
-extern AstBasicType basic_type_i64;
-extern AstBasicType basic_type_u64;
-extern AstBasicType basic_type_f32;
-extern AstBasicType basic_type_f64;
-extern AstBasicType basic_type_rawptr;
-extern AstBasicType basic_type_type_expr; // :TypeExprHack
-
-extern AstBasicType basic_type_int_unsized;
-extern AstBasicType basic_type_float_unsized;
-
-extern AstBasicType basic_type_i8x16;
-extern AstBasicType basic_type_i16x8;
-extern AstBasicType basic_type_i32x4;
-extern AstBasicType basic_type_i64x2;
-extern AstBasicType basic_type_f32x4;
-extern AstBasicType basic_type_f64x2;
-extern AstBasicType basic_type_v128;
-
-// HACK
-// :AutoReturnType
-extern Type type_auto_return;
-extern AstBasicType basic_type_auto_return;
-
-extern OnyxToken builtin_package_token;
-extern AstNumLit builtin_heap_start;
-extern AstGlobal builtin_stack_top;
-extern AstType *builtin_string_type;
-extern AstType *builtin_range_type;
-extern Type *builtin_range_type_type;
-extern AstType *builtin_vararg_type;
-extern Type *builtin_vararg_type_type;
-extern AstTyped *builtin_context_variable;
-extern AstType *builtin_allocator_type;
-extern AstType *builtin_iterator_type;
-extern AstType *builtin_callsite_type;
-extern AstType *builtin_any_type;
-extern AstType *builtin_code_type;
-extern AstTyped *type_table_node;
-
-typedef struct BuiltinSymbol {
- char* package;
- char* sym;
- AstNode* node;
-} BuiltinSymbol;
-
-extern const BuiltinSymbol builtin_symbols[];
-
-typedef struct IntrinsicMap {
- char* name;
- OnyxIntrinsic intrinsic;
-} IntrinsicMap;
-
-extern bh_table(OnyxIntrinsic) intrinsic_table;
-
-extern bh_arr(OverloadOption) operator_overloads[Binary_Op_Count];
-
-void initialize_builtins(bh_allocator a);
-void introduce_build_options(bh_allocator a);
-
-
-// NOTE: Useful not inlined functions
-AstTyped* ast_reduce(bh_allocator a, AstTyped* node);
-AstNode* ast_clone(bh_allocator a, void* n);
-AstFunction* clone_function_header(bh_allocator a, AstFunction* func);
-void clone_function_body(bh_allocator a, AstFunction* dest, AstFunction* source);
-
-void promote_numlit_to_larger(AstNumLit* num);
-b32 convert_numlit_to_type(AstNumLit* num, Type* type);
-
-b32 type_check_or_auto_cast(AstTyped** pnode, Type* type);
-Type* resolve_expression_type(AstTyped* node);
-i64 get_expression_integer_value(AstTyped* node);
-
-b32 cast_is_legal(Type* from_, Type* to_, char** err_msg);
-char* get_function_name(AstFunction* func);
-
-AstNode* strip_aliases(AstNode* node);
-
-AstNumLit* make_bool_literal(bh_allocator, b32 b);
-AstNumLit* make_int_literal(bh_allocator a, i64 value);
-AstNumLit* make_float_literal(bh_allocator a, f64 value);
-AstRangeLiteral* make_range_literal(bh_allocator a, AstTyped* low, AstTyped* high);
-AstBinaryOp* make_binary_op(bh_allocator a, BinaryOp operation, AstTyped* left, AstTyped* right);
-AstArgument* make_argument(bh_allocator a, AstTyped* value);
-AstFieldAccess* make_field_access(bh_allocator a, AstTyped* node, char* field);
-AstAddressOf* make_address_of(bh_allocator a, AstTyped* node);
-AstLocal* make_local(bh_allocator a, OnyxToken* token, AstType* type_node);
-AstNode* make_symbol(bh_allocator a, OnyxToken* sym);
-
-void arguments_initialize(Arguments* args);
-b32 fill_in_arguments(Arguments* args, AstNode* provider, char** err_msg);
-void arguments_ensure_length(Arguments* args, u32 count);
-void arguments_copy(Arguments* dest, Arguments* src);
-void arguments_clone(Arguments* dest, Arguments* src);
-void arguments_deep_clone(bh_allocator a, Arguments* dest, Arguments* src);
-void arguments_remove_baked(Arguments* args);
-
-// GROSS: Using void* to avoid having to cast everything.
-const char* node_get_type_name(void* node);
-
-b32 static_if_resolution(AstIf* static_if);
-
-typedef enum PolyProcLookupMethod {
- PPLM_By_Arguments,
- PPLM_By_Function_Type,
-} PolyProcLookupMethod;
-AstFunction* polymorphic_proc_lookup(AstPolyProc* pp, PolyProcLookupMethod pp_lookup, ptr actual, OnyxToken* tkn);
-AstFunction* polymorphic_proc_solidify(AstPolyProc* pp, bh_arr(AstPolySolution) slns, OnyxToken* tkn);
-AstNode* polymorphic_proc_try_solidify(AstPolyProc* pp, bh_arr(AstPolySolution) slns, OnyxToken* tkn);
-AstFunction* polymorphic_proc_build_only_header(AstPolyProc* pp, PolyProcLookupMethod pp_lookup, ptr actual);
-
-void add_overload_option(bh_arr(OverloadOption)* poverloads, u64 precedence, AstTyped* overload);
-AstTyped* find_matching_overload_by_arguments(bh_arr(OverloadOption) overloads, Arguments* args, b32* should_yield);
-AstTyped* find_matching_overload_by_type(bh_arr(OverloadOption) overloads, Type* type);
-void report_unable_to_match_overload(AstCall* call);
-
-void expand_macro(AstCall** pcall, AstFunction* template);
-AstFunction* macro_resolve_header(AstMacro* macro, Arguments* args, OnyxToken* callsite);
-
-AstStructType* polymorphic_struct_lookup(AstPolyStructType* ps_type, bh_arr(AstPolySolution) slns, OnyxFilePos pos);
-
-// NOTE: Useful inlined functions
-static inline b32 is_lval(AstNode* node) {
- if ((node->kind == Ast_Kind_Local)
- || (node->kind == Ast_Kind_Param)
- || (node->kind == Ast_Kind_Global)
- || (node->kind == Ast_Kind_Dereference)
- || (node->kind == Ast_Kind_Subscript)
- || (node->kind == Ast_Kind_Field_Access)
- || (node->kind == Ast_Kind_Memres))
- return 1;
-
- if (node->kind == Ast_Kind_Compound) {
- b32 all_lval = 1;
- bh_arr_each(AstTyped *, expr, ((AstCompound *) node)->exprs) all_lval = all_lval && is_lval((AstNode *) *expr);
- return all_lval;
- }
-
- return 0;
-}
-
-static inline b32 binop_is_assignment(BinaryOp binop) {
- return (binop >= Binary_Op_Assign_Start && binop <= Binary_Op_Assign_End);
-}
-
-static inline b32 binop_is_compare(BinaryOp binop) {
- return (binop >= Binary_Op_Equal && binop <= Binary_Op_Greater_Equal);
-}
-
-static inline b32 node_is_type(AstNode* node) {
- node = strip_aliases(node);
-
- return (node->kind > Ast_Kind_Type_Start) && (node->kind < Ast_Kind_Type_End);
-}
-
-static inline b32 node_is_auto_cast(AstNode* node) {
- return (node->kind == Ast_Kind_Unary_Op) && (((AstUnaryOp *) node)->operation == Unary_Op_Auto_Cast);
-}
-
-static inline CallingConvention type_function_get_cc(Type* type) {
- if (type == NULL) return CC_Undefined;
- if (type->kind != Type_Kind_Function) return CC_Undefined;
- if (type_is_compound(type->Function.return_type)) return CC_Return_Stack;
- return CC_Return_Wasm;
-}
-
-static inline ParamPassType type_get_param_pass(Type* type) {
- if (type_is_structlike_strict(type) && !type_structlike_is_simple(type)) return Param_Pass_By_Implicit_Pointer;
- return Param_Pass_By_Value;
-}
-
-static inline AstFunction* get_function_from_node(AstNode* node) {
- if (node->kind == Ast_Kind_Function) return (AstFunction *) node;
- if (node->kind == Ast_Kind_Polymorphic_Proc) return ((AstPolyProc *) node)->base_func;
- if (node->kind == Ast_Kind_Macro) return get_function_from_node((AstNode*) ((AstMacro *) node)->body);
- return NULL;
-}
-
-#endif // #ifndef ONYXASTNODES_H
+++ /dev/null
-#ifndef ONYXDOC_H
-#define ONYXDOC_H
-
-#include "bh.h"
-#include "onyxastnodes.h"
-
-typedef enum DocFormat {
- Doc_Format_Human,
- Doc_Format_Tags,
- Doc_Format_Html,
-} DocFormat;
-
-typedef struct DocEntry {
- OnyxFilePos pos;
- char* sym; // Unused by doc generator
- char* def;
- char* additional;
-} DocEntry;
-
-typedef struct DocPackage {
- const char* name;
-
- bh_arr(DocEntry) public_entries;
- bh_arr(DocEntry) private_entries;
-} DocPackage;
-
-typedef struct OnyxDocumentation {
- bh_arena doc_arena;
-
- DocFormat format;
-
- bh_arr(DocPackage) package_docs;
-} OnyxDocumentation;
-
-OnyxDocumentation onyx_docs_generate();
-void onyx_docs_emit(OnyxDocumentation* doc, const char* filename);
-
-#endif
+++ /dev/null
-#ifndef ONYXERRORS_H
-#define ONYXERRORS_H
-
-#include "bh.h"
-#include "onyxlex.h"
-
-#include <stdarg.h>
-
-typedef struct OnyxError {
- OnyxFilePos pos;
- char *text;
-} OnyxError;
-
-typedef struct OnyxErrors {
- bh_arena msg_arena;
- bh_allocator msg_alloc;
-
- // NOTE: Pointer to a array of all the loaded files.
- bh_arr(bh_file_contents)* file_contents;
-
- bh_arr(OnyxError) errors;
-} OnyxErrors;
-
-extern OnyxErrors msgs;
-
-void onyx_errors_init(bh_arr(bh_file_contents)* files);
-void onyx_report_error(OnyxFilePos pos, char * format, ...);
-void onyx_report_warning(OnyxFilePos pos, char* format, ...);
-void onyx_errors_print();
-b32 onyx_has_errors();
-void onyx_clear_errors();
-
-#endif
+++ /dev/null
-#ifndef ONYXLEX_H
-#define ONYXLEX_H
-
-#include "bh.h"
-
-// NOTE: Used for global statistics
-extern u64 lexer_lines_processed;
-extern u64 lexer_tokens_processed;
-
-typedef enum TokenType {
- Token_Type_Ascii_End = 256,
- Token_Type_Unknown = 256,
- Token_Type_End_Stream = 257,
-
- Token_Type_Comment = 258,
-
- Token_Type_Keyword_Package,
- Token_Type_Keyword_Struct,
- Token_Type_Keyword_Enum,
- Token_Type_Keyword_Use,
- Token_Type_Keyword_If,
- Token_Type_Keyword_Else,
- Token_Type_Keyword_Elseif,
- Token_Type_Keyword_Return,
- Token_Type_Keyword_Global,
- Token_Type_Keyword_Proc,
- Token_Type_Keyword_As,
- Token_Type_Keyword_Cast,
- Token_Type_Keyword_While,
- Token_Type_Keyword_For,
- Token_Type_Keyword_Break,
- Token_Type_Keyword_Continue,
- Token_Type_Keyword_Sizeof,
- Token_Type_Keyword_Alignof,
- Token_Type_Keyword_Typeof,
- Token_Type_Keyword_Defer,
- Token_Type_Keyword_Do,
- Token_Type_Keyword_Case,
- Token_Type_Keyword_Switch,
- Token_Type_Keyword_Fallthrough,
- Token_Type_Keyword_Macro,
-
- Token_Type_Right_Arrow,
- Token_Type_Left_Arrow,
- Token_Type_Empty_Block,
- Token_Type_Pipe,
-
- Token_Type_Greater_Equal,
- Token_Type_Less_Equal,
- Token_Type_Equal_Equal,
- Token_Type_Not_Equal,
- Token_Type_Plus_Equal,
- Token_Type_Minus_Equal,
- Token_Type_Star_Equal,
- Token_Type_Fslash_Equal,
- Token_Type_Percent_Equal,
- Token_Type_And_Equal,
- Token_Type_Or_Equal,
- Token_Type_Xor_Equal,
- Token_Type_And_And,
- Token_Type_Or_Or,
- Token_Type_Shift_Left,
- Token_Type_Shift_Right,
- Token_Type_Shift_Arith_Right,
- Token_Type_Shl_Equal,
- Token_Type_Shr_Equal,
- Token_Type_Sar_Equal,
-
- Token_Type_Dot_Dot,
- Token_Type_Tilde_Tilde,
-
- Token_Type_Symbol,
- Token_Type_Literal_String,
- Token_Type_Literal_Integer,
- Token_Type_Literal_Float,
- Token_Type_Literal_True,
- Token_Type_Literal_False,
-
- Token_Type_Note,
-
- Token_Type_Count,
-} TokenType;
-
-typedef struct OnyxFilePos {
- const char* filename;
- char* line_start;
- u32 line;
-
- // NOTE: This assumes that no line is no longer than 2^16 chars
- u16 column, length;
-} OnyxFilePos;
-
-typedef struct OnyxToken {
- TokenType type;
- i32 length;
- char* text;
- OnyxFilePos pos;
-} OnyxToken;
-
-typedef struct OnyxTokenizer {
- char *start, *curr, *end;
-
- const char* filename;
-
- char* line_start;
- u64 line_number;
-
- bh_arr(OnyxToken) tokens;
-} OnyxTokenizer;
-
-const char* token_name(TokenType tkn_type);
-void token_toggle_end(OnyxToken* tkn);
-OnyxToken* onyx_get_token(OnyxTokenizer* tokenizer);
-OnyxTokenizer onyx_tokenizer_create(bh_allocator allocator, bh_file_contents *fc);
-void onyx_tokenizer_free(OnyxTokenizer* tokenizer);
-void onyx_lex_tokens(OnyxTokenizer* tokenizer);
-
-b32 token_equals(OnyxToken* tkn1, OnyxToken* tkn2);
-b32 token_text_equals(OnyxToken* tkn, char* text);
-
-#endif
+++ /dev/null
-#ifndef ONYXPARSER_H
-#define ONYXPARSER_H
-
-#include "bh.h"
-
-#include "onyxlex.h"
-#include "onyxerrors.h"
-#include "onyxastnodes.h"
-
-typedef struct PolymorphicContext {
- AstType* root_node;
- bh_arr(AstPolyParam)* poly_params;
-} PolymorphicContext;
-
-typedef struct OnyxParser {
- bh_allocator allocator;
-
- Package *package;
- Scope *file_scope;
-
- // NOTE: not used since all tokens are lexed before parsing starts
- OnyxTokenizer *tokenizer;
- OnyxToken *prev;
- OnyxToken *curr;
-
- PolymorphicContext polymorph_context;
-
- Scope *current_scope;
- bh_arr(bh_arr(Entity *) *) alternate_entity_placement_stack;
-
- bh_arr(AstFlags) scope_flags;
-
- b32 hit_unexpected_token : 1;
-} OnyxParser;
-
-const char* onyx_ast_node_kind_string(AstKind kind);
-void* onyx_ast_node_new(bh_allocator alloc, i32 size, AstKind kind);
-OnyxParser onyx_parser_create(bh_allocator alloc, OnyxTokenizer *tokenizer);
-void onyx_parser_free(OnyxParser* parser);
-void onyx_parse(OnyxParser *parser);
-
-#endif // #ifndef ONYXPARSER_H
+++ /dev/null
-#ifndef ONYXTYPES_H
-#define ONYXTYPES_H
-
-#include "bh.h"
-
-enum BasicKind {
- Basic_Kind_Void,
-
- Basic_Kind_Bool,
-
- Basic_Kind_Int_Unsized,
- Basic_Kind_I8,
- Basic_Kind_U8,
- Basic_Kind_I16,
- Basic_Kind_U16,
- Basic_Kind_I32,
- Basic_Kind_U32,
- Basic_Kind_I64,
- Basic_Kind_U64,
-
- Basic_Kind_Float_Unsized,
- Basic_Kind_F32,
- Basic_Kind_F64,
-
- Basic_Kind_Rawptr,
-
- Basic_Kind_I8X16,
- Basic_Kind_I16X8,
- Basic_Kind_I32X4,
- Basic_Kind_I64X2,
- Basic_Kind_F32X4,
- Basic_Kind_F64X2,
- Basic_Kind_V128,
-
- Basic_Kind_Type_Index,
-
- Basic_Kind_Count,
-};
-
-enum BasicFlag {
- Basic_Flag_Boolean = BH_BIT(0),
- Basic_Flag_Integer = BH_BIT(1),
- Basic_Flag_Unsigned = BH_BIT(2),
- Basic_Flag_Float = BH_BIT(3),
- Basic_Flag_Pointer = BH_BIT(4),
-
- Basic_Flag_SIMD = BH_BIT(5),
-
- Basic_Flag_Type_Index = BH_BIT(6),
-
- Basic_Flag_Numeric = Basic_Flag_Integer | Basic_Flag_Float,
- Basic_Flag_Ordered = Basic_Flag_Integer | Basic_Flag_Float | Basic_Flag_Pointer,
- Basic_Flag_Equality = Basic_Flag_Ordered | Basic_Flag_Type_Index | Basic_Flag_Boolean,
- Basic_Flag_Constant_Type = Basic_Flag_Boolean | Basic_Flag_Numeric | Basic_Flag_Pointer,
- Basic_Flag_Numeric_Ordered = Basic_Flag_Numeric | Basic_Flag_Ordered,
-};
-
-typedef struct TypeBasic {
- enum BasicKind kind;
- u32 flags;
- u32 size, alignment; // NOTE: In bytes
- const char* name;
-} TypeBasic;
-
-// NOTE: Forward declaration for some of the types below
-typedef struct Type Type;
-
-typedef struct StructMember {
- u32 offset, idx;
- Type *type;
-
- // NOTE: Since the name is stored here, it may not be necessary to,
- // store a hash table of struct members. There realistically will not
- // be many struct members, and iterating through an array would be
- // easier and less costly. - brendanfh 2020/09/17
- char *name;
-
- struct AstTyped** initial_value;
- b32 included_through_use : 1;
- b32 used : 1;
-} StructMember;
-
-typedef struct TypeWithOffset TypeWithOffset;
-struct TypeWithOffset {
- Type* type;
- u32 offset;
-};
-
-#define TYPE_KINDS \
- TYPE_KIND(Basic, TypeBasic) \
- TYPE_KIND(Pointer, struct { TypeBasic base; Type *elem; }) \
- TYPE_KIND(Function, struct { \
- Type *return_type; \
- u16 param_count; \
- u16 needed_param_count; \
- i16 vararg_arg_pos; \
- Type* params[]; \
- }) \
- TYPE_KIND(Struct, struct { \
- char* name; \
- u32 size; \
- u16 alignment, mem_count; \
- bh_table(StructMember) members; \
- bh_arr(StructMember *) memarr; \
- bh_arr(struct AstPolySolution) poly_sln; \
- bh_arr(TypeWithOffset) linear_members; \
- struct AstType *constructed_from; \
- }) \
- TYPE_KIND(Compound, struct { \
- u32 count; \
- u32 size; \
- bh_arr(TypeWithOffset) linear_members; \
- Type* types[]; \
- }) \
- TYPE_KIND(Array, struct { u32 size; u32 count; Type *elem; }) \
- TYPE_KIND(Slice, struct { Type *ptr_to_data; }) \
- TYPE_KIND(DynArray, struct { Type *ptr_to_data; }) \
- TYPE_KIND(VarArgs, struct { Type *ptr_to_data; }) \
- TYPE_KIND(Enum, struct { \
- char* name; \
- Type* backing; \
- b32 is_flags; \
- })
-
-
-typedef enum TypeKind {
- Type_Kind_Invalid,
-
-#define TYPE_KIND(k, ...) Type_Kind_##k,
- TYPE_KINDS
-#undef TYPE_KIND
-
- Type_Kind_Count,
-} TypeKind;
-
-#define TYPE_KIND(k, ...) typedef __VA_ARGS__ Type ## k;
- TYPE_KINDS
-#undef TYPE_KIND
-
-enum TypeFlag {
- Type_Flag_Default
-};
-
-struct Type {
- TypeKind kind;
-
- u32 id;
- u32 flags;
-
- // NOTE(Brendan Hansen): The abstract syntax tree node used to create
- // the type. Primarily used to look up symbols in scopes that are embedded
- // in the type.
- struct AstType* ast_type;
-
- union {
-#define TYPE_KIND(k, ...) Type##k k;
- TYPE_KINDS
-#undef TYPE_KIND
- };
-};
-
-extern bh_imap type_map;
-
-extern Type basic_types[];
-
-struct AstType;
-struct AstFunction;
-struct AstCompound;
-
-void types_init();
-void types_dump_type_info();
-
-b32 types_are_compatible(Type* t1, Type* t2);
-u32 type_size_of(Type* type);
-u32 type_alignment_of(Type* type);
-Type* type_build_from_ast(bh_allocator alloc, struct AstType* type_node);
-
-Type* type_build_function_type(bh_allocator alloc, struct AstFunction* func);
-Type* type_build_compound_type(bh_allocator alloc, struct AstCompound* compound);
-
-Type* type_make_pointer(bh_allocator alloc, Type* to);
-Type* type_make_array(bh_allocator alloc, Type* to, u32 count);
-Type* type_make_slice(bh_allocator alloc, Type* of);
-Type* type_make_dynarray(bh_allocator alloc, Type* of);
-Type* type_make_varargs(bh_allocator alloc, Type* of);
-
-void build_linear_types_with_offset(Type* type, bh_arr(TypeWithOffset)* pdest, u32 offset);
-
-const char* type_get_unique_name(Type* type);
-const char* type_get_name(Type* type);
-u32 type_get_alignment_log2(Type* type);
-
-b32 type_lookup_member(Type* type, char* member, StructMember* smem);
-b32 type_lookup_member_by_idx(Type* type, i32 idx, StructMember* smem);
-
-i32 type_linear_member_count(Type* type);
-b32 type_linear_member_lookup(Type* type, i32 idx, TypeWithOffset* two);
-i32 type_get_idx_of_linear_member_with_offset(Type* type, u32 offset);
-
-b32 type_struct_is_simple(Type* type);
-
-b32 type_is_pointer(Type* type);
-b32 type_is_rawptr(Type* type);
-b32 type_is_array(Type* tyoe);
-b32 type_is_struct(Type* type);
-b32 type_is_bool(Type* type);
-b32 type_is_small_integer(Type* type);
-b32 type_is_integer(Type* type);
-b32 type_is_numeric(Type* type);
-b32 type_is_compound(Type* type);
-b32 type_is_simd(Type* type);
-b32 type_results_in_void(Type* type);
-b32 type_is_array_accessible(Type* type);
-b32 type_is_structlike(Type* type);
-b32 type_is_structlike_strict(Type* type);
-u32 type_structlike_mem_count(Type* type);
-u32 type_structlike_is_simple(Type* type);
-b32 type_is_sl_constructable(Type* type);
-b32 type_struct_constructed_from_poly_struct(Type* struct_type, struct AstType* from);
-
-#endif // #ifndef ONYX_TYPES
+++ /dev/null
-#include "bh.h"
-
-#include "onyxastnodes.h"
-
-extern bh_scratch global_scratch;
-extern bh_allocator global_scratch_allocator;
-
-extern bh_managed_heap global_heap;
-extern bh_allocator global_heap_allocator;
-
-const char* onyx_ast_node_kind_string(AstKind kind);
-
-Package* package_lookup(char* package_name);
-Package* package_lookup_or_create(char* package_name, Scope* parent_scope, bh_allocator alloc);
-void package_track_use_package(Package* package, Entity* entity);
-void package_reinsert_use_packages(Package* package);
-
-Scope* scope_create(bh_allocator a, Scope* parent, OnyxFilePos created_at);
-void scope_include(Scope* target, Scope* source, OnyxFilePos pos);
-b32 symbol_introduce(Scope* scope, OnyxToken* tkn, AstNode* symbol);
-b32 symbol_raw_introduce(Scope* scope, char* tkn, OnyxFilePos pos, AstNode* symbol);
-void symbol_builtin_introduce(Scope* scope, char* sym, AstNode *node);
-void symbol_subpackage_introduce(Scope* scope, char* sym, AstPackage *node);
-AstNode* symbol_raw_resolve(Scope* start_scope, char* sym);
-AstNode* symbol_resolve(Scope* start_scope, OnyxToken* tkn);
-AstNode* try_symbol_raw_resolve_from_node(AstNode* node, char* symbol);
-AstNode* try_symbol_resolve_from_node(AstNode* node, OnyxToken* token);
-
-void build_all_overload_options(bh_arr(OverloadOption) overloads, bh_imap* all_overloads);
-
-u32 char_to_base16_value(char x);
-
-// Returns the length after processing the string.
-i32 string_process_escape_seqs(char* dest, char* src, i32 len);
-
-// NOTE: This should not be called until immediately before using the return value.
-// This function can return a static variable which will change if this is called
-// another time. -brendanfh 2020/10/09
-// :RelativeFiles This should lookup for the file relative to "relative_to"
-char* lookup_included_file(char* filename, char* relative_to, b32 add_onyx_suffix, b32 search_included_folders);
-
-extern AstTyped node_that_signals_a_yield;
+++ /dev/null
-#ifndef ONYXWASM_H
-#define ONYXWASM_H
-
-#include "bh.h"
-
-#include "onyxastnodes.h"
-#include "onyxerrors.h"
-
-typedef u8 WasmType;
-
-typedef struct WasmFuncType {
- // NOTE: For now, WASM only allows for 1 return value.
- // This may be lifted in the future.
- i32 param_count;
- WasmType return_type;
- WasmType param_types[];
-} WasmFuncType;
-
-#define SIMD_INSTR_MASK 0x10000
-#define EXT_INSTR_MASK 0x20000
-
-typedef enum WasmInstructionType {
- WI_UNREACHABLE = 0x00,
- WI_NOP = 0x01,
-
- // NOTE: Control flow
- WI_BLOCK_START = 0x02,
- WI_BLOCK_END = 0x0B, // NOTE: These ends are not unique
- WI_LOOP_START = 0x03,
- WI_LOOP_END = 0x0B,
- WI_IF_START = 0x04,
- WI_ELSE = 0x05,
- WI_IF_END = 0x0B,
- WI_JUMP = 0x0C,
- WI_COND_JUMP = 0x0D,
- WI_JUMP_TABLE = 0x0E,
- WI_RETURN = 0x0F,
- WI_CALL = 0x10,
- WI_CALL_INDIRECT = 0x11,
-
- // NOTE: Parametric instructions
- WI_DROP = 0x1A,
- WI_SELECT = 0x1B,
-
- // NOTE: Variable instructions
- WI_LOCAL_GET = 0x20,
- WI_LOCAL_SET = 0x21,
- WI_LOCAL_TEE = 0x22,
- WI_GLOBAL_GET = 0x23,
- WI_GLOBAL_SET = 0x24,
-
- // NOTE: Memory instructions
- WI_I32_LOAD = 0x28,
- WI_I64_LOAD = 0x29,
- WI_F32_LOAD = 0x2A,
- WI_F64_LOAD = 0x2B,
- WI_I32_LOAD_8_S = 0x2C,
- WI_I32_LOAD_8_U = 0x2D,
- WI_I32_LOAD_16_S = 0x2E,
- WI_I32_LOAD_16_U = 0x2F,
- WI_I64_LOAD_8_S = 0x30,
- WI_I64_LOAD_8_U = 0x31,
- WI_I64_LOAD_16_S = 0x32,
- WI_I64_LOAD_16_U = 0x33,
- WI_I64_LOAD_32_S = 0x34,
- WI_I64_LOAD_32_U = 0x35,
- WI_I32_STORE = 0x36,
- WI_I64_STORE = 0x37,
- WI_F32_STORE = 0x38,
- WI_F64_STORE = 0x39,
- WI_I32_STORE_8 = 0x3A,
- WI_I32_STORE_16 = 0x3B,
- WI_I64_STORE_8 = 0x3C,
- WI_I64_STORE_16 = 0x3D,
- WI_I64_STORE_32 = 0x3E,
- WI_MEMORY_SIZE = 0x3F,
- WI_MEMORY_GROW = 0x40,
-
- // NOTE: Numeric Instructions
- WI_I32_CONST = 0x41,
- WI_I64_CONST = 0x42,
- WI_F32_CONST = 0x43,
- WI_F64_CONST = 0x44,
-
- WI_I32_EQZ = 0x45,
- WI_I32_EQ = 0x46,
- WI_I32_NE = 0x47,
- WI_I32_LT_S = 0x48,
- WI_I32_LT_U = 0x49,
- WI_I32_GT_S = 0x4a,
- WI_I32_GT_U = 0x4b,
- WI_I32_LE_S = 0x4c,
- WI_I32_LE_U = 0x4d,
- WI_I32_GE_S = 0x4e,
- WI_I32_GE_U = 0x4f,
-
- WI_I64_EQZ = 0x50,
- WI_I64_EQ = 0x51,
- WI_I64_NE = 0x52,
- WI_I64_LT_S = 0x53,
- WI_I64_LT_U = 0x54,
- WI_I64_GT_S = 0x55,
- WI_I64_GT_U = 0x56,
- WI_I64_LE_S = 0x57,
- WI_I64_LE_U = 0x58,
- WI_I64_GE_S = 0x59,
- WI_I64_GE_U = 0x5a,
-
- WI_F32_EQ = 0x5b,
- WI_F32_NE = 0x5c,
- WI_F32_LT = 0x5d,
- WI_F32_GT = 0x5e,
- WI_F32_LE = 0x5f,
- WI_F32_GE = 0x60,
-
- WI_F64_EQ = 0x61,
- WI_F64_NE = 0x62,
- WI_F64_LT = 0x63,
- WI_F64_GT = 0x64,
- WI_F64_LE = 0x65,
- WI_F64_GE = 0x66,
-
- WI_I32_CLZ = 0x67,
- WI_I32_CTZ = 0x68,
- WI_I32_POPCNT = 0x69,
- WI_I32_ADD = 0x6a,
- WI_I32_SUB = 0x6b,
- WI_I32_MUL = 0x6c,
- WI_I32_DIV_S = 0x6d,
- WI_I32_DIV_U = 0x6e,
- WI_I32_REM_S = 0x6f,
- WI_I32_REM_U = 0x70,
- WI_I32_AND = 0x71,
- WI_I32_OR = 0x72,
- WI_I32_XOR = 0x73,
- WI_I32_SHL = 0x74,
- WI_I32_SHR_S = 0x75,
- WI_I32_SHR_U = 0x76,
- WI_I32_ROTL = 0x77,
- WI_I32_ROTR = 0x78,
-
- WI_I64_CLZ = 0x79,
- WI_I64_CTZ = 0x7a,
- WI_I64_POPCNT = 0x7b,
- WI_I64_ADD = 0x7c,
- WI_I64_SUB = 0x7d,
- WI_I64_MUL = 0x7e,
- WI_I64_DIV_S = 0x7f,
- WI_I64_DIV_U = 0x80,
- WI_I64_REM_S = 0x81,
- WI_I64_REM_U = 0x82,
- WI_I64_AND = 0x83,
- WI_I64_OR = 0x84,
- WI_I64_XOR = 0x85,
- WI_I64_SHL = 0x86,
- WI_I64_SHR_S = 0x87,
- WI_I64_SHR_U = 0x88,
- WI_I64_ROTL = 0x89,
- WI_I64_ROTR = 0x8a,
-
- WI_F32_ABS = 0x8b,
- WI_F32_NEG = 0x8c,
- WI_F32_CEIL = 0x8d,
- WI_F32_FLOOR = 0x8e,
- WI_F32_TRUNC = 0x8f,
- WI_F32_NEAREST = 0x90,
- WI_F32_SQRT = 0x91,
- WI_F32_ADD = 0x92,
- WI_F32_SUB = 0x93,
- WI_F32_MUL = 0x94,
- WI_F32_DIV = 0x95,
- WI_F32_MIN = 0x96,
- WI_F32_MAX = 0x97,
- WI_F32_COPYSIGN = 0x98,
-
- WI_F64_ABS = 0x99,
- WI_F64_NEG = 0x9a,
- WI_F64_CEIL = 0x9b,
- WI_F64_FLOOR = 0x9c,
- WI_F64_TRUNC = 0x9d,
- WI_F64_NEAREST = 0x9e,
- WI_F64_SQRT = 0x9f,
- WI_F64_ADD = 0xA0,
- WI_F64_SUB = 0xA1,
- WI_F64_MUL = 0xA2,
- WI_F64_DIV = 0xA3,
- WI_F64_MIN = 0xA4,
- WI_F64_MAX = 0xA5,
- WI_F64_COPYSIGN = 0xA6,
-
- WI_I32_FROM_I64 = 0xA7,
- WI_I32_FROM_F32_S = 0xA8,
- WI_I32_FROM_F32_U = 0xA9,
- WI_I32_FROM_F64_S = 0xAA,
- WI_I32_FROM_F64_U = 0xAB,
-
- WI_I64_FROM_I32_S = 0xAC,
- WI_I64_FROM_I32_U = 0xAD,
- WI_I64_FROM_F32_S = 0xAE,
- WI_I64_FROM_F32_U = 0xAF,
- WI_I64_FROM_F64_S = 0xB0,
- WI_I64_FROM_F64_U = 0xB1,
-
- WI_F32_FROM_I32_S = 0xB2,
- WI_F32_FROM_I32_U = 0xB3,
- WI_F32_FROM_I64_S = 0xB4,
- WI_F32_FROM_I64_U = 0xB5,
- WI_F32_FROM_F64 = 0xB6,
-
- WI_F64_FROM_I32_S = 0xB7,
- WI_F64_FROM_I32_U = 0xB8,
- WI_F64_FROM_I64_S = 0xB9,
- WI_F64_FROM_I64_U = 0xBA,
- WI_F64_FROM_F32 = 0xBB,
-
- WI_I32_REINTERPRET_F32 = 0xBC,
- WI_I64_REINTERPRET_F64 = 0xBD,
- WI_F32_REINTERPRET_I32 = 0xBE,
- WI_F64_REINTERPRET_I64 = 0xBF,
-
- WI_I32_EXTEND_8_S = 0xC0,
- WI_I32_EXTEND_16_S = 0xC1,
- WI_I64_EXTEND_8_S = 0xC2,
- WI_I64_EXTEND_16_S = 0xC3,
- WI_I64_EXTEND_32_S = 0xC4,
-
- // Pointer stuff; this will make it easier to switch to 64-bit
- // pointers, whenever WebAssembly standarizes that.
- WI_PTR_CONST = WI_I32_CONST,
- WI_PTR_LOAD = WI_I32_LOAD,
- WI_PTR_STORE = WI_I32_STORE,
- WI_PTR_ADD = WI_I32_ADD,
- WI_PTR_SUB = WI_I32_SUB,
- WI_PTR_MUL = WI_I32_MUL,
- WI_PTR_GE = WI_I32_GE_U,
- WI_PTR_GT = WI_I32_GT_U,
- WI_PTR_EQ = WI_I32_EQ,
-
- WI_V128_LOAD = SIMD_INSTR_MASK | 0,
- WI_V128_STORE = SIMD_INSTR_MASK | 11,
-
- WI_V128_CONST = SIMD_INSTR_MASK | 12,
-
- WI_I8X16_SHUFFLE = SIMD_INSTR_MASK | 13,
-
- WI_I8X16_EXTRACT_LANE_S = SIMD_INSTR_MASK | 21,
- WI_I8X16_EXTRACT_LANE_U = SIMD_INSTR_MASK | 22,
- WI_I8X16_REPLACE_LANE = SIMD_INSTR_MASK | 23,
- WI_I16X8_EXTRACT_LANE_S = SIMD_INSTR_MASK | 24,
- WI_I16X8_EXTRACT_LANE_U = SIMD_INSTR_MASK | 25,
- WI_I16X8_REPLACE_LANE = SIMD_INSTR_MASK | 26,
- WI_I32X4_EXTRACT_LANE = SIMD_INSTR_MASK | 27,
- WI_I32X4_REPLACE_LANE = SIMD_INSTR_MASK | 28,
- WI_I64X2_EXTRACT_LANE = SIMD_INSTR_MASK | 29,
- WI_I64X2_REPLACE_LANE = SIMD_INSTR_MASK | 30,
- WI_F32X4_EXTRACT_LANE = SIMD_INSTR_MASK | 31,
- WI_F32X4_REPLACE_LANE = SIMD_INSTR_MASK | 32,
- WI_F64X2_EXTRACT_LANE = SIMD_INSTR_MASK | 33,
- WI_F64X2_REPLACE_LANE = SIMD_INSTR_MASK | 34,
-
- WI_I8X16_SWIZZLE = SIMD_INSTR_MASK | 14,
- WI_I8X16_SPLAT = SIMD_INSTR_MASK | 15,
- WI_I16X8_SPLAT = SIMD_INSTR_MASK | 16,
- WI_I32X4_SPLAT = SIMD_INSTR_MASK | 17,
- WI_I64X2_SPLAT = SIMD_INSTR_MASK | 18,
- WI_F32X4_SPLAT = SIMD_INSTR_MASK | 19,
- WI_F64X2_SPLAT = SIMD_INSTR_MASK | 20,
-
- WI_I8X16_EQ = SIMD_INSTR_MASK | 35,
- WI_I8X16_NEQ = SIMD_INSTR_MASK | 36,
- WI_I8X16_LT_S = SIMD_INSTR_MASK | 37,
- WI_I8X16_LT_U = SIMD_INSTR_MASK | 38,
- WI_I8X16_GT_S = SIMD_INSTR_MASK | 39,
- WI_I8X16_GT_U = SIMD_INSTR_MASK | 40,
- WI_I8X16_LE_S = SIMD_INSTR_MASK | 41,
- WI_I8X16_LE_U = SIMD_INSTR_MASK | 42,
- WI_I8X16_GE_S = SIMD_INSTR_MASK | 43,
- WI_I8X16_GE_U = SIMD_INSTR_MASK | 44,
-
- WI_I16X8_EQ = SIMD_INSTR_MASK | 45,
- WI_I16X8_NEQ = SIMD_INSTR_MASK | 46,
- WI_I16X8_LT_S = SIMD_INSTR_MASK | 47,
- WI_I16X8_LT_U = SIMD_INSTR_MASK | 48,
- WI_I16X8_GT_S = SIMD_INSTR_MASK | 49,
- WI_I16X8_GT_U = SIMD_INSTR_MASK | 50,
- WI_I16X8_LE_S = SIMD_INSTR_MASK | 51,
- WI_I16X8_LE_U = SIMD_INSTR_MASK | 52,
- WI_I16X8_GE_S = SIMD_INSTR_MASK | 53,
- WI_I16X8_GE_U = SIMD_INSTR_MASK | 54,
-
- WI_I32X4_EQ = SIMD_INSTR_MASK | 55,
- WI_I32X4_NEQ = SIMD_INSTR_MASK | 56,
- WI_I32X4_LT_S = SIMD_INSTR_MASK | 57,
- WI_I32X4_LT_U = SIMD_INSTR_MASK | 58,
- WI_I32X4_GT_S = SIMD_INSTR_MASK | 59,
- WI_I32X4_GT_U = SIMD_INSTR_MASK | 60,
- WI_I32X4_LE_S = SIMD_INSTR_MASK | 61,
- WI_I32X4_LE_U = SIMD_INSTR_MASK | 62,
- WI_I32X4_GE_S = SIMD_INSTR_MASK | 63,
- WI_I32X4_GE_U = SIMD_INSTR_MASK | 64,
-
- WI_F32X4_EQ = SIMD_INSTR_MASK | 65,
- WI_F32X4_NEQ = SIMD_INSTR_MASK | 66,
- WI_F32X4_LT = SIMD_INSTR_MASK | 67,
- WI_F32X4_GT = SIMD_INSTR_MASK | 68,
- WI_F32X4_LE = SIMD_INSTR_MASK | 69,
- WI_F32X4_GE = SIMD_INSTR_MASK | 70,
-
- WI_F64X2_EQ = SIMD_INSTR_MASK | 71,
- WI_F64X2_NEQ = SIMD_INSTR_MASK | 72,
- WI_F64X2_LT = SIMD_INSTR_MASK | 73,
- WI_F64X2_GT = SIMD_INSTR_MASK | 74,
- WI_F64X2_LE = SIMD_INSTR_MASK | 75,
- WI_F64X2_GE = SIMD_INSTR_MASK | 76,
-
- WI_V128_NOT = SIMD_INSTR_MASK | 77,
- WI_V128_AND = SIMD_INSTR_MASK | 78,
- WI_V128_ANDNOT = SIMD_INSTR_MASK | 79,
- WI_V128_OR = SIMD_INSTR_MASK | 80,
- WI_V128_XOR = SIMD_INSTR_MASK | 81,
- WI_V128_BITSELECT = SIMD_INSTR_MASK | 82,
-
- WI_I8X16_ABS = SIMD_INSTR_MASK | 96,
- WI_I8X16_NEG = SIMD_INSTR_MASK | 97,
- WI_I8X16_ANY_TRUE = SIMD_INSTR_MASK | 98,
- WI_I8X16_ALL_TRUE = SIMD_INSTR_MASK | 99,
- WI_I8X16_BITMASK = SIMD_INSTR_MASK | 100,
- WI_I8X16_NARROW_I16X8_S = SIMD_INSTR_MASK | 101,
- WI_I8X16_NARROW_I16X8_U = SIMD_INSTR_MASK | 102,
- WI_I8X16_SHL = SIMD_INSTR_MASK | 107,
- WI_I8X16_SHR_S = SIMD_INSTR_MASK | 108,
- WI_I8X16_SHR_U = SIMD_INSTR_MASK | 109,
- WI_I8X16_ADD = SIMD_INSTR_MASK | 110,
- WI_I8X16_ADD_SAT_S = SIMD_INSTR_MASK | 111,
- WI_I8X16_ADD_SAT_U = SIMD_INSTR_MASK | 112,
- WI_I8X16_SUB = SIMD_INSTR_MASK | 113,
- WI_I8X16_SUB_SAT_S = SIMD_INSTR_MASK | 114,
- WI_I8X16_SUB_SAT_U = SIMD_INSTR_MASK | 115,
- WI_I8X16_MIN_S = SIMD_INSTR_MASK | 118,
- WI_I8X16_MIN_U = SIMD_INSTR_MASK | 119,
- WI_I8X16_MAX_S = SIMD_INSTR_MASK | 120,
- WI_I8X16_MAX_U = SIMD_INSTR_MASK | 121,
- WI_I8X16_AVGR_U = SIMD_INSTR_MASK | 123,
-
- WI_I16X8_ABS = SIMD_INSTR_MASK | 128,
- WI_I16X8_NEG = SIMD_INSTR_MASK | 129,
- WI_I16X8_ANY_TRUE = SIMD_INSTR_MASK | 130,
- WI_I16X8_ALL_TRUE = SIMD_INSTR_MASK | 131,
- WI_I16X8_BITMASK = SIMD_INSTR_MASK | 132,
- WI_I16X8_NARROW_I32X4_S = SIMD_INSTR_MASK | 133,
- WI_I16X8_NARROW_I32X4_U = SIMD_INSTR_MASK | 134,
- WI_I16X8_WIDEN_LOW_I8X16_S = SIMD_INSTR_MASK | 135,
- WI_I16X8_WIDEN_HIGH_I8X16_S = SIMD_INSTR_MASK | 136,
- WI_I16X8_WIDEN_LOW_I8X16_U = SIMD_INSTR_MASK | 137,
- WI_I16X8_WIDEN_HIGH_I8X16_U = SIMD_INSTR_MASK | 138,
- WI_I16X8_SHL = SIMD_INSTR_MASK | 139,
- WI_I16X8_SHR_S = SIMD_INSTR_MASK | 140,
- WI_I16X8_SHR_U = SIMD_INSTR_MASK | 141,
- WI_I16X8_ADD = SIMD_INSTR_MASK | 142,
- WI_I16X8_ADD_SAT_S = SIMD_INSTR_MASK | 143,
- WI_I16X8_ADD_SAT_U = SIMD_INSTR_MASK | 144,
- WI_I16X8_SUB = SIMD_INSTR_MASK | 145,
- WI_I16X8_SUB_SAT_S = SIMD_INSTR_MASK | 146,
- WI_I16X8_SUB_SAT_U = SIMD_INSTR_MASK | 147,
- WI_I16X8_MUL = SIMD_INSTR_MASK | 149,
- WI_I16X8_MIN_S = SIMD_INSTR_MASK | 150,
- WI_I16X8_MIN_U = SIMD_INSTR_MASK | 151,
- WI_I16X8_MAX_S = SIMD_INSTR_MASK | 152,
- WI_I16X8_MAX_U = SIMD_INSTR_MASK | 153,
- WI_I16X8_AVGR_U = SIMD_INSTR_MASK | 155,
-
- WI_I32X4_ABS = SIMD_INSTR_MASK | 160,
- WI_I32X4_NEG = SIMD_INSTR_MASK | 161,
- WI_I32X4_ANY_TRUE = SIMD_INSTR_MASK | 162,
- WI_I32X4_ALL_TRUE = SIMD_INSTR_MASK | 163,
- WI_I32X4_BITMASK = SIMD_INSTR_MASK | 164,
- WI_I32X4_WIDEN_LOW_I16X8_S = SIMD_INSTR_MASK | 167,
- WI_I32X4_WIDEN_HIGH_I16X8_S = SIMD_INSTR_MASK | 168,
- WI_I32X4_WIDEN_LOW_I16X8_U = SIMD_INSTR_MASK | 169,
- WI_I32X4_WIDEN_HIGH_I16X8_U = SIMD_INSTR_MASK | 170,
- WI_I32X4_SHL = SIMD_INSTR_MASK | 171,
- WI_I32X4_SHR_S = SIMD_INSTR_MASK | 172,
- WI_I32X4_SHR_U = SIMD_INSTR_MASK | 173,
- WI_I32X4_ADD = SIMD_INSTR_MASK | 174,
- WI_I32X4_SUB = SIMD_INSTR_MASK | 177,
- WI_I32X4_MUL = SIMD_INSTR_MASK | 181,
- WI_I32X4_MIN_S = SIMD_INSTR_MASK | 182,
- WI_I32X4_MIN_U = SIMD_INSTR_MASK | 183,
- WI_I32X4_MAX_S = SIMD_INSTR_MASK | 184,
- WI_I32X4_MAX_U = SIMD_INSTR_MASK | 185,
-
- WI_I64X2_NEG = SIMD_INSTR_MASK | 193,
- WI_I64X2_SHL = SIMD_INSTR_MASK | 203,
- WI_I64X2_SHR_S = SIMD_INSTR_MASK | 204,
- WI_I64X2_SHR_U = SIMD_INSTR_MASK | 205,
- WI_I64X2_ADD = SIMD_INSTR_MASK | 206,
- WI_I64X2_SUB = SIMD_INSTR_MASK | 209,
- WI_I64X2_MUL = SIMD_INSTR_MASK | 213,
-
- WI_F32X4_ABS = SIMD_INSTR_MASK | 224,
- WI_F32X4_NEG = SIMD_INSTR_MASK | 225,
- WI_F32X4_SQRT = SIMD_INSTR_MASK | 227,
- WI_F32X4_ADD = SIMD_INSTR_MASK | 228,
- WI_F32X4_SUB = SIMD_INSTR_MASK | 229,
- WI_F32X4_MUL = SIMD_INSTR_MASK | 230,
- WI_F32X4_DIV = SIMD_INSTR_MASK | 231,
- WI_F32X4_MIN = SIMD_INSTR_MASK | 232,
- WI_F32X4_MAX = SIMD_INSTR_MASK | 233,
-
- WI_F64X2_ABS = SIMD_INSTR_MASK | 236,
- WI_F64X2_NEG = SIMD_INSTR_MASK | 237,
- WI_F64X2_SQRT = SIMD_INSTR_MASK | 239,
- WI_F64X2_ADD = SIMD_INSTR_MASK | 240,
- WI_F64X2_SUB = SIMD_INSTR_MASK | 241,
- WI_F64X2_MUL = SIMD_INSTR_MASK | 242,
- WI_F64X2_DIV = SIMD_INSTR_MASK | 243,
- WI_F64X2_MIN = SIMD_INSTR_MASK | 244,
- WI_F64X2_MAX = SIMD_INSTR_MASK | 245,
-
- WI_I32X4_TRUNC_SAT_F32X4_S = SIMD_INSTR_MASK | 248,
- WI_I32X4_TRUNC_SAT_F32X4_U = SIMD_INSTR_MASK | 249,
- WI_F32X4_CONVERT_I32X4_S = SIMD_INSTR_MASK | 250,
- WI_F32X4_CONVERT_I32X4_U = SIMD_INSTR_MASK | 251,
-
-
- WI_MEMORY_COPY = EXT_INSTR_MASK | 0x0a,
- WI_MEMORY_FILL = EXT_INSTR_MASK | 0x0b,
-} WasmInstructionType;
-
-typedef union {
- struct {
- i32 i1, i2;
- };
- i64 l;
- float f;
- double d;
- ptr p;
-} WasmInstructionData;
-
-typedef struct WasmInstruction {
- WasmInstructionType type;
- WasmInstructionData data;
-} WasmInstruction;
-
-typedef struct BranchTable {
- u32 count;
- u32 default_case;
- u32 cases[];
-} BranchTable;
-
-#define LOCAL_IS_WASM 0x8000000000000
-typedef struct LocalAllocator {
- u32 param_count;
-
- u32 allocated[5];
- u32 freed[5];
-
- i32 max_stack;
- i32 curr_stack;
-} LocalAllocator;
-
-typedef struct WasmFunc {
- i32 type_idx;
- LocalAllocator locals;
- bh_arr(WasmInstruction) code;
-} WasmFunc;
-
-typedef struct WasmGlobal {
- WasmType type;
- u32 mutable : 1;
- bh_arr(WasmInstruction) initial_value;
-} WasmGlobal;
-
-typedef enum WasmForeignKind {
- WASM_FOREIGN_FUNCTION = 0x00,
- WASM_FOREIGN_TABLE = 0x01,
- WASM_FOREIGN_MEMORY = 0x02,
- WASM_FOREIGN_GLOBAL = 0x03,
-} WasmForeignKind;
-
-typedef struct WasmExport {
- WasmForeignKind kind;
- i32 idx;
-} WasmExport;
-
-typedef struct WasmImport {
- WasmForeignKind kind;
- i32 idx;
- OnyxToken *mod, *name;
-} WasmImport;
-
-typedef struct WasmDatum {
- u32 offset, length;
- ptr data;
-} WasmDatum;
-
-typedef enum DeferredStmtType {
- Deferred_Stmt_Node,
- Deferred_Stmt_Code,
-} DeferredStmtType;
-
-typedef struct DeferredStmt {
- DeferredStmtType type;
- u32 depth;
-
- union {
- AstNode *stmt;
- struct {
- WasmInstruction* instructions;
- u32 instruction_count;
- };
- };
-} DeferredStmt;
-
-typedef struct AllocatedSpace {
- u64 depth;
- AstTyped *expr;
-} AllocatedSpace;
-
-typedef struct StrLitInfo {
- u32 addr;
- u32 len;
-} StrLitInfo;
-
-typedef struct PatchInfo {
- u32 instruction_index;
-} PatchInfo;
-
-typedef struct OnyxWasmModule {
- bh_allocator allocator;
-
- bh_arena *extended_instr_data;
- bh_allocator extended_instr_alloc;
-
- // NOTE: Mapping ptrs to function / global indicies
- bh_imap index_map;
-
- // NOTE: Mapping from local ast node ptrs to indicies or offsets, depending on the mode
- bh_imap local_map;
-
- LocalAllocator* local_alloc;
-
- // NOTE: Mapping ptrs to elements
- bh_imap elem_map;
-
- bh_arr(DeferredStmt) deferred_stmts;
- bh_arr(AllocatedSpace) local_allocations;
-
- bh_arr(PatchInfo) stack_leave_patches;
-
- // NOTE: Used internally as a map from strings that represent function types,
- // 0x7f 0x7f : 0x7f ( (i32, i32) -> i32 )
- // to the function type index if it has been created.
- bh_table(i32) type_map;
-
- bh_table(StrLitInfo) loaded_file_info;
- bh_table(StrLitInfo) string_literals;
-
- bh_arr(u8) structured_jump_target;
- bh_arr(AstLocal*) return_location_stack; // NOTE: Used for do-block return expressions.
-
- bh_arr(WasmFuncType*) types; // NOTE: This have to be pointers because the type is variadic in size
- bh_arr(WasmImport) imports;
- bh_table(WasmExport) exports;
- bh_arr(WasmGlobal) globals;
- bh_arr(WasmFunc) funcs;
- bh_arr(WasmDatum) data;
- bh_arr(i32) elems;
-
- // NOTE: Set of things used when compiling; not part of the actual module
- u32 export_count;
- u32 next_type_idx;
- u32 next_func_idx;
- u32 next_global_idx;
- u32 next_datum_offset;
- u32 next_elem_idx;
- u32 foreign_function_count;
- u32 foreign_global_count;
-
- i32 *stack_top_ptr;
- u64 stack_base_idx;
- CallingConvention curr_cc;
- i32 null_proc_func_idx;
-
- b32 has_stack_locals : 1;
-} OnyxWasmModule;
-
-OnyxWasmModule onyx_wasm_module_create(bh_allocator alloc);
-void onyx_wasm_module_free(OnyxWasmModule* module);
-void onyx_wasm_module_write_to_file(OnyxWasmModule* module, bh_file file);
-
-#endif
--- /dev/null
+#ifndef ONYXPARSER_H
+#define ONYXPARSER_H
+
+#include "bh.h"
+
+#include "lex.h"
+#include "errors.h"
+#include "astnodes.h"
+
+typedef struct PolymorphicContext {
+ AstType* root_node;
+ bh_arr(AstPolyParam)* poly_params;
+} PolymorphicContext;
+
+typedef struct OnyxParser {
+ bh_allocator allocator;
+
+ Package *package;
+ Scope *file_scope;
+
+ // NOTE: not used since all tokens are lexed before parsing starts
+ OnyxTokenizer *tokenizer;
+ OnyxToken *prev;
+ OnyxToken *curr;
+
+ PolymorphicContext polymorph_context;
+
+ Scope *current_scope;
+ bh_arr(bh_arr(Entity *) *) alternate_entity_placement_stack;
+
+ bh_arr(AstFlags) scope_flags;
+
+ b32 hit_unexpected_token : 1;
+} OnyxParser;
+
+const char* onyx_ast_node_kind_string(AstKind kind);
+void* onyx_ast_node_new(bh_allocator alloc, i32 size, AstKind kind);
+OnyxParser onyx_parser_create(bh_allocator alloc, OnyxTokenizer *tokenizer);
+void onyx_parser_free(OnyxParser* parser);
+void onyx_parse(OnyxParser *parser);
+
+#endif // #ifndef ONYXPARSER_H
--- /dev/null
+#ifndef ONYXTYPES_H
+#define ONYXTYPES_H
+
+#include "bh.h"
+
+enum BasicKind {
+ Basic_Kind_Void,
+
+ Basic_Kind_Bool,
+
+ Basic_Kind_Int_Unsized,
+ Basic_Kind_I8,
+ Basic_Kind_U8,
+ Basic_Kind_I16,
+ Basic_Kind_U16,
+ Basic_Kind_I32,
+ Basic_Kind_U32,
+ Basic_Kind_I64,
+ Basic_Kind_U64,
+
+ Basic_Kind_Float_Unsized,
+ Basic_Kind_F32,
+ Basic_Kind_F64,
+
+ Basic_Kind_Rawptr,
+
+ Basic_Kind_I8X16,
+ Basic_Kind_I16X8,
+ Basic_Kind_I32X4,
+ Basic_Kind_I64X2,
+ Basic_Kind_F32X4,
+ Basic_Kind_F64X2,
+ Basic_Kind_V128,
+
+ Basic_Kind_Type_Index,
+
+ Basic_Kind_Count,
+};
+
+enum BasicFlag {
+ Basic_Flag_Boolean = BH_BIT(0),
+ Basic_Flag_Integer = BH_BIT(1),
+ Basic_Flag_Unsigned = BH_BIT(2),
+ Basic_Flag_Float = BH_BIT(3),
+ Basic_Flag_Pointer = BH_BIT(4),
+
+ Basic_Flag_SIMD = BH_BIT(5),
+
+ Basic_Flag_Type_Index = BH_BIT(6),
+
+ Basic_Flag_Numeric = Basic_Flag_Integer | Basic_Flag_Float,
+ Basic_Flag_Ordered = Basic_Flag_Integer | Basic_Flag_Float | Basic_Flag_Pointer,
+ Basic_Flag_Equality = Basic_Flag_Ordered | Basic_Flag_Type_Index | Basic_Flag_Boolean,
+ Basic_Flag_Constant_Type = Basic_Flag_Boolean | Basic_Flag_Numeric | Basic_Flag_Pointer,
+ Basic_Flag_Numeric_Ordered = Basic_Flag_Numeric | Basic_Flag_Ordered,
+};
+
+typedef struct TypeBasic {
+ enum BasicKind kind;
+ u32 flags;
+ u32 size, alignment; // NOTE: In bytes
+ const char* name;
+} TypeBasic;
+
+// NOTE: Forward declaration for some of the types below
+typedef struct Type Type;
+
+typedef struct StructMember {
+ u32 offset, idx;
+ Type *type;
+
+ // NOTE: Since the name is stored here, it may not be necessary to,
+ // store a hash table of struct members. There realistically will not
+ // be many struct members, and iterating through an array would be
+ // easier and less costly. - brendanfh 2020/09/17
+ char *name;
+
+ struct AstTyped** initial_value;
+ b32 included_through_use : 1;
+ b32 used : 1;
+} StructMember;
+
+typedef struct TypeWithOffset TypeWithOffset;
+struct TypeWithOffset {
+ Type* type;
+ u32 offset;
+};
+
+#define TYPE_KINDS \
+ TYPE_KIND(Basic, TypeBasic) \
+ TYPE_KIND(Pointer, struct { TypeBasic base; Type *elem; }) \
+ TYPE_KIND(Function, struct { \
+ Type *return_type; \
+ u16 param_count; \
+ u16 needed_param_count; \
+ i16 vararg_arg_pos; \
+ Type* params[]; \
+ }) \
+ TYPE_KIND(Struct, struct { \
+ char* name; \
+ u32 size; \
+ u16 alignment, mem_count; \
+ bh_table(StructMember) members; \
+ bh_arr(StructMember *) memarr; \
+ bh_arr(struct AstPolySolution) poly_sln; \
+ bh_arr(TypeWithOffset) linear_members; \
+ struct AstType *constructed_from; \
+ }) \
+ TYPE_KIND(Compound, struct { \
+ u32 count; \
+ u32 size; \
+ bh_arr(TypeWithOffset) linear_members; \
+ Type* types[]; \
+ }) \
+ TYPE_KIND(Array, struct { u32 size; u32 count; Type *elem; }) \
+ TYPE_KIND(Slice, struct { Type *ptr_to_data; }) \
+ TYPE_KIND(DynArray, struct { Type *ptr_to_data; }) \
+ TYPE_KIND(VarArgs, struct { Type *ptr_to_data; }) \
+ TYPE_KIND(Enum, struct { \
+ char* name; \
+ Type* backing; \
+ b32 is_flags; \
+ })
+
+
+typedef enum TypeKind {
+ Type_Kind_Invalid,
+
+#define TYPE_KIND(k, ...) Type_Kind_##k,
+ TYPE_KINDS
+#undef TYPE_KIND
+
+ Type_Kind_Count,
+} TypeKind;
+
+#define TYPE_KIND(k, ...) typedef __VA_ARGS__ Type ## k;
+ TYPE_KINDS
+#undef TYPE_KIND
+
+enum TypeFlag {
+ Type_Flag_Default
+};
+
+struct Type {
+ TypeKind kind;
+
+ u32 id;
+ u32 flags;
+
+ // NOTE(Brendan Hansen): The abstract syntax tree node used to create
+ // the type. Primarily used to look up symbols in scopes that are embedded
+ // in the type.
+ struct AstType* ast_type;
+
+ union {
+#define TYPE_KIND(k, ...) Type##k k;
+ TYPE_KINDS
+#undef TYPE_KIND
+ };
+};
+
+extern bh_imap type_map;
+
+extern Type basic_types[];
+
+struct AstType;
+struct AstFunction;
+struct AstCompound;
+
+void types_init();
+void types_dump_type_info();
+
+b32 types_are_compatible(Type* t1, Type* t2);
+u32 type_size_of(Type* type);
+u32 type_alignment_of(Type* type);
+Type* type_build_from_ast(bh_allocator alloc, struct AstType* type_node);
+
+Type* type_build_function_type(bh_allocator alloc, struct AstFunction* func);
+Type* type_build_compound_type(bh_allocator alloc, struct AstCompound* compound);
+
+Type* type_make_pointer(bh_allocator alloc, Type* to);
+Type* type_make_array(bh_allocator alloc, Type* to, u32 count);
+Type* type_make_slice(bh_allocator alloc, Type* of);
+Type* type_make_dynarray(bh_allocator alloc, Type* of);
+Type* type_make_varargs(bh_allocator alloc, Type* of);
+
+void build_linear_types_with_offset(Type* type, bh_arr(TypeWithOffset)* pdest, u32 offset);
+
+const char* type_get_unique_name(Type* type);
+const char* type_get_name(Type* type);
+u32 type_get_alignment_log2(Type* type);
+
+b32 type_lookup_member(Type* type, char* member, StructMember* smem);
+b32 type_lookup_member_by_idx(Type* type, i32 idx, StructMember* smem);
+
+i32 type_linear_member_count(Type* type);
+b32 type_linear_member_lookup(Type* type, i32 idx, TypeWithOffset* two);
+i32 type_get_idx_of_linear_member_with_offset(Type* type, u32 offset);
+
+b32 type_struct_is_simple(Type* type);
+
+b32 type_is_pointer(Type* type);
+b32 type_is_rawptr(Type* type);
+b32 type_is_array(Type* tyoe);
+b32 type_is_struct(Type* type);
+b32 type_is_bool(Type* type);
+b32 type_is_small_integer(Type* type);
+b32 type_is_integer(Type* type);
+b32 type_is_numeric(Type* type);
+b32 type_is_compound(Type* type);
+b32 type_is_simd(Type* type);
+b32 type_results_in_void(Type* type);
+b32 type_is_array_accessible(Type* type);
+b32 type_is_structlike(Type* type);
+b32 type_is_structlike_strict(Type* type);
+u32 type_structlike_mem_count(Type* type);
+u32 type_structlike_is_simple(Type* type);
+b32 type_is_sl_constructable(Type* type);
+b32 type_struct_constructed_from_poly_struct(Type* struct_type, struct AstType* from);
+
+#endif // #ifndef ONYX_TYPES
--- /dev/null
+#include "bh.h"
+
+#include "astnodes.h"
+
+extern bh_scratch global_scratch;
+extern bh_allocator global_scratch_allocator;
+
+extern bh_managed_heap global_heap;
+extern bh_allocator global_heap_allocator;
+
+const char* onyx_ast_node_kind_string(AstKind kind);
+
+Package* package_lookup(char* package_name);
+Package* package_lookup_or_create(char* package_name, Scope* parent_scope, bh_allocator alloc);
+void package_track_use_package(Package* package, Entity* entity);
+void package_reinsert_use_packages(Package* package);
+
+Scope* scope_create(bh_allocator a, Scope* parent, OnyxFilePos created_at);
+void scope_include(Scope* target, Scope* source, OnyxFilePos pos);
+b32 symbol_introduce(Scope* scope, OnyxToken* tkn, AstNode* symbol);
+b32 symbol_raw_introduce(Scope* scope, char* tkn, OnyxFilePos pos, AstNode* symbol);
+void symbol_builtin_introduce(Scope* scope, char* sym, AstNode *node);
+void symbol_subpackage_introduce(Scope* scope, char* sym, AstPackage *node);
+AstNode* symbol_raw_resolve(Scope* start_scope, char* sym);
+AstNode* symbol_resolve(Scope* start_scope, OnyxToken* tkn);
+AstNode* try_symbol_raw_resolve_from_node(AstNode* node, char* symbol);
+AstNode* try_symbol_resolve_from_node(AstNode* node, OnyxToken* token);
+
+void build_all_overload_options(bh_arr(OverloadOption) overloads, bh_imap* all_overloads);
+
+u32 char_to_base16_value(char x);
+
+// Returns the length after processing the string.
+i32 string_process_escape_seqs(char* dest, char* src, i32 len);
+
+// NOTE: This should not be called until immediately before using the return value.
+// This function can return a static variable which will change if this is called
+// another time. -brendanfh 2020/10/09
+// :RelativeFiles This should lookup for the file relative to "relative_to"
+char* lookup_included_file(char* filename, char* relative_to, b32 add_onyx_suffix, b32 search_included_folders);
+
+extern AstTyped node_that_signals_a_yield;
--- /dev/null
+#ifndef ONYXWASM_H
+#define ONYXWASM_H
+
+#include "bh.h"
+
+#include "astnodes.h"
+#include "errors.h"
+
+typedef u8 WasmType;
+
+typedef struct WasmFuncType {
+ // NOTE: For now, WASM only allows for 1 return value.
+ // This may be lifted in the future.
+ i32 param_count;
+ WasmType return_type;
+ WasmType param_types[];
+} WasmFuncType;
+
+#define SIMD_INSTR_MASK 0x10000
+#define EXT_INSTR_MASK 0x20000
+
+typedef enum WasmInstructionType {
+ WI_UNREACHABLE = 0x00,
+ WI_NOP = 0x01,
+
+ // NOTE: Control flow
+ WI_BLOCK_START = 0x02,
+ WI_BLOCK_END = 0x0B, // NOTE: These ends are not unique
+ WI_LOOP_START = 0x03,
+ WI_LOOP_END = 0x0B,
+ WI_IF_START = 0x04,
+ WI_ELSE = 0x05,
+ WI_IF_END = 0x0B,
+ WI_JUMP = 0x0C,
+ WI_COND_JUMP = 0x0D,
+ WI_JUMP_TABLE = 0x0E,
+ WI_RETURN = 0x0F,
+ WI_CALL = 0x10,
+ WI_CALL_INDIRECT = 0x11,
+
+ // NOTE: Parametric instructions
+ WI_DROP = 0x1A,
+ WI_SELECT = 0x1B,
+
+ // NOTE: Variable instructions
+ WI_LOCAL_GET = 0x20,
+ WI_LOCAL_SET = 0x21,
+ WI_LOCAL_TEE = 0x22,
+ WI_GLOBAL_GET = 0x23,
+ WI_GLOBAL_SET = 0x24,
+
+ // NOTE: Memory instructions
+ WI_I32_LOAD = 0x28,
+ WI_I64_LOAD = 0x29,
+ WI_F32_LOAD = 0x2A,
+ WI_F64_LOAD = 0x2B,
+ WI_I32_LOAD_8_S = 0x2C,
+ WI_I32_LOAD_8_U = 0x2D,
+ WI_I32_LOAD_16_S = 0x2E,
+ WI_I32_LOAD_16_U = 0x2F,
+ WI_I64_LOAD_8_S = 0x30,
+ WI_I64_LOAD_8_U = 0x31,
+ WI_I64_LOAD_16_S = 0x32,
+ WI_I64_LOAD_16_U = 0x33,
+ WI_I64_LOAD_32_S = 0x34,
+ WI_I64_LOAD_32_U = 0x35,
+ WI_I32_STORE = 0x36,
+ WI_I64_STORE = 0x37,
+ WI_F32_STORE = 0x38,
+ WI_F64_STORE = 0x39,
+ WI_I32_STORE_8 = 0x3A,
+ WI_I32_STORE_16 = 0x3B,
+ WI_I64_STORE_8 = 0x3C,
+ WI_I64_STORE_16 = 0x3D,
+ WI_I64_STORE_32 = 0x3E,
+ WI_MEMORY_SIZE = 0x3F,
+ WI_MEMORY_GROW = 0x40,
+
+ // NOTE: Numeric Instructions
+ WI_I32_CONST = 0x41,
+ WI_I64_CONST = 0x42,
+ WI_F32_CONST = 0x43,
+ WI_F64_CONST = 0x44,
+
+ WI_I32_EQZ = 0x45,
+ WI_I32_EQ = 0x46,
+ WI_I32_NE = 0x47,
+ WI_I32_LT_S = 0x48,
+ WI_I32_LT_U = 0x49,
+ WI_I32_GT_S = 0x4a,
+ WI_I32_GT_U = 0x4b,
+ WI_I32_LE_S = 0x4c,
+ WI_I32_LE_U = 0x4d,
+ WI_I32_GE_S = 0x4e,
+ WI_I32_GE_U = 0x4f,
+
+ WI_I64_EQZ = 0x50,
+ WI_I64_EQ = 0x51,
+ WI_I64_NE = 0x52,
+ WI_I64_LT_S = 0x53,
+ WI_I64_LT_U = 0x54,
+ WI_I64_GT_S = 0x55,
+ WI_I64_GT_U = 0x56,
+ WI_I64_LE_S = 0x57,
+ WI_I64_LE_U = 0x58,
+ WI_I64_GE_S = 0x59,
+ WI_I64_GE_U = 0x5a,
+
+ WI_F32_EQ = 0x5b,
+ WI_F32_NE = 0x5c,
+ WI_F32_LT = 0x5d,
+ WI_F32_GT = 0x5e,
+ WI_F32_LE = 0x5f,
+ WI_F32_GE = 0x60,
+
+ WI_F64_EQ = 0x61,
+ WI_F64_NE = 0x62,
+ WI_F64_LT = 0x63,
+ WI_F64_GT = 0x64,
+ WI_F64_LE = 0x65,
+ WI_F64_GE = 0x66,
+
+ WI_I32_CLZ = 0x67,
+ WI_I32_CTZ = 0x68,
+ WI_I32_POPCNT = 0x69,
+ WI_I32_ADD = 0x6a,
+ WI_I32_SUB = 0x6b,
+ WI_I32_MUL = 0x6c,
+ WI_I32_DIV_S = 0x6d,
+ WI_I32_DIV_U = 0x6e,
+ WI_I32_REM_S = 0x6f,
+ WI_I32_REM_U = 0x70,
+ WI_I32_AND = 0x71,
+ WI_I32_OR = 0x72,
+ WI_I32_XOR = 0x73,
+ WI_I32_SHL = 0x74,
+ WI_I32_SHR_S = 0x75,
+ WI_I32_SHR_U = 0x76,
+ WI_I32_ROTL = 0x77,
+ WI_I32_ROTR = 0x78,
+
+ WI_I64_CLZ = 0x79,
+ WI_I64_CTZ = 0x7a,
+ WI_I64_POPCNT = 0x7b,
+ WI_I64_ADD = 0x7c,
+ WI_I64_SUB = 0x7d,
+ WI_I64_MUL = 0x7e,
+ WI_I64_DIV_S = 0x7f,
+ WI_I64_DIV_U = 0x80,
+ WI_I64_REM_S = 0x81,
+ WI_I64_REM_U = 0x82,
+ WI_I64_AND = 0x83,
+ WI_I64_OR = 0x84,
+ WI_I64_XOR = 0x85,
+ WI_I64_SHL = 0x86,
+ WI_I64_SHR_S = 0x87,
+ WI_I64_SHR_U = 0x88,
+ WI_I64_ROTL = 0x89,
+ WI_I64_ROTR = 0x8a,
+
+ WI_F32_ABS = 0x8b,
+ WI_F32_NEG = 0x8c,
+ WI_F32_CEIL = 0x8d,
+ WI_F32_FLOOR = 0x8e,
+ WI_F32_TRUNC = 0x8f,
+ WI_F32_NEAREST = 0x90,
+ WI_F32_SQRT = 0x91,
+ WI_F32_ADD = 0x92,
+ WI_F32_SUB = 0x93,
+ WI_F32_MUL = 0x94,
+ WI_F32_DIV = 0x95,
+ WI_F32_MIN = 0x96,
+ WI_F32_MAX = 0x97,
+ WI_F32_COPYSIGN = 0x98,
+
+ WI_F64_ABS = 0x99,
+ WI_F64_NEG = 0x9a,
+ WI_F64_CEIL = 0x9b,
+ WI_F64_FLOOR = 0x9c,
+ WI_F64_TRUNC = 0x9d,
+ WI_F64_NEAREST = 0x9e,
+ WI_F64_SQRT = 0x9f,
+ WI_F64_ADD = 0xA0,
+ WI_F64_SUB = 0xA1,
+ WI_F64_MUL = 0xA2,
+ WI_F64_DIV = 0xA3,
+ WI_F64_MIN = 0xA4,
+ WI_F64_MAX = 0xA5,
+ WI_F64_COPYSIGN = 0xA6,
+
+ WI_I32_FROM_I64 = 0xA7,
+ WI_I32_FROM_F32_S = 0xA8,
+ WI_I32_FROM_F32_U = 0xA9,
+ WI_I32_FROM_F64_S = 0xAA,
+ WI_I32_FROM_F64_U = 0xAB,
+
+ WI_I64_FROM_I32_S = 0xAC,
+ WI_I64_FROM_I32_U = 0xAD,
+ WI_I64_FROM_F32_S = 0xAE,
+ WI_I64_FROM_F32_U = 0xAF,
+ WI_I64_FROM_F64_S = 0xB0,
+ WI_I64_FROM_F64_U = 0xB1,
+
+ WI_F32_FROM_I32_S = 0xB2,
+ WI_F32_FROM_I32_U = 0xB3,
+ WI_F32_FROM_I64_S = 0xB4,
+ WI_F32_FROM_I64_U = 0xB5,
+ WI_F32_FROM_F64 = 0xB6,
+
+ WI_F64_FROM_I32_S = 0xB7,
+ WI_F64_FROM_I32_U = 0xB8,
+ WI_F64_FROM_I64_S = 0xB9,
+ WI_F64_FROM_I64_U = 0xBA,
+ WI_F64_FROM_F32 = 0xBB,
+
+ WI_I32_REINTERPRET_F32 = 0xBC,
+ WI_I64_REINTERPRET_F64 = 0xBD,
+ WI_F32_REINTERPRET_I32 = 0xBE,
+ WI_F64_REINTERPRET_I64 = 0xBF,
+
+ WI_I32_EXTEND_8_S = 0xC0,
+ WI_I32_EXTEND_16_S = 0xC1,
+ WI_I64_EXTEND_8_S = 0xC2,
+ WI_I64_EXTEND_16_S = 0xC3,
+ WI_I64_EXTEND_32_S = 0xC4,
+
+ // Pointer stuff; this will make it easier to switch to 64-bit
+ // pointers, whenever WebAssembly standarizes that.
+ WI_PTR_CONST = WI_I32_CONST,
+ WI_PTR_LOAD = WI_I32_LOAD,
+ WI_PTR_STORE = WI_I32_STORE,
+ WI_PTR_ADD = WI_I32_ADD,
+ WI_PTR_SUB = WI_I32_SUB,
+ WI_PTR_MUL = WI_I32_MUL,
+ WI_PTR_GE = WI_I32_GE_U,
+ WI_PTR_GT = WI_I32_GT_U,
+ WI_PTR_EQ = WI_I32_EQ,
+
+ WI_V128_LOAD = SIMD_INSTR_MASK | 0,
+ WI_V128_STORE = SIMD_INSTR_MASK | 11,
+
+ WI_V128_CONST = SIMD_INSTR_MASK | 12,
+
+ WI_I8X16_SHUFFLE = SIMD_INSTR_MASK | 13,
+
+ WI_I8X16_EXTRACT_LANE_S = SIMD_INSTR_MASK | 21,
+ WI_I8X16_EXTRACT_LANE_U = SIMD_INSTR_MASK | 22,
+ WI_I8X16_REPLACE_LANE = SIMD_INSTR_MASK | 23,
+ WI_I16X8_EXTRACT_LANE_S = SIMD_INSTR_MASK | 24,
+ WI_I16X8_EXTRACT_LANE_U = SIMD_INSTR_MASK | 25,
+ WI_I16X8_REPLACE_LANE = SIMD_INSTR_MASK | 26,
+ WI_I32X4_EXTRACT_LANE = SIMD_INSTR_MASK | 27,
+ WI_I32X4_REPLACE_LANE = SIMD_INSTR_MASK | 28,
+ WI_I64X2_EXTRACT_LANE = SIMD_INSTR_MASK | 29,
+ WI_I64X2_REPLACE_LANE = SIMD_INSTR_MASK | 30,
+ WI_F32X4_EXTRACT_LANE = SIMD_INSTR_MASK | 31,
+ WI_F32X4_REPLACE_LANE = SIMD_INSTR_MASK | 32,
+ WI_F64X2_EXTRACT_LANE = SIMD_INSTR_MASK | 33,
+ WI_F64X2_REPLACE_LANE = SIMD_INSTR_MASK | 34,
+
+ WI_I8X16_SWIZZLE = SIMD_INSTR_MASK | 14,
+ WI_I8X16_SPLAT = SIMD_INSTR_MASK | 15,
+ WI_I16X8_SPLAT = SIMD_INSTR_MASK | 16,
+ WI_I32X4_SPLAT = SIMD_INSTR_MASK | 17,
+ WI_I64X2_SPLAT = SIMD_INSTR_MASK | 18,
+ WI_F32X4_SPLAT = SIMD_INSTR_MASK | 19,
+ WI_F64X2_SPLAT = SIMD_INSTR_MASK | 20,
+
+ WI_I8X16_EQ = SIMD_INSTR_MASK | 35,
+ WI_I8X16_NEQ = SIMD_INSTR_MASK | 36,
+ WI_I8X16_LT_S = SIMD_INSTR_MASK | 37,
+ WI_I8X16_LT_U = SIMD_INSTR_MASK | 38,
+ WI_I8X16_GT_S = SIMD_INSTR_MASK | 39,
+ WI_I8X16_GT_U = SIMD_INSTR_MASK | 40,
+ WI_I8X16_LE_S = SIMD_INSTR_MASK | 41,
+ WI_I8X16_LE_U = SIMD_INSTR_MASK | 42,
+ WI_I8X16_GE_S = SIMD_INSTR_MASK | 43,
+ WI_I8X16_GE_U = SIMD_INSTR_MASK | 44,
+
+ WI_I16X8_EQ = SIMD_INSTR_MASK | 45,
+ WI_I16X8_NEQ = SIMD_INSTR_MASK | 46,
+ WI_I16X8_LT_S = SIMD_INSTR_MASK | 47,
+ WI_I16X8_LT_U = SIMD_INSTR_MASK | 48,
+ WI_I16X8_GT_S = SIMD_INSTR_MASK | 49,
+ WI_I16X8_GT_U = SIMD_INSTR_MASK | 50,
+ WI_I16X8_LE_S = SIMD_INSTR_MASK | 51,
+ WI_I16X8_LE_U = SIMD_INSTR_MASK | 52,
+ WI_I16X8_GE_S = SIMD_INSTR_MASK | 53,
+ WI_I16X8_GE_U = SIMD_INSTR_MASK | 54,
+
+ WI_I32X4_EQ = SIMD_INSTR_MASK | 55,
+ WI_I32X4_NEQ = SIMD_INSTR_MASK | 56,
+ WI_I32X4_LT_S = SIMD_INSTR_MASK | 57,
+ WI_I32X4_LT_U = SIMD_INSTR_MASK | 58,
+ WI_I32X4_GT_S = SIMD_INSTR_MASK | 59,
+ WI_I32X4_GT_U = SIMD_INSTR_MASK | 60,
+ WI_I32X4_LE_S = SIMD_INSTR_MASK | 61,
+ WI_I32X4_LE_U = SIMD_INSTR_MASK | 62,
+ WI_I32X4_GE_S = SIMD_INSTR_MASK | 63,
+ WI_I32X4_GE_U = SIMD_INSTR_MASK | 64,
+
+ WI_F32X4_EQ = SIMD_INSTR_MASK | 65,
+ WI_F32X4_NEQ = SIMD_INSTR_MASK | 66,
+ WI_F32X4_LT = SIMD_INSTR_MASK | 67,
+ WI_F32X4_GT = SIMD_INSTR_MASK | 68,
+ WI_F32X4_LE = SIMD_INSTR_MASK | 69,
+ WI_F32X4_GE = SIMD_INSTR_MASK | 70,
+
+ WI_F64X2_EQ = SIMD_INSTR_MASK | 71,
+ WI_F64X2_NEQ = SIMD_INSTR_MASK | 72,
+ WI_F64X2_LT = SIMD_INSTR_MASK | 73,
+ WI_F64X2_GT = SIMD_INSTR_MASK | 74,
+ WI_F64X2_LE = SIMD_INSTR_MASK | 75,
+ WI_F64X2_GE = SIMD_INSTR_MASK | 76,
+
+ WI_V128_NOT = SIMD_INSTR_MASK | 77,
+ WI_V128_AND = SIMD_INSTR_MASK | 78,
+ WI_V128_ANDNOT = SIMD_INSTR_MASK | 79,
+ WI_V128_OR = SIMD_INSTR_MASK | 80,
+ WI_V128_XOR = SIMD_INSTR_MASK | 81,
+ WI_V128_BITSELECT = SIMD_INSTR_MASK | 82,
+
+ WI_I8X16_ABS = SIMD_INSTR_MASK | 96,
+ WI_I8X16_NEG = SIMD_INSTR_MASK | 97,
+ WI_I8X16_ANY_TRUE = SIMD_INSTR_MASK | 98,
+ WI_I8X16_ALL_TRUE = SIMD_INSTR_MASK | 99,
+ WI_I8X16_BITMASK = SIMD_INSTR_MASK | 100,
+ WI_I8X16_NARROW_I16X8_S = SIMD_INSTR_MASK | 101,
+ WI_I8X16_NARROW_I16X8_U = SIMD_INSTR_MASK | 102,
+ WI_I8X16_SHL = SIMD_INSTR_MASK | 107,
+ WI_I8X16_SHR_S = SIMD_INSTR_MASK | 108,
+ WI_I8X16_SHR_U = SIMD_INSTR_MASK | 109,
+ WI_I8X16_ADD = SIMD_INSTR_MASK | 110,
+ WI_I8X16_ADD_SAT_S = SIMD_INSTR_MASK | 111,
+ WI_I8X16_ADD_SAT_U = SIMD_INSTR_MASK | 112,
+ WI_I8X16_SUB = SIMD_INSTR_MASK | 113,
+ WI_I8X16_SUB_SAT_S = SIMD_INSTR_MASK | 114,
+ WI_I8X16_SUB_SAT_U = SIMD_INSTR_MASK | 115,
+ WI_I8X16_MIN_S = SIMD_INSTR_MASK | 118,
+ WI_I8X16_MIN_U = SIMD_INSTR_MASK | 119,
+ WI_I8X16_MAX_S = SIMD_INSTR_MASK | 120,
+ WI_I8X16_MAX_U = SIMD_INSTR_MASK | 121,
+ WI_I8X16_AVGR_U = SIMD_INSTR_MASK | 123,
+
+ WI_I16X8_ABS = SIMD_INSTR_MASK | 128,
+ WI_I16X8_NEG = SIMD_INSTR_MASK | 129,
+ WI_I16X8_ANY_TRUE = SIMD_INSTR_MASK | 130,
+ WI_I16X8_ALL_TRUE = SIMD_INSTR_MASK | 131,
+ WI_I16X8_BITMASK = SIMD_INSTR_MASK | 132,
+ WI_I16X8_NARROW_I32X4_S = SIMD_INSTR_MASK | 133,
+ WI_I16X8_NARROW_I32X4_U = SIMD_INSTR_MASK | 134,
+ WI_I16X8_WIDEN_LOW_I8X16_S = SIMD_INSTR_MASK | 135,
+ WI_I16X8_WIDEN_HIGH_I8X16_S = SIMD_INSTR_MASK | 136,
+ WI_I16X8_WIDEN_LOW_I8X16_U = SIMD_INSTR_MASK | 137,
+ WI_I16X8_WIDEN_HIGH_I8X16_U = SIMD_INSTR_MASK | 138,
+ WI_I16X8_SHL = SIMD_INSTR_MASK | 139,
+ WI_I16X8_SHR_S = SIMD_INSTR_MASK | 140,
+ WI_I16X8_SHR_U = SIMD_INSTR_MASK | 141,
+ WI_I16X8_ADD = SIMD_INSTR_MASK | 142,
+ WI_I16X8_ADD_SAT_S = SIMD_INSTR_MASK | 143,
+ WI_I16X8_ADD_SAT_U = SIMD_INSTR_MASK | 144,
+ WI_I16X8_SUB = SIMD_INSTR_MASK | 145,
+ WI_I16X8_SUB_SAT_S = SIMD_INSTR_MASK | 146,
+ WI_I16X8_SUB_SAT_U = SIMD_INSTR_MASK | 147,
+ WI_I16X8_MUL = SIMD_INSTR_MASK | 149,
+ WI_I16X8_MIN_S = SIMD_INSTR_MASK | 150,
+ WI_I16X8_MIN_U = SIMD_INSTR_MASK | 151,
+ WI_I16X8_MAX_S = SIMD_INSTR_MASK | 152,
+ WI_I16X8_MAX_U = SIMD_INSTR_MASK | 153,
+ WI_I16X8_AVGR_U = SIMD_INSTR_MASK | 155,
+
+ WI_I32X4_ABS = SIMD_INSTR_MASK | 160,
+ WI_I32X4_NEG = SIMD_INSTR_MASK | 161,
+ WI_I32X4_ANY_TRUE = SIMD_INSTR_MASK | 162,
+ WI_I32X4_ALL_TRUE = SIMD_INSTR_MASK | 163,
+ WI_I32X4_BITMASK = SIMD_INSTR_MASK | 164,
+ WI_I32X4_WIDEN_LOW_I16X8_S = SIMD_INSTR_MASK | 167,
+ WI_I32X4_WIDEN_HIGH_I16X8_S = SIMD_INSTR_MASK | 168,
+ WI_I32X4_WIDEN_LOW_I16X8_U = SIMD_INSTR_MASK | 169,
+ WI_I32X4_WIDEN_HIGH_I16X8_U = SIMD_INSTR_MASK | 170,
+ WI_I32X4_SHL = SIMD_INSTR_MASK | 171,
+ WI_I32X4_SHR_S = SIMD_INSTR_MASK | 172,
+ WI_I32X4_SHR_U = SIMD_INSTR_MASK | 173,
+ WI_I32X4_ADD = SIMD_INSTR_MASK | 174,
+ WI_I32X4_SUB = SIMD_INSTR_MASK | 177,
+ WI_I32X4_MUL = SIMD_INSTR_MASK | 181,
+ WI_I32X4_MIN_S = SIMD_INSTR_MASK | 182,
+ WI_I32X4_MIN_U = SIMD_INSTR_MASK | 183,
+ WI_I32X4_MAX_S = SIMD_INSTR_MASK | 184,
+ WI_I32X4_MAX_U = SIMD_INSTR_MASK | 185,
+
+ WI_I64X2_NEG = SIMD_INSTR_MASK | 193,
+ WI_I64X2_SHL = SIMD_INSTR_MASK | 203,
+ WI_I64X2_SHR_S = SIMD_INSTR_MASK | 204,
+ WI_I64X2_SHR_U = SIMD_INSTR_MASK | 205,
+ WI_I64X2_ADD = SIMD_INSTR_MASK | 206,
+ WI_I64X2_SUB = SIMD_INSTR_MASK | 209,
+ WI_I64X2_MUL = SIMD_INSTR_MASK | 213,
+
+ WI_F32X4_ABS = SIMD_INSTR_MASK | 224,
+ WI_F32X4_NEG = SIMD_INSTR_MASK | 225,
+ WI_F32X4_SQRT = SIMD_INSTR_MASK | 227,
+ WI_F32X4_ADD = SIMD_INSTR_MASK | 228,
+ WI_F32X4_SUB = SIMD_INSTR_MASK | 229,
+ WI_F32X4_MUL = SIMD_INSTR_MASK | 230,
+ WI_F32X4_DIV = SIMD_INSTR_MASK | 231,
+ WI_F32X4_MIN = SIMD_INSTR_MASK | 232,
+ WI_F32X4_MAX = SIMD_INSTR_MASK | 233,
+
+ WI_F64X2_ABS = SIMD_INSTR_MASK | 236,
+ WI_F64X2_NEG = SIMD_INSTR_MASK | 237,
+ WI_F64X2_SQRT = SIMD_INSTR_MASK | 239,
+ WI_F64X2_ADD = SIMD_INSTR_MASK | 240,
+ WI_F64X2_SUB = SIMD_INSTR_MASK | 241,
+ WI_F64X2_MUL = SIMD_INSTR_MASK | 242,
+ WI_F64X2_DIV = SIMD_INSTR_MASK | 243,
+ WI_F64X2_MIN = SIMD_INSTR_MASK | 244,
+ WI_F64X2_MAX = SIMD_INSTR_MASK | 245,
+
+ WI_I32X4_TRUNC_SAT_F32X4_S = SIMD_INSTR_MASK | 248,
+ WI_I32X4_TRUNC_SAT_F32X4_U = SIMD_INSTR_MASK | 249,
+ WI_F32X4_CONVERT_I32X4_S = SIMD_INSTR_MASK | 250,
+ WI_F32X4_CONVERT_I32X4_U = SIMD_INSTR_MASK | 251,
+
+
+ WI_MEMORY_COPY = EXT_INSTR_MASK | 0x0a,
+ WI_MEMORY_FILL = EXT_INSTR_MASK | 0x0b,
+} WasmInstructionType;
+
+typedef union {
+ struct {
+ i32 i1, i2;
+ };
+ i64 l;
+ float f;
+ double d;
+ ptr p;
+} WasmInstructionData;
+
+typedef struct WasmInstruction {
+ WasmInstructionType type;
+ WasmInstructionData data;
+} WasmInstruction;
+
+typedef struct BranchTable {
+ u32 count;
+ u32 default_case;
+ u32 cases[];
+} BranchTable;
+
+#define LOCAL_IS_WASM 0x8000000000000
+typedef struct LocalAllocator {
+ u32 param_count;
+
+ u32 allocated[5];
+ u32 freed[5];
+
+ i32 max_stack;
+ i32 curr_stack;
+} LocalAllocator;
+
+typedef struct WasmFunc {
+ i32 type_idx;
+ LocalAllocator locals;
+ bh_arr(WasmInstruction) code;
+} WasmFunc;
+
+typedef struct WasmGlobal {
+ WasmType type;
+ u32 mutable : 1;
+ bh_arr(WasmInstruction) initial_value;
+} WasmGlobal;
+
+typedef enum WasmForeignKind {
+ WASM_FOREIGN_FUNCTION = 0x00,
+ WASM_FOREIGN_TABLE = 0x01,
+ WASM_FOREIGN_MEMORY = 0x02,
+ WASM_FOREIGN_GLOBAL = 0x03,
+} WasmForeignKind;
+
+typedef struct WasmExport {
+ WasmForeignKind kind;
+ i32 idx;
+} WasmExport;
+
+typedef struct WasmImport {
+ WasmForeignKind kind;
+ i32 idx;
+ OnyxToken *mod, *name;
+} WasmImport;
+
+typedef struct WasmDatum {
+ u32 offset, length;
+ ptr data;
+} WasmDatum;
+
+typedef enum DeferredStmtType {
+ Deferred_Stmt_Node,
+ Deferred_Stmt_Code,
+} DeferredStmtType;
+
+typedef struct DeferredStmt {
+ DeferredStmtType type;
+ u32 depth;
+
+ union {
+ AstNode *stmt;
+ struct {
+ WasmInstruction* instructions;
+ u32 instruction_count;
+ };
+ };
+} DeferredStmt;
+
+typedef struct AllocatedSpace {
+ u64 depth;
+ AstTyped *expr;
+} AllocatedSpace;
+
+typedef struct StrLitInfo {
+ u32 addr;
+ u32 len;
+} StrLitInfo;
+
+typedef struct PatchInfo {
+ u32 instruction_index;
+} PatchInfo;
+
+typedef struct OnyxWasmModule {
+ bh_allocator allocator;
+
+ bh_arena *extended_instr_data;
+ bh_allocator extended_instr_alloc;
+
+ // NOTE: Mapping ptrs to function / global indicies
+ bh_imap index_map;
+
+ // NOTE: Mapping from local ast node ptrs to indicies or offsets, depending on the mode
+ bh_imap local_map;
+
+ LocalAllocator* local_alloc;
+
+ // NOTE: Mapping ptrs to elements
+ bh_imap elem_map;
+
+ bh_arr(DeferredStmt) deferred_stmts;
+ bh_arr(AllocatedSpace) local_allocations;
+
+ bh_arr(PatchInfo) stack_leave_patches;
+
+ // NOTE: Used internally as a map from strings that represent function types,
+ // 0x7f 0x7f : 0x7f ( (i32, i32) -> i32 )
+ // to the function type index if it has been created.
+ bh_table(i32) type_map;
+
+ bh_table(StrLitInfo) loaded_file_info;
+ bh_table(StrLitInfo) string_literals;
+
+ bh_arr(u8) structured_jump_target;
+ bh_arr(AstLocal*) return_location_stack; // NOTE: Used for do-block return expressions.
+
+ bh_arr(WasmFuncType*) types; // NOTE: This have to be pointers because the type is variadic in size
+ bh_arr(WasmImport) imports;
+ bh_table(WasmExport) exports;
+ bh_arr(WasmGlobal) globals;
+ bh_arr(WasmFunc) funcs;
+ bh_arr(WasmDatum) data;
+ bh_arr(i32) elems;
+
+ // NOTE: Set of things used when compiling; not part of the actual module
+ u32 export_count;
+ u32 next_type_idx;
+ u32 next_func_idx;
+ u32 next_global_idx;
+ u32 next_datum_offset;
+ u32 next_elem_idx;
+ u32 foreign_function_count;
+ u32 foreign_global_count;
+
+ i32 *stack_top_ptr;
+ u64 stack_base_idx;
+ CallingConvention curr_cc;
+ i32 null_proc_func_idx;
+
+ b32 has_stack_locals : 1;
+} OnyxWasmModule;
+
+OnyxWasmModule onyx_wasm_module_create(bh_allocator alloc);
+void onyx_wasm_module_free(OnyxWasmModule* module);
+void onyx_wasm_module_write_to_file(OnyxWasmModule* module, bh_file file);
+
+#endif
--- /dev/null
+#include "astnodes.h"
+#include "parser.h"
+#include "utils.h"
+
+static const char* ast_node_names[] = {
+ "ERROR",
+ "PACKAGE",
+ "INCLUDE FILE",
+ "INCLUDE FOLDER",
+ "MEMORY RESERVATION",
+
+ "BINDING",
+ "ALIAS",
+ "FUNCTION",
+ "OVERLOADED_FUNCTION",
+ "POLYMORPHIC PROC",
+ "BLOCK",
+ "LOCAL",
+ "GLOBAL",
+ "SYMBOL",
+
+ "UN_OP",
+ "BIN_OP",
+
+ "COMPOUND",
+ "NAMED_VALUE",
+
+ "TYPE_START (BAD)",
+ "TYPE",
+ "BASIC_TYPE",
+ "POINTER_TYPE",
+ "FUNCTION_TYPE",
+ "ARRAY TYPE",
+ "SLICE TYPE",
+ "DYNARR TYPE",
+ "VARARG TYPE",
+ "STRUCT TYPE",
+ "POLYMORPHIC STRUCT TYPE",
+ "POLYMORPHIC STRUCT CALL TYPE",
+ "ENUM TYPE",
+ "TYPE_ALIAS",
+ "TYPE RAW ALIAS",
+ "COMPOUND TYPE",
+ "TYPE OF",
+ "TYPE_END (BAD)",
+
+ "STRUCT MEMBER",
+ "ENUM VALUE",
+
+ "NUMERIC LITERAL",
+ "STRING LITERAL",
+ "PARAM",
+ "ARGUMENT",
+ "CALL",
+ "INTRINSIC CALL",
+ "RETURN",
+ "ADDRESS OF",
+ "DEREFERENCE",
+ "ARRAY ACCESS",
+ "SLICE",
+ "FIELD ACCESS",
+ "UNARY FIELD ACCESS",
+ "PIPE",
+ "METHOD_CALL",
+ "RANGE",
+ "SIZE OF",
+ "ALIGN OF",
+ "FILE CONTENTS",
+ "STRUCT LITERAL",
+ "ARRAY LITERAL",
+ "IF EXPRESSION",
+
+ "IF",
+ "FOR",
+ "WHILE",
+ "JUMP",
+ "USE",
+ "DEFER",
+ "SWITCH",
+ "SWITCH CASE",
+
+ "SOLIDIFY",
+ "STATIC IF",
+ "STATIC ERROR",
+ "ADD OVERLOAD",
+ "OPERATOR OVERLOAD",
+ "EXPORT",
+ "DEFINED",
+ "CALL SITE",
+
+ "CODE BLOCK",
+ "DIRECTIVE INSERT",
+ "MACRO",
+ "DO BLOCK",
+
+ "NOTE",
+
+ "AST_NODE_KIND_COUNT",
+};
+
+const char* onyx_ast_node_kind_string(AstKind kind) {
+ return ast_node_names[kind];
+}
+
+const char *binaryop_string[Binary_Op_Count] = {
+ "+", "-", "*", "/", "%",
+ "==", "!=", "<", "<=", ">", ">=",
+ "&", "|", "^", "<<", ">>", ">>>",
+ "&&", "||",
+
+ "NONE",
+ "=", "+=", "-=", "*=", "/=", "%=",
+ "&=", "|=", "^=", "<<=", ">>=", ">>>=",
+ "NONE",
+
+ "|>", "..", "->",
+
+ "[]",
+};
+
+const char* entity_state_strings[Entity_State_Count] = {
+ "Error",
+ "Parse Builtin",
+ "Introduce Symbols",
+ "Parse",
+ "Resolve Symbols",
+ "Check Types",
+ "Code Gen",
+ "Finalized",
+};
+
+const char* entity_type_strings[Entity_Type_Count] = {
+ "Unknown",
+ "Error",
+ "Note",
+ "Add to Load Path",
+ "Load File",
+ "Binding (Declaration)",
+ "Use Package",
+ "Static If",
+ "String Literal",
+ "File Contents",
+ "Enum",
+ "Type Alias",
+ "Memory Reservation Type",
+ "Use",
+ "Polymorphic Proc",
+ "Macro",
+ "Foreign_Function Header",
+ "Foreign_Global Header",
+ "Function Header",
+ "Global Header",
+ "Process Directive",
+ "Struct Member Default",
+ "Memory Reservation",
+ "Expression",
+ "Global",
+ "Overloaded_Function",
+ "Function",
+};
+
+#define REDUCE_BINOP_ALL(op) \
+ if (type_is_small_integer(res->type) || type_is_bool(res->type)) { \
+ res->value.i = left->value.i op right->value.i; \
+ } else if (type_is_integer(res->type) \
+ || res->type->Basic.kind == Basic_Kind_Int_Unsized \
+ || res->type->kind == Type_Kind_Enum) { \
+ res->value.l = left->value.l op right->value.l; \
+ } else if (res->type->Basic.kind == Basic_Kind_F32) { \
+ res->value.f = left->value.f op right->value.f; \
+ } else if (res->type->Basic.kind == Basic_Kind_F64 || res->type->Basic.kind == Basic_Kind_Float_Unsized) { \
+ res->value.d = left->value.d op right->value.d; \
+ } \
+ break;
+
+#define REDUCE_BINOP_INT(op) \
+ if (type_is_small_integer(res->type) || type_is_bool(res->type)) { \
+ res->value.i = left->value.i op right->value.i; \
+ } else if (type_is_integer(res->type) \
+ || res->type->Basic.kind == Basic_Kind_Int_Unsized \
+ || res->type->kind == Type_Kind_Enum) { \
+ res->value.l = left->value.l op right->value.l; \
+ } \
+ break;
+
+#define REDUCE_BINOP_BOOL(op) \
+ if (type_is_bool(res->type)) { \
+ res->value.i = left->value.i op right->value.i; \
+ } \
+ break;
+
+AstNumLit* ast_reduce_binop(bh_allocator a, AstBinaryOp* node) {
+ AstNumLit* left = (AstNumLit *) ast_reduce(a, node->left);
+ AstNumLit* right = (AstNumLit *) ast_reduce(a, node->right);
+
+ if (left->kind != Ast_Kind_NumLit || right->kind != Ast_Kind_NumLit) {
+ node->left = (AstTyped *) left;
+ node->right = (AstTyped *) right;
+ return (AstNumLit *) node;
+ }
+
+ AstNumLit* res = onyx_ast_node_new(a, sizeof(AstNumLit), Ast_Kind_NumLit);
+ res->token = node->token;
+ res->flags |= node->flags;
+ res->flags |= Ast_Flag_Comptime;
+ res->type_node = node->type_node;
+ res->type = node->type;
+ res->next = node->next;
+
+ switch (node->operation) {
+ case Binary_Op_Add: REDUCE_BINOP_ALL(+);
+ case Binary_Op_Minus: REDUCE_BINOP_ALL(-);
+ case Binary_Op_Multiply: REDUCE_BINOP_ALL(*);
+ case Binary_Op_Divide: REDUCE_BINOP_ALL(/);
+ case Binary_Op_Modulus: REDUCE_BINOP_INT(%);
+
+ case Binary_Op_Equal: REDUCE_BINOP_ALL(==);
+ case Binary_Op_Not_Equal: REDUCE_BINOP_ALL(!=);
+ case Binary_Op_Less: REDUCE_BINOP_ALL(<);
+ case Binary_Op_Less_Equal: REDUCE_BINOP_ALL(<=);
+ case Binary_Op_Greater: REDUCE_BINOP_ALL(>);
+ case Binary_Op_Greater_Equal: REDUCE_BINOP_ALL(>=);
+
+ case Binary_Op_And: REDUCE_BINOP_INT(&);
+ case Binary_Op_Or: REDUCE_BINOP_INT(|);
+ case Binary_Op_Xor: REDUCE_BINOP_INT(^);
+ case Binary_Op_Shl: REDUCE_BINOP_INT(<<);
+ case Binary_Op_Shr: REDUCE_BINOP_INT(>>);
+ case Binary_Op_Sar: REDUCE_BINOP_INT(>>);
+
+ case Binary_Op_Bool_And: REDUCE_BINOP_BOOL(&&);
+ case Binary_Op_Bool_Or: REDUCE_BINOP_BOOL(||);
+
+ default: break;
+ }
+
+ return res;
+}
+
+#define REDUCE_UNOP(op) \
+ if (type_is_small_integer(unop->type) || type_is_bool(unop->type)) { \
+ res->value.i = op (operand)->value.i; \
+ } else if (type_is_integer(unop->type) || unop->type->Basic.kind == Basic_Kind_Int_Unsized) { \
+ res->value.l = op (operand)->value.l; \
+ } else if (unop->type->Basic.kind == Basic_Kind_F32) { \
+ res->value.f = op (operand)->value.f; \
+ } else if (unop->type->Basic.kind == Basic_Kind_F64 || unop->type->Basic.kind == Basic_Kind_Float_Unsized) { \
+ res->value.d = op (operand)->value.d; \
+ } \
+ break;
+
+#define REDUCE_UNOP_INT(op) \
+ if (type_is_small_integer(unop->type) || type_is_bool(unop->type)) { \
+ res->value.i = op (operand)->value.i; \
+ } else if (type_is_integer(unop->type) || res->type->Basic.kind == Basic_Kind_Int_Unsized) { \
+ res->value.l = op (operand)->value.l; \
+ }
+
+AstTyped* ast_reduce_unaryop(bh_allocator a, AstUnaryOp* unop) {
+ // GROSS
+ AstNumLit* operand = (AstNumLit *) ast_reduce(a, unop->expr);
+ unop->expr = (AstTyped *) operand;
+
+ if (operand->kind != Ast_Kind_NumLit) {
+ return (AstTyped *) unop;
+ }
+
+ AstNumLit* res = onyx_ast_node_new(a, sizeof(AstNumLit), Ast_Kind_NumLit);
+ res->token = unop->token;
+ res->flags |= Ast_Flag_Comptime;
+ res->type_node = unop->type_node;
+ res->type = unop->type;
+ res->next = unop->next;
+
+ switch (unop->operation) {
+ case Unary_Op_Negate: REDUCE_UNOP(-);
+ case Unary_Op_Not: {
+ if (type_is_bool(res->type)) res->value.i = ! (operand)->value.i;
+ break;
+ }
+ case Unary_Op_Bitwise_Not: REDUCE_UNOP_INT(~);
+
+ case Unary_Op_Cast: {
+ #if 0
+ Type* from = operand->type;
+ Type* to = unop->type;
+
+ if (from->kind == Type_Kind_Enum) from = from->Enum.backing;
+ if (to->kind == Type_Kind_Enum) to = to->Enum.backing;
+
+ if (from->kind != Type_Kind_Pointer && from->kind != Type_Kind_Basic) return (AstTyped *) unop;
+ if (to->kind != Type_Kind_Pointer && to->kind != Type_Kind_Basic) return (AstTyped *) unop;
+
+ b32 from_is_int=0, to_is_int=0;
+ i32 from_size, to_size;
+
+ from_size = type_size_of(from);
+ to_size = type_size_of(to);
+
+ if (from->kind == Type_Kind_Basic) from_is_int = (from->flags & (Basic_Flag_Integer | Basic_Flag_Pointer)) != 0;
+ if (to->kind == Type_Kind_Basic) to_is_int = (to->flags & (Basic_Flag_Integer | Basic_Flag_Pointer)) != 0;
+
+ if (from->kind == Type_Kind_Pointer) from_is_int = 1;
+ if (to->kind == Type_Kind_Pointer) to_is_int = 1;
+
+ if (from_is_int == to_is_int && from_size == to_size) {
+ // If they are already equal, nothing to do
+ return (AstTyped *) res;
+ }
+
+ if (from_is_int && !to_is_int) {
+ if (from_size == to_size) {
+ if (from_size == 4) {
+ res->value.f = (f32) operand->value.i;
+ }
+ else if (from_size == 8) {
+ res->value.d = (f64) operand->value.l;
+ }
+ } else {
+ if (from_size == 4) {
+ res->value.d = (f64) operand->value.i;
+ }
+ else if (from_size == 8) {
+ res->value.f = (f32) operand->value.l;
+ }
+ }
+ } else {
+ if (from_size == to_size) {
+ if (from_size == 4) {
+ res->value.i = (i32) operand->value.f;
+ }
+ else if (from_size == 8) {
+ res->value.l = (i64) operand->value.d;
+ }
+ } else {
+ if (from_size == 4) {
+ res->value.l = (i64) operand->value.f;
+ }
+ else if (from_size == 8) {
+ res->value.i = (i32) operand->value.d;
+ }
+ }
+ }
+ break;
+
+ #endif
+ }
+
+ default: return (AstTyped *) unop;
+ }
+
+ return (AstTyped *) res;
+}
+
+AstTyped* ast_reduce(bh_allocator a, AstTyped* node) {
+ assert(node->flags & Ast_Flag_Comptime);
+
+ switch (node->kind) {
+ case Ast_Kind_Binary_Op: return (AstTyped *) ast_reduce_binop(a, (AstBinaryOp *) node);
+ case Ast_Kind_Unary_Op: return (AstTyped *) ast_reduce_unaryop(a, (AstUnaryOp *) node);
+ case Ast_Kind_Enum_Value: return (AstTyped *) ((AstEnumValue *) node)->value;
+ case Ast_Kind_Alias: return (AstTyped *) ast_reduce(a, ((AstAlias *) node)->alias);
+ default: return node;
+ }
+}
+
+void promote_numlit_to_larger(AstNumLit* num) {
+ assert(num->type != NULL);
+
+ if (type_is_integer(num->type) && num->type->Basic.size <= 4) {
+ // NOTE: Int32, Int16, Int8
+ i64 val = (i64) num->value.i;
+ num->value.l = val;
+ num->type = &basic_types[Basic_Kind_I64];
+ } else if (num->type->Basic.size <= 4) {
+ // NOTE: Float32
+ f64 val = (f64) num->value.f;
+ num->value.d = val;
+ num->type = &basic_types[Basic_Kind_F64];
+ }
+}
+
+// NOTE: Returns 1 if the conversion was successful.
+b32 convert_numlit_to_type(AstNumLit* num, Type* type) {
+ if (num->type == NULL)
+ num->type = type_build_from_ast(context.ast_alloc, num->type_node);
+ assert(num->type);
+
+ if (types_are_compatible(num->type, type)) return 1;
+ if (!type_is_numeric(type)) return 0;
+
+ if (num->type->Basic.kind == Basic_Kind_Int_Unsized) {
+
+ //
+ // Integer literal auto cast rules:
+ // - Up in size always works
+ // - Down in size only works if value is in range of smaller type.
+ // - Cast to float only works if value is less than the maximum precise value for float size.
+ //
+
+ if (type->Basic.flags & Basic_Flag_Integer) {
+ if (type->Basic.flags & Basic_Flag_Unsigned) {
+ u64 value = (u64) num->value.l;
+ if (type->Basic.size == 8) {
+ num->type = type;
+ return 1;
+ }
+ switch (type->Basic.size) {
+ case 1: if (value <= 255) {
+ num->type = type;
+ return 1;
+ }
+ case 2: if (value <= 65535) {
+ num->type = type;
+ return 1;
+ }
+ case 4: if (value <= 4294967295) {
+ num->type = type;
+ return 1;
+ }
+ }
+
+ onyx_report_error(num->token->pos, "Unsigned integer constant with value '%l' does not fit into %d-bits.",
+ num->value.l,
+ type->Basic.size * 8);
+
+ } else {
+ i64 value = (i64) num->value.l;
+ switch (type->Basic.size) {
+ case 1: if (-128ll <= value && value <= 127ll) {
+ num->value.i = (i32) value;
+ num->type = type;
+ return 1;
+ } break;
+ case 2: if (-32768ll <= value && value <= 32767ll) {
+ num->value.i = (i32) value;
+ num->type = type;
+ return 1;
+ } break;
+ case 4: if (-2147483648ll <= value && value <= 2147483647ll) {
+ num->value.i = (i32) value;
+ num->type = type;
+ return 1;
+ } break;
+ case 8: { num->type = type;
+ return 1;
+ } break;
+ }
+
+ onyx_report_error(num->token->pos, "Integer constant with value '%l' does not fit into %d-bits.",
+ num->value.l,
+ type->Basic.size * 8);
+ }
+ }
+
+ else if (type->Basic.flags & Basic_Flag_Float) {
+ if (type->Basic.size == 4) {
+ // TODO(Brendan): Check these boundary conditions
+ if (bh_abs(num->value.l) >= (1 << 23)) {
+ onyx_report_error(num->token->pos, "Integer '%l' does not fit in 32-bit float exactly.", num->value.l);
+ return 0;
+ }
+
+ num->type = type;
+ num->value.f = (f32) num->value.l;
+ return 1;
+ }
+ if (type->Basic.size == 8) {
+ // TODO(Brendan): Check these boundary conditions
+ if (bh_abs(num->value.l) >= (1ull << 52)) {
+ onyx_report_error(num->token->pos, "Integer '%l' does not fit in 64-bit float exactly.", num->value.l);
+ return 0;
+ }
+
+ num->type = type;
+ num->value.d = (f64) num->value.l;
+ return 1;
+ }
+ }
+ }
+ else if (num->type->Basic.kind == Basic_Kind_Float_Unsized) {
+ // NOTE: Floats don't cast to integers implicitly.
+ if ((type->Basic.flags & Basic_Flag_Float) == 0) return 0;
+
+ if (type->Basic.kind == Basic_Kind_F32) {
+ num->value.f = (f32) num->value.d;
+ }
+
+ num->type = type;
+ return 1;
+ }
+ else if (num->type->Basic.kind == Basic_Kind_F32) {
+ // NOTE: Floats don't cast to integers implicitly.
+ if ((type->Basic.flags & Basic_Flag_Float) == 0) return 0;
+
+ if (type->Basic.kind == Basic_Kind_F64) {
+ num->value.d = (f64) num->value.f;
+ num->type = type;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+// NOTE: Returns 0 if it was not possible to make the types compatible.
+b32 type_check_or_auto_cast(AstTyped** pnode, Type* type) {
+ AstTyped* node = *pnode;
+ if (type == NULL) return 0;
+ if (node == NULL) return 0;
+
+ // if (node_is_type((AstNode *) node)) return 0;
+
+ if (node->kind == Ast_Kind_Struct_Literal && node->type_node == NULL) {
+ if (node->entity != NULL) return 1;
+ if (type->kind == Type_Kind_VarArgs) type = type->VarArgs.ptr_to_data->Pointer.elem;
+ if (!type_is_sl_constructable(type)) return 0;
+
+ node->type = type;
+
+ add_entities_for_node(NULL, (AstNode *) node, NULL, NULL);
+ return 1;
+ }
+
+ if (node->kind == Ast_Kind_Array_Literal && node->type_node == NULL) {
+ if (node->entity != NULL) return 1;
+ node->type = type;
+ node->flags |= Ast_Flag_Array_Literal_Typed;
+
+ add_entities_for_node(NULL, (AstNode *) node, NULL, NULL);
+ return 1;
+ }
+
+ if (node->kind == Ast_Kind_Unary_Field_Access) {
+ AstType* ast_type = type->ast_type;
+ AstNode* resolved = try_symbol_resolve_from_node((AstNode *) ast_type, node->token);
+ if (resolved == NULL) return 0;
+
+ *pnode = (AstTyped *) resolved;
+ return 1;
+ }
+
+ if (node->kind == Ast_Kind_Overloaded_Function) {
+ AstTyped* func = find_matching_overload_by_type(((AstOverloadedFunction *) node)->overloads, type);
+ if (func == NULL) return 0;
+
+ // HACK: It feels like there should be a better place to flag that a procedure was definitely used.
+ if (func->kind == Ast_Kind_Function)
+ func->flags |= Ast_Flag_Function_Used;
+
+ *pnode = func;
+ node = *pnode;
+ }
+
+ if (node->kind == Ast_Kind_Polymorphic_Proc) {
+ AstFunction* func = polymorphic_proc_lookup((AstPolyProc *) node, PPLM_By_Function_Type, type, node->token);
+ if (func == NULL) return 0;
+
+ *pnode = (AstTyped *) func;
+ node = *pnode;
+ }
+
+ // HACK: NullProcHack
+ if (type->kind == Type_Kind_Function && (node->flags & Ast_Flag_Proc_Is_Null) != 0) return 1;
+
+ if (types_are_compatible(node->type, type)) return 1;
+ // :AutoReturnType
+ if (node->type && node->type->kind == Type_Kind_Function
+ && node->type->Function.return_type == &type_auto_return
+ && type->kind == Type_Kind_Function) {
+
+ node->type->Function.return_type = type->Function.return_type;
+ return 1;
+ }
+ if (node_is_auto_cast((AstNode *) node)) {
+ char* dummy;
+ Type* from_type = ((AstUnaryOp *) node)->expr->type;
+ if (!from_type || !cast_is_legal(from_type, type, &dummy)) {
+ return 0;
+
+ } else {
+ ((AstUnaryOp *) node)->type = type;
+ return 1;
+ }
+ }
+ else if (node->kind == Ast_Kind_NumLit) {
+ if (convert_numlit_to_type((AstNumLit *) node, type)) return 1;
+ }
+ else if (node->kind == Ast_Kind_Compound) {
+ if (type->kind != Type_Kind_Compound) return 0;
+
+ AstCompound* compound = (AstCompound *) node;
+
+ u32 expr_count = bh_arr_length(compound->exprs);
+ if (expr_count != type->Compound.count) return 0;
+
+ fori (i, 0, (i64) expr_count) {
+ if (!type_check_or_auto_cast(&compound->exprs[i], type->Compound.types[i])) return 0;
+ }
+
+ compound->type = type_build_compound_type(context.ast_alloc, compound);
+
+ return 1;
+ }
+ else if (node->kind == Ast_Kind_If_Expression) {
+ AstIfExpression* if_expr = (AstIfExpression *) node;
+
+ b32 true_success = type_check_or_auto_cast(&if_expr->true_expr, type);
+ b32 false_success = type_check_or_auto_cast(&if_expr->false_expr, type);
+
+ if (true_success && false_success) {
+ if_expr->type = type;
+ return 1;
+
+ } else {
+ return 0;
+ }
+ }
+ else if (node->kind == Ast_Kind_Alias) {
+ AstAlias* alias = (AstAlias *) node;
+ return type_check_or_auto_cast(&alias->alias, type);
+ }
+
+ return 0;
+}
+
+Type* resolve_expression_type(AstTyped* node) {
+ if (node == NULL) return NULL;
+
+ if (node->kind == Ast_Kind_Compound) {
+ bh_arr_each(AstTyped *, expr, ((AstCompound *) node)->exprs) {
+ resolve_expression_type(*expr);
+ }
+ }
+
+ if (node->kind == Ast_Kind_Argument) {
+ node->type = resolve_expression_type(((AstArgument *) node)->value);
+ }
+
+ if (node->kind == Ast_Kind_If_Expression) {
+ AstIfExpression* if_expr = (AstIfExpression *) node;
+
+ Type* ltype = resolve_expression_type(if_expr->true_expr);
+ type_check_or_auto_cast(&if_expr->false_expr, ltype);
+
+ if_expr->type = ltype;
+ }
+
+ if (node->kind == Ast_Kind_Alias) {
+ AstAlias* alias = (AstAlias *) node;
+ alias->type = resolve_expression_type(alias->alias);
+ }
+
+ if (node_is_type((AstNode *) node)) {
+ return &basic_types[Basic_Kind_Type_Index];
+ }
+
+ if (node->type == NULL)
+ node->type = type_build_from_ast(context.ast_alloc, node->type_node);
+
+ if (node->kind == Ast_Kind_NumLit && node->type->kind == Type_Kind_Basic) {
+ if (node->type->Basic.kind == Basic_Kind_Int_Unsized) {
+ if (bh_abs(((AstNumLit *) node)->value.l) >= (1ull << 32))
+ convert_numlit_to_type((AstNumLit *) node, &basic_types[Basic_Kind_I64]);
+ else
+ convert_numlit_to_type((AstNumLit *) node, &basic_types[Basic_Kind_I32]);
+ }
+ else if (node->type->Basic.kind == Basic_Kind_Float_Unsized) {
+ convert_numlit_to_type((AstNumLit *) node, &basic_types[Basic_Kind_F64]);
+ }
+ }
+
+ return node->type;
+}
+
+i64 get_expression_integer_value(AstTyped* node) {
+ resolve_expression_type(node);
+
+ if (node->kind == Ast_Kind_NumLit && type_is_integer(node->type)) {
+ return ((AstNumLit *) node)->value.l;
+ }
+
+ if (node->kind == Ast_Kind_NumLit && type_is_bool(node->type)) {
+ return ((AstNumLit *) node)->value.i;
+ }
+
+ if (node->kind == Ast_Kind_Argument) {
+ return get_expression_integer_value(((AstArgument *) node)->value);
+ }
+
+ if (node->kind == Ast_Kind_Size_Of) {
+ return ((AstSizeOf *) node)->size;
+ }
+
+ if (node->kind == Ast_Kind_Align_Of) {
+ return ((AstAlignOf *) node)->alignment;
+ }
+
+ if (node->kind == Ast_Kind_Alias) {
+ return get_expression_integer_value(((AstAlias *) node)->alias);
+ }
+
+ if (node_is_type((AstNode*) node)) {
+ Type* type = type_build_from_ast(context.ast_alloc, (AstType *) node);
+ return type->id;
+ }
+
+ return 0;
+}
+
+static const b32 cast_legality[][12] = {
+ /* I8 */ { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
+ /* U8 */ { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
+ /* I16 */ { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
+ /* U16 */ { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
+ /* I32 */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+ /* U32 */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+ /* I64 */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 },
+ /* U64 */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 },
+ /* F32 */ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0 },
+ /* F64 */ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0 },
+ /* PTR */ { 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0 },
+ /* TYP */ { 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1,}
+};
+
+b32 cast_is_legal(Type* from_, Type* to_, char** err_msg) {
+ Type* from = from_;
+ Type* to = to_;
+
+ if (from == NULL) {
+ if (err_msg) *err_msg = "'from' is null. (Compiler Error)";
+ return 0;
+ }
+ if (to == NULL) {
+ if (err_msg) *err_msg = "'to' is null. (Compiler Error)";
+ return 0;
+ }
+
+ if (from->kind == Type_Kind_Enum) from = from->Enum.backing;
+ if (to->kind == Type_Kind_Enum) to = to->Enum.backing;
+
+ if (from->kind == Type_Kind_Struct || to->kind == Type_Kind_Struct) {
+ *err_msg = "Cannot cast to or from a struct.";
+ return 0;
+ }
+
+ if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_Array) {
+ if (!types_are_compatible(to->Slice.ptr_to_data->Pointer.elem, from->Array.elem)) {
+ *err_msg = "Array to slice cast is not valid here because the types are different.";
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+
+ if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_VarArgs) {
+ if (!types_are_compatible(to->Slice.ptr_to_data->Pointer.elem, from->VarArgs.ptr_to_data->Pointer.elem)) {
+ *err_msg = "Variadic argument to slice cast is not valid here because the types are different.";
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+
+ if (from->kind == Type_Kind_Slice || to->kind == Type_Kind_Slice) {
+ *err_msg = "Cannot cast to or from a slice.";
+ return 0;
+ }
+
+ if (from->kind == Type_Kind_DynArray || to->kind == Type_Kind_DynArray) {
+ *err_msg = "Cannot cast to or from a dynamic array.";
+ return 0;
+ }
+
+ if (to->kind == Type_Kind_Function) {
+ *err_msg = "Cannot cast to a function.";
+ return 0;
+ }
+
+ if ( (type_is_simd(to) && !type_is_simd(from))
+ || (!type_is_simd(to) && type_is_simd(from))) {
+ *err_msg = "Can only perform a SIMD cast between SIMD types.";
+ return 0;
+ }
+
+ if (from->kind == Type_Kind_Basic && from->Basic.kind == Basic_Kind_Void) {
+ *err_msg = "Cannot cast from void.";
+ return 0;
+ }
+ i32 fromidx = -1, toidx = -1;
+ if (from->Basic.flags & Basic_Flag_Pointer || from->kind == Type_Kind_Array) {
+ fromidx = 10;
+ }
+ else if (from->Basic.flags & Basic_Flag_Integer) {
+ b32 unsign = (from->Basic.flags & Basic_Flag_Unsigned) != 0;
+
+ fromidx = log2_dumb(from->Basic.size) * 2 + unsign;
+ }
+ else if (from->Basic.flags & Basic_Flag_Float) {
+ if (from->Basic.size == 4) fromidx = 8;
+ else if (from->Basic.size == 8) fromidx = 9;
+ }
+ else if (from->Basic.flags & Basic_Flag_Boolean) {
+ fromidx = 0;
+ }
+ else if (from->Basic.flags & Basic_Flag_Type_Index) {
+ fromidx = 11;
+ }
+
+ if (to->Basic.flags & Basic_Flag_Pointer || to->kind == Type_Kind_Array) {
+ toidx = 10;
+ }
+ else if (to->Basic.flags & Basic_Flag_Integer) {
+ b32 unsign = (to->Basic.flags & Basic_Flag_Unsigned) != 0;
+
+ toidx = log2_dumb(to->Basic.size) * 2 + unsign;
+ }
+ else if (to->Basic.flags & Basic_Flag_Float) {
+ if (to->Basic.size == 4) toidx = 8;
+ else if (to->Basic.size == 8) toidx = 9;
+ }
+ else if (to->Basic.flags & Basic_Flag_Boolean) {
+ toidx = 0;
+ }
+ else if (to->Basic.flags & Basic_Flag_Type_Index) {
+ toidx = 11;
+ }
+
+ if (fromidx != -1 && toidx != -1) {
+ if (!cast_legality[fromidx][toidx]) {
+ *err_msg = bh_aprintf(global_heap_allocator, "Cast from '%s' to '%s' is not allowed.", type_get_name(from_), type_get_name(to_));
+ return 0;
+ }
+ } else {
+ *err_msg = bh_aprintf(global_heap_allocator, "Cast from '%s' to '%s' is not allowed.", type_get_name(from_), type_get_name(to_));
+ return 0;
+ }
+
+ *err_msg = NULL;
+ return 1;
+}
+
+char* get_function_name(AstFunction* func) {
+ if (func->kind != Ast_Kind_Function) return "<procedure>";
+
+ if (func->name != NULL) {
+ return bh_aprintf(global_scratch_allocator, "%b", func->name->text, func->name->length);
+ }
+
+ if (func->exported_name != NULL) {
+ return bh_aprintf(global_scratch_allocator,
+ "EXPORTED:%b",
+ func->exported_name->text,
+ func->exported_name->length);
+ }
+
+ return "<anonymous procedure>";
+}
+
+AstNode* strip_aliases(AstNode* n) {
+ if (n == NULL) return n;
+
+ while (n->kind == Ast_Kind_Alias) n = (AstNode *) ((AstAlias *) n)->alias;
+
+ return n;
+}
+
+AstNumLit* make_bool_literal(bh_allocator a, b32 b) {
+ AstNumLit* bl = onyx_ast_node_new(a, sizeof(AstNumLit), Ast_Kind_NumLit);
+ bl->flags |= Ast_Flag_Comptime;
+ bl->type_node = (AstType *) &basic_type_bool;
+
+ bl->value.i = b ? 1 : 0;
+ return bl;
+}
+
+AstNumLit* make_int_literal(bh_allocator a, i64 i) {
+ AstNumLit* num = onyx_ast_node_new(a, sizeof(AstNumLit), Ast_Kind_NumLit);
+ num->flags |= Ast_Flag_Comptime;
+
+ if (bh_abs(i) >= ((u64) 1 << 32))
+ num->type_node = (AstType *) &basic_type_i64;
+ else
+ num->type_node = (AstType *) &basic_type_i32;
+
+ num->value.l = i;
+ return num;
+}
+
+AstNumLit* make_float_literal(bh_allocator a, f64 d) {
+ // NOTE: Use convert_numlit_to_type to make this a concrete float
+ AstNumLit* num = onyx_ast_node_new(a, sizeof(AstNumLit), Ast_Kind_NumLit);
+ num->flags |= Ast_Flag_Comptime;
+ num->type_node = (AstType *) &basic_type_float_unsized;
+ num->value.d = d;
+ return num;
+}
+
+AstRangeLiteral* make_range_literal(bh_allocator a, AstTyped* low, AstTyped* high) {
+ AstRangeLiteral* rl = onyx_ast_node_new(a, sizeof(AstRangeLiteral), Ast_Kind_Range_Literal);
+ rl->type = builtin_range_type_type;
+ rl->low = low;
+ rl->high = high;
+ return rl;
+}
+
+AstBinaryOp* make_binary_op(bh_allocator a, BinaryOp operation, AstTyped* left, AstTyped* right) {
+ AstBinaryOp* binop_node = onyx_ast_node_new(a, sizeof(AstBinaryOp), Ast_Kind_Binary_Op);
+ binop_node->left = left;
+ binop_node->right = right;
+ binop_node->operation = operation;
+ return binop_node;
+}
+
+AstArgument* make_argument(bh_allocator a, AstTyped* value) {
+ AstArgument* arg = onyx_ast_node_new(a, sizeof(AstArgument), Ast_Kind_Argument);
+ if (value->token) arg->token = value->token;
+ arg->value = value;
+ arg->type = value->type;
+ arg->next = NULL;
+ arg->va_kind = VA_Kind_Not_VA;
+ return arg;
+}
+
+AstFieldAccess* make_field_access(bh_allocator a, AstTyped* node, char* field) {
+ AstFieldAccess* fa = onyx_ast_node_new(a, sizeof(AstFieldAccess), Ast_Kind_Field_Access);
+ if (node->token) fa->token = node->token;
+ fa->field = field;
+ fa->expr = node;
+
+ return fa;
+}
+
+AstAddressOf* make_address_of(bh_allocator a, AstTyped* node) {
+ AstAddressOf* ao = onyx_ast_node_new(a, sizeof(AstAddressOf), Ast_Kind_Address_Of);
+ if (node->token) ao->token = node->token;
+ ao->expr = node;
+
+ return ao;
+}
+
+AstLocal* make_local(bh_allocator a, OnyxToken* token, AstType* type_node) {
+ AstLocal* local = onyx_ast_node_new(a, sizeof(AstLocal), Ast_Kind_Local);
+ local->token = token;
+ local->type_node = type_node;
+
+ return local;
+}
+
+AstNode* make_symbol(bh_allocator a, OnyxToken* sym) {
+ AstNode* symbol = onyx_ast_node_new(a, sizeof(AstNode), Ast_Kind_Symbol);
+ symbol->token = sym;
+ return symbol;
+}
+
+void arguments_initialize(Arguments* args) {
+ if (args->values == NULL) bh_arr_new(global_heap_allocator, args->values, 2);
+ if (args->named_values == NULL) bh_arr_new(global_heap_allocator, args->named_values, 2);
+
+ // CLEANUP: I'm not sure if I need to initialize these to NULL values, but it doesn't hurt.
+ fori (i, 0, 2) {
+ args->values[i] = NULL;
+ args->named_values[i] = NULL;
+ }
+}
+
+void arguments_ensure_length(Arguments* args, u32 count) {
+ // Make the array big enough
+ bh_arr_grow(args->values, count);
+
+ // NULL initialize the new elements
+ fori (i, bh_arr_length(args->values), count) args->values[i] = NULL;
+
+ // Set the actual length to the count, but never let it decrease in size
+ bh_arr_set_length(args->values, bh_max(count, (u32) bh_arr_length(args->values)));
+}
+
+void arguments_copy(Arguments* dest, Arguments* src) {
+ dest->named_values = src->named_values;
+
+ arguments_ensure_length(dest, bh_arr_length(src->values));
+ bh_arr_each(AstTyped*, arg, dest->values) *arg = NULL;
+ fori (i, 0, bh_arr_length(src->values)) dest->values[i] = src->values[i];
+}
+
+// In clone, the named_values are not copied. This is used in find_matching_overload_by_arguments since it doesn't need them to be copied.
+void arguments_clone(Arguments* dest, Arguments* src) {
+ dest->named_values = src->named_values;
+ dest->values = bh_arr_copy(global_heap_allocator, src->values);
+}
+
+void arguments_deep_clone(bh_allocator a, Arguments* dest, Arguments* src) {
+ dest->values = NULL;
+ dest->named_values = NULL;
+
+ bh_arr_new(global_heap_allocator, dest->named_values, bh_arr_length(src->named_values));
+ bh_arr_new(global_heap_allocator, dest->values, bh_arr_length(src->values));
+
+ bh_arr_each(AstNamedValue *, nv, src->named_values)
+ bh_arr_push(dest->named_values, (AstNamedValue *) ast_clone(a, *nv));
+
+ bh_arr_each(AstTyped *, val, src->values)
+ bh_arr_push(dest->values, (AstTyped *) ast_clone(a, (AstNode *) *val));
+}
+
+void arguments_remove_baked(Arguments* args) {
+ fori (i, 0, bh_arr_length(args->values)) {
+ if (args->values[i]->kind != Ast_Kind_Argument) continue;
+ if (!((AstArgument *) args->values[i])->is_baked) continue;
+
+ bh_arr_deleten(args->values, i, 1);
+ i--;
+ }
+
+ fori (i, 0, bh_arr_length(args->named_values)) {
+ if (args->named_values[i]->value->kind != Ast_Kind_Argument) continue;
+ if (!((AstArgument *) args->named_values[i]->value)->is_baked) continue;
+
+ bh_arr_deleten(args->named_values, i, 1);
+ i--;
+ }
+}
+
+// GROSS: Using void* to avoid having to cast everything.
+const char* node_get_type_name(void* node) {
+ if (node_is_type((AstNode *) node)) return "type_expr";
+
+ if (((AstNode *) node)->kind == Ast_Kind_Argument) {
+ return node_get_type_name(((AstArgument *) node)->value);
+ }
+
+ if (((AstNode *) node)->kind == Ast_Kind_Polymorphic_Proc) {
+ return "polymorphic procedure";
+ }
+
+ return type_get_name(((AstTyped *) node)->type);
+}
+
+b32 static_if_resolution(AstIf* static_if) {
+ if (static_if->kind != Ast_Kind_Static_If) return 0;
+
+ // assert(condition_value->kind == Ast_Kind_NumLit); // This should be right, right?
+ i64 value = get_expression_integer_value(static_if->cond);
+
+ return value != 0;
+}
--- /dev/null
+#include "astnodes.h"
+#include "types.h"
+#include "errors.h"
+#include "utils.h"
+
+AstBasicType basic_type_void = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_Void] };
+AstBasicType basic_type_bool = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_Bool] };
+AstBasicType basic_type_i8 = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_I8] };
+AstBasicType basic_type_u8 = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_U8] };
+AstBasicType basic_type_i16 = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_I16] };
+AstBasicType basic_type_u16 = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_U16] };
+AstBasicType basic_type_i32 = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_I32] };
+AstBasicType basic_type_u32 = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_U32] };
+AstBasicType basic_type_i64 = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_I64] };
+AstBasicType basic_type_u64 = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_U64] };
+AstBasicType basic_type_f32 = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_F32] };
+AstBasicType basic_type_f64 = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_F64] };
+AstBasicType basic_type_rawptr = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_Rawptr] };
+AstBasicType basic_type_type_expr = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_Type_Index] };
+
+// NOTE: Types used for numeric literals
+AstBasicType basic_type_int_unsized = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_Int_Unsized] };
+AstBasicType basic_type_float_unsized = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_Float_Unsized] };
+
+static OnyxToken simd_token = { Token_Type_Symbol, 0, "", { 0 } };
+AstBasicType basic_type_i8x16 = { Ast_Kind_Basic_Type, 0, &simd_token, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_I8X16] };
+AstBasicType basic_type_i16x8 = { Ast_Kind_Basic_Type, 0, &simd_token, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_I16X8] };
+AstBasicType basic_type_i32x4 = { Ast_Kind_Basic_Type, 0, &simd_token, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_I32X4] };
+AstBasicType basic_type_i64x2 = { Ast_Kind_Basic_Type, 0, &simd_token, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_I64X2] };
+AstBasicType basic_type_f32x4 = { Ast_Kind_Basic_Type, 0, &simd_token, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_F32X4] };
+AstBasicType basic_type_f64x2 = { Ast_Kind_Basic_Type, 0, &simd_token, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_F64X2] };
+AstBasicType basic_type_v128 = { Ast_Kind_Basic_Type, 0, &simd_token, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_V128] };
+
+// HACK
+// :AutoReturnType
+Type type_auto_return = { 0 };
+AstBasicType basic_type_auto_return = { Ast_Kind_Basic_Type, 0, &simd_token, NULL, 0, NULL, NULL, &type_auto_return };
+
+OnyxToken builtin_package_token = { Token_Type_Symbol, 7, "builtin ", { 0 } };
+
+static OnyxToken builtin_heap_start_token = { Token_Type_Symbol, 12, "__heap_start ", { 0 } };
+static OnyxToken builtin_stack_top_token = { Token_Type_Symbol, 11, "__stack_top ", { 0 } };
+AstNumLit builtin_heap_start = { Ast_Kind_NumLit, Ast_Flag_Const, &builtin_heap_start_token, NULL, NULL, (AstType *) &basic_type_rawptr, NULL, 0 };
+AstGlobal builtin_stack_top = { Ast_Kind_Global, Ast_Flag_Const | Ast_Flag_Global_Stack_Top, &builtin_stack_top_token, NULL, NULL, (AstType *) &basic_type_rawptr, NULL };
+
+AstType *builtin_string_type;
+AstType *builtin_range_type;
+Type *builtin_range_type_type;
+AstType *builtin_vararg_type;
+Type *builtin_vararg_type_type;
+AstTyped *builtin_context_variable;
+AstType *builtin_allocator_type;
+AstType *builtin_iterator_type;
+AstType *builtin_callsite_type;
+AstType *builtin_any_type;
+AstType *builtin_code_type;
+
+AstTyped *type_table_node = NULL;
+
+const BuiltinSymbol builtin_symbols[] = {
+ { NULL, "void", (AstNode *) &basic_type_void },
+ { NULL, "bool", (AstNode *) &basic_type_bool },
+ { NULL, "i8", (AstNode *) &basic_type_i8 },
+ { NULL, "u8", (AstNode *) &basic_type_u8 },
+ { NULL, "i16", (AstNode *) &basic_type_i16 },
+ { NULL, "u16", (AstNode *) &basic_type_u16 },
+ { NULL, "i32", (AstNode *) &basic_type_i32 },
+ { NULL, "u32", (AstNode *) &basic_type_u32 },
+ { NULL, "i64", (AstNode *) &basic_type_i64 },
+ { NULL, "u64", (AstNode *) &basic_type_u64 },
+ { NULL, "f32", (AstNode *) &basic_type_f32 },
+ { NULL, "f64", (AstNode *) &basic_type_f64 },
+ { NULL, "rawptr", (AstNode *) &basic_type_rawptr },
+ { NULL, "type_expr", (AstNode *) &basic_type_type_expr },
+
+ { "simd", "i8x16", (AstNode *) &basic_type_i8x16 },
+ { "simd", "i16x8", (AstNode *) &basic_type_i16x8 },
+ { "simd", "i32x4", (AstNode *) &basic_type_i32x4 },
+ { "simd", "i64x2", (AstNode *) &basic_type_i64x2 },
+ { "simd", "f32x4", (AstNode *) &basic_type_f32x4 },
+ { "simd", "f64x2", (AstNode *) &basic_type_f64x2 },
+ { "simd", "v128", (AstNode *) &basic_type_v128 },
+
+ { "builtin", "__heap_start", (AstNode *) &builtin_heap_start },
+ { "builtin", "__stack_top", (AstNode *) &builtin_stack_top },
+
+ { NULL, NULL, NULL },
+};
+
+bh_table(OnyxIntrinsic) intrinsic_table;
+
+static IntrinsicMap builtin_intrinsics[] = {
+ { "memory_size", ONYX_INTRINSIC_MEMORY_SIZE },
+ { "memory_grow", ONYX_INTRINSIC_MEMORY_GROW },
+ { "memory_copy", ONYX_INTRINSIC_MEMORY_COPY },
+ { "memory_fill", ONYX_INTRINSIC_MEMORY_FILL },
+
+ { "__initialize", ONYX_INTRINSIC_INITIALIZE },
+ { "__zero_value", ONYX_INTRINSIC_ZERO_VALUE },
+
+ { "clz_i32", ONYX_INTRINSIC_I32_CLZ },
+ { "ctz_i32", ONYX_INTRINSIC_I32_CTZ },
+ { "popcnt_i32", ONYX_INTRINSIC_I32_POPCNT },
+ { "and_i32", ONYX_INTRINSIC_I32_AND },
+ { "or_i32", ONYX_INTRINSIC_I32_OR },
+ { "xor_i32", ONYX_INTRINSIC_I32_XOR },
+ { "shl_i32", ONYX_INTRINSIC_I32_SHL },
+ { "slr_i32", ONYX_INTRINSIC_I32_SLR },
+ { "sar_i32", ONYX_INTRINSIC_I32_SAR },
+ { "rotl_i32", ONYX_INTRINSIC_I32_ROTL },
+ { "rotr_i32", ONYX_INTRINSIC_I32_ROTR },
+
+ { "clz_i64", ONYX_INTRINSIC_I64_CLZ },
+ { "ctz_i64", ONYX_INTRINSIC_I64_CTZ },
+ { "popcnt_i64", ONYX_INTRINSIC_I64_POPCNT },
+ { "and_i64", ONYX_INTRINSIC_I64_AND },
+ { "or_i64", ONYX_INTRINSIC_I64_OR },
+ { "xor_i64", ONYX_INTRINSIC_I64_XOR },
+ { "shl_i64", ONYX_INTRINSIC_I64_SHL },
+ { "slr_i64", ONYX_INTRINSIC_I64_SLR },
+ { "sar_i64", ONYX_INTRINSIC_I64_SAR },
+ { "rotl_i64", ONYX_INTRINSIC_I64_ROTL },
+ { "rotr_i64", ONYX_INTRINSIC_I64_ROTR },
+
+ { "abs_f32", ONYX_INTRINSIC_F32_ABS },
+ { "ceil_f32", ONYX_INTRINSIC_F32_CEIL },
+ { "floor_f32", ONYX_INTRINSIC_F32_FLOOR },
+ { "trunc_f32", ONYX_INTRINSIC_F32_TRUNC },
+ { "nearest_f32", ONYX_INTRINSIC_F32_NEAREST },
+ { "sqrt_f32", ONYX_INTRINSIC_F32_SQRT },
+ { "min_f32", ONYX_INTRINSIC_F32_MIN },
+ { "max_f32", ONYX_INTRINSIC_F32_MAX },
+ { "copysign_f32", ONYX_INTRINSIC_F32_COPYSIGN },
+
+ { "abs_f64", ONYX_INTRINSIC_F64_ABS },
+ { "ceil_f64", ONYX_INTRINSIC_F64_CEIL },
+ { "floor_f64", ONYX_INTRINSIC_F64_FLOOR },
+ { "trunc_f64", ONYX_INTRINSIC_F64_TRUNC },
+ { "nearest_f64", ONYX_INTRINSIC_F64_NEAREST },
+ { "sqrt_f64", ONYX_INTRINSIC_F64_SQRT },
+ { "min_f64", ONYX_INTRINSIC_F64_MIN },
+ { "max_f64", ONYX_INTRINSIC_F64_MAX },
+ { "copysign_f64", ONYX_INTRINSIC_F64_COPYSIGN },
+
+
+ // SIMD Intrinsics
+ { "v128_const", ONYX_INTRINSIC_V128_CONST },
+ { "i8x16_const", ONYX_INTRINSIC_I8X16_CONST },
+ { "i16x8_const", ONYX_INTRINSIC_I16X8_CONST },
+ { "i32x4_const", ONYX_INTRINSIC_I32X4_CONST },
+ { "i64x2_const", ONYX_INTRINSIC_I64X2_CONST },
+ { "f32x4_const", ONYX_INTRINSIC_F32X4_CONST },
+ { "f64x2_const", ONYX_INTRINSIC_F64X2_CONST },
+ { "i8x16_shuffle", ONYX_INTRINSIC_I8X16_SHUFFLE },
+
+ { "i8x16_extract_lane_s", ONYX_INTRINSIC_I8X16_EXTRACT_LANE_S },
+ { "i8x16_extract_lane_u", ONYX_INTRINSIC_I8X16_EXTRACT_LANE_U },
+ { "i8x16_replace_lane", ONYX_INTRINSIC_I8X16_REPLACE_LANE },
+ { "i16x8_extract_lane_s", ONYX_INTRINSIC_I16X8_EXTRACT_LANE_S },
+ { "i16x8_extract_lane_u", ONYX_INTRINSIC_I16X8_EXTRACT_LANE_U },
+ { "i16x8_replace_lane", ONYX_INTRINSIC_I16X8_REPLACE_LANE },
+ { "i32x4_extract_lane", ONYX_INTRINSIC_I32X4_EXTRACT_LANE },
+ { "i32x4_replace_lane", ONYX_INTRINSIC_I32X4_REPLACE_LANE },
+ { "i64x2_extract_lane", ONYX_INTRINSIC_I64X2_EXTRACT_LANE },
+ { "i64x2_replace_lane", ONYX_INTRINSIC_I64X2_REPLACE_LANE },
+ { "f32x4_extract_lane", ONYX_INTRINSIC_F32X4_EXTRACT_LANE },
+ { "f32x4_replace_lane", ONYX_INTRINSIC_F32X4_REPLACE_LANE },
+ { "f64x2_extract_lane", ONYX_INTRINSIC_F64X2_EXTRACT_LANE },
+ { "f64x2_replace_lane", ONYX_INTRINSIC_F64X2_REPLACE_LANE },
+
+ { "i8x16_swizzle", ONYX_INTRINSIC_I8X16_SWIZZLE },
+ { "i8x16_splat", ONYX_INTRINSIC_I8X16_SPLAT },
+ { "i16x8_splat", ONYX_INTRINSIC_I16X8_SPLAT },
+ { "i32x4_splat", ONYX_INTRINSIC_I32X4_SPLAT },
+ { "i64x2_splat", ONYX_INTRINSIC_I64X2_SPLAT },
+ { "f32x4_splat", ONYX_INTRINSIC_F32X4_SPLAT },
+ { "f64x2_splat", ONYX_INTRINSIC_F64X2_SPLAT },
+
+ { "i8x16_eq", ONYX_INTRINSIC_I8X16_EQ },
+ { "i8x16_neq", ONYX_INTRINSIC_I8X16_NEQ },
+ { "i8x16_lt_s", ONYX_INTRINSIC_I8X16_LT_S },
+ { "i8x16_lt_u", ONYX_INTRINSIC_I8X16_LT_U },
+ { "i8x16_gt_s", ONYX_INTRINSIC_I8X16_GT_S },
+ { "i8x16_gt_u", ONYX_INTRINSIC_I8X16_GT_U },
+ { "i8x16_le_s", ONYX_INTRINSIC_I8X16_LE_S },
+ { "i8x16_le_u", ONYX_INTRINSIC_I8X16_LE_U },
+ { "i8x16_ge_s", ONYX_INTRINSIC_I8X16_GE_S },
+ { "i8x16_ge_u", ONYX_INTRINSIC_I8X16_GE_U },
+
+ { "i16x8_eq", ONYX_INTRINSIC_I16X8_EQ },
+ { "i16x8_neq", ONYX_INTRINSIC_I16X8_NEQ },
+ { "i16x8_lt_s", ONYX_INTRINSIC_I16X8_LT_S },
+ { "i16x8_lt_u", ONYX_INTRINSIC_I16X8_LT_U },
+ { "i16x8_gt_s", ONYX_INTRINSIC_I16X8_GT_S },
+ { "i16x8_gt_u", ONYX_INTRINSIC_I16X8_GT_U },
+ { "i16x8_le_s", ONYX_INTRINSIC_I16X8_LE_S },
+ { "i16x8_le_u", ONYX_INTRINSIC_I16X8_LE_U },
+ { "i16x8_ge_s", ONYX_INTRINSIC_I16X8_GE_S },
+ { "i16x8_ge_u", ONYX_INTRINSIC_I16X8_GE_U },
+
+ { "i32x4_eq", ONYX_INTRINSIC_I32X4_EQ },
+ { "i32x4_neq", ONYX_INTRINSIC_I32X4_NEQ },
+ { "i32x4_lt_s", ONYX_INTRINSIC_I32X4_LT_S },
+ { "i32x4_lt_u", ONYX_INTRINSIC_I32X4_LT_U },
+ { "i32x4_gt_s", ONYX_INTRINSIC_I32X4_GT_S },
+ { "i32x4_gt_u", ONYX_INTRINSIC_I32X4_GT_U },
+ { "i32x4_le_s", ONYX_INTRINSIC_I32X4_LE_S },
+ { "i32x4_le_u", ONYX_INTRINSIC_I32X4_LE_U },
+ { "i32x4_ge_s", ONYX_INTRINSIC_I32X4_GE_S },
+ { "i32x4_ge_u", ONYX_INTRINSIC_I32X4_GE_U },
+
+ { "f32x4_eq", ONYX_INTRINSIC_F32X4_EQ },
+ { "f32x4_neq", ONYX_INTRINSIC_F32X4_NEQ },
+ { "f32x4_lt", ONYX_INTRINSIC_F32X4_LT },
+ { "f32x4_gt", ONYX_INTRINSIC_F32X4_GT },
+ { "f32x4_le", ONYX_INTRINSIC_F32X4_LE },
+ { "f32x4_ge", ONYX_INTRINSIC_F32X4_GE },
+
+ { "f64x2_eq", ONYX_INTRINSIC_F64X2_EQ },
+ { "f64x2_neq", ONYX_INTRINSIC_F64X2_NEQ },
+ { "f64x2_lt", ONYX_INTRINSIC_F64X2_LT },
+ { "f64x2_gt", ONYX_INTRINSIC_F64X2_GT },
+ { "f64x2_le", ONYX_INTRINSIC_F64X2_LE },
+ { "f64x2_ge", ONYX_INTRINSIC_F64X2_GE },
+
+ { "v128_not", ONYX_INTRINSIC_V128_NOT },
+ { "v128_and", ONYX_INTRINSIC_V128_AND },
+ { "v128_andnot", ONYX_INTRINSIC_V128_ANDNOT },
+ { "v128_or", ONYX_INTRINSIC_V128_OR },
+ { "v128_xor", ONYX_INTRINSIC_V128_XOR },
+ { "v128_bitselect", ONYX_INTRINSIC_V128_BITSELECT },
+
+ { "i8x16_abs", ONYX_INTRINSIC_I8X16_ABS },
+ { "i8x16_neg", ONYX_INTRINSIC_I8X16_NEG },
+ { "i8x16_any_true", ONYX_INTRINSIC_I8X16_ANY_TRUE },
+ { "i8x16_all_true", ONYX_INTRINSIC_I8X16_ALL_TRUE },
+ { "i8x16_bitmask", ONYX_INTRINSIC_I8X16_BITMASK },
+ { "i8x16_narrow_i16x8_s", ONYX_INTRINSIC_I8X16_NARROW_I16X8_S },
+ { "i8x16_narrow_i16x8_u", ONYX_INTRINSIC_I8X16_NARROW_I16X8_U },
+ { "i8x16_shl", ONYX_INTRINSIC_I8X16_SHL },
+ { "i8x16_shr_s", ONYX_INTRINSIC_I8X16_SHR_S },
+ { "i8x16_shr_u", ONYX_INTRINSIC_I8X16_SHR_U },
+ { "i8x16_add", ONYX_INTRINSIC_I8X16_ADD },
+ { "i8x16_add_sat_s", ONYX_INTRINSIC_I8X16_ADD_SAT_S },
+ { "i8x16_add_sat_u", ONYX_INTRINSIC_I8X16_ADD_SAT_U },
+ { "i8x16_sub", ONYX_INTRINSIC_I8X16_SUB },
+ { "i8x16_sub_sat_s", ONYX_INTRINSIC_I8X16_SUB_SAT_S },
+ { "i8x16_sub_sat_u", ONYX_INTRINSIC_I8X16_SUB_SAT_U },
+ { "i8x16_min_s", ONYX_INTRINSIC_I8X16_MIN_S },
+ { "i8x16_min_u", ONYX_INTRINSIC_I8X16_MIN_U },
+ { "i8x16_max_s", ONYX_INTRINSIC_I8X16_MAX_S },
+ { "i8x16_max_u", ONYX_INTRINSIC_I8X16_MAX_U },
+ { "i8x16_avgr_u", ONYX_INTRINSIC_I8X16_AVGR_U },
+
+ { "i16x8_abs", ONYX_INTRINSIC_I16X8_ABS },
+ { "i16x8_neg", ONYX_INTRINSIC_I16X8_NEG },
+ { "i16x8_any_true", ONYX_INTRINSIC_I16X8_ANY_TRUE },
+ { "i16x8_all_true", ONYX_INTRINSIC_I16X8_ALL_TRUE },
+ { "i16x8_bitmask", ONYX_INTRINSIC_I16X8_BITMASK },
+ { "i16x8_narrow_i32x4_s", ONYX_INTRINSIC_I16X8_NARROW_I32X4_S },
+ { "i16x8_narrow_i32x4_u", ONYX_INTRINSIC_I16X8_NARROW_I32X4_U },
+ { "i16x8_widen_low_i8x16_s", ONYX_INTRINSIC_I16X8_WIDEN_LOW_I8X16_S },
+ { "i16x8_widen_high_i8x16_s", ONYX_INTRINSIC_I16X8_WIDEN_HIGH_I8X16_S },
+ { "i16x8_widen_low_i8x16_u", ONYX_INTRINSIC_I16X8_WIDEN_LOW_I8X16_U },
+ { "i16x8_widen_high_i8x16_u", ONYX_INTRINSIC_I16X8_WIDEN_HIGH_I8X16_U },
+ { "i16x8_shl", ONYX_INTRINSIC_I16X8_SHL },
+ { "i16x8_shr_s", ONYX_INTRINSIC_I16X8_SHR_S },
+ { "i16x8_shr_u", ONYX_INTRINSIC_I16X8_SHR_U },
+ { "i16x8_add", ONYX_INTRINSIC_I16X8_ADD },
+ { "i16x8_add_sat_s", ONYX_INTRINSIC_I16X8_ADD_SAT_S },
+ { "i16x8_add_sat_u", ONYX_INTRINSIC_I16X8_ADD_SAT_U },
+ { "i16x8_sub", ONYX_INTRINSIC_I16X8_SUB },
+ { "i16x8_sub_sat_s", ONYX_INTRINSIC_I16X8_SUB_SAT_S },
+ { "i16x8_sub_sat_u", ONYX_INTRINSIC_I16X8_SUB_SAT_U },
+ { "i16x8_mul", ONYX_INTRINSIC_I16X8_MUL },
+ { "i16x8_min_s", ONYX_INTRINSIC_I16X8_MIN_S },
+ { "i16x8_min_u", ONYX_INTRINSIC_I16X8_MIN_U },
+ { "i16x8_max_s", ONYX_INTRINSIC_I16X8_MAX_S },
+ { "i16x8_max_u", ONYX_INTRINSIC_I16X8_MAX_U },
+ { "i16x8_avgr_u", ONYX_INTRINSIC_I16X8_AVGR_U },
+
+ { "i32x4_abs", ONYX_INTRINSIC_I32X4_ABS },
+ { "i32x4_neg", ONYX_INTRINSIC_I32X4_NEG },
+ { "i32x4_any_true", ONYX_INTRINSIC_I32X4_ANY_TRUE },
+ { "i32x4_all_true", ONYX_INTRINSIC_I32X4_ALL_TRUE },
+ { "i32x4_bitmask", ONYX_INTRINSIC_I32X4_BITMASK },
+ { "i32x4_widen_low_i16x8_s", ONYX_INTRINSIC_I32X4_WIDEN_LOW_I16X8_S },
+ { "i32x4_widen_high_i16x8_s", ONYX_INTRINSIC_I32X4_WIDEN_HIGH_I16X8_S },
+ { "i32x4_widen_low_i16x8_u", ONYX_INTRINSIC_I32X4_WIDEN_LOW_I16X8_U },
+ { "i32x4_widen_high_i16x8_u", ONYX_INTRINSIC_I32X4_WIDEN_HIGH_I16X8_U },
+ { "i32x4_shl", ONYX_INTRINSIC_I32X4_SHL },
+ { "i32x4_shr_s", ONYX_INTRINSIC_I32X4_SHR_S },
+ { "i32x4_shl_u", ONYX_INTRINSIC_I32X4_SHR_U },
+ { "i32x4_add", ONYX_INTRINSIC_I32X4_ADD },
+ { "i32x4_sub", ONYX_INTRINSIC_I32X4_SUB },
+ { "i32x4_mul", ONYX_INTRINSIC_I32X4_MUL },
+ { "i32x4_min_s", ONYX_INTRINSIC_I32X4_MIN_S },
+ { "i32x4_min_u", ONYX_INTRINSIC_I32X4_MIN_U },
+ { "i32x4_max_s", ONYX_INTRINSIC_I32X4_MAX_S },
+ { "i32x4_max_u", ONYX_INTRINSIC_I32X4_MAX_U },
+
+ { "i64x2_neg", ONYX_INTRINSIC_I64X2_NEG },
+ { "i64x2_shl", ONYX_INTRINSIC_I64X2_SHL },
+ { "i64x2_shr_s", ONYX_INTRINSIC_I64X2_SHR_S },
+ { "i64x2_shr_u", ONYX_INTRINSIC_I64X2_SHR_U },
+ { "i64x2_add", ONYX_INTRINSIC_I64X2_ADD },
+ { "i64x2_sub", ONYX_INTRINSIC_I64X2_SUB },
+ { "i64x2_mul", ONYX_INTRINSIC_I64X2_MUL },
+
+ { "f32x4_abs", ONYX_INTRINSIC_F32X4_ABS },
+ { "f32x4_neg", ONYX_INTRINSIC_F32X4_NEG },
+ { "f32x4_sqrt", ONYX_INTRINSIC_F32X4_SQRT },
+ { "f32x4_add", ONYX_INTRINSIC_F32X4_ADD },
+ { "f32x4_sub", ONYX_INTRINSIC_F32X4_SUB },
+ { "f32x4_mul", ONYX_INTRINSIC_F32X4_MUL },
+ { "f32x4_div", ONYX_INTRINSIC_F32X4_DIV },
+ { "f32x4_min", ONYX_INTRINSIC_F32X4_MIN },
+ { "f32x4_max", ONYX_INTRINSIC_F32X4_MAX },
+
+ { "f64x2_abs", ONYX_INTRINSIC_F64X2_ABS },
+ { "f64x2_neg", ONYX_INTRINSIC_F64X2_NEG },
+ { "f64x2_sqrt", ONYX_INTRINSIC_F64X2_SQRT },
+ { "f64x2_add", ONYX_INTRINSIC_F64X2_ADD },
+ { "f64x2_sub", ONYX_INTRINSIC_F64X2_SUB },
+ { "f64x2_mul", ONYX_INTRINSIC_F64X2_MUL },
+ { "f64x2_div", ONYX_INTRINSIC_F64X2_DIV },
+ { "f64x2_min", ONYX_INTRINSIC_F64X2_MIN },
+ { "f64x2_max", ONYX_INTRINSIC_F64X2_MAX },
+
+ { "i32x4_trunc_sat_f32x4_s", ONYX_INTRINSIC_I32X4_TRUNC_SAT_F32X4_S },
+ { "i32x4_trunc_sat_f32x4_u", ONYX_INTRINSIC_I32X4_TRUNC_SAT_F32X4_U },
+ { "f32x4_convert_i32x4_s", ONYX_INTRINSIC_F32X4_CONVERT_I32X4_S },
+ { "f32x4_convert_i32x4_u", ONYX_INTRINSIC_F32X4_CONVERT_I32X4_U },
+
+ { NULL, ONYX_INTRINSIC_UNDEFINED },
+};
+
+bh_arr(OverloadOption) operator_overloads[Binary_Op_Count] = { 0 };
+
+void initialize_builtins(bh_allocator a) {
+ // HACK
+ builtin_package_token.text = bh_strdup(global_heap_allocator, builtin_package_token.text);
+
+ BuiltinSymbol* bsym = (BuiltinSymbol *) &builtin_symbols[0];
+ while (bsym->sym != NULL) {
+ if (bsym->package == NULL)
+ symbol_builtin_introduce(context.global_scope, bsym->sym, bsym->node);
+ else {
+ Package* p = package_lookup_or_create(bsym->package, context.global_scope, a);
+ assert(p);
+
+ symbol_builtin_introduce(p->scope, bsym->sym, bsym->node);
+ }
+ bsym++;
+ }
+
+ Package* p = package_lookup_or_create("builtin", context.global_scope, a);
+
+ builtin_string_type = (AstType *) symbol_raw_resolve(p->scope, "str");
+ if (builtin_string_type == NULL) {
+ onyx_report_error((OnyxFilePos) { 0 }, "'str' struct not found in builtin package.");
+ return;
+ }
+
+ builtin_range_type = (AstType *) symbol_raw_resolve(p->scope, "range");
+ if (builtin_range_type == NULL) {
+ onyx_report_error((OnyxFilePos) { 0 }, "'range' struct not found in builtin package.");
+ return;
+ }
+
+ builtin_vararg_type = (AstType *) symbol_raw_resolve(p->scope, "vararg");
+ if (builtin_range_type == NULL) {
+ onyx_report_error((OnyxFilePos) { 0 }, "'vararg' struct not found in builtin package.");
+ return;
+ }
+
+ builtin_context_variable = (AstTyped *) symbol_raw_resolve(p->scope, "context");
+ if (builtin_context_variable == NULL) {
+ onyx_report_error((OnyxFilePos) { 0 }, "'context' variable not found in builtin package.");
+ return;
+ }
+
+ builtin_allocator_type = (AstType *) symbol_raw_resolve(p->scope, "Allocator");
+ if (builtin_allocator_type == NULL) {
+ onyx_report_error((OnyxFilePos) { 0 }, "'Allocator' struct not found in builtin package.");
+ return;
+ }
+
+ builtin_iterator_type = (AstType *) symbol_raw_resolve(p->scope, "Iterator");
+ if (builtin_iterator_type == NULL) {
+ onyx_report_error((OnyxFilePos) { 0 }, "'Iterator' struct not found in builtin package.");
+ return;
+ }
+
+ builtin_callsite_type = (AstType *) symbol_raw_resolve(p->scope, "CallSite");
+ if (builtin_callsite_type == NULL) {
+ onyx_report_error((OnyxFilePos) { 0 }, "'CallSite' struct not found in builtin package.");
+ return;
+ }
+
+ builtin_any_type = (AstType *) symbol_raw_resolve(p->scope, "any");
+ if (builtin_any_type == NULL) {
+ onyx_report_error((OnyxFilePos) { 0 }, "'any' struct not found in builtin package.");
+ return;
+ }
+
+ builtin_code_type = (AstType *) symbol_raw_resolve(p->scope, "Code");
+ if (builtin_code_type == NULL) {
+ onyx_report_error((OnyxFilePos) { 0 }, "'Code' struct not found in builtin package.");
+ return;
+ }
+
+ p = package_lookup("builtin.type_info");
+ if (p != NULL) {
+ type_table_node = (AstTyped *) symbol_raw_resolve(p->scope, "type_table");
+ }
+
+ fori (i, 0, Binary_Op_Count) {
+ bh_arr_new(global_heap_allocator, operator_overloads[i], 4);
+ }
+
+ bh_table_init(global_heap_allocator, intrinsic_table, 128);
+ IntrinsicMap* intrinsic = &builtin_intrinsics[0];
+ while (intrinsic->name != NULL) {
+ bh_table_put(OnyxIntrinsic, intrinsic_table, intrinsic->name, intrinsic->intrinsic);
+ intrinsic++;
+ }
+}
+
+void introduce_build_options(bh_allocator a) {
+ Package* p = package_lookup_or_create("runtime", context.global_scope, a);
+
+ AstNumLit* runtime_type = make_int_literal(a, context.options->runtime);
+ symbol_builtin_introduce(p->scope, "Runtime", (AstNode *) runtime_type);
+}
--- /dev/null
+#define BH_DEBUG
+#include "parser.h"
+#include "utils.h"
+
+// All of the `check` functions return a boolean that signals if an issue
+// was reached while processing the node. These error booleans propagate
+// up the call stack until they reach `check_entity`.
+
+#define CHECK(kind, ...) do { \
+ CheckStatus cs = check_ ## kind (__VA_ARGS__); \
+ if (cs > Check_Errors_Start) return cs; \
+ } while (0)
+
+#define YIELD(loc, msg) do { \
+ if (context.cycle_detected) { \
+ onyx_report_error(loc, msg); \
+ return Check_Error; \
+ } else { \
+ return Check_Yield_Macro; \
+ } \
+ } while (0)
+
+#define YIELD_(loc, msg, ...) do { \
+ if (context.cycle_detected) { \
+ onyx_report_error(loc, msg, __VA_ARGS__); \
+ return Check_Error; \
+ } else { \
+ return Check_Yield_Macro; \
+ } \
+ } while (0)
+
+#define ERROR(loc, msg) do { \
+ onyx_report_error(loc, msg); \
+ return Check_Error; \
+ } while (0)
+
+#define ERROR_(loc, msg, ...) do { \
+ onyx_report_error(loc, msg, __VA_ARGS__); \
+ return Check_Error; \
+ } while (0)
+
+typedef enum CheckStatus {
+ Check_Success, // The node was successfully checked with out errors
+ Check_Complete, // The node is done processing
+
+ Check_Errors_Start,
+ Check_Return_To_Symres, // Return this node for further symres processing
+ Check_Yield_Macro,
+ Check_Error, // There was an error when checking the node
+} CheckStatus;
+
+CheckStatus check_block(AstBlock* block);
+CheckStatus check_statement_chain(AstNode** start);
+CheckStatus check_statement(AstNode** pstmt);
+CheckStatus check_return(AstReturn* retnode);
+CheckStatus check_if(AstIfWhile* ifnode);
+CheckStatus check_while(AstIfWhile* whilenode);
+CheckStatus check_for(AstFor* fornode);
+CheckStatus check_switch(AstSwitch* switchnode);
+CheckStatus check_call(AstCall** pcall);
+CheckStatus check_binaryop(AstBinaryOp** pbinop);
+CheckStatus check_unaryop(AstUnaryOp** punop);
+CheckStatus check_struct_literal(AstStructLiteral* sl);
+CheckStatus check_array_literal(AstArrayLiteral* al);
+CheckStatus check_range_literal(AstRangeLiteral** range);
+CheckStatus check_compound(AstCompound* compound);
+CheckStatus check_if_expression(AstIfExpression* if_expr);
+CheckStatus check_expression(AstTyped** expr);
+CheckStatus check_address_of(AstAddressOf* aof);
+CheckStatus check_dereference(AstDereference* deref);
+CheckStatus check_subscript(AstSubscript** paa);
+CheckStatus check_field_access(AstFieldAccess** pfield);
+CheckStatus check_method_call(AstBinaryOp** mcall);
+CheckStatus check_size_of(AstSizeOf* so);
+CheckStatus check_align_of(AstAlignOf* ao);
+CheckStatus check_global(AstGlobal* global);
+CheckStatus check_function(AstFunction* func);
+CheckStatus check_overloaded_function(AstOverloadedFunction* func);
+CheckStatus check_struct(AstStructType* s_node);
+CheckStatus check_function_header(AstFunction* func);
+CheckStatus check_memres_type(AstMemRes* memres);
+CheckStatus check_memres(AstMemRes* memres);
+CheckStatus check_type(AstType* type);
+CheckStatus check_insert_directive(AstDirectiveInsert** pinsert);
+CheckStatus check_do_block(AstDoBlock** pdoblock);
+
+// HACK HACK HACK
+b32 expression_types_must_be_known = 0;
+
+#define STATEMENT_LEVEL 1
+#define EXPRESSION_LEVEL 2
+u32 current_checking_level=0;
+
+static inline void fill_in_type(AstTyped* node);
+
+static inline void fill_in_array_count(AstType* type_node) {
+ if (type_node == NULL) return;
+
+ if (type_node->kind == Ast_Kind_Type_Alias) {
+ fill_in_array_count(((AstTypeAlias *) type_node)->to);
+ }
+
+ if (type_node->kind == Ast_Kind_Array_Type) {
+ if (((AstArrayType *) type_node)->count_expr) {
+ // CLEANUP: The return value is not checked on this call.
+ check_expression(&((AstArrayType *) type_node)->count_expr);
+
+ resolve_expression_type(((AstArrayType *) type_node)->count_expr);
+ }
+ }
+}
+
+static inline void fill_in_poly_call_args(AstType* type_node) {
+ if (type_node == NULL) return;
+ if (type_node->kind != Ast_Kind_Poly_Call_Type) return;
+
+ AstPolyCallType* pctype = (AstPolyCallType *) type_node;
+
+ bh_arr_each(AstNode *, param, pctype->params) {
+ if (!node_is_type(*param)) {
+ // CLEANUP: The return value is not checked on this call.
+ check_expression((AstTyped **) param);
+
+ resolve_expression_type((AstTyped *) *param);
+ fill_in_type((AstTyped *) *param);
+ }
+ }
+}
+
+static inline void fill_in_type(AstTyped* node) {
+ fill_in_array_count(node->type_node);
+ fill_in_poly_call_args(node->type_node);
+
+ if (node->type == NULL)
+ node->type = type_build_from_ast(context.ast_alloc, node->type_node);
+}
+
+// HACK: This should be baked into a structure, not a global variable.
+static Type** expected_return_type = NULL;
+
+CheckStatus check_return(AstReturn* retnode) {
+ if (retnode->expr) {
+ CHECK(expression, &retnode->expr);
+
+ if (*expected_return_type == &type_auto_return) {
+ resolve_expression_type(retnode->expr);
+ if (retnode->expr->type == NULL)
+ YIELD(retnode->token->pos, "Trying to determine automatic return type.");
+
+ *expected_return_type = retnode->expr->type;
+ return Check_Success;
+ }
+
+ if (!type_check_or_auto_cast(&retnode->expr, *expected_return_type)) {
+ ERROR_(retnode->token->pos,
+ "Expected to return a value of type '%s', returning value of type '%s'.",
+ type_get_name(*expected_return_type),
+ node_get_type_name(retnode->expr));
+ }
+
+ } else {
+ if (*expected_return_type == &type_auto_return) {
+ *expected_return_type = &basic_types[Basic_Kind_Void];
+ return Check_Success;
+ }
+
+ if ((*expected_return_type)->Basic.size > 0) {
+ ERROR_(retnode->token->pos,
+ "Returning from non-void function without a value. Expected a value of type '%s'.",
+ type_get_name(*expected_return_type));
+ }
+ }
+
+ return Check_Success;
+}
+
+CheckStatus check_if(AstIfWhile* ifnode) {
+ if (ifnode->initialization != NULL) CHECK(statement_chain, &ifnode->initialization);
+
+ if (ifnode->kind == Ast_Kind_Static_If) {
+ if ((ifnode->flags & Ast_Flag_Static_If_Resolved) == 0) {
+ YIELD(ifnode->token->pos, "Waiting for static if to be resolved.");
+ }
+
+ if (static_if_resolution(ifnode)) {
+ if (ifnode->true_stmt != NULL) CHECK(statement, (AstNode **) &ifnode->true_stmt);
+
+ } else {
+ if (ifnode->false_stmt != NULL) CHECK(statement, (AstNode **) &ifnode->false_stmt);
+ }
+
+ } else {
+ CHECK(expression, &ifnode->cond);
+
+ if (!type_is_bool(ifnode->cond->type)) {
+ ERROR_(ifnode->cond->token->pos, "Expected expression of type 'bool' for condition, got '%s'", type_get_name(ifnode->cond->type));
+ }
+
+ if (ifnode->true_stmt) CHECK(statement, (AstNode **) &ifnode->true_stmt);
+ if (ifnode->false_stmt) CHECK(statement, (AstNode **) &ifnode->false_stmt);
+ }
+
+ return Check_Success;
+}
+
+CheckStatus check_while(AstIfWhile* whilenode) {
+ if (whilenode->initialization != NULL) CHECK(statement_chain, &whilenode->initialization);
+
+ CHECK(expression, &whilenode->cond);
+
+ if (!type_is_bool(whilenode->cond->type)) {
+ ERROR_(whilenode->cond->token->pos, "Expected expression of type 'bool' for condition, got '%s'", type_get_name(whilenode->cond->type));
+ }
+
+ if (whilenode->true_stmt) CHECK(statement, (AstNode **) &whilenode->true_stmt);
+ if (whilenode->false_stmt) CHECK(statement, (AstNode **) &whilenode->false_stmt);
+
+ return Check_Success;
+}
+
+CheckStatus check_for(AstFor* fornode) {
+ CHECK(expression, &fornode->iter);
+ resolve_expression_type(fornode->iter);
+
+ Type* iter_type = fornode->iter->type;
+ if (iter_type == NULL) YIELD(fornode->token->pos, "Waiting for iteration expression type to be known.");
+
+ fornode->loop_type = For_Loop_Invalid;
+ if (types_are_compatible(iter_type, &basic_types[Basic_Kind_I32])) {
+ if (fornode->by_pointer) {
+ ERROR(fornode->var->token->pos, "Cannot iterate by pointer over a range.");
+ }
+
+ AstNumLit* low_0 = make_int_literal(context.ast_alloc, 0);
+ AstRangeLiteral* rl = make_range_literal(context.ast_alloc, (AstTyped *) low_0, fornode->iter);
+ CHECK(range_literal, &rl);
+ fornode->iter = (AstTyped *) rl;
+
+ fornode->var->type = builtin_range_type_type->Struct.memarr[0]->type;
+ fornode->var->flags |= Ast_Flag_Cannot_Take_Addr;
+ fornode->loop_type = For_Loop_Range;
+ }
+ else if (types_are_compatible(iter_type, builtin_range_type_type)) {
+ if (fornode->by_pointer) {
+ ERROR(fornode->var->token->pos, "Cannot iterate by pointer over a range.");
+ }
+
+ // NOTE: Blindly copy the first range member's type which will
+ // be the low value. - brendanfh 2020/09/04
+ fornode->var->type = builtin_range_type_type->Struct.memarr[0]->type;
+ fornode->var->flags |= Ast_Flag_Cannot_Take_Addr;
+ fornode->loop_type = For_Loop_Range;
+
+ }
+ else if (iter_type->kind == Type_Kind_Array) {
+ if (fornode->by_pointer) fornode->var->type = type_make_pointer(context.ast_alloc, iter_type->Array.elem);
+ else fornode->var->type = iter_type->Array.elem;
+
+ fornode->loop_type = For_Loop_Array;
+ }
+ else if (iter_type->kind == Type_Kind_Slice) {
+ if (fornode->by_pointer) fornode->var->type = iter_type->Slice.ptr_to_data;
+ else fornode->var->type = iter_type->Slice.ptr_to_data->Pointer.elem;
+
+ fornode->loop_type = For_Loop_Slice;
+
+ }
+ else if (iter_type->kind == Type_Kind_VarArgs) {
+ if (fornode->by_pointer) {
+ ERROR_(fornode->var->token->pos, "Cannot iterate by pointer over '%s'.", type_get_name(iter_type));
+ }
+
+ fornode->var->type = iter_type->VarArgs.ptr_to_data->Pointer.elem;
+
+ // NOTE: Slices are VarArgs are being treated the same here.
+ fornode->loop_type = For_Loop_Slice;
+ }
+ else if (iter_type->kind == Type_Kind_DynArray) {
+ if (fornode->by_pointer) fornode->var->type = iter_type->DynArray.ptr_to_data;
+ else fornode->var->type = iter_type->DynArray.ptr_to_data->Pointer.elem;
+
+ fornode->loop_type = For_Loop_DynArr;
+ }
+ else if (type_struct_constructed_from_poly_struct(iter_type, builtin_iterator_type)) {
+ if (fornode->by_pointer) {
+ ERROR(fornode->var->token->pos, "Cannot iterate by pointer over an iterator.");
+ }
+
+ // HACK: This assumes the Iterator type only has a single type argument.
+ fornode->var->type = iter_type->Struct.poly_sln[0].type;
+ fornode->loop_type = For_Loop_Iterator;
+ }
+
+ if (fornode->by_pointer)
+ fornode->var->flags |= Ast_Flag_Cannot_Take_Addr;
+
+ if (fornode->loop_type == For_Loop_Invalid) {
+ ERROR_(fornode->iter->token->pos,
+ "Cannot iterate over a '%s'.",
+ type_get_name(iter_type));
+ }
+
+ CHECK(block, fornode->stmt);
+
+ return Check_Success;
+}
+
+static b32 add_case_to_switch_statement(AstSwitch* switchnode, u64 case_value, AstBlock* block, OnyxFilePos pos) {
+ switchnode->min_case = bh_min(switchnode->min_case, case_value);
+ switchnode->max_case = bh_max(switchnode->max_case, case_value);
+
+ if (bh_imap_has(&switchnode->case_map, case_value)) {
+ onyx_report_error(pos, "Multiple cases for values '%d'.", case_value);
+ return 1;
+ }
+
+ bh_imap_put(&switchnode->case_map, case_value, (u64) block);
+ return 0;
+}
+
+CheckStatus check_switch(AstSwitch* switchnode) {
+ if (switchnode->initialization != NULL) CHECK(statement_chain, &switchnode->initialization);
+
+ CHECK(expression, &switchnode->expr);
+ Type* resolved_expr_type = resolve_expression_type(switchnode->expr);
+ if (!type_is_integer(switchnode->expr->type) && switchnode->expr->type->kind != Type_Kind_Enum) {
+ ERROR(switchnode->expr->token->pos, "expected integer or enum type for switch expression");
+ }
+
+ // LEAK if this has to be yielded
+ bh_imap_init(&switchnode->case_map, global_heap_allocator, bh_arr_length(switchnode->cases) * 2);
+
+ switchnode->min_case = 0xffffffffffffffff;
+
+ // Umm, this doesn't check the type of the case expression to the type of the expression
+ bh_arr_each(AstSwitchCase, sc, switchnode->cases) {
+ CHECK(block, sc->block);
+
+ bh_arr_each(AstTyped *, value, sc->values) {
+ CHECK(expression, value);
+
+ if ((*value)->kind == Ast_Kind_Range_Literal) {
+ AstRangeLiteral* rl = (AstRangeLiteral *) (*value);
+ resolve_expression_type(rl->low);
+ resolve_expression_type(rl->high);
+
+ if (rl->low->kind != Ast_Kind_NumLit || rl->high->kind != Ast_Kind_NumLit) {
+ ERROR(rl->token->pos, "case statement expected compile time known range.");
+ }
+
+ promote_numlit_to_larger((AstNumLit *) rl->low);
+ promote_numlit_to_larger((AstNumLit *) rl->high);
+
+ i64 lower = ((AstNumLit *) rl->low)->value.l;
+ i64 upper = ((AstNumLit *) rl->high)->value.l;
+
+ // NOTE: This is inclusive!!!!
+ fori (case_value, lower, upper + 1) {
+ if (add_case_to_switch_statement(switchnode, case_value, sc->block, rl->token->pos))
+ return Check_Error;
+ }
+
+ continue;
+ }
+
+ if (!type_check_or_auto_cast(value, resolved_expr_type)) {
+ OnyxToken* tkn = sc->block->token;
+ if ((*value)->token) tkn = (*value)->token;
+
+ ERROR_(tkn->pos, "Mismatched types in switch-case. Expected '%s', got '%s'.",
+ type_get_name(resolved_expr_type), type_get_name((*value)->type));
+ }
+
+ if (node_is_type((AstNode*) (*value))) {
+ Type* type = type_build_from_ast(context.ast_alloc, (AstType*) (*value));
+
+ if (add_case_to_switch_statement(switchnode, type->id, sc->block, sc->block->token->pos))
+ return Check_Error;
+
+ continue;
+ }
+
+ if ((*value)->kind == Ast_Kind_Enum_Value) {
+ (*value) = (AstTyped *) ((AstEnumValue *) (*value))->value;
+ }
+
+ if ((*value)->kind != Ast_Kind_NumLit) {
+ ERROR((*value)->token->pos, "case statement expected compile time known integer");
+ }
+
+ resolve_expression_type((*value));
+ // promote_numlit_to_larger((AstNumLit *) (*value));
+
+ if (add_case_to_switch_statement(switchnode, ((AstNumLit *) (*value))->value.l, sc->block, sc->block->token->pos))
+ return Check_Error;
+ }
+ }
+
+ if (switchnode->default_case)
+ CHECK(block, switchnode->default_case);
+
+ return 0;
+}
+
+CheckStatus check_arguments(Arguments* args) {
+ bh_arr_each(AstTyped *, actual, args->values)
+ CHECK(expression, actual);
+
+ bh_arr_each(AstNamedValue *, named_value, args->named_values)
+ CHECK(expression, &(*named_value)->value);
+
+ return Check_Success;
+}
+
+CheckStatus check_argument(AstArgument** parg) {
+ CHECK(expression, &(*parg)->value);
+ (*parg)->type = (*parg)->value->type;
+
+ return Check_Success;
+}
+
+static i32 non_baked_argument_count(Arguments* args) {
+ i32 count = 0;
+
+ bh_arr_each(AstTyped *, actual, args->values) {
+ assert((*actual)->kind == Ast_Kind_Argument);
+ if (!((AstArgument *) (*actual))->is_baked) count++;
+ }
+
+ bh_arr_each(AstNamedValue *, named_value, args->named_values) {
+ assert((*named_value)->value->kind == Ast_Kind_Argument);
+ if (!((AstArgument *) (*named_value)->value)->is_baked) count++;
+ }
+
+ return count;
+}
+
+static CheckStatus check_resolve_callee(AstCall* call, AstTyped** effective_callee) {
+ if (call->kind == Ast_Kind_Intrinsic_Call) return Check_Success;
+
+ call->callee = (AstTyped *) strip_aliases((AstNode *) call->callee);
+
+ AstTyped* callee = call->callee;
+ b32 calling_a_macro = 0;
+
+ if (callee->kind == Ast_Kind_Overloaded_Function) {
+ b32 should_yield = 0;
+ AstTyped* new_callee = find_matching_overload_by_arguments(
+ ((AstOverloadedFunction *) callee)->overloads,
+ &call->args,
+ &should_yield);
+
+ if (new_callee == NULL) {
+ if (callee->entity->state > Entity_State_Check_Types && !should_yield) {
+ report_unable_to_match_overload(call);
+ return Check_Error;
+
+ } else {
+ YIELD(call->token->pos, "Waiting for overloaded function option to pass type-checking.");
+ }
+ }
+
+ callee = new_callee;
+ }
+
+ if (callee->kind == Ast_Kind_Macro) {
+ calling_a_macro = 1;
+ call->callee = callee;
+
+ AstTyped* new_callee = (AstTyped *) macro_resolve_header((AstMacro *) callee, &call->args, call->token);
+ if (new_callee == NULL) return Check_Error;
+ if (new_callee == (AstTyped *) &node_that_signals_a_yield) {
+ YIELD(call->token->pos, "Waiting for macro header to pass type-checking.");
+ }
+
+ arguments_remove_baked(&call->args);
+ callee = new_callee;
+
+ } else if (callee->kind == Ast_Kind_Polymorphic_Proc) {
+ AstTyped* new_callee = (AstTyped *) polymorphic_proc_lookup((AstPolyProc *) callee, PPLM_By_Arguments, &call->args, call->token);
+ if (new_callee == NULL) return Check_Error;
+ if (new_callee == (AstTyped *) &node_that_signals_a_yield) {
+ YIELD(call->token->pos, "Waiting for polymorphic procedure header to pass type-checking.");
+ }
+
+ arguments_remove_baked(&call->args);
+ callee = new_callee;
+ }
+
+ if (!calling_a_macro) call->callee = callee;
+
+ // NOTE: Build callee's type
+ fill_in_type((AstTyped *) callee);
+ if (callee->type == NULL) {
+ YIELD(call->token->pos, "Trying to resolve function type for callee.");
+ }
+
+ if (callee->type->kind != Type_Kind_Function) {
+ ERROR_(call->token->pos,
+ "Attempting to call something that is not a function, '%b'.",
+ callee->token->text, callee->token->length);
+ }
+
+ *effective_callee = callee;
+ return Check_Success;
+}
+
+typedef enum ArgState {
+ AS_Expecting_Exact,
+ AS_Expecting_Typed_VA,
+ AS_Expecting_Untyped_VA,
+} ArgState;
+
+CheckStatus check_call(AstCall** pcall) {
+ // All the things that need to be done when checking a call node.
+ // 1. Ensure the callee is not a symbol
+ // 2. Check the callee expression (since it could be a variable or a field access, etc)
+ // 3. Check all arguments
+ // * Cannot pass overloaded functions (ROBUSTNESS)
+ // 4. If callee is an overloaded function, use the argument types to determine which overload is used.
+ // 5. If callee is polymorphic, use the arguments type to generate a polymorphic function.
+ // 7. Fill in arguments
+ // 8. If callee is an intrinsic, turn call into an Intrinsic_Call node
+ // 9. Check types of formal and actual params against each other, handling varargs
+ AstCall* call = *pcall;
+
+ if (call->flags & Ast_Flag_Has_Been_Checked) return Check_Success;
+
+ u32 current_checking_level_store = current_checking_level;
+ CHECK(expression, &call->callee);
+ CHECK(arguments, &call->args);
+ current_checking_level = current_checking_level_store;
+
+ // SPEED CLEANUP: Keeping an original copy for basically no reason except that sometimes you
+ // need to know the baked argument values in code generation.
+ // This should only be done once, but right now it is being done everytime this is checked,
+ // which can be multiple if we have to yield on a callee's type.
+ arguments_clone(&call->original_args, &call->args);
+
+ AstFunction* callee=NULL;
+ CHECK(resolve_callee, call, (AstTyped **) &callee);
+
+ // CLEANUP maybe make function_get_expected_arguments?
+ i32 non_vararg_param_count = (i32) callee->type->Function.param_count;
+ if (non_vararg_param_count > 0 &&
+ callee->type->Function.params[callee->type->Function.param_count - 1] == builtin_vararg_type_type)
+ non_vararg_param_count--;
+
+ i32 arg_count = bh_max(non_vararg_param_count, non_baked_argument_count(&call->args));
+ arguments_ensure_length(&call->args, arg_count);
+
+ char* err_msg = NULL;
+ fill_in_arguments(&call->args, (AstNode *) callee, &err_msg);
+ if (err_msg != NULL) ERROR(call->token->pos, err_msg);
+
+ bh_arr(AstArgument *) arg_arr = (bh_arr(AstArgument *)) call->args.values;
+ bh_arr_each(AstArgument *, arg, arg_arr) {
+ if (*arg != NULL) continue;
+
+ ERROR(call->token->pos, "Not all arguments were given a value.");
+ }
+
+ // HACK HACK HACK
+ // :CallSiteIsGross
+ bh_arr_each(AstArgument *, arg, arg_arr) {
+ AstTyped** arg_value = &(*arg)->value;
+
+ if ((*arg_value)->kind == Ast_Kind_Call_Site) {
+ AstCallSite* callsite = (AstCallSite *) ast_clone(context.ast_alloc, *arg_value);
+ callsite->callsite_token = call->token;
+
+ // HACK CLEANUP
+ OnyxToken* str_token = bh_alloc(context.ast_alloc, sizeof(OnyxToken));
+ str_token->text = bh_strdup(global_heap_allocator, (char *) call->token->pos.filename);
+ str_token->length = strlen(call->token->pos.filename);
+ str_token->pos = call->token->pos;
+ str_token->type = Token_Type_Literal_String;
+
+ AstStrLit* filename = bh_alloc_item(context.ast_alloc, AstStrLit);
+ memset(filename, 0, sizeof(AstStrLit));
+ filename->kind = Ast_Kind_StrLit;
+ filename->token = str_token;
+ filename->addr = 0;
+
+ add_entities_for_node(NULL, (AstNode *) filename, NULL, NULL);
+ callsite->filename = filename;
+
+ callsite->line = make_int_literal(context.ast_alloc, call->token->pos.line);
+ callsite->column = make_int_literal(context.ast_alloc, call->token->pos.column);
+
+ convert_numlit_to_type(callsite->line, &basic_types[Basic_Kind_U32]);
+ convert_numlit_to_type(callsite->column, &basic_types[Basic_Kind_U32]);
+
+ *arg_value = (AstTyped *) callsite;
+ }
+ }
+
+ // NOTE: If we are calling an intrinsic function, translate the
+ // call into an intrinsic call node.
+ if (callee->flags & Ast_Flag_Intrinsic) {
+ call->kind = Ast_Kind_Intrinsic_Call;
+ call->callee = NULL;
+
+ token_toggle_end(callee->intrinsic_name);
+ char* intr_name = callee->intrinsic_name->text;
+
+ if (bh_table_has(OnyxIntrinsic, intrinsic_table, intr_name)) {
+ call->intrinsic = bh_table_get(OnyxIntrinsic, intrinsic_table, intr_name);
+
+ } else {
+ onyx_report_error(callee->token->pos, "Intrinsic not supported, '%s'.", intr_name);
+ token_toggle_end(callee->intrinsic_name);
+ return Check_Error;
+ }
+
+ token_toggle_end(callee->intrinsic_name);
+ }
+
+ call->va_kind = VA_Kind_Not_VA;
+ call->type = callee->type->Function.return_type;
+ if (call->type == &type_auto_return && call->callee->kind != Ast_Kind_Macro) {
+ YIELD(call->token->pos, "Waiting for auto-return type to be solved.");
+ }
+
+ Type **formal_params = callee->type->Function.params;
+
+ Type* variadic_type = NULL;
+ AstParam* variadic_param = NULL;
+
+ // SPEED CLEANUP: Caching the any type here.
+ Type* any_type = type_build_from_ast(context.ast_alloc, builtin_any_type);
+
+ ArgState arg_state = AS_Expecting_Exact;
+ u32 arg_pos = 0;
+ while (1) {
+ switch (arg_state) {
+ case AS_Expecting_Exact: {
+ if (arg_pos >= callee->type->Function.param_count) goto type_checking_done;
+
+ if (formal_params[arg_pos]->kind == Type_Kind_VarArgs) {
+ variadic_type = formal_params[arg_pos]->VarArgs.ptr_to_data->Pointer.elem;
+ variadic_param = &callee->params[arg_pos];
+ arg_state = AS_Expecting_Typed_VA;
+ continue;
+ }
+
+ if ((i16) arg_pos == callee->type->Function.vararg_arg_pos) {
+ arg_state = AS_Expecting_Untyped_VA;
+ continue;
+ }
+
+ if (arg_pos >= (u32) bh_arr_length(arg_arr)) goto type_checking_done;
+ if (!type_check_or_auto_cast(&arg_arr[arg_pos]->value, formal_params[arg_pos])) {
+ ERROR_(arg_arr[arg_pos]->token->pos,
+ "The procedure '%s' expects a value of type '%s' for %d%s parameter, got '%s'.",
+ get_function_name(callee),
+ type_get_name(formal_params[arg_pos]),
+ arg_pos + 1,
+ bh_num_suffix(arg_pos + 1),
+ node_get_type_name(arg_arr[arg_pos]->value));
+ }
+
+ arg_arr[arg_pos]->va_kind = VA_Kind_Not_VA;
+ break;
+ }
+
+ case AS_Expecting_Typed_VA: {
+ if (arg_pos >= (u32) bh_arr_length(arg_arr)) goto type_checking_done;
+
+ if (variadic_type->id == any_type->id) {
+ call->va_kind = VA_Kind_Any;
+
+ resolve_expression_type(arg_arr[arg_pos]->value);
+ arg_arr[arg_pos]->va_kind = VA_Kind_Any;
+ break;
+ }
+
+ call->va_kind = VA_Kind_Typed;
+
+ if (!type_check_or_auto_cast(&arg_arr[arg_pos]->value, variadic_type)) {
+ onyx_report_error(arg_arr[arg_pos]->token->pos,
+ "The procedure '%s' expects a value of type '%s' for the variadic parameter, '%b', got '%s'.",
+ get_function_name(callee),
+ type_get_name(variadic_type),
+ variadic_param->local->token->text,
+ variadic_param->local->token->length,
+ node_get_type_name(arg_arr[arg_pos]->value));
+ return Check_Error;
+ }
+
+ arg_arr[arg_pos]->va_kind = VA_Kind_Typed;
+ break;
+ }
+
+ case AS_Expecting_Untyped_VA: {
+ call->va_kind = VA_Kind_Untyped;
+
+ if (arg_pos >= (u32) bh_arr_length(arg_arr)) goto type_checking_done;
+
+ resolve_expression_type(arg_arr[arg_pos]->value);
+ if (arg_arr[arg_pos]->value->type == NULL) {
+ ERROR(arg_arr[arg_pos]->token->pos, "Unable to resolve type for argument.");
+ }
+
+ arg_arr[arg_pos]->va_kind = VA_Kind_Untyped;
+ break;
+ }
+ }
+
+ arg_pos++;
+ }
+
+type_checking_done:
+
+ call->flags |= Ast_Flag_Has_Been_Checked;
+
+ if (arg_pos < callee->type->Function.needed_param_count)
+ ERROR(call->token->pos, "Too few arguments to function call.");
+
+ if (arg_pos < (u32) arg_count)
+ ERROR(call->token->pos, "Too many arguments to function call.");
+
+ callee->flags |= Ast_Flag_Function_Used;
+
+ if (call->kind == Ast_Kind_Call && call->callee->kind == Ast_Kind_Macro) {
+ expand_macro(pcall, callee);
+ return Check_Return_To_Symres;
+ }
+
+ return Check_Success;
+}
+
+static void report_bad_binaryop(AstBinaryOp* binop) {
+ onyx_report_error(binop->token->pos, "Binary operator '%s' not understood for arguments of type '%s' and '%s'.",
+ binaryop_string[binop->operation],
+ node_get_type_name(binop->left),
+ node_get_type_name(binop->right));
+}
+
+static AstCall* binaryop_try_operator_overload(AstBinaryOp* binop) {
+ if (bh_arr_length(operator_overloads[binop->operation]) == 0) return NULL;
+
+ Arguments args = ((Arguments) { NULL, NULL });
+ bh_arr_new(global_heap_allocator, args.values, 2);
+ bh_arr_push(args.values, binop->left);
+ bh_arr_push(args.values, binop->right);
+
+ if (binop_is_assignment(binop->operation)) {
+ args.values[0] = (AstTyped *) make_address_of(context.ast_alloc, binop->left);
+
+ u32 current_checking_level_store = current_checking_level;
+ CheckStatus cs = check_address_of((AstAddressOf *) args.values[0]);
+ current_checking_level = current_checking_level_store;
+
+ if (cs == Check_Yield_Macro) return (AstCall *) &node_that_signals_a_yield;
+ if (cs == Check_Error) {
+ return NULL;
+ }
+ }
+
+ b32 should_yield = 0;
+ AstTyped* overload = find_matching_overload_by_arguments(operator_overloads[binop->operation], &args, &should_yield);
+ if (should_yield) {
+ bh_arr_free(args.values);
+ return (AstCall *) &node_that_signals_a_yield;
+ }
+
+ if (overload == NULL) {
+ bh_arr_free(args.values);
+ return NULL;
+ }
+
+ AstCall* implicit_call = onyx_ast_node_new(context.ast_alloc, sizeof(AstCall), Ast_Kind_Call);
+ implicit_call->token = binop->token;
+ implicit_call->callee = overload;
+ implicit_call->va_kind = VA_Kind_Not_VA;
+
+ bh_arr_each(AstTyped *, arg, args.values)
+ *arg = (AstTyped *) make_argument(context.ast_alloc, *arg);
+
+ implicit_call->args = args;
+ return implicit_call;
+}
+
+
+CheckStatus check_binaryop_assignment(AstBinaryOp** pbinop) {
+ AstBinaryOp* binop = *pbinop;
+ if (current_checking_level == EXPRESSION_LEVEL)
+ ERROR(binop->token->pos, "Assignment not valid in expression.");
+
+ if (!is_lval((AstNode *) binop->left))
+ ERROR_(binop->left->token->pos,
+ "Cannot assign to '%b'.",
+ binop->left->token->text, binop->left->token->length);
+
+ if ((binop->left->flags & Ast_Flag_Const) != 0 && binop->left->type != NULL)
+ ERROR_(binop->token->pos,
+ "Cannot assign to constant '%b.'.",
+ binop->left->token->text, binop->left->token->length);
+
+ if (binop->operation == Binary_Op_Assign) {
+ // NOTE: Raw assignment
+
+ // NOTE: This is the 'type inference' system. Very stupid, but very easy.
+ // If a left operand has an unknown type, fill it in with the type of
+ // the right hand side.
+ if (binop->left->type == NULL) {
+ resolve_expression_type(binop->right);
+
+ if (binop->right->type == NULL) {
+ if (binop->right->entity == NULL || binop->right->entity->state > Entity_State_Check_Types) {
+ ERROR(binop->token->pos, "Could not resolve type of right hand side to infer.");
+
+ } else {
+ YIELD(binop->token->pos, "Trying to resolve try of right hand side.");
+ }
+ }
+
+ if (binop->right->type->kind == Type_Kind_Compound) {
+ AstCompound* lhs = (AstCompound *) binop->left;
+ if (lhs->kind != Ast_Kind_Compound) {
+ ERROR_(binop->token->pos,
+ "Expected left hand side to have %d expressions.",
+ binop->right->type->Compound.count);
+ }
+
+ i32 expr_count = binop->right->type->Compound.count;
+ if (bh_arr_length(lhs->exprs) != expr_count) {
+ ERROR_(binop->token->pos,
+ "Expected left hand side to have %d expressions.",
+ binop->right->type->Compound.count);
+ }
+
+ fori (i, 0, expr_count) {
+ lhs->exprs[i]->type = binop->right->type->Compound.types[i];
+ }
+
+ lhs->type = type_build_compound_type(context.ast_alloc, lhs);
+
+ } else {
+ binop->left->type = binop->right->type;
+ }
+ }
+
+ } else {
+ // NOTE: +=, -=, ...
+
+ BinaryOp operation = -1;
+ if (binop->operation == Binary_Op_Assign_Add) operation = Binary_Op_Add;
+ else if (binop->operation == Binary_Op_Assign_Minus) operation = Binary_Op_Minus;
+ else if (binop->operation == Binary_Op_Assign_Multiply) operation = Binary_Op_Multiply;
+ else if (binop->operation == Binary_Op_Assign_Divide) operation = Binary_Op_Divide;
+ else if (binop->operation == Binary_Op_Assign_Modulus) operation = Binary_Op_Modulus;
+ else if (binop->operation == Binary_Op_Assign_And) operation = Binary_Op_And;
+ else if (binop->operation == Binary_Op_Assign_Or) operation = Binary_Op_Or;
+ else if (binop->operation == Binary_Op_Assign_Xor) operation = Binary_Op_Xor;
+ else if (binop->operation == Binary_Op_Assign_Shl) operation = Binary_Op_Shl;
+ else if (binop->operation == Binary_Op_Assign_Shr) operation = Binary_Op_Shr;
+ else if (binop->operation == Binary_Op_Assign_Sar) operation = Binary_Op_Sar;
+
+ AstBinaryOp* new_right = make_binary_op(context.ast_alloc, operation, binop->left, binop->right);
+ binop->right = (AstTyped *) new_right;
+ new_right->token = binop->token;
+ binop->operation = Binary_Op_Assign;
+
+ CHECK(binaryop, (AstBinaryOp **) &binop->right);
+ }
+
+ if (binop->right->type == NULL) {
+ if (binop->right->entity != NULL && binop->right->entity->state <= Entity_State_Check_Types) {
+ YIELD(binop->token->pos, "Trying to resolve type of right hand side.");
+ }
+ }
+
+ if (!type_check_or_auto_cast(&binop->right, binop->left->type)) {
+ ERROR_(binop->token->pos,
+ "Cannot assign value of type '%s' to a '%s'.",
+ node_get_type_name(binop->right),
+ node_get_type_name(binop->left));
+ }
+
+ binop->type = &basic_types[Basic_Kind_Void];
+
+ return Check_Success;
+}
+
+static b32 binary_op_is_allowed(BinaryOp operation, Type* type) {
+ static const u8 binop_allowed[Binary_Op_Count] = {
+ /* Add */ Basic_Flag_Numeric | Basic_Flag_Pointer,
+ /* Minus */ Basic_Flag_Numeric | Basic_Flag_Pointer,
+ /* Multiply */ Basic_Flag_Numeric,
+ /* Divide */ Basic_Flag_Numeric,
+ /* Modulus */ Basic_Flag_Integer,
+
+ /* Equal */ Basic_Flag_Equality,
+ /* Not_Equal */ Basic_Flag_Equality,
+ /* Less */ Basic_Flag_Ordered,
+ /* Less_Equal */ Basic_Flag_Ordered,
+ /* Greater */ Basic_Flag_Ordered,
+ /* Greater_Equal */ Basic_Flag_Ordered,
+
+ /* And */ Basic_Flag_Integer,
+ /* Or */ Basic_Flag_Integer,
+ /* Xor */ Basic_Flag_Integer,
+ /* Shl */ Basic_Flag_Integer,
+ /* Shr */ Basic_Flag_Integer,
+ /* Sar */ Basic_Flag_Integer,
+
+ /* Bool_And */ Basic_Flag_Boolean,
+ /* Bool_Or */ Basic_Flag_Boolean,
+
+ /* Assign_Start */ 0,
+ /* Assign */ 0,
+ /* Assign_Add */ 0,
+ /* Assign_Minus */ 0,
+ /* Assign_Multiply */ 0,
+ /* Assign_Divide */ 0,
+ /* Assign_Modulus */ 0,
+ /* Assign_And */ 0,
+ /* Assign_Or */ 0,
+ /* Assign_Xor */ 0,
+ /* Assign_Shl */ 0,
+ /* Assign_Shr */ 0,
+ /* Assign_Sar */ 0,
+ /* Assign_End */ 0,
+
+ /* Pipe */ 0,
+ /* Range */ 0,
+ };
+
+ enum BasicFlag effective_flags = 0;
+ switch (type->kind) {
+ case Type_Kind_Basic: effective_flags = type->Basic.flags; break;
+ case Type_Kind_Pointer: effective_flags = Basic_Flag_Pointer; break;
+ case Type_Kind_Enum: effective_flags = Basic_Flag_Integer; break;
+ case Type_Kind_Function: effective_flags = Basic_Flag_Equality; break;
+ }
+
+ return (binop_allowed[operation] & effective_flags) != 0;
+}
+
+CheckStatus check_binaryop_compare(AstBinaryOp** pbinop) {
+ AstBinaryOp* binop = *pbinop;
+
+ // HACK: Since ^... to rawptr is a one way conversion, strip any pointers
+ // away so they can be compared as expected
+ Type* ltype = binop->left->type;
+ Type* rtype = binop->right->type;
+
+ if (ltype->kind == Type_Kind_Pointer) ltype = &basic_types[Basic_Kind_Rawptr];
+ if (rtype->kind == Type_Kind_Pointer) rtype = &basic_types[Basic_Kind_Rawptr];
+
+ if (!types_are_compatible(ltype, rtype)) {
+ b32 left_ac = node_is_auto_cast((AstNode *) binop->left);
+ b32 right_ac = node_is_auto_cast((AstNode *) binop->right);
+
+ if (left_ac && right_ac) ERROR(binop->token->pos, "Cannot have auto cast on both sides of binary operator.");
+ else if (type_check_or_auto_cast(&binop->left, rtype));
+ else if (type_check_or_auto_cast(&binop->right, ltype));
+ else {
+ ERROR_(binop->token->pos,
+ "Cannot compare '%s' to '%s'.",
+ type_get_name(ltype),
+ type_get_name(rtype));
+ }
+ }
+
+ if (!binary_op_is_allowed(binop->operation, binop->left->type)) {
+ report_bad_binaryop(binop);
+ return Check_Error;
+ }
+
+ binop->type = &basic_types[Basic_Kind_Bool];
+ if (binop->flags & Ast_Flag_Comptime) {
+ // NOTE: Not a binary op
+ *pbinop = (AstBinaryOp *) ast_reduce(context.ast_alloc, (AstTyped *) binop);
+ }
+
+ return Check_Success;
+}
+
+CheckStatus check_binaryop_bool(AstBinaryOp** pbinop) {
+ AstBinaryOp* binop = *pbinop;
+
+ if (!type_is_bool(binop->left->type) || !type_is_bool(binop->right->type)) {
+ report_bad_binaryop(binop);
+ return Check_Error;
+ }
+
+ binop->type = &basic_types[Basic_Kind_Bool];
+
+ if (binop->flags & Ast_Flag_Comptime) {
+ // NOTE: Not a binary op
+ *pbinop = (AstBinaryOp *) ast_reduce(context.ast_alloc, (AstTyped *) binop);
+ }
+ return Check_Success;
+}
+
+CheckStatus check_binaryop(AstBinaryOp** pbinop) {
+ AstBinaryOp* binop = *pbinop;
+
+ if (binop->flags & Ast_Flag_Has_Been_Checked) return Check_Success;
+
+ u32 current_checking_level_store = current_checking_level;
+ CHECK(expression, &binop->left);
+ CHECK(expression, &binop->right);
+ current_checking_level = current_checking_level_store;
+
+ if ((binop->left->flags & Ast_Flag_Comptime) && (binop->right->flags & Ast_Flag_Comptime)) {
+ binop->flags |= Ast_Flag_Comptime;
+ }
+
+ if (expression_types_must_be_known) {
+ if (binop->left->type == NULL || binop->right->type == NULL) {
+ ERROR(binop->token->pos, "Internal compiler error: one of the operands types is unknown here.");
+ }
+ }
+
+ // :UnaryFieldAccessIsGross
+ if (binop->left->kind == Ast_Kind_Unary_Field_Access || binop->right->kind == Ast_Kind_Unary_Field_Access) {
+ if (type_check_or_auto_cast(&binop->left, binop->right->type));
+ else if (type_check_or_auto_cast(&binop->right, binop->left->type));
+ else {
+ report_bad_binaryop(binop);
+ return Check_Error;
+ }
+ }
+
+ // NOTE: Try operator overloading before checking everything else.
+ if ((binop->left->type != NULL && binop->right->type != NULL) &&
+ (binop->left->type->kind != Type_Kind_Basic || binop->right->type->kind != Type_Kind_Basic)) {
+ AstCall *implicit_call = binaryop_try_operator_overload(binop);
+
+ if (implicit_call == (AstCall *) &node_that_signals_a_yield)
+ YIELD(binop->token->pos, "Trying to resolve operator overload.");
+
+ if (implicit_call != NULL) {
+ // NOTE: Not a binary op
+ implicit_call->next = binop->next;
+ *pbinop = (AstBinaryOp *) implicit_call;
+
+ CHECK(call, (AstCall **) pbinop);
+ return Check_Success;
+ }
+ }
+
+ if (binop_is_assignment(binop->operation)) return check_binaryop_assignment(pbinop);
+
+ // NOTE: Comparision operators and boolean operators are handled separately.
+ if (binop_is_compare(binop->operation))
+ return check_binaryop_compare(pbinop);
+ if (binop->operation == Binary_Op_Bool_And || binop->operation == Binary_Op_Bool_Or)
+ return check_binaryop_bool(pbinop);
+
+ // NOTE: The left side cannot be compound.
+ // The right side always is numeric.
+ // The left side cannot be rawptr.
+ if (type_is_compound(binop->left->type)) goto bad_binaryop;
+ if (!type_is_numeric(binop->right->type)) goto bad_binaryop;
+ if (type_is_rawptr(binop->left->type)) {
+ ERROR(binop->token->pos, "Cannot operate on a 'rawptr'. Cast it to a another pointer type first.");
+ }
+
+ // NOTE: Handle basic pointer math.
+ if (type_is_pointer(binop->left->type)) {
+ if (binop->operation != Binary_Op_Add && binop->operation != Binary_Op_Minus) goto bad_binaryop;
+
+ resolve_expression_type(binop->right);
+ if (!type_is_integer(binop->right->type)) goto bad_binaryop;
+
+ AstNumLit* numlit = make_int_literal(context.ast_alloc, type_size_of(binop->left->type->Pointer.elem));
+ numlit->token = binop->right->token;
+ numlit->type = binop->right->type;
+
+ AstBinaryOp* binop_node = make_binary_op(context.ast_alloc, Binary_Op_Multiply, binop->right, (AstTyped *) numlit);
+ binop_node->token = binop->token;
+ CHECK(binaryop, &binop_node);
+
+ binop->right = (AstTyped *) binop_node;
+ binop->type = binop->left->type;
+ binop->right->type = binop->left->type;
+ }
+
+ if (!types_are_compatible(binop->left->type, binop->right->type)) {
+ b32 left_ac = node_is_auto_cast((AstNode *) binop->left);
+ b32 right_ac = node_is_auto_cast((AstNode *) binop->right);
+
+ if (left_ac && right_ac) {
+ ERROR(binop->token->pos, "Cannot have auto cast on both sides of binary operator.");
+ }
+ else if (type_check_or_auto_cast(&binop->left, binop->right->type));
+ else if (type_check_or_auto_cast(&binop->right, binop->left->type));
+ else {
+ ERROR_(binop->token->pos,
+ "Mismatched types for binary operation '%s'. left: '%s', right: '%s'.",
+ binaryop_string[binop->operation],
+ node_get_type_name(binop->left),
+ node_get_type_name(binop->right));
+ }
+ }
+
+ binop->type = binop->left->type;
+ if (!binary_op_is_allowed(binop->operation, binop->type)) goto bad_binaryop;
+
+ // NOTE: Enum flags with '&' result in a boolean value
+ if (binop->type->kind == Type_Kind_Enum && binop->type->Enum.is_flags && binop->operation == Binary_Op_And) {
+ binop->type = &basic_types[Basic_Kind_Bool];
+ }
+
+ binop->flags |= Ast_Flag_Has_Been_Checked;
+
+ if (binop->flags & Ast_Flag_Comptime) {
+ // NOTE: Not a binary op
+ *pbinop = (AstBinaryOp *) ast_reduce(context.ast_alloc, (AstTyped *) binop);
+ }
+ return Check_Success;
+
+bad_binaryop:
+ report_bad_binaryop(binop);
+
+ return Check_Error;
+}
+
+CheckStatus check_unaryop(AstUnaryOp** punop) {
+ AstUnaryOp* unaryop = *punop;
+
+ CHECK(expression, &unaryop->expr);
+
+ if (unaryop->operation != Unary_Op_Negate) {
+ resolve_expression_type(unaryop->expr);
+ }
+
+ if (unaryop->operation == Unary_Op_Cast) {
+ char* err;
+ if (unaryop->type == NULL)
+ YIELD(unaryop->token->pos, "Trying to resolve destination type for cast.");
+
+ if (!cast_is_legal(unaryop->expr->type, unaryop->type, &err)) {
+ ERROR_(unaryop->token->pos, "Cast Error: %s", err);
+ }
+
+ } else {
+ unaryop->type = unaryop->expr->type;
+ }
+
+ if (unaryop->operation == Unary_Op_Not) {
+ if (!type_is_bool(unaryop->expr->type)) {
+ ERROR_(unaryop->token->pos,
+ "Bool negation operator expected bool type, got '%s'.",
+ node_get_type_name(unaryop->expr));
+ }
+ }
+
+ if (unaryop->operation == Unary_Op_Bitwise_Not) {
+ if (!type_is_integer(unaryop->expr->type)) {
+ ERROR_(unaryop->token->pos,
+ "Bitwise operator expected integer type, got '%s'.",
+ node_get_type_name(unaryop->expr));
+ }
+ }
+
+ if (unaryop->expr->flags & Ast_Flag_Comptime) {
+ unaryop->flags |= Ast_Flag_Comptime;
+ // NOTE: Not a unary op
+ *punop = (AstUnaryOp *) ast_reduce(context.ast_alloc, (AstTyped *) unaryop);
+ }
+
+ return Check_Success;
+}
+
+CheckStatus check_struct_literal(AstStructLiteral* sl) {
+ if (sl->type == NULL) {
+ // NOTE: This is used for automatically typed struct literals. If there is no provided
+ // type for the struct literal, assume that it is passes successfully. When it is used
+ // elsewhere, it will be added as an expression entity that will be processed once the
+ // stnode is filled out.
+ if (sl->stnode == NULL) return Check_Success;
+
+ if (!node_is_type((AstNode *) sl->stnode)) {
+ ERROR(sl->token->pos, "Type used for struct literal is not a type.");
+ }
+
+ fill_in_type((AstTyped *) sl);
+ if (sl->type == NULL)
+ YIELD(sl->token->pos, "Trying to resolve type of struct literal.");
+ }
+
+ if (!type_is_structlike_strict(sl->type)) {
+ ERROR_(sl->token->pos,
+ "'%s' is not constructable using a struct literal.",
+ type_get_name(sl->type));
+ }
+
+ i32 mem_count = type_structlike_mem_count(sl->type);
+ arguments_ensure_length(&sl->args, mem_count);
+
+ // :Idempotency
+ if ((sl->flags & Ast_Flag_Has_Been_Checked) == 0) {
+ char* err_msg = NULL;
+ if (!fill_in_arguments(&sl->args, (AstNode *) sl, &err_msg)) {
+ onyx_report_error(sl->token->pos, err_msg);
+
+ bh_arr_each(AstTyped *, value, sl->args.values) {
+ if (*value == NULL) {
+ i32 member_idx = value - sl->args.values; // Pointer subtraction hack
+ StructMember smem;
+ type_lookup_member_by_idx(sl->type, member_idx, &smem);
+
+ onyx_report_error(sl->token->pos,
+ "Value not given for %d%s member, '%s', for type '%s'.",
+ member_idx + 1, bh_num_suffix(member_idx + 1),
+ smem.name, type_get_name(sl->type));
+ }
+ }
+
+ return Check_Error;
+ }
+ }
+ sl->flags |= Ast_Flag_Has_Been_Checked;
+
+ AstTyped** actual = sl->args.values;
+ StructMember smem;
+
+ // BUG: There are problems setting the comptime flag this late in the checking because
+ // if the struct literal was type inferred, then the literal won't be correctly determined
+ // to be comptime on the first pass, which is needed for top level expressions.
+ sl->flags |= Ast_Flag_Comptime;
+
+ fori (i, 0, mem_count) {
+ // NOTE: Not checking the return on this function because
+ // this for loop is bounded by the number of members in the
+ // type.
+ type_lookup_member_by_idx(sl->type, i, &smem);
+ Type* formal = smem.type;
+
+ CHECK(expression, actual);
+
+ // HACK HACK HACK
+ if ((*actual)->type == NULL &&
+ (*actual)->entity != NULL &&
+ (*actual)->entity->state <= Entity_State_Check_Types) {
+ YIELD_((*actual)->token->pos, "Trying to resolve type of expression for member '%s'.", smem.name);
+ }
+
+ if (!type_check_or_auto_cast(actual, formal)) {
+ ERROR_(sl->token->pos,
+ "Mismatched types for %d%s member named '%s', expected '%s', got '%s'.",
+ i + 1, bh_num_suffix(i + 1),
+ smem.name,
+ type_get_name(formal),
+ node_get_type_name(*actual));
+ }
+
+ sl->flags &= ((*actual)->flags & Ast_Flag_Comptime) | (sl->flags &~ Ast_Flag_Comptime);
+ actual++;
+ }
+
+ return Check_Success;
+}
+
+CheckStatus check_array_literal(AstArrayLiteral* al) {
+ // :Idempotency
+ if ((al->flags & Ast_Flag_Array_Literal_Typed) == 0) {
+ if (al->atnode == NULL)
+ YIELD(al->token->pos, "Waiting for array literal type to be known.");
+
+ if (!node_is_type((AstNode *) al->atnode))
+ ERROR(al->token->pos, "Array type is not a type.");
+
+ fill_in_type((AstTyped *) al);
+ if (al->type == NULL)
+ YIELD(al->token->pos, "Trying to resolve type of array literal.");
+
+ al->type = type_make_array(context.ast_alloc, al->type, bh_arr_length(al->values));
+ if (al->type == NULL || al->type->kind != Type_Kind_Array)
+ ERROR(al->token->pos, "Expected array type for array literal. This is a compiler bug.");
+
+ al->flags |= Ast_Flag_Array_Literal_Typed;
+ }
+
+ if (al->type->Array.count != (u32) bh_arr_length(al->values)) {
+ ERROR_(al->token->pos, "Wrong array size (%d) for number of values (%d).",
+ al->type->Array.count, bh_arr_length(al->values));
+ }
+
+ al->flags |= Ast_Flag_Comptime;
+
+ Type* elem_type = al->type->Array.elem;
+ bh_arr_each(AstTyped *, expr, al->values) {
+ CHECK(expression, expr);
+
+ // HACK HACK HACK
+ if ((*expr)->type == NULL &&
+ (*expr)->entity != NULL &&
+ (*expr)->entity->state <= Entity_State_Check_Types) {
+ YIELD_(al->token->pos, "Trying to resolve type of %d%s element of array literal.", expr - al->values, bh_num_suffix(expr - al->values));
+ }
+
+ al->flags &= ((*expr)->flags & Ast_Flag_Comptime) | (al->flags &~ Ast_Flag_Comptime);
+
+ if (!type_check_or_auto_cast(expr, elem_type)) {
+ ERROR_((*expr)->token->pos, "Mismatched types for value of in array, expected '%s', got '%s'.",
+ type_get_name(elem_type),
+ node_get_type_name(*expr));
+ }
+ }
+
+ return Check_Success;
+}
+
+CheckStatus check_range_literal(AstRangeLiteral** prange) {
+ AstRangeLiteral* range = *prange;
+ CHECK(expression, &range->low);
+ CHECK(expression, &range->high);
+
+ Type* expected_range_type = builtin_range_type_type;
+ StructMember smem;
+
+ type_lookup_member(expected_range_type, "low", &smem);
+ if (!type_check_or_auto_cast(&range->low, smem.type)) {
+ ERROR_(range->token->pos,
+ "Expected left side of range to be a 32-bit integer, got '%s'.",
+ node_get_type_name(range->low));
+ }
+
+ type_lookup_member(expected_range_type, "high", &smem);
+ if (!type_check_or_auto_cast(&range->high, smem.type)) {
+ ERROR_(range->token->pos,
+ "Expected right side of range to be a 32-bit integer, got '%s'.",
+ node_get_type_name(range->high));
+ }
+
+ if (range->step == NULL) {
+ type_lookup_member(expected_range_type, "step", &smem);
+ assert(smem.initial_value != NULL);
+ CHECK(expression, smem.initial_value);
+
+ range->step = *smem.initial_value;
+ }
+
+ return Check_Success;
+}
+
+CheckStatus check_compound(AstCompound* compound) {
+ bh_arr_each(AstTyped *, expr, compound->exprs) {
+ CHECK(expression, expr);
+ }
+
+ compound->type = type_build_compound_type(context.ast_alloc, compound);
+ return Check_Success;
+}
+
+CheckStatus check_if_expression(AstIfExpression* if_expr) {
+ CHECK(expression, &if_expr->cond);
+ CHECK(expression, &if_expr->true_expr);
+ CHECK(expression, &if_expr->false_expr);
+
+ if (!type_check_or_auto_cast(&if_expr->cond, &basic_types[Basic_Kind_Bool])) {
+ ERROR_(if_expr->token->pos, "If-expression expected boolean for condition, got '%s'.",
+ type_get_name(if_expr->cond->type));
+ }
+
+ resolve_expression_type((AstTyped *) if_expr);
+
+ if (!types_are_compatible(if_expr->true_expr->type, if_expr->false_expr->type)) {
+ ERROR_(if_expr->token->pos, "Mismatched types for if-expression, left side is '%s', and right side is '%s'.",
+ type_get_name(if_expr->true_expr->type), type_get_name(if_expr->false_expr->type));
+ }
+
+ return Check_Success;
+}
+
+CheckStatus check_do_block(AstDoBlock** pdoblock) {
+ AstDoBlock* doblock = *pdoblock;
+ if (doblock->flags & Ast_Flag_Has_Been_Checked) return Check_Success;
+
+ fill_in_type((AstTyped *) doblock);
+
+ Type** old_expected_return_type = expected_return_type;
+ expected_return_type = &doblock->type;
+
+ doblock->block->rules = Block_Rule_Do_Block;
+ CHECK(block, doblock->block);
+
+ if (doblock->type == &type_auto_return) {
+ // ERROR(doblock->token->pos, "Unable to determine type of do-block expression.");
+ doblock->type = &basic_types[Basic_Kind_Void];
+ }
+
+ expected_return_type = old_expected_return_type;
+ doblock->flags |= Ast_Flag_Has_Been_Checked;
+ return Check_Success;
+}
+
+CheckStatus check_address_of(AstAddressOf* aof) {
+ CHECK(expression, &aof->expr);
+ if (aof->expr->type == NULL) {
+ YIELD(aof->token->pos, "Trying to resolve type of expression to take a reference.");
+ }
+
+ if ((aof->expr->kind != Ast_Kind_Subscript
+ && aof->expr->kind != Ast_Kind_Dereference
+ && aof->expr->kind != Ast_Kind_Field_Access
+ && aof->expr->kind != Ast_Kind_Memres
+ && aof->expr->kind != Ast_Kind_Local)
+ || (aof->expr->flags & Ast_Flag_Cannot_Take_Addr) != 0) {
+ ERROR(aof->token->pos, "Cannot take the address of something that is not an l-value.");
+ }
+
+ aof->expr->flags |= Ast_Flag_Address_Taken;
+
+ aof->type = type_make_pointer(context.ast_alloc, aof->expr->type);
+
+ return Check_Success;
+}
+
+CheckStatus check_dereference(AstDereference* deref) {
+ CHECK(expression, &deref->expr);
+
+ if (!type_is_pointer(deref->expr->type))
+ ERROR(deref->token->pos, "Cannot dereference non-pointer value.");
+
+ if (deref->expr->type == basic_type_rawptr.basic_type)
+ ERROR(deref->token->pos, "Cannot dereference 'rawptr'. Cast to another pointer type first.");
+
+ deref->type = deref->expr->type->Pointer.elem;
+
+ return Check_Success;
+}
+
+CheckStatus check_subscript(AstSubscript** psub) {
+ AstSubscript* sub = *psub;
+ CHECK(expression, &sub->addr);
+ CHECK(expression, &sub->expr);
+
+ // NOTE: Try operator overloading before checking everything else.
+ if ((sub->addr->type != NULL && sub->expr->type != NULL) &&
+ (sub->addr->type->kind != Type_Kind_Basic || sub->expr->type->kind != Type_Kind_Basic)) {
+ // AstSubscript is the same as AstBinaryOp for the first sizeof(AstBinaryOp) bytes
+ AstBinaryOp* binop = (AstBinaryOp *) sub;
+ AstCall *implicit_call = binaryop_try_operator_overload(binop);
+
+ if (implicit_call == (AstCall *) &node_that_signals_a_yield)
+ YIELD(sub->token->pos, "Trying to resolve operator overload.");
+
+ if (implicit_call != NULL) {
+ // NOTE: Not an array access
+ implicit_call->next = sub->next;
+ *psub = (AstSubscript *) implicit_call;
+
+ CHECK(call, (AstCall **) psub);
+ return Check_Success;
+ }
+ }
+
+ if (!type_is_array_accessible(sub->addr->type)) {
+ report_bad_binaryop((AstBinaryOp *) sub);
+ return Check_Error;
+ }
+
+ if (types_are_compatible(sub->expr->type, builtin_range_type_type)) {
+ Type *of = NULL;
+ if (sub->addr->type->kind == Type_Kind_Pointer)
+ of = sub->addr->type->Pointer.elem;
+ else if (sub->addr->type->kind == Type_Kind_Array)
+ of = sub->addr->type->Array.elem;
+ else {
+ // FIXME: Slice creation should be allowed for slice types and dynamic array types, like it
+ // is below, but this code doesn't look at that.
+ report_bad_binaryop((AstBinaryOp *) sub);
+ ERROR(sub->token->pos, "Invalid type for left of slice creation.");
+ }
+
+ sub->kind = Ast_Kind_Slice;
+ sub->type = type_make_slice(context.ast_alloc, of);
+ sub->elem_size = type_size_of(of);
+
+ return Check_Success;
+ }
+
+ resolve_expression_type(sub->expr);
+ if (sub->expr->type->kind != Type_Kind_Basic
+ || (sub->expr->type->Basic.kind != Basic_Kind_I32 && sub->expr->type->Basic.kind != Basic_Kind_U32)) {
+ report_bad_binaryop((AstBinaryOp *) sub);
+ ERROR_(sub->token->pos,
+ "Expected type u32 or i32 for index, got '%s'.",
+ node_get_type_name(sub->expr));
+ }
+
+ if (sub->addr->type->kind == Type_Kind_Pointer)
+ sub->type = sub->addr->type->Pointer.elem;
+ else if (sub->addr->type->kind == Type_Kind_Array)
+ sub->type = sub->addr->type->Array.elem;
+ else if (sub->addr->type->kind == Type_Kind_Slice
+ || sub->addr->type->kind == Type_Kind_DynArray
+ || sub->addr->type->kind == Type_Kind_VarArgs) {
+ // If we are accessing on a slice or a dynamic array, implicitly add a field access for the data member
+ StructMember smem;
+ type_lookup_member(sub->addr->type, "data", &smem);
+
+ AstFieldAccess* fa = make_field_access(context.ast_alloc, sub->addr, "data");
+ fa->type = smem.type;
+ fa->offset = smem.offset;
+ fa->idx = smem.idx;
+
+ sub->addr = (AstTyped *) fa;
+ sub->type = sub->addr->type->Pointer.elem;
+ }
+ else {
+ report_bad_binaryop((AstBinaryOp *) sub);
+ ERROR(sub->token->pos, "Invalid type for left of array access.");
+ }
+
+ sub->elem_size = type_size_of(sub->type);
+
+ return Check_Success;
+}
+
+CheckStatus check_field_access(AstFieldAccess** pfield) {
+ AstFieldAccess* field = *pfield;
+ CHECK(expression, &field->expr);
+ if (field->expr->type == NULL) {
+ // onyx_report_error(field->token->pos, "Unable to deduce type of expression for accessing field.");
+ YIELD(field->token->pos, "Trying to resolve type of source expression.");
+ }
+
+ if (!type_is_structlike(field->expr->type)) {
+ ERROR_(field->token->pos,
+ "Cannot access field '%b' on '%s'. Type is not a struct.",
+ field->token->text,
+ field->token->length,
+ node_get_type_name(field->expr));
+ }
+
+ // Optimization for (*foo).member.
+ if (field->expr->kind == Ast_Kind_Dereference) {
+ field->expr = ((AstDereference *) field->expr)->expr;
+ }
+
+ StructMember smem;
+ if (field->token != NULL && field->field == NULL) {
+ token_toggle_end(field->token);
+ // CLEANUP: Duplicating the string here isn't the best for effiency,
+ // but it fixes a lot of bugs, so here we are.
+ // - brendanfh 2020/12/08
+ field->field = bh_strdup(context.ast_alloc, field->token->text);
+ token_toggle_end(field->token);
+ }
+
+ if (!type_lookup_member(field->expr->type, field->field, &smem)) {
+ AstType* type_node = field->expr->type->ast_type;
+ AstNode* n = try_symbol_raw_resolve_from_node((AstNode *) type_node, field->field);
+ if (n) {
+ *pfield = (AstFieldAccess *) n;
+ return Check_Success;
+ }
+
+ ERROR_(field->token->pos,
+ "Field '%s' does not exists on '%s'.",
+ field->field,
+ node_get_type_name(field->expr));
+ }
+
+ field->offset = smem.offset;
+ field->idx = smem.idx;
+ field->type = smem.type;
+
+ return Check_Success;
+}
+
+CheckStatus check_method_call(AstBinaryOp** mcall) {
+ CHECK(expression, &(*mcall)->left);
+ if ((*mcall)->left->type == NULL) {
+ YIELD((*mcall)->token->pos, "Trying to resolve type of left hand side.");
+ }
+
+ AstTyped* implicit_argument = (*mcall)->left;
+
+ // Symbol resolution should have ensured that this is call node.
+ AstCall* call_node = (AstCall *) (*mcall)->right;
+ assert(call_node->kind == Ast_Kind_Call);
+
+ // :Idempotency
+ if (((*mcall)->flags & Ast_Flag_Has_Been_Checked) == 0) {
+ // Implicitly take the address of the value if it is not already a pointer type.
+ // This could be weird to think about semantically so some testing with real code
+ // would be good. - brendanfh 2020/02/05
+ if (implicit_argument->type->kind != Type_Kind_Pointer)
+ implicit_argument = (AstTyped *) make_address_of(context.ast_alloc, implicit_argument);
+
+ implicit_argument = (AstTyped *) make_argument(context.ast_alloc, implicit_argument);
+
+ bh_arr_insertn(call_node->args.values, 0, 1);
+ call_node->args.values[0] = implicit_argument;
+ }
+ (*mcall)->flags |= Ast_Flag_Has_Been_Checked;
+
+ CHECK(call, &call_node);
+ call_node->next = (*mcall)->next;
+
+ *mcall = (AstBinaryOp *) call_node;
+ return Check_Success;
+}
+
+CheckStatus check_size_of(AstSizeOf* so) {
+ fill_in_array_count(so->so_ast_type);
+ CHECK(type, so->so_ast_type);
+
+ so->so_type = type_build_from_ast(context.ast_alloc, so->so_ast_type);
+ if (so->so_type == NULL)
+ YIELD(so->token->pos, "Trying to resolve type to take the size of.");
+
+ so->size = type_size_of(so->so_type);
+
+ return Check_Success;
+}
+
+CheckStatus check_align_of(AstAlignOf* ao) {
+ fill_in_array_count(ao->ao_ast_type);
+ CHECK(type, ao->ao_ast_type);
+
+ ao->ao_type = type_build_from_ast(context.ast_alloc, ao->ao_ast_type);
+ if (ao->ao_type == NULL)
+ YIELD(ao->token->pos, "Trying to resolve type to take the alignment of.");
+
+ ao->alignment = type_alignment_of(ao->ao_type);
+
+ return Check_Success;
+}
+
+CheckStatus check_expression(AstTyped** pexpr) {
+ AstTyped* expr = *pexpr;
+ if (expr->kind > Ast_Kind_Type_Start && expr->kind < Ast_Kind_Type_End) {
+ // This is to ensure that the type will exist when compiling. For example, a poly-call type
+ // would have to wait for the entity to pass through, which the code generation does not know
+ // about.
+ if (expr->kind == Ast_Kind_Typeof) {
+ CHECK(type, (AstType *) expr);
+ }
+
+ // Don't try to construct a polystruct ahead of time because you can't.
+ if (expr->kind != Ast_Kind_Poly_Struct_Type) {
+ if (type_build_from_ast(context.ast_alloc, (AstType*) expr) == NULL) {
+ YIELD(expr->token->pos, "Trying to construct type.");
+ }
+ }
+
+ expr->type = &basic_types[Basic_Kind_Type_Index];
+ return Check_Success;
+ }
+
+ if (expr->kind == Ast_Kind_Polymorphic_Proc) {
+ // polymorphic procedures do not need to be checked. Their concrete instantiations
+ // will be checked when they are created.
+ return Check_Success;
+ }
+
+ if (expr->kind == Ast_Kind_Macro) {
+ return Check_Success;
+ }
+
+ fill_in_type(expr);
+ current_checking_level = EXPRESSION_LEVEL;
+
+ CheckStatus retval = Check_Success;
+ switch (expr->kind) {
+ case Ast_Kind_Binary_Op: retval = check_binaryop((AstBinaryOp **) pexpr); break;
+ case Ast_Kind_Unary_Op: retval = check_unaryop((AstUnaryOp **) pexpr); break;
+
+ case Ast_Kind_Call: retval = check_call((AstCall **) pexpr); break;
+ case Ast_Kind_Argument: retval = check_argument((AstArgument **) pexpr); break;
+ case Ast_Kind_Block: retval = check_block((AstBlock *) expr); break;
+
+ case Ast_Kind_Symbol:
+ onyx_report_error(expr->token->pos,
+ "Symbol was unresolved in symbol resolution phase, '%b'. This is definitely a compiler bug.",
+ expr->token->text, expr->token->length);
+ retval = Check_Error;
+ break;
+
+ case Ast_Kind_Param:
+ if (expr->type == NULL) {
+ onyx_report_error(expr->token->pos, "Parameter with bad type.");
+ retval = Check_Error;
+ }
+ break;
+
+ case Ast_Kind_Local: break;
+
+ case Ast_Kind_Address_Of: retval = check_address_of((AstAddressOf *) expr); break;
+ case Ast_Kind_Dereference: retval = check_dereference((AstDereference *) expr); break;
+ case Ast_Kind_Slice:
+ case Ast_Kind_Subscript: retval = check_subscript((AstSubscript **) pexpr); break;
+ case Ast_Kind_Field_Access: retval = check_field_access((AstFieldAccess **) pexpr); break;
+ case Ast_Kind_Method_Call: retval = check_method_call((AstBinaryOp **) pexpr); break;
+ case Ast_Kind_Size_Of: retval = check_size_of((AstSizeOf *) expr); break;
+ case Ast_Kind_Align_Of: retval = check_align_of((AstAlignOf *) expr); break;
+ case Ast_Kind_Range_Literal: retval = check_range_literal((AstRangeLiteral **) pexpr); break;
+
+ case Ast_Kind_Global:
+ if (expr->type == NULL) {
+ onyx_report_error(expr->token->pos, "Global with unknown type.");
+ retval = Check_Error;
+ }
+ break;
+
+ case Ast_Kind_NumLit:
+ assert(expr->type != NULL);
+ break;
+
+ case Ast_Kind_Struct_Literal:
+ retval = check_struct_literal((AstStructLiteral *) expr);
+ break;
+
+ case Ast_Kind_Array_Literal:
+ retval = check_array_literal((AstArrayLiteral *) expr);
+ break;
+
+ case Ast_Kind_Function:
+ // NOTE: Will need something like this at some point
+ // AstFunction* func = (AstFunction *) expr;
+ // bh_arr_each(AstParam, param, func->params) {
+ // if (param->default_value != NULL) {
+ // onyx_message_add(Msg_Type_Literal,
+ // func->token->pos,
+ // "cannot use functions with default parameters in this way");
+ // retval = 1;
+ // break;
+ // }
+ // }
+ if (expr->type == NULL)
+ YIELD(expr->token->pos, "Waiting for function type to be resolved.");
+
+ expr->flags |= Ast_Flag_Function_Used;
+ break;
+
+ case Ast_Kind_Directive_Solidify:
+ *pexpr = (AstTyped *) ((AstDirectiveSolidify *) expr)->resolved_proc;
+ break;
+
+ case Ast_Kind_Directive_Defined:
+ *pexpr = (AstTyped *) make_bool_literal(context.ast_alloc, ((AstDirectiveDefined *) expr)->is_defined);
+ fill_in_type(*pexpr);
+ break;
+
+ case Ast_Kind_Compound:
+ CHECK(compound, (AstCompound *) expr);
+ break;
+
+ case Ast_Kind_Call_Site:
+ // NOTE: This has to be set here because if it were to be set in the parser,
+ // builtin_callsite_type wouldn't be known when parsing the builtin.onyx file.
+ expr->type_node = builtin_callsite_type;
+ break;
+
+ case Ast_Kind_If_Expression:
+ CHECK(if_expression, (AstIfExpression *) expr);
+ break;
+
+ case Ast_Kind_Alias:
+ CHECK(expression, &((AstAlias *) expr)->alias);
+ expr->flags |= (((AstAlias *) expr)->alias->flags & Ast_Flag_Comptime);
+ expr->type = ((AstAlias *) expr)->alias->type;
+ break;
+
+ case Ast_Kind_Directive_Insert:
+ retval = check_insert_directive((AstDirectiveInsert **) pexpr);
+ break;
+
+ case Ast_Kind_Code_Block:
+ expr->flags |= Ast_Flag_Comptime;
+ fill_in_type(expr);
+ break;
+
+ case Ast_Kind_Do_Block:
+ retval = check_do_block((AstDoBlock **) pexpr);
+ break;
+
+ case Ast_Kind_StrLit: break;
+ case Ast_Kind_File_Contents: break;
+ case Ast_Kind_Overloaded_Function: break;
+ case Ast_Kind_Enum_Value: break;
+ case Ast_Kind_Memres: break;
+ case Ast_Kind_Polymorphic_Proc: break;
+ case Ast_Kind_Package: break;
+ case Ast_Kind_Error: break;
+ case Ast_Kind_Unary_Field_Access: break;
+
+ // NOTE: The only way to have an Intrinsic_Call node is to have gone through the
+ // checking of a call node at least once.
+ case Ast_Kind_Intrinsic_Call: break;
+
+ default:
+ retval = Check_Error;
+ onyx_report_error(expr->token->pos, "UNEXPECTED INTERNAL COMPILER ERROR");
+ DEBUG_HERE;
+ break;
+ }
+
+ return retval;
+}
+
+CheckStatus check_global(AstGlobal* global) {
+ fill_in_type((AstTyped *) global);
+
+ if (global->type == NULL) {
+ YIELD(global->token->pos, "Trying to resolve type for global.");
+ }
+
+ return Check_Success;
+}
+
+CheckStatus check_insert_directive(AstDirectiveInsert** pinsert) {
+ AstDirectiveInsert* insert = *pinsert;
+ if (insert->flags & Ast_Flag_Has_Been_Checked) return Check_Success;
+
+ CHECK(expression, &insert->code_expr);
+ if (insert->code_expr->type == NULL) {
+ if (insert->code_expr->entity && insert->code_expr->entity->state >= Entity_State_Code_Gen) {
+ ERROR(insert->token->pos, "Expected expression of type 'Code'.");
+ }
+
+ // Bad wording for the message.
+ YIELD(insert->token->pos, "Waiting for resolution to code expression type.");
+ }
+
+ Type* code_type = type_build_from_ast(context.ast_alloc, builtin_code_type);
+
+ if (!type_check_or_auto_cast(&insert->code_expr, code_type)) {
+ ERROR_(insert->token->pos, "#insert expected a value of type 'Code', got '%s'.",
+ type_get_name(insert->code_expr->type));
+ }
+
+ AstCodeBlock* code_block = (AstCodeBlock *) insert->code_expr;
+ code_block = (AstCodeBlock *) strip_aliases((AstNode *) code_block);
+
+ assert(code_block->kind == Ast_Kind_Code_Block);
+
+ AstNode* cloned_block = ast_clone(context.ast_alloc, code_block->code);
+ cloned_block->next = insert->next;
+ *(AstNode **) pinsert = cloned_block;
+
+ insert->flags |= Ast_Flag_Has_Been_Checked;
+
+ return Check_Return_To_Symres;
+}
+
+CheckStatus check_statement(AstNode** pstmt) {
+ AstNode* stmt = *pstmt;
+
+ current_checking_level = STATEMENT_LEVEL;
+
+ switch (stmt->kind) {
+ case Ast_Kind_Jump: return Check_Success;
+
+ case Ast_Kind_Return: return check_return((AstReturn *) stmt);
+ case Ast_Kind_If: return check_if((AstIfWhile *) stmt);
+ case Ast_Kind_Static_If: return check_if((AstIfWhile *) stmt);
+ case Ast_Kind_While: return check_while((AstIfWhile *) stmt);
+ case Ast_Kind_For: return check_for((AstFor *) stmt);
+ case Ast_Kind_Switch: return check_switch((AstSwitch *) stmt);
+ case Ast_Kind_Block: return check_block((AstBlock *) stmt);
+ case Ast_Kind_Defer: return check_statement(&((AstDefer *) stmt)->stmt);
+ case Ast_Kind_Call: {
+ CHECK(call, (AstCall **) pstmt);
+ stmt->flags |= Ast_Flag_Expr_Ignored;
+ return Check_Success;
+ }
+
+ case Ast_Kind_Binary_Op:
+ CHECK(binaryop, (AstBinaryOp **) pstmt);
+ (*pstmt)->flags |= Ast_Flag_Expr_Ignored;
+ return Check_Success;
+
+ // NOTE: Local variable declarations used to be removed after the symbol
+ // resolution phase because long long ago, all locals needed to be known
+ // in a block in order to efficiently allocate enough space and registers
+ // for them all. Now with LocalAllocator, this is no longer necessary.
+ // Therefore, locals stay in the tree and need to be passed along.
+ case Ast_Kind_Local: {
+ AstTyped* typed_stmt = (AstTyped *) stmt;
+ fill_in_type(typed_stmt);
+ if (typed_stmt->type_node != NULL && typed_stmt->type == NULL) {
+ CHECK(type, typed_stmt->type_node);
+
+ if (!node_is_type((AstNode *) typed_stmt->type_node)) {
+ ERROR(stmt->token->pos, "Local's type is not a type.");
+ }
+
+ YIELD(typed_stmt->token->pos, "Waiting for local variable's type.");
+ }
+ return Check_Success;
+ }
+
+ default:
+ CHECK(expression, (AstTyped **) pstmt);
+ (*pstmt)->flags |= Ast_Flag_Expr_Ignored;
+ return Check_Success;
+ }
+}
+
+CheckStatus check_statement_chain(AstNode** start) {
+ while (*start) {
+ CHECK(statement, start);
+ start = &(*start)->next;
+ }
+
+ return Check_Success;
+}
+
+CheckStatus check_block(AstBlock* block) {
+ CHECK(statement_chain, &block->body);
+
+ // CLEANUP: There will need to be some other method of
+ // checking the following conditions.
+ //
+ // bh_arr_each(AstTyped *, value, block->allocate_exprs) {
+ // fill_in_type(*value);
+
+ // if ((*value)->kind == Ast_Kind_Local) {
+ // if ((*value)->type == NULL) {
+ // onyx_report_error((*value)->token->pos,
+ // "Unable to resolve type for local '%b'.",
+ // (*value)->token->text, (*value)->token->length);
+ // return Check_Error;
+ // }
+
+ // if ((*value)->type->kind == Type_Kind_Compound) {
+ // onyx_report_error((*value)->token->pos,
+ // "Compound type not allowed as local variable type. Try splitting this into multiple variables.");
+ // return Check_Error;
+ // }
+ // }
+ // }
+
+ return Check_Success;
+}
+
+CheckStatus check_function(AstFunction* func) {
+ if (func->flags & Ast_Flag_Already_Checked) return Check_Success;
+ if (func->entity_header && func->entity_header->state < Entity_State_Code_Gen)
+ YIELD(func->token->pos, "Waiting for procedure header to pass type-checking");
+
+ expected_return_type = &func->type->Function.return_type;
+ if (func->body) {
+ CheckStatus status = check_block(func->body);
+ if (status == Check_Error && func->generated_from && context.cycle_detected == 0)
+ ERROR(func->generated_from->pos, "Error in polymorphic procedure generated from this location.");
+
+ if (status != Check_Success) return status;
+ }
+
+ if (*expected_return_type == &type_auto_return) {
+ *expected_return_type = &basic_types[Basic_Kind_Void];
+ }
+
+ func->flags |= Ast_Flag_Already_Checked;
+ return Check_Success;
+}
+
+CheckStatus check_overloaded_function(AstOverloadedFunction* func) {
+ b32 done = 1;
+
+ bh_imap all_overloads;
+ bh_imap_init(&all_overloads, global_heap_allocator, 4);
+ build_all_overload_options(func->overloads, &all_overloads);
+
+ bh_arr_each(bh__imap_entry, entry, all_overloads.entries) {
+ AstTyped* node = (AstTyped *) entry->key;
+ if (node->kind == Ast_Kind_Overloaded_Function) continue;
+
+ if ( node->kind != Ast_Kind_Function
+ && node->kind != Ast_Kind_Polymorphic_Proc
+ && node->kind != Ast_Kind_Macro) {
+ onyx_report_error(node->token->pos, "Overload option not procedure or macro. Got '%s'",
+ onyx_ast_node_kind_string(node->kind));
+
+ bh_imap_free(&all_overloads);
+ return Check_Error;
+ }
+
+ if (node->kind == Ast_Kind_Function) {
+ AstFunction* func = (AstFunction *) node;
+
+ if (func->entity_header && func->entity_header->state <= Entity_State_Check_Types) {
+ done = 0;
+ }
+ }
+ }
+
+ bh_imap_free(&all_overloads);
+
+ if (done) return Check_Success;
+ else YIELD(func->token->pos, "Waiting for all options to pass type-checking.");
+}
+
+CheckStatus check_struct(AstStructType* s_node) {
+ if (s_node->entity_defaults && s_node->entity_defaults->state < Entity_State_Check_Types)
+ YIELD(s_node->token->pos, "Waiting for struct member defaults to pass symbol resolution.");
+
+ bh_arr_each(AstStructMember *, smem, s_node->members) {
+ if ((*smem)->type_node != NULL) {
+ CHECK(type, (*smem)->type_node);
+ }
+
+ if ((*smem)->type_node == NULL && (*smem)->initial_value != NULL) {
+ CHECK(expression, &(*smem)->initial_value);
+
+ fill_in_type((*smem)->initial_value);
+ if ((*smem)->initial_value->type == NULL)
+ YIELD((*smem)->initial_value->token->pos, "Trying to resolve type for initial value for member.");
+
+ resolve_expression_type((*smem)->initial_value);
+ if ((*smem)->type == NULL) (*smem)->type = (*smem)->initial_value->type;
+
+ if ((*smem)->type == NULL) {
+ ERROR((*smem)->initial_value->token->pos, "Unable to deduce type of initial value. This is probably a compiler bug.");
+ }
+ }
+ }
+
+ // NOTE: fills in the stcache
+ type_build_from_ast(context.ast_alloc, (AstType *) s_node);
+ if (s_node->stcache == NULL || !s_node->stcache_is_valid)
+ YIELD(s_node->token->pos, "Waiting for type to be constructed.");
+
+ bh_arr_each(StructMember *, smem, s_node->stcache->Struct.memarr) {
+ if ((*smem)->type->kind == Type_Kind_Compound) {
+ ERROR(s_node->token->pos, "Compound types are not allowed as struct member types.");
+ }
+ }
+
+ return Check_Success;
+}
+
+CheckStatus check_struct_defaults(AstStructType* s_node) {
+ if (s_node->entity_type && s_node->entity_type->state < Entity_State_Code_Gen)
+ YIELD(s_node->token->pos, "Waiting for struct type to be constructed before checking defaulted members.");
+
+ bh_arr_each(StructMember *, smem, s_node->stcache->Struct.memarr) {
+ if ((*smem)->initial_value && *(*smem)->initial_value) {
+ CHECK(expression, (*smem)->initial_value);
+
+ if (!type_check_or_auto_cast((*smem)->initial_value, (*smem)->type)) {
+ ERROR_((*(*smem)->initial_value)->token->pos,
+ "Mismatched type for initial value, expected '%s', got '%s'.",
+ type_get_name((*smem)->type),
+ type_get_name((*(*smem)->initial_value)->type));
+ }
+
+ resolve_expression_type(*(*smem)->initial_value);
+ }
+ }
+
+ return Check_Success;
+}
+
+CheckStatus check_function_header(AstFunction* func) {
+ //if (func->entity_body && func->entity_body->state < Entity_State_Check_Types)
+ // YIELD(func->token->pos, "Waiting for function body to complete symbol resolution to check header.");
+
+ b32 expect_default_param = 0;
+ b32 has_had_varargs = 0;
+
+ bh_arr_each(AstParam, param, func->params) {
+ AstLocal* local = param->local;
+
+ if (expect_default_param && param->default_value == NULL) {
+ ERROR(local->token->pos,
+ "All parameters must have default values after the first default valued parameter.");
+ }
+
+ if (has_had_varargs && param->vararg_kind != VA_Kind_Not_VA) {
+ ERROR(local->token->pos,
+ "Can only have one param that is of variable argument type.");
+ }
+
+ if (has_had_varargs && param->vararg_kind != VA_Kind_Not_VA) {
+ ERROR(local->token->pos,
+ "Variable arguments must be last in parameter list");
+ }
+
+ if (param->vararg_kind == VA_Kind_Untyped) {
+ // HACK
+ if (builtin_vararg_type_type == NULL)
+ builtin_vararg_type_type = type_build_from_ast(context.ast_alloc, builtin_vararg_type);
+
+ local->type = builtin_vararg_type_type;
+ }
+
+ if (param->default_value != NULL) {
+ if (param->vararg_kind != VA_Kind_Not_VA) {
+ ERROR(local->token->pos, "Variadic arguments cannot have default values.");
+ }
+
+ CHECK(expression, ¶m->default_value);
+
+ if (local->type_node == NULL && local->type == NULL) {
+ local->type = resolve_expression_type(param->default_value);
+ }
+
+ expect_default_param = 1;
+ }
+
+ if (local->type_node != NULL) CHECK(type, local->type_node);
+ if (local->type_node != NULL) {
+ if (!node_is_type((AstNode *) local->type_node)) {
+ ERROR(local->token->pos, "Parameter type is not a type.");
+ }
+ }
+
+ fill_in_type((AstTyped *) local);
+ if (local->type == NULL) {
+ // onyx_report_error(param->local->token->pos,
+ // "Unable to resolve type for parameter, '%b'",
+ // local->token->text,
+ // local->token->length);
+ YIELD(local->token->pos, "Waiting for parameter type to be known.");
+ }
+
+ if (local->type->kind == Type_Kind_Compound) {
+ ERROR(param->local->token->pos, "Compound types are not allowed as parameter types. Try splitting this into multiple parameters.");
+ }
+
+ // NOTE: I decided to make parameter default values not type checked against
+ // the actual parameter type. The actual type checking will happen in check_call
+ // when the default value is used as an argument and then has to be checked against
+ // the parameter type - brendanfh 2021/01/06
+ // if (param->default_value != NULL) {
+ // if (!type_check_or_auto_cast(¶m->default_value, param->local->type)) {
+ // onyx_report_error(param->local->token->pos,
+ // "Expected default value of type '%s', was of type '%s'.",
+ // type_get_name(param->local->type),
+ // type_get_name(param->default_value->type));
+ // return Check_Error;
+ // }
+ // }
+
+ if (param->vararg_kind != VA_Kind_Not_VA) has_had_varargs = 1;
+
+ if (local->type->kind != Type_Kind_Array && type_size_of(local->type) == 0) {
+ ERROR(local->token->pos, "Function parameters cannot have zero-width types.");
+ }
+ }
+
+ if (func->return_type != NULL) CHECK(type, func->return_type);
+
+ func->type = type_build_function_type(context.ast_alloc, func);
+ if (func->type == NULL) YIELD(func->token->pos, "Waiting for function type to be constructed");
+
+ return Check_Success;
+}
+
+CheckStatus check_memres_type(AstMemRes* memres) {
+ CHECK(type, memres->type_node);
+ fill_in_type((AstTyped *) memres);
+ if (memres->type_node && !memres->type) YIELD(memres->token->pos, "Waiting for global type to be constructed.");
+ return Check_Success;
+}
+
+CheckStatus check_memres(AstMemRes* memres) {
+ if (memres->initial_value != NULL) {
+ CHECK(expression, &memres->initial_value);
+ resolve_expression_type(memres->initial_value);
+
+ if ((memres->initial_value->flags & Ast_Flag_Comptime) == 0) {
+ ERROR(memres->initial_value->token->pos, "Top level expressions must be compile time known.");
+ }
+
+ if (memres->type != NULL) {
+ Type* memres_type = memres->type;
+ if (!type_check_or_auto_cast(&memres->initial_value, memres_type)) {
+ ERROR_(memres->token->pos,
+ "Cannot assign value of type '%s' to a '%s'.",
+ node_get_type_name(memres->initial_value),
+ type_get_name(memres_type));
+ }
+
+ } else {
+ if (memres->initial_value->type == NULL && memres->initial_value->entity != NULL && memres->initial_value->entity->state <= Entity_State_Check_Types) {
+ YIELD(memres->token->pos, "Waiting for global type to be constructed.");
+ }
+ memres->type = memres->initial_value->type;
+ }
+ }
+
+ return Check_Success;
+}
+
+CheckStatus check_type(AstType* type) {
+ if (type == NULL) return Check_Success;
+
+ while (type->kind == Ast_Kind_Type_Alias)
+ type = ((AstTypeAlias *) type)->to;
+
+ switch (type->kind) {
+ case Ast_Kind_Poly_Call_Type: {
+ AstPolyCallType* pc_node = (AstPolyCallType *) type;
+
+ bh_arr_each(AstNode *, param, pc_node->params) {
+ if (!node_is_type(*param)) {
+ CHECK(expression, (AstTyped **) param);
+ resolve_expression_type((AstTyped *) *param);
+ }
+ }
+
+ break;
+ }
+
+ case Ast_Kind_Typeof: {
+ AstTypeOf *type_of = (AstTypeOf *) type;
+ CHECK(expression, (AstTyped **) &type_of->expr);
+ resolve_expression_type(type_of->expr);
+
+ if (type_of->expr->type == NULL) {
+ YIELD(type_of->token->pos, "Trying to check type for type-of expression.");
+ }
+
+ type_of->resolved_type = type_of->expr->type;
+ break;
+ }
+
+ case Ast_Kind_Pointer_Type: CHECK(type, ((AstPointerType *) type)->elem); break;
+ case Ast_Kind_Slice_Type: CHECK(type, ((AstSliceType *) type)->elem); break;
+ case Ast_Kind_DynArr_Type: CHECK(type, ((AstDynArrType *) type)->elem); break;
+ case Ast_Kind_VarArg_Type: CHECK(type, ((AstVarArgType *) type)->elem); break;
+
+ case Ast_Kind_Function_Type: {
+ AstFunctionType* ftype = (AstFunctionType *) type;
+
+ CHECK(type, ftype->return_type);
+
+ if (ftype->param_count > 0) {
+ fori (i, 0, (i64) ftype->param_count) {
+ CHECK(type, ftype->params[i]);
+ }
+ }
+ break;
+ }
+
+ case Ast_Kind_Type_Compound: {
+ AstCompoundType* ctype = (AstCompoundType *) type;
+
+ bh_arr_each(AstType *, type, ctype->types) CHECK(type, *type);
+ break;
+ }
+ }
+
+ return Check_Success;
+}
+
+CheckStatus check_static_if(AstIf* static_if) {
+ expression_types_must_be_known = 1;
+ CheckStatus result = check_expression(&static_if->cond);
+ if (result == Check_Yield_Macro) return Check_Yield_Macro;
+ expression_types_must_be_known = 0;
+
+ if (result > Check_Errors_Start || !(static_if->cond->flags & Ast_Flag_Comptime)) {
+ ERROR(static_if->token->pos, "Expected this condition to be compile time known.");
+ }
+
+ if (!type_is_bool(static_if->cond->type)) {
+ ERROR(static_if->token->pos, "Expected this condition to be a boolean value.");
+ }
+
+ static_if->flags |= Ast_Flag_Static_If_Resolved;
+
+ b32 resolution = static_if_resolution(static_if);
+
+ if (context.options->print_static_if_results)
+ bh_printf("Static if statement at %s:%d:%d resulted in %s\n",
+ static_if->token->pos.filename,
+ static_if->token->pos.line,
+ static_if->token->pos.column,
+ resolution ? "true" : "false");
+
+ if (resolution) {
+ bh_arr_each(Entity *, ent, static_if->true_entities) {
+ entity_heap_insert_existing(&context.entities, *ent);
+ }
+
+ } else {
+ bh_arr_each(Entity *, ent, static_if->false_entities) {
+ entity_heap_insert_existing(&context.entities, *ent);
+ }
+ }
+
+ return Check_Complete;
+}
+
+CheckStatus check_process_directive(AstNode* directive) {
+ if (directive->kind == Ast_Kind_Directive_Export) {
+ AstTyped* export = ((AstDirectiveExport *) directive)->export;
+ if (export->entity && export->entity->state <= Entity_State_Check_Types)
+ YIELD(directive->token->pos, "Waiting for export type to be known.");
+ }
+
+ return Check_Success;
+}
+
+CheckStatus check_macro(AstMacro* macro) {
+ if (macro->body->kind == Ast_Kind_Function) {
+ CHECK(function_header, (AstFunction *) macro->body);
+ }
+
+ return Check_Success;
+}
+
+CheckStatus check_node(AstNode* node) {
+ switch (node->kind) {
+ case Ast_Kind_Function: return check_function((AstFunction *) node);
+ case Ast_Kind_Overloaded_Function: return check_overloaded_function((AstOverloadedFunction *) node);
+ case Ast_Kind_Block: return check_block((AstBlock *) node);
+ case Ast_Kind_Return: return check_return((AstReturn *) node);
+ case Ast_Kind_If: return check_if((AstIfWhile *) node);
+ case Ast_Kind_Static_If: return check_if((AstIfWhile *) node);
+ case Ast_Kind_While: return check_while((AstIfWhile *) node);
+ case Ast_Kind_Call: return check_call((AstCall **) &node);
+ case Ast_Kind_Binary_Op: return check_binaryop((AstBinaryOp **) &node);
+ default: return check_expression((AstTyped **) &node);
+ }
+}
+
+void check_entity(Entity* ent) {
+ CheckStatus cs = Check_Success;
+
+ switch (ent->type) {
+ case Entity_Type_Foreign_Function_Header:
+ case Entity_Type_Function_Header: cs = check_function_header(ent->function); break;
+ case Entity_Type_Function: cs = check_function(ent->function); break;
+ case Entity_Type_Overloaded_Function: cs = check_overloaded_function(ent->overloaded_function); break;
+ case Entity_Type_Global: cs = check_global(ent->global); break;
+ case Entity_Type_Struct_Member_Default: cs = check_struct_defaults((AstStructType *) ent->type_alias); break;
+ case Entity_Type_Memory_Reservation_Type: cs = check_memres_type(ent->mem_res); break;
+ case Entity_Type_Memory_Reservation: cs = check_memres(ent->mem_res); break;
+ case Entity_Type_Static_If: cs = check_static_if(ent->static_if); break;
+ case Entity_Type_Macro: cs = check_macro(ent->macro);
+
+ case Entity_Type_Expression:
+ cs = check_expression(&ent->expr);
+ resolve_expression_type(ent->expr);
+ break;
+
+ case Entity_Type_Type_Alias:
+ if (ent->type_alias->kind == Ast_Kind_Struct_Type)
+ cs = check_struct((AstStructType *) ent->type_alias);
+ else
+ cs = check_type(ent->type_alias);
+ break;
+
+ case Entity_Type_Process_Directive: cs = check_process_directive((AstNode *) ent->expr); break;
+
+ case Entity_Type_File_Contents:
+ if (context.options->no_file_contents) {
+ onyx_report_error(ent->expr->token->pos, "#file_contents is disabled for this compilation.");
+ }
+ break;
+
+ default: break;
+ }
+
+ if (cs == Check_Success) ent->state = Entity_State_Code_Gen;
+ if (cs == Check_Complete) ent->state = Entity_State_Finalized;
+ if (cs == Check_Return_To_Symres) ent->state = Entity_State_Resolve_Symbols;
+ if (cs == Check_Yield_Macro) ent->macro_attempts++;
+ else {
+ ent->macro_attempts = 0;
+ ent->micro_attempts = 0;
+ }
+}
--- /dev/null
+#include "astnodes.h"
+#include "parser.h"
+#include "utils.h"
+
+static inline b32 should_clone(AstNode* node) {
+ if (node->flags & Ast_Flag_No_Clone) return 0;
+
+ switch (node->kind) {
+ // List of nodes that should not be copied
+ case Ast_Kind_Global:
+ case Ast_Kind_Memres:
+ case Ast_Kind_StrLit:
+ case Ast_Kind_Package:
+ case Ast_Kind_Enum_Type:
+ case Ast_Kind_Enum_Value:
+ case Ast_Kind_Overloaded_Function:
+ case Ast_Kind_Polymorphic_Proc:
+ case Ast_Kind_Alias:
+ case Ast_Kind_Code_Block:
+ case Ast_Kind_Macro:
+ case Ast_Kind_File_Contents:
+ return 0;
+
+ default: return 1;
+ }
+}
+
+static inline i32 ast_kind_to_size(AstNode* node) {
+ switch (node->kind) {
+ case Ast_Kind_Error: return sizeof(AstNode);
+ case Ast_Kind_Package: return sizeof(AstPackage);
+ case Ast_Kind_Load_File: return sizeof(AstInclude);
+ case Ast_Kind_Load_Path: return sizeof(AstInclude);
+ case Ast_Kind_Memres: return sizeof(AstMemRes);
+ case Ast_Kind_Binding: return sizeof(AstBinding);
+ case Ast_Kind_Function: return sizeof(AstFunction);
+ case Ast_Kind_Overloaded_Function: return sizeof(AstOverloadedFunction);
+ case Ast_Kind_Polymorphic_Proc: return sizeof(AstPolyProc);
+ case Ast_Kind_Block: return sizeof(AstBlock);
+ case Ast_Kind_Local: return sizeof(AstLocal);
+ case Ast_Kind_Global: return sizeof(AstGlobal);
+ case Ast_Kind_Symbol: return sizeof(AstNode);
+ case Ast_Kind_Unary_Op: return sizeof(AstUnaryOp);
+ case Ast_Kind_Binary_Op: return sizeof(AstBinaryOp);
+ case Ast_Kind_Type_Start: return 0;
+ case Ast_Kind_Type: return sizeof(AstType);
+ case Ast_Kind_Basic_Type: return sizeof(AstBasicType);
+ case Ast_Kind_Pointer_Type: return sizeof(AstPointerType);
+ case Ast_Kind_Function_Type: return sizeof(AstFunctionType) + ((AstFunctionType *) node)->param_count * sizeof(AstType *);
+ case Ast_Kind_Array_Type: return sizeof(AstArrayType);
+ case Ast_Kind_Slice_Type: return sizeof(AstSliceType);
+ case Ast_Kind_DynArr_Type: return sizeof(AstDynArrType);
+ case Ast_Kind_VarArg_Type: return sizeof(AstVarArgType);
+ case Ast_Kind_Struct_Type: return sizeof(AstStructType);
+ case Ast_Kind_Poly_Struct_Type: return sizeof(AstPolyStructType);
+ case Ast_Kind_Poly_Call_Type: return sizeof(AstPolyCallType);
+ case Ast_Kind_Enum_Type: return sizeof(AstEnumType);
+ case Ast_Kind_Type_Alias: return sizeof(AstTypeAlias);
+ case Ast_Kind_Type_Raw_Alias: return sizeof(AstTypeRawAlias);
+ case Ast_Kind_Type_Compound: return sizeof(AstCompoundType);
+ case Ast_Kind_Typeof: return sizeof(AstTypeOf);
+ case Ast_Kind_Type_End: return 0;
+ case Ast_Kind_Struct_Member: return sizeof(AstStructMember);
+ case Ast_Kind_Enum_Value: return sizeof(AstEnumValue);
+ case Ast_Kind_NumLit: return sizeof(AstNumLit);
+ case Ast_Kind_StrLit: return sizeof(AstStrLit);
+ case Ast_Kind_Param: return sizeof(AstLocal);
+ case Ast_Kind_Argument: return sizeof(AstArgument);
+ case Ast_Kind_Call: return sizeof(AstCall);
+ case Ast_Kind_Intrinsic_Call: return sizeof(AstCall);
+ case Ast_Kind_Return: return sizeof(AstReturn);
+ case Ast_Kind_Address_Of: return sizeof(AstAddressOf);
+ case Ast_Kind_Dereference: return sizeof(AstDereference);
+ case Ast_Kind_Subscript: return sizeof(AstSubscript);
+ case Ast_Kind_Slice: return sizeof(AstSubscript);
+ case Ast_Kind_Field_Access: return sizeof(AstFieldAccess);
+ case Ast_Kind_Unary_Field_Access: return sizeof(AstUnaryFieldAccess);
+ case Ast_Kind_Pipe: return sizeof(AstBinaryOp);
+ case Ast_Kind_Range_Literal: return sizeof(AstRangeLiteral);
+ case Ast_Kind_Method_Call: return sizeof(AstBinaryOp);
+ case Ast_Kind_Size_Of: return sizeof(AstSizeOf);
+ case Ast_Kind_Align_Of: return sizeof(AstAlignOf);
+ case Ast_Kind_File_Contents: return sizeof(AstFileContents);
+ case Ast_Kind_Struct_Literal: return sizeof(AstStructLiteral);
+ case Ast_Kind_Array_Literal: return sizeof(AstArrayLiteral);
+ case Ast_Kind_If: return sizeof(AstIfWhile);
+ case Ast_Kind_For: return sizeof(AstFor);
+ case Ast_Kind_While: return sizeof(AstIfWhile);
+ case Ast_Kind_Jump: return sizeof(AstJump);
+ case Ast_Kind_Use: return sizeof(AstUse);
+ case Ast_Kind_Defer: return sizeof(AstDefer);
+ case Ast_Kind_Switch: return sizeof(AstSwitch);
+ case Ast_Kind_Switch_Case: return sizeof(AstSwitchCase);
+ case Ast_Kind_Directive_Solidify: return sizeof(AstDirectiveSolidify);
+ case Ast_Kind_Compound: return sizeof(AstCompound);
+ case Ast_Kind_Named_Value: return sizeof(AstNamedValue);
+ case Ast_Kind_Call_Site: return sizeof(AstCallSite);
+ case Ast_Kind_Static_If: return sizeof(AstIfWhile);
+ case Ast_Kind_If_Expression: return sizeof(AstIfExpression);
+ case Ast_Kind_Directive_Insert: return sizeof(AstDirectiveInsert);
+ case Ast_Kind_Do_Block: return sizeof(AstDoBlock);
+ case Ast_Kind_Count: return 0;
+ }
+
+ return 0;
+}
+
+AstNode* ast_clone_list(bh_allocator a, void* n) {
+ AstNode* node = (AstNode *) n;
+ if (node == NULL) return NULL;
+
+ AstNode* root = ast_clone(a, node);
+ AstNode* curr = root->next;
+ AstNode** insertion = &root->next;
+
+ while (curr != NULL) {
+ curr = ast_clone(a, curr);
+ *insertion = curr;
+ insertion = &curr->next;
+ curr = curr->next;
+ }
+
+ return root;
+}
+
+#define C(nt, mname) ((nt *) nn)->mname = (void *) ast_clone(a, ((nt *) node)->mname);
+
+// NOTE: Using void* to avoid a lot of unnecessary casting
+AstNode* ast_clone(bh_allocator a, void* n) {
+ AstNode* node = (AstNode *) n;
+
+ if (node == NULL) return NULL;
+ if (!should_clone(node)) return node;
+
+ static int clone_depth = 0;
+ clone_depth++;
+
+ i32 node_size = ast_kind_to_size(node);
+ // bh_printf("Cloning %s with size %d\n", onyx_ast_node_kind_string(node->kind), node_size);
+
+ AstNode* nn = onyx_ast_node_new(a, node_size, node->kind);
+ memmove(nn, node, node_size);
+
+ switch ((u16) node->kind) {
+ case Ast_Kind_Binary_Op:
+ case Ast_Kind_Pipe:
+ case Ast_Kind_Method_Call:
+ C(AstBinaryOp, left);
+ C(AstBinaryOp, right);
+ break;
+
+ case Ast_Kind_Unary_Op:
+ C(AstUnaryOp, expr);
+ C(AstUnaryOp, type_node);
+ break;
+
+ case Ast_Kind_Param:
+ case Ast_Kind_Local:
+ C(AstLocal, type_node);
+ break;
+
+ case Ast_Kind_Call:
+ arguments_deep_clone(a, &((AstCall *) nn)->args, &((AstCall *) node)->args);
+ break;
+
+ case Ast_Kind_Argument:
+ C(AstArgument, value);
+ break;
+
+ case Ast_Kind_Address_Of:
+ C(AstAddressOf, expr);
+ break;
+
+ case Ast_Kind_Dereference:
+ C(AstDereference, expr);
+ break;
+
+ case Ast_Kind_Slice:
+ case Ast_Kind_Subscript:
+ C(AstSubscript, addr);
+ C(AstSubscript, expr);
+ break;
+
+ case Ast_Kind_Field_Access:
+ C(AstFieldAccess, expr);
+ break;
+
+ case Ast_Kind_Size_Of:
+ C(AstSizeOf, so_ast_type);
+ break;
+
+ case Ast_Kind_Align_Of:
+ C(AstAlignOf, ao_ast_type);
+ break;
+
+ case Ast_Kind_Struct_Literal: {
+ AstStructLiteral* st = (AstStructLiteral *) node;
+ AstStructLiteral* dt = (AstStructLiteral *) nn;
+
+ dt->stnode = (AstTyped *) ast_clone(a, st->stnode);
+
+ arguments_deep_clone(a, &dt->args, &st->args);
+ break;
+ }
+
+ case Ast_Kind_Array_Literal: {
+ AstArrayLiteral* st = (AstArrayLiteral *) node;
+ AstArrayLiteral* dt = (AstArrayLiteral *) nn;
+
+ dt->atnode = (AstTyped *) ast_clone(a, st->atnode);
+
+ dt->values = NULL;
+ bh_arr_new(global_heap_allocator, dt->values, bh_arr_length(st->values));
+ bh_arr_each(AstTyped *, val, st->values)
+ bh_arr_push(dt->values, (AstTyped *) ast_clone(a, *val));
+
+ break;
+ }
+
+ case Ast_Kind_Range_Literal:
+ C(AstRangeLiteral, low);
+ C(AstRangeLiteral, high);
+ C(AstRangeLiteral, step);
+ break;
+
+ case Ast_Kind_Return:
+ C(AstReturn, expr);
+ break;
+
+ case Ast_Kind_Block:
+ ((AstBlock *) nn)->body = ast_clone_list(a, ((AstBlock *) node)->body);
+ break;
+
+ case Ast_Kind_Defer:
+ C(AstDefer, stmt);
+ break;
+
+ case Ast_Kind_For:
+ C(AstFor, var);
+ C(AstFor, iter);
+ C(AstFor, stmt);
+ break;
+
+ case Ast_Kind_If:
+ case Ast_Kind_While:
+ ((AstIfWhile *) nn)->initialization = ast_clone_list(a, ((AstIfWhile *) node)->initialization);
+
+ C(AstIfWhile, cond);
+ //fallthrough
+
+ case Ast_Kind_Static_If:
+ C(AstIfWhile, true_stmt);
+ C(AstIfWhile, false_stmt);
+ break;
+
+ case Ast_Kind_Switch: {
+ AstSwitch* dw = (AstSwitch *) nn;
+ AstSwitch* sw = (AstSwitch *) node;
+
+ ((AstSwitch *) nn)->initialization = ast_clone_list(a, ((AstSwitch *) node)->initialization);
+ dw->expr = (AstTyped *) ast_clone(a, sw->expr);
+
+ dw->default_case = (AstBlock *) ast_clone(a, sw->default_case);
+
+ dw->cases = NULL;
+ bh_arr_new(global_heap_allocator, dw->cases, bh_arr_length(sw->cases));
+
+ bh_arr_each(AstSwitchCase, c, sw->cases) {
+ bh_arr(AstTyped *) new_values = NULL;
+ bh_arr_new(global_heap_allocator, new_values, bh_arr_length(c->values));
+ bh_arr_each(AstTyped *, value, c->values)
+ bh_arr_push(new_values, (AstTyped *) ast_clone(a, *value));
+
+ AstSwitchCase sc;
+ sc.values = new_values;
+ sc.block = (AstBlock *) ast_clone(a, c->block);
+ bh_arr_push(dw->cases, sc);
+ }
+ break;
+ }
+
+ case Ast_Kind_Pointer_Type:
+ C(AstPointerType, elem);
+ break;
+
+ case Ast_Kind_Array_Type:
+ C(AstArrayType, count_expr);
+ C(AstArrayType, elem);
+ break;
+
+ case Ast_Kind_Slice_Type:
+ C(AstSliceType, elem);
+ break;
+
+ case Ast_Kind_DynArr_Type:
+ C(AstDynArrType, elem);
+ break;
+
+ case Ast_Kind_VarArg_Type:
+ C(AstVarArgType, elem);
+ break;
+
+ case Ast_Kind_Type_Alias:
+ C(AstTypeAlias, to);
+ break;
+
+ case Ast_Kind_Struct_Type: {
+ AstStructType* ds = (AstStructType *) nn;
+ AstStructType* ss = (AstStructType *) node;
+
+ ds->members = NULL;
+ bh_arr_new(global_heap_allocator, ds->members, bh_arr_length(ss->members));
+
+ bh_arr_each(AstStructMember *, smem, ss->members) {
+ bh_arr_push(ds->members, (AstStructMember *) ast_clone(a, *smem));
+ }
+
+ ds->stcache = NULL;
+ break;
+ }
+
+ case Ast_Kind_Struct_Member:
+ C(AstStructMember, type_node);
+ C(AstStructMember, initial_value);
+ break;
+
+ case Ast_Kind_Poly_Call_Type: {
+ AstPolyCallType* pcd = (AstPolyCallType *) nn;
+ AstPolyCallType* pcs = (AstPolyCallType *) node;
+
+ pcd->callee = (AstType *) ast_clone(a, pcs->callee);
+ pcd->params = NULL;
+ bh_arr_new(global_heap_allocator, pcd->params, bh_arr_length(pcs->params));
+
+ bh_arr_each(AstNode *, param, pcs->params) {
+ bh_arr_push(pcd->params, ast_clone(a, *param));
+ }
+
+ break;
+ }
+
+ case Ast_Kind_Type_Compound: {
+ AstCompoundType* cd = (AstCompoundType *) nn;
+ AstCompoundType* cs = (AstCompoundType *) node;
+
+ cd->types = NULL;
+ bh_arr_new(global_heap_allocator, cd->types, bh_arr_length(cs->types));
+
+ bh_arr_each(AstType *, type, cs->types) {
+ bh_arr_push(cd->types, (AstType *) ast_clone(a, (AstNode *) *type));
+ }
+ break;
+ }
+
+ case Ast_Kind_Function_Type:
+ C(AstFunctionType, return_type);
+ ((AstFunctionType *) nn)->param_count = ((AstFunctionType *) node)->param_count;
+ fori (i, 0, (i64) ((AstFunctionType *) nn)->param_count) {
+ ((AstFunctionType *) nn)->params[i] = (AstType *) ast_clone(a, ((AstFunctionType *) node)->params[i]);
+ }
+ break;
+
+ case Ast_Kind_Binding:
+ bh_printf("Cloning binding: %b\n", node->token->text, node->token->length);
+ C(AstTyped, type_node);
+ C(AstBinding, node);
+ break;
+
+ case Ast_Kind_Function: {
+ if (clone_depth > 1) {
+ clone_depth--;
+ return node;
+ }
+
+ AstFunction* df = (AstFunction *) nn;
+ AstFunction* sf = (AstFunction *) node;
+
+ if (sf->flags & Ast_Flag_Foreign) return node;
+
+ df->return_type = (AstType *) ast_clone(a, sf->return_type);
+ df->body = (AstBlock *) ast_clone(a, sf->body);
+
+ df->params = NULL;
+ bh_arr_new(global_heap_allocator, df->params, bh_arr_length(sf->params));
+
+ bh_arr_each(AstParam, param, sf->params) {
+ AstParam new_param = { 0 };
+ new_param.local = (AstLocal *) ast_clone(a, param->local);
+ new_param.default_value = (AstTyped *) ast_clone(a, param->default_value);
+ new_param.vararg_kind = param->vararg_kind;
+ bh_arr_push(df->params, new_param);
+ }
+
+ break;
+ }
+
+ case Ast_Kind_Use:
+ C(AstUse, expr);
+ break;
+
+ case Ast_Kind_Directive_Solidify: {
+ AstDirectiveSolidify* dd = (AstDirectiveSolidify *) nn;
+ AstDirectiveSolidify* sd = (AstDirectiveSolidify *) node;
+
+ dd->poly_proc = (AstPolyProc *) ast_clone(a, (AstNode *) sd->poly_proc);
+ dd->resolved_proc = NULL;
+
+ dd->known_polyvars = NULL;
+ bh_arr_new(global_heap_allocator, dd->known_polyvars, bh_arr_length(sd->known_polyvars));
+
+ bh_arr_each(AstPolySolution, sln, sd->known_polyvars) {
+ AstPolySolution new_sln;
+ new_sln.kind = sln->kind;
+ new_sln.poly_sym = (AstNode *) ast_clone(a, (AstNode *) sln->poly_sym);
+ new_sln.ast_type = (AstType *) ast_clone(a, (AstNode *) sln->ast_type);
+ bh_arr_push(dd->known_polyvars, new_sln);
+ }
+
+ break;
+ }
+
+ case Ast_Kind_Compound: {
+ AstCompound* cd = (AstCompound *) nn;
+ AstCompound* cs = (AstCompound *) node;
+
+ cd->exprs = NULL;
+ bh_arr_new(global_heap_allocator, cd->exprs, bh_arr_length(cs->exprs));
+
+ bh_arr_each(AstTyped *, expr, cs->exprs) {
+ bh_arr_push(cd->exprs, (AstTyped *) ast_clone(a, (AstNode *) *expr));
+ }
+ break;
+ }
+
+ case Ast_Kind_Named_Value:
+ C(AstNamedValue, value);
+ break;
+
+ case Ast_Kind_If_Expression:
+ C(AstIfExpression, cond);
+ C(AstIfExpression, true_expr);
+ C(AstIfExpression, false_expr);
+ break;
+
+ case Ast_Kind_Directive_Insert:
+ C(AstDirectiveInsert, code_expr);
+ break;
+
+ case Ast_Kind_Typeof:
+ C(AstTypeOf, expr);
+ ((AstTypeOf *) nn)->resolved_type = NULL;
+ break;
+
+ case Ast_Kind_Do_Block:
+ C(AstDoBlock, block);
+ ((AstDoBlock *) nn)->type_node = (AstType *) &basic_type_auto_return;
+ break;
+ }
+
+ clone_depth--;
+ return nn;
+}
+
+#undef C
+
+AstFunction* clone_function_header(bh_allocator a, AstFunction* func) {
+ if (func->kind != Ast_Kind_Function) return NULL;
+
+ if (func->flags & Ast_Flag_Foreign) return func;
+
+ AstFunction* new_func = onyx_ast_node_new(a, sizeof(AstFunction), func->kind);
+ memmove(new_func, func, sizeof(AstFunction));
+
+ new_func->return_type = (AstType *) ast_clone(a, func->return_type);
+
+ new_func->params = NULL;
+ bh_arr_new(global_heap_allocator, new_func->params, bh_arr_length(func->params));
+ bh_arr_each(AstParam, param, func->params) {
+ AstParam new_param;
+ new_param.local = (AstLocal *) ast_clone(a, param->local);
+ new_param.default_value = (AstTyped *) ast_clone(a, param->default_value);
+ new_param.vararg_kind = param->vararg_kind;
+ bh_arr_push(new_func->params, new_param);
+ }
+
+ return new_func;
+}
+
+// Clones a function body from a given function. It is assumed that `dest` is
+// a function from `clone_function_header`.
+void clone_function_body(bh_allocator a, AstFunction* dest, AstFunction* source) {
+ if (dest->kind != Ast_Kind_Function) return;
+ if (source->kind != Ast_Kind_Function) return;
+
+ dest->body = (AstBlock *) ast_clone(a, source->body);
+}
--- /dev/null
+#include "doc.h"
+#include "utils.h"
+#include "types.h"
+
+static i32 cmp_doc_entry(const void * a, const void * b) {
+ DocEntry* d1 = (DocEntry *) a;
+ DocEntry* d2 = (DocEntry *) b;
+
+ return strncmp(d1->def, d2->def, 1024);
+}
+
+static i32 cmp_doc_package(const void * a, const void * b) {
+ DocPackage* d1 = (DocPackage *) a;
+ DocPackage* d2 = (DocPackage *) b;
+
+ return strncmp(d1->name, d2->name, 1024);
+}
+
+static char* node_to_doc_def(const char* sym, AstNode *node, bh_allocator a) {
+ static char buf[1024];
+ memset(buf, 0, 1023);
+
+ strncat(buf, sym, 1023);
+ strncat(buf, " :: ", 1023);
+
+ switch (node->kind) {
+ case Ast_Kind_Function: {
+ strncat(buf, "(", 1023);
+
+ AstFunction *func = (AstFunction *) node;
+ bh_arr_each(AstParam, param, func->params) {
+ if (param != func->params)
+ strncat(buf, ", ", 1023);
+
+ token_toggle_end(param->local->token);
+ strncat(buf, param->local->token->text, 1023);
+ token_toggle_end(param->local->token);
+
+ strncat(buf, ": ", 1023);
+
+ strncat(buf, type_get_name(param->local->type), 1023);
+
+ if (param->default_value != NULL) {
+ strncat(buf, " = <default>", 1023);
+ }
+ }
+
+ strncat(buf, ") -> ", 1023);
+ strncat(buf, type_get_name(func->type->Function.return_type), 1023);
+
+ break;
+ }
+
+ case Ast_Kind_Struct_Type: {
+ strncat(buf, "struct { ", 1023);
+
+ AstStructType* st = (AstStructType *) node;
+ bh_arr_each(AstStructMember *, smem, st->members) {
+
+ token_toggle_end((*smem)->token);
+ strncat(buf, (*smem)->token->text, 1023);
+ token_toggle_end((*smem)->token);
+
+ strncat(buf, ": ", 1023);
+
+ strncat(buf, type_get_name((*smem)->type), 1023);
+
+ strncat(buf, "; ", 1023);
+ }
+
+ strncat(buf, "}", 1023);
+ break;
+ }
+
+ case Ast_Kind_Basic_Type:
+ case Ast_Kind_Array_Type:
+ case Ast_Kind_Type_Alias:
+ case Ast_Kind_Slice_Type:
+ case Ast_Kind_DynArr_Type: {
+ strncat(buf, type_get_name(type_build_from_ast(global_heap_allocator, (AstType *) node)), 1023);
+ break;
+ }
+
+ default: {
+ strncat(buf, "<unimplemented printing>", 1023);
+ }
+ }
+
+ return bh_strdup(a, buf);
+}
+
+static DocPackage doc_package_create(Package* p, bh_allocator a) {
+ DocPackage dp;
+ dp.name = p->name;
+ dp.public_entries = NULL;
+ dp.private_entries = NULL;
+
+ bh_arr_new(global_heap_allocator, dp.public_entries, 16);
+ bh_arr_new(global_heap_allocator, dp.private_entries, 16);
+
+ bh_table_each_start(AstNode *, p->scope->symbols)
+ DocEntry de;
+ if (value->token) de.pos = value->token->pos;
+ de.def = node_to_doc_def(key, value, a);
+ de.sym = (char *) key;
+ de.additional = NULL;
+
+ bh_arr_push(dp.public_entries, de);
+ bh_table_each_end;
+
+ bh_table_each_start(AstNode *, p->private_scope->symbols)
+ DocEntry de;
+ if (value->token) de.pos = value->token->pos;
+ de.def = node_to_doc_def(key, value, a);
+ de.sym = (char *) key;
+ de.additional = NULL;
+
+ bh_arr_push(dp.private_entries, de);
+ bh_table_each_end;
+
+ qsort(dp.public_entries, bh_arr_length(dp.public_entries), sizeof(DocEntry), cmp_doc_entry);
+ qsort(dp.private_entries, bh_arr_length(dp.private_entries), sizeof(DocEntry), cmp_doc_entry);
+
+ return dp;
+}
+
+OnyxDocumentation onyx_docs_generate() {
+ OnyxDocumentation doc;
+
+ bh_arena_init(&doc.doc_arena, global_heap_allocator, 16 * 1024);
+ bh_allocator a = bh_arena_allocator(&doc.doc_arena);
+
+ doc.package_docs = NULL;
+ bh_arr_new(global_heap_allocator, doc.package_docs, 16);
+
+ bh_table_each_start(Package *, context.packages);
+ DocPackage dp = doc_package_create(value, a);
+ bh_arr_push(doc.package_docs, dp);
+ bh_table_each_end;
+
+ qsort(doc.package_docs, bh_arr_length(doc.package_docs), sizeof(DocPackage), cmp_doc_package);
+
+ return doc;
+}
+
+static void onyx_docs_emit_human(OnyxDocumentation* doc, bh_file* file) {
+ // NOTE: Disabling fancy line printing until I can make it better
+ #if 0
+ bh_arr_each(DocPackage, dp, doc->package_docs) {
+ bh_printf("Package '%s'\n", dp->name);
+
+ if (bh_arr_length(dp->public_entries) > 0) {
+ bh_printf("\xe2\x94\x9c\xe2\x94\x80 Public symbols\n");
+ bh_arr_each(DocEntry, de, dp->public_entries) {
+ bh_printf("\xe2\x94\x82 \xe2\x94\x9c\xe2\x94\x80 %s\n", de->def);
+
+ if (de->pos.filename != NULL)
+ bh_printf("\xe2\x94\x82 \xe2\x94\x82 at %s:%d,%d\n", de->pos.filename, de->pos.line, de->pos.column);
+ else
+ bh_printf("\xe2\x94\x82 \xe2\x94\x82 compiler built-in\n");
+
+ bh_printf("\xe2\x94\x82 \xe2\x94\x82\n");
+ }
+ }
+
+ if (bh_arr_length(dp->private_entries) > 0) {
+ bh_printf("\xe2\x94\x9c\xe2\x94\x80 Private symbols\n");
+ bh_arr_each(DocEntry, de, dp->private_entries) {
+ bh_printf("\xe2\x94\x82 \xe2\x94\x9c\xe2\x94\x80 %s\n", de->def);
+ if (de->pos.filename != NULL)
+ bh_printf("\xe2\x94\x82 \xe2\x94\x82 at %s:%d,%d\n", de->pos.filename, de->pos.line, de->pos.column);
+ else
+ bh_printf("\xe2\x94\x82 \xe2\x94\x82 compiler built-in\n");
+
+ bh_printf("\xe2\x94\x82 \xe2\x94\x82\n");
+ }
+
+ bh_printf("\n");
+ }
+ }
+ #else
+ bh_arr_each(DocPackage, dp, doc->package_docs) {
+ bh_fprintf(file, "Package '%s'\n", dp->name);
+
+ if (bh_arr_length(dp->public_entries) > 0) {
+ bh_fprintf(file, " Public symbols\n");
+ bh_arr_each(DocEntry, de, dp->public_entries) {
+ bh_fprintf(file, " %s\n", de->def);
+
+ if (de->pos.filename != NULL)
+ bh_fprintf(file, " at %s:%d,%d\n", de->pos.filename, de->pos.line, de->pos.column);
+ else
+ bh_fprintf(file, " compiler built-in\n");
+
+ bh_fprintf(file, " \n");
+ }
+ }
+
+ if (bh_arr_length(dp->private_entries) > 0) {
+ bh_fprintf(file, " Private symbols\n");
+ bh_arr_each(DocEntry, de, dp->private_entries) {
+ bh_fprintf(file, " %s\n", de->def);
+ if (de->pos.filename != NULL)
+ bh_fprintf(file, " at %s:%d,%d\n", de->pos.filename, de->pos.line, de->pos.column);
+ else
+ bh_fprintf(file, " compiler built-in\n");
+
+ bh_fprintf(file, " \n");
+ }
+
+ bh_fprintf(file, "\n");
+ }
+ }
+ #endif
+}
+
+
+static i32 sort_tags(const void* a, const void* b) {
+ DocEntry *da = *((DocEntry **) a);
+ DocEntry *db = *((DocEntry **) b);
+
+ return strcmp(da->sym, db->sym);
+}
+
+static void onyx_docs_emit_tags(OnyxDocumentation* doc) {
+ bh_file tags_file;
+ if (bh_file_create(&tags_file, "./tags") != BH_FILE_ERROR_NONE) {
+ bh_printf("Cannot create 'tags'.\n");
+ return;
+ }
+
+ bh_fprintf(&tags_file, "!_TAG_FILE_FORMAT\t2\n");
+ bh_fprintf(&tags_file, "!_TAG_FILE_SORTED\t1\n");
+ bh_fprintf(&tags_file, "!_TAG_OUTPUT_FILESEP\tslash\n");
+ bh_fprintf(&tags_file, "!_TAG_OUTPUT_MODE\tu-ctags\n");
+ bh_fprintf(&tags_file, "!_TAG_PROGRAM_AUTHOR\tOnyx Compiler\n");
+ bh_fprintf(&tags_file, "!_TAG_PROGRAM_NAME\tOnyx Compiler\n");
+ bh_fprintf(&tags_file, "!_TAG_PROGRAM_URL\thttps://github.com/brendanfh/onyx\n");
+ bh_fprintf(&tags_file, "!_TAG_PROGRAM_VERSION\t0.0.1\n");
+
+ bh_arr(DocEntry *) tags = NULL;
+ bh_arr_new(global_heap_allocator, tags, 256);
+
+ bh_arr_each(DocPackage, dp, doc->package_docs) {
+ bh_arr_each(DocEntry, de, dp->public_entries) bh_arr_push(tags, de);
+ bh_arr_each(DocEntry, de, dp->private_entries) bh_arr_push(tags, de);
+ }
+
+ qsort(tags, bh_arr_length(tags), sizeof(DocEntry *), sort_tags);
+
+ bh_arr_each(DocEntry *, tag, tags) {
+ if ((*tag)->pos.filename == NULL) continue;
+
+ i32 line_len = 0;
+ char *c = (*tag)->pos.line_start;
+ while (*c++ != '\n') line_len++;
+
+ bh_fprintf(&tags_file, "%s\t%s\t/^%b$/\n",
+ (*tag)->sym, (*tag)->pos.filename,
+ (*tag)->pos.line_start, line_len);
+ }
+
+ bh_file_close(&tags_file);
+}
+
+static void onyx_docs_emit_html(OnyxDocumentation* doc, bh_file* file) {
+ bh_fprintf(file, "HTML documentation output not supported yet.\n");
+ return;
+}
+
+void onyx_docs_emit(OnyxDocumentation* doc, const char* filename) {
+ bh_file file;
+ if (bh_file_create(&file, filename) != BH_FILE_ERROR_NONE) {
+ bh_printf("ERROR: Failed to open file '%s' for writing documentation.\n", filename);
+ return;
+ }
+
+ switch (doc->format) {
+ case Doc_Format_Human: onyx_docs_emit_human(doc, &file); break;
+ case Doc_Format_Html: onyx_docs_emit_html(doc, &file); break;
+ case Doc_Format_Tags: onyx_docs_emit_tags(doc); break;
+ }
+
+ bh_file_close(&file);
+}
+
--- /dev/null
+#include "bh.h"
+#include "astnodes.h"
+#include "utils.h"
+
+static inline i32 entity_phase(Entity* e1) {
+ if (e1->state <= Entity_State_Parse) return 1;
+ if (e1->state < Entity_State_Code_Gen) return 2;
+ return 3;
+}
+
+// NOTE: Returns >0 if e1 should be processed after e2.
+static i32 entity_compare(Entity* e1, Entity* e2) {
+ i32 phase1 = entity_phase(e1);
+ i32 phase2 = entity_phase(e2);
+
+ if (phase1 != phase2)
+ return phase1 - phase2;
+ else if (e1->macro_attempts != e2->macro_attempts)
+ return (i32) e1->macro_attempts - (i32) e2->macro_attempts;
+ else if (e1->state != e2->state)
+ return (i32) e1->state - (i32) e2->state;
+ else if (e1->type != e2->type)
+ return (i32) e1->type - (i32) e2->type;
+ else if (e1->micro_attempts != e2->micro_attempts)
+ return (i32) (e1->micro_attempts - e2->micro_attempts);
+ else
+ return (i32) (e1->id - e2->id);
+}
+
+#define eh_parent(index) (((index) - 1) / 2)
+#define eh_lchild(index) (((index) * 2) + 1)
+#define eh_rchild(index) (((index) * 2) + 2)
+
+static void eh_shift_up(EntityHeap* entities, i32 index) {
+ while (index > 0 && entity_compare(entities->entities[eh_parent(index)], entities->entities[index]) > 0) {
+ Entity* tmp = entities->entities[eh_parent(index)];
+ entities->entities[eh_parent(index)] = entities->entities[index];
+ entities->entities[index] = tmp;
+
+ index = eh_parent(index);
+ }
+}
+
+static void eh_shift_down(EntityHeap* entities, i32 index) {
+ i32 min_index = index;
+
+ i32 l = eh_lchild(index);
+ if (l < bh_arr_length(entities->entities)
+ && entity_compare(entities->entities[l], entities->entities[min_index]) < 0) {
+ min_index = l;
+ }
+
+ i32 r = eh_rchild(index);
+ if (r < bh_arr_length(entities->entities)
+ && entity_compare(entities->entities[r], entities->entities[min_index]) < 0) {
+ min_index = r;
+ }
+
+ if (index != min_index) {
+ Entity* tmp = entities->entities[min_index];
+ entities->entities[min_index] = entities->entities[index];
+ entities->entities[index] = tmp;
+
+ eh_shift_down(entities, min_index);
+ }
+}
+
+void entity_heap_init(EntityHeap* entities) {
+ bh_arena_init(&entities->entity_arena, global_heap_allocator, 32 * 1024);
+}
+
+// Allocates the entity in the entity heap. Don't quite feel this is necessary...
+Entity* entity_heap_register(EntityHeap* entities, Entity e) {
+ bh_allocator alloc = bh_arena_allocator(&entities->entity_arena);
+ Entity* entity = bh_alloc_item(alloc, Entity);
+ *entity = e;
+ entity->macro_attempts = 0;
+ entity->micro_attempts = 0;
+ entity->entered_in_queue = 0;
+
+ return entity;
+}
+
+void entity_heap_insert_existing(EntityHeap* entities, Entity* e) {
+ if (e->entered_in_queue) return;
+
+ if (entities->entities == NULL) {
+ bh_arr_new(global_heap_allocator, entities->entities, 128);
+ }
+
+ bh_arr_push(entities->entities, e);
+ eh_shift_up(entities, bh_arr_length(entities->entities) - 1);
+ e->entered_in_queue = 1;
+
+ entities->state_count[e->state]++;
+ entities->type_count[e->type]++;
+ entities->all_count[e->state][e->type]++;
+}
+
+Entity* entity_heap_insert(EntityHeap* entities, Entity e) {
+ Entity* entity = entity_heap_register(entities, e);
+ entity_heap_insert_existing(entities, entity);
+ return entity;
+}
+
+Entity* entity_heap_top(EntityHeap* entities) {
+ return entities->entities[0];
+}
+
+void entity_heap_change_top(EntityHeap* entities, Entity* new_top) {
+ entities->state_count[entities->entities[0]->state]--;
+ entities->state_count[new_top->state]++;
+
+ entities->type_count[entities->entities[0]->type]--;
+ entities->type_count[new_top->type]++;
+
+ entities->all_count[entities->entities[0]->state][entities->entities[0]->type]--;
+ entities->all_count[new_top->state][new_top->type]++;
+
+ entities->entities[0] = new_top;
+ eh_shift_down(entities, 0);
+}
+
+void entity_heap_remove_top(EntityHeap* entities) {
+ entities->state_count[entities->entities[0]->state]--;
+ entities->type_count[entities->entities[0]->type]--;
+ entities->all_count[entities->entities[0]->state][entities->entities[0]->type]--;
+ entities->entities[0]->entered_in_queue = 0;
+
+ entities->entities[0] = entities->entities[bh_arr_length(entities->entities) - 1];
+ bh_arr_pop(entities->entities);
+ eh_shift_down(entities, 0);
+}
+
+// NOTE(Brendan Hansen): Uses the entity heap in the context structure
+void add_entities_for_node(bh_arr(Entity *) *target_arr, AstNode* node, Scope* scope, Package* package) {
+#define ENTITY_INSERT(_ent) \
+ entity = entity_heap_register(entities, _ent); \
+ if (target_arr) { \
+ bh_arr(Entity *) __tmp_arr = *target_arr; \
+ bh_arr_push(__tmp_arr, entity); \
+ *target_arr = __tmp_arr; \
+ } else { \
+ entity_heap_insert_existing(entities, entity); \
+ } \
+
+
+ EntityHeap* entities = &context.entities;
+ Entity* entity;
+
+ Entity ent;
+ ent.id = entities->next_id++;
+ ent.state = Entity_State_Resolve_Symbols;
+ ent.package = package;
+ ent.scope = scope;
+
+ switch (node->kind) {
+ case Ast_Kind_Load_File: {
+ ent.state = Entity_State_Parse;
+ ent.type = Entity_Type_Load_File;
+ ent.include = (AstInclude *) node;
+ ENTITY_INSERT(ent);
+ break;
+ }
+
+ case Ast_Kind_Load_Path: {
+ ent.state = Entity_State_Parse;
+ ent.type = Entity_Type_Load_Path;
+ ent.include = (AstInclude *) node;
+ ENTITY_INSERT(ent);
+ break;
+ }
+
+ case Ast_Kind_Binding: {
+ ent.state = Entity_State_Introduce_Symbols;
+ ent.type = Entity_Type_Binding;
+ ent.binding = (AstBinding *) node;
+ ENTITY_INSERT(ent);
+ break;
+ }
+
+ case Ast_Kind_Function: {
+ if ((node->flags & Ast_Flag_Foreign) != 0) {
+ ent.type = Entity_Type_Foreign_Function_Header;
+ ent.function = (AstFunction *) node;
+ ENTITY_INSERT(ent);
+
+ } else {
+ ent.type = Entity_Type_Function_Header;
+ ent.function = (AstFunction *) node;
+ ENTITY_INSERT(ent);
+ ((AstFunction *) node)->entity_header = entity;
+
+ ent.id = entities->next_id++;
+ ent.type = Entity_Type_Function;
+ ent.function = (AstFunction *) node;
+ ENTITY_INSERT(ent);
+ ((AstFunction *) node)->entity_body = entity;
+ }
+ break;
+ }
+
+ case Ast_Kind_Overloaded_Function: {
+ ent.type = Entity_Type_Overloaded_Function;
+ ent.overloaded_function = (AstOverloadedFunction *) node;
+ ENTITY_INSERT(ent);
+ break;
+ }
+
+ case Ast_Kind_Global: {
+ if ((node->flags & Ast_Flag_Foreign) != 0) {
+ ent.type = Entity_Type_Foreign_Global_Header;
+ ent.global = (AstGlobal *) node;
+ ENTITY_INSERT(ent);
+
+ } else {
+ ent.type = Entity_Type_Global_Header;
+ ent.global = (AstGlobal *) node;
+ ENTITY_INSERT(ent);
+
+ ent.id = entities->next_id++;
+ ent.type = Entity_Type_Global;
+ ent.global = (AstGlobal *) node;
+ ENTITY_INSERT(ent);
+ }
+ break;
+ }
+
+ case Ast_Kind_StrLit: {
+ ent.type = Entity_Type_String_Literal;
+ ent.strlit = (AstStrLit *) node;
+ ENTITY_INSERT(ent);
+ break;
+ }
+
+ case Ast_Kind_File_Contents: {
+ ent.type = Entity_Type_File_Contents;
+ ent.file_contents = (AstFileContents *) node;
+ ENTITY_INSERT(ent);
+ break;
+ }
+
+ case Ast_Kind_Struct_Type: {
+ ent.type = Entity_Type_Struct_Member_Default;
+ ent.type_alias = (AstType *) node;
+ ENTITY_INSERT(ent);
+ ((AstStructType *) node)->entity_defaults = entity;
+
+ ent.id = entities->next_id++;
+ // fallthrough
+ }
+
+ case Ast_Kind_Poly_Struct_Type:
+ case Ast_Kind_Type_Alias: {
+ ent.type = Entity_Type_Type_Alias;
+ ent.type_alias = (AstType *) node;
+ ENTITY_INSERT(ent);
+
+ if (node->kind == Ast_Kind_Struct_Type) {
+ ((AstStructType *) node)->entity_type = entity;
+ }
+
+ break;
+ }
+
+ case Ast_Kind_Enum_Type: {
+ ent.type = Entity_Type_Enum;
+ ent.enum_type = (AstEnumType *) node;
+ ENTITY_INSERT(ent);
+ break;
+ }
+
+ case Ast_Kind_Use: {
+ if (((AstUse *) node)->expr->kind == Ast_Kind_Package) {
+ ent.state = Entity_State_Resolve_Symbols;
+ ent.type = Entity_Type_Use_Package;
+ } else {
+ ent.type = Entity_Type_Use;
+ }
+
+ ent.use = (AstUse *) node;
+ ENTITY_INSERT(ent);
+ break;
+ }
+
+ case Ast_Kind_Memres: {
+ ent.type = Entity_Type_Memory_Reservation_Type;
+ ent.mem_res = (AstMemRes *) node;
+ ENTITY_INSERT(ent);
+
+ ent.id = entities->next_id++;
+ ent.type = Entity_Type_Memory_Reservation;
+ ent.mem_res = (AstMemRes *) node;
+ ENTITY_INSERT(ent);
+ break;
+ }
+
+ case Ast_Kind_Macro: {
+ ent.type = Entity_Type_Macro;
+ ent.macro = (AstMacro *) node;
+ ENTITY_INSERT(ent);
+ break;
+ }
+
+ case Ast_Kind_Polymorphic_Proc: {
+ ent.type = Entity_Type_Polymorphic_Proc;
+ ent.poly_proc = (AstPolyProc *) node;
+ ENTITY_INSERT(ent);
+ break;
+ }
+
+ case Ast_Kind_Static_If: {
+ ent.state = Entity_State_Resolve_Symbols;
+ ent.type = Entity_Type_Static_If;
+ ent.static_if = (AstIf *) node;
+ ENTITY_INSERT(ent);
+ break;
+ }
+
+ case Ast_Kind_Directive_Error: {
+ ent.state = Entity_State_Error;
+ ent.type = Entity_Type_Error;
+ ent.error = (AstDirectiveError *) node;
+ ENTITY_INSERT(ent);
+ break;
+ }
+
+ case Ast_Kind_Directive_Export:
+ case Ast_Kind_Directive_Add_Overload:
+ case Ast_Kind_Directive_Operator: {
+ ent.type = Entity_Type_Process_Directive;
+ ent.expr = (AstTyped *) node;
+ ENTITY_INSERT(ent);
+ break;
+ }
+
+ case Ast_Kind_Note: {
+ ent.type = Entity_Type_Note;
+ ent.expr = (AstTyped *) node;
+ ent.state = Entity_State_Code_Gen;
+ ENTITY_INSERT(ent);
+ break;
+ }
+
+ default: {
+ ent.type = Entity_Type_Expression;
+ ent.expr = (AstTyped *) node;
+ ENTITY_INSERT(ent);
+ break;
+ }
+ }
+
+ node->entity = entity;
+}
--- /dev/null
+#include "errors.h"
+#include "utils.h"
+
+OnyxErrors errors;
+
+void onyx_errors_init(bh_arr(bh_file_contents)* files) {
+ errors.file_contents = files;
+
+ bh_arena_init(&errors.msg_arena, global_heap_allocator, 16 * 1024);
+ errors.msg_alloc = bh_arena_allocator(&errors.msg_arena);
+
+ bh_arr_new(global_heap_allocator, errors.errors, 4);
+}
+
+static void print_detailed_message(OnyxError* err, bh_file_contents* fc) {
+ bh_printf("(%s:%l,%l) %s\n", err->pos.filename, err->pos.line, err->pos.column, err->text);
+
+ b32 colored_printing = 0;
+ #ifdef _BH_LINUX
+ colored_printing = !context.options->no_colors;
+ #endif
+
+ i32 linelength = 0;
+ i32 first_char = 0;
+ char* walker = err->pos.line_start;
+ while (*walker == ' ' || *walker == '\t') first_char++, linelength++, walker++;
+ while (*walker != '\n') linelength++, walker++;
+
+ if (colored_printing) bh_printf("\033[90m");
+ i32 numlen = bh_printf(" %d | ", err->pos.line);
+ if (colored_printing) bh_printf("\033[94m");
+ bh_printf("%b\n", err->pos.line_start, linelength);
+
+ char* pointer_str = bh_alloc_array(global_scratch_allocator, char, linelength + numlen);
+ memset(pointer_str, ' ', numlen);
+ memcpy(pointer_str + numlen - 1, err->pos.line_start, first_char);
+ memset(pointer_str + first_char + numlen - 1, ' ', err->pos.column - first_char);
+ memset(pointer_str + err->pos.column + numlen - 1, '~', err->pos.length - 1);
+ pointer_str[err->pos.column + numlen - 2] = '^';
+ pointer_str[err->pos.column + numlen + err->pos.length - 1] = 0;
+
+ if (colored_printing) bh_printf("\033[91m");
+ bh_printf("%s\n", pointer_str);
+
+ if (colored_printing) bh_printf("\033[97m");
+}
+
+void onyx_errors_print() {
+ // NOTE: If the format of the error messages is ever changed,
+ // update onyx_compile.vim and onyx.sublime-build to match
+ // the new format. This was editor error highlighting is still
+ // supported.
+ //
+ // - brendanfh 2020/09/03
+
+ bh_arr_each(OnyxError, err, errors.errors) {
+ if (err->pos.filename) {
+ bh_file_contents file_contents = { 0 };
+ bh_arr_each(bh_file_contents, fc, *errors.file_contents) {
+ if (!strcmp(fc->filename, err->pos.filename)) {
+ file_contents = *fc;
+ break;
+ }
+ }
+
+ print_detailed_message(err, &file_contents);
+
+ } else {
+ bh_printf("(%l,%l) %s\n", err->pos.line, err->pos.column, err->text);
+ }
+ }
+}
+
+b32 onyx_has_errors() {
+ return bh_arr_length(errors.errors) > 0;
+}
+
+void onyx_clear_errors() {
+ bh_arr_set_length(errors.errors, 0);
+}
+
+void onyx_report_error(OnyxFilePos pos, char * format, ...) {
+
+ va_list vargs;
+ va_start(vargs, format);
+ char* msg = bh_bprintf_va(format, vargs);
+ va_end(vargs);
+
+ OnyxError err = {
+ .pos = pos,
+ .text = bh_strdup(errors.msg_alloc, msg),
+ };
+
+ bh_arr_push(errors.errors, err);
+}
+
+void onyx_report_warning(OnyxFilePos pos, char* format, ...) {
+ va_list vargs;
+ va_start(vargs, format);
+ char* msg = bh_bprintf_va(format, vargs);
+ va_end(vargs);
+
+ OnyxError err = {
+ .pos = pos,
+ .text = msg,
+ };
+
+ bh_file_contents file_contents = { 0 };
+ bh_arr_each(bh_file_contents, fc, *errors.file_contents) {
+ if (!strcmp(fc->filename, pos.filename)) {
+ file_contents = *fc;
+ break;
+ }
+ }
+
+ print_detailed_message(&err, &file_contents);
+}
--- /dev/null
+#include "bh.h"
+#include "lex.h"
+#include "utils.h"
+#include "errors.h"
+
+u64 lexer_lines_processed = 0;
+u64 lexer_tokens_processed = 0;
+
+static const char* token_type_names[] = {
+ "TOKEN_TYPE_UNKNOWN",
+ "TOKEN_TYPE_END_STREAM",
+
+ "TOKEN_TYPE_COMMENT",
+
+ "package",
+ "struct",
+ "enum",
+ "use",
+ "if",
+ "else",
+ "elseif",
+ "return",
+ "global",
+ "proc",
+ "as",
+ "cast",
+ "while",
+ "for",
+ "break",
+ "continue",
+ "sizeof",
+ "alignof",
+ "typeof",
+ "defer",
+ "do",
+ "switch",
+ "case",
+ "fallthrough",
+ "macro",
+
+ "->",
+ "<-",
+ "---",
+ "|>",
+
+ ">=",
+ "<=",
+ "==",
+ "!=",
+ "+=",
+ "-=",
+ "*=",
+ "/=",
+ "%=",
+ "&=",
+ "|=",
+ "^=",
+ "&&",
+ "||",
+ "<<",
+ ">>",
+ ">>>",
+ "<<=",
+ ">>=",
+ ">>>=",
+ "..",
+ "~~",
+
+ "TOKEN_TYPE_SYMBOL",
+ "TOKEN_TYPE_LITERAL_STRING",
+ "TOKEN_TYPE_LITERAL_INTEGER",
+ "TOKEN_TYPE_LITERAL_FLOAT",
+ "true",
+ "false",
+
+ "NOTE",
+
+ "TOKEN_TYPE_COUNT"
+};
+
+#ifndef LITERAL_TOKEN
+#define LITERAL_TOKEN(token, word, token_type) \
+ if (token_lit(tokenizer, &tk, token, word, token_type)) goto token_parsed;
+#endif
+
+#ifndef INCREMENT_CURR_TOKEN
+#define INCREMENT_CURR_TOKEN(tkn) { \
+ if (*(tkn)->curr == '\n') { \
+ (tkn)->line_number++; \
+ (tkn)->line_start = (tkn)->curr + 1; \
+ } \
+ (tkn)->curr++; \
+}
+#endif
+
+#define char_is_alphanum(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z') || ((c) >= '0' && (c) <= '9'))
+#define char_is_num(c) ((c) >= '0' && (c) <= '9')
+#define char_is_alpha(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z'))
+
+static inline b32 token_lit(OnyxTokenizer* tokenizer, OnyxToken* tk, char* lit, b32 is_word, TokenType type) {
+ i64 len = 0;
+ char* ptr1 = tokenizer->curr;
+ char* ptr2 = lit;
+ while (*ptr2 != '\0' && *ptr1 == *ptr2) ptr1++, ptr2++, len++;
+ if (*ptr2 != '\0') return 0;
+
+ if (is_word && (char_is_alphanum(*ptr1) || *ptr1 == '_'))
+ return 0;
+
+ tk->type = type;
+ tk->text = tokenizer->curr;
+ tk->length = len;
+ tk->pos.line = tokenizer->line_number;
+ tk->pos.column = (i32)(tokenizer->curr - tokenizer->line_start) + 1;
+
+ tokenizer->curr += len;
+
+ return 1;
+}
+
+const char* token_name(TokenType tkn_type) {
+ if (tkn_type < Token_Type_Ascii_End) {
+ return bh_aprintf(global_scratch_allocator, "%c", (char) tkn_type);
+ } else {
+ return token_type_names[tkn_type - Token_Type_Ascii_End];
+ }
+}
+
+void token_toggle_end(OnyxToken* tkn) {
+ static char backup = 0;
+ char tmp = tkn->text[tkn->length];
+ tkn->text[tkn->length] = backup;
+ backup = tmp;
+}
+
+OnyxToken* onyx_get_token(OnyxTokenizer* tokenizer) {
+ OnyxToken tk;
+
+ // Skip whitespace
+ while (1) {
+ if (tokenizer->curr == tokenizer->end) break;
+
+ switch (*tokenizer->curr) {
+ case ' ':
+ case '\n':
+ case '\t':
+ case '\r':
+ INCREMENT_CURR_TOKEN(tokenizer);
+ break;
+ default:
+ goto whitespace_skipped;
+ }
+ }
+
+whitespace_skipped:
+
+ tk.type = Token_Type_Unknown;
+ tk.text = tokenizer->curr;
+ tk.length = 1;
+ tk.pos.line_start = tokenizer->line_start;
+ tk.pos.filename = tokenizer->filename;
+ tk.pos.line = tokenizer->line_number;
+ tk.pos.column = (u16)(tokenizer->curr - tokenizer->line_start) + 1;
+
+ if (tokenizer->curr == tokenizer->end) {
+ tk.type = Token_Type_End_Stream;
+ goto token_parsed;
+ }
+
+ // Comments
+ if (*tokenizer->curr == '/' && *(tokenizer->curr + 1) == '/') {
+ tokenizer->curr += 2;
+ tk.type = Token_Type_Comment;
+ tk.text = tokenizer->curr;
+
+ while (*tokenizer->curr != '\n' && tokenizer->curr != tokenizer->end) {
+ INCREMENT_CURR_TOKEN(tokenizer);
+ }
+
+ tk.length = tokenizer->curr - tk.text - 2;
+ goto token_parsed;
+ }
+
+ if (*tokenizer->curr == '/' && *(tokenizer->curr + 1) == '*') {
+ tokenizer->curr += 2;
+ tk.type = Token_Type_Comment;
+ tk.text = tokenizer->curr;
+
+ i32 comment_depth = 1;
+
+ while (comment_depth > 0 && tokenizer->curr != tokenizer->end) {
+ if (*tokenizer->curr == '/' && *(tokenizer->curr + 1) == '*') {
+ tokenizer->curr += 2;
+ comment_depth += 1;
+ }
+
+ else if (*tokenizer->curr == '*' && *(tokenizer->curr + 1) == '/') {
+ tokenizer->curr += 2;
+ comment_depth -= 1;
+ }
+
+ else {
+ INCREMENT_CURR_TOKEN(tokenizer);
+ }
+ }
+
+ goto token_parsed;
+ }
+
+ // String literal
+ if (*tk.text == '"') {
+ u64 len = 0;
+ u64 slash_count = 0;
+
+ INCREMENT_CURR_TOKEN(tokenizer);
+
+ while (!(*tokenizer->curr == '"' && slash_count == 0)) {
+ len++;
+
+ // if (*tokenizer->curr == '\n') {
+ // onyx_report_error(tk.pos, "String literal not terminated by end of line.");
+ // }
+
+ if (*tokenizer->curr == '\\') {
+ slash_count += 1;
+ slash_count %= 2;
+ } else {
+ slash_count = 0;
+ }
+
+ INCREMENT_CURR_TOKEN(tokenizer);
+ }
+
+ INCREMENT_CURR_TOKEN(tokenizer);
+
+ tk.text++;
+ tk.type = Token_Type_Literal_String;
+ tk.length = len;
+ goto token_parsed;
+ }
+
+ // Hex literal
+ if (*tokenizer->curr == '0' && *(tokenizer->curr + 1) == 'x' && charset_contains("0123456789abcdefABCDEF", *(tokenizer->curr + 2))) {
+ INCREMENT_CURR_TOKEN(tokenizer);
+ INCREMENT_CURR_TOKEN(tokenizer);
+ u32 len = 3;
+ while (char_is_num(*(tokenizer->curr + 1)) || charset_contains("abcdefABCDEF", *(tokenizer->curr + 1))) {
+ len++;
+ INCREMENT_CURR_TOKEN(tokenizer);
+ }
+
+ tk.type = Token_Type_Literal_Integer;
+ tk.length = len;
+
+ INCREMENT_CURR_TOKEN(tokenizer);
+ goto token_parsed;
+ }
+
+ if (*tokenizer->curr == '@') {
+ INCREMENT_CURR_TOKEN(tokenizer);
+ u32 len = 2;
+ while (char_is_alphanum(*(tokenizer->curr + 1)) || *(tokenizer->curr + 1) == '_') {
+ len++;
+ INCREMENT_CURR_TOKEN(tokenizer);
+ }
+
+ tk.type = Token_Type_Note;
+ tk.length = len;
+ INCREMENT_CURR_TOKEN(tokenizer);
+ goto token_parsed;
+ }
+
+ // Number literal
+ if (char_is_num(*tokenizer->curr)
+ || (*(tokenizer->curr) == '.' && char_is_num(*(tokenizer->curr + 1)))) {
+ tk.type = Token_Type_Literal_Integer;
+
+ b32 hit_decimal = 0;
+ if (*tokenizer->curr == '.') hit_decimal = 1;
+
+ u32 len = 1;
+ while (char_is_num(*(tokenizer->curr + 1))
+ || (!hit_decimal && *(tokenizer->curr + 1) == '.' && *(tokenizer->curr + 2) != '.')) {
+ len++;
+ INCREMENT_CURR_TOKEN(tokenizer);
+
+ if (*tokenizer->curr == '.') hit_decimal = 1;
+ }
+
+ if (*(tokenizer->curr + 1) == 'f') {
+ tk.type = Token_Type_Literal_Float;
+
+ len++;
+ INCREMENT_CURR_TOKEN(tokenizer);
+ }
+
+ if (hit_decimal) tk.type = Token_Type_Literal_Float;
+
+ tk.length = len;
+
+ INCREMENT_CURR_TOKEN(tokenizer);
+ goto token_parsed;
+ }
+
+ char curr = *tokenizer->curr;
+ switch (curr) {
+ case 'a':
+ LITERAL_TOKEN("alignof", 1, Token_Type_Keyword_Alignof);
+ LITERAL_TOKEN("as", 1, Token_Type_Keyword_As);
+ break;
+ case 'b':
+ LITERAL_TOKEN("break", 1, Token_Type_Keyword_Break);
+ break;
+ case 'c':
+ LITERAL_TOKEN("case", 1, Token_Type_Keyword_Case);
+ LITERAL_TOKEN("cast", 1, Token_Type_Keyword_Cast);
+ LITERAL_TOKEN("continue", 1, Token_Type_Keyword_Continue);
+ break;
+ case 'd':
+ LITERAL_TOKEN("defer", 1, Token_Type_Keyword_Defer);
+ LITERAL_TOKEN("do", 1, Token_Type_Keyword_Do);
+ break;
+ case 'e':
+ LITERAL_TOKEN("enum", 1, Token_Type_Keyword_Enum);
+ LITERAL_TOKEN("elseif", 1, Token_Type_Keyword_Elseif);
+ LITERAL_TOKEN("else", 1, Token_Type_Keyword_Else);
+ break;
+ case 'f':
+ LITERAL_TOKEN("for", 1, Token_Type_Keyword_For);
+ LITERAL_TOKEN("false", 1, Token_Type_Literal_False);
+ LITERAL_TOKEN("fallthrough", 1, Token_Type_Keyword_Fallthrough);
+ break;
+ case 'g':
+ LITERAL_TOKEN("global", 1, Token_Type_Keyword_Global);
+ break;
+ case 'i':
+ LITERAL_TOKEN("if", 1, Token_Type_Keyword_If);
+ break;
+ case 'm':
+ LITERAL_TOKEN("macro", 1, Token_Type_Keyword_Macro);
+ break;
+ case 'p':
+ LITERAL_TOKEN("package", 1, Token_Type_Keyword_Package);
+ LITERAL_TOKEN("proc", 1, Token_Type_Keyword_Proc);
+ break;
+ case 'r':
+ LITERAL_TOKEN("return", 1, Token_Type_Keyword_Return);
+ break;
+ case 's':
+ LITERAL_TOKEN("struct", 1, Token_Type_Keyword_Struct);
+ LITERAL_TOKEN("sizeof", 1, Token_Type_Keyword_Sizeof);
+ LITERAL_TOKEN("switch", 1, Token_Type_Keyword_Switch);
+ break;
+ case 't':
+ LITERAL_TOKEN("true", 1, Token_Type_Literal_True);
+ LITERAL_TOKEN("typeof", 1, Token_Type_Keyword_Typeof);
+ break;
+ case 'u':
+ LITERAL_TOKEN("use", 1, Token_Type_Keyword_Use);
+ break;
+ case 'w':
+ LITERAL_TOKEN("while", 1, Token_Type_Keyword_While);
+ break;
+
+ case '-':
+ LITERAL_TOKEN("->", 0, Token_Type_Right_Arrow);
+ LITERAL_TOKEN("---", 0, Token_Type_Empty_Block);
+ LITERAL_TOKEN("-=", 0, Token_Type_Minus_Equal);
+ break;
+
+ case '<':
+ LITERAL_TOKEN("<-", 0, Token_Type_Right_Arrow);
+ LITERAL_TOKEN("<<=", 0, Token_Type_Shl_Equal);
+ LITERAL_TOKEN("<<", 0, Token_Type_Shift_Left);
+ LITERAL_TOKEN("<=", 0, Token_Type_Less_Equal);
+ break;
+
+ case '>':
+ LITERAL_TOKEN(">>>=", 0, Token_Type_Sar_Equal);
+ LITERAL_TOKEN(">>=", 0, Token_Type_Shr_Equal);
+ LITERAL_TOKEN(">>>", 0, Token_Type_Shift_Arith_Right);
+ LITERAL_TOKEN(">>", 0, Token_Type_Shift_Right);
+ LITERAL_TOKEN(">=", 0, Token_Type_Greater_Equal);
+ break;
+
+ case '&':
+ LITERAL_TOKEN("&&", 0, Token_Type_And_And);
+ LITERAL_TOKEN("&=", 0, Token_Type_And_Equal);
+ break;
+
+ case '|':
+ LITERAL_TOKEN("|>", 0, Token_Type_Pipe);
+ LITERAL_TOKEN("||", 0, Token_Type_Or_Or);
+ LITERAL_TOKEN("|=", 0, Token_Type_Or_Equal);
+ break;
+
+ case '=':
+ LITERAL_TOKEN("==", 0, Token_Type_Equal_Equal);
+ break;
+
+ case '!':
+ LITERAL_TOKEN("!=", 0, Token_Type_Not_Equal);
+ break;
+
+ case '+':
+ LITERAL_TOKEN("+=", 0, Token_Type_Plus_Equal);
+ break;
+
+ case '*':
+ LITERAL_TOKEN("*=", 0, Token_Type_Star_Equal);
+ break;
+
+ case '^':
+ LITERAL_TOKEN("^=", 0, Token_Type_Xor_Equal);
+ break;
+
+ case '/':
+ LITERAL_TOKEN("/=", 0, Token_Type_Fslash_Equal);
+ break;
+
+ case '%':
+ LITERAL_TOKEN("%=", 0, Token_Type_Percent_Equal);
+ break;
+
+ case '.':
+ LITERAL_TOKEN("..", 0, Token_Type_Dot_Dot);
+ break;
+
+ case '~':
+ LITERAL_TOKEN("~~", 0, Token_Type_Tilde_Tilde);
+ break;
+ }
+
+ // Symbols
+ if (char_is_alpha(*tk.text) || *tokenizer->curr == '_') {
+ u64 len = 0;
+ while (char_is_alphanum(*tokenizer->curr) || charset_contains("_$", *tokenizer->curr)) {
+ len++;
+ INCREMENT_CURR_TOKEN(tokenizer);
+ }
+
+ tk.length = len;
+ tk.type = Token_Type_Symbol;
+ goto token_parsed;
+ }
+
+
+ tk.type = (TokenType) *tokenizer->curr;
+ INCREMENT_CURR_TOKEN(tokenizer);
+
+token_parsed:
+ tk.pos.length = (u16) tk.length;
+ bh_arr_push(tokenizer->tokens, tk);
+
+ return &tokenizer->tokens[bh_arr_length(tokenizer->tokens) - 1];
+}
+
+OnyxTokenizer onyx_tokenizer_create(bh_allocator allocator, bh_file_contents *fc) {
+ OnyxTokenizer tknizer = {
+ .start = fc->data,
+ .curr = fc->data,
+ .end = bh_pointer_add(fc->data, fc->length),
+
+ .filename = fc->filename,
+
+ .line_number = 1,
+ .line_start = fc->data,
+ .tokens = NULL,
+ };
+
+ bh_arr_new(allocator, tknizer.tokens, 1 << 12);
+ return tknizer;
+}
+
+void onyx_tokenizer_free(OnyxTokenizer* tokenizer) {
+ bh_arr_free(tokenizer->tokens);
+}
+
+void onyx_lex_tokens(OnyxTokenizer* tokenizer) {
+ OnyxToken* tk;
+ do {
+ tk = onyx_get_token(tokenizer);
+ } while (tk->type != Token_Type_End_Stream);
+
+ lexer_lines_processed += tokenizer->line_number - 1;
+ lexer_tokens_processed += bh_arr_length(tokenizer->tokens);
+}
+
+b32 token_equals(OnyxToken* tkn1, OnyxToken* tkn2) {
+ if (tkn1->length != tkn2->length) return 0;
+ fori (i, 0, tkn1->length)
+ if (tkn1->text[i] != tkn2->text[i]) return 0;
+ return 1;
+}
+
+b32 token_text_equals(OnyxToken* tkn, char* text) {
+ i32 text_len = strlen(text);
+ if (tkn->length != text_len) return 0;
+
+ fori (i, 0, tkn->length)
+ if (tkn->text[i] != text[i]) return 0;
+
+ return 1;
+}
#define BH_DEFINE
#include "bh.h"
-#include "onyxlex.h"
-#include "onyxerrors.h"
-#include "onyxparser.h"
-#include "onyxutils.h"
-#include "onyxwasm.h"
-#include "onyxdoc.h"
+#include "lex.h"
+#include "errors.h"
+#include "parser.h"
+#include "utils.h"
+#include "wasm.h"
+#include "doc.h"
#define VERSION "v0.1.0-beta"
+++ /dev/null
-#include "onyxastnodes.h"
-#include "onyxparser.h"
-#include "onyxutils.h"
-
-static const char* ast_node_names[] = {
- "ERROR",
- "PACKAGE",
- "INCLUDE FILE",
- "INCLUDE FOLDER",
- "MEMORY RESERVATION",
-
- "BINDING",
- "ALIAS",
- "FUNCTION",
- "OVERLOADED_FUNCTION",
- "POLYMORPHIC PROC",
- "BLOCK",
- "LOCAL",
- "GLOBAL",
- "SYMBOL",
-
- "UN_OP",
- "BIN_OP",
-
- "COMPOUND",
- "NAMED_VALUE",
-
- "TYPE_START (BAD)",
- "TYPE",
- "BASIC_TYPE",
- "POINTER_TYPE",
- "FUNCTION_TYPE",
- "ARRAY TYPE",
- "SLICE TYPE",
- "DYNARR TYPE",
- "VARARG TYPE",
- "STRUCT TYPE",
- "POLYMORPHIC STRUCT TYPE",
- "POLYMORPHIC STRUCT CALL TYPE",
- "ENUM TYPE",
- "TYPE_ALIAS",
- "TYPE RAW ALIAS",
- "COMPOUND TYPE",
- "TYPE OF",
- "TYPE_END (BAD)",
-
- "STRUCT MEMBER",
- "ENUM VALUE",
-
- "NUMERIC LITERAL",
- "STRING LITERAL",
- "PARAM",
- "ARGUMENT",
- "CALL",
- "INTRINSIC CALL",
- "RETURN",
- "ADDRESS OF",
- "DEREFERENCE",
- "ARRAY ACCESS",
- "SLICE",
- "FIELD ACCESS",
- "UNARY FIELD ACCESS",
- "PIPE",
- "METHOD_CALL",
- "RANGE",
- "SIZE OF",
- "ALIGN OF",
- "FILE CONTENTS",
- "STRUCT LITERAL",
- "ARRAY LITERAL",
- "IF EXPRESSION",
-
- "IF",
- "FOR",
- "WHILE",
- "JUMP",
- "USE",
- "DEFER",
- "SWITCH",
- "SWITCH CASE",
-
- "SOLIDIFY",
- "STATIC IF",
- "STATIC ERROR",
- "ADD OVERLOAD",
- "OPERATOR OVERLOAD",
- "EXPORT",
- "DEFINED",
- "CALL SITE",
-
- "CODE BLOCK",
- "DIRECTIVE INSERT",
- "MACRO",
- "DO BLOCK",
-
- "NOTE",
-
- "AST_NODE_KIND_COUNT",
-};
-
-const char* onyx_ast_node_kind_string(AstKind kind) {
- return ast_node_names[kind];
-}
-
-const char *binaryop_string[Binary_Op_Count] = {
- "+", "-", "*", "/", "%",
- "==", "!=", "<", "<=", ">", ">=",
- "&", "|", "^", "<<", ">>", ">>>",
- "&&", "||",
-
- "NONE",
- "=", "+=", "-=", "*=", "/=", "%=",
- "&=", "|=", "^=", "<<=", ">>=", ">>>=",
- "NONE",
-
- "|>", "..", "->",
-
- "[]",
-};
-
-const char* entity_state_strings[Entity_State_Count] = {
- "Error",
- "Parse Builtin",
- "Introduce Symbols",
- "Parse",
- "Resolve Symbols",
- "Check Types",
- "Code Gen",
- "Finalized",
-};
-
-const char* entity_type_strings[Entity_Type_Count] = {
- "Unknown",
- "Error",
- "Note",
- "Add to Load Path",
- "Load File",
- "Binding (Declaration)",
- "Use Package",
- "Static If",
- "String Literal",
- "File Contents",
- "Enum",
- "Type Alias",
- "Memory Reservation Type",
- "Use",
- "Polymorphic Proc",
- "Macro",
- "Foreign_Function Header",
- "Foreign_Global Header",
- "Function Header",
- "Global Header",
- "Process Directive",
- "Struct Member Default",
- "Memory Reservation",
- "Expression",
- "Global",
- "Overloaded_Function",
- "Function",
-};
-
-#define REDUCE_BINOP_ALL(op) \
- if (type_is_small_integer(res->type) || type_is_bool(res->type)) { \
- res->value.i = left->value.i op right->value.i; \
- } else if (type_is_integer(res->type) \
- || res->type->Basic.kind == Basic_Kind_Int_Unsized \
- || res->type->kind == Type_Kind_Enum) { \
- res->value.l = left->value.l op right->value.l; \
- } else if (res->type->Basic.kind == Basic_Kind_F32) { \
- res->value.f = left->value.f op right->value.f; \
- } else if (res->type->Basic.kind == Basic_Kind_F64 || res->type->Basic.kind == Basic_Kind_Float_Unsized) { \
- res->value.d = left->value.d op right->value.d; \
- } \
- break;
-
-#define REDUCE_BINOP_INT(op) \
- if (type_is_small_integer(res->type) || type_is_bool(res->type)) { \
- res->value.i = left->value.i op right->value.i; \
- } else if (type_is_integer(res->type) \
- || res->type->Basic.kind == Basic_Kind_Int_Unsized \
- || res->type->kind == Type_Kind_Enum) { \
- res->value.l = left->value.l op right->value.l; \
- } \
- break;
-
-#define REDUCE_BINOP_BOOL(op) \
- if (type_is_bool(res->type)) { \
- res->value.i = left->value.i op right->value.i; \
- } \
- break;
-
-AstNumLit* ast_reduce_binop(bh_allocator a, AstBinaryOp* node) {
- AstNumLit* left = (AstNumLit *) ast_reduce(a, node->left);
- AstNumLit* right = (AstNumLit *) ast_reduce(a, node->right);
-
- if (left->kind != Ast_Kind_NumLit || right->kind != Ast_Kind_NumLit) {
- node->left = (AstTyped *) left;
- node->right = (AstTyped *) right;
- return (AstNumLit *) node;
- }
-
- AstNumLit* res = onyx_ast_node_new(a, sizeof(AstNumLit), Ast_Kind_NumLit);
- res->token = node->token;
- res->flags |= node->flags;
- res->flags |= Ast_Flag_Comptime;
- res->type_node = node->type_node;
- res->type = node->type;
- res->next = node->next;
-
- switch (node->operation) {
- case Binary_Op_Add: REDUCE_BINOP_ALL(+);
- case Binary_Op_Minus: REDUCE_BINOP_ALL(-);
- case Binary_Op_Multiply: REDUCE_BINOP_ALL(*);
- case Binary_Op_Divide: REDUCE_BINOP_ALL(/);
- case Binary_Op_Modulus: REDUCE_BINOP_INT(%);
-
- case Binary_Op_Equal: REDUCE_BINOP_ALL(==);
- case Binary_Op_Not_Equal: REDUCE_BINOP_ALL(!=);
- case Binary_Op_Less: REDUCE_BINOP_ALL(<);
- case Binary_Op_Less_Equal: REDUCE_BINOP_ALL(<=);
- case Binary_Op_Greater: REDUCE_BINOP_ALL(>);
- case Binary_Op_Greater_Equal: REDUCE_BINOP_ALL(>=);
-
- case Binary_Op_And: REDUCE_BINOP_INT(&);
- case Binary_Op_Or: REDUCE_BINOP_INT(|);
- case Binary_Op_Xor: REDUCE_BINOP_INT(^);
- case Binary_Op_Shl: REDUCE_BINOP_INT(<<);
- case Binary_Op_Shr: REDUCE_BINOP_INT(>>);
- case Binary_Op_Sar: REDUCE_BINOP_INT(>>);
-
- case Binary_Op_Bool_And: REDUCE_BINOP_BOOL(&&);
- case Binary_Op_Bool_Or: REDUCE_BINOP_BOOL(||);
-
- default: break;
- }
-
- return res;
-}
-
-#define REDUCE_UNOP(op) \
- if (type_is_small_integer(unop->type) || type_is_bool(unop->type)) { \
- res->value.i = op (operand)->value.i; \
- } else if (type_is_integer(unop->type) || unop->type->Basic.kind == Basic_Kind_Int_Unsized) { \
- res->value.l = op (operand)->value.l; \
- } else if (unop->type->Basic.kind == Basic_Kind_F32) { \
- res->value.f = op (operand)->value.f; \
- } else if (unop->type->Basic.kind == Basic_Kind_F64 || unop->type->Basic.kind == Basic_Kind_Float_Unsized) { \
- res->value.d = op (operand)->value.d; \
- } \
- break;
-
-#define REDUCE_UNOP_INT(op) \
- if (type_is_small_integer(unop->type) || type_is_bool(unop->type)) { \
- res->value.i = op (operand)->value.i; \
- } else if (type_is_integer(unop->type) || res->type->Basic.kind == Basic_Kind_Int_Unsized) { \
- res->value.l = op (operand)->value.l; \
- }
-
-AstTyped* ast_reduce_unaryop(bh_allocator a, AstUnaryOp* unop) {
- // GROSS
- AstNumLit* operand = (AstNumLit *) ast_reduce(a, unop->expr);
- unop->expr = (AstTyped *) operand;
-
- if (operand->kind != Ast_Kind_NumLit) {
- return (AstTyped *) unop;
- }
-
- AstNumLit* res = onyx_ast_node_new(a, sizeof(AstNumLit), Ast_Kind_NumLit);
- res->token = unop->token;
- res->flags |= Ast_Flag_Comptime;
- res->type_node = unop->type_node;
- res->type = unop->type;
- res->next = unop->next;
-
- switch (unop->operation) {
- case Unary_Op_Negate: REDUCE_UNOP(-);
- case Unary_Op_Not: {
- if (type_is_bool(res->type)) res->value.i = ! (operand)->value.i;
- break;
- }
- case Unary_Op_Bitwise_Not: REDUCE_UNOP_INT(~);
-
- case Unary_Op_Cast: {
- #if 0
- Type* from = operand->type;
- Type* to = unop->type;
-
- if (from->kind == Type_Kind_Enum) from = from->Enum.backing;
- if (to->kind == Type_Kind_Enum) to = to->Enum.backing;
-
- if (from->kind != Type_Kind_Pointer && from->kind != Type_Kind_Basic) return (AstTyped *) unop;
- if (to->kind != Type_Kind_Pointer && to->kind != Type_Kind_Basic) return (AstTyped *) unop;
-
- b32 from_is_int=0, to_is_int=0;
- i32 from_size, to_size;
-
- from_size = type_size_of(from);
- to_size = type_size_of(to);
-
- if (from->kind == Type_Kind_Basic) from_is_int = (from->flags & (Basic_Flag_Integer | Basic_Flag_Pointer)) != 0;
- if (to->kind == Type_Kind_Basic) to_is_int = (to->flags & (Basic_Flag_Integer | Basic_Flag_Pointer)) != 0;
-
- if (from->kind == Type_Kind_Pointer) from_is_int = 1;
- if (to->kind == Type_Kind_Pointer) to_is_int = 1;
-
- if (from_is_int == to_is_int && from_size == to_size) {
- // If they are already equal, nothing to do
- return (AstTyped *) res;
- }
-
- if (from_is_int && !to_is_int) {
- if (from_size == to_size) {
- if (from_size == 4) {
- res->value.f = (f32) operand->value.i;
- }
- else if (from_size == 8) {
- res->value.d = (f64) operand->value.l;
- }
- } else {
- if (from_size == 4) {
- res->value.d = (f64) operand->value.i;
- }
- else if (from_size == 8) {
- res->value.f = (f32) operand->value.l;
- }
- }
- } else {
- if (from_size == to_size) {
- if (from_size == 4) {
- res->value.i = (i32) operand->value.f;
- }
- else if (from_size == 8) {
- res->value.l = (i64) operand->value.d;
- }
- } else {
- if (from_size == 4) {
- res->value.l = (i64) operand->value.f;
- }
- else if (from_size == 8) {
- res->value.i = (i32) operand->value.d;
- }
- }
- }
- break;
-
- #endif
- }
-
- default: return (AstTyped *) unop;
- }
-
- return (AstTyped *) res;
-}
-
-AstTyped* ast_reduce(bh_allocator a, AstTyped* node) {
- assert(node->flags & Ast_Flag_Comptime);
-
- switch (node->kind) {
- case Ast_Kind_Binary_Op: return (AstTyped *) ast_reduce_binop(a, (AstBinaryOp *) node);
- case Ast_Kind_Unary_Op: return (AstTyped *) ast_reduce_unaryop(a, (AstUnaryOp *) node);
- case Ast_Kind_Enum_Value: return (AstTyped *) ((AstEnumValue *) node)->value;
- case Ast_Kind_Alias: return (AstTyped *) ast_reduce(a, ((AstAlias *) node)->alias);
- default: return node;
- }
-}
-
-void promote_numlit_to_larger(AstNumLit* num) {
- assert(num->type != NULL);
-
- if (type_is_integer(num->type) && num->type->Basic.size <= 4) {
- // NOTE: Int32, Int16, Int8
- i64 val = (i64) num->value.i;
- num->value.l = val;
- num->type = &basic_types[Basic_Kind_I64];
- } else if (num->type->Basic.size <= 4) {
- // NOTE: Float32
- f64 val = (f64) num->value.f;
- num->value.d = val;
- num->type = &basic_types[Basic_Kind_F64];
- }
-}
-
-// NOTE: Returns 1 if the conversion was successful.
-b32 convert_numlit_to_type(AstNumLit* num, Type* type) {
- if (num->type == NULL)
- num->type = type_build_from_ast(context.ast_alloc, num->type_node);
- assert(num->type);
-
- if (types_are_compatible(num->type, type)) return 1;
- if (!type_is_numeric(type)) return 0;
-
- if (num->type->Basic.kind == Basic_Kind_Int_Unsized) {
-
- //
- // Integer literal auto cast rules:
- // - Up in size always works
- // - Down in size only works if value is in range of smaller type.
- // - Cast to float only works if value is less than the maximum precise value for float size.
- //
-
- if (type->Basic.flags & Basic_Flag_Integer) {
- if (type->Basic.flags & Basic_Flag_Unsigned) {
- u64 value = (u64) num->value.l;
- if (type->Basic.size == 8) {
- num->type = type;
- return 1;
- }
- switch (type->Basic.size) {
- case 1: if (value <= 255) {
- num->type = type;
- return 1;
- }
- case 2: if (value <= 65535) {
- num->type = type;
- return 1;
- }
- case 4: if (value <= 4294967295) {
- num->type = type;
- return 1;
- }
- }
-
- onyx_report_error(num->token->pos, "Unsigned integer constant with value '%l' does not fit into %d-bits.",
- num->value.l,
- type->Basic.size * 8);
-
- } else {
- i64 value = (i64) num->value.l;
- switch (type->Basic.size) {
- case 1: if (-128ll <= value && value <= 127ll) {
- num->value.i = (i32) value;
- num->type = type;
- return 1;
- } break;
- case 2: if (-32768ll <= value && value <= 32767ll) {
- num->value.i = (i32) value;
- num->type = type;
- return 1;
- } break;
- case 4: if (-2147483648ll <= value && value <= 2147483647ll) {
- num->value.i = (i32) value;
- num->type = type;
- return 1;
- } break;
- case 8: { num->type = type;
- return 1;
- } break;
- }
-
- onyx_report_error(num->token->pos, "Integer constant with value '%l' does not fit into %d-bits.",
- num->value.l,
- type->Basic.size * 8);
- }
- }
-
- else if (type->Basic.flags & Basic_Flag_Float) {
- if (type->Basic.size == 4) {
- // TODO(Brendan): Check these boundary conditions
- if (bh_abs(num->value.l) >= (1 << 23)) {
- onyx_report_error(num->token->pos, "Integer '%l' does not fit in 32-bit float exactly.", num->value.l);
- return 0;
- }
-
- num->type = type;
- num->value.f = (f32) num->value.l;
- return 1;
- }
- if (type->Basic.size == 8) {
- // TODO(Brendan): Check these boundary conditions
- if (bh_abs(num->value.l) >= (1ull << 52)) {
- onyx_report_error(num->token->pos, "Integer '%l' does not fit in 64-bit float exactly.", num->value.l);
- return 0;
- }
-
- num->type = type;
- num->value.d = (f64) num->value.l;
- return 1;
- }
- }
- }
- else if (num->type->Basic.kind == Basic_Kind_Float_Unsized) {
- // NOTE: Floats don't cast to integers implicitly.
- if ((type->Basic.flags & Basic_Flag_Float) == 0) return 0;
-
- if (type->Basic.kind == Basic_Kind_F32) {
- num->value.f = (f32) num->value.d;
- }
-
- num->type = type;
- return 1;
- }
- else if (num->type->Basic.kind == Basic_Kind_F32) {
- // NOTE: Floats don't cast to integers implicitly.
- if ((type->Basic.flags & Basic_Flag_Float) == 0) return 0;
-
- if (type->Basic.kind == Basic_Kind_F64) {
- num->value.d = (f64) num->value.f;
- num->type = type;
- return 1;
- }
- }
-
- return 0;
-}
-
-// NOTE: Returns 0 if it was not possible to make the types compatible.
-b32 type_check_or_auto_cast(AstTyped** pnode, Type* type) {
- AstTyped* node = *pnode;
- if (type == NULL) return 0;
- if (node == NULL) return 0;
-
- // if (node_is_type((AstNode *) node)) return 0;
-
- if (node->kind == Ast_Kind_Struct_Literal && node->type_node == NULL) {
- if (node->entity != NULL) return 1;
- if (type->kind == Type_Kind_VarArgs) type = type->VarArgs.ptr_to_data->Pointer.elem;
- if (!type_is_sl_constructable(type)) return 0;
-
- node->type = type;
-
- add_entities_for_node(NULL, (AstNode *) node, NULL, NULL);
- return 1;
- }
-
- if (node->kind == Ast_Kind_Array_Literal && node->type_node == NULL) {
- if (node->entity != NULL) return 1;
- node->type = type;
- node->flags |= Ast_Flag_Array_Literal_Typed;
-
- add_entities_for_node(NULL, (AstNode *) node, NULL, NULL);
- return 1;
- }
-
- if (node->kind == Ast_Kind_Unary_Field_Access) {
- AstType* ast_type = type->ast_type;
- AstNode* resolved = try_symbol_resolve_from_node((AstNode *) ast_type, node->token);
- if (resolved == NULL) return 0;
-
- *pnode = (AstTyped *) resolved;
- return 1;
- }
-
- if (node->kind == Ast_Kind_Overloaded_Function) {
- AstTyped* func = find_matching_overload_by_type(((AstOverloadedFunction *) node)->overloads, type);
- if (func == NULL) return 0;
-
- // HACK: It feels like there should be a better place to flag that a procedure was definitely used.
- if (func->kind == Ast_Kind_Function)
- func->flags |= Ast_Flag_Function_Used;
-
- *pnode = func;
- node = *pnode;
- }
-
- if (node->kind == Ast_Kind_Polymorphic_Proc) {
- AstFunction* func = polymorphic_proc_lookup((AstPolyProc *) node, PPLM_By_Function_Type, type, node->token);
- if (func == NULL) return 0;
-
- *pnode = (AstTyped *) func;
- node = *pnode;
- }
-
- // HACK: NullProcHack
- if (type->kind == Type_Kind_Function && (node->flags & Ast_Flag_Proc_Is_Null) != 0) return 1;
-
- if (types_are_compatible(node->type, type)) return 1;
- // :AutoReturnType
- if (node->type && node->type->kind == Type_Kind_Function
- && node->type->Function.return_type == &type_auto_return
- && type->kind == Type_Kind_Function) {
-
- node->type->Function.return_type = type->Function.return_type;
- return 1;
- }
- if (node_is_auto_cast((AstNode *) node)) {
- char* dummy;
- Type* from_type = ((AstUnaryOp *) node)->expr->type;
- if (!from_type || !cast_is_legal(from_type, type, &dummy)) {
- return 0;
-
- } else {
- ((AstUnaryOp *) node)->type = type;
- return 1;
- }
- }
- else if (node->kind == Ast_Kind_NumLit) {
- if (convert_numlit_to_type((AstNumLit *) node, type)) return 1;
- }
- else if (node->kind == Ast_Kind_Compound) {
- if (type->kind != Type_Kind_Compound) return 0;
-
- AstCompound* compound = (AstCompound *) node;
-
- u32 expr_count = bh_arr_length(compound->exprs);
- if (expr_count != type->Compound.count) return 0;
-
- fori (i, 0, (i64) expr_count) {
- if (!type_check_or_auto_cast(&compound->exprs[i], type->Compound.types[i])) return 0;
- }
-
- compound->type = type_build_compound_type(context.ast_alloc, compound);
-
- return 1;
- }
- else if (node->kind == Ast_Kind_If_Expression) {
- AstIfExpression* if_expr = (AstIfExpression *) node;
-
- b32 true_success = type_check_or_auto_cast(&if_expr->true_expr, type);
- b32 false_success = type_check_or_auto_cast(&if_expr->false_expr, type);
-
- if (true_success && false_success) {
- if_expr->type = type;
- return 1;
-
- } else {
- return 0;
- }
- }
- else if (node->kind == Ast_Kind_Alias) {
- AstAlias* alias = (AstAlias *) node;
- return type_check_or_auto_cast(&alias->alias, type);
- }
-
- return 0;
-}
-
-Type* resolve_expression_type(AstTyped* node) {
- if (node == NULL) return NULL;
-
- if (node->kind == Ast_Kind_Compound) {
- bh_arr_each(AstTyped *, expr, ((AstCompound *) node)->exprs) {
- resolve_expression_type(*expr);
- }
- }
-
- if (node->kind == Ast_Kind_Argument) {
- node->type = resolve_expression_type(((AstArgument *) node)->value);
- }
-
- if (node->kind == Ast_Kind_If_Expression) {
- AstIfExpression* if_expr = (AstIfExpression *) node;
-
- Type* ltype = resolve_expression_type(if_expr->true_expr);
- type_check_or_auto_cast(&if_expr->false_expr, ltype);
-
- if_expr->type = ltype;
- }
-
- if (node->kind == Ast_Kind_Alias) {
- AstAlias* alias = (AstAlias *) node;
- alias->type = resolve_expression_type(alias->alias);
- }
-
- if (node_is_type((AstNode *) node)) {
- return &basic_types[Basic_Kind_Type_Index];
- }
-
- if (node->type == NULL)
- node->type = type_build_from_ast(context.ast_alloc, node->type_node);
-
- if (node->kind == Ast_Kind_NumLit && node->type->kind == Type_Kind_Basic) {
- if (node->type->Basic.kind == Basic_Kind_Int_Unsized) {
- if (bh_abs(((AstNumLit *) node)->value.l) >= (1ull << 32))
- convert_numlit_to_type((AstNumLit *) node, &basic_types[Basic_Kind_I64]);
- else
- convert_numlit_to_type((AstNumLit *) node, &basic_types[Basic_Kind_I32]);
- }
- else if (node->type->Basic.kind == Basic_Kind_Float_Unsized) {
- convert_numlit_to_type((AstNumLit *) node, &basic_types[Basic_Kind_F64]);
- }
- }
-
- return node->type;
-}
-
-i64 get_expression_integer_value(AstTyped* node) {
- resolve_expression_type(node);
-
- if (node->kind == Ast_Kind_NumLit && type_is_integer(node->type)) {
- return ((AstNumLit *) node)->value.l;
- }
-
- if (node->kind == Ast_Kind_NumLit && type_is_bool(node->type)) {
- return ((AstNumLit *) node)->value.i;
- }
-
- if (node->kind == Ast_Kind_Argument) {
- return get_expression_integer_value(((AstArgument *) node)->value);
- }
-
- if (node->kind == Ast_Kind_Size_Of) {
- return ((AstSizeOf *) node)->size;
- }
-
- if (node->kind == Ast_Kind_Align_Of) {
- return ((AstAlignOf *) node)->alignment;
- }
-
- if (node->kind == Ast_Kind_Alias) {
- return get_expression_integer_value(((AstAlias *) node)->alias);
- }
-
- if (node_is_type((AstNode*) node)) {
- Type* type = type_build_from_ast(context.ast_alloc, (AstType *) node);
- return type->id;
- }
-
- return 0;
-}
-
-static const b32 cast_legality[][12] = {
- /* I8 */ { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
- /* U8 */ { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
- /* I16 */ { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
- /* U16 */ { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
- /* I32 */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
- /* U32 */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
- /* I64 */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 },
- /* U64 */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 },
- /* F32 */ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0 },
- /* F64 */ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0 },
- /* PTR */ { 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0 },
- /* TYP */ { 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1,}
-};
-
-b32 cast_is_legal(Type* from_, Type* to_, char** err_msg) {
- Type* from = from_;
- Type* to = to_;
-
- if (from == NULL) {
- if (err_msg) *err_msg = "'from' is null. (Compiler Error)";
- return 0;
- }
- if (to == NULL) {
- if (err_msg) *err_msg = "'to' is null. (Compiler Error)";
- return 0;
- }
-
- if (from->kind == Type_Kind_Enum) from = from->Enum.backing;
- if (to->kind == Type_Kind_Enum) to = to->Enum.backing;
-
- if (from->kind == Type_Kind_Struct || to->kind == Type_Kind_Struct) {
- *err_msg = "Cannot cast to or from a struct.";
- return 0;
- }
-
- if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_Array) {
- if (!types_are_compatible(to->Slice.ptr_to_data->Pointer.elem, from->Array.elem)) {
- *err_msg = "Array to slice cast is not valid here because the types are different.";
- return 0;
- } else {
- return 1;
- }
- }
-
- if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_VarArgs) {
- if (!types_are_compatible(to->Slice.ptr_to_data->Pointer.elem, from->VarArgs.ptr_to_data->Pointer.elem)) {
- *err_msg = "Variadic argument to slice cast is not valid here because the types are different.";
- return 0;
- } else {
- return 1;
- }
- }
-
- if (from->kind == Type_Kind_Slice || to->kind == Type_Kind_Slice) {
- *err_msg = "Cannot cast to or from a slice.";
- return 0;
- }
-
- if (from->kind == Type_Kind_DynArray || to->kind == Type_Kind_DynArray) {
- *err_msg = "Cannot cast to or from a dynamic array.";
- return 0;
- }
-
- if (to->kind == Type_Kind_Function) {
- *err_msg = "Cannot cast to a function.";
- return 0;
- }
-
- if ( (type_is_simd(to) && !type_is_simd(from))
- || (!type_is_simd(to) && type_is_simd(from))) {
- *err_msg = "Can only perform a SIMD cast between SIMD types.";
- return 0;
- }
-
- if (from->kind == Type_Kind_Basic && from->Basic.kind == Basic_Kind_Void) {
- *err_msg = "Cannot cast from void.";
- return 0;
- }
- i32 fromidx = -1, toidx = -1;
- if (from->Basic.flags & Basic_Flag_Pointer || from->kind == Type_Kind_Array) {
- fromidx = 10;
- }
- else if (from->Basic.flags & Basic_Flag_Integer) {
- b32 unsign = (from->Basic.flags & Basic_Flag_Unsigned) != 0;
-
- fromidx = log2_dumb(from->Basic.size) * 2 + unsign;
- }
- else if (from->Basic.flags & Basic_Flag_Float) {
- if (from->Basic.size == 4) fromidx = 8;
- else if (from->Basic.size == 8) fromidx = 9;
- }
- else if (from->Basic.flags & Basic_Flag_Boolean) {
- fromidx = 0;
- }
- else if (from->Basic.flags & Basic_Flag_Type_Index) {
- fromidx = 11;
- }
-
- if (to->Basic.flags & Basic_Flag_Pointer || to->kind == Type_Kind_Array) {
- toidx = 10;
- }
- else if (to->Basic.flags & Basic_Flag_Integer) {
- b32 unsign = (to->Basic.flags & Basic_Flag_Unsigned) != 0;
-
- toidx = log2_dumb(to->Basic.size) * 2 + unsign;
- }
- else if (to->Basic.flags & Basic_Flag_Float) {
- if (to->Basic.size == 4) toidx = 8;
- else if (to->Basic.size == 8) toidx = 9;
- }
- else if (to->Basic.flags & Basic_Flag_Boolean) {
- toidx = 0;
- }
- else if (to->Basic.flags & Basic_Flag_Type_Index) {
- toidx = 11;
- }
-
- if (fromidx != -1 && toidx != -1) {
- if (!cast_legality[fromidx][toidx]) {
- *err_msg = bh_aprintf(global_heap_allocator, "Cast from '%s' to '%s' is not allowed.", type_get_name(from_), type_get_name(to_));
- return 0;
- }
- } else {
- *err_msg = bh_aprintf(global_heap_allocator, "Cast from '%s' to '%s' is not allowed.", type_get_name(from_), type_get_name(to_));
- return 0;
- }
-
- *err_msg = NULL;
- return 1;
-}
-
-char* get_function_name(AstFunction* func) {
- if (func->kind != Ast_Kind_Function) return "<procedure>";
-
- if (func->name != NULL) {
- return bh_aprintf(global_scratch_allocator, "%b", func->name->text, func->name->length);
- }
-
- if (func->exported_name != NULL) {
- return bh_aprintf(global_scratch_allocator,
- "EXPORTED:%b",
- func->exported_name->text,
- func->exported_name->length);
- }
-
- return "<anonymous procedure>";
-}
-
-AstNode* strip_aliases(AstNode* n) {
- if (n == NULL) return n;
-
- while (n->kind == Ast_Kind_Alias) n = (AstNode *) ((AstAlias *) n)->alias;
-
- return n;
-}
-
-AstNumLit* make_bool_literal(bh_allocator a, b32 b) {
- AstNumLit* bl = onyx_ast_node_new(a, sizeof(AstNumLit), Ast_Kind_NumLit);
- bl->flags |= Ast_Flag_Comptime;
- bl->type_node = (AstType *) &basic_type_bool;
-
- bl->value.i = b ? 1 : 0;
- return bl;
-}
-
-AstNumLit* make_int_literal(bh_allocator a, i64 i) {
- AstNumLit* num = onyx_ast_node_new(a, sizeof(AstNumLit), Ast_Kind_NumLit);
- num->flags |= Ast_Flag_Comptime;
-
- if (bh_abs(i) >= ((u64) 1 << 32))
- num->type_node = (AstType *) &basic_type_i64;
- else
- num->type_node = (AstType *) &basic_type_i32;
-
- num->value.l = i;
- return num;
-}
-
-AstNumLit* make_float_literal(bh_allocator a, f64 d) {
- // NOTE: Use convert_numlit_to_type to make this a concrete float
- AstNumLit* num = onyx_ast_node_new(a, sizeof(AstNumLit), Ast_Kind_NumLit);
- num->flags |= Ast_Flag_Comptime;
- num->type_node = (AstType *) &basic_type_float_unsized;
- num->value.d = d;
- return num;
-}
-
-AstRangeLiteral* make_range_literal(bh_allocator a, AstTyped* low, AstTyped* high) {
- AstRangeLiteral* rl = onyx_ast_node_new(a, sizeof(AstRangeLiteral), Ast_Kind_Range_Literal);
- rl->type = builtin_range_type_type;
- rl->low = low;
- rl->high = high;
- return rl;
-}
-
-AstBinaryOp* make_binary_op(bh_allocator a, BinaryOp operation, AstTyped* left, AstTyped* right) {
- AstBinaryOp* binop_node = onyx_ast_node_new(a, sizeof(AstBinaryOp), Ast_Kind_Binary_Op);
- binop_node->left = left;
- binop_node->right = right;
- binop_node->operation = operation;
- return binop_node;
-}
-
-AstArgument* make_argument(bh_allocator a, AstTyped* value) {
- AstArgument* arg = onyx_ast_node_new(a, sizeof(AstArgument), Ast_Kind_Argument);
- if (value->token) arg->token = value->token;
- arg->value = value;
- arg->type = value->type;
- arg->next = NULL;
- arg->va_kind = VA_Kind_Not_VA;
- return arg;
-}
-
-AstFieldAccess* make_field_access(bh_allocator a, AstTyped* node, char* field) {
- AstFieldAccess* fa = onyx_ast_node_new(a, sizeof(AstFieldAccess), Ast_Kind_Field_Access);
- if (node->token) fa->token = node->token;
- fa->field = field;
- fa->expr = node;
-
- return fa;
-}
-
-AstAddressOf* make_address_of(bh_allocator a, AstTyped* node) {
- AstAddressOf* ao = onyx_ast_node_new(a, sizeof(AstAddressOf), Ast_Kind_Address_Of);
- if (node->token) ao->token = node->token;
- ao->expr = node;
-
- return ao;
-}
-
-AstLocal* make_local(bh_allocator a, OnyxToken* token, AstType* type_node) {
- AstLocal* local = onyx_ast_node_new(a, sizeof(AstLocal), Ast_Kind_Local);
- local->token = token;
- local->type_node = type_node;
-
- return local;
-}
-
-AstNode* make_symbol(bh_allocator a, OnyxToken* sym) {
- AstNode* symbol = onyx_ast_node_new(a, sizeof(AstNode), Ast_Kind_Symbol);
- symbol->token = sym;
- return symbol;
-}
-
-void arguments_initialize(Arguments* args) {
- if (args->values == NULL) bh_arr_new(global_heap_allocator, args->values, 2);
- if (args->named_values == NULL) bh_arr_new(global_heap_allocator, args->named_values, 2);
-
- // CLEANUP: I'm not sure if I need to initialize these to NULL values, but it doesn't hurt.
- fori (i, 0, 2) {
- args->values[i] = NULL;
- args->named_values[i] = NULL;
- }
-}
-
-void arguments_ensure_length(Arguments* args, u32 count) {
- // Make the array big enough
- bh_arr_grow(args->values, count);
-
- // NULL initialize the new elements
- fori (i, bh_arr_length(args->values), count) args->values[i] = NULL;
-
- // Set the actual length to the count, but never let it decrease in size
- bh_arr_set_length(args->values, bh_max(count, (u32) bh_arr_length(args->values)));
-}
-
-void arguments_copy(Arguments* dest, Arguments* src) {
- dest->named_values = src->named_values;
-
- arguments_ensure_length(dest, bh_arr_length(src->values));
- bh_arr_each(AstTyped*, arg, dest->values) *arg = NULL;
- fori (i, 0, bh_arr_length(src->values)) dest->values[i] = src->values[i];
-}
-
-// In clone, the named_values are not copied. This is used in find_matching_overload_by_arguments since it doesn't need them to be copied.
-void arguments_clone(Arguments* dest, Arguments* src) {
- dest->named_values = src->named_values;
- dest->values = bh_arr_copy(global_heap_allocator, src->values);
-}
-
-void arguments_deep_clone(bh_allocator a, Arguments* dest, Arguments* src) {
- dest->values = NULL;
- dest->named_values = NULL;
-
- bh_arr_new(global_heap_allocator, dest->named_values, bh_arr_length(src->named_values));
- bh_arr_new(global_heap_allocator, dest->values, bh_arr_length(src->values));
-
- bh_arr_each(AstNamedValue *, nv, src->named_values)
- bh_arr_push(dest->named_values, (AstNamedValue *) ast_clone(a, *nv));
-
- bh_arr_each(AstTyped *, val, src->values)
- bh_arr_push(dest->values, (AstTyped *) ast_clone(a, (AstNode *) *val));
-}
-
-void arguments_remove_baked(Arguments* args) {
- fori (i, 0, bh_arr_length(args->values)) {
- if (args->values[i]->kind != Ast_Kind_Argument) continue;
- if (!((AstArgument *) args->values[i])->is_baked) continue;
-
- bh_arr_deleten(args->values, i, 1);
- i--;
- }
-
- fori (i, 0, bh_arr_length(args->named_values)) {
- if (args->named_values[i]->value->kind != Ast_Kind_Argument) continue;
- if (!((AstArgument *) args->named_values[i]->value)->is_baked) continue;
-
- bh_arr_deleten(args->named_values, i, 1);
- i--;
- }
-}
-
-// GROSS: Using void* to avoid having to cast everything.
-const char* node_get_type_name(void* node) {
- if (node_is_type((AstNode *) node)) return "type_expr";
-
- if (((AstNode *) node)->kind == Ast_Kind_Argument) {
- return node_get_type_name(((AstArgument *) node)->value);
- }
-
- if (((AstNode *) node)->kind == Ast_Kind_Polymorphic_Proc) {
- return "polymorphic procedure";
- }
-
- return type_get_name(((AstTyped *) node)->type);
-}
-
-b32 static_if_resolution(AstIf* static_if) {
- if (static_if->kind != Ast_Kind_Static_If) return 0;
-
- // assert(condition_value->kind == Ast_Kind_NumLit); // This should be right, right?
- i64 value = get_expression_integer_value(static_if->cond);
-
- return value != 0;
-}
+++ /dev/null
-#include "onyxastnodes.h"
-#include "onyxtypes.h"
-#include "onyxerrors.h"
-#include "onyxutils.h"
-
-AstBasicType basic_type_void = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_Void] };
-AstBasicType basic_type_bool = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_Bool] };
-AstBasicType basic_type_i8 = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_I8] };
-AstBasicType basic_type_u8 = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_U8] };
-AstBasicType basic_type_i16 = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_I16] };
-AstBasicType basic_type_u16 = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_U16] };
-AstBasicType basic_type_i32 = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_I32] };
-AstBasicType basic_type_u32 = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_U32] };
-AstBasicType basic_type_i64 = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_I64] };
-AstBasicType basic_type_u64 = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_U64] };
-AstBasicType basic_type_f32 = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_F32] };
-AstBasicType basic_type_f64 = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_F64] };
-AstBasicType basic_type_rawptr = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_Rawptr] };
-AstBasicType basic_type_type_expr = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_Type_Index] };
-
-// NOTE: Types used for numeric literals
-AstBasicType basic_type_int_unsized = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_Int_Unsized] };
-AstBasicType basic_type_float_unsized = { Ast_Kind_Basic_Type, 0, NULL, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_Float_Unsized] };
-
-static OnyxToken simd_token = { Token_Type_Symbol, 0, "", { 0 } };
-AstBasicType basic_type_i8x16 = { Ast_Kind_Basic_Type, 0, &simd_token, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_I8X16] };
-AstBasicType basic_type_i16x8 = { Ast_Kind_Basic_Type, 0, &simd_token, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_I16X8] };
-AstBasicType basic_type_i32x4 = { Ast_Kind_Basic_Type, 0, &simd_token, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_I32X4] };
-AstBasicType basic_type_i64x2 = { Ast_Kind_Basic_Type, 0, &simd_token, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_I64X2] };
-AstBasicType basic_type_f32x4 = { Ast_Kind_Basic_Type, 0, &simd_token, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_F32X4] };
-AstBasicType basic_type_f64x2 = { Ast_Kind_Basic_Type, 0, &simd_token, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_F64X2] };
-AstBasicType basic_type_v128 = { Ast_Kind_Basic_Type, 0, &simd_token, NULL, 0, NULL, NULL, &basic_types[Basic_Kind_V128] };
-
-// HACK
-// :AutoReturnType
-Type type_auto_return = { 0 };
-AstBasicType basic_type_auto_return = { Ast_Kind_Basic_Type, 0, &simd_token, NULL, 0, NULL, NULL, &type_auto_return };
-
-OnyxToken builtin_package_token = { Token_Type_Symbol, 7, "builtin ", { 0 } };
-
-static OnyxToken builtin_heap_start_token = { Token_Type_Symbol, 12, "__heap_start ", { 0 } };
-static OnyxToken builtin_stack_top_token = { Token_Type_Symbol, 11, "__stack_top ", { 0 } };
-AstNumLit builtin_heap_start = { Ast_Kind_NumLit, Ast_Flag_Const, &builtin_heap_start_token, NULL, NULL, (AstType *) &basic_type_rawptr, NULL, 0 };
-AstGlobal builtin_stack_top = { Ast_Kind_Global, Ast_Flag_Const | Ast_Flag_Global_Stack_Top, &builtin_stack_top_token, NULL, NULL, (AstType *) &basic_type_rawptr, NULL };
-
-AstType *builtin_string_type;
-AstType *builtin_range_type;
-Type *builtin_range_type_type;
-AstType *builtin_vararg_type;
-Type *builtin_vararg_type_type;
-AstTyped *builtin_context_variable;
-AstType *builtin_allocator_type;
-AstType *builtin_iterator_type;
-AstType *builtin_callsite_type;
-AstType *builtin_any_type;
-AstType *builtin_code_type;
-
-AstTyped *type_table_node = NULL;
-
-const BuiltinSymbol builtin_symbols[] = {
- { NULL, "void", (AstNode *) &basic_type_void },
- { NULL, "bool", (AstNode *) &basic_type_bool },
- { NULL, "i8", (AstNode *) &basic_type_i8 },
- { NULL, "u8", (AstNode *) &basic_type_u8 },
- { NULL, "i16", (AstNode *) &basic_type_i16 },
- { NULL, "u16", (AstNode *) &basic_type_u16 },
- { NULL, "i32", (AstNode *) &basic_type_i32 },
- { NULL, "u32", (AstNode *) &basic_type_u32 },
- { NULL, "i64", (AstNode *) &basic_type_i64 },
- { NULL, "u64", (AstNode *) &basic_type_u64 },
- { NULL, "f32", (AstNode *) &basic_type_f32 },
- { NULL, "f64", (AstNode *) &basic_type_f64 },
- { NULL, "rawptr", (AstNode *) &basic_type_rawptr },
- { NULL, "type_expr", (AstNode *) &basic_type_type_expr },
-
- { "simd", "i8x16", (AstNode *) &basic_type_i8x16 },
- { "simd", "i16x8", (AstNode *) &basic_type_i16x8 },
- { "simd", "i32x4", (AstNode *) &basic_type_i32x4 },
- { "simd", "i64x2", (AstNode *) &basic_type_i64x2 },
- { "simd", "f32x4", (AstNode *) &basic_type_f32x4 },
- { "simd", "f64x2", (AstNode *) &basic_type_f64x2 },
- { "simd", "v128", (AstNode *) &basic_type_v128 },
-
- { "builtin", "__heap_start", (AstNode *) &builtin_heap_start },
- { "builtin", "__stack_top", (AstNode *) &builtin_stack_top },
-
- { NULL, NULL, NULL },
-};
-
-bh_table(OnyxIntrinsic) intrinsic_table;
-
-static IntrinsicMap builtin_intrinsics[] = {
- { "memory_size", ONYX_INTRINSIC_MEMORY_SIZE },
- { "memory_grow", ONYX_INTRINSIC_MEMORY_GROW },
- { "memory_copy", ONYX_INTRINSIC_MEMORY_COPY },
- { "memory_fill", ONYX_INTRINSIC_MEMORY_FILL },
-
- { "__initialize", ONYX_INTRINSIC_INITIALIZE },
- { "__zero_value", ONYX_INTRINSIC_ZERO_VALUE },
-
- { "clz_i32", ONYX_INTRINSIC_I32_CLZ },
- { "ctz_i32", ONYX_INTRINSIC_I32_CTZ },
- { "popcnt_i32", ONYX_INTRINSIC_I32_POPCNT },
- { "and_i32", ONYX_INTRINSIC_I32_AND },
- { "or_i32", ONYX_INTRINSIC_I32_OR },
- { "xor_i32", ONYX_INTRINSIC_I32_XOR },
- { "shl_i32", ONYX_INTRINSIC_I32_SHL },
- { "slr_i32", ONYX_INTRINSIC_I32_SLR },
- { "sar_i32", ONYX_INTRINSIC_I32_SAR },
- { "rotl_i32", ONYX_INTRINSIC_I32_ROTL },
- { "rotr_i32", ONYX_INTRINSIC_I32_ROTR },
-
- { "clz_i64", ONYX_INTRINSIC_I64_CLZ },
- { "ctz_i64", ONYX_INTRINSIC_I64_CTZ },
- { "popcnt_i64", ONYX_INTRINSIC_I64_POPCNT },
- { "and_i64", ONYX_INTRINSIC_I64_AND },
- { "or_i64", ONYX_INTRINSIC_I64_OR },
- { "xor_i64", ONYX_INTRINSIC_I64_XOR },
- { "shl_i64", ONYX_INTRINSIC_I64_SHL },
- { "slr_i64", ONYX_INTRINSIC_I64_SLR },
- { "sar_i64", ONYX_INTRINSIC_I64_SAR },
- { "rotl_i64", ONYX_INTRINSIC_I64_ROTL },
- { "rotr_i64", ONYX_INTRINSIC_I64_ROTR },
-
- { "abs_f32", ONYX_INTRINSIC_F32_ABS },
- { "ceil_f32", ONYX_INTRINSIC_F32_CEIL },
- { "floor_f32", ONYX_INTRINSIC_F32_FLOOR },
- { "trunc_f32", ONYX_INTRINSIC_F32_TRUNC },
- { "nearest_f32", ONYX_INTRINSIC_F32_NEAREST },
- { "sqrt_f32", ONYX_INTRINSIC_F32_SQRT },
- { "min_f32", ONYX_INTRINSIC_F32_MIN },
- { "max_f32", ONYX_INTRINSIC_F32_MAX },
- { "copysign_f32", ONYX_INTRINSIC_F32_COPYSIGN },
-
- { "abs_f64", ONYX_INTRINSIC_F64_ABS },
- { "ceil_f64", ONYX_INTRINSIC_F64_CEIL },
- { "floor_f64", ONYX_INTRINSIC_F64_FLOOR },
- { "trunc_f64", ONYX_INTRINSIC_F64_TRUNC },
- { "nearest_f64", ONYX_INTRINSIC_F64_NEAREST },
- { "sqrt_f64", ONYX_INTRINSIC_F64_SQRT },
- { "min_f64", ONYX_INTRINSIC_F64_MIN },
- { "max_f64", ONYX_INTRINSIC_F64_MAX },
- { "copysign_f64", ONYX_INTRINSIC_F64_COPYSIGN },
-
-
- // SIMD Intrinsics
- { "v128_const", ONYX_INTRINSIC_V128_CONST },
- { "i8x16_const", ONYX_INTRINSIC_I8X16_CONST },
- { "i16x8_const", ONYX_INTRINSIC_I16X8_CONST },
- { "i32x4_const", ONYX_INTRINSIC_I32X4_CONST },
- { "i64x2_const", ONYX_INTRINSIC_I64X2_CONST },
- { "f32x4_const", ONYX_INTRINSIC_F32X4_CONST },
- { "f64x2_const", ONYX_INTRINSIC_F64X2_CONST },
- { "i8x16_shuffle", ONYX_INTRINSIC_I8X16_SHUFFLE },
-
- { "i8x16_extract_lane_s", ONYX_INTRINSIC_I8X16_EXTRACT_LANE_S },
- { "i8x16_extract_lane_u", ONYX_INTRINSIC_I8X16_EXTRACT_LANE_U },
- { "i8x16_replace_lane", ONYX_INTRINSIC_I8X16_REPLACE_LANE },
- { "i16x8_extract_lane_s", ONYX_INTRINSIC_I16X8_EXTRACT_LANE_S },
- { "i16x8_extract_lane_u", ONYX_INTRINSIC_I16X8_EXTRACT_LANE_U },
- { "i16x8_replace_lane", ONYX_INTRINSIC_I16X8_REPLACE_LANE },
- { "i32x4_extract_lane", ONYX_INTRINSIC_I32X4_EXTRACT_LANE },
- { "i32x4_replace_lane", ONYX_INTRINSIC_I32X4_REPLACE_LANE },
- { "i64x2_extract_lane", ONYX_INTRINSIC_I64X2_EXTRACT_LANE },
- { "i64x2_replace_lane", ONYX_INTRINSIC_I64X2_REPLACE_LANE },
- { "f32x4_extract_lane", ONYX_INTRINSIC_F32X4_EXTRACT_LANE },
- { "f32x4_replace_lane", ONYX_INTRINSIC_F32X4_REPLACE_LANE },
- { "f64x2_extract_lane", ONYX_INTRINSIC_F64X2_EXTRACT_LANE },
- { "f64x2_replace_lane", ONYX_INTRINSIC_F64X2_REPLACE_LANE },
-
- { "i8x16_swizzle", ONYX_INTRINSIC_I8X16_SWIZZLE },
- { "i8x16_splat", ONYX_INTRINSIC_I8X16_SPLAT },
- { "i16x8_splat", ONYX_INTRINSIC_I16X8_SPLAT },
- { "i32x4_splat", ONYX_INTRINSIC_I32X4_SPLAT },
- { "i64x2_splat", ONYX_INTRINSIC_I64X2_SPLAT },
- { "f32x4_splat", ONYX_INTRINSIC_F32X4_SPLAT },
- { "f64x2_splat", ONYX_INTRINSIC_F64X2_SPLAT },
-
- { "i8x16_eq", ONYX_INTRINSIC_I8X16_EQ },
- { "i8x16_neq", ONYX_INTRINSIC_I8X16_NEQ },
- { "i8x16_lt_s", ONYX_INTRINSIC_I8X16_LT_S },
- { "i8x16_lt_u", ONYX_INTRINSIC_I8X16_LT_U },
- { "i8x16_gt_s", ONYX_INTRINSIC_I8X16_GT_S },
- { "i8x16_gt_u", ONYX_INTRINSIC_I8X16_GT_U },
- { "i8x16_le_s", ONYX_INTRINSIC_I8X16_LE_S },
- { "i8x16_le_u", ONYX_INTRINSIC_I8X16_LE_U },
- { "i8x16_ge_s", ONYX_INTRINSIC_I8X16_GE_S },
- { "i8x16_ge_u", ONYX_INTRINSIC_I8X16_GE_U },
-
- { "i16x8_eq", ONYX_INTRINSIC_I16X8_EQ },
- { "i16x8_neq", ONYX_INTRINSIC_I16X8_NEQ },
- { "i16x8_lt_s", ONYX_INTRINSIC_I16X8_LT_S },
- { "i16x8_lt_u", ONYX_INTRINSIC_I16X8_LT_U },
- { "i16x8_gt_s", ONYX_INTRINSIC_I16X8_GT_S },
- { "i16x8_gt_u", ONYX_INTRINSIC_I16X8_GT_U },
- { "i16x8_le_s", ONYX_INTRINSIC_I16X8_LE_S },
- { "i16x8_le_u", ONYX_INTRINSIC_I16X8_LE_U },
- { "i16x8_ge_s", ONYX_INTRINSIC_I16X8_GE_S },
- { "i16x8_ge_u", ONYX_INTRINSIC_I16X8_GE_U },
-
- { "i32x4_eq", ONYX_INTRINSIC_I32X4_EQ },
- { "i32x4_neq", ONYX_INTRINSIC_I32X4_NEQ },
- { "i32x4_lt_s", ONYX_INTRINSIC_I32X4_LT_S },
- { "i32x4_lt_u", ONYX_INTRINSIC_I32X4_LT_U },
- { "i32x4_gt_s", ONYX_INTRINSIC_I32X4_GT_S },
- { "i32x4_gt_u", ONYX_INTRINSIC_I32X4_GT_U },
- { "i32x4_le_s", ONYX_INTRINSIC_I32X4_LE_S },
- { "i32x4_le_u", ONYX_INTRINSIC_I32X4_LE_U },
- { "i32x4_ge_s", ONYX_INTRINSIC_I32X4_GE_S },
- { "i32x4_ge_u", ONYX_INTRINSIC_I32X4_GE_U },
-
- { "f32x4_eq", ONYX_INTRINSIC_F32X4_EQ },
- { "f32x4_neq", ONYX_INTRINSIC_F32X4_NEQ },
- { "f32x4_lt", ONYX_INTRINSIC_F32X4_LT },
- { "f32x4_gt", ONYX_INTRINSIC_F32X4_GT },
- { "f32x4_le", ONYX_INTRINSIC_F32X4_LE },
- { "f32x4_ge", ONYX_INTRINSIC_F32X4_GE },
-
- { "f64x2_eq", ONYX_INTRINSIC_F64X2_EQ },
- { "f64x2_neq", ONYX_INTRINSIC_F64X2_NEQ },
- { "f64x2_lt", ONYX_INTRINSIC_F64X2_LT },
- { "f64x2_gt", ONYX_INTRINSIC_F64X2_GT },
- { "f64x2_le", ONYX_INTRINSIC_F64X2_LE },
- { "f64x2_ge", ONYX_INTRINSIC_F64X2_GE },
-
- { "v128_not", ONYX_INTRINSIC_V128_NOT },
- { "v128_and", ONYX_INTRINSIC_V128_AND },
- { "v128_andnot", ONYX_INTRINSIC_V128_ANDNOT },
- { "v128_or", ONYX_INTRINSIC_V128_OR },
- { "v128_xor", ONYX_INTRINSIC_V128_XOR },
- { "v128_bitselect", ONYX_INTRINSIC_V128_BITSELECT },
-
- { "i8x16_abs", ONYX_INTRINSIC_I8X16_ABS },
- { "i8x16_neg", ONYX_INTRINSIC_I8X16_NEG },
- { "i8x16_any_true", ONYX_INTRINSIC_I8X16_ANY_TRUE },
- { "i8x16_all_true", ONYX_INTRINSIC_I8X16_ALL_TRUE },
- { "i8x16_bitmask", ONYX_INTRINSIC_I8X16_BITMASK },
- { "i8x16_narrow_i16x8_s", ONYX_INTRINSIC_I8X16_NARROW_I16X8_S },
- { "i8x16_narrow_i16x8_u", ONYX_INTRINSIC_I8X16_NARROW_I16X8_U },
- { "i8x16_shl", ONYX_INTRINSIC_I8X16_SHL },
- { "i8x16_shr_s", ONYX_INTRINSIC_I8X16_SHR_S },
- { "i8x16_shr_u", ONYX_INTRINSIC_I8X16_SHR_U },
- { "i8x16_add", ONYX_INTRINSIC_I8X16_ADD },
- { "i8x16_add_sat_s", ONYX_INTRINSIC_I8X16_ADD_SAT_S },
- { "i8x16_add_sat_u", ONYX_INTRINSIC_I8X16_ADD_SAT_U },
- { "i8x16_sub", ONYX_INTRINSIC_I8X16_SUB },
- { "i8x16_sub_sat_s", ONYX_INTRINSIC_I8X16_SUB_SAT_S },
- { "i8x16_sub_sat_u", ONYX_INTRINSIC_I8X16_SUB_SAT_U },
- { "i8x16_min_s", ONYX_INTRINSIC_I8X16_MIN_S },
- { "i8x16_min_u", ONYX_INTRINSIC_I8X16_MIN_U },
- { "i8x16_max_s", ONYX_INTRINSIC_I8X16_MAX_S },
- { "i8x16_max_u", ONYX_INTRINSIC_I8X16_MAX_U },
- { "i8x16_avgr_u", ONYX_INTRINSIC_I8X16_AVGR_U },
-
- { "i16x8_abs", ONYX_INTRINSIC_I16X8_ABS },
- { "i16x8_neg", ONYX_INTRINSIC_I16X8_NEG },
- { "i16x8_any_true", ONYX_INTRINSIC_I16X8_ANY_TRUE },
- { "i16x8_all_true", ONYX_INTRINSIC_I16X8_ALL_TRUE },
- { "i16x8_bitmask", ONYX_INTRINSIC_I16X8_BITMASK },
- { "i16x8_narrow_i32x4_s", ONYX_INTRINSIC_I16X8_NARROW_I32X4_S },
- { "i16x8_narrow_i32x4_u", ONYX_INTRINSIC_I16X8_NARROW_I32X4_U },
- { "i16x8_widen_low_i8x16_s", ONYX_INTRINSIC_I16X8_WIDEN_LOW_I8X16_S },
- { "i16x8_widen_high_i8x16_s", ONYX_INTRINSIC_I16X8_WIDEN_HIGH_I8X16_S },
- { "i16x8_widen_low_i8x16_u", ONYX_INTRINSIC_I16X8_WIDEN_LOW_I8X16_U },
- { "i16x8_widen_high_i8x16_u", ONYX_INTRINSIC_I16X8_WIDEN_HIGH_I8X16_U },
- { "i16x8_shl", ONYX_INTRINSIC_I16X8_SHL },
- { "i16x8_shr_s", ONYX_INTRINSIC_I16X8_SHR_S },
- { "i16x8_shr_u", ONYX_INTRINSIC_I16X8_SHR_U },
- { "i16x8_add", ONYX_INTRINSIC_I16X8_ADD },
- { "i16x8_add_sat_s", ONYX_INTRINSIC_I16X8_ADD_SAT_S },
- { "i16x8_add_sat_u", ONYX_INTRINSIC_I16X8_ADD_SAT_U },
- { "i16x8_sub", ONYX_INTRINSIC_I16X8_SUB },
- { "i16x8_sub_sat_s", ONYX_INTRINSIC_I16X8_SUB_SAT_S },
- { "i16x8_sub_sat_u", ONYX_INTRINSIC_I16X8_SUB_SAT_U },
- { "i16x8_mul", ONYX_INTRINSIC_I16X8_MUL },
- { "i16x8_min_s", ONYX_INTRINSIC_I16X8_MIN_S },
- { "i16x8_min_u", ONYX_INTRINSIC_I16X8_MIN_U },
- { "i16x8_max_s", ONYX_INTRINSIC_I16X8_MAX_S },
- { "i16x8_max_u", ONYX_INTRINSIC_I16X8_MAX_U },
- { "i16x8_avgr_u", ONYX_INTRINSIC_I16X8_AVGR_U },
-
- { "i32x4_abs", ONYX_INTRINSIC_I32X4_ABS },
- { "i32x4_neg", ONYX_INTRINSIC_I32X4_NEG },
- { "i32x4_any_true", ONYX_INTRINSIC_I32X4_ANY_TRUE },
- { "i32x4_all_true", ONYX_INTRINSIC_I32X4_ALL_TRUE },
- { "i32x4_bitmask", ONYX_INTRINSIC_I32X4_BITMASK },
- { "i32x4_widen_low_i16x8_s", ONYX_INTRINSIC_I32X4_WIDEN_LOW_I16X8_S },
- { "i32x4_widen_high_i16x8_s", ONYX_INTRINSIC_I32X4_WIDEN_HIGH_I16X8_S },
- { "i32x4_widen_low_i16x8_u", ONYX_INTRINSIC_I32X4_WIDEN_LOW_I16X8_U },
- { "i32x4_widen_high_i16x8_u", ONYX_INTRINSIC_I32X4_WIDEN_HIGH_I16X8_U },
- { "i32x4_shl", ONYX_INTRINSIC_I32X4_SHL },
- { "i32x4_shr_s", ONYX_INTRINSIC_I32X4_SHR_S },
- { "i32x4_shl_u", ONYX_INTRINSIC_I32X4_SHR_U },
- { "i32x4_add", ONYX_INTRINSIC_I32X4_ADD },
- { "i32x4_sub", ONYX_INTRINSIC_I32X4_SUB },
- { "i32x4_mul", ONYX_INTRINSIC_I32X4_MUL },
- { "i32x4_min_s", ONYX_INTRINSIC_I32X4_MIN_S },
- { "i32x4_min_u", ONYX_INTRINSIC_I32X4_MIN_U },
- { "i32x4_max_s", ONYX_INTRINSIC_I32X4_MAX_S },
- { "i32x4_max_u", ONYX_INTRINSIC_I32X4_MAX_U },
-
- { "i64x2_neg", ONYX_INTRINSIC_I64X2_NEG },
- { "i64x2_shl", ONYX_INTRINSIC_I64X2_SHL },
- { "i64x2_shr_s", ONYX_INTRINSIC_I64X2_SHR_S },
- { "i64x2_shr_u", ONYX_INTRINSIC_I64X2_SHR_U },
- { "i64x2_add", ONYX_INTRINSIC_I64X2_ADD },
- { "i64x2_sub", ONYX_INTRINSIC_I64X2_SUB },
- { "i64x2_mul", ONYX_INTRINSIC_I64X2_MUL },
-
- { "f32x4_abs", ONYX_INTRINSIC_F32X4_ABS },
- { "f32x4_neg", ONYX_INTRINSIC_F32X4_NEG },
- { "f32x4_sqrt", ONYX_INTRINSIC_F32X4_SQRT },
- { "f32x4_add", ONYX_INTRINSIC_F32X4_ADD },
- { "f32x4_sub", ONYX_INTRINSIC_F32X4_SUB },
- { "f32x4_mul", ONYX_INTRINSIC_F32X4_MUL },
- { "f32x4_div", ONYX_INTRINSIC_F32X4_DIV },
- { "f32x4_min", ONYX_INTRINSIC_F32X4_MIN },
- { "f32x4_max", ONYX_INTRINSIC_F32X4_MAX },
-
- { "f64x2_abs", ONYX_INTRINSIC_F64X2_ABS },
- { "f64x2_neg", ONYX_INTRINSIC_F64X2_NEG },
- { "f64x2_sqrt", ONYX_INTRINSIC_F64X2_SQRT },
- { "f64x2_add", ONYX_INTRINSIC_F64X2_ADD },
- { "f64x2_sub", ONYX_INTRINSIC_F64X2_SUB },
- { "f64x2_mul", ONYX_INTRINSIC_F64X2_MUL },
- { "f64x2_div", ONYX_INTRINSIC_F64X2_DIV },
- { "f64x2_min", ONYX_INTRINSIC_F64X2_MIN },
- { "f64x2_max", ONYX_INTRINSIC_F64X2_MAX },
-
- { "i32x4_trunc_sat_f32x4_s", ONYX_INTRINSIC_I32X4_TRUNC_SAT_F32X4_S },
- { "i32x4_trunc_sat_f32x4_u", ONYX_INTRINSIC_I32X4_TRUNC_SAT_F32X4_U },
- { "f32x4_convert_i32x4_s", ONYX_INTRINSIC_F32X4_CONVERT_I32X4_S },
- { "f32x4_convert_i32x4_u", ONYX_INTRINSIC_F32X4_CONVERT_I32X4_U },
-
- { NULL, ONYX_INTRINSIC_UNDEFINED },
-};
-
-bh_arr(OverloadOption) operator_overloads[Binary_Op_Count] = { 0 };
-
-void initialize_builtins(bh_allocator a) {
- // HACK
- builtin_package_token.text = bh_strdup(global_heap_allocator, builtin_package_token.text);
-
- BuiltinSymbol* bsym = (BuiltinSymbol *) &builtin_symbols[0];
- while (bsym->sym != NULL) {
- if (bsym->package == NULL)
- symbol_builtin_introduce(context.global_scope, bsym->sym, bsym->node);
- else {
- Package* p = package_lookup_or_create(bsym->package, context.global_scope, a);
- assert(p);
-
- symbol_builtin_introduce(p->scope, bsym->sym, bsym->node);
- }
- bsym++;
- }
-
- Package* p = package_lookup_or_create("builtin", context.global_scope, a);
-
- builtin_string_type = (AstType *) symbol_raw_resolve(p->scope, "str");
- if (builtin_string_type == NULL) {
- onyx_report_error((OnyxFilePos) { 0 }, "'str' struct not found in builtin package.");
- return;
- }
-
- builtin_range_type = (AstType *) symbol_raw_resolve(p->scope, "range");
- if (builtin_range_type == NULL) {
- onyx_report_error((OnyxFilePos) { 0 }, "'range' struct not found in builtin package.");
- return;
- }
-
- builtin_vararg_type = (AstType *) symbol_raw_resolve(p->scope, "vararg");
- if (builtin_range_type == NULL) {
- onyx_report_error((OnyxFilePos) { 0 }, "'vararg' struct not found in builtin package.");
- return;
- }
-
- builtin_context_variable = (AstTyped *) symbol_raw_resolve(p->scope, "context");
- if (builtin_context_variable == NULL) {
- onyx_report_error((OnyxFilePos) { 0 }, "'context' variable not found in builtin package.");
- return;
- }
-
- builtin_allocator_type = (AstType *) symbol_raw_resolve(p->scope, "Allocator");
- if (builtin_allocator_type == NULL) {
- onyx_report_error((OnyxFilePos) { 0 }, "'Allocator' struct not found in builtin package.");
- return;
- }
-
- builtin_iterator_type = (AstType *) symbol_raw_resolve(p->scope, "Iterator");
- if (builtin_iterator_type == NULL) {
- onyx_report_error((OnyxFilePos) { 0 }, "'Iterator' struct not found in builtin package.");
- return;
- }
-
- builtin_callsite_type = (AstType *) symbol_raw_resolve(p->scope, "CallSite");
- if (builtin_callsite_type == NULL) {
- onyx_report_error((OnyxFilePos) { 0 }, "'CallSite' struct not found in builtin package.");
- return;
- }
-
- builtin_any_type = (AstType *) symbol_raw_resolve(p->scope, "any");
- if (builtin_any_type == NULL) {
- onyx_report_error((OnyxFilePos) { 0 }, "'any' struct not found in builtin package.");
- return;
- }
-
- builtin_code_type = (AstType *) symbol_raw_resolve(p->scope, "Code");
- if (builtin_code_type == NULL) {
- onyx_report_error((OnyxFilePos) { 0 }, "'Code' struct not found in builtin package.");
- return;
- }
-
- p = package_lookup("builtin.type_info");
- if (p != NULL) {
- type_table_node = (AstTyped *) symbol_raw_resolve(p->scope, "type_table");
- }
-
- fori (i, 0, Binary_Op_Count) {
- bh_arr_new(global_heap_allocator, operator_overloads[i], 4);
- }
-
- bh_table_init(global_heap_allocator, intrinsic_table, 128);
- IntrinsicMap* intrinsic = &builtin_intrinsics[0];
- while (intrinsic->name != NULL) {
- bh_table_put(OnyxIntrinsic, intrinsic_table, intrinsic->name, intrinsic->intrinsic);
- intrinsic++;
- }
-}
-
-void introduce_build_options(bh_allocator a) {
- Package* p = package_lookup_or_create("runtime", context.global_scope, a);
-
- AstNumLit* runtime_type = make_int_literal(a, context.options->runtime);
- symbol_builtin_introduce(p->scope, "Runtime", (AstNode *) runtime_type);
-}
+++ /dev/null
-#define BH_DEBUG
-#include "onyxparser.h"
-#include "onyxutils.h"
-
-// All of the `check` functions return a boolean that signals if an issue
-// was reached while processing the node. These error booleans propagate
-// up the call stack until they reach `check_entity`.
-
-#define CHECK(kind, ...) do { \
- CheckStatus cs = check_ ## kind (__VA_ARGS__); \
- if (cs > Check_Errors_Start) return cs; \
- } while (0)
-
-#define YIELD(loc, msg) do { \
- if (context.cycle_detected) { \
- onyx_report_error(loc, msg); \
- return Check_Error; \
- } else { \
- return Check_Yield_Macro; \
- } \
- } while (0)
-
-#define YIELD_(loc, msg, ...) do { \
- if (context.cycle_detected) { \
- onyx_report_error(loc, msg, __VA_ARGS__); \
- return Check_Error; \
- } else { \
- return Check_Yield_Macro; \
- } \
- } while (0)
-
-#define ERROR(loc, msg) do { \
- onyx_report_error(loc, msg); \
- return Check_Error; \
- } while (0)
-
-#define ERROR_(loc, msg, ...) do { \
- onyx_report_error(loc, msg, __VA_ARGS__); \
- return Check_Error; \
- } while (0)
-
-typedef enum CheckStatus {
- Check_Success, // The node was successfully checked with out errors
- Check_Complete, // The node is done processing
-
- Check_Errors_Start,
- Check_Return_To_Symres, // Return this node for further symres processing
- Check_Yield_Macro,
- Check_Error, // There was an error when checking the node
-} CheckStatus;
-
-CheckStatus check_block(AstBlock* block);
-CheckStatus check_statement_chain(AstNode** start);
-CheckStatus check_statement(AstNode** pstmt);
-CheckStatus check_return(AstReturn* retnode);
-CheckStatus check_if(AstIfWhile* ifnode);
-CheckStatus check_while(AstIfWhile* whilenode);
-CheckStatus check_for(AstFor* fornode);
-CheckStatus check_switch(AstSwitch* switchnode);
-CheckStatus check_call(AstCall** pcall);
-CheckStatus check_binaryop(AstBinaryOp** pbinop);
-CheckStatus check_unaryop(AstUnaryOp** punop);
-CheckStatus check_struct_literal(AstStructLiteral* sl);
-CheckStatus check_array_literal(AstArrayLiteral* al);
-CheckStatus check_range_literal(AstRangeLiteral** range);
-CheckStatus check_compound(AstCompound* compound);
-CheckStatus check_if_expression(AstIfExpression* if_expr);
-CheckStatus check_expression(AstTyped** expr);
-CheckStatus check_address_of(AstAddressOf* aof);
-CheckStatus check_dereference(AstDereference* deref);
-CheckStatus check_subscript(AstSubscript** paa);
-CheckStatus check_field_access(AstFieldAccess** pfield);
-CheckStatus check_method_call(AstBinaryOp** mcall);
-CheckStatus check_size_of(AstSizeOf* so);
-CheckStatus check_align_of(AstAlignOf* ao);
-CheckStatus check_global(AstGlobal* global);
-CheckStatus check_function(AstFunction* func);
-CheckStatus check_overloaded_function(AstOverloadedFunction* func);
-CheckStatus check_struct(AstStructType* s_node);
-CheckStatus check_function_header(AstFunction* func);
-CheckStatus check_memres_type(AstMemRes* memres);
-CheckStatus check_memres(AstMemRes* memres);
-CheckStatus check_type(AstType* type);
-CheckStatus check_insert_directive(AstDirectiveInsert** pinsert);
-CheckStatus check_do_block(AstDoBlock** pdoblock);
-
-// HACK HACK HACK
-b32 expression_types_must_be_known = 0;
-
-#define STATEMENT_LEVEL 1
-#define EXPRESSION_LEVEL 2
-u32 current_checking_level=0;
-
-static inline void fill_in_type(AstTyped* node);
-
-static inline void fill_in_array_count(AstType* type_node) {
- if (type_node == NULL) return;
-
- if (type_node->kind == Ast_Kind_Type_Alias) {
- fill_in_array_count(((AstTypeAlias *) type_node)->to);
- }
-
- if (type_node->kind == Ast_Kind_Array_Type) {
- if (((AstArrayType *) type_node)->count_expr) {
- // CLEANUP: The return value is not checked on this call.
- check_expression(&((AstArrayType *) type_node)->count_expr);
-
- resolve_expression_type(((AstArrayType *) type_node)->count_expr);
- }
- }
-}
-
-static inline void fill_in_poly_call_args(AstType* type_node) {
- if (type_node == NULL) return;
- if (type_node->kind != Ast_Kind_Poly_Call_Type) return;
-
- AstPolyCallType* pctype = (AstPolyCallType *) type_node;
-
- bh_arr_each(AstNode *, param, pctype->params) {
- if (!node_is_type(*param)) {
- // CLEANUP: The return value is not checked on this call.
- check_expression((AstTyped **) param);
-
- resolve_expression_type((AstTyped *) *param);
- fill_in_type((AstTyped *) *param);
- }
- }
-}
-
-static inline void fill_in_type(AstTyped* node) {
- fill_in_array_count(node->type_node);
- fill_in_poly_call_args(node->type_node);
-
- if (node->type == NULL)
- node->type = type_build_from_ast(context.ast_alloc, node->type_node);
-}
-
-// HACK: This should be baked into a structure, not a global variable.
-static Type** expected_return_type = NULL;
-
-CheckStatus check_return(AstReturn* retnode) {
- if (retnode->expr) {
- CHECK(expression, &retnode->expr);
-
- if (*expected_return_type == &type_auto_return) {
- resolve_expression_type(retnode->expr);
- if (retnode->expr->type == NULL)
- YIELD(retnode->token->pos, "Trying to determine automatic return type.");
-
- *expected_return_type = retnode->expr->type;
- return Check_Success;
- }
-
- if (!type_check_or_auto_cast(&retnode->expr, *expected_return_type)) {
- ERROR_(retnode->token->pos,
- "Expected to return a value of type '%s', returning value of type '%s'.",
- type_get_name(*expected_return_type),
- node_get_type_name(retnode->expr));
- }
-
- } else {
- if (*expected_return_type == &type_auto_return) {
- *expected_return_type = &basic_types[Basic_Kind_Void];
- return Check_Success;
- }
-
- if ((*expected_return_type)->Basic.size > 0) {
- ERROR_(retnode->token->pos,
- "Returning from non-void function without a value. Expected a value of type '%s'.",
- type_get_name(*expected_return_type));
- }
- }
-
- return Check_Success;
-}
-
-CheckStatus check_if(AstIfWhile* ifnode) {
- if (ifnode->initialization != NULL) CHECK(statement_chain, &ifnode->initialization);
-
- if (ifnode->kind == Ast_Kind_Static_If) {
- if ((ifnode->flags & Ast_Flag_Static_If_Resolved) == 0) {
- YIELD(ifnode->token->pos, "Waiting for static if to be resolved.");
- }
-
- if (static_if_resolution(ifnode)) {
- if (ifnode->true_stmt != NULL) CHECK(statement, (AstNode **) &ifnode->true_stmt);
-
- } else {
- if (ifnode->false_stmt != NULL) CHECK(statement, (AstNode **) &ifnode->false_stmt);
- }
-
- } else {
- CHECK(expression, &ifnode->cond);
-
- if (!type_is_bool(ifnode->cond->type)) {
- ERROR_(ifnode->cond->token->pos, "Expected expression of type 'bool' for condition, got '%s'", type_get_name(ifnode->cond->type));
- }
-
- if (ifnode->true_stmt) CHECK(statement, (AstNode **) &ifnode->true_stmt);
- if (ifnode->false_stmt) CHECK(statement, (AstNode **) &ifnode->false_stmt);
- }
-
- return Check_Success;
-}
-
-CheckStatus check_while(AstIfWhile* whilenode) {
- if (whilenode->initialization != NULL) CHECK(statement_chain, &whilenode->initialization);
-
- CHECK(expression, &whilenode->cond);
-
- if (!type_is_bool(whilenode->cond->type)) {
- ERROR_(whilenode->cond->token->pos, "Expected expression of type 'bool' for condition, got '%s'", type_get_name(whilenode->cond->type));
- }
-
- if (whilenode->true_stmt) CHECK(statement, (AstNode **) &whilenode->true_stmt);
- if (whilenode->false_stmt) CHECK(statement, (AstNode **) &whilenode->false_stmt);
-
- return Check_Success;
-}
-
-CheckStatus check_for(AstFor* fornode) {
- CHECK(expression, &fornode->iter);
- resolve_expression_type(fornode->iter);
-
- Type* iter_type = fornode->iter->type;
- if (iter_type == NULL) YIELD(fornode->token->pos, "Waiting for iteration expression type to be known.");
-
- fornode->loop_type = For_Loop_Invalid;
- if (types_are_compatible(iter_type, &basic_types[Basic_Kind_I32])) {
- if (fornode->by_pointer) {
- ERROR(fornode->var->token->pos, "Cannot iterate by pointer over a range.");
- }
-
- AstNumLit* low_0 = make_int_literal(context.ast_alloc, 0);
- AstRangeLiteral* rl = make_range_literal(context.ast_alloc, (AstTyped *) low_0, fornode->iter);
- CHECK(range_literal, &rl);
- fornode->iter = (AstTyped *) rl;
-
- fornode->var->type = builtin_range_type_type->Struct.memarr[0]->type;
- fornode->var->flags |= Ast_Flag_Cannot_Take_Addr;
- fornode->loop_type = For_Loop_Range;
- }
- else if (types_are_compatible(iter_type, builtin_range_type_type)) {
- if (fornode->by_pointer) {
- ERROR(fornode->var->token->pos, "Cannot iterate by pointer over a range.");
- }
-
- // NOTE: Blindly copy the first range member's type which will
- // be the low value. - brendanfh 2020/09/04
- fornode->var->type = builtin_range_type_type->Struct.memarr[0]->type;
- fornode->var->flags |= Ast_Flag_Cannot_Take_Addr;
- fornode->loop_type = For_Loop_Range;
-
- }
- else if (iter_type->kind == Type_Kind_Array) {
- if (fornode->by_pointer) fornode->var->type = type_make_pointer(context.ast_alloc, iter_type->Array.elem);
- else fornode->var->type = iter_type->Array.elem;
-
- fornode->loop_type = For_Loop_Array;
- }
- else if (iter_type->kind == Type_Kind_Slice) {
- if (fornode->by_pointer) fornode->var->type = iter_type->Slice.ptr_to_data;
- else fornode->var->type = iter_type->Slice.ptr_to_data->Pointer.elem;
-
- fornode->loop_type = For_Loop_Slice;
-
- }
- else if (iter_type->kind == Type_Kind_VarArgs) {
- if (fornode->by_pointer) {
- ERROR_(fornode->var->token->pos, "Cannot iterate by pointer over '%s'.", type_get_name(iter_type));
- }
-
- fornode->var->type = iter_type->VarArgs.ptr_to_data->Pointer.elem;
-
- // NOTE: Slices are VarArgs are being treated the same here.
- fornode->loop_type = For_Loop_Slice;
- }
- else if (iter_type->kind == Type_Kind_DynArray) {
- if (fornode->by_pointer) fornode->var->type = iter_type->DynArray.ptr_to_data;
- else fornode->var->type = iter_type->DynArray.ptr_to_data->Pointer.elem;
-
- fornode->loop_type = For_Loop_DynArr;
- }
- else if (type_struct_constructed_from_poly_struct(iter_type, builtin_iterator_type)) {
- if (fornode->by_pointer) {
- ERROR(fornode->var->token->pos, "Cannot iterate by pointer over an iterator.");
- }
-
- // HACK: This assumes the Iterator type only has a single type argument.
- fornode->var->type = iter_type->Struct.poly_sln[0].type;
- fornode->loop_type = For_Loop_Iterator;
- }
-
- if (fornode->by_pointer)
- fornode->var->flags |= Ast_Flag_Cannot_Take_Addr;
-
- if (fornode->loop_type == For_Loop_Invalid) {
- ERROR_(fornode->iter->token->pos,
- "Cannot iterate over a '%s'.",
- type_get_name(iter_type));
- }
-
- CHECK(block, fornode->stmt);
-
- return Check_Success;
-}
-
-static b32 add_case_to_switch_statement(AstSwitch* switchnode, u64 case_value, AstBlock* block, OnyxFilePos pos) {
- switchnode->min_case = bh_min(switchnode->min_case, case_value);
- switchnode->max_case = bh_max(switchnode->max_case, case_value);
-
- if (bh_imap_has(&switchnode->case_map, case_value)) {
- onyx_report_error(pos, "Multiple cases for values '%d'.", case_value);
- return 1;
- }
-
- bh_imap_put(&switchnode->case_map, case_value, (u64) block);
- return 0;
-}
-
-CheckStatus check_switch(AstSwitch* switchnode) {
- if (switchnode->initialization != NULL) CHECK(statement_chain, &switchnode->initialization);
-
- CHECK(expression, &switchnode->expr);
- Type* resolved_expr_type = resolve_expression_type(switchnode->expr);
- if (!type_is_integer(switchnode->expr->type) && switchnode->expr->type->kind != Type_Kind_Enum) {
- ERROR(switchnode->expr->token->pos, "expected integer or enum type for switch expression");
- }
-
- // LEAK if this has to be yielded
- bh_imap_init(&switchnode->case_map, global_heap_allocator, bh_arr_length(switchnode->cases) * 2);
-
- switchnode->min_case = 0xffffffffffffffff;
-
- // Umm, this doesn't check the type of the case expression to the type of the expression
- bh_arr_each(AstSwitchCase, sc, switchnode->cases) {
- CHECK(block, sc->block);
-
- bh_arr_each(AstTyped *, value, sc->values) {
- CHECK(expression, value);
-
- if ((*value)->kind == Ast_Kind_Range_Literal) {
- AstRangeLiteral* rl = (AstRangeLiteral *) (*value);
- resolve_expression_type(rl->low);
- resolve_expression_type(rl->high);
-
- if (rl->low->kind != Ast_Kind_NumLit || rl->high->kind != Ast_Kind_NumLit) {
- ERROR(rl->token->pos, "case statement expected compile time known range.");
- }
-
- promote_numlit_to_larger((AstNumLit *) rl->low);
- promote_numlit_to_larger((AstNumLit *) rl->high);
-
- i64 lower = ((AstNumLit *) rl->low)->value.l;
- i64 upper = ((AstNumLit *) rl->high)->value.l;
-
- // NOTE: This is inclusive!!!!
- fori (case_value, lower, upper + 1) {
- if (add_case_to_switch_statement(switchnode, case_value, sc->block, rl->token->pos))
- return Check_Error;
- }
-
- continue;
- }
-
- if (!type_check_or_auto_cast(value, resolved_expr_type)) {
- OnyxToken* tkn = sc->block->token;
- if ((*value)->token) tkn = (*value)->token;
-
- ERROR_(tkn->pos, "Mismatched types in switch-case. Expected '%s', got '%s'.",
- type_get_name(resolved_expr_type), type_get_name((*value)->type));
- }
-
- if (node_is_type((AstNode*) (*value))) {
- Type* type = type_build_from_ast(context.ast_alloc, (AstType*) (*value));
-
- if (add_case_to_switch_statement(switchnode, type->id, sc->block, sc->block->token->pos))
- return Check_Error;
-
- continue;
- }
-
- if ((*value)->kind == Ast_Kind_Enum_Value) {
- (*value) = (AstTyped *) ((AstEnumValue *) (*value))->value;
- }
-
- if ((*value)->kind != Ast_Kind_NumLit) {
- ERROR((*value)->token->pos, "case statement expected compile time known integer");
- }
-
- resolve_expression_type((*value));
- // promote_numlit_to_larger((AstNumLit *) (*value));
-
- if (add_case_to_switch_statement(switchnode, ((AstNumLit *) (*value))->value.l, sc->block, sc->block->token->pos))
- return Check_Error;
- }
- }
-
- if (switchnode->default_case)
- CHECK(block, switchnode->default_case);
-
- return 0;
-}
-
-CheckStatus check_arguments(Arguments* args) {
- bh_arr_each(AstTyped *, actual, args->values)
- CHECK(expression, actual);
-
- bh_arr_each(AstNamedValue *, named_value, args->named_values)
- CHECK(expression, &(*named_value)->value);
-
- return Check_Success;
-}
-
-CheckStatus check_argument(AstArgument** parg) {
- CHECK(expression, &(*parg)->value);
- (*parg)->type = (*parg)->value->type;
-
- return Check_Success;
-}
-
-static i32 non_baked_argument_count(Arguments* args) {
- i32 count = 0;
-
- bh_arr_each(AstTyped *, actual, args->values) {
- assert((*actual)->kind == Ast_Kind_Argument);
- if (!((AstArgument *) (*actual))->is_baked) count++;
- }
-
- bh_arr_each(AstNamedValue *, named_value, args->named_values) {
- assert((*named_value)->value->kind == Ast_Kind_Argument);
- if (!((AstArgument *) (*named_value)->value)->is_baked) count++;
- }
-
- return count;
-}
-
-static CheckStatus check_resolve_callee(AstCall* call, AstTyped** effective_callee) {
- if (call->kind == Ast_Kind_Intrinsic_Call) return Check_Success;
-
- call->callee = (AstTyped *) strip_aliases((AstNode *) call->callee);
-
- AstTyped* callee = call->callee;
- b32 calling_a_macro = 0;
-
- if (callee->kind == Ast_Kind_Overloaded_Function) {
- b32 should_yield = 0;
- AstTyped* new_callee = find_matching_overload_by_arguments(
- ((AstOverloadedFunction *) callee)->overloads,
- &call->args,
- &should_yield);
-
- if (new_callee == NULL) {
- if (callee->entity->state > Entity_State_Check_Types && !should_yield) {
- report_unable_to_match_overload(call);
- return Check_Error;
-
- } else {
- YIELD(call->token->pos, "Waiting for overloaded function option to pass type-checking.");
- }
- }
-
- callee = new_callee;
- }
-
- if (callee->kind == Ast_Kind_Macro) {
- calling_a_macro = 1;
- call->callee = callee;
-
- AstTyped* new_callee = (AstTyped *) macro_resolve_header((AstMacro *) callee, &call->args, call->token);
- if (new_callee == NULL) return Check_Error;
- if (new_callee == (AstTyped *) &node_that_signals_a_yield) {
- YIELD(call->token->pos, "Waiting for macro header to pass type-checking.");
- }
-
- arguments_remove_baked(&call->args);
- callee = new_callee;
-
- } else if (callee->kind == Ast_Kind_Polymorphic_Proc) {
- AstTyped* new_callee = (AstTyped *) polymorphic_proc_lookup((AstPolyProc *) callee, PPLM_By_Arguments, &call->args, call->token);
- if (new_callee == NULL) return Check_Error;
- if (new_callee == (AstTyped *) &node_that_signals_a_yield) {
- YIELD(call->token->pos, "Waiting for polymorphic procedure header to pass type-checking.");
- }
-
- arguments_remove_baked(&call->args);
- callee = new_callee;
- }
-
- if (!calling_a_macro) call->callee = callee;
-
- // NOTE: Build callee's type
- fill_in_type((AstTyped *) callee);
- if (callee->type == NULL) {
- YIELD(call->token->pos, "Trying to resolve function type for callee.");
- }
-
- if (callee->type->kind != Type_Kind_Function) {
- ERROR_(call->token->pos,
- "Attempting to call something that is not a function, '%b'.",
- callee->token->text, callee->token->length);
- }
-
- *effective_callee = callee;
- return Check_Success;
-}
-
-typedef enum ArgState {
- AS_Expecting_Exact,
- AS_Expecting_Typed_VA,
- AS_Expecting_Untyped_VA,
-} ArgState;
-
-CheckStatus check_call(AstCall** pcall) {
- // All the things that need to be done when checking a call node.
- // 1. Ensure the callee is not a symbol
- // 2. Check the callee expression (since it could be a variable or a field access, etc)
- // 3. Check all arguments
- // * Cannot pass overloaded functions (ROBUSTNESS)
- // 4. If callee is an overloaded function, use the argument types to determine which overload is used.
- // 5. If callee is polymorphic, use the arguments type to generate a polymorphic function.
- // 7. Fill in arguments
- // 8. If callee is an intrinsic, turn call into an Intrinsic_Call node
- // 9. Check types of formal and actual params against each other, handling varargs
- AstCall* call = *pcall;
-
- if (call->flags & Ast_Flag_Has_Been_Checked) return Check_Success;
-
- u32 current_checking_level_store = current_checking_level;
- CHECK(expression, &call->callee);
- CHECK(arguments, &call->args);
- current_checking_level = current_checking_level_store;
-
- // SPEED CLEANUP: Keeping an original copy for basically no reason except that sometimes you
- // need to know the baked argument values in code generation.
- // This should only be done once, but right now it is being done everytime this is checked,
- // which can be multiple if we have to yield on a callee's type.
- arguments_clone(&call->original_args, &call->args);
-
- AstFunction* callee=NULL;
- CHECK(resolve_callee, call, (AstTyped **) &callee);
-
- // CLEANUP maybe make function_get_expected_arguments?
- i32 non_vararg_param_count = (i32) callee->type->Function.param_count;
- if (non_vararg_param_count > 0 &&
- callee->type->Function.params[callee->type->Function.param_count - 1] == builtin_vararg_type_type)
- non_vararg_param_count--;
-
- i32 arg_count = bh_max(non_vararg_param_count, non_baked_argument_count(&call->args));
- arguments_ensure_length(&call->args, arg_count);
-
- char* err_msg = NULL;
- fill_in_arguments(&call->args, (AstNode *) callee, &err_msg);
- if (err_msg != NULL) ERROR(call->token->pos, err_msg);
-
- bh_arr(AstArgument *) arg_arr = (bh_arr(AstArgument *)) call->args.values;
- bh_arr_each(AstArgument *, arg, arg_arr) {
- if (*arg != NULL) continue;
-
- ERROR(call->token->pos, "Not all arguments were given a value.");
- }
-
- // HACK HACK HACK
- // :CallSiteIsGross
- bh_arr_each(AstArgument *, arg, arg_arr) {
- AstTyped** arg_value = &(*arg)->value;
-
- if ((*arg_value)->kind == Ast_Kind_Call_Site) {
- AstCallSite* callsite = (AstCallSite *) ast_clone(context.ast_alloc, *arg_value);
- callsite->callsite_token = call->token;
-
- // HACK CLEANUP
- OnyxToken* str_token = bh_alloc(context.ast_alloc, sizeof(OnyxToken));
- str_token->text = bh_strdup(global_heap_allocator, (char *) call->token->pos.filename);
- str_token->length = strlen(call->token->pos.filename);
- str_token->pos = call->token->pos;
- str_token->type = Token_Type_Literal_String;
-
- AstStrLit* filename = bh_alloc_item(context.ast_alloc, AstStrLit);
- memset(filename, 0, sizeof(AstStrLit));
- filename->kind = Ast_Kind_StrLit;
- filename->token = str_token;
- filename->addr = 0;
-
- add_entities_for_node(NULL, (AstNode *) filename, NULL, NULL);
- callsite->filename = filename;
-
- callsite->line = make_int_literal(context.ast_alloc, call->token->pos.line);
- callsite->column = make_int_literal(context.ast_alloc, call->token->pos.column);
-
- convert_numlit_to_type(callsite->line, &basic_types[Basic_Kind_U32]);
- convert_numlit_to_type(callsite->column, &basic_types[Basic_Kind_U32]);
-
- *arg_value = (AstTyped *) callsite;
- }
- }
-
- // NOTE: If we are calling an intrinsic function, translate the
- // call into an intrinsic call node.
- if (callee->flags & Ast_Flag_Intrinsic) {
- call->kind = Ast_Kind_Intrinsic_Call;
- call->callee = NULL;
-
- token_toggle_end(callee->intrinsic_name);
- char* intr_name = callee->intrinsic_name->text;
-
- if (bh_table_has(OnyxIntrinsic, intrinsic_table, intr_name)) {
- call->intrinsic = bh_table_get(OnyxIntrinsic, intrinsic_table, intr_name);
-
- } else {
- onyx_report_error(callee->token->pos, "Intrinsic not supported, '%s'.", intr_name);
- token_toggle_end(callee->intrinsic_name);
- return Check_Error;
- }
-
- token_toggle_end(callee->intrinsic_name);
- }
-
- call->va_kind = VA_Kind_Not_VA;
- call->type = callee->type->Function.return_type;
- if (call->type == &type_auto_return && call->callee->kind != Ast_Kind_Macro) {
- YIELD(call->token->pos, "Waiting for auto-return type to be solved.");
- }
-
- Type **formal_params = callee->type->Function.params;
-
- Type* variadic_type = NULL;
- AstParam* variadic_param = NULL;
-
- // SPEED CLEANUP: Caching the any type here.
- Type* any_type = type_build_from_ast(context.ast_alloc, builtin_any_type);
-
- ArgState arg_state = AS_Expecting_Exact;
- u32 arg_pos = 0;
- while (1) {
- switch (arg_state) {
- case AS_Expecting_Exact: {
- if (arg_pos >= callee->type->Function.param_count) goto type_checking_done;
-
- if (formal_params[arg_pos]->kind == Type_Kind_VarArgs) {
- variadic_type = formal_params[arg_pos]->VarArgs.ptr_to_data->Pointer.elem;
- variadic_param = &callee->params[arg_pos];
- arg_state = AS_Expecting_Typed_VA;
- continue;
- }
-
- if ((i16) arg_pos == callee->type->Function.vararg_arg_pos) {
- arg_state = AS_Expecting_Untyped_VA;
- continue;
- }
-
- if (arg_pos >= (u32) bh_arr_length(arg_arr)) goto type_checking_done;
- if (!type_check_or_auto_cast(&arg_arr[arg_pos]->value, formal_params[arg_pos])) {
- ERROR_(arg_arr[arg_pos]->token->pos,
- "The procedure '%s' expects a value of type '%s' for %d%s parameter, got '%s'.",
- get_function_name(callee),
- type_get_name(formal_params[arg_pos]),
- arg_pos + 1,
- bh_num_suffix(arg_pos + 1),
- node_get_type_name(arg_arr[arg_pos]->value));
- }
-
- arg_arr[arg_pos]->va_kind = VA_Kind_Not_VA;
- break;
- }
-
- case AS_Expecting_Typed_VA: {
- if (arg_pos >= (u32) bh_arr_length(arg_arr)) goto type_checking_done;
-
- if (variadic_type->id == any_type->id) {
- call->va_kind = VA_Kind_Any;
-
- resolve_expression_type(arg_arr[arg_pos]->value);
- arg_arr[arg_pos]->va_kind = VA_Kind_Any;
- break;
- }
-
- call->va_kind = VA_Kind_Typed;
-
- if (!type_check_or_auto_cast(&arg_arr[arg_pos]->value, variadic_type)) {
- onyx_report_error(arg_arr[arg_pos]->token->pos,
- "The procedure '%s' expects a value of type '%s' for the variadic parameter, '%b', got '%s'.",
- get_function_name(callee),
- type_get_name(variadic_type),
- variadic_param->local->token->text,
- variadic_param->local->token->length,
- node_get_type_name(arg_arr[arg_pos]->value));
- return Check_Error;
- }
-
- arg_arr[arg_pos]->va_kind = VA_Kind_Typed;
- break;
- }
-
- case AS_Expecting_Untyped_VA: {
- call->va_kind = VA_Kind_Untyped;
-
- if (arg_pos >= (u32) bh_arr_length(arg_arr)) goto type_checking_done;
-
- resolve_expression_type(arg_arr[arg_pos]->value);
- if (arg_arr[arg_pos]->value->type == NULL) {
- ERROR(arg_arr[arg_pos]->token->pos, "Unable to resolve type for argument.");
- }
-
- arg_arr[arg_pos]->va_kind = VA_Kind_Untyped;
- break;
- }
- }
-
- arg_pos++;
- }
-
-type_checking_done:
-
- call->flags |= Ast_Flag_Has_Been_Checked;
-
- if (arg_pos < callee->type->Function.needed_param_count)
- ERROR(call->token->pos, "Too few arguments to function call.");
-
- if (arg_pos < (u32) arg_count)
- ERROR(call->token->pos, "Too many arguments to function call.");
-
- callee->flags |= Ast_Flag_Function_Used;
-
- if (call->kind == Ast_Kind_Call && call->callee->kind == Ast_Kind_Macro) {
- expand_macro(pcall, callee);
- return Check_Return_To_Symres;
- }
-
- return Check_Success;
-}
-
-static void report_bad_binaryop(AstBinaryOp* binop) {
- onyx_report_error(binop->token->pos, "Binary operator '%s' not understood for arguments of type '%s' and '%s'.",
- binaryop_string[binop->operation],
- node_get_type_name(binop->left),
- node_get_type_name(binop->right));
-}
-
-static AstCall* binaryop_try_operator_overload(AstBinaryOp* binop) {
- if (bh_arr_length(operator_overloads[binop->operation]) == 0) return NULL;
-
- Arguments args = ((Arguments) { NULL, NULL });
- bh_arr_new(global_heap_allocator, args.values, 2);
- bh_arr_push(args.values, binop->left);
- bh_arr_push(args.values, binop->right);
-
- if (binop_is_assignment(binop->operation)) {
- args.values[0] = (AstTyped *) make_address_of(context.ast_alloc, binop->left);
-
- u32 current_checking_level_store = current_checking_level;
- CheckStatus cs = check_address_of((AstAddressOf *) args.values[0]);
- current_checking_level = current_checking_level_store;
-
- if (cs == Check_Yield_Macro) return (AstCall *) &node_that_signals_a_yield;
- if (cs == Check_Error) {
- return NULL;
- }
- }
-
- b32 should_yield = 0;
- AstTyped* overload = find_matching_overload_by_arguments(operator_overloads[binop->operation], &args, &should_yield);
- if (should_yield) {
- bh_arr_free(args.values);
- return (AstCall *) &node_that_signals_a_yield;
- }
-
- if (overload == NULL) {
- bh_arr_free(args.values);
- return NULL;
- }
-
- AstCall* implicit_call = onyx_ast_node_new(context.ast_alloc, sizeof(AstCall), Ast_Kind_Call);
- implicit_call->token = binop->token;
- implicit_call->callee = overload;
- implicit_call->va_kind = VA_Kind_Not_VA;
-
- bh_arr_each(AstTyped *, arg, args.values)
- *arg = (AstTyped *) make_argument(context.ast_alloc, *arg);
-
- implicit_call->args = args;
- return implicit_call;
-}
-
-
-CheckStatus check_binaryop_assignment(AstBinaryOp** pbinop) {
- AstBinaryOp* binop = *pbinop;
- if (current_checking_level == EXPRESSION_LEVEL)
- ERROR(binop->token->pos, "Assignment not valid in expression.");
-
- if (!is_lval((AstNode *) binop->left))
- ERROR_(binop->left->token->pos,
- "Cannot assign to '%b'.",
- binop->left->token->text, binop->left->token->length);
-
- if ((binop->left->flags & Ast_Flag_Const) != 0 && binop->left->type != NULL)
- ERROR_(binop->token->pos,
- "Cannot assign to constant '%b.'.",
- binop->left->token->text, binop->left->token->length);
-
- if (binop->operation == Binary_Op_Assign) {
- // NOTE: Raw assignment
-
- // NOTE: This is the 'type inference' system. Very stupid, but very easy.
- // If a left operand has an unknown type, fill it in with the type of
- // the right hand side.
- if (binop->left->type == NULL) {
- resolve_expression_type(binop->right);
-
- if (binop->right->type == NULL) {
- if (binop->right->entity == NULL || binop->right->entity->state > Entity_State_Check_Types) {
- ERROR(binop->token->pos, "Could not resolve type of right hand side to infer.");
-
- } else {
- YIELD(binop->token->pos, "Trying to resolve try of right hand side.");
- }
- }
-
- if (binop->right->type->kind == Type_Kind_Compound) {
- AstCompound* lhs = (AstCompound *) binop->left;
- if (lhs->kind != Ast_Kind_Compound) {
- ERROR_(binop->token->pos,
- "Expected left hand side to have %d expressions.",
- binop->right->type->Compound.count);
- }
-
- i32 expr_count = binop->right->type->Compound.count;
- if (bh_arr_length(lhs->exprs) != expr_count) {
- ERROR_(binop->token->pos,
- "Expected left hand side to have %d expressions.",
- binop->right->type->Compound.count);
- }
-
- fori (i, 0, expr_count) {
- lhs->exprs[i]->type = binop->right->type->Compound.types[i];
- }
-
- lhs->type = type_build_compound_type(context.ast_alloc, lhs);
-
- } else {
- binop->left->type = binop->right->type;
- }
- }
-
- } else {
- // NOTE: +=, -=, ...
-
- BinaryOp operation = -1;
- if (binop->operation == Binary_Op_Assign_Add) operation = Binary_Op_Add;
- else if (binop->operation == Binary_Op_Assign_Minus) operation = Binary_Op_Minus;
- else if (binop->operation == Binary_Op_Assign_Multiply) operation = Binary_Op_Multiply;
- else if (binop->operation == Binary_Op_Assign_Divide) operation = Binary_Op_Divide;
- else if (binop->operation == Binary_Op_Assign_Modulus) operation = Binary_Op_Modulus;
- else if (binop->operation == Binary_Op_Assign_And) operation = Binary_Op_And;
- else if (binop->operation == Binary_Op_Assign_Or) operation = Binary_Op_Or;
- else if (binop->operation == Binary_Op_Assign_Xor) operation = Binary_Op_Xor;
- else if (binop->operation == Binary_Op_Assign_Shl) operation = Binary_Op_Shl;
- else if (binop->operation == Binary_Op_Assign_Shr) operation = Binary_Op_Shr;
- else if (binop->operation == Binary_Op_Assign_Sar) operation = Binary_Op_Sar;
-
- AstBinaryOp* new_right = make_binary_op(context.ast_alloc, operation, binop->left, binop->right);
- binop->right = (AstTyped *) new_right;
- new_right->token = binop->token;
- binop->operation = Binary_Op_Assign;
-
- CHECK(binaryop, (AstBinaryOp **) &binop->right);
- }
-
- if (binop->right->type == NULL) {
- if (binop->right->entity != NULL && binop->right->entity->state <= Entity_State_Check_Types) {
- YIELD(binop->token->pos, "Trying to resolve type of right hand side.");
- }
- }
-
- if (!type_check_or_auto_cast(&binop->right, binop->left->type)) {
- ERROR_(binop->token->pos,
- "Cannot assign value of type '%s' to a '%s'.",
- node_get_type_name(binop->right),
- node_get_type_name(binop->left));
- }
-
- binop->type = &basic_types[Basic_Kind_Void];
-
- return Check_Success;
-}
-
-static b32 binary_op_is_allowed(BinaryOp operation, Type* type) {
- static const u8 binop_allowed[Binary_Op_Count] = {
- /* Add */ Basic_Flag_Numeric | Basic_Flag_Pointer,
- /* Minus */ Basic_Flag_Numeric | Basic_Flag_Pointer,
- /* Multiply */ Basic_Flag_Numeric,
- /* Divide */ Basic_Flag_Numeric,
- /* Modulus */ Basic_Flag_Integer,
-
- /* Equal */ Basic_Flag_Equality,
- /* Not_Equal */ Basic_Flag_Equality,
- /* Less */ Basic_Flag_Ordered,
- /* Less_Equal */ Basic_Flag_Ordered,
- /* Greater */ Basic_Flag_Ordered,
- /* Greater_Equal */ Basic_Flag_Ordered,
-
- /* And */ Basic_Flag_Integer,
- /* Or */ Basic_Flag_Integer,
- /* Xor */ Basic_Flag_Integer,
- /* Shl */ Basic_Flag_Integer,
- /* Shr */ Basic_Flag_Integer,
- /* Sar */ Basic_Flag_Integer,
-
- /* Bool_And */ Basic_Flag_Boolean,
- /* Bool_Or */ Basic_Flag_Boolean,
-
- /* Assign_Start */ 0,
- /* Assign */ 0,
- /* Assign_Add */ 0,
- /* Assign_Minus */ 0,
- /* Assign_Multiply */ 0,
- /* Assign_Divide */ 0,
- /* Assign_Modulus */ 0,
- /* Assign_And */ 0,
- /* Assign_Or */ 0,
- /* Assign_Xor */ 0,
- /* Assign_Shl */ 0,
- /* Assign_Shr */ 0,
- /* Assign_Sar */ 0,
- /* Assign_End */ 0,
-
- /* Pipe */ 0,
- /* Range */ 0,
- };
-
- enum BasicFlag effective_flags = 0;
- switch (type->kind) {
- case Type_Kind_Basic: effective_flags = type->Basic.flags; break;
- case Type_Kind_Pointer: effective_flags = Basic_Flag_Pointer; break;
- case Type_Kind_Enum: effective_flags = Basic_Flag_Integer; break;
- case Type_Kind_Function: effective_flags = Basic_Flag_Equality; break;
- }
-
- return (binop_allowed[operation] & effective_flags) != 0;
-}
-
-CheckStatus check_binaryop_compare(AstBinaryOp** pbinop) {
- AstBinaryOp* binop = *pbinop;
-
- // HACK: Since ^... to rawptr is a one way conversion, strip any pointers
- // away so they can be compared as expected
- Type* ltype = binop->left->type;
- Type* rtype = binop->right->type;
-
- if (ltype->kind == Type_Kind_Pointer) ltype = &basic_types[Basic_Kind_Rawptr];
- if (rtype->kind == Type_Kind_Pointer) rtype = &basic_types[Basic_Kind_Rawptr];
-
- if (!types_are_compatible(ltype, rtype)) {
- b32 left_ac = node_is_auto_cast((AstNode *) binop->left);
- b32 right_ac = node_is_auto_cast((AstNode *) binop->right);
-
- if (left_ac && right_ac) ERROR(binop->token->pos, "Cannot have auto cast on both sides of binary operator.");
- else if (type_check_or_auto_cast(&binop->left, rtype));
- else if (type_check_or_auto_cast(&binop->right, ltype));
- else {
- ERROR_(binop->token->pos,
- "Cannot compare '%s' to '%s'.",
- type_get_name(ltype),
- type_get_name(rtype));
- }
- }
-
- if (!binary_op_is_allowed(binop->operation, binop->left->type)) {
- report_bad_binaryop(binop);
- return Check_Error;
- }
-
- binop->type = &basic_types[Basic_Kind_Bool];
- if (binop->flags & Ast_Flag_Comptime) {
- // NOTE: Not a binary op
- *pbinop = (AstBinaryOp *) ast_reduce(context.ast_alloc, (AstTyped *) binop);
- }
-
- return Check_Success;
-}
-
-CheckStatus check_binaryop_bool(AstBinaryOp** pbinop) {
- AstBinaryOp* binop = *pbinop;
-
- if (!type_is_bool(binop->left->type) || !type_is_bool(binop->right->type)) {
- report_bad_binaryop(binop);
- return Check_Error;
- }
-
- binop->type = &basic_types[Basic_Kind_Bool];
-
- if (binop->flags & Ast_Flag_Comptime) {
- // NOTE: Not a binary op
- *pbinop = (AstBinaryOp *) ast_reduce(context.ast_alloc, (AstTyped *) binop);
- }
- return Check_Success;
-}
-
-CheckStatus check_binaryop(AstBinaryOp** pbinop) {
- AstBinaryOp* binop = *pbinop;
-
- if (binop->flags & Ast_Flag_Has_Been_Checked) return Check_Success;
-
- u32 current_checking_level_store = current_checking_level;
- CHECK(expression, &binop->left);
- CHECK(expression, &binop->right);
- current_checking_level = current_checking_level_store;
-
- if ((binop->left->flags & Ast_Flag_Comptime) && (binop->right->flags & Ast_Flag_Comptime)) {
- binop->flags |= Ast_Flag_Comptime;
- }
-
- if (expression_types_must_be_known) {
- if (binop->left->type == NULL || binop->right->type == NULL) {
- ERROR(binop->token->pos, "Internal compiler error: one of the operands types is unknown here.");
- }
- }
-
- // :UnaryFieldAccessIsGross
- if (binop->left->kind == Ast_Kind_Unary_Field_Access || binop->right->kind == Ast_Kind_Unary_Field_Access) {
- if (type_check_or_auto_cast(&binop->left, binop->right->type));
- else if (type_check_or_auto_cast(&binop->right, binop->left->type));
- else {
- report_bad_binaryop(binop);
- return Check_Error;
- }
- }
-
- // NOTE: Try operator overloading before checking everything else.
- if ((binop->left->type != NULL && binop->right->type != NULL) &&
- (binop->left->type->kind != Type_Kind_Basic || binop->right->type->kind != Type_Kind_Basic)) {
- AstCall *implicit_call = binaryop_try_operator_overload(binop);
-
- if (implicit_call == (AstCall *) &node_that_signals_a_yield)
- YIELD(binop->token->pos, "Trying to resolve operator overload.");
-
- if (implicit_call != NULL) {
- // NOTE: Not a binary op
- implicit_call->next = binop->next;
- *pbinop = (AstBinaryOp *) implicit_call;
-
- CHECK(call, (AstCall **) pbinop);
- return Check_Success;
- }
- }
-
- if (binop_is_assignment(binop->operation)) return check_binaryop_assignment(pbinop);
-
- // NOTE: Comparision operators and boolean operators are handled separately.
- if (binop_is_compare(binop->operation))
- return check_binaryop_compare(pbinop);
- if (binop->operation == Binary_Op_Bool_And || binop->operation == Binary_Op_Bool_Or)
- return check_binaryop_bool(pbinop);
-
- // NOTE: The left side cannot be compound.
- // The right side always is numeric.
- // The left side cannot be rawptr.
- if (type_is_compound(binop->left->type)) goto bad_binaryop;
- if (!type_is_numeric(binop->right->type)) goto bad_binaryop;
- if (type_is_rawptr(binop->left->type)) {
- ERROR(binop->token->pos, "Cannot operate on a 'rawptr'. Cast it to a another pointer type first.");
- }
-
- // NOTE: Handle basic pointer math.
- if (type_is_pointer(binop->left->type)) {
- if (binop->operation != Binary_Op_Add && binop->operation != Binary_Op_Minus) goto bad_binaryop;
-
- resolve_expression_type(binop->right);
- if (!type_is_integer(binop->right->type)) goto bad_binaryop;
-
- AstNumLit* numlit = make_int_literal(context.ast_alloc, type_size_of(binop->left->type->Pointer.elem));
- numlit->token = binop->right->token;
- numlit->type = binop->right->type;
-
- AstBinaryOp* binop_node = make_binary_op(context.ast_alloc, Binary_Op_Multiply, binop->right, (AstTyped *) numlit);
- binop_node->token = binop->token;
- CHECK(binaryop, &binop_node);
-
- binop->right = (AstTyped *) binop_node;
- binop->type = binop->left->type;
- binop->right->type = binop->left->type;
- }
-
- if (!types_are_compatible(binop->left->type, binop->right->type)) {
- b32 left_ac = node_is_auto_cast((AstNode *) binop->left);
- b32 right_ac = node_is_auto_cast((AstNode *) binop->right);
-
- if (left_ac && right_ac) {
- ERROR(binop->token->pos, "Cannot have auto cast on both sides of binary operator.");
- }
- else if (type_check_or_auto_cast(&binop->left, binop->right->type));
- else if (type_check_or_auto_cast(&binop->right, binop->left->type));
- else {
- ERROR_(binop->token->pos,
- "Mismatched types for binary operation '%s'. left: '%s', right: '%s'.",
- binaryop_string[binop->operation],
- node_get_type_name(binop->left),
- node_get_type_name(binop->right));
- }
- }
-
- binop->type = binop->left->type;
- if (!binary_op_is_allowed(binop->operation, binop->type)) goto bad_binaryop;
-
- // NOTE: Enum flags with '&' result in a boolean value
- if (binop->type->kind == Type_Kind_Enum && binop->type->Enum.is_flags && binop->operation == Binary_Op_And) {
- binop->type = &basic_types[Basic_Kind_Bool];
- }
-
- binop->flags |= Ast_Flag_Has_Been_Checked;
-
- if (binop->flags & Ast_Flag_Comptime) {
- // NOTE: Not a binary op
- *pbinop = (AstBinaryOp *) ast_reduce(context.ast_alloc, (AstTyped *) binop);
- }
- return Check_Success;
-
-bad_binaryop:
- report_bad_binaryop(binop);
-
- return Check_Error;
-}
-
-CheckStatus check_unaryop(AstUnaryOp** punop) {
- AstUnaryOp* unaryop = *punop;
-
- CHECK(expression, &unaryop->expr);
-
- if (unaryop->operation != Unary_Op_Negate) {
- resolve_expression_type(unaryop->expr);
- }
-
- if (unaryop->operation == Unary_Op_Cast) {
- char* err;
- if (unaryop->type == NULL)
- YIELD(unaryop->token->pos, "Trying to resolve destination type for cast.");
-
- if (!cast_is_legal(unaryop->expr->type, unaryop->type, &err)) {
- ERROR_(unaryop->token->pos, "Cast Error: %s", err);
- }
-
- } else {
- unaryop->type = unaryop->expr->type;
- }
-
- if (unaryop->operation == Unary_Op_Not) {
- if (!type_is_bool(unaryop->expr->type)) {
- ERROR_(unaryop->token->pos,
- "Bool negation operator expected bool type, got '%s'.",
- node_get_type_name(unaryop->expr));
- }
- }
-
- if (unaryop->operation == Unary_Op_Bitwise_Not) {
- if (!type_is_integer(unaryop->expr->type)) {
- ERROR_(unaryop->token->pos,
- "Bitwise operator expected integer type, got '%s'.",
- node_get_type_name(unaryop->expr));
- }
- }
-
- if (unaryop->expr->flags & Ast_Flag_Comptime) {
- unaryop->flags |= Ast_Flag_Comptime;
- // NOTE: Not a unary op
- *punop = (AstUnaryOp *) ast_reduce(context.ast_alloc, (AstTyped *) unaryop);
- }
-
- return Check_Success;
-}
-
-CheckStatus check_struct_literal(AstStructLiteral* sl) {
- if (sl->type == NULL) {
- // NOTE: This is used for automatically typed struct literals. If there is no provided
- // type for the struct literal, assume that it is passes successfully. When it is used
- // elsewhere, it will be added as an expression entity that will be processed once the
- // stnode is filled out.
- if (sl->stnode == NULL) return Check_Success;
-
- if (!node_is_type((AstNode *) sl->stnode)) {
- ERROR(sl->token->pos, "Type used for struct literal is not a type.");
- }
-
- fill_in_type((AstTyped *) sl);
- if (sl->type == NULL)
- YIELD(sl->token->pos, "Trying to resolve type of struct literal.");
- }
-
- if (!type_is_structlike_strict(sl->type)) {
- ERROR_(sl->token->pos,
- "'%s' is not constructable using a struct literal.",
- type_get_name(sl->type));
- }
-
- i32 mem_count = type_structlike_mem_count(sl->type);
- arguments_ensure_length(&sl->args, mem_count);
-
- // :Idempotency
- if ((sl->flags & Ast_Flag_Has_Been_Checked) == 0) {
- char* err_msg = NULL;
- if (!fill_in_arguments(&sl->args, (AstNode *) sl, &err_msg)) {
- onyx_report_error(sl->token->pos, err_msg);
-
- bh_arr_each(AstTyped *, value, sl->args.values) {
- if (*value == NULL) {
- i32 member_idx = value - sl->args.values; // Pointer subtraction hack
- StructMember smem;
- type_lookup_member_by_idx(sl->type, member_idx, &smem);
-
- onyx_report_error(sl->token->pos,
- "Value not given for %d%s member, '%s', for type '%s'.",
- member_idx + 1, bh_num_suffix(member_idx + 1),
- smem.name, type_get_name(sl->type));
- }
- }
-
- return Check_Error;
- }
- }
- sl->flags |= Ast_Flag_Has_Been_Checked;
-
- AstTyped** actual = sl->args.values;
- StructMember smem;
-
- // BUG: There are problems setting the comptime flag this late in the checking because
- // if the struct literal was type inferred, then the literal won't be correctly determined
- // to be comptime on the first pass, which is needed for top level expressions.
- sl->flags |= Ast_Flag_Comptime;
-
- fori (i, 0, mem_count) {
- // NOTE: Not checking the return on this function because
- // this for loop is bounded by the number of members in the
- // type.
- type_lookup_member_by_idx(sl->type, i, &smem);
- Type* formal = smem.type;
-
- CHECK(expression, actual);
-
- // HACK HACK HACK
- if ((*actual)->type == NULL &&
- (*actual)->entity != NULL &&
- (*actual)->entity->state <= Entity_State_Check_Types) {
- YIELD_((*actual)->token->pos, "Trying to resolve type of expression for member '%s'.", smem.name);
- }
-
- if (!type_check_or_auto_cast(actual, formal)) {
- ERROR_(sl->token->pos,
- "Mismatched types for %d%s member named '%s', expected '%s', got '%s'.",
- i + 1, bh_num_suffix(i + 1),
- smem.name,
- type_get_name(formal),
- node_get_type_name(*actual));
- }
-
- sl->flags &= ((*actual)->flags & Ast_Flag_Comptime) | (sl->flags &~ Ast_Flag_Comptime);
- actual++;
- }
-
- return Check_Success;
-}
-
-CheckStatus check_array_literal(AstArrayLiteral* al) {
- // :Idempotency
- if ((al->flags & Ast_Flag_Array_Literal_Typed) == 0) {
- if (al->atnode == NULL)
- YIELD(al->token->pos, "Waiting for array literal type to be known.");
-
- if (!node_is_type((AstNode *) al->atnode))
- ERROR(al->token->pos, "Array type is not a type.");
-
- fill_in_type((AstTyped *) al);
- if (al->type == NULL)
- YIELD(al->token->pos, "Trying to resolve type of array literal.");
-
- al->type = type_make_array(context.ast_alloc, al->type, bh_arr_length(al->values));
- if (al->type == NULL || al->type->kind != Type_Kind_Array)
- ERROR(al->token->pos, "Expected array type for array literal. This is a compiler bug.");
-
- al->flags |= Ast_Flag_Array_Literal_Typed;
- }
-
- if (al->type->Array.count != (u32) bh_arr_length(al->values)) {
- ERROR_(al->token->pos, "Wrong array size (%d) for number of values (%d).",
- al->type->Array.count, bh_arr_length(al->values));
- }
-
- al->flags |= Ast_Flag_Comptime;
-
- Type* elem_type = al->type->Array.elem;
- bh_arr_each(AstTyped *, expr, al->values) {
- CHECK(expression, expr);
-
- // HACK HACK HACK
- if ((*expr)->type == NULL &&
- (*expr)->entity != NULL &&
- (*expr)->entity->state <= Entity_State_Check_Types) {
- YIELD_(al->token->pos, "Trying to resolve type of %d%s element of array literal.", expr - al->values, bh_num_suffix(expr - al->values));
- }
-
- al->flags &= ((*expr)->flags & Ast_Flag_Comptime) | (al->flags &~ Ast_Flag_Comptime);
-
- if (!type_check_or_auto_cast(expr, elem_type)) {
- ERROR_((*expr)->token->pos, "Mismatched types for value of in array, expected '%s', got '%s'.",
- type_get_name(elem_type),
- node_get_type_name(*expr));
- }
- }
-
- return Check_Success;
-}
-
-CheckStatus check_range_literal(AstRangeLiteral** prange) {
- AstRangeLiteral* range = *prange;
- CHECK(expression, &range->low);
- CHECK(expression, &range->high);
-
- Type* expected_range_type = builtin_range_type_type;
- StructMember smem;
-
- type_lookup_member(expected_range_type, "low", &smem);
- if (!type_check_or_auto_cast(&range->low, smem.type)) {
- ERROR_(range->token->pos,
- "Expected left side of range to be a 32-bit integer, got '%s'.",
- node_get_type_name(range->low));
- }
-
- type_lookup_member(expected_range_type, "high", &smem);
- if (!type_check_or_auto_cast(&range->high, smem.type)) {
- ERROR_(range->token->pos,
- "Expected right side of range to be a 32-bit integer, got '%s'.",
- node_get_type_name(range->high));
- }
-
- if (range->step == NULL) {
- type_lookup_member(expected_range_type, "step", &smem);
- assert(smem.initial_value != NULL);
- CHECK(expression, smem.initial_value);
-
- range->step = *smem.initial_value;
- }
-
- return Check_Success;
-}
-
-CheckStatus check_compound(AstCompound* compound) {
- bh_arr_each(AstTyped *, expr, compound->exprs) {
- CHECK(expression, expr);
- }
-
- compound->type = type_build_compound_type(context.ast_alloc, compound);
- return Check_Success;
-}
-
-CheckStatus check_if_expression(AstIfExpression* if_expr) {
- CHECK(expression, &if_expr->cond);
- CHECK(expression, &if_expr->true_expr);
- CHECK(expression, &if_expr->false_expr);
-
- if (!type_check_or_auto_cast(&if_expr->cond, &basic_types[Basic_Kind_Bool])) {
- ERROR_(if_expr->token->pos, "If-expression expected boolean for condition, got '%s'.",
- type_get_name(if_expr->cond->type));
- }
-
- resolve_expression_type((AstTyped *) if_expr);
-
- if (!types_are_compatible(if_expr->true_expr->type, if_expr->false_expr->type)) {
- ERROR_(if_expr->token->pos, "Mismatched types for if-expression, left side is '%s', and right side is '%s'.",
- type_get_name(if_expr->true_expr->type), type_get_name(if_expr->false_expr->type));
- }
-
- return Check_Success;
-}
-
-CheckStatus check_do_block(AstDoBlock** pdoblock) {
- AstDoBlock* doblock = *pdoblock;
- if (doblock->flags & Ast_Flag_Has_Been_Checked) return Check_Success;
-
- fill_in_type((AstTyped *) doblock);
-
- Type** old_expected_return_type = expected_return_type;
- expected_return_type = &doblock->type;
-
- doblock->block->rules = Block_Rule_Do_Block;
- CHECK(block, doblock->block);
-
- if (doblock->type == &type_auto_return) {
- // ERROR(doblock->token->pos, "Unable to determine type of do-block expression.");
- doblock->type = &basic_types[Basic_Kind_Void];
- }
-
- expected_return_type = old_expected_return_type;
- doblock->flags |= Ast_Flag_Has_Been_Checked;
- return Check_Success;
-}
-
-CheckStatus check_address_of(AstAddressOf* aof) {
- CHECK(expression, &aof->expr);
- if (aof->expr->type == NULL) {
- YIELD(aof->token->pos, "Trying to resolve type of expression to take a reference.");
- }
-
- if ((aof->expr->kind != Ast_Kind_Subscript
- && aof->expr->kind != Ast_Kind_Dereference
- && aof->expr->kind != Ast_Kind_Field_Access
- && aof->expr->kind != Ast_Kind_Memres
- && aof->expr->kind != Ast_Kind_Local)
- || (aof->expr->flags & Ast_Flag_Cannot_Take_Addr) != 0) {
- ERROR(aof->token->pos, "Cannot take the address of something that is not an l-value.");
- }
-
- aof->expr->flags |= Ast_Flag_Address_Taken;
-
- aof->type = type_make_pointer(context.ast_alloc, aof->expr->type);
-
- return Check_Success;
-}
-
-CheckStatus check_dereference(AstDereference* deref) {
- CHECK(expression, &deref->expr);
-
- if (!type_is_pointer(deref->expr->type))
- ERROR(deref->token->pos, "Cannot dereference non-pointer value.");
-
- if (deref->expr->type == basic_type_rawptr.basic_type)
- ERROR(deref->token->pos, "Cannot dereference 'rawptr'. Cast to another pointer type first.");
-
- deref->type = deref->expr->type->Pointer.elem;
-
- return Check_Success;
-}
-
-CheckStatus check_subscript(AstSubscript** psub) {
- AstSubscript* sub = *psub;
- CHECK(expression, &sub->addr);
- CHECK(expression, &sub->expr);
-
- // NOTE: Try operator overloading before checking everything else.
- if ((sub->addr->type != NULL && sub->expr->type != NULL) &&
- (sub->addr->type->kind != Type_Kind_Basic || sub->expr->type->kind != Type_Kind_Basic)) {
- // AstSubscript is the same as AstBinaryOp for the first sizeof(AstBinaryOp) bytes
- AstBinaryOp* binop = (AstBinaryOp *) sub;
- AstCall *implicit_call = binaryop_try_operator_overload(binop);
-
- if (implicit_call == (AstCall *) &node_that_signals_a_yield)
- YIELD(sub->token->pos, "Trying to resolve operator overload.");
-
- if (implicit_call != NULL) {
- // NOTE: Not an array access
- implicit_call->next = sub->next;
- *psub = (AstSubscript *) implicit_call;
-
- CHECK(call, (AstCall **) psub);
- return Check_Success;
- }
- }
-
- if (!type_is_array_accessible(sub->addr->type)) {
- report_bad_binaryop((AstBinaryOp *) sub);
- return Check_Error;
- }
-
- if (types_are_compatible(sub->expr->type, builtin_range_type_type)) {
- Type *of = NULL;
- if (sub->addr->type->kind == Type_Kind_Pointer)
- of = sub->addr->type->Pointer.elem;
- else if (sub->addr->type->kind == Type_Kind_Array)
- of = sub->addr->type->Array.elem;
- else {
- // FIXME: Slice creation should be allowed for slice types and dynamic array types, like it
- // is below, but this code doesn't look at that.
- report_bad_binaryop((AstBinaryOp *) sub);
- ERROR(sub->token->pos, "Invalid type for left of slice creation.");
- }
-
- sub->kind = Ast_Kind_Slice;
- sub->type = type_make_slice(context.ast_alloc, of);
- sub->elem_size = type_size_of(of);
-
- return Check_Success;
- }
-
- resolve_expression_type(sub->expr);
- if (sub->expr->type->kind != Type_Kind_Basic
- || (sub->expr->type->Basic.kind != Basic_Kind_I32 && sub->expr->type->Basic.kind != Basic_Kind_U32)) {
- report_bad_binaryop((AstBinaryOp *) sub);
- ERROR_(sub->token->pos,
- "Expected type u32 or i32 for index, got '%s'.",
- node_get_type_name(sub->expr));
- }
-
- if (sub->addr->type->kind == Type_Kind_Pointer)
- sub->type = sub->addr->type->Pointer.elem;
- else if (sub->addr->type->kind == Type_Kind_Array)
- sub->type = sub->addr->type->Array.elem;
- else if (sub->addr->type->kind == Type_Kind_Slice
- || sub->addr->type->kind == Type_Kind_DynArray
- || sub->addr->type->kind == Type_Kind_VarArgs) {
- // If we are accessing on a slice or a dynamic array, implicitly add a field access for the data member
- StructMember smem;
- type_lookup_member(sub->addr->type, "data", &smem);
-
- AstFieldAccess* fa = make_field_access(context.ast_alloc, sub->addr, "data");
- fa->type = smem.type;
- fa->offset = smem.offset;
- fa->idx = smem.idx;
-
- sub->addr = (AstTyped *) fa;
- sub->type = sub->addr->type->Pointer.elem;
- }
- else {
- report_bad_binaryop((AstBinaryOp *) sub);
- ERROR(sub->token->pos, "Invalid type for left of array access.");
- }
-
- sub->elem_size = type_size_of(sub->type);
-
- return Check_Success;
-}
-
-CheckStatus check_field_access(AstFieldAccess** pfield) {
- AstFieldAccess* field = *pfield;
- CHECK(expression, &field->expr);
- if (field->expr->type == NULL) {
- // onyx_report_error(field->token->pos, "Unable to deduce type of expression for accessing field.");
- YIELD(field->token->pos, "Trying to resolve type of source expression.");
- }
-
- if (!type_is_structlike(field->expr->type)) {
- ERROR_(field->token->pos,
- "Cannot access field '%b' on '%s'. Type is not a struct.",
- field->token->text,
- field->token->length,
- node_get_type_name(field->expr));
- }
-
- // Optimization for (*foo).member.
- if (field->expr->kind == Ast_Kind_Dereference) {
- field->expr = ((AstDereference *) field->expr)->expr;
- }
-
- StructMember smem;
- if (field->token != NULL && field->field == NULL) {
- token_toggle_end(field->token);
- // CLEANUP: Duplicating the string here isn't the best for effiency,
- // but it fixes a lot of bugs, so here we are.
- // - brendanfh 2020/12/08
- field->field = bh_strdup(context.ast_alloc, field->token->text);
- token_toggle_end(field->token);
- }
-
- if (!type_lookup_member(field->expr->type, field->field, &smem)) {
- AstType* type_node = field->expr->type->ast_type;
- AstNode* n = try_symbol_raw_resolve_from_node((AstNode *) type_node, field->field);
- if (n) {
- *pfield = (AstFieldAccess *) n;
- return Check_Success;
- }
-
- ERROR_(field->token->pos,
- "Field '%s' does not exists on '%s'.",
- field->field,
- node_get_type_name(field->expr));
- }
-
- field->offset = smem.offset;
- field->idx = smem.idx;
- field->type = smem.type;
-
- return Check_Success;
-}
-
-CheckStatus check_method_call(AstBinaryOp** mcall) {
- CHECK(expression, &(*mcall)->left);
- if ((*mcall)->left->type == NULL) {
- YIELD((*mcall)->token->pos, "Trying to resolve type of left hand side.");
- }
-
- AstTyped* implicit_argument = (*mcall)->left;
-
- // Symbol resolution should have ensured that this is call node.
- AstCall* call_node = (AstCall *) (*mcall)->right;
- assert(call_node->kind == Ast_Kind_Call);
-
- // :Idempotency
- if (((*mcall)->flags & Ast_Flag_Has_Been_Checked) == 0) {
- // Implicitly take the address of the value if it is not already a pointer type.
- // This could be weird to think about semantically so some testing with real code
- // would be good. - brendanfh 2020/02/05
- if (implicit_argument->type->kind != Type_Kind_Pointer)
- implicit_argument = (AstTyped *) make_address_of(context.ast_alloc, implicit_argument);
-
- implicit_argument = (AstTyped *) make_argument(context.ast_alloc, implicit_argument);
-
- bh_arr_insertn(call_node->args.values, 0, 1);
- call_node->args.values[0] = implicit_argument;
- }
- (*mcall)->flags |= Ast_Flag_Has_Been_Checked;
-
- CHECK(call, &call_node);
- call_node->next = (*mcall)->next;
-
- *mcall = (AstBinaryOp *) call_node;
- return Check_Success;
-}
-
-CheckStatus check_size_of(AstSizeOf* so) {
- fill_in_array_count(so->so_ast_type);
- CHECK(type, so->so_ast_type);
-
- so->so_type = type_build_from_ast(context.ast_alloc, so->so_ast_type);
- if (so->so_type == NULL)
- YIELD(so->token->pos, "Trying to resolve type to take the size of.");
-
- so->size = type_size_of(so->so_type);
-
- return Check_Success;
-}
-
-CheckStatus check_align_of(AstAlignOf* ao) {
- fill_in_array_count(ao->ao_ast_type);
- CHECK(type, ao->ao_ast_type);
-
- ao->ao_type = type_build_from_ast(context.ast_alloc, ao->ao_ast_type);
- if (ao->ao_type == NULL)
- YIELD(ao->token->pos, "Trying to resolve type to take the alignment of.");
-
- ao->alignment = type_alignment_of(ao->ao_type);
-
- return Check_Success;
-}
-
-CheckStatus check_expression(AstTyped** pexpr) {
- AstTyped* expr = *pexpr;
- if (expr->kind > Ast_Kind_Type_Start && expr->kind < Ast_Kind_Type_End) {
- // This is to ensure that the type will exist when compiling. For example, a poly-call type
- // would have to wait for the entity to pass through, which the code generation does not know
- // about.
- if (expr->kind == Ast_Kind_Typeof) {
- CHECK(type, (AstType *) expr);
- }
-
- // Don't try to construct a polystruct ahead of time because you can't.
- if (expr->kind != Ast_Kind_Poly_Struct_Type) {
- if (type_build_from_ast(context.ast_alloc, (AstType*) expr) == NULL) {
- YIELD(expr->token->pos, "Trying to construct type.");
- }
- }
-
- expr->type = &basic_types[Basic_Kind_Type_Index];
- return Check_Success;
- }
-
- if (expr->kind == Ast_Kind_Polymorphic_Proc) {
- // polymorphic procedures do not need to be checked. Their concrete instantiations
- // will be checked when they are created.
- return Check_Success;
- }
-
- if (expr->kind == Ast_Kind_Macro) {
- return Check_Success;
- }
-
- fill_in_type(expr);
- current_checking_level = EXPRESSION_LEVEL;
-
- CheckStatus retval = Check_Success;
- switch (expr->kind) {
- case Ast_Kind_Binary_Op: retval = check_binaryop((AstBinaryOp **) pexpr); break;
- case Ast_Kind_Unary_Op: retval = check_unaryop((AstUnaryOp **) pexpr); break;
-
- case Ast_Kind_Call: retval = check_call((AstCall **) pexpr); break;
- case Ast_Kind_Argument: retval = check_argument((AstArgument **) pexpr); break;
- case Ast_Kind_Block: retval = check_block((AstBlock *) expr); break;
-
- case Ast_Kind_Symbol:
- onyx_report_error(expr->token->pos,
- "Symbol was unresolved in symbol resolution phase, '%b'. This is definitely a compiler bug.",
- expr->token->text, expr->token->length);
- retval = Check_Error;
- break;
-
- case Ast_Kind_Param:
- if (expr->type == NULL) {
- onyx_report_error(expr->token->pos, "Parameter with bad type.");
- retval = Check_Error;
- }
- break;
-
- case Ast_Kind_Local: break;
-
- case Ast_Kind_Address_Of: retval = check_address_of((AstAddressOf *) expr); break;
- case Ast_Kind_Dereference: retval = check_dereference((AstDereference *) expr); break;
- case Ast_Kind_Slice:
- case Ast_Kind_Subscript: retval = check_subscript((AstSubscript **) pexpr); break;
- case Ast_Kind_Field_Access: retval = check_field_access((AstFieldAccess **) pexpr); break;
- case Ast_Kind_Method_Call: retval = check_method_call((AstBinaryOp **) pexpr); break;
- case Ast_Kind_Size_Of: retval = check_size_of((AstSizeOf *) expr); break;
- case Ast_Kind_Align_Of: retval = check_align_of((AstAlignOf *) expr); break;
- case Ast_Kind_Range_Literal: retval = check_range_literal((AstRangeLiteral **) pexpr); break;
-
- case Ast_Kind_Global:
- if (expr->type == NULL) {
- onyx_report_error(expr->token->pos, "Global with unknown type.");
- retval = Check_Error;
- }
- break;
-
- case Ast_Kind_NumLit:
- assert(expr->type != NULL);
- break;
-
- case Ast_Kind_Struct_Literal:
- retval = check_struct_literal((AstStructLiteral *) expr);
- break;
-
- case Ast_Kind_Array_Literal:
- retval = check_array_literal((AstArrayLiteral *) expr);
- break;
-
- case Ast_Kind_Function:
- // NOTE: Will need something like this at some point
- // AstFunction* func = (AstFunction *) expr;
- // bh_arr_each(AstParam, param, func->params) {
- // if (param->default_value != NULL) {
- // onyx_message_add(Msg_Type_Literal,
- // func->token->pos,
- // "cannot use functions with default parameters in this way");
- // retval = 1;
- // break;
- // }
- // }
- if (expr->type == NULL)
- YIELD(expr->token->pos, "Waiting for function type to be resolved.");
-
- expr->flags |= Ast_Flag_Function_Used;
- break;
-
- case Ast_Kind_Directive_Solidify:
- *pexpr = (AstTyped *) ((AstDirectiveSolidify *) expr)->resolved_proc;
- break;
-
- case Ast_Kind_Directive_Defined:
- *pexpr = (AstTyped *) make_bool_literal(context.ast_alloc, ((AstDirectiveDefined *) expr)->is_defined);
- fill_in_type(*pexpr);
- break;
-
- case Ast_Kind_Compound:
- CHECK(compound, (AstCompound *) expr);
- break;
-
- case Ast_Kind_Call_Site:
- // NOTE: This has to be set here because if it were to be set in the parser,
- // builtin_callsite_type wouldn't be known when parsing the builtin.onyx file.
- expr->type_node = builtin_callsite_type;
- break;
-
- case Ast_Kind_If_Expression:
- CHECK(if_expression, (AstIfExpression *) expr);
- break;
-
- case Ast_Kind_Alias:
- CHECK(expression, &((AstAlias *) expr)->alias);
- expr->flags |= (((AstAlias *) expr)->alias->flags & Ast_Flag_Comptime);
- expr->type = ((AstAlias *) expr)->alias->type;
- break;
-
- case Ast_Kind_Directive_Insert:
- retval = check_insert_directive((AstDirectiveInsert **) pexpr);
- break;
-
- case Ast_Kind_Code_Block:
- expr->flags |= Ast_Flag_Comptime;
- fill_in_type(expr);
- break;
-
- case Ast_Kind_Do_Block:
- retval = check_do_block((AstDoBlock **) pexpr);
- break;
-
- case Ast_Kind_StrLit: break;
- case Ast_Kind_File_Contents: break;
- case Ast_Kind_Overloaded_Function: break;
- case Ast_Kind_Enum_Value: break;
- case Ast_Kind_Memres: break;
- case Ast_Kind_Polymorphic_Proc: break;
- case Ast_Kind_Package: break;
- case Ast_Kind_Error: break;
- case Ast_Kind_Unary_Field_Access: break;
-
- // NOTE: The only way to have an Intrinsic_Call node is to have gone through the
- // checking of a call node at least once.
- case Ast_Kind_Intrinsic_Call: break;
-
- default:
- retval = Check_Error;
- onyx_report_error(expr->token->pos, "UNEXPECTED INTERNAL COMPILER ERROR");
- DEBUG_HERE;
- break;
- }
-
- return retval;
-}
-
-CheckStatus check_global(AstGlobal* global) {
- fill_in_type((AstTyped *) global);
-
- if (global->type == NULL) {
- YIELD(global->token->pos, "Trying to resolve type for global.");
- }
-
- return Check_Success;
-}
-
-CheckStatus check_insert_directive(AstDirectiveInsert** pinsert) {
- AstDirectiveInsert* insert = *pinsert;
- if (insert->flags & Ast_Flag_Has_Been_Checked) return Check_Success;
-
- CHECK(expression, &insert->code_expr);
- if (insert->code_expr->type == NULL) {
- if (insert->code_expr->entity && insert->code_expr->entity->state >= Entity_State_Code_Gen) {
- ERROR(insert->token->pos, "Expected expression of type 'Code'.");
- }
-
- // Bad wording for the message.
- YIELD(insert->token->pos, "Waiting for resolution to code expression type.");
- }
-
- Type* code_type = type_build_from_ast(context.ast_alloc, builtin_code_type);
-
- if (!type_check_or_auto_cast(&insert->code_expr, code_type)) {
- ERROR_(insert->token->pos, "#insert expected a value of type 'Code', got '%s'.",
- type_get_name(insert->code_expr->type));
- }
-
- AstCodeBlock* code_block = (AstCodeBlock *) insert->code_expr;
- code_block = (AstCodeBlock *) strip_aliases((AstNode *) code_block);
-
- assert(code_block->kind == Ast_Kind_Code_Block);
-
- AstNode* cloned_block = ast_clone(context.ast_alloc, code_block->code);
- cloned_block->next = insert->next;
- *(AstNode **) pinsert = cloned_block;
-
- insert->flags |= Ast_Flag_Has_Been_Checked;
-
- return Check_Return_To_Symres;
-}
-
-CheckStatus check_statement(AstNode** pstmt) {
- AstNode* stmt = *pstmt;
-
- current_checking_level = STATEMENT_LEVEL;
-
- switch (stmt->kind) {
- case Ast_Kind_Jump: return Check_Success;
-
- case Ast_Kind_Return: return check_return((AstReturn *) stmt);
- case Ast_Kind_If: return check_if((AstIfWhile *) stmt);
- case Ast_Kind_Static_If: return check_if((AstIfWhile *) stmt);
- case Ast_Kind_While: return check_while((AstIfWhile *) stmt);
- case Ast_Kind_For: return check_for((AstFor *) stmt);
- case Ast_Kind_Switch: return check_switch((AstSwitch *) stmt);
- case Ast_Kind_Block: return check_block((AstBlock *) stmt);
- case Ast_Kind_Defer: return check_statement(&((AstDefer *) stmt)->stmt);
- case Ast_Kind_Call: {
- CHECK(call, (AstCall **) pstmt);
- stmt->flags |= Ast_Flag_Expr_Ignored;
- return Check_Success;
- }
-
- case Ast_Kind_Binary_Op:
- CHECK(binaryop, (AstBinaryOp **) pstmt);
- (*pstmt)->flags |= Ast_Flag_Expr_Ignored;
- return Check_Success;
-
- // NOTE: Local variable declarations used to be removed after the symbol
- // resolution phase because long long ago, all locals needed to be known
- // in a block in order to efficiently allocate enough space and registers
- // for them all. Now with LocalAllocator, this is no longer necessary.
- // Therefore, locals stay in the tree and need to be passed along.
- case Ast_Kind_Local: {
- AstTyped* typed_stmt = (AstTyped *) stmt;
- fill_in_type(typed_stmt);
- if (typed_stmt->type_node != NULL && typed_stmt->type == NULL) {
- CHECK(type, typed_stmt->type_node);
-
- if (!node_is_type((AstNode *) typed_stmt->type_node)) {
- ERROR(stmt->token->pos, "Local's type is not a type.");
- }
-
- YIELD(typed_stmt->token->pos, "Waiting for local variable's type.");
- }
- return Check_Success;
- }
-
- default:
- CHECK(expression, (AstTyped **) pstmt);
- (*pstmt)->flags |= Ast_Flag_Expr_Ignored;
- return Check_Success;
- }
-}
-
-CheckStatus check_statement_chain(AstNode** start) {
- while (*start) {
- CHECK(statement, start);
- start = &(*start)->next;
- }
-
- return Check_Success;
-}
-
-CheckStatus check_block(AstBlock* block) {
- CHECK(statement_chain, &block->body);
-
- // CLEANUP: There will need to be some other method of
- // checking the following conditions.
- //
- // bh_arr_each(AstTyped *, value, block->allocate_exprs) {
- // fill_in_type(*value);
-
- // if ((*value)->kind == Ast_Kind_Local) {
- // if ((*value)->type == NULL) {
- // onyx_report_error((*value)->token->pos,
- // "Unable to resolve type for local '%b'.",
- // (*value)->token->text, (*value)->token->length);
- // return Check_Error;
- // }
-
- // if ((*value)->type->kind == Type_Kind_Compound) {
- // onyx_report_error((*value)->token->pos,
- // "Compound type not allowed as local variable type. Try splitting this into multiple variables.");
- // return Check_Error;
- // }
- // }
- // }
-
- return Check_Success;
-}
-
-CheckStatus check_function(AstFunction* func) {
- if (func->flags & Ast_Flag_Already_Checked) return Check_Success;
- if (func->entity_header && func->entity_header->state < Entity_State_Code_Gen)
- YIELD(func->token->pos, "Waiting for procedure header to pass type-checking");
-
- expected_return_type = &func->type->Function.return_type;
- if (func->body) {
- CheckStatus status = check_block(func->body);
- if (status == Check_Error && func->generated_from && context.cycle_detected == 0)
- ERROR(func->generated_from->pos, "Error in polymorphic procedure generated from this location.");
-
- if (status != Check_Success) return status;
- }
-
- if (*expected_return_type == &type_auto_return) {
- *expected_return_type = &basic_types[Basic_Kind_Void];
- }
-
- func->flags |= Ast_Flag_Already_Checked;
- return Check_Success;
-}
-
-CheckStatus check_overloaded_function(AstOverloadedFunction* func) {
- b32 done = 1;
-
- bh_imap all_overloads;
- bh_imap_init(&all_overloads, global_heap_allocator, 4);
- build_all_overload_options(func->overloads, &all_overloads);
-
- bh_arr_each(bh__imap_entry, entry, all_overloads.entries) {
- AstTyped* node = (AstTyped *) entry->key;
- if (node->kind == Ast_Kind_Overloaded_Function) continue;
-
- if ( node->kind != Ast_Kind_Function
- && node->kind != Ast_Kind_Polymorphic_Proc
- && node->kind != Ast_Kind_Macro) {
- onyx_report_error(node->token->pos, "Overload option not procedure or macro. Got '%s'",
- onyx_ast_node_kind_string(node->kind));
-
- bh_imap_free(&all_overloads);
- return Check_Error;
- }
-
- if (node->kind == Ast_Kind_Function) {
- AstFunction* func = (AstFunction *) node;
-
- if (func->entity_header && func->entity_header->state <= Entity_State_Check_Types) {
- done = 0;
- }
- }
- }
-
- bh_imap_free(&all_overloads);
-
- if (done) return Check_Success;
- else YIELD(func->token->pos, "Waiting for all options to pass type-checking.");
-}
-
-CheckStatus check_struct(AstStructType* s_node) {
- if (s_node->entity_defaults && s_node->entity_defaults->state < Entity_State_Check_Types)
- YIELD(s_node->token->pos, "Waiting for struct member defaults to pass symbol resolution.");
-
- bh_arr_each(AstStructMember *, smem, s_node->members) {
- if ((*smem)->type_node != NULL) {
- CHECK(type, (*smem)->type_node);
- }
-
- if ((*smem)->type_node == NULL && (*smem)->initial_value != NULL) {
- CHECK(expression, &(*smem)->initial_value);
-
- fill_in_type((*smem)->initial_value);
- if ((*smem)->initial_value->type == NULL)
- YIELD((*smem)->initial_value->token->pos, "Trying to resolve type for initial value for member.");
-
- resolve_expression_type((*smem)->initial_value);
- if ((*smem)->type == NULL) (*smem)->type = (*smem)->initial_value->type;
-
- if ((*smem)->type == NULL) {
- ERROR((*smem)->initial_value->token->pos, "Unable to deduce type of initial value. This is probably a compiler bug.");
- }
- }
- }
-
- // NOTE: fills in the stcache
- type_build_from_ast(context.ast_alloc, (AstType *) s_node);
- if (s_node->stcache == NULL || !s_node->stcache_is_valid)
- YIELD(s_node->token->pos, "Waiting for type to be constructed.");
-
- bh_arr_each(StructMember *, smem, s_node->stcache->Struct.memarr) {
- if ((*smem)->type->kind == Type_Kind_Compound) {
- ERROR(s_node->token->pos, "Compound types are not allowed as struct member types.");
- }
- }
-
- return Check_Success;
-}
-
-CheckStatus check_struct_defaults(AstStructType* s_node) {
- if (s_node->entity_type && s_node->entity_type->state < Entity_State_Code_Gen)
- YIELD(s_node->token->pos, "Waiting for struct type to be constructed before checking defaulted members.");
-
- bh_arr_each(StructMember *, smem, s_node->stcache->Struct.memarr) {
- if ((*smem)->initial_value && *(*smem)->initial_value) {
- CHECK(expression, (*smem)->initial_value);
-
- if (!type_check_or_auto_cast((*smem)->initial_value, (*smem)->type)) {
- ERROR_((*(*smem)->initial_value)->token->pos,
- "Mismatched type for initial value, expected '%s', got '%s'.",
- type_get_name((*smem)->type),
- type_get_name((*(*smem)->initial_value)->type));
- }
-
- resolve_expression_type(*(*smem)->initial_value);
- }
- }
-
- return Check_Success;
-}
-
-CheckStatus check_function_header(AstFunction* func) {
- //if (func->entity_body && func->entity_body->state < Entity_State_Check_Types)
- // YIELD(func->token->pos, "Waiting for function body to complete symbol resolution to check header.");
-
- b32 expect_default_param = 0;
- b32 has_had_varargs = 0;
-
- bh_arr_each(AstParam, param, func->params) {
- AstLocal* local = param->local;
-
- if (expect_default_param && param->default_value == NULL) {
- ERROR(local->token->pos,
- "All parameters must have default values after the first default valued parameter.");
- }
-
- if (has_had_varargs && param->vararg_kind != VA_Kind_Not_VA) {
- ERROR(local->token->pos,
- "Can only have one param that is of variable argument type.");
- }
-
- if (has_had_varargs && param->vararg_kind != VA_Kind_Not_VA) {
- ERROR(local->token->pos,
- "Variable arguments must be last in parameter list");
- }
-
- if (param->vararg_kind == VA_Kind_Untyped) {
- // HACK
- if (builtin_vararg_type_type == NULL)
- builtin_vararg_type_type = type_build_from_ast(context.ast_alloc, builtin_vararg_type);
-
- local->type = builtin_vararg_type_type;
- }
-
- if (param->default_value != NULL) {
- if (param->vararg_kind != VA_Kind_Not_VA) {
- ERROR(local->token->pos, "Variadic arguments cannot have default values.");
- }
-
- CHECK(expression, ¶m->default_value);
-
- if (local->type_node == NULL && local->type == NULL) {
- local->type = resolve_expression_type(param->default_value);
- }
-
- expect_default_param = 1;
- }
-
- if (local->type_node != NULL) CHECK(type, local->type_node);
- if (local->type_node != NULL) {
- if (!node_is_type((AstNode *) local->type_node)) {
- ERROR(local->token->pos, "Parameter type is not a type.");
- }
- }
-
- fill_in_type((AstTyped *) local);
- if (local->type == NULL) {
- // onyx_report_error(param->local->token->pos,
- // "Unable to resolve type for parameter, '%b'",
- // local->token->text,
- // local->token->length);
- YIELD(local->token->pos, "Waiting for parameter type to be known.");
- }
-
- if (local->type->kind == Type_Kind_Compound) {
- ERROR(param->local->token->pos, "Compound types are not allowed as parameter types. Try splitting this into multiple parameters.");
- }
-
- // NOTE: I decided to make parameter default values not type checked against
- // the actual parameter type. The actual type checking will happen in check_call
- // when the default value is used as an argument and then has to be checked against
- // the parameter type - brendanfh 2021/01/06
- // if (param->default_value != NULL) {
- // if (!type_check_or_auto_cast(¶m->default_value, param->local->type)) {
- // onyx_report_error(param->local->token->pos,
- // "Expected default value of type '%s', was of type '%s'.",
- // type_get_name(param->local->type),
- // type_get_name(param->default_value->type));
- // return Check_Error;
- // }
- // }
-
- if (param->vararg_kind != VA_Kind_Not_VA) has_had_varargs = 1;
-
- if (local->type->kind != Type_Kind_Array && type_size_of(local->type) == 0) {
- ERROR(local->token->pos, "Function parameters cannot have zero-width types.");
- }
- }
-
- if (func->return_type != NULL) CHECK(type, func->return_type);
-
- func->type = type_build_function_type(context.ast_alloc, func);
- if (func->type == NULL) YIELD(func->token->pos, "Waiting for function type to be constructed");
-
- return Check_Success;
-}
-
-CheckStatus check_memres_type(AstMemRes* memres) {
- CHECK(type, memres->type_node);
- fill_in_type((AstTyped *) memres);
- if (memres->type_node && !memres->type) YIELD(memres->token->pos, "Waiting for global type to be constructed.");
- return Check_Success;
-}
-
-CheckStatus check_memres(AstMemRes* memres) {
- if (memres->initial_value != NULL) {
- CHECK(expression, &memres->initial_value);
- resolve_expression_type(memres->initial_value);
-
- if ((memres->initial_value->flags & Ast_Flag_Comptime) == 0) {
- ERROR(memres->initial_value->token->pos, "Top level expressions must be compile time known.");
- }
-
- if (memres->type != NULL) {
- Type* memres_type = memres->type;
- if (!type_check_or_auto_cast(&memres->initial_value, memres_type)) {
- ERROR_(memres->token->pos,
- "Cannot assign value of type '%s' to a '%s'.",
- node_get_type_name(memres->initial_value),
- type_get_name(memres_type));
- }
-
- } else {
- if (memres->initial_value->type == NULL && memres->initial_value->entity != NULL && memres->initial_value->entity->state <= Entity_State_Check_Types) {
- YIELD(memres->token->pos, "Waiting for global type to be constructed.");
- }
- memres->type = memres->initial_value->type;
- }
- }
-
- return Check_Success;
-}
-
-CheckStatus check_type(AstType* type) {
- if (type == NULL) return Check_Success;
-
- while (type->kind == Ast_Kind_Type_Alias)
- type = ((AstTypeAlias *) type)->to;
-
- switch (type->kind) {
- case Ast_Kind_Poly_Call_Type: {
- AstPolyCallType* pc_node = (AstPolyCallType *) type;
-
- bh_arr_each(AstNode *, param, pc_node->params) {
- if (!node_is_type(*param)) {
- CHECK(expression, (AstTyped **) param);
- resolve_expression_type((AstTyped *) *param);
- }
- }
-
- break;
- }
-
- case Ast_Kind_Typeof: {
- AstTypeOf *type_of = (AstTypeOf *) type;
- CHECK(expression, (AstTyped **) &type_of->expr);
- resolve_expression_type(type_of->expr);
-
- if (type_of->expr->type == NULL) {
- YIELD(type_of->token->pos, "Trying to check type for type-of expression.");
- }
-
- type_of->resolved_type = type_of->expr->type;
- break;
- }
-
- case Ast_Kind_Pointer_Type: CHECK(type, ((AstPointerType *) type)->elem); break;
- case Ast_Kind_Slice_Type: CHECK(type, ((AstSliceType *) type)->elem); break;
- case Ast_Kind_DynArr_Type: CHECK(type, ((AstDynArrType *) type)->elem); break;
- case Ast_Kind_VarArg_Type: CHECK(type, ((AstVarArgType *) type)->elem); break;
-
- case Ast_Kind_Function_Type: {
- AstFunctionType* ftype = (AstFunctionType *) type;
-
- CHECK(type, ftype->return_type);
-
- if (ftype->param_count > 0) {
- fori (i, 0, (i64) ftype->param_count) {
- CHECK(type, ftype->params[i]);
- }
- }
- break;
- }
-
- case Ast_Kind_Type_Compound: {
- AstCompoundType* ctype = (AstCompoundType *) type;
-
- bh_arr_each(AstType *, type, ctype->types) CHECK(type, *type);
- break;
- }
- }
-
- return Check_Success;
-}
-
-CheckStatus check_static_if(AstIf* static_if) {
- expression_types_must_be_known = 1;
- CheckStatus result = check_expression(&static_if->cond);
- if (result == Check_Yield_Macro) return Check_Yield_Macro;
- expression_types_must_be_known = 0;
-
- if (result > Check_Errors_Start || !(static_if->cond->flags & Ast_Flag_Comptime)) {
- ERROR(static_if->token->pos, "Expected this condition to be compile time known.");
- }
-
- if (!type_is_bool(static_if->cond->type)) {
- ERROR(static_if->token->pos, "Expected this condition to be a boolean value.");
- }
-
- static_if->flags |= Ast_Flag_Static_If_Resolved;
-
- b32 resolution = static_if_resolution(static_if);
-
- if (context.options->print_static_if_results)
- bh_printf("Static if statement at %s:%d:%d resulted in %s\n",
- static_if->token->pos.filename,
- static_if->token->pos.line,
- static_if->token->pos.column,
- resolution ? "true" : "false");
-
- if (resolution) {
- bh_arr_each(Entity *, ent, static_if->true_entities) {
- entity_heap_insert_existing(&context.entities, *ent);
- }
-
- } else {
- bh_arr_each(Entity *, ent, static_if->false_entities) {
- entity_heap_insert_existing(&context.entities, *ent);
- }
- }
-
- return Check_Complete;
-}
-
-CheckStatus check_process_directive(AstNode* directive) {
- if (directive->kind == Ast_Kind_Directive_Export) {
- AstTyped* export = ((AstDirectiveExport *) directive)->export;
- if (export->entity && export->entity->state <= Entity_State_Check_Types)
- YIELD(directive->token->pos, "Waiting for export type to be known.");
- }
-
- return Check_Success;
-}
-
-CheckStatus check_macro(AstMacro* macro) {
- if (macro->body->kind == Ast_Kind_Function) {
- CHECK(function_header, (AstFunction *) macro->body);
- }
-
- return Check_Success;
-}
-
-CheckStatus check_node(AstNode* node) {
- switch (node->kind) {
- case Ast_Kind_Function: return check_function((AstFunction *) node);
- case Ast_Kind_Overloaded_Function: return check_overloaded_function((AstOverloadedFunction *) node);
- case Ast_Kind_Block: return check_block((AstBlock *) node);
- case Ast_Kind_Return: return check_return((AstReturn *) node);
- case Ast_Kind_If: return check_if((AstIfWhile *) node);
- case Ast_Kind_Static_If: return check_if((AstIfWhile *) node);
- case Ast_Kind_While: return check_while((AstIfWhile *) node);
- case Ast_Kind_Call: return check_call((AstCall **) &node);
- case Ast_Kind_Binary_Op: return check_binaryop((AstBinaryOp **) &node);
- default: return check_expression((AstTyped **) &node);
- }
-}
-
-void check_entity(Entity* ent) {
- CheckStatus cs = Check_Success;
-
- switch (ent->type) {
- case Entity_Type_Foreign_Function_Header:
- case Entity_Type_Function_Header: cs = check_function_header(ent->function); break;
- case Entity_Type_Function: cs = check_function(ent->function); break;
- case Entity_Type_Overloaded_Function: cs = check_overloaded_function(ent->overloaded_function); break;
- case Entity_Type_Global: cs = check_global(ent->global); break;
- case Entity_Type_Struct_Member_Default: cs = check_struct_defaults((AstStructType *) ent->type_alias); break;
- case Entity_Type_Memory_Reservation_Type: cs = check_memres_type(ent->mem_res); break;
- case Entity_Type_Memory_Reservation: cs = check_memres(ent->mem_res); break;
- case Entity_Type_Static_If: cs = check_static_if(ent->static_if); break;
- case Entity_Type_Macro: cs = check_macro(ent->macro);
-
- case Entity_Type_Expression:
- cs = check_expression(&ent->expr);
- resolve_expression_type(ent->expr);
- break;
-
- case Entity_Type_Type_Alias:
- if (ent->type_alias->kind == Ast_Kind_Struct_Type)
- cs = check_struct((AstStructType *) ent->type_alias);
- else
- cs = check_type(ent->type_alias);
- break;
-
- case Entity_Type_Process_Directive: cs = check_process_directive((AstNode *) ent->expr); break;
-
- case Entity_Type_File_Contents:
- if (context.options->no_file_contents) {
- onyx_report_error(ent->expr->token->pos, "#file_contents is disabled for this compilation.");
- }
- break;
-
- default: break;
- }
-
- if (cs == Check_Success) ent->state = Entity_State_Code_Gen;
- if (cs == Check_Complete) ent->state = Entity_State_Finalized;
- if (cs == Check_Return_To_Symres) ent->state = Entity_State_Resolve_Symbols;
- if (cs == Check_Yield_Macro) ent->macro_attempts++;
- else {
- ent->macro_attempts = 0;
- ent->micro_attempts = 0;
- }
-}
+++ /dev/null
-#include "onyxastnodes.h"
-#include "onyxparser.h"
-#include "onyxutils.h"
-
-static inline b32 should_clone(AstNode* node) {
- if (node->flags & Ast_Flag_No_Clone) return 0;
-
- switch (node->kind) {
- // List of nodes that should not be copied
- case Ast_Kind_Global:
- case Ast_Kind_Memres:
- case Ast_Kind_StrLit:
- case Ast_Kind_Package:
- case Ast_Kind_Enum_Type:
- case Ast_Kind_Enum_Value:
- case Ast_Kind_Overloaded_Function:
- case Ast_Kind_Polymorphic_Proc:
- case Ast_Kind_Alias:
- case Ast_Kind_Code_Block:
- case Ast_Kind_Macro:
- case Ast_Kind_File_Contents:
- return 0;
-
- default: return 1;
- }
-}
-
-static inline i32 ast_kind_to_size(AstNode* node) {
- switch (node->kind) {
- case Ast_Kind_Error: return sizeof(AstNode);
- case Ast_Kind_Package: return sizeof(AstPackage);
- case Ast_Kind_Load_File: return sizeof(AstInclude);
- case Ast_Kind_Load_Path: return sizeof(AstInclude);
- case Ast_Kind_Memres: return sizeof(AstMemRes);
- case Ast_Kind_Binding: return sizeof(AstBinding);
- case Ast_Kind_Function: return sizeof(AstFunction);
- case Ast_Kind_Overloaded_Function: return sizeof(AstOverloadedFunction);
- case Ast_Kind_Polymorphic_Proc: return sizeof(AstPolyProc);
- case Ast_Kind_Block: return sizeof(AstBlock);
- case Ast_Kind_Local: return sizeof(AstLocal);
- case Ast_Kind_Global: return sizeof(AstGlobal);
- case Ast_Kind_Symbol: return sizeof(AstNode);
- case Ast_Kind_Unary_Op: return sizeof(AstUnaryOp);
- case Ast_Kind_Binary_Op: return sizeof(AstBinaryOp);
- case Ast_Kind_Type_Start: return 0;
- case Ast_Kind_Type: return sizeof(AstType);
- case Ast_Kind_Basic_Type: return sizeof(AstBasicType);
- case Ast_Kind_Pointer_Type: return sizeof(AstPointerType);
- case Ast_Kind_Function_Type: return sizeof(AstFunctionType) + ((AstFunctionType *) node)->param_count * sizeof(AstType *);
- case Ast_Kind_Array_Type: return sizeof(AstArrayType);
- case Ast_Kind_Slice_Type: return sizeof(AstSliceType);
- case Ast_Kind_DynArr_Type: return sizeof(AstDynArrType);
- case Ast_Kind_VarArg_Type: return sizeof(AstVarArgType);
- case Ast_Kind_Struct_Type: return sizeof(AstStructType);
- case Ast_Kind_Poly_Struct_Type: return sizeof(AstPolyStructType);
- case Ast_Kind_Poly_Call_Type: return sizeof(AstPolyCallType);
- case Ast_Kind_Enum_Type: return sizeof(AstEnumType);
- case Ast_Kind_Type_Alias: return sizeof(AstTypeAlias);
- case Ast_Kind_Type_Raw_Alias: return sizeof(AstTypeRawAlias);
- case Ast_Kind_Type_Compound: return sizeof(AstCompoundType);
- case Ast_Kind_Typeof: return sizeof(AstTypeOf);
- case Ast_Kind_Type_End: return 0;
- case Ast_Kind_Struct_Member: return sizeof(AstStructMember);
- case Ast_Kind_Enum_Value: return sizeof(AstEnumValue);
- case Ast_Kind_NumLit: return sizeof(AstNumLit);
- case Ast_Kind_StrLit: return sizeof(AstStrLit);
- case Ast_Kind_Param: return sizeof(AstLocal);
- case Ast_Kind_Argument: return sizeof(AstArgument);
- case Ast_Kind_Call: return sizeof(AstCall);
- case Ast_Kind_Intrinsic_Call: return sizeof(AstCall);
- case Ast_Kind_Return: return sizeof(AstReturn);
- case Ast_Kind_Address_Of: return sizeof(AstAddressOf);
- case Ast_Kind_Dereference: return sizeof(AstDereference);
- case Ast_Kind_Subscript: return sizeof(AstSubscript);
- case Ast_Kind_Slice: return sizeof(AstSubscript);
- case Ast_Kind_Field_Access: return sizeof(AstFieldAccess);
- case Ast_Kind_Unary_Field_Access: return sizeof(AstUnaryFieldAccess);
- case Ast_Kind_Pipe: return sizeof(AstBinaryOp);
- case Ast_Kind_Range_Literal: return sizeof(AstRangeLiteral);
- case Ast_Kind_Method_Call: return sizeof(AstBinaryOp);
- case Ast_Kind_Size_Of: return sizeof(AstSizeOf);
- case Ast_Kind_Align_Of: return sizeof(AstAlignOf);
- case Ast_Kind_File_Contents: return sizeof(AstFileContents);
- case Ast_Kind_Struct_Literal: return sizeof(AstStructLiteral);
- case Ast_Kind_Array_Literal: return sizeof(AstArrayLiteral);
- case Ast_Kind_If: return sizeof(AstIfWhile);
- case Ast_Kind_For: return sizeof(AstFor);
- case Ast_Kind_While: return sizeof(AstIfWhile);
- case Ast_Kind_Jump: return sizeof(AstJump);
- case Ast_Kind_Use: return sizeof(AstUse);
- case Ast_Kind_Defer: return sizeof(AstDefer);
- case Ast_Kind_Switch: return sizeof(AstSwitch);
- case Ast_Kind_Switch_Case: return sizeof(AstSwitchCase);
- case Ast_Kind_Directive_Solidify: return sizeof(AstDirectiveSolidify);
- case Ast_Kind_Compound: return sizeof(AstCompound);
- case Ast_Kind_Named_Value: return sizeof(AstNamedValue);
- case Ast_Kind_Call_Site: return sizeof(AstCallSite);
- case Ast_Kind_Static_If: return sizeof(AstIfWhile);
- case Ast_Kind_If_Expression: return sizeof(AstIfExpression);
- case Ast_Kind_Directive_Insert: return sizeof(AstDirectiveInsert);
- case Ast_Kind_Do_Block: return sizeof(AstDoBlock);
- case Ast_Kind_Count: return 0;
- }
-
- return 0;
-}
-
-AstNode* ast_clone_list(bh_allocator a, void* n) {
- AstNode* node = (AstNode *) n;
- if (node == NULL) return NULL;
-
- AstNode* root = ast_clone(a, node);
- AstNode* curr = root->next;
- AstNode** insertion = &root->next;
-
- while (curr != NULL) {
- curr = ast_clone(a, curr);
- *insertion = curr;
- insertion = &curr->next;
- curr = curr->next;
- }
-
- return root;
-}
-
-#define C(nt, mname) ((nt *) nn)->mname = (void *) ast_clone(a, ((nt *) node)->mname);
-
-// NOTE: Using void* to avoid a lot of unnecessary casting
-AstNode* ast_clone(bh_allocator a, void* n) {
- AstNode* node = (AstNode *) n;
-
- if (node == NULL) return NULL;
- if (!should_clone(node)) return node;
-
- static int clone_depth = 0;
- clone_depth++;
-
- i32 node_size = ast_kind_to_size(node);
- // bh_printf("Cloning %s with size %d\n", onyx_ast_node_kind_string(node->kind), node_size);
-
- AstNode* nn = onyx_ast_node_new(a, node_size, node->kind);
- memmove(nn, node, node_size);
-
- switch ((u16) node->kind) {
- case Ast_Kind_Binary_Op:
- case Ast_Kind_Pipe:
- case Ast_Kind_Method_Call:
- C(AstBinaryOp, left);
- C(AstBinaryOp, right);
- break;
-
- case Ast_Kind_Unary_Op:
- C(AstUnaryOp, expr);
- C(AstUnaryOp, type_node);
- break;
-
- case Ast_Kind_Param:
- case Ast_Kind_Local:
- C(AstLocal, type_node);
- break;
-
- case Ast_Kind_Call:
- arguments_deep_clone(a, &((AstCall *) nn)->args, &((AstCall *) node)->args);
- break;
-
- case Ast_Kind_Argument:
- C(AstArgument, value);
- break;
-
- case Ast_Kind_Address_Of:
- C(AstAddressOf, expr);
- break;
-
- case Ast_Kind_Dereference:
- C(AstDereference, expr);
- break;
-
- case Ast_Kind_Slice:
- case Ast_Kind_Subscript:
- C(AstSubscript, addr);
- C(AstSubscript, expr);
- break;
-
- case Ast_Kind_Field_Access:
- C(AstFieldAccess, expr);
- break;
-
- case Ast_Kind_Size_Of:
- C(AstSizeOf, so_ast_type);
- break;
-
- case Ast_Kind_Align_Of:
- C(AstAlignOf, ao_ast_type);
- break;
-
- case Ast_Kind_Struct_Literal: {
- AstStructLiteral* st = (AstStructLiteral *) node;
- AstStructLiteral* dt = (AstStructLiteral *) nn;
-
- dt->stnode = (AstTyped *) ast_clone(a, st->stnode);
-
- arguments_deep_clone(a, &dt->args, &st->args);
- break;
- }
-
- case Ast_Kind_Array_Literal: {
- AstArrayLiteral* st = (AstArrayLiteral *) node;
- AstArrayLiteral* dt = (AstArrayLiteral *) nn;
-
- dt->atnode = (AstTyped *) ast_clone(a, st->atnode);
-
- dt->values = NULL;
- bh_arr_new(global_heap_allocator, dt->values, bh_arr_length(st->values));
- bh_arr_each(AstTyped *, val, st->values)
- bh_arr_push(dt->values, (AstTyped *) ast_clone(a, *val));
-
- break;
- }
-
- case Ast_Kind_Range_Literal:
- C(AstRangeLiteral, low);
- C(AstRangeLiteral, high);
- C(AstRangeLiteral, step);
- break;
-
- case Ast_Kind_Return:
- C(AstReturn, expr);
- break;
-
- case Ast_Kind_Block:
- ((AstBlock *) nn)->body = ast_clone_list(a, ((AstBlock *) node)->body);
- break;
-
- case Ast_Kind_Defer:
- C(AstDefer, stmt);
- break;
-
- case Ast_Kind_For:
- C(AstFor, var);
- C(AstFor, iter);
- C(AstFor, stmt);
- break;
-
- case Ast_Kind_If:
- case Ast_Kind_While:
- ((AstIfWhile *) nn)->initialization = ast_clone_list(a, ((AstIfWhile *) node)->initialization);
-
- C(AstIfWhile, cond);
- //fallthrough
-
- case Ast_Kind_Static_If:
- C(AstIfWhile, true_stmt);
- C(AstIfWhile, false_stmt);
- break;
-
- case Ast_Kind_Switch: {
- AstSwitch* dw = (AstSwitch *) nn;
- AstSwitch* sw = (AstSwitch *) node;
-
- ((AstSwitch *) nn)->initialization = ast_clone_list(a, ((AstSwitch *) node)->initialization);
- dw->expr = (AstTyped *) ast_clone(a, sw->expr);
-
- dw->default_case = (AstBlock *) ast_clone(a, sw->default_case);
-
- dw->cases = NULL;
- bh_arr_new(global_heap_allocator, dw->cases, bh_arr_length(sw->cases));
-
- bh_arr_each(AstSwitchCase, c, sw->cases) {
- bh_arr(AstTyped *) new_values = NULL;
- bh_arr_new(global_heap_allocator, new_values, bh_arr_length(c->values));
- bh_arr_each(AstTyped *, value, c->values)
- bh_arr_push(new_values, (AstTyped *) ast_clone(a, *value));
-
- AstSwitchCase sc;
- sc.values = new_values;
- sc.block = (AstBlock *) ast_clone(a, c->block);
- bh_arr_push(dw->cases, sc);
- }
- break;
- }
-
- case Ast_Kind_Pointer_Type:
- C(AstPointerType, elem);
- break;
-
- case Ast_Kind_Array_Type:
- C(AstArrayType, count_expr);
- C(AstArrayType, elem);
- break;
-
- case Ast_Kind_Slice_Type:
- C(AstSliceType, elem);
- break;
-
- case Ast_Kind_DynArr_Type:
- C(AstDynArrType, elem);
- break;
-
- case Ast_Kind_VarArg_Type:
- C(AstVarArgType, elem);
- break;
-
- case Ast_Kind_Type_Alias:
- C(AstTypeAlias, to);
- break;
-
- case Ast_Kind_Struct_Type: {
- AstStructType* ds = (AstStructType *) nn;
- AstStructType* ss = (AstStructType *) node;
-
- ds->members = NULL;
- bh_arr_new(global_heap_allocator, ds->members, bh_arr_length(ss->members));
-
- bh_arr_each(AstStructMember *, smem, ss->members) {
- bh_arr_push(ds->members, (AstStructMember *) ast_clone(a, *smem));
- }
-
- ds->stcache = NULL;
- break;
- }
-
- case Ast_Kind_Struct_Member:
- C(AstStructMember, type_node);
- C(AstStructMember, initial_value);
- break;
-
- case Ast_Kind_Poly_Call_Type: {
- AstPolyCallType* pcd = (AstPolyCallType *) nn;
- AstPolyCallType* pcs = (AstPolyCallType *) node;
-
- pcd->callee = (AstType *) ast_clone(a, pcs->callee);
- pcd->params = NULL;
- bh_arr_new(global_heap_allocator, pcd->params, bh_arr_length(pcs->params));
-
- bh_arr_each(AstNode *, param, pcs->params) {
- bh_arr_push(pcd->params, ast_clone(a, *param));
- }
-
- break;
- }
-
- case Ast_Kind_Type_Compound: {
- AstCompoundType* cd = (AstCompoundType *) nn;
- AstCompoundType* cs = (AstCompoundType *) node;
-
- cd->types = NULL;
- bh_arr_new(global_heap_allocator, cd->types, bh_arr_length(cs->types));
-
- bh_arr_each(AstType *, type, cs->types) {
- bh_arr_push(cd->types, (AstType *) ast_clone(a, (AstNode *) *type));
- }
- break;
- }
-
- case Ast_Kind_Function_Type:
- C(AstFunctionType, return_type);
- ((AstFunctionType *) nn)->param_count = ((AstFunctionType *) node)->param_count;
- fori (i, 0, (i64) ((AstFunctionType *) nn)->param_count) {
- ((AstFunctionType *) nn)->params[i] = (AstType *) ast_clone(a, ((AstFunctionType *) node)->params[i]);
- }
- break;
-
- case Ast_Kind_Binding:
- bh_printf("Cloning binding: %b\n", node->token->text, node->token->length);
- C(AstTyped, type_node);
- C(AstBinding, node);
- break;
-
- case Ast_Kind_Function: {
- if (clone_depth > 1) {
- clone_depth--;
- return node;
- }
-
- AstFunction* df = (AstFunction *) nn;
- AstFunction* sf = (AstFunction *) node;
-
- if (sf->flags & Ast_Flag_Foreign) return node;
-
- df->return_type = (AstType *) ast_clone(a, sf->return_type);
- df->body = (AstBlock *) ast_clone(a, sf->body);
-
- df->params = NULL;
- bh_arr_new(global_heap_allocator, df->params, bh_arr_length(sf->params));
-
- bh_arr_each(AstParam, param, sf->params) {
- AstParam new_param = { 0 };
- new_param.local = (AstLocal *) ast_clone(a, param->local);
- new_param.default_value = (AstTyped *) ast_clone(a, param->default_value);
- new_param.vararg_kind = param->vararg_kind;
- bh_arr_push(df->params, new_param);
- }
-
- break;
- }
-
- case Ast_Kind_Use:
- C(AstUse, expr);
- break;
-
- case Ast_Kind_Directive_Solidify: {
- AstDirectiveSolidify* dd = (AstDirectiveSolidify *) nn;
- AstDirectiveSolidify* sd = (AstDirectiveSolidify *) node;
-
- dd->poly_proc = (AstPolyProc *) ast_clone(a, (AstNode *) sd->poly_proc);
- dd->resolved_proc = NULL;
-
- dd->known_polyvars = NULL;
- bh_arr_new(global_heap_allocator, dd->known_polyvars, bh_arr_length(sd->known_polyvars));
-
- bh_arr_each(AstPolySolution, sln, sd->known_polyvars) {
- AstPolySolution new_sln;
- new_sln.kind = sln->kind;
- new_sln.poly_sym = (AstNode *) ast_clone(a, (AstNode *) sln->poly_sym);
- new_sln.ast_type = (AstType *) ast_clone(a, (AstNode *) sln->ast_type);
- bh_arr_push(dd->known_polyvars, new_sln);
- }
-
- break;
- }
-
- case Ast_Kind_Compound: {
- AstCompound* cd = (AstCompound *) nn;
- AstCompound* cs = (AstCompound *) node;
-
- cd->exprs = NULL;
- bh_arr_new(global_heap_allocator, cd->exprs, bh_arr_length(cs->exprs));
-
- bh_arr_each(AstTyped *, expr, cs->exprs) {
- bh_arr_push(cd->exprs, (AstTyped *) ast_clone(a, (AstNode *) *expr));
- }
- break;
- }
-
- case Ast_Kind_Named_Value:
- C(AstNamedValue, value);
- break;
-
- case Ast_Kind_If_Expression:
- C(AstIfExpression, cond);
- C(AstIfExpression, true_expr);
- C(AstIfExpression, false_expr);
- break;
-
- case Ast_Kind_Directive_Insert:
- C(AstDirectiveInsert, code_expr);
- break;
-
- case Ast_Kind_Typeof:
- C(AstTypeOf, expr);
- ((AstTypeOf *) nn)->resolved_type = NULL;
- break;
-
- case Ast_Kind_Do_Block:
- C(AstDoBlock, block);
- ((AstDoBlock *) nn)->type_node = (AstType *) &basic_type_auto_return;
- break;
- }
-
- clone_depth--;
- return nn;
-}
-
-#undef C
-
-AstFunction* clone_function_header(bh_allocator a, AstFunction* func) {
- if (func->kind != Ast_Kind_Function) return NULL;
-
- if (func->flags & Ast_Flag_Foreign) return func;
-
- AstFunction* new_func = onyx_ast_node_new(a, sizeof(AstFunction), func->kind);
- memmove(new_func, func, sizeof(AstFunction));
-
- new_func->return_type = (AstType *) ast_clone(a, func->return_type);
-
- new_func->params = NULL;
- bh_arr_new(global_heap_allocator, new_func->params, bh_arr_length(func->params));
- bh_arr_each(AstParam, param, func->params) {
- AstParam new_param;
- new_param.local = (AstLocal *) ast_clone(a, param->local);
- new_param.default_value = (AstTyped *) ast_clone(a, param->default_value);
- new_param.vararg_kind = param->vararg_kind;
- bh_arr_push(new_func->params, new_param);
- }
-
- return new_func;
-}
-
-// Clones a function body from a given function. It is assumed that `dest` is
-// a function from `clone_function_header`.
-void clone_function_body(bh_allocator a, AstFunction* dest, AstFunction* source) {
- if (dest->kind != Ast_Kind_Function) return;
- if (source->kind != Ast_Kind_Function) return;
-
- dest->body = (AstBlock *) ast_clone(a, source->body);
-}
+++ /dev/null
-#include "onyxdoc.h"
-#include "onyxutils.h"
-#include "onyxtypes.h"
-
-static i32 cmp_doc_entry(const void * a, const void * b) {
- DocEntry* d1 = (DocEntry *) a;
- DocEntry* d2 = (DocEntry *) b;
-
- return strncmp(d1->def, d2->def, 1024);
-}
-
-static i32 cmp_doc_package(const void * a, const void * b) {
- DocPackage* d1 = (DocPackage *) a;
- DocPackage* d2 = (DocPackage *) b;
-
- return strncmp(d1->name, d2->name, 1024);
-}
-
-static char* node_to_doc_def(const char* sym, AstNode *node, bh_allocator a) {
- static char buf[1024];
- memset(buf, 0, 1023);
-
- strncat(buf, sym, 1023);
- strncat(buf, " :: ", 1023);
-
- switch (node->kind) {
- case Ast_Kind_Function: {
- strncat(buf, "(", 1023);
-
- AstFunction *func = (AstFunction *) node;
- bh_arr_each(AstParam, param, func->params) {
- if (param != func->params)
- strncat(buf, ", ", 1023);
-
- token_toggle_end(param->local->token);
- strncat(buf, param->local->token->text, 1023);
- token_toggle_end(param->local->token);
-
- strncat(buf, ": ", 1023);
-
- strncat(buf, type_get_name(param->local->type), 1023);
-
- if (param->default_value != NULL) {
- strncat(buf, " = <default>", 1023);
- }
- }
-
- strncat(buf, ") -> ", 1023);
- strncat(buf, type_get_name(func->type->Function.return_type), 1023);
-
- break;
- }
-
- case Ast_Kind_Struct_Type: {
- strncat(buf, "struct { ", 1023);
-
- AstStructType* st = (AstStructType *) node;
- bh_arr_each(AstStructMember *, smem, st->members) {
-
- token_toggle_end((*smem)->token);
- strncat(buf, (*smem)->token->text, 1023);
- token_toggle_end((*smem)->token);
-
- strncat(buf, ": ", 1023);
-
- strncat(buf, type_get_name((*smem)->type), 1023);
-
- strncat(buf, "; ", 1023);
- }
-
- strncat(buf, "}", 1023);
- break;
- }
-
- case Ast_Kind_Basic_Type:
- case Ast_Kind_Array_Type:
- case Ast_Kind_Type_Alias:
- case Ast_Kind_Slice_Type:
- case Ast_Kind_DynArr_Type: {
- strncat(buf, type_get_name(type_build_from_ast(global_heap_allocator, (AstType *) node)), 1023);
- break;
- }
-
- default: {
- strncat(buf, "<unimplemented printing>", 1023);
- }
- }
-
- return bh_strdup(a, buf);
-}
-
-static DocPackage doc_package_create(Package* p, bh_allocator a) {
- DocPackage dp;
- dp.name = p->name;
- dp.public_entries = NULL;
- dp.private_entries = NULL;
-
- bh_arr_new(global_heap_allocator, dp.public_entries, 16);
- bh_arr_new(global_heap_allocator, dp.private_entries, 16);
-
- bh_table_each_start(AstNode *, p->scope->symbols)
- DocEntry de;
- if (value->token) de.pos = value->token->pos;
- de.def = node_to_doc_def(key, value, a);
- de.sym = (char *) key;
- de.additional = NULL;
-
- bh_arr_push(dp.public_entries, de);
- bh_table_each_end;
-
- bh_table_each_start(AstNode *, p->private_scope->symbols)
- DocEntry de;
- if (value->token) de.pos = value->token->pos;
- de.def = node_to_doc_def(key, value, a);
- de.sym = (char *) key;
- de.additional = NULL;
-
- bh_arr_push(dp.private_entries, de);
- bh_table_each_end;
-
- qsort(dp.public_entries, bh_arr_length(dp.public_entries), sizeof(DocEntry), cmp_doc_entry);
- qsort(dp.private_entries, bh_arr_length(dp.private_entries), sizeof(DocEntry), cmp_doc_entry);
-
- return dp;
-}
-
-OnyxDocumentation onyx_docs_generate() {
- OnyxDocumentation doc;
-
- bh_arena_init(&doc.doc_arena, global_heap_allocator, 16 * 1024);
- bh_allocator a = bh_arena_allocator(&doc.doc_arena);
-
- doc.package_docs = NULL;
- bh_arr_new(global_heap_allocator, doc.package_docs, 16);
-
- bh_table_each_start(Package *, context.packages);
- DocPackage dp = doc_package_create(value, a);
- bh_arr_push(doc.package_docs, dp);
- bh_table_each_end;
-
- qsort(doc.package_docs, bh_arr_length(doc.package_docs), sizeof(DocPackage), cmp_doc_package);
-
- return doc;
-}
-
-static void onyx_docs_emit_human(OnyxDocumentation* doc, bh_file* file) {
- // NOTE: Disabling fancy line printing until I can make it better
- #if 0
- bh_arr_each(DocPackage, dp, doc->package_docs) {
- bh_printf("Package '%s'\n", dp->name);
-
- if (bh_arr_length(dp->public_entries) > 0) {
- bh_printf("\xe2\x94\x9c\xe2\x94\x80 Public symbols\n");
- bh_arr_each(DocEntry, de, dp->public_entries) {
- bh_printf("\xe2\x94\x82 \xe2\x94\x9c\xe2\x94\x80 %s\n", de->def);
-
- if (de->pos.filename != NULL)
- bh_printf("\xe2\x94\x82 \xe2\x94\x82 at %s:%d,%d\n", de->pos.filename, de->pos.line, de->pos.column);
- else
- bh_printf("\xe2\x94\x82 \xe2\x94\x82 compiler built-in\n");
-
- bh_printf("\xe2\x94\x82 \xe2\x94\x82\n");
- }
- }
-
- if (bh_arr_length(dp->private_entries) > 0) {
- bh_printf("\xe2\x94\x9c\xe2\x94\x80 Private symbols\n");
- bh_arr_each(DocEntry, de, dp->private_entries) {
- bh_printf("\xe2\x94\x82 \xe2\x94\x9c\xe2\x94\x80 %s\n", de->def);
- if (de->pos.filename != NULL)
- bh_printf("\xe2\x94\x82 \xe2\x94\x82 at %s:%d,%d\n", de->pos.filename, de->pos.line, de->pos.column);
- else
- bh_printf("\xe2\x94\x82 \xe2\x94\x82 compiler built-in\n");
-
- bh_printf("\xe2\x94\x82 \xe2\x94\x82\n");
- }
-
- bh_printf("\n");
- }
- }
- #else
- bh_arr_each(DocPackage, dp, doc->package_docs) {
- bh_fprintf(file, "Package '%s'\n", dp->name);
-
- if (bh_arr_length(dp->public_entries) > 0) {
- bh_fprintf(file, " Public symbols\n");
- bh_arr_each(DocEntry, de, dp->public_entries) {
- bh_fprintf(file, " %s\n", de->def);
-
- if (de->pos.filename != NULL)
- bh_fprintf(file, " at %s:%d,%d\n", de->pos.filename, de->pos.line, de->pos.column);
- else
- bh_fprintf(file, " compiler built-in\n");
-
- bh_fprintf(file, " \n");
- }
- }
-
- if (bh_arr_length(dp->private_entries) > 0) {
- bh_fprintf(file, " Private symbols\n");
- bh_arr_each(DocEntry, de, dp->private_entries) {
- bh_fprintf(file, " %s\n", de->def);
- if (de->pos.filename != NULL)
- bh_fprintf(file, " at %s:%d,%d\n", de->pos.filename, de->pos.line, de->pos.column);
- else
- bh_fprintf(file, " compiler built-in\n");
-
- bh_fprintf(file, " \n");
- }
-
- bh_fprintf(file, "\n");
- }
- }
- #endif
-}
-
-
-static i32 sort_tags(const void* a, const void* b) {
- DocEntry *da = *((DocEntry **) a);
- DocEntry *db = *((DocEntry **) b);
-
- return strcmp(da->sym, db->sym);
-}
-
-static void onyx_docs_emit_tags(OnyxDocumentation* doc) {
- bh_file tags_file;
- if (bh_file_create(&tags_file, "./tags") != BH_FILE_ERROR_NONE) {
- bh_printf("Cannot create 'tags'.\n");
- return;
- }
-
- bh_fprintf(&tags_file, "!_TAG_FILE_FORMAT\t2\n");
- bh_fprintf(&tags_file, "!_TAG_FILE_SORTED\t1\n");
- bh_fprintf(&tags_file, "!_TAG_OUTPUT_FILESEP\tslash\n");
- bh_fprintf(&tags_file, "!_TAG_OUTPUT_MODE\tu-ctags\n");
- bh_fprintf(&tags_file, "!_TAG_PROGRAM_AUTHOR\tOnyx Compiler\n");
- bh_fprintf(&tags_file, "!_TAG_PROGRAM_NAME\tOnyx Compiler\n");
- bh_fprintf(&tags_file, "!_TAG_PROGRAM_URL\thttps://github.com/brendanfh/onyx\n");
- bh_fprintf(&tags_file, "!_TAG_PROGRAM_VERSION\t0.0.1\n");
-
- bh_arr(DocEntry *) tags = NULL;
- bh_arr_new(global_heap_allocator, tags, 256);
-
- bh_arr_each(DocPackage, dp, doc->package_docs) {
- bh_arr_each(DocEntry, de, dp->public_entries) bh_arr_push(tags, de);
- bh_arr_each(DocEntry, de, dp->private_entries) bh_arr_push(tags, de);
- }
-
- qsort(tags, bh_arr_length(tags), sizeof(DocEntry *), sort_tags);
-
- bh_arr_each(DocEntry *, tag, tags) {
- if ((*tag)->pos.filename == NULL) continue;
-
- i32 line_len = 0;
- char *c = (*tag)->pos.line_start;
- while (*c++ != '\n') line_len++;
-
- bh_fprintf(&tags_file, "%s\t%s\t/^%b$/\n",
- (*tag)->sym, (*tag)->pos.filename,
- (*tag)->pos.line_start, line_len);
- }
-
- bh_file_close(&tags_file);
-}
-
-static void onyx_docs_emit_html(OnyxDocumentation* doc, bh_file* file) {
- bh_fprintf(file, "HTML documentation output not supported yet.\n");
- return;
-}
-
-void onyx_docs_emit(OnyxDocumentation* doc, const char* filename) {
- bh_file file;
- if (bh_file_create(&file, filename) != BH_FILE_ERROR_NONE) {
- bh_printf("ERROR: Failed to open file '%s' for writing documentation.\n", filename);
- return;
- }
-
- switch (doc->format) {
- case Doc_Format_Human: onyx_docs_emit_human(doc, &file); break;
- case Doc_Format_Html: onyx_docs_emit_html(doc, &file); break;
- case Doc_Format_Tags: onyx_docs_emit_tags(doc); break;
- }
-
- bh_file_close(&file);
-}
-
+++ /dev/null
-#include "bh.h"
-#include "onyxastnodes.h"
-#include "onyxutils.h"
-
-static inline i32 entity_phase(Entity* e1) {
- if (e1->state <= Entity_State_Parse) return 1;
- if (e1->state < Entity_State_Code_Gen) return 2;
- return 3;
-}
-
-// NOTE: Returns >0 if e1 should be processed after e2.
-static i32 entity_compare(Entity* e1, Entity* e2) {
- i32 phase1 = entity_phase(e1);
- i32 phase2 = entity_phase(e2);
-
- if (phase1 != phase2)
- return phase1 - phase2;
- else if (e1->macro_attempts != e2->macro_attempts)
- return (i32) e1->macro_attempts - (i32) e2->macro_attempts;
- else if (e1->state != e2->state)
- return (i32) e1->state - (i32) e2->state;
- else if (e1->type != e2->type)
- return (i32) e1->type - (i32) e2->type;
- else if (e1->micro_attempts != e2->micro_attempts)
- return (i32) (e1->micro_attempts - e2->micro_attempts);
- else
- return (i32) (e1->id - e2->id);
-}
-
-#define eh_parent(index) (((index) - 1) / 2)
-#define eh_lchild(index) (((index) * 2) + 1)
-#define eh_rchild(index) (((index) * 2) + 2)
-
-static void eh_shift_up(EntityHeap* entities, i32 index) {
- while (index > 0 && entity_compare(entities->entities[eh_parent(index)], entities->entities[index]) > 0) {
- Entity* tmp = entities->entities[eh_parent(index)];
- entities->entities[eh_parent(index)] = entities->entities[index];
- entities->entities[index] = tmp;
-
- index = eh_parent(index);
- }
-}
-
-static void eh_shift_down(EntityHeap* entities, i32 index) {
- i32 min_index = index;
-
- i32 l = eh_lchild(index);
- if (l < bh_arr_length(entities->entities)
- && entity_compare(entities->entities[l], entities->entities[min_index]) < 0) {
- min_index = l;
- }
-
- i32 r = eh_rchild(index);
- if (r < bh_arr_length(entities->entities)
- && entity_compare(entities->entities[r], entities->entities[min_index]) < 0) {
- min_index = r;
- }
-
- if (index != min_index) {
- Entity* tmp = entities->entities[min_index];
- entities->entities[min_index] = entities->entities[index];
- entities->entities[index] = tmp;
-
- eh_shift_down(entities, min_index);
- }
-}
-
-void entity_heap_init(EntityHeap* entities) {
- bh_arena_init(&entities->entity_arena, global_heap_allocator, 32 * 1024);
-}
-
-// Allocates the entity in the entity heap. Don't quite feel this is necessary...
-Entity* entity_heap_register(EntityHeap* entities, Entity e) {
- bh_allocator alloc = bh_arena_allocator(&entities->entity_arena);
- Entity* entity = bh_alloc_item(alloc, Entity);
- *entity = e;
- entity->macro_attempts = 0;
- entity->micro_attempts = 0;
- entity->entered_in_queue = 0;
-
- return entity;
-}
-
-void entity_heap_insert_existing(EntityHeap* entities, Entity* e) {
- if (e->entered_in_queue) return;
-
- if (entities->entities == NULL) {
- bh_arr_new(global_heap_allocator, entities->entities, 128);
- }
-
- bh_arr_push(entities->entities, e);
- eh_shift_up(entities, bh_arr_length(entities->entities) - 1);
- e->entered_in_queue = 1;
-
- entities->state_count[e->state]++;
- entities->type_count[e->type]++;
- entities->all_count[e->state][e->type]++;
-}
-
-Entity* entity_heap_insert(EntityHeap* entities, Entity e) {
- Entity* entity = entity_heap_register(entities, e);
- entity_heap_insert_existing(entities, entity);
- return entity;
-}
-
-Entity* entity_heap_top(EntityHeap* entities) {
- return entities->entities[0];
-}
-
-void entity_heap_change_top(EntityHeap* entities, Entity* new_top) {
- entities->state_count[entities->entities[0]->state]--;
- entities->state_count[new_top->state]++;
-
- entities->type_count[entities->entities[0]->type]--;
- entities->type_count[new_top->type]++;
-
- entities->all_count[entities->entities[0]->state][entities->entities[0]->type]--;
- entities->all_count[new_top->state][new_top->type]++;
-
- entities->entities[0] = new_top;
- eh_shift_down(entities, 0);
-}
-
-void entity_heap_remove_top(EntityHeap* entities) {
- entities->state_count[entities->entities[0]->state]--;
- entities->type_count[entities->entities[0]->type]--;
- entities->all_count[entities->entities[0]->state][entities->entities[0]->type]--;
- entities->entities[0]->entered_in_queue = 0;
-
- entities->entities[0] = entities->entities[bh_arr_length(entities->entities) - 1];
- bh_arr_pop(entities->entities);
- eh_shift_down(entities, 0);
-}
-
-// NOTE(Brendan Hansen): Uses the entity heap in the context structure
-void add_entities_for_node(bh_arr(Entity *) *target_arr, AstNode* node, Scope* scope, Package* package) {
-#define ENTITY_INSERT(_ent) \
- entity = entity_heap_register(entities, _ent); \
- if (target_arr) { \
- bh_arr(Entity *) __tmp_arr = *target_arr; \
- bh_arr_push(__tmp_arr, entity); \
- *target_arr = __tmp_arr; \
- } else { \
- entity_heap_insert_existing(entities, entity); \
- } \
-
-
- EntityHeap* entities = &context.entities;
- Entity* entity;
-
- Entity ent;
- ent.id = entities->next_id++;
- ent.state = Entity_State_Resolve_Symbols;
- ent.package = package;
- ent.scope = scope;
-
- switch (node->kind) {
- case Ast_Kind_Load_File: {
- ent.state = Entity_State_Parse;
- ent.type = Entity_Type_Load_File;
- ent.include = (AstInclude *) node;
- ENTITY_INSERT(ent);
- break;
- }
-
- case Ast_Kind_Load_Path: {
- ent.state = Entity_State_Parse;
- ent.type = Entity_Type_Load_Path;
- ent.include = (AstInclude *) node;
- ENTITY_INSERT(ent);
- break;
- }
-
- case Ast_Kind_Binding: {
- ent.state = Entity_State_Introduce_Symbols;
- ent.type = Entity_Type_Binding;
- ent.binding = (AstBinding *) node;
- ENTITY_INSERT(ent);
- break;
- }
-
- case Ast_Kind_Function: {
- if ((node->flags & Ast_Flag_Foreign) != 0) {
- ent.type = Entity_Type_Foreign_Function_Header;
- ent.function = (AstFunction *) node;
- ENTITY_INSERT(ent);
-
- } else {
- ent.type = Entity_Type_Function_Header;
- ent.function = (AstFunction *) node;
- ENTITY_INSERT(ent);
- ((AstFunction *) node)->entity_header = entity;
-
- ent.id = entities->next_id++;
- ent.type = Entity_Type_Function;
- ent.function = (AstFunction *) node;
- ENTITY_INSERT(ent);
- ((AstFunction *) node)->entity_body = entity;
- }
- break;
- }
-
- case Ast_Kind_Overloaded_Function: {
- ent.type = Entity_Type_Overloaded_Function;
- ent.overloaded_function = (AstOverloadedFunction *) node;
- ENTITY_INSERT(ent);
- break;
- }
-
- case Ast_Kind_Global: {
- if ((node->flags & Ast_Flag_Foreign) != 0) {
- ent.type = Entity_Type_Foreign_Global_Header;
- ent.global = (AstGlobal *) node;
- ENTITY_INSERT(ent);
-
- } else {
- ent.type = Entity_Type_Global_Header;
- ent.global = (AstGlobal *) node;
- ENTITY_INSERT(ent);
-
- ent.id = entities->next_id++;
- ent.type = Entity_Type_Global;
- ent.global = (AstGlobal *) node;
- ENTITY_INSERT(ent);
- }
- break;
- }
-
- case Ast_Kind_StrLit: {
- ent.type = Entity_Type_String_Literal;
- ent.strlit = (AstStrLit *) node;
- ENTITY_INSERT(ent);
- break;
- }
-
- case Ast_Kind_File_Contents: {
- ent.type = Entity_Type_File_Contents;
- ent.file_contents = (AstFileContents *) node;
- ENTITY_INSERT(ent);
- break;
- }
-
- case Ast_Kind_Struct_Type: {
- ent.type = Entity_Type_Struct_Member_Default;
- ent.type_alias = (AstType *) node;
- ENTITY_INSERT(ent);
- ((AstStructType *) node)->entity_defaults = entity;
-
- ent.id = entities->next_id++;
- // fallthrough
- }
-
- case Ast_Kind_Poly_Struct_Type:
- case Ast_Kind_Type_Alias: {
- ent.type = Entity_Type_Type_Alias;
- ent.type_alias = (AstType *) node;
- ENTITY_INSERT(ent);
-
- if (node->kind == Ast_Kind_Struct_Type) {
- ((AstStructType *) node)->entity_type = entity;
- }
-
- break;
- }
-
- case Ast_Kind_Enum_Type: {
- ent.type = Entity_Type_Enum;
- ent.enum_type = (AstEnumType *) node;
- ENTITY_INSERT(ent);
- break;
- }
-
- case Ast_Kind_Use: {
- if (((AstUse *) node)->expr->kind == Ast_Kind_Package) {
- ent.state = Entity_State_Resolve_Symbols;
- ent.type = Entity_Type_Use_Package;
- } else {
- ent.type = Entity_Type_Use;
- }
-
- ent.use = (AstUse *) node;
- ENTITY_INSERT(ent);
- break;
- }
-
- case Ast_Kind_Memres: {
- ent.type = Entity_Type_Memory_Reservation_Type;
- ent.mem_res = (AstMemRes *) node;
- ENTITY_INSERT(ent);
-
- ent.id = entities->next_id++;
- ent.type = Entity_Type_Memory_Reservation;
- ent.mem_res = (AstMemRes *) node;
- ENTITY_INSERT(ent);
- break;
- }
-
- case Ast_Kind_Macro: {
- ent.type = Entity_Type_Macro;
- ent.macro = (AstMacro *) node;
- ENTITY_INSERT(ent);
- break;
- }
-
- case Ast_Kind_Polymorphic_Proc: {
- ent.type = Entity_Type_Polymorphic_Proc;
- ent.poly_proc = (AstPolyProc *) node;
- ENTITY_INSERT(ent);
- break;
- }
-
- case Ast_Kind_Static_If: {
- ent.state = Entity_State_Resolve_Symbols;
- ent.type = Entity_Type_Static_If;
- ent.static_if = (AstIf *) node;
- ENTITY_INSERT(ent);
- break;
- }
-
- case Ast_Kind_Directive_Error: {
- ent.state = Entity_State_Error;
- ent.type = Entity_Type_Error;
- ent.error = (AstDirectiveError *) node;
- ENTITY_INSERT(ent);
- break;
- }
-
- case Ast_Kind_Directive_Export:
- case Ast_Kind_Directive_Add_Overload:
- case Ast_Kind_Directive_Operator: {
- ent.type = Entity_Type_Process_Directive;
- ent.expr = (AstTyped *) node;
- ENTITY_INSERT(ent);
- break;
- }
-
- case Ast_Kind_Note: {
- ent.type = Entity_Type_Note;
- ent.expr = (AstTyped *) node;
- ent.state = Entity_State_Code_Gen;
- ENTITY_INSERT(ent);
- break;
- }
-
- default: {
- ent.type = Entity_Type_Expression;
- ent.expr = (AstTyped *) node;
- ENTITY_INSERT(ent);
- break;
- }
- }
-
- node->entity = entity;
-}
+++ /dev/null
-#include "onyxerrors.h"
-#include "onyxutils.h"
-
-OnyxErrors errors;
-
-void onyx_errors_init(bh_arr(bh_file_contents)* files) {
- errors.file_contents = files;
-
- bh_arena_init(&errors.msg_arena, global_heap_allocator, 16 * 1024);
- errors.msg_alloc = bh_arena_allocator(&errors.msg_arena);
-
- bh_arr_new(global_heap_allocator, errors.errors, 4);
-}
-
-static void print_detailed_message(OnyxError* err, bh_file_contents* fc) {
- bh_printf("(%s:%l,%l) %s\n", err->pos.filename, err->pos.line, err->pos.column, err->text);
-
- b32 colored_printing = 0;
- #ifdef _BH_LINUX
- colored_printing = !context.options->no_colors;
- #endif
-
- i32 linelength = 0;
- i32 first_char = 0;
- char* walker = err->pos.line_start;
- while (*walker == ' ' || *walker == '\t') first_char++, linelength++, walker++;
- while (*walker != '\n') linelength++, walker++;
-
- if (colored_printing) bh_printf("\033[90m");
- i32 numlen = bh_printf(" %d | ", err->pos.line);
- if (colored_printing) bh_printf("\033[94m");
- bh_printf("%b\n", err->pos.line_start, linelength);
-
- char* pointer_str = bh_alloc_array(global_scratch_allocator, char, linelength + numlen);
- memset(pointer_str, ' ', numlen);
- memcpy(pointer_str + numlen - 1, err->pos.line_start, first_char);
- memset(pointer_str + first_char + numlen - 1, ' ', err->pos.column - first_char);
- memset(pointer_str + err->pos.column + numlen - 1, '~', err->pos.length - 1);
- pointer_str[err->pos.column + numlen - 2] = '^';
- pointer_str[err->pos.column + numlen + err->pos.length - 1] = 0;
-
- if (colored_printing) bh_printf("\033[91m");
- bh_printf("%s\n", pointer_str);
-
- if (colored_printing) bh_printf("\033[97m");
-}
-
-void onyx_errors_print() {
- // NOTE: If the format of the error messages is ever changed,
- // update onyx_compile.vim and onyx.sublime-build to match
- // the new format. This was editor error highlighting is still
- // supported.
- //
- // - brendanfh 2020/09/03
-
- bh_arr_each(OnyxError, err, errors.errors) {
- if (err->pos.filename) {
- bh_file_contents file_contents = { 0 };
- bh_arr_each(bh_file_contents, fc, *errors.file_contents) {
- if (!strcmp(fc->filename, err->pos.filename)) {
- file_contents = *fc;
- break;
- }
- }
-
- print_detailed_message(err, &file_contents);
-
- } else {
- bh_printf("(%l,%l) %s\n", err->pos.line, err->pos.column, err->text);
- }
- }
-}
-
-b32 onyx_has_errors() {
- return bh_arr_length(errors.errors) > 0;
-}
-
-void onyx_clear_errors() {
- bh_arr_set_length(errors.errors, 0);
-}
-
-void onyx_report_error(OnyxFilePos pos, char * format, ...) {
-
- va_list vargs;
- va_start(vargs, format);
- char* msg = bh_bprintf_va(format, vargs);
- va_end(vargs);
-
- OnyxError err = {
- .pos = pos,
- .text = bh_strdup(errors.msg_alloc, msg),
- };
-
- bh_arr_push(errors.errors, err);
-}
-
-void onyx_report_warning(OnyxFilePos pos, char* format, ...) {
- va_list vargs;
- va_start(vargs, format);
- char* msg = bh_bprintf_va(format, vargs);
- va_end(vargs);
-
- OnyxError err = {
- .pos = pos,
- .text = msg,
- };
-
- bh_file_contents file_contents = { 0 };
- bh_arr_each(bh_file_contents, fc, *errors.file_contents) {
- if (!strcmp(fc->filename, pos.filename)) {
- file_contents = *fc;
- break;
- }
- }
-
- print_detailed_message(&err, &file_contents);
-}
+++ /dev/null
-#include "bh.h"
-#include "onyxlex.h"
-#include "onyxutils.h"
-#include "onyxerrors.h"
-
-u64 lexer_lines_processed = 0;
-u64 lexer_tokens_processed = 0;
-
-static const char* token_type_names[] = {
- "TOKEN_TYPE_UNKNOWN",
- "TOKEN_TYPE_END_STREAM",
-
- "TOKEN_TYPE_COMMENT",
-
- "package",
- "struct",
- "enum",
- "use",
- "if",
- "else",
- "elseif",
- "return",
- "global",
- "proc",
- "as",
- "cast",
- "while",
- "for",
- "break",
- "continue",
- "sizeof",
- "alignof",
- "typeof",
- "defer",
- "do",
- "switch",
- "case",
- "fallthrough",
- "macro",
-
- "->",
- "<-",
- "---",
- "|>",
-
- ">=",
- "<=",
- "==",
- "!=",
- "+=",
- "-=",
- "*=",
- "/=",
- "%=",
- "&=",
- "|=",
- "^=",
- "&&",
- "||",
- "<<",
- ">>",
- ">>>",
- "<<=",
- ">>=",
- ">>>=",
- "..",
- "~~",
-
- "TOKEN_TYPE_SYMBOL",
- "TOKEN_TYPE_LITERAL_STRING",
- "TOKEN_TYPE_LITERAL_INTEGER",
- "TOKEN_TYPE_LITERAL_FLOAT",
- "true",
- "false",
-
- "NOTE",
-
- "TOKEN_TYPE_COUNT"
-};
-
-#ifndef LITERAL_TOKEN
-#define LITERAL_TOKEN(token, word, token_type) \
- if (token_lit(tokenizer, &tk, token, word, token_type)) goto token_parsed;
-#endif
-
-#ifndef INCREMENT_CURR_TOKEN
-#define INCREMENT_CURR_TOKEN(tkn) { \
- if (*(tkn)->curr == '\n') { \
- (tkn)->line_number++; \
- (tkn)->line_start = (tkn)->curr + 1; \
- } \
- (tkn)->curr++; \
-}
-#endif
-
-#define char_is_alphanum(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z') || ((c) >= '0' && (c) <= '9'))
-#define char_is_num(c) ((c) >= '0' && (c) <= '9')
-#define char_is_alpha(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z'))
-
-static inline b32 token_lit(OnyxTokenizer* tokenizer, OnyxToken* tk, char* lit, b32 is_word, TokenType type) {
- i64 len = 0;
- char* ptr1 = tokenizer->curr;
- char* ptr2 = lit;
- while (*ptr2 != '\0' && *ptr1 == *ptr2) ptr1++, ptr2++, len++;
- if (*ptr2 != '\0') return 0;
-
- if (is_word && (char_is_alphanum(*ptr1) || *ptr1 == '_'))
- return 0;
-
- tk->type = type;
- tk->text = tokenizer->curr;
- tk->length = len;
- tk->pos.line = tokenizer->line_number;
- tk->pos.column = (i32)(tokenizer->curr - tokenizer->line_start) + 1;
-
- tokenizer->curr += len;
-
- return 1;
-}
-
-const char* token_name(TokenType tkn_type) {
- if (tkn_type < Token_Type_Ascii_End) {
- return bh_aprintf(global_scratch_allocator, "%c", (char) tkn_type);
- } else {
- return token_type_names[tkn_type - Token_Type_Ascii_End];
- }
-}
-
-void token_toggle_end(OnyxToken* tkn) {
- static char backup = 0;
- char tmp = tkn->text[tkn->length];
- tkn->text[tkn->length] = backup;
- backup = tmp;
-}
-
-OnyxToken* onyx_get_token(OnyxTokenizer* tokenizer) {
- OnyxToken tk;
-
- // Skip whitespace
- while (1) {
- if (tokenizer->curr == tokenizer->end) break;
-
- switch (*tokenizer->curr) {
- case ' ':
- case '\n':
- case '\t':
- case '\r':
- INCREMENT_CURR_TOKEN(tokenizer);
- break;
- default:
- goto whitespace_skipped;
- }
- }
-
-whitespace_skipped:
-
- tk.type = Token_Type_Unknown;
- tk.text = tokenizer->curr;
- tk.length = 1;
- tk.pos.line_start = tokenizer->line_start;
- tk.pos.filename = tokenizer->filename;
- tk.pos.line = tokenizer->line_number;
- tk.pos.column = (u16)(tokenizer->curr - tokenizer->line_start) + 1;
-
- if (tokenizer->curr == tokenizer->end) {
- tk.type = Token_Type_End_Stream;
- goto token_parsed;
- }
-
- // Comments
- if (*tokenizer->curr == '/' && *(tokenizer->curr + 1) == '/') {
- tokenizer->curr += 2;
- tk.type = Token_Type_Comment;
- tk.text = tokenizer->curr;
-
- while (*tokenizer->curr != '\n' && tokenizer->curr != tokenizer->end) {
- INCREMENT_CURR_TOKEN(tokenizer);
- }
-
- tk.length = tokenizer->curr - tk.text - 2;
- goto token_parsed;
- }
-
- if (*tokenizer->curr == '/' && *(tokenizer->curr + 1) == '*') {
- tokenizer->curr += 2;
- tk.type = Token_Type_Comment;
- tk.text = tokenizer->curr;
-
- i32 comment_depth = 1;
-
- while (comment_depth > 0 && tokenizer->curr != tokenizer->end) {
- if (*tokenizer->curr == '/' && *(tokenizer->curr + 1) == '*') {
- tokenizer->curr += 2;
- comment_depth += 1;
- }
-
- else if (*tokenizer->curr == '*' && *(tokenizer->curr + 1) == '/') {
- tokenizer->curr += 2;
- comment_depth -= 1;
- }
-
- else {
- INCREMENT_CURR_TOKEN(tokenizer);
- }
- }
-
- goto token_parsed;
- }
-
- // String literal
- if (*tk.text == '"') {
- u64 len = 0;
- u64 slash_count = 0;
-
- INCREMENT_CURR_TOKEN(tokenizer);
-
- while (!(*tokenizer->curr == '"' && slash_count == 0)) {
- len++;
-
- // if (*tokenizer->curr == '\n') {
- // onyx_report_error(tk.pos, "String literal not terminated by end of line.");
- // }
-
- if (*tokenizer->curr == '\\') {
- slash_count += 1;
- slash_count %= 2;
- } else {
- slash_count = 0;
- }
-
- INCREMENT_CURR_TOKEN(tokenizer);
- }
-
- INCREMENT_CURR_TOKEN(tokenizer);
-
- tk.text++;
- tk.type = Token_Type_Literal_String;
- tk.length = len;
- goto token_parsed;
- }
-
- // Hex literal
- if (*tokenizer->curr == '0' && *(tokenizer->curr + 1) == 'x' && charset_contains("0123456789abcdefABCDEF", *(tokenizer->curr + 2))) {
- INCREMENT_CURR_TOKEN(tokenizer);
- INCREMENT_CURR_TOKEN(tokenizer);
- u32 len = 3;
- while (char_is_num(*(tokenizer->curr + 1)) || charset_contains("abcdefABCDEF", *(tokenizer->curr + 1))) {
- len++;
- INCREMENT_CURR_TOKEN(tokenizer);
- }
-
- tk.type = Token_Type_Literal_Integer;
- tk.length = len;
-
- INCREMENT_CURR_TOKEN(tokenizer);
- goto token_parsed;
- }
-
- if (*tokenizer->curr == '@') {
- INCREMENT_CURR_TOKEN(tokenizer);
- u32 len = 2;
- while (char_is_alphanum(*(tokenizer->curr + 1)) || *(tokenizer->curr + 1) == '_') {
- len++;
- INCREMENT_CURR_TOKEN(tokenizer);
- }
-
- tk.type = Token_Type_Note;
- tk.length = len;
- INCREMENT_CURR_TOKEN(tokenizer);
- goto token_parsed;
- }
-
- // Number literal
- if (char_is_num(*tokenizer->curr)
- || (*(tokenizer->curr) == '.' && char_is_num(*(tokenizer->curr + 1)))) {
- tk.type = Token_Type_Literal_Integer;
-
- b32 hit_decimal = 0;
- if (*tokenizer->curr == '.') hit_decimal = 1;
-
- u32 len = 1;
- while (char_is_num(*(tokenizer->curr + 1))
- || (!hit_decimal && *(tokenizer->curr + 1) == '.' && *(tokenizer->curr + 2) != '.')) {
- len++;
- INCREMENT_CURR_TOKEN(tokenizer);
-
- if (*tokenizer->curr == '.') hit_decimal = 1;
- }
-
- if (*(tokenizer->curr + 1) == 'f') {
- tk.type = Token_Type_Literal_Float;
-
- len++;
- INCREMENT_CURR_TOKEN(tokenizer);
- }
-
- if (hit_decimal) tk.type = Token_Type_Literal_Float;
-
- tk.length = len;
-
- INCREMENT_CURR_TOKEN(tokenizer);
- goto token_parsed;
- }
-
- char curr = *tokenizer->curr;
- switch (curr) {
- case 'a':
- LITERAL_TOKEN("alignof", 1, Token_Type_Keyword_Alignof);
- LITERAL_TOKEN("as", 1, Token_Type_Keyword_As);
- break;
- case 'b':
- LITERAL_TOKEN("break", 1, Token_Type_Keyword_Break);
- break;
- case 'c':
- LITERAL_TOKEN("case", 1, Token_Type_Keyword_Case);
- LITERAL_TOKEN("cast", 1, Token_Type_Keyword_Cast);
- LITERAL_TOKEN("continue", 1, Token_Type_Keyword_Continue);
- break;
- case 'd':
- LITERAL_TOKEN("defer", 1, Token_Type_Keyword_Defer);
- LITERAL_TOKEN("do", 1, Token_Type_Keyword_Do);
- break;
- case 'e':
- LITERAL_TOKEN("enum", 1, Token_Type_Keyword_Enum);
- LITERAL_TOKEN("elseif", 1, Token_Type_Keyword_Elseif);
- LITERAL_TOKEN("else", 1, Token_Type_Keyword_Else);
- break;
- case 'f':
- LITERAL_TOKEN("for", 1, Token_Type_Keyword_For);
- LITERAL_TOKEN("false", 1, Token_Type_Literal_False);
- LITERAL_TOKEN("fallthrough", 1, Token_Type_Keyword_Fallthrough);
- break;
- case 'g':
- LITERAL_TOKEN("global", 1, Token_Type_Keyword_Global);
- break;
- case 'i':
- LITERAL_TOKEN("if", 1, Token_Type_Keyword_If);
- break;
- case 'm':
- LITERAL_TOKEN("macro", 1, Token_Type_Keyword_Macro);
- break;
- case 'p':
- LITERAL_TOKEN("package", 1, Token_Type_Keyword_Package);
- LITERAL_TOKEN("proc", 1, Token_Type_Keyword_Proc);
- break;
- case 'r':
- LITERAL_TOKEN("return", 1, Token_Type_Keyword_Return);
- break;
- case 's':
- LITERAL_TOKEN("struct", 1, Token_Type_Keyword_Struct);
- LITERAL_TOKEN("sizeof", 1, Token_Type_Keyword_Sizeof);
- LITERAL_TOKEN("switch", 1, Token_Type_Keyword_Switch);
- break;
- case 't':
- LITERAL_TOKEN("true", 1, Token_Type_Literal_True);
- LITERAL_TOKEN("typeof", 1, Token_Type_Keyword_Typeof);
- break;
- case 'u':
- LITERAL_TOKEN("use", 1, Token_Type_Keyword_Use);
- break;
- case 'w':
- LITERAL_TOKEN("while", 1, Token_Type_Keyword_While);
- break;
-
- case '-':
- LITERAL_TOKEN("->", 0, Token_Type_Right_Arrow);
- LITERAL_TOKEN("---", 0, Token_Type_Empty_Block);
- LITERAL_TOKEN("-=", 0, Token_Type_Minus_Equal);
- break;
-
- case '<':
- LITERAL_TOKEN("<-", 0, Token_Type_Right_Arrow);
- LITERAL_TOKEN("<<=", 0, Token_Type_Shl_Equal);
- LITERAL_TOKEN("<<", 0, Token_Type_Shift_Left);
- LITERAL_TOKEN("<=", 0, Token_Type_Less_Equal);
- break;
-
- case '>':
- LITERAL_TOKEN(">>>=", 0, Token_Type_Sar_Equal);
- LITERAL_TOKEN(">>=", 0, Token_Type_Shr_Equal);
- LITERAL_TOKEN(">>>", 0, Token_Type_Shift_Arith_Right);
- LITERAL_TOKEN(">>", 0, Token_Type_Shift_Right);
- LITERAL_TOKEN(">=", 0, Token_Type_Greater_Equal);
- break;
-
- case '&':
- LITERAL_TOKEN("&&", 0, Token_Type_And_And);
- LITERAL_TOKEN("&=", 0, Token_Type_And_Equal);
- break;
-
- case '|':
- LITERAL_TOKEN("|>", 0, Token_Type_Pipe);
- LITERAL_TOKEN("||", 0, Token_Type_Or_Or);
- LITERAL_TOKEN("|=", 0, Token_Type_Or_Equal);
- break;
-
- case '=':
- LITERAL_TOKEN("==", 0, Token_Type_Equal_Equal);
- break;
-
- case '!':
- LITERAL_TOKEN("!=", 0, Token_Type_Not_Equal);
- break;
-
- case '+':
- LITERAL_TOKEN("+=", 0, Token_Type_Plus_Equal);
- break;
-
- case '*':
- LITERAL_TOKEN("*=", 0, Token_Type_Star_Equal);
- break;
-
- case '^':
- LITERAL_TOKEN("^=", 0, Token_Type_Xor_Equal);
- break;
-
- case '/':
- LITERAL_TOKEN("/=", 0, Token_Type_Fslash_Equal);
- break;
-
- case '%':
- LITERAL_TOKEN("%=", 0, Token_Type_Percent_Equal);
- break;
-
- case '.':
- LITERAL_TOKEN("..", 0, Token_Type_Dot_Dot);
- break;
-
- case '~':
- LITERAL_TOKEN("~~", 0, Token_Type_Tilde_Tilde);
- break;
- }
-
- // Symbols
- if (char_is_alpha(*tk.text) || *tokenizer->curr == '_') {
- u64 len = 0;
- while (char_is_alphanum(*tokenizer->curr) || charset_contains("_$", *tokenizer->curr)) {
- len++;
- INCREMENT_CURR_TOKEN(tokenizer);
- }
-
- tk.length = len;
- tk.type = Token_Type_Symbol;
- goto token_parsed;
- }
-
-
- tk.type = (TokenType) *tokenizer->curr;
- INCREMENT_CURR_TOKEN(tokenizer);
-
-token_parsed:
- tk.pos.length = (u16) tk.length;
- bh_arr_push(tokenizer->tokens, tk);
-
- return &tokenizer->tokens[bh_arr_length(tokenizer->tokens) - 1];
-}
-
-OnyxTokenizer onyx_tokenizer_create(bh_allocator allocator, bh_file_contents *fc) {
- OnyxTokenizer tknizer = {
- .start = fc->data,
- .curr = fc->data,
- .end = bh_pointer_add(fc->data, fc->length),
-
- .filename = fc->filename,
-
- .line_number = 1,
- .line_start = fc->data,
- .tokens = NULL,
- };
-
- bh_arr_new(allocator, tknizer.tokens, 1 << 12);
- return tknizer;
-}
-
-void onyx_tokenizer_free(OnyxTokenizer* tokenizer) {
- bh_arr_free(tokenizer->tokens);
-}
-
-void onyx_lex_tokens(OnyxTokenizer* tokenizer) {
- OnyxToken* tk;
- do {
- tk = onyx_get_token(tokenizer);
- } while (tk->type != Token_Type_End_Stream);
-
- lexer_lines_processed += tokenizer->line_number - 1;
- lexer_tokens_processed += bh_arr_length(tokenizer->tokens);
-}
-
-b32 token_equals(OnyxToken* tkn1, OnyxToken* tkn2) {
- if (tkn1->length != tkn2->length) return 0;
- fori (i, 0, tkn1->length)
- if (tkn1->text[i] != tkn2->text[i]) return 0;
- return 1;
-}
-
-b32 token_text_equals(OnyxToken* tkn, char* text) {
- i32 text_len = strlen(text);
- if (tkn->length != text_len) return 0;
-
- fori (i, 0, tkn->length)
- if (tkn->text[i] != text[i]) return 0;
-
- return 1;
-}
+++ /dev/null
-// The sole job of the parser for Onyx is to submit nodes to the
-// entity heap for further processing. These nodes include things
-// such as procedure definitions, string literals, struct definitions
-// and declarations to be introduced into scopes.
-
-// Things that need to be cleaned up in the parser:
-// - control block local variables should be more extensible and reuse more code
-
-#include "onyxlex.h"
-#include "onyxerrors.h"
-#include "onyxparser.h"
-#include "onyxutils.h"
-
-#define make_node(nclass, kind) onyx_ast_node_new(parser->allocator, sizeof(nclass), kind)
-// :LinearTokenDependent
-#define peek_token(ahead) (parser->curr + ahead)
-
-static AstNode error_node = { Ast_Kind_Error, 0, NULL, NULL };
-
-#define ENTITY_SUBMIT(node) (submit_entity_in_scope(parser, (AstNode *) (node), parser->current_scope, parser->package))
-#define ENTITY_SUBMIT_IN_SCOPE(node, scope) (submit_entity_in_scope(parser, (AstNode *) (node), scope, parser->package))
-
-void submit_entity_in_scope(OnyxParser* parser, AstNode* node, Scope* scope, Package* package) {
- if (bh_arr_length(parser->alternate_entity_placement_stack) == 0) {
- add_entities_for_node(NULL, node, scope, package);
-
- } else {
- bh_arr(Entity *) *entity_array = bh_arr_last(parser->alternate_entity_placement_stack);
- add_entities_for_node(entity_array, node, scope, package);
- }
-}
-
-// Parsing Utilities
-static void consume_token(OnyxParser* parser);
-static OnyxToken* expect_token(OnyxParser* parser, TokenType token_type);
-static b32 consume_token_if_next(OnyxParser* parser, TokenType token_type);
-static b32 next_tokens_are(OnyxParser* parser, i32 n, ...);
-static OnyxToken* find_matching_paren(OnyxToken* paren);
-
-static AstNumLit* parse_int_literal(OnyxParser* parser);
-static AstNumLit* parse_float_literal(OnyxParser* parser);
-static b32 parse_possible_struct_literal(OnyxParser* parser, AstTyped* left, AstTyped** ret);
-static b32 parse_possible_array_literal(OnyxParser* parser, AstTyped* left, AstTyped** ret);
-static b32 parse_possible_unary_field_access(OnyxParser* parser, AstTyped** ret);
-static void parse_arguments(OnyxParser* parser, TokenType end_token, Arguments* args);
-static AstTyped* parse_factor(OnyxParser* parser);
-static AstTyped* parse_compound_assignment(OnyxParser* parser, AstTyped* lhs);
-static AstTyped* parse_compound_expression(OnyxParser* parser, b32 assignment_allowed);
-static AstTyped* parse_expression(OnyxParser* parser, b32 assignment_allowed);
-static AstIfWhile* parse_if_stmt(OnyxParser* parser);
-static AstIfWhile* parse_while_stmt(OnyxParser* parser);
-static AstFor* parse_for_stmt(OnyxParser* parser);
-static AstSwitch* parse_switch_stmt(OnyxParser* parser);
-static i32 parse_possible_symbol_declaration(OnyxParser* parser, AstNode** ret);
-static AstReturn* parse_return_stmt(OnyxParser* parser);
-static AstNode* parse_use_stmt(OnyxParser* parser);
-static AstBlock* parse_block(OnyxParser* parser, b32 make_a_new_scope);
-static AstNode* parse_statement(OnyxParser* parser);
-static AstType* parse_type(OnyxParser* parser);
-static AstTypeOf* parse_typeof(OnyxParser* parser);
-static AstStructType* parse_struct(OnyxParser* parser);
-static void parse_function_params(OnyxParser* parser, AstFunction* func);
-static b32 parse_possible_directive(OnyxParser* parser, const char* dir);
-static b32 parse_possible_function_definition(OnyxParser* parser, AstTyped** ret);
-static b32 parse_possible_quick_function_definition(OnyxParser* parser, AstTyped** ret);
-static AstFunction* parse_function_definition(OnyxParser* parser, OnyxToken* token);
-static AstTyped* parse_global_declaration(OnyxParser* parser);
-static AstEnumType* parse_enum_declaration(OnyxParser* parser);
-static AstMacro* parse_macro(OnyxParser* parser);
-static AstIf* parse_static_if_stmt(OnyxParser* parser, b32 parse_block_as_statements);
-static AstTyped* parse_top_level_expression(OnyxParser* parser);
-static AstBinding* parse_top_level_binding(OnyxParser* parser, OnyxToken* symbol);
-static void parse_top_level_statement(OnyxParser* parser);
-static AstPackage* parse_package_expression(OnyxParser* parser);
-static void parse_top_level_statements_until(OnyxParser* parser, TokenType tt);
-
-static void consume_token(OnyxParser* parser) {
- if (parser->hit_unexpected_token) return;
-
- parser->prev = parser->curr;
- // :LinearTokenDependent
- parser->curr++;
- while (parser->curr->type == Token_Type_Comment || parser->curr->type == Token_Type_Note) {
- if (parser->curr->type == Token_Type_Note) {
- AstNote* note = make_node(AstNode, Ast_Kind_Note);
- note->token = parser->curr;
- ENTITY_SUBMIT(note);
- }
-
- parser->curr++;
- }
-}
-
-static OnyxToken* find_matching_paren(OnyxToken* paren) {
- TokenType match = 0;
- switch ((u16) paren->type) {
- case '(': match = ')'; break;
- case '[': match = ']'; break;
- case '{': match = '}'; break;
- case '<': match = '>'; break;
- default: return NULL;
- }
-
- i32 paren_count = 1;
- i32 i = 1;
- while (paren_count > 0) {
- // :LinearTokenDependent
- TokenType type = (paren + i)->type;
- if (type == Token_Type_End_Stream) return NULL;
-
- if (type == paren->type) paren_count++;
- else if (type == match) paren_count--;
-
- i++;
- }
-
- // :LinearTokenDependent
- return paren + (i - 1);
-}
-
-// NOTE: This advances to next token no matter what
-static OnyxToken* expect_token(OnyxParser* parser, TokenType token_type) {
- if (parser->hit_unexpected_token) return NULL;
-
- OnyxToken* token = parser->curr;
- consume_token(parser);
-
- if (token->type != token_type) {
- onyx_report_error(token->pos, "expected token '%s', got '%s'.", token_name(token_type), token_name(token->type));
- parser->hit_unexpected_token = 1;
- // :LinearTokenDependent
- parser->curr = &parser->tokenizer->tokens[bh_arr_length(parser->tokenizer->tokens) - 1];
- return NULL;
- }
-
- return token;
-}
-
-static b32 consume_token_if_next(OnyxParser* parser, TokenType token_type) {
- if (parser->hit_unexpected_token) return 0;
-
- if (parser->curr->type == token_type) {
- consume_token(parser);
- return 1;
- } else {
- return 0;
- }
-}
-
-static void consume_tokens(OnyxParser* parser, i32 n) {
- fori (i, 0, n) consume_token(parser);
-}
-
-static b32 next_tokens_are(OnyxParser* parser, i32 n, ...) {
- va_list va;
- va_start(va, n);
-
- i32 matched = 1;
-
- // BUG: This does not take into consideration comments and notes that can occur between any tokens.
- fori (i, 0, n) {
- TokenType expected_type = va_arg(va, TokenType);
- if (peek_token(i)->type != expected_type) {
- matched = 0;
- break;
- }
- }
-
- va_end(va);
- return matched;
-}
-
-
-// TODO: Make parsing numeric literals not rely on the C standard libary.
-static AstNumLit* parse_int_literal(OnyxParser* parser) {
- AstNumLit* int_node = make_node(AstNumLit, Ast_Kind_NumLit);
- int_node->token = expect_token(parser, Token_Type_Literal_Integer);
- int_node->flags |= Ast_Flag_Comptime;
- int_node->value.l = 0ll;
-
- token_toggle_end(int_node->token);
-
- char* first_invalid = NULL;
- i64 value = strtoll(int_node->token->text, &first_invalid, 0);
-
- int_node->value.l = value;
-
- // NOTE: Hex literals are unsigned.
- if (int_node->token->length >= 2 && int_node->token->text[1] == 'x') {
- if ((u64) value >= ((u64) 1 << 32))
- int_node->type_node = (AstType *) &basic_type_u64;
- else
- int_node->type_node = (AstType *) &basic_type_u32;
- } else {
- int_node->type_node = (AstType *) &basic_type_int_unsized;
- }
-
- token_toggle_end(int_node->token);
- return int_node;
-}
-
-static AstNumLit* parse_float_literal(OnyxParser* parser) {
- AstNumLit* float_node = make_node(AstNumLit, Ast_Kind_NumLit);
- float_node->token = expect_token(parser, Token_Type_Literal_Float);
- float_node->flags |= Ast_Flag_Comptime;
- float_node->value.d = 0.0;
-
- AstType* type = (AstType *) &basic_type_float_unsized;
- token_toggle_end(float_node->token);
-
- if (float_node->token->text[float_node->token->length - 1] == 'f') {
- type = (AstType *) &basic_type_f32;
- float_node->value.f = strtof(float_node->token->text, NULL);
- } else {
- float_node->value.d = strtod(float_node->token->text, NULL);
- }
-
- float_node->type_node = type;
-
- token_toggle_end(float_node->token);
- return float_node;
-}
-
-static b32 parse_possible_directive(OnyxParser* parser, const char* dir) {
- if (!next_tokens_are(parser, 2, '#', Token_Type_Symbol)) return 0;
-
- OnyxToken* sym = peek_token(1);
-
- b32 match = (strlen(dir) == (u64) sym->length) && (strncmp(dir, sym->text, sym->length) == 0);
- if (match) consume_tokens(parser, 2);
-
- return match;
-}
-
-static b32 parse_possible_struct_literal(OnyxParser* parser, AstTyped* left, AstTyped** ret) {
- if (!next_tokens_are(parser, 2, '.', '{')) return 0;
-
- AstStructLiteral* sl = make_node(AstStructLiteral, Ast_Kind_Struct_Literal);
- sl->token = parser->curr;
- sl->stnode = left;
-
- arguments_initialize(&sl->args);
-
- expect_token(parser, '.');
- expect_token(parser, '{');
-
- parse_arguments(parser, '}', &sl->args);
-
- *ret = (AstTyped *) sl;
- return 1;
-}
-
-static b32 parse_possible_array_literal(OnyxParser* parser, AstTyped* left, AstTyped** ret) {
- if (!next_tokens_are(parser, 2, '.', '[')) return 0;
-
- AstArrayLiteral* al = make_node(AstArrayLiteral, Ast_Kind_Array_Literal);
- al->token = parser->curr;
- al->atnode = left;
-
- bh_arr_new(global_heap_allocator, al->values, 4);
- fori (i, 0, 4) al->values[i] = NULL;
-
- expect_token(parser, '.');
- expect_token(parser, '[');
- while (!consume_token_if_next(parser, ']')) {
- if (parser->hit_unexpected_token) return 1;
-
- AstTyped* value = parse_expression(parser, 0);
- bh_arr_push(al->values, value);
-
- if (parser->curr->type != ']')
- expect_token(parser, ',');
- }
-
- *ret = (AstTyped *) al;
- return 1;
-}
-
-static b32 parse_possible_unary_field_access(OnyxParser* parser, AstTyped** ret) {
- if (!next_tokens_are(parser, 2, '.', Token_Type_Symbol)) return 0;
-
- AstUnaryFieldAccess* ufl = make_node(AstUnaryFieldAccess, Ast_Kind_Unary_Field_Access);
- expect_token(parser, '.');
- ufl->token = expect_token(parser, Token_Type_Symbol);
-
- *ret = (AstTyped *) ufl;
- return 1;
-}
-
-static void parse_arguments(OnyxParser* parser, TokenType end_token, Arguments* args) {
- while (!consume_token_if_next(parser, end_token)) {
- if (parser->hit_unexpected_token) return;
-
- if (next_tokens_are(parser, 2, Token_Type_Symbol, '=')) {
- OnyxToken* name = expect_token(parser, Token_Type_Symbol);
- expect_token(parser, '=');
-
- AstNamedValue* named_value = make_node(AstNamedValue, Ast_Kind_Named_Value);
- named_value->token = name;
- named_value->value = parse_expression(parser, 0);
-
- bh_arr_push(args->named_values, named_value);
-
- } else {
- AstTyped* value = parse_expression(parser, 0);
- bh_arr_push(args->values, value);
- }
-
- if (parser->curr->type != end_token)
- expect_token(parser, ',');
- }
-}
-
-static AstTyped* parse_factor(OnyxParser* parser) {
- AstTyped* retval = NULL;
-
- switch ((u16) parser->curr->type) {
- case '(': {
- if (parse_possible_function_definition(parser, &retval)) break;
- if (parse_possible_quick_function_definition(parser, &retval)) break;
-
- consume_token(parser);
- retval = parse_compound_expression(parser, 0);
- expect_token(parser, ')');
- break;
- }
-
- case '-': {
- AstUnaryOp* negate_node = make_node(AstUnaryOp, Ast_Kind_Unary_Op);
- negate_node->operation = Unary_Op_Negate;
- negate_node->token = expect_token(parser, '-');
- negate_node->expr = parse_factor(parser);
-
- retval = (AstTyped *) negate_node;
- break;
- }
-
- case '!': {
- AstUnaryOp* not_node = make_node(AstUnaryOp, Ast_Kind_Unary_Op);
- not_node->operation = Unary_Op_Not;
- not_node->token = expect_token(parser, '!');
- not_node->expr = parse_factor(parser);
-
- retval = (AstTyped *) not_node;
- break;
- }
-
- case '~': {
- AstUnaryOp* not_node = make_node(AstUnaryOp, Ast_Kind_Unary_Op);
- not_node->operation = Unary_Op_Bitwise_Not;
- not_node->token = expect_token(parser, '~');
- not_node->expr = parse_factor(parser);
-
- retval = (AstTyped *) not_node;
- break;
- }
-
- case '*': {
- AstDereference* deref_node = make_node(AstDereference, Ast_Kind_Dereference);
- deref_node->token = expect_token(parser, '*');
- deref_node->expr = parse_factor(parser);
-
- retval = (AstTyped *) deref_node;
- break;
- }
-
- case '^': {
- AstAddressOf* aof_node = make_node(AstAddressOf, Ast_Kind_Address_Of);
- aof_node->token = expect_token(parser, '^');
- aof_node->expr = parse_factor(parser);
-
- retval = (AstTyped *) aof_node;
- break;
- }
-
- case '.': {
- if (parse_possible_struct_literal(parser, NULL, &retval)) return retval;
- if (parse_possible_array_literal(parser, NULL, &retval)) return retval;
- if (parse_possible_unary_field_access(parser, &retval)) return retval;
- goto no_match;
- }
-
- case Token_Type_Tilde_Tilde: {
- AstUnaryOp* ac_node = make_node(AstUnaryOp, Ast_Kind_Unary_Op);
- ac_node->operation = Unary_Op_Auto_Cast;
- ac_node->token = expect_token(parser, Token_Type_Tilde_Tilde);
- ac_node->expr = parse_factor(parser);
-
- retval = (AstTyped *) ac_node;
- break;
- }
-
- case Token_Type_Keyword_Cast: {
- AstUnaryOp* cast_node = make_node(AstUnaryOp, Ast_Kind_Unary_Op);
- cast_node->operation = Unary_Op_Cast;
- cast_node->token = expect_token(parser, Token_Type_Keyword_Cast);
-
- expect_token(parser, '(');
- cast_node->type_node = parse_type(parser);
- expect_token(parser, ')');
-
- cast_node->expr = parse_factor(parser);
-
- retval = (AstTyped *) cast_node;
- break;
- }
-
- case Token_Type_Keyword_Sizeof: {
- AstSizeOf* so_node = make_node(AstSizeOf, Ast_Kind_Size_Of);
- so_node->token = expect_token(parser, Token_Type_Keyword_Sizeof);
- so_node->so_ast_type = (AstType *) parse_type(parser);
- so_node->type_node = (AstType *) &basic_type_i32;
-
- retval = (AstTyped *) so_node;
- break;
- }
-
- case Token_Type_Keyword_Alignof: {
- AstAlignOf* ao_node = make_node(AstAlignOf, Ast_Kind_Align_Of);
- ao_node->token = expect_token(parser, Token_Type_Keyword_Alignof);
- ao_node->ao_ast_type = (AstType *) parse_type(parser);
- ao_node->type_node = (AstType *) &basic_type_i32;
-
- retval = (AstTyped *) ao_node;
- break;
- }
-
- case Token_Type_Keyword_Typeof: {
- retval = (AstTyped *) parse_typeof(parser);
- break;
- }
-
- case Token_Type_Symbol: {
- OnyxToken* sym_token = expect_token(parser, Token_Type_Symbol);
- AstTyped* sym_node = make_node(AstTyped, Ast_Kind_Symbol);
- sym_node->token = sym_token;
-
- retval = sym_node;
- break;
- }
-
- case Token_Type_Literal_Integer:
- retval = (AstTyped *) parse_int_literal(parser);
- break;
-
- case Token_Type_Literal_Float:
- retval = (AstTyped *) parse_float_literal(parser);
- break;
-
- case Token_Type_Literal_String: {
- AstStrLit* str_node = make_node(AstStrLit, Ast_Kind_StrLit);
- str_node->token = expect_token(parser, Token_Type_Literal_String);
- str_node->addr = 0;
- str_node->flags |= Ast_Flag_Comptime;
-
- ENTITY_SUBMIT(str_node);
-
- retval = (AstTyped *) str_node;
- break;
- }
-
- case Token_Type_Literal_True: {
- AstNumLit* bool_node = make_node(AstNumLit, Ast_Kind_NumLit);
- bool_node->type_node = (AstType *) &basic_type_bool;
- bool_node->token = expect_token(parser, Token_Type_Literal_True);
- bool_node->value.i = 1;
- bool_node->flags |= Ast_Flag_Comptime;
- retval = (AstTyped *) bool_node;
- break;
- }
-
- case Token_Type_Literal_False: {
- AstNumLit* bool_node = make_node(AstNumLit, Ast_Kind_NumLit);
- bool_node->type_node = (AstType *) &basic_type_bool;
- bool_node->token = expect_token(parser, Token_Type_Literal_False);
- bool_node->value.i = 0;
- bool_node->flags |= Ast_Flag_Comptime;
- retval = (AstTyped *) bool_node;
- break;
- }
-
- case Token_Type_Keyword_Package: {
- retval = (AstTyped *) parse_package_expression(parser);
- break;
- }
-
- case Token_Type_Keyword_Macro: {
- retval = (AstTyped *) parse_macro(parser);
- break;
- }
-
- case Token_Type_Keyword_Do: {
- OnyxToken* do_token = expect_token(parser, Token_Type_Keyword_Do);
- AstDoBlock* do_block = make_node(AstDoBlock, Ast_Kind_Do_Block);
- do_block->token = do_token;
- do_block->type_node = (AstType *) &basic_type_auto_return;
-
- if (parser->curr->type != '{') {
- onyx_report_error(parser->curr->pos, "Expected '{' after 'do', got '%s'.", token_name(parser->curr->type));
- retval = NULL;
- break;
- }
-
- do_block->block = parse_block(parser, 1);
-
- retval = (AstTyped *) do_block;
- break;
- }
-
- // :TypeValueInterchange
- case '<': {
- AstTypeAlias* alias = make_node(AstTypeAlias, Ast_Kind_Type_Alias);
- alias->token = expect_token(parser, '<');
- alias->to = parse_type(parser);
- expect_token(parser, '>');
-
- retval = (AstTyped *) alias;
- break;
- }
-
- case '#': {
- if (parse_possible_directive(parser, "file_contents")) {
- AstFileContents* fc = make_node(AstFileContents, Ast_Kind_File_Contents);
- fc->token = parser->prev - 1;
- fc->filename_token = expect_token(parser, Token_Type_Literal_String);
- fc->type = type_make_slice(parser->allocator, &basic_types[Basic_Kind_U8]);
-
- ENTITY_SUBMIT(fc);
-
- retval = (AstTyped *) fc;
- break;
- }
- else if (parse_possible_directive(parser, "file")) {
- OnyxToken* dir_token = parser->curr - 2;
-
- OnyxToken* str_token = bh_alloc(parser->allocator, sizeof(OnyxToken));
- str_token->text = bh_strdup(global_heap_allocator, (char *) dir_token->pos.filename);
- str_token->length = strlen(dir_token->pos.filename);
- str_token->pos = dir_token->pos;
- str_token->type = Token_Type_Literal_String;
-
- AstStrLit* filename = make_node(AstStrLit, Ast_Kind_StrLit);
- filename->token = str_token;
- filename->addr = 0;
-
- ENTITY_SUBMIT(filename);
- retval = (AstTyped *) filename;
- break;
- }
- else if (parse_possible_directive(parser, "line")) {
- OnyxToken* dir_token = parser->curr - 2;
-
- AstNumLit* line_num = make_int_literal(parser->allocator, dir_token->pos.line);
- retval = (AstTyped *) line_num;
- break;
- }
- else if (parse_possible_directive(parser, "column")) {
- OnyxToken* dir_token = parser->curr - 2;
-
- AstNumLit* col_num = make_int_literal(parser->allocator, dir_token->pos.column);
- retval = (AstTyped *) col_num;
- break;
- }
- else if (parse_possible_directive(parser, "char")) {
- AstNumLit* char_lit = make_node(AstNumLit, Ast_Kind_NumLit);
- char_lit->flags |= Ast_Flag_Comptime;
- char_lit->type_node = (AstType *) &basic_type_int_unsized;
- char_lit->token = expect_token(parser, Token_Type_Literal_String);
-
- i8 dest = '\0';
- i32 length = string_process_escape_seqs((char *) &dest, char_lit->token->text, 1);
- char_lit->value.i = (u32) dest;
-
- if (length != 1) {
- onyx_report_error(char_lit->token->pos, "Expected only a single character in character literal.");
- }
-
- retval = (AstTyped *) char_lit;
- break;
- }
- else if (parse_possible_directive(parser, "type")) {
- AstTypeAlias* alias = make_node(AstTypeAlias, Ast_Kind_Type_Alias);
- alias->token = parser->curr - 2;
- alias->to = parse_type(parser);
- retval = (AstTyped *) alias;
- break;
- }
- else if (parse_possible_directive(parser, "solidify")) {
- AstDirectiveSolidify* solid = make_node(AstDirectiveSolidify, Ast_Kind_Directive_Solidify);
- // :LinearTokenDependent
- solid->token = parser->curr - 1;
-
- solid->poly_proc = (AstPolyProc *) parse_factor(parser);
-
- solid->known_polyvars = NULL;
- bh_arr_new(global_heap_allocator, solid->known_polyvars, 2);
-
- expect_token(parser, '{');
- while (!consume_token_if_next(parser, '}')) {
- if (parser->hit_unexpected_token) break;
-
- AstNode* poly_var = make_node(AstNode, Ast_Kind_Symbol);
- poly_var->token = expect_token(parser, Token_Type_Symbol);
-
- expect_token(parser, '=');
- AstType* poly_type = parse_type(parser);
-
- bh_arr_push(solid->known_polyvars, ((AstPolySolution) {
- .kind = PSK_Undefined,
- .poly_sym = poly_var,
- .ast_type = poly_type,
- .type = NULL
- }));
-
- if (parser->curr->type != '}')
- expect_token(parser, ',');
- }
-
- retval = (AstTyped *) solid;
- break;
- }
- else if (parse_possible_directive(parser, "defined")) {
- AstDirectiveDefined* defined = make_node(AstDirectiveDefined, Ast_Kind_Directive_Defined);
- // :LinearTokenDependent
- defined->token = parser->curr - 1;
- defined->type_node = (AstType *) &basic_type_bool;
-
- expect_token(parser, '(');
- defined->expr = parse_expression(parser, 0);
- expect_token(parser, ')');
-
- retval = (AstTyped *) defined;
- break;
- }
- else if (parse_possible_directive(parser, "code")) {
- OnyxToken* code_token = parser->curr - 1;
-
- AstCodeBlock* code_block = make_node(AstCodeBlock, Ast_Kind_Code_Block);
- code_block->token = code_token;
-
- assert(builtin_code_type != NULL);
- code_block->type_node = builtin_code_type;
-
- if (parser->curr->type == '{') {
- code_block->code = (AstNode *) parse_block(parser, 1);
- ((AstBlock *) code_block->code)->rules = Block_Rule_Code_Block;
-
- } else {
- code_block->code = (AstNode *) parse_expression(parser, 0);
- }
-
- retval = (AstTyped *) code_block;
- break;
- }
- else if (parse_possible_directive(parser, "insert")) {
- AstDirectiveInsert* insert = make_node(AstDirectiveInsert, Ast_Kind_Directive_Insert);
- insert->token = parser->curr - 1;
- insert->code_expr = parse_expression(parser, 0);
-
- retval = (AstTyped *) insert;
- break;
- }
-
- onyx_report_error(parser->curr->pos, "Invalid directive in expression.");
- return NULL;
- }
-
- default:
- no_match:
- onyx_report_error(parser->curr->pos, "Unexpected token '%s'.", token_name(parser->curr->type));
- return NULL;
- }
-
- while (1) {
- if (parser->hit_unexpected_token) return retval;
-
- switch ((u16) parser->curr->type) {
- case '[': {
- OnyxToken *open_bracket = expect_token(parser, '[');
- AstTyped *expr = parse_compound_expression(parser, 0);
-
- AstSubscript *sub_node = make_node(AstSubscript, Ast_Kind_Subscript);
- sub_node->token = open_bracket;
- sub_node->addr = retval;
- sub_node->expr = expr;
- sub_node->__unused_operation = Binary_Op_Subscript;
-
- retval = (AstTyped *) sub_node;
- expect_token(parser, ']');
- break;
- }
-
- case '.': {
- if (parse_possible_struct_literal(parser, retval, &retval)) return retval;
- if (parse_possible_array_literal(parser, retval, &retval)) return retval;
-
- consume_token(parser);
- AstFieldAccess* field = make_node(AstFieldAccess, Ast_Kind_Field_Access);
- field->token = expect_token(parser, Token_Type_Symbol);
- field->expr = retval;
-
- retval = (AstTyped *) field;
- break;
- }
-
- case '(': {
- AstCall* call_node = make_node(AstCall, Ast_Kind_Call);
- call_node->token = expect_token(parser, '(');
- call_node->callee = retval;
-
- arguments_initialize(&call_node->args);
-
- parse_arguments(parser, ')', &call_node->args);
-
- // Wrap expressions in AstArgument
- bh_arr_each(AstTyped *, arg, call_node->args.values) {
- if ((*arg) == NULL) continue;
- *arg = (AstTyped *) make_argument(parser->allocator, *arg);
- }
-
- bh_arr_each(AstNamedValue *, named_value, call_node->args.named_values) {
- if ((*named_value)->value == NULL) continue;
- (*named_value)->value = (AstTyped *) make_argument(parser->allocator, (AstTyped *) (*named_value)->value);
- }
-
- retval = (AstTyped *) call_node;
- break;
- }
-
- case Token_Type_Right_Arrow: {
- AstBinaryOp* method_call = make_node(AstBinaryOp, Ast_Kind_Method_Call);
- method_call->token = expect_token(parser, Token_Type_Right_Arrow);
- method_call->left = retval;
- method_call->right = parse_factor(parser);
-
- retval = (AstTyped *) method_call;
- break;
- }
-
- case Token_Type_Keyword_If: {
- AstIfExpression* if_expression = make_node(AstIfExpression, Ast_Kind_If_Expression);
- if_expression->token = expect_token(parser, Token_Type_Keyword_If);
-
- if_expression->true_expr = retval;
- if_expression->cond = parse_expression(parser, 0);
- expect_token(parser, Token_Type_Keyword_Else);
- if_expression->false_expr = parse_expression(parser, 0);
-
- retval = (AstTyped *) if_expression;
-
- // nocheckin This should maybe be goto factor_parsed; ??
- break;
- }
-
- default: goto factor_parsed;
- }
- }
-
-factor_parsed:
-
- return retval;
-}
-
-static inline i32 get_precedence(BinaryOp kind) {
- switch (kind) {
- case Binary_Op_Assign: return 1;
- case Binary_Op_Assign_Add: return 1;
- case Binary_Op_Assign_Minus: return 1;
- case Binary_Op_Assign_Multiply: return 1;
- case Binary_Op_Assign_Divide: return 1;
- case Binary_Op_Assign_Modulus: return 1;
- case Binary_Op_Assign_And: return 1;
- case Binary_Op_Assign_Or: return 1;
- case Binary_Op_Assign_Xor: return 1;
- case Binary_Op_Assign_Shl: return 1;
- case Binary_Op_Assign_Shr: return 1;
- case Binary_Op_Assign_Sar: return 1;
-
- case Binary_Op_Pipe: return 2;
- case Binary_Op_Range: return 2;
-
- case Binary_Op_Bool_And: return 3;
- case Binary_Op_Bool_Or: return 3;
-
- case Binary_Op_Equal: return 4;
- case Binary_Op_Not_Equal: return 4;
-
- case Binary_Op_Less_Equal: return 5;
- case Binary_Op_Less: return 5;
- case Binary_Op_Greater_Equal: return 5;
- case Binary_Op_Greater: return 5;
-
- case Binary_Op_And: return 6;
- case Binary_Op_Or: return 6;
- case Binary_Op_Xor: return 6;
- case Binary_Op_Shl: return 6;
- case Binary_Op_Shr: return 6;
- case Binary_Op_Sar: return 6;
-
- case Binary_Op_Add: return 7;
- case Binary_Op_Minus: return 7;
-
- case Binary_Op_Multiply: return 8;
- case Binary_Op_Divide: return 8;
-
- case Binary_Op_Modulus: return 9;
-
- default: return -1;
- }
-}
-
-static BinaryOp binary_op_from_token_type(TokenType t) {
- switch ((u16) t) {
- case Token_Type_Equal_Equal: return Binary_Op_Equal;
- case Token_Type_Not_Equal: return Binary_Op_Not_Equal;
- case Token_Type_Less_Equal: return Binary_Op_Less_Equal;
- case Token_Type_Greater_Equal: return Binary_Op_Greater_Equal;
- case '<': return Binary_Op_Less;
- case '>': return Binary_Op_Greater;
-
- case '+': return Binary_Op_Add;
- case '-': return Binary_Op_Minus;
- case '*': return Binary_Op_Multiply;
- case '/': return Binary_Op_Divide;
- case '%': return Binary_Op_Modulus;
-
- case '&': return Binary_Op_And;
- case '|': return Binary_Op_Or;
- case '^': return Binary_Op_Xor;
- case Token_Type_Shift_Left: return Binary_Op_Shl;
- case Token_Type_Shift_Right: return Binary_Op_Shr;
- case Token_Type_Shift_Arith_Right: return Binary_Op_Sar;
-
- case Token_Type_And_And: return Binary_Op_Bool_And;
- case Token_Type_Or_Or: return Binary_Op_Bool_Or;
-
- case '=': return Binary_Op_Assign;
- case Token_Type_Plus_Equal: return Binary_Op_Assign_Add;
- case Token_Type_Minus_Equal: return Binary_Op_Assign_Minus;
- case Token_Type_Star_Equal: return Binary_Op_Assign_Multiply;
- case Token_Type_Fslash_Equal: return Binary_Op_Assign_Divide;
- case Token_Type_Percent_Equal: return Binary_Op_Assign_Modulus;
- case Token_Type_And_Equal: return Binary_Op_Assign_And;
- case Token_Type_Or_Equal: return Binary_Op_Assign_Or;
- case Token_Type_Xor_Equal: return Binary_Op_Assign_Xor;
- case Token_Type_Shl_Equal: return Binary_Op_Assign_Shl;
- case Token_Type_Shr_Equal: return Binary_Op_Assign_Shr;
- case Token_Type_Sar_Equal: return Binary_Op_Assign_Sar;
-
- case Token_Type_Pipe: return Binary_Op_Pipe;
- case Token_Type_Dot_Dot: return Binary_Op_Range;
- case '[': return Binary_Op_Subscript;
- default: return Binary_Op_Count;
- }
-}
-
-static AstTyped* parse_compound_assignment(OnyxParser* parser, AstTyped* lhs) {
- if (parser->curr->type != '=') return lhs;
-
- AstBinaryOp* assignment = make_node(AstBinaryOp, Ast_Kind_Binary_Op);
- assignment->token = expect_token(parser, '=');
- assignment->operation = Binary_Op_Assign;
- assignment->left = lhs;
- assignment->right = parse_compound_expression(parser, 0);
-
- return (AstTyped *) assignment;
-}
-
-static AstTyped* parse_compound_expression(OnyxParser* parser, b32 assignment_allowed) {
- AstTyped* first = parse_expression(parser, assignment_allowed);
-
- if (parser->curr->type == ',') {
- AstCompound* compound = make_node(AstCompound, Ast_Kind_Compound);
- compound->token = parser->curr;
-
- bh_arr_new(global_heap_allocator, compound->exprs, 2);
- bh_arr_push(compound->exprs, first);
-
- while (consume_token_if_next(parser, ',')) {
- if (parser->hit_unexpected_token) return (AstTyped *) compound;
-
- AstTyped* expr = parse_expression(parser, 0);
- bh_arr_push(compound->exprs, expr);
-
- if (assignment_allowed && parser->curr->type == '=') {
- return parse_compound_assignment(parser, (AstTyped *) compound);
- }
- }
-
- return (AstTyped *) compound;
-
- } else {
- return first;
- }
-}
-
-static AstTyped* parse_expression(OnyxParser* parser, b32 assignment_allowed) {
- bh_arr(AstBinaryOp*) tree_stack = NULL;
- bh_arr_new(global_heap_allocator, tree_stack, 4);
- bh_arr_set_length(tree_stack, 0);
-
- AstTyped* left = parse_factor(parser);
- AstTyped* right;
- AstTyped* root = left;
-
- BinaryOp bin_op_kind;
- OnyxToken* bin_op_tok;
-
- while (1) {
- if (parser->hit_unexpected_token) return root;
-
- bin_op_kind = binary_op_from_token_type(parser->curr->type);
- if (bin_op_kind == Binary_Op_Count) goto expression_done;
- if (binop_is_assignment(bin_op_kind) && !assignment_allowed) goto expression_done;
- if (bin_op_kind == Binary_Op_Subscript) goto expression_done;
-
- bin_op_tok = parser->curr;
- consume_token(parser);
-
- AstBinaryOp* bin_op;
- if (bin_op_kind == Binary_Op_Pipe) bin_op = make_node(AstBinaryOp, Ast_Kind_Pipe);
- else if (bin_op_kind == Binary_Op_Range) bin_op = (AstBinaryOp *) make_node(AstRangeLiteral, Ast_Kind_Range_Literal);
- else bin_op = make_node(AstBinaryOp, Ast_Kind_Binary_Op);
-
- bin_op->token = bin_op_tok;
- bin_op->operation = bin_op_kind;
-
- while ( !bh_arr_is_empty(tree_stack) &&
- get_precedence(bh_arr_last(tree_stack)->operation) >= get_precedence(bin_op_kind))
- bh_arr_pop(tree_stack);
-
- if (bh_arr_is_empty(tree_stack)) {
- // NOTE: new is now the root node
- bin_op->left = root;
- root = (AstTyped *) bin_op;
-
- } else {
- bin_op->left = bh_arr_last(tree_stack)->right;
- bh_arr_last(tree_stack)->right = (AstTyped *) bin_op;
- }
-
- bh_arr_push(tree_stack, bin_op);
-
- right = parse_factor(parser);
- bin_op->right = right;
- }
-
-expression_done:
- bh_arr_free(tree_stack);
- return root;
-}
-
-static AstIfWhile* parse_if_stmt(OnyxParser* parser) {
- AstIfWhile* if_node = make_node(AstIfWhile, Ast_Kind_If);
- if_node->token = expect_token(parser, Token_Type_Keyword_If);
-
- AstIfWhile* root_if = if_node;
- AstTyped* cond;
- AstNode* initialization_or_cond=NULL;
- b32 had_initialization = 0;
- if (parse_possible_symbol_declaration(parser, &initialization_or_cond)) {
- had_initialization = 1;
-
- } else {
- // NOTE: Assignment is allowed here because instead of not parsing correctly,
- // an error is reported in the typechecking, saying that assignment isn't allowed
- // here, which is better than an unexpected token error.
- initialization_or_cond = (AstNode *) parse_compound_expression(parser, 1);
- }
-
- if (had_initialization || parser->curr->type == ';') {
- expect_token(parser, ';');
- cond = parse_expression(parser, 1);
- } else {
- cond = (AstTyped *) initialization_or_cond;
- initialization_or_cond = NULL;
- }
-
- AstBlock* true_stmt = parse_block(parser, 1);
-
- if_node->initialization = initialization_or_cond;
- if_node->cond = cond;
- if (true_stmt != NULL)
- if_node->true_stmt = true_stmt;
-
- while (consume_token_if_next(parser, Token_Type_Keyword_Elseif)) {
- if (parser->hit_unexpected_token) return root_if;
-
- AstIfWhile* elseif_node = make_node(AstIfWhile, Ast_Kind_If);
- elseif_node->token = parser->curr - 1;
-
- cond = parse_expression(parser, 1);
- true_stmt = parse_block(parser, 1);
-
- elseif_node->cond = cond;
- if (true_stmt != NULL)
- elseif_node->true_stmt = true_stmt;
-
- if_node->false_stmt = (AstBlock *) elseif_node;
- if_node = elseif_node;
- }
-
- if (consume_token_if_next(parser, Token_Type_Keyword_Else)) {
- AstBlock* false_stmt = parse_block(parser, 1);
- if (false_stmt != NULL)
- if_node->false_stmt = false_stmt;
- }
-
- return root_if;
-}
-
-static AstIfWhile* parse_while_stmt(OnyxParser* parser) {
- OnyxToken* while_token = expect_token(parser, Token_Type_Keyword_While);
- AstIfWhile* while_node = make_node(AstIfWhile, Ast_Kind_While);
- while_node->token = while_token;
-
- AstTyped* cond;
- AstNode* initialization_or_cond=NULL;
- b32 had_initialization = 0;
- if (parse_possible_symbol_declaration(parser, &initialization_or_cond)) {
- had_initialization = 1;
-
- } else {
- // NOTE: Assignment is allowed here because instead of not parsing correctly,
- // an error is reported in the typechecking, saying that assignment isn't allowed
- // here, which is better than an unexpected token error.
- initialization_or_cond = (AstNode *) parse_compound_expression(parser, 1);
- }
-
- if (had_initialization || parser->curr->type == ';') {
- expect_token(parser, ';');
- cond = parse_expression(parser, 1);
- } else {
- cond = (AstTyped *) initialization_or_cond;
- initialization_or_cond = NULL;
- }
-
- while_node->initialization = initialization_or_cond;
- while_node->cond = cond;
- while_node->true_stmt = parse_block(parser, 1);
-
- if (consume_token_if_next(parser, Token_Type_Keyword_Else)) {
- while_node->false_stmt = parse_block(parser, 1);
- }
-
- return while_node;
-}
-
-static AstFor* parse_for_stmt(OnyxParser* parser) {
- AstFor* for_node = make_node(AstFor, Ast_Kind_For);
- for_node->token = expect_token(parser, Token_Type_Keyword_For);
-
- if (consume_token_if_next(parser, '^')) {
- for_node->by_pointer = 1;
- }
-
- OnyxToken* local_sym = expect_token(parser, Token_Type_Symbol);
- AstLocal* var_node = make_local(parser->allocator, local_sym, NULL);
-
- for_node->var = var_node;
-
- expect_token(parser, ':');
- for_node->iter = parse_expression(parser, 1);
- for_node->stmt = parse_block(parser, 1);
-
- return for_node;
-}
-
-static AstSwitch* parse_switch_stmt(OnyxParser* parser) {
- AstSwitch* switch_node = make_node(AstSwitch, Ast_Kind_Switch);
- switch_node->token = expect_token(parser, Token_Type_Keyword_Switch);
-
- bh_arr_new(global_heap_allocator, switch_node->cases, 4);
-
- AstTyped* expr;
- AstNode* initialization_or_expr=NULL;
- b32 had_initialization = 0;
- if (parse_possible_symbol_declaration(parser, &initialization_or_expr)) {
- had_initialization = 1;
-
- } else {
- // NOTE: Assignment is allowed here because instead of not parsing correctly,
- // an error is reported in the typechecking, saying that assignment isn't allowed
- // here, which is better than an unexpected token error.
- initialization_or_expr = (AstNode *) parse_compound_expression(parser, 1);
- }
-
- if (had_initialization || parser->curr->type == ';') {
- expect_token(parser, ';');
- expr = parse_expression(parser, 1);
-
- } else {
- expr = (AstTyped *) initialization_or_expr;
- initialization_or_expr = NULL;
- }
-
- switch_node->initialization = initialization_or_expr;
- switch_node->expr = expr;
-
- expect_token(parser, '{');
-
- while (consume_token_if_next(parser, Token_Type_Keyword_Case)) {
- if (parser->hit_unexpected_token) return switch_node;
-
- bh_arr(AstTyped *) case_values = NULL;
- bh_arr_new(global_heap_allocator, case_values, 1);
-
- if (parse_possible_directive(parser, "default")) {
- switch_node->default_case = parse_block(parser, 1);
-
- if (parser->curr->type != '}') {
- onyx_report_error(parser->curr->pos, "The #default case must be the last case in a switch statement.\n");
- }
- break;
- }
-
- AstTyped* value = parse_expression(parser, 1);
- bh_arr_push(case_values, value);
- while (consume_token_if_next(parser, ',')) {
- if (parser->hit_unexpected_token) return switch_node;
-
- value = parse_expression(parser, 1);
- bh_arr_push(case_values, value);
- }
-
- AstBlock* block = parse_block(parser, 1);
-
- AstSwitchCase sc_node;
- sc_node.block = block;
- sc_node.values = case_values;
-
- bh_arr_push(switch_node->cases, sc_node);
- }
-
- expect_token(parser, '}');
- return switch_node;
-}
-
-static i32 parse_possible_compound_symbol_declaration(OnyxParser* parser, AstNode** ret) {
- u32 token_offset = 0;
- while (peek_token(token_offset)->type == Token_Type_Symbol) {
- token_offset += 1;
-
- if (peek_token(token_offset)->type != ',') break;
- token_offset += 1;
- }
-
- if (peek_token(token_offset)->type != ':') return 0;
-
- // At this point, we are sure it is a compound declaration.
- AstCompound* local_compound = make_node(AstCompound, Ast_Kind_Compound);
- bh_arr_new(global_heap_allocator, local_compound->exprs, token_offset / 2);
-
- AstLocal* first_local = NULL;
- AstLocal* prev_local = NULL;
-
- while (parser->curr->type == Token_Type_Symbol) {
- if (parser->hit_unexpected_token) return 1;
-
- OnyxToken* local_sym = expect_token(parser, Token_Type_Symbol);
- AstLocal* new_local = make_local(parser->allocator, local_sym, NULL);
-
- if (prev_local == NULL) {
- first_local = new_local;
- } else {
- prev_local->next = (AstNode *) new_local;
- }
- prev_local = new_local;
-
- AstNode* sym_node = make_symbol(parser->allocator, local_sym);
- bh_arr_push(local_compound->exprs, (AstTyped *) sym_node);
-
- consume_token_if_next(parser, ',');
- }
-
- expect_token(parser, ':');
-
- if (parser->curr->type == '=') {
- AstBinaryOp* assignment = make_binary_op(parser->allocator, Binary_Op_Assign, (AstTyped *) local_compound, NULL);
- assignment->token = expect_token(parser, '=');
- assignment->right = parse_compound_expression(parser, 0);
-
- prev_local->next = (AstNode *) assignment;
-
- } else {
- AstType* type_for_all = parse_type(parser);
- forll (AstLocal, local, first_local, next) {
- local->type_node = type_for_all;
- }
- }
-
- *ret = (AstNode *) first_local;
- return 1;
-}
-
-// Returns:
-// 0 - if this was not a symbol declaration.
-// 1 - if this was a local declaration.
-// 2 - if this was binding declaration.
-// ret is set to the statement to insert
-static i32 parse_possible_symbol_declaration(OnyxParser* parser, AstNode** ret) {
- // Has to start with a symbol to be a declaration
- if (parser->curr->type != Token_Type_Symbol) return 0;
-
- // If the token after the symbol is a comma, assume this is a compound declaration.
- if (peek_token(1)->type == ',') {
- return parse_possible_compound_symbol_declaration(parser, ret);
- }
-
- if (peek_token(1)->type != ':') return 0;
-
- OnyxToken* symbol = expect_token(parser, Token_Type_Symbol);
- expect_token(parser, ':');
-
- if (parser->curr->type == ':') {
- AstBinding* binding = parse_top_level_binding(parser, symbol);
- if (parser->hit_unexpected_token) return 2;
-
- ENTITY_SUBMIT(binding);
- return 2;
- }
-
- AstType* type_node = NULL;
- if (parser->curr->type != '=') {
- type_node = parse_type(parser);
- }
-
- AstLocal* local = make_local(parser->allocator, symbol, type_node);
- *ret = (AstNode *) local;
-
- if (parser->curr->type == '=') {
- AstBinaryOp* assignment = make_node(AstBinaryOp, Ast_Kind_Binary_Op);
- assignment->operation = Binary_Op_Assign;
- assignment->token = expect_token(parser, '=');
- local->next = (AstNode *) assignment;
-
- AstTyped* expr = parse_expression(parser, 1);
- if (expr == NULL) return 1;
- assignment->right = expr;
-
- // INVESTIGATE: I don't know why, but appearantly, this has to be a
- // symbol node, not a direct link to the local. There is an error about
- // being unable to resolve the type of the local if it is immediately set.
- AstNode* left_symbol = make_node(AstNode, Ast_Kind_Symbol);
- left_symbol->token = symbol;
- assignment->left = (AstTyped *) left_symbol;
- }
-
- return 1;
-}
-
-static AstReturn* parse_return_stmt(OnyxParser* parser) {
- AstReturn* return_node = make_node(AstReturn, Ast_Kind_Return);
- return_node->token = expect_token(parser, Token_Type_Keyword_Return);
-
- AstTyped* expr = NULL;
-
- if (parser->curr->type != ';') {
- expr = parse_compound_expression(parser, 0);
-
- if (expr == NULL || expr == (AstTyped *) &error_node) {
- return (AstReturn *) &error_node;
- } else {
- return_node->expr = expr;
- }
- }
-
- return return_node;
-}
-
-static AstNode* parse_use_stmt(OnyxParser* parser) {
- OnyxToken* use_token = expect_token(parser, Token_Type_Keyword_Use);
- AstUse* use_node = make_node(AstUse, Ast_Kind_Use);
- use_node->token = use_token;
- use_node->expr = parse_expression(parser, 1);
-
- if (consume_token_if_next(parser, '{')) {
- bh_arr_new(global_heap_allocator, use_node->only, 4);
-
- while (!consume_token_if_next(parser, '}')) {
- if (parser->hit_unexpected_token) return NULL;
-
- QualifiedUse qu;
- qu.symbol_name = expect_token(parser, Token_Type_Symbol);
-
- if (consume_token_if_next(parser, Token_Type_Keyword_As))
- qu.as_name = expect_token(parser, Token_Type_Symbol);
- else
- qu.as_name = qu.symbol_name;
-
- bh_arr_push(use_node->only, qu);
-
- if (parser->curr->type != '}')
- expect_token(parser, ',');
- }
- }
-
- if (use_node->expr->kind == Ast_Kind_Package) {
- ENTITY_SUBMIT(use_node);
- return NULL;
-
- } else {
- return (AstNode *) use_node;
- }
-}
-
-static AstNode* parse_jump_stmt(OnyxParser* parser, TokenType token_type, JumpType jump_type) {
- AstJump* jnode = make_node(AstJump, Ast_Kind_Jump);
- jnode->token = expect_token(parser, token_type);
- jnode->jump = jump_type;
-
- u64 count = 1;
- while (consume_token_if_next(parser, token_type)) count++;
- jnode->count = count;
-
- return (AstNode *) jnode;
-}
-
-static AstNode* parse_statement(OnyxParser* parser) {
- b32 needs_semicolon = 1;
- AstNode* retval = NULL;
-
- switch ((u16) parser->curr->type) {
- case Token_Type_Keyword_Return:
- retval = (AstNode *) parse_return_stmt(parser);
- break;
-
- case '{':
- case Token_Type_Empty_Block:
- case Token_Type_Keyword_Do:
- needs_semicolon = 0;
- retval = (AstNode *) parse_block(parser, 1);
- break;
-
- case Token_Type_Symbol: {
- i32 symbol_res = parse_possible_symbol_declaration(parser, &retval);
- if (symbol_res == 2) needs_semicolon = 0;
- if (symbol_res != 0) break;
-
- // fallthrough
- }
-
- case '(': case '+': case '-': case '!': case '*': case '^':
- case Token_Type_Literal_Integer:
- case Token_Type_Literal_Float:
- case Token_Type_Literal_String:
- retval = (AstNode *) parse_compound_expression(parser, 1);
- break;
-
- case Token_Type_Keyword_If:
- needs_semicolon = 0;
- retval = (AstNode *) parse_if_stmt(parser);
- break;
-
- case Token_Type_Keyword_While:
- needs_semicolon = 0;
- retval = (AstNode *) parse_while_stmt(parser);
- break;
-
- case Token_Type_Keyword_For:
- needs_semicolon = 0;
- retval = (AstNode *) parse_for_stmt(parser);
- break;
-
- case Token_Type_Keyword_Switch:
- needs_semicolon = 0;
- retval = (AstNode *) parse_switch_stmt(parser);
- break;
-
- case Token_Type_Keyword_Break:
- retval = parse_jump_stmt(parser, Token_Type_Keyword_Break, Jump_Type_Break);
- break;
-
- case Token_Type_Keyword_Continue:
- retval = parse_jump_stmt(parser, Token_Type_Keyword_Continue, Jump_Type_Continue);
- break;
-
- case Token_Type_Keyword_Fallthrough:
- retval = parse_jump_stmt(parser, Token_Type_Keyword_Fallthrough, Jump_Type_Fallthrough);
- break;
-
- case Token_Type_Keyword_Defer: {
- needs_semicolon = 0;
-
- AstDefer* defer = make_node(AstDefer, Ast_Kind_Defer);
- defer->token = expect_token(parser, Token_Type_Keyword_Defer);
- defer->stmt = parse_statement(parser);
-
- retval = (AstNode *) defer;
- break;
- }
-
- case Token_Type_Keyword_Use: {
- needs_semicolon = 0;
-
- retval = (AstNode *) parse_use_stmt(parser);
- break;
- }
-
- case '#': {
- if (parse_possible_directive(parser, "context_scope")) {
- AstLocal* context_tmp = make_local(parser->allocator, NULL, builtin_context_variable->type_node);
-
- AstBinaryOp* assignment = make_node(AstBinaryOp, Ast_Kind_Binary_Op);
- assignment->operation = Binary_Op_Assign;
- assignment->left = (AstTyped *) context_tmp;
- assignment->right = builtin_context_variable;
- context_tmp->next = (AstNode *) assignment;
-
- AstBinaryOp* assignment2 = make_node(AstBinaryOp, Ast_Kind_Binary_Op);
- assignment2->operation = Binary_Op_Assign;
- assignment2->left = builtin_context_variable;
- assignment2->right = (AstTyped *) context_tmp;
-
- AstBlock* context_block = parse_block(parser, 1);
- assignment->next = (AstNode *) context_block;
-
- AstDefer* defer_node = make_node(AstDefer, Ast_Kind_Defer);
- defer_node->stmt = (AstNode *) assignment2;
- defer_node->next = context_block->body;
- context_block->body = (AstNode *) defer_node;
-
- needs_semicolon = 0;
- retval = (AstNode *) context_tmp;
- break;
- }
-
- if (next_tokens_are(parser, 2, '#', Token_Type_Keyword_If)) {
- AstIf* static_if = parse_static_if_stmt(parser, 1);
- ENTITY_SUBMIT(static_if);
-
- needs_semicolon = 0;
- retval = (AstNode *) static_if;
- break;
- }
-
- if (parse_possible_directive(parser, "persist")) {
- // :Duplicated from parse_top_level_statement
- AstMemRes* memres = make_node(AstMemRes, Ast_Kind_Memres);
- memres->token = expect_token(parser, Token_Type_Symbol);
- expect_token(parser, ':');
-
- if (parser->curr->type != '=')
- memres->type_node = parse_type(parser);
-
- if (consume_token_if_next(parser, '='))
- memres->initial_value = parse_expression(parser, 1);
-
-
- ENTITY_SUBMIT(memres);
-
- AstBinding* binding = make_node(AstBinding, Ast_Kind_Binding);
- binding->token = memres->token;
- binding->node = (AstNode *) memres;
- ENTITY_SUBMIT(binding);
- break;
- }
-
- if (next_tokens_are(parser, 2, '#', Token_Type_Symbol)) {
- retval = (AstNode *) parse_factor(parser);
- break;
- }
- }
-
- default:
- break;
- }
-
- if (needs_semicolon) expect_token(parser, ';');
-
- return retval;
-}
-
-static AstBlock* parse_block(OnyxParser* parser, b32 make_a_new_scope) {
- AstBlock* block = make_node(AstBlock, Ast_Kind_Block);
- block->rules = Block_Rule_Normal;
-
- // NOTE: --- is for an empty block
- if (parser->curr->type == Token_Type_Empty_Block) {
- block->token = expect_token(parser, Token_Type_Empty_Block);
- return block;
- }
-
- if (make_a_new_scope) {
- block->binding_scope = scope_create(parser->allocator, parser->current_scope, parser->curr->pos);
- parser->current_scope = block->binding_scope;
- }
-
- if (parser->curr->type == Token_Type_Keyword_Do) {
- block->token = expect_token(parser, Token_Type_Keyword_Do);
- block->body = parse_statement(parser);
- if (make_a_new_scope) parser->current_scope = parser->current_scope->parent;
- return block;
- }
-
- block->token = expect_token(parser, '{');
-
- AstNode** next = &block->body;
- AstNode* stmt = NULL;
- while (!consume_token_if_next(parser, '}')) {
- if (parser->hit_unexpected_token) {
- if (make_a_new_scope) parser->current_scope = parser->current_scope->parent;
- return block;
- }
-
- stmt = parse_statement(parser);
-
- if (stmt != NULL && stmt->kind != Ast_Kind_Error) {
- *next = stmt;
-
- while (stmt->next != NULL) stmt = stmt->next;
- next = &stmt->next;
- }
- }
-
- if (make_a_new_scope) parser->current_scope = parser->current_scope->parent;
- return block;
-}
-
-static void parse_polymorphic_variable(OnyxParser* parser, AstType*** next_insertion) {
- bh_arr(AstPolyParam) pv = NULL;
-
- if (parser->polymorph_context.poly_params == NULL)
- onyx_report_error(parser->curr->pos, "Polymorphic variable not valid here.");
- else
- pv = *parser->polymorph_context.poly_params;
-
- consume_token(parser);
-
- AstNode* symbol_node = make_node(AstNode, Ast_Kind_Symbol);
- symbol_node->token = expect_token(parser, Token_Type_Symbol);
-
- **next_insertion = (AstType *) symbol_node;
- *next_insertion = NULL;
-
- if (pv != NULL) {
- bh_arr_push(pv, ((AstPolyParam) {
- .kind = PPK_Poly_Type,
- .poly_sym = symbol_node,
-
- // These will be filled out by function_params()
- .type_expr = NULL,
- .idx = -1,
- }));
-
- *parser->polymorph_context.poly_params = pv;
- }
-}
-
-static AstType* parse_compound_type(OnyxParser* parser) {
- // CLEANUP this is little weird having this here because it means that this parses:
- //
- // foo :: (x: (something_here: i32)) -> void ---
- //
- if (next_tokens_are(parser, 2, Token_Type_Symbol, ':')) {
- consume_tokens(parser, 2);
- }
-
- AstType* first = parse_type(parser);
-
- if (parser->curr->type == ',') {
- AstCompoundType* ctype = make_node(AstCompoundType, Ast_Kind_Type_Compound);
- ctype->token = parser->curr;
-
- bh_arr_new(global_heap_allocator, ctype->types, 2);
- bh_arr_push(ctype->types, first);
-
- while (consume_token_if_next(parser, ',')) {
- if (parser->hit_unexpected_token) return (AstType *) ctype;
-
- if (next_tokens_are(parser, 2, Token_Type_Symbol, ':')) {
- consume_tokens(parser, 2);
- }
-
- bh_arr_push(ctype->types, parse_type(parser));
- }
-
- return (AstType *) ctype;
-
- } else {
- return first;
- }
-}
-
-static AstType* parse_function_type(OnyxParser* parser, OnyxToken* proc_token) {
- bh_arr(AstType *) params = NULL;
- bh_arr_new(global_scratch_allocator, params, 4);
- bh_arr_set_length(params, 0);
-
- expect_token(parser, '(');
- while (!consume_token_if_next(parser, ')')) {
- if (parser->hit_unexpected_token) return NULL;
-
- // NOTE: Allows for names to be put in the function types, just for readability.
- if (next_tokens_are(parser, 2, Token_Type_Symbol, ':')) consume_tokens(parser, 2);
-
- AstType* param_type = parse_type(parser);
- bh_arr_push(params, param_type);
-
- if (parser->curr->type != ')')
- expect_token(parser, ',');
- }
-
- AstType* return_type = (AstType *) &basic_type_void;
- if (consume_token_if_next(parser, Token_Type_Right_Arrow))
- return_type = parse_type(parser);
-
- i64 param_count = bh_arr_length(params);
- AstFunctionType* new = onyx_ast_node_new(parser->allocator,
- sizeof(AstFunctionType) + sizeof(AstType*) * param_count,
- Ast_Kind_Function_Type);
- new->token = proc_token;
- new->param_count = param_count;
- new->return_type = return_type;
-
- if (param_count > 0)
- fori (i, 0, param_count) new->params[i] = params[i];
-
- return (AstType *) new;
-}
-
-static AstType* parse_type(OnyxParser* parser) {
- AstType* root = NULL;
- AstType** next_insertion = &root;
-
- while (1) {
- if (parser->hit_unexpected_token) return root;
-
- switch ((u16) parser->curr->type) {
- case '^': {
- AstPointerType* new = make_node(AstPointerType, Ast_Kind_Pointer_Type);
- new->flags |= Basic_Flag_Pointer;
- new->token = expect_token(parser, '^');
-
- *next_insertion = (AstType *) new;
- next_insertion = &new->elem;
- break;
- }
-
- case '[': {
- AstType *new;
- OnyxToken *open_bracket = expect_token(parser, '[');
-
- if (parser->curr->type == ']') {
- new = make_node(AstSliceType, Ast_Kind_Slice_Type);
- new->token = open_bracket;
-
- } else if (parser->curr->type == Token_Type_Dot_Dot) {
- new = make_node(AstDynArrType, Ast_Kind_DynArr_Type);
- new->token = open_bracket;
- consume_token(parser);
-
- } else {
- new = make_node(AstArrayType, Ast_Kind_Array_Type);
- new->token = open_bracket;
-
- if (parser->curr->type == '$') {
- AstType** insertion = (AstType **) &((AstArrayType *) new)->count_expr;
- parse_polymorphic_variable(parser, &insertion);
- } else {
- ((AstArrayType *) new)->count_expr = parse_expression(parser, 0);
- }
- }
-
- expect_token(parser, ']');
- *next_insertion = (AstType *) new;
- next_insertion = &((AstSliceType *) new)->elem;
- break;
- }
-
- case Token_Type_Keyword_Proc: {
- OnyxToken* proc_token = expect_token(parser, Token_Type_Keyword_Proc);
- onyx_report_warning(proc_token->pos, "Warning: 'proc' is a deprecated keyword.");
- *next_insertion = parse_function_type(parser, proc_token);
- next_insertion = NULL;
- break;
- }
-
- case '$': {
- parse_polymorphic_variable(parser, &next_insertion);
- break;
- }
-
- case Token_Type_Symbol: {
- AstNode* symbol_node = make_node(AstNode, Ast_Kind_Symbol);
- symbol_node->token = expect_token(parser, Token_Type_Symbol);
-
- *next_insertion = (AstType *) symbol_node;
-
- while (consume_token_if_next(parser, '.')) {
- AstFieldAccess* field = make_node(AstFieldAccess, Ast_Kind_Field_Access);
- field->token = expect_token(parser, Token_Type_Symbol);
- field->expr = (AstTyped *) *next_insertion;
-
- *next_insertion = (AstType *) field;
- }
-
- if (parser->curr->type == '(') {
- OnyxToken* paren_token = expect_token(parser, '(');
-
- bh_arr(AstNode *) params = NULL;
- bh_arr_new(global_heap_allocator, params, 2);
-
- while (!consume_token_if_next(parser, ')')) {
- if (parser->hit_unexpected_token) break;
-
- AstNode* t = (AstNode *) parse_type(parser);
- bh_arr_push(params, t);
-
- if (parser->curr->type != ')')
- expect_token(parser, ',');
- }
-
- AstPolyCallType* pc_type = make_node(AstPolyCallType, Ast_Kind_Poly_Call_Type);
- pc_type->token = paren_token;
- pc_type->callee = *next_insertion;
- pc_type->params = params;
-
- *next_insertion = (AstType *) pc_type;
- }
-
- next_insertion = NULL;
- break;
- }
-
- case Token_Type_Keyword_Struct: {
- AstStructType* s_node = parse_struct(parser);
- *next_insertion = (AstType *) s_node;
- next_insertion = NULL;
- break;
- }
-
- case '#': {
- // :ValueDirectiveHack
- if (parse_possible_directive(parser, "value")) {
- // It is very weird to put these here.
- case Token_Type_Literal_Integer:
- case Token_Type_Literal_String:
- case Token_Type_Literal_Float:
- case Token_Type_Literal_True:
- case Token_Type_Literal_False:
- *next_insertion = (AstType *) parse_expression(parser, 0);
- next_insertion = NULL;
- break;
- }
-
- next_insertion = NULL;
- break;
- }
-
- case '(': {
- OnyxToken* matching = find_matching_paren(parser->curr);
-
- // :LinearTokenDependent
- if ((matching + 1)->type == Token_Type_Right_Arrow) {
- *next_insertion = parse_function_type(parser, parser->curr);
-
- } else {
- expect_token(parser, '(');
- *next_insertion = parse_compound_type(parser);
- expect_token(parser, ')');
- }
-
- next_insertion = NULL;
- break;
- }
-
- case Token_Type_Keyword_Typeof: {
- *next_insertion = (AstType *) parse_typeof(parser);
- next_insertion = NULL;
- break;
- }
-
- default:
- onyx_report_error(parser->curr->pos, "unexpected token '%b'.", parser->curr->text, parser->curr->length);
- consume_token(parser);
- break;
- }
-
- if (next_insertion == NULL) break;
- }
-
- return root;
-}
-
-static AstTypeOf* parse_typeof(OnyxParser* parser) {
- OnyxToken* token = expect_token(parser, Token_Type_Keyword_Typeof);
-
- AstTypeOf* type_of = make_node(AstTypeOf, Ast_Kind_Typeof);
- type_of->token = token;
- type_of->expr = parse_expression(parser, 0);
- type_of->resolved_type = NULL;
-
- return type_of;
-}
-
-static AstStructType* parse_struct(OnyxParser* parser) {
- OnyxToken *s_token = expect_token(parser, Token_Type_Keyword_Struct);
-
- AstStructType* s_node;
- AstPolyStructType* poly_struct = NULL;
-
- s_node = make_node(AstStructType, Ast_Kind_Struct_Type);
- s_node->token = s_token;
-
- if (consume_token_if_next(parser, '(')) {
- bh_arr(AstPolyStructParam) poly_params = NULL;
- bh_arr_new(global_heap_allocator, poly_params, 1);
-
- while (!consume_token_if_next(parser, ')')) {
- if (parser->hit_unexpected_token) return NULL;
-
- OnyxToken* sym_token = expect_token(parser, Token_Type_Symbol);
- expect_token(parser, ':');
-
- AstType* param_type = parse_type(parser);
-
- bh_arr_push(poly_params, ((AstPolyStructParam) {
- .token = sym_token,
- .type_node = param_type,
- .type = NULL,
- }));
-
- if (parser->curr->type != ')')
- expect_token(parser, ',');
- }
-
- poly_struct = make_node(AstPolyStructType, Ast_Kind_Poly_Struct_Type);
- poly_struct->token = s_token;
- poly_struct->poly_params = poly_params;
- poly_struct->base_struct = s_node;
- }
-
- bh_arr_new(global_heap_allocator, s_node->members, 4);
-
- while (parser->curr->type == '#') {
- if (parser->hit_unexpected_token) return NULL;
-
- if (parse_possible_directive(parser, "union")) {
- s_node->flags |= Ast_Flag_Struct_Is_Union;
- }
-
- else if (parse_possible_directive(parser, "align")) {
- AstNumLit* numlit = parse_int_literal(parser);
- if (numlit == NULL) return NULL;
-
- s_node->min_alignment = numlit->value.i;
- }
-
- else if (parse_possible_directive(parser, "size")) {
- AstNumLit* numlit = parse_int_literal(parser);
- if (numlit == NULL) return NULL;
-
- s_node->min_size = numlit->value.i;
- }
-
- else {
- OnyxToken* directive_token = expect_token(parser, '#');
- OnyxToken* symbol_token = expect_token(parser, Token_Type_Symbol);
-
- onyx_report_error(directive_token->pos, "unknown directive '#%b'.", symbol_token->text, symbol_token->length);
- }
- }
-
- expect_token(parser, '{');
-
- b32 member_is_used = 0;
- bh_arr(OnyxToken *) member_list_temp = NULL;
- bh_arr_new(global_heap_allocator, member_list_temp, 4);
-
- while (!consume_token_if_next(parser, '}')) {
- if (parser->hit_unexpected_token) return s_node;
-
- member_is_used = consume_token_if_next(parser, Token_Type_Keyword_Use);
-
- if (next_tokens_are(parser, 3, Token_Type_Symbol, ':', ':')) {
- if (!s_node->scope) {
- s_node->scope = scope_create(context.ast_alloc, parser->current_scope, s_node->token->pos);
- parser->current_scope = s_node->scope;
- }
-
- OnyxToken* binding_name = expect_token(parser, Token_Type_Symbol);
- consume_token(parser);
-
- AstBinding* binding = parse_top_level_binding(parser, binding_name);
- if (binding) ENTITY_SUBMIT(binding);
-
- consume_token_if_next(parser, ';');
-
- } else {
- bh_arr_clear(member_list_temp);
- while (!consume_token_if_next(parser, ':')) {
- if (parser->hit_unexpected_token) return NULL;
- bh_arr_push(member_list_temp, expect_token(parser, Token_Type_Symbol));
-
- if (parser->curr->type != ':')
- expect_token(parser, ',');
- }
-
- AstType* member_type = NULL;
- if (parser->curr->type != '=')
- member_type = parse_type(parser);
-
- AstTyped* initial_value = NULL;
- if (consume_token_if_next(parser, '='))
- initial_value = parse_expression(parser, 0);
-
- // RECONSIDER: There are seamingly arbitrary limitations put in place here which do two things:
- // 1. Prevent multiple struct members being used in the same declaration.
- // This makes sense because the members will be of the same type, which means
- // they have the same members. Using both of the members would immediately result
- // in name collisions.
- //
- // 2. Prevent multiple struct members having an initializer set for them.
- // I think the semantics could be confusing either way, so I'm deciding to leave
- // them out of discussion for now. Initialized members should be treated special and
- // deserve their own line.
- if (bh_arr_length(member_list_temp) > 1) {
- if (member_is_used) onyx_report_error((member_list_temp[0] - 1)->pos, "'use' is only allowed for a single struct member declaration. Try splitting this compound declaration into multiple lines.");
- if (initial_value) onyx_report_error(initial_value->token->pos, "Intialized values are only allowed on single struct member declarations. Try splitting this compound initializer into multiple lines.");
- }
-
- bh_arr_each(OnyxToken *, member_name, member_list_temp) {
- AstStructMember* mem = make_node(AstStructMember, Ast_Kind_Struct_Member);
- mem->token = *member_name;
- mem->type_node = member_type;
- mem->initial_value = initial_value;
-
- if (member_is_used) mem->flags |= Ast_Flag_Struct_Mem_Used;
-
- bh_arr_push(s_node->members, mem);
- }
-
- expect_token(parser, ';');
- }
- }
-
- if (s_node->scope) parser->current_scope = parser->current_scope->parent;
-
- bh_arr_free(member_list_temp);
-
- if (poly_struct != NULL) {
- // NOTE: Not a StructType
- return (AstStructType *) poly_struct;
-
- } else {
- return s_node;
- }
-}
-
-static void parse_function_params(OnyxParser* parser, AstFunction* func) {
- expect_token(parser, '(');
-
- if (consume_token_if_next(parser, ')')) return;
-
- u32 param_idx = 0;
- assert(parser->polymorph_context.poly_params != NULL);
-
- b32 param_use = 0;
- b32 param_is_baked = 0;
- OnyxToken* symbol;
- while (!consume_token_if_next(parser, ')')) {
- if (parser->hit_unexpected_token) return;
-
- AstParam curr_param = { 0 };
-
- if (consume_token_if_next(parser, Token_Type_Keyword_Use)) param_use = 1;
- if (consume_token_if_next(parser, '$')) param_is_baked = 1;
-
- symbol = expect_token(parser, Token_Type_Symbol);
- expect_token(parser, ':');
-
- curr_param.vararg_kind = VA_Kind_Not_VA;
- curr_param.local = make_local(parser->allocator, symbol, NULL);
- curr_param.local->kind = Ast_Kind_Param;
-
- if (param_use) {
- curr_param.local->flags |= Ast_Flag_Param_Use;
- param_use = 0;
- }
-
- if (parser->curr->type != '=') {
- if (consume_token_if_next(parser, Token_Type_Dot_Dot)) {
- if (consume_token_if_next(parser, '.')) curr_param.vararg_kind = VA_Kind_Untyped;
- else curr_param.vararg_kind = VA_Kind_Typed;
- }
-
- if (curr_param.vararg_kind != VA_Kind_Untyped) {
- // CLEANUP: This is mess and it is hard to follow what is going on here.
- // I think with recent rewrites, this should be easier to do.
- i32 old_len = bh_arr_length(*parser->polymorph_context.poly_params);
- curr_param.local->type_node = parse_type(parser);
- i32 new_len = bh_arr_length(*parser->polymorph_context.poly_params);
-
- if (curr_param.vararg_kind == VA_Kind_Typed) {
- AstVarArgType* va_type = make_node(AstVarArgType, Ast_Kind_VarArg_Type);
- va_type->elem = curr_param.local->type_node;
- va_type->token = curr_param.local->type_node->token;
- curr_param.local->type_node = (AstType *) va_type;
- }
-
- fori (i, 0, new_len - old_len) {
- (*parser->polymorph_context.poly_params)[old_len + i].type_expr = curr_param.local->type_node;
- (*parser->polymorph_context.poly_params)[old_len + i].idx = param_idx;
- }
- }
- }
-
- if (curr_param.vararg_kind == VA_Kind_Not_VA && consume_token_if_next(parser, '=')) {
- OnyxToken* directive_token = parser->curr;
-
- // :Callsite currently #callsite is only valid as a default value for a funciton parameter.
- if (parse_possible_directive(parser, "callsite")) {
- AstCallSite* cs = make_node(AstCallSite, Ast_Kind_Call_Site);
- cs->token = directive_token;
- curr_param.default_value = (AstTyped *) cs;
-
- } else {
- curr_param.default_value = parse_expression(parser, 0);
- }
- }
-
- if (param_is_baked) {
- param_is_baked = 0;
-
- bh_arr(AstPolyParam) pv = *parser->polymorph_context.poly_params;
- bh_arr_push(pv, ((AstPolyParam) {
- .kind = PPK_Baked_Value,
- .idx = param_idx,
-
- .poly_sym = (AstNode *) curr_param.local,
- .type_expr = curr_param.local->type_node,
- }));
-
- *parser->polymorph_context.poly_params = pv;
- }
-
- bh_arr_push(func->params, curr_param);
- param_idx++;
-
- if (parser->curr->type != ')')
- expect_token(parser, ',');
- }
- return;
-}
-
-static AstOverloadedFunction* parse_overloaded_function(OnyxParser* parser, OnyxToken* token) {
- expect_token(parser, '{');
-
- AstOverloadedFunction* ofunc = make_node(AstOverloadedFunction, Ast_Kind_Overloaded_Function);
- ofunc->token = token;
- ofunc->flags |= Ast_Flag_Comptime;
-
- bh_arr_new(global_heap_allocator, ofunc->overloads, 4);
-
- u64 precedence = 0;
- while (!consume_token_if_next(parser, '}')) {
- if (parser->hit_unexpected_token) return ofunc;
-
- if (parse_possible_directive(parser, "precedence")) {
- AstNumLit* pre = parse_int_literal(parser);
- if (parser->hit_unexpected_token) return ofunc;
-
- precedence = bh_max(pre->value.l, 0);
- }
-
- AstTyped* option = parse_expression(parser, 0);
- add_overload_option(&ofunc->overloads, precedence++, option);
-
- if (parser->curr->type != '}')
- expect_token(parser, ',');
- }
-
- ENTITY_SUBMIT(ofunc);
- return ofunc;
-}
-
-static AstFunction* parse_function_definition(OnyxParser* parser, OnyxToken* token) {
- AstFunction* func_def = make_node(AstFunction, Ast_Kind_Function);
- func_def->token = token;
-
- bh_arr_new(global_heap_allocator, func_def->params, 4);
-
- bh_arr(AstPolyParam) polymorphic_vars = NULL;
- bh_arr_new(global_heap_allocator, polymorphic_vars, 4);
-
- parser->polymorph_context.poly_params = &polymorphic_vars;
- parse_function_params(parser, func_def);
- parser->polymorph_context.poly_params = NULL;
-
- func_def->return_type = (AstType *) &basic_type_void;
- if (consume_token_if_next(parser, Token_Type_Right_Arrow)) {
- if (parse_possible_directive(parser, "auto")) {
- func_def->return_type = (AstType *) &basic_type_auto_return;
- } else {
- func_def->return_type = parse_type(parser);
- }
- }
-
- while (parser->curr->type == '#') {
- if (parse_possible_directive(parser, "intrinsic")) {
- func_def->flags |= Ast_Flag_Intrinsic;
-
- if (parser->curr->type == Token_Type_Literal_String) {
- func_def->intrinsic_name = expect_token(parser, Token_Type_Literal_String);
- }
- }
-
- else if (parse_possible_directive(parser, "foreign")) {
- func_def->foreign_module = expect_token(parser, Token_Type_Literal_String);
- func_def->foreign_name = expect_token(parser, Token_Type_Literal_String);
-
- func_def->flags |= Ast_Flag_Foreign;
- }
-
- // HACK: NullProcHack
- else if (parse_possible_directive(parser, "null")) {
- func_def->flags |= Ast_Flag_Proc_Is_Null;
- }
-
- else {
- OnyxToken* directive_token = expect_token(parser, '#');
- OnyxToken* symbol_token = expect_token(parser, Token_Type_Symbol);
-
- onyx_report_error(directive_token->pos, "unknown directive '#%b'.", symbol_token->text, symbol_token->length);
- }
- }
-
- func_def->body = parse_block(parser, 1);
-
- if (bh_arr_length(polymorphic_vars) > 0) {
- AstPolyProc* pp = make_node(AstPolyProc, Ast_Kind_Polymorphic_Proc);
- pp->token = func_def->token;
- pp->poly_params = polymorphic_vars;
- pp->base_func = func_def;
-
- return (AstFunction *) pp;
-
- } else {
- bh_arr_free(polymorphic_vars);
- return func_def;
- }
-}
-
-static b32 parse_possible_function_definition(OnyxParser* parser, AstTyped** ret) {
- if (parser->curr->type == '(') {
- OnyxToken* matching_paren = find_matching_paren(parser->curr);
- if (matching_paren == NULL) return 0;
-
- // :LinearTokenDependent
- OnyxToken* token_after_paren = matching_paren + 1;
- if (token_after_paren->type != Token_Type_Right_Arrow
- && token_after_paren->type != '{'
- && token_after_paren->type != Token_Type_Keyword_Do
- && token_after_paren->type != Token_Type_Empty_Block)
- return 0;
-
- // :LinearTokenDependent
- b32 is_params = (parser->curr + 1) == matching_paren;
- OnyxToken* tmp_token = parser->curr;
- while (!is_params && tmp_token < matching_paren) {
- if (tmp_token->type == ':') is_params = 1;
-
- tmp_token++;
- }
-
- if (!is_params) return 0;
-
- OnyxToken* proc_token = parser->curr;
- AstFunction* func_node = parse_function_definition(parser, proc_token);
- ENTITY_SUBMIT(func_node);
- *ret = (AstTyped *) func_node;
- return 1;
- }
-
- return 0;
-}
-
-static b32 parse_possible_quick_function_definition(OnyxParser* parser, AstTyped** ret) {
- if (parser->curr->type != '(') return 0;
-
- OnyxToken* matching_paren = find_matching_paren(parser->curr);
- if (matching_paren == NULL) return 0;
-
- // :LinearTokenDependent
- OnyxToken* token_after_paren = matching_paren + 1;
- if (token_after_paren->type != '=' || (token_after_paren + 1)->type != '>')
- return 0;
-
- OnyxToken* proc_token = expect_token(parser, '(');
-
- bh_arr(OnyxToken*) params=NULL;
- bh_arr_new(global_heap_allocator, params, 4);
-
- while (parser->curr->type != ')') {
- if (parser->hit_unexpected_token) return 0;
-
- bh_arr_push(params, expect_token(parser, Token_Type_Symbol));
-
- if (parser->curr->type != ')') {
- expect_token(parser, ',');
- }
- }
-
- expect_token(parser, ')');
- expect_token(parser, '=');
- expect_token(parser, '>');
-
- bh_arr(AstNode*) poly_params=NULL;
- bh_arr_new(global_heap_allocator, poly_params, bh_arr_length(params));
- bh_arr_each(OnyxToken*, param, params) {
- char text[512];
- memset(text, 0, 512);
- strncat(text, "__type_", 511);
- token_toggle_end(*param);
- strncat(text, (*param)->text, 511);
- token_toggle_end(*param);
-
- OnyxToken* new_token = bh_alloc(parser->allocator, sizeof(OnyxToken));
- new_token->type = Token_Type_Symbol;
- new_token->length = 7 + (*param)->length;
- new_token->text = bh_strdup(parser->allocator, text);
- new_token->pos = (*param)->pos;
-
- AstNode* type_node = make_symbol(parser->allocator, new_token);
- bh_arr_push(poly_params, type_node);
- }
-
- AstFunction* func_node = make_node(AstFunction, Ast_Kind_Function);
- AstPolyProc* poly_proc = make_node(AstPolyProc, Ast_Kind_Polymorphic_Proc);
-
- bh_arr_new(global_heap_allocator, func_node->params, bh_arr_length(params));
- fori (i, 0, bh_arr_length(params)) {
- AstLocal* param_local = make_local(parser->allocator, params[i], (AstType *) poly_params[i]);
- param_local->kind = Ast_Kind_Param;
-
- bh_arr_push(func_node->params, ((AstParam) {
- .local = param_local,
- .default_value = NULL,
-
- .vararg_kind = 0,
- .use_processed = 0,
- }));
- }
-
- AstBlock* body_block;
- AstType* return_type;
-
- if (parser->curr->type == '{') {
- body_block = parse_block(parser, 1);
- return_type = (AstType *) &basic_type_auto_return;
-
- } else {
- AstTyped* body = parse_compound_expression(parser, 0);
-
- AstReturn* return_node = make_node(AstReturn, Ast_Kind_Return);
- return_node->token = body->token;
- return_node->expr = body;
-
- body_block = make_node(AstBlock, Ast_Kind_Block);
- body_block->token = body->token;
- body_block->body = (AstNode *) return_node;
-
- AstTypeOf* return_type_of = make_node(AstTypeOf, Ast_Kind_Typeof);
- return_type_of->token = body->token;
- return_type_of->expr = body;
- return_type = (AstType *) return_type_of;
- }
-
- func_node->token = proc_token;
- func_node->body = body_block;
- func_node->return_type = (AstType *) return_type;
-
- poly_proc->token = proc_token;
- bh_arr_new(global_heap_allocator, poly_proc->poly_params, bh_arr_length(params));
- fori (i, 0, bh_arr_length(params)) {
- bh_arr_push(poly_proc->poly_params, ((AstPolyParam) {
- .kind = PSK_Type,
- .idx = i,
- .poly_sym = poly_params[i],
- .type_expr = (AstType *) poly_params[i],
- .type = NULL,
- }));
- }
- poly_proc->base_func = func_node;
-
- ENTITY_SUBMIT(poly_proc);
- *ret = (AstTyped *) poly_proc;
-
- bh_arr_free(params);
- bh_arr_free(poly_params);
- return 1;
-}
-
-static AstTyped* parse_global_declaration(OnyxParser* parser) {
- AstGlobal* global_node = make_node(AstGlobal, Ast_Kind_Global);
- global_node->token = expect_token(parser, Token_Type_Keyword_Global);
-
- while (parser->curr->type == '#') {
- if (parse_possible_directive(parser, "foreign")) {
- global_node->foreign_module = expect_token(parser, Token_Type_Literal_String);
- global_node->foreign_name = expect_token(parser, Token_Type_Literal_String);
-
- global_node->flags |= Ast_Flag_Foreign;
- }
-
- else {
- OnyxToken* directive_token = expect_token(parser, '#');
- OnyxToken* symbol_token = expect_token(parser, Token_Type_Symbol);
-
- onyx_report_error(directive_token->pos, "unknown directive '#%b'.", symbol_token->text, symbol_token->length);
- }
- }
-
- global_node->type_node = parse_type(parser);
-
- ENTITY_SUBMIT(global_node);
-
- return (AstTyped *) global_node;
-}
-
-static AstEnumType* parse_enum_declaration(OnyxParser* parser) {
- AstEnumType* enum_node = make_node(AstEnumType, Ast_Kind_Enum_Type);
- enum_node->token = expect_token(parser, Token_Type_Keyword_Enum);
-
- bh_arr_new(global_heap_allocator, enum_node->values, 4);
-
- while (parser->curr->type == '#') {
- if (parse_possible_directive(parser, "flags")) {
- enum_node->flags |= Ast_Flag_Enum_Is_Flags;
- } else {
- OnyxToken* directive_token = expect_token(parser, '#');
- OnyxToken* symbol_token = expect_token(parser, Token_Type_Symbol);
-
- onyx_report_error(directive_token->pos, "unknown directive '#%b'.", symbol_token->text, symbol_token->length);
- }
- }
-
- AstType* backing = (AstType *) &basic_type_u32;
- if (consume_token_if_next(parser, '(')) {
- AstNode* backing_sym = make_node(AstNode, Ast_Kind_Symbol);
- backing_sym->token = expect_token(parser, Token_Type_Symbol);
- backing = (AstType *) backing_sym;
-
- expect_token(parser, ')');
- }
- enum_node->backing = backing;
-
- expect_token(parser, '{');
-
- while (!consume_token_if_next(parser, '}')) {
- if (parser->hit_unexpected_token) return enum_node;
-
- AstEnumValue* evalue = make_node(AstEnumValue, Ast_Kind_Enum_Value);
- evalue->token = expect_token(parser, Token_Type_Symbol);
- evalue->type_node = (AstType *) enum_node;
-
- if (consume_token_if_next(parser, ':')) {
- expect_token(parser, ':');
-
- // TODO: Make this work for any expression.
- evalue->value = parse_int_literal(parser);
- }
-
- expect_token(parser, ';');
-
- bh_arr_push(enum_node->values, evalue);
- }
-
- return enum_node;
-}
-
-static AstIf* parse_static_if_stmt(OnyxParser* parser, b32 parse_block_as_statements) {
- AstIf* static_if_node = make_node(AstIf, Ast_Kind_Static_If);
- static_if_node->token = expect_token(parser, '#');
- expect_token(parser, Token_Type_Keyword_If);
-
- static_if_node->cond = parse_expression(parser, 0);
-
- bh_arr_new(global_heap_allocator, static_if_node->true_entities, 2);
- bh_arr_push(parser->alternate_entity_placement_stack, &static_if_node->true_entities);
-
- if (parse_block_as_statements) {
- static_if_node->true_stmt = parse_block(parser, 0);
-
- } else {
- expect_token(parser, '{');
- while (!consume_token_if_next(parser, '}')) {
- if (parser->hit_unexpected_token) return static_if_node;
-
- parse_top_level_statement(parser);
- }
- }
-
- bh_arr_pop(parser->alternate_entity_placement_stack);
-
- if (consume_token_if_next(parser, Token_Type_Keyword_Else)) {
- bh_arr_new(global_heap_allocator, static_if_node->false_entities, 2);
- bh_arr_push(parser->alternate_entity_placement_stack, &static_if_node->false_entities);
-
- if (parse_block_as_statements) {
- static_if_node->false_stmt = parse_block(parser, 0);
-
- } else {
- expect_token(parser, '{');
- while (!consume_token_if_next(parser, '}')) {
- if (parser->hit_unexpected_token) return static_if_node;
-
- parse_top_level_statement(parser);
- }
- }
-
- bh_arr_pop(parser->alternate_entity_placement_stack);
- }
-
- return static_if_node;
-}
-
-static AstMacro* parse_macro(OnyxParser* parser) {
- AstMacro* macro = make_node(AstMacro, Ast_Kind_Macro);
- macro->token = expect_token(parser, Token_Type_Keyword_Macro);
-
- // First try quick function
- if (!parse_possible_quick_function_definition(parser, ¯o->body)) {
- // Otherwise, do a normal function
- macro->body = (AstTyped *) parse_function_definition(parser, macro->token);
- }
-
- ENTITY_SUBMIT(macro);
- return macro;
-}
-
-static AstTyped* parse_top_level_expression(OnyxParser* parser) {
- if (parser->curr->type == Token_Type_Keyword_Global) return parse_global_declaration(parser);
- if (parser->curr->type == Token_Type_Keyword_Struct) return (AstTyped *) parse_struct(parser);
- if (parser->curr->type == Token_Type_Keyword_Enum) return (AstTyped *) parse_enum_declaration(parser);
- if (parser->curr->type == Token_Type_Keyword_Macro) return (AstTyped *) parse_macro(parser);
-
- if (parse_possible_directive(parser, "type")) {
- AstTypeAlias* alias = make_node(AstTypeAlias, Ast_Kind_Type_Alias);
- alias->to = parse_type(parser);
- return (AstTyped *) alias;
- }
-
- if (parse_possible_directive(parser, "match")) {
- // :LinearTokenDependent
- OnyxToken* directive_token = parser->curr - 2;
- AstOverloadedFunction* ofunc = parse_overloaded_function(parser, directive_token);
- return (AstTyped *) ofunc;
- }
-
- return parse_expression(parser, 1);
-}
-
-static AstBinding* parse_top_level_binding(OnyxParser* parser, OnyxToken* symbol) {
- expect_token(parser, ':');
-
- AstTyped* node = parse_top_level_expression(parser);
- if (parser->hit_unexpected_token || node == NULL)
- return NULL;
-
- // CLEANUP
- if (node->kind == Ast_Kind_Function) {
- AstFunction* func = (AstFunction *) node;
-
- if (func->intrinsic_name == NULL)
- func->intrinsic_name = symbol;
-
- func->name = symbol;
-
- } else if (node->kind == Ast_Kind_Polymorphic_Proc) {
- AstPolyProc* proc = (AstPolyProc *) node;
-
- if (proc->base_func->intrinsic_name == NULL)
- proc->base_func->intrinsic_name = symbol;
-
- proc->base_func->name = symbol;
-
- } else if (node->kind == Ast_Kind_Macro) {
- AstMacro* macro = (AstMacro *) node;
-
- AstFunction* func = (AstFunction *) macro->body;
- if (func->kind == Ast_Kind_Polymorphic_Proc)
- func = (AstFunction *) ((AstPolyProc *) func)->base_func;
-
- func->name = symbol;
-
- } else if (node->kind == Ast_Kind_Global) {
- AstGlobal* global = (AstGlobal *) node;
-
- global->name = symbol;
-
- } else if (node->kind != Ast_Kind_Overloaded_Function
- && node->kind != Ast_Kind_StrLit) {
-
- if (node->kind == Ast_Kind_Struct_Type
- || node->kind == Ast_Kind_Enum_Type
- || node->kind == Ast_Kind_Poly_Struct_Type) {
- ((AstStructType *)node)->name = bh_aprintf(global_heap_allocator,
- "%b", symbol->text, symbol->length);
- }
-
- if (node->kind == Ast_Kind_Type_Alias) node->token = symbol;
- else if (node_is_type((AstNode *) node));
- else if (node->kind == Ast_Kind_Package);
- else if (node->kind == Ast_Kind_NumLit);
- else {
- AstAlias* alias = make_node(AstAlias, Ast_Kind_Alias);
- alias->token = node->token;
- alias->alias = node;
- node = (AstTyped *) alias;
- }
-
- // HACK: This should maybe be entered elsewhere?
- ENTITY_SUBMIT(node);
- }
-
- AstBinding* binding = make_node(AstBinding, Ast_Kind_Binding);
- binding->token = symbol;
- binding->node = (AstNode *) node;
-
- return binding;
-}
-
-static void parse_top_level_statement(OnyxParser* parser) {
- AstFlags private_kind = 0;
- if (bh_arr_length(parser->scope_flags) > 0)
- private_kind = bh_arr_last(parser->scope_flags);
-
- // :CLEANUP this very repetetive code...
- if (parse_possible_directive(parser, "private")) {
- private_kind = Ast_Flag_Private_Package;
- if (parser->curr->type == '{') {
- bh_arr_push(parser->scope_flags, private_kind);
-
- expect_token(parser, '{');
- parse_top_level_statements_until(parser, '}');
- expect_token(parser, '}');
-
- bh_arr_pop(parser->scope_flags);
- return;
- }
- }
- else if (parse_possible_directive(parser, "private_file")) {
- private_kind = Ast_Flag_Private_File;
- if (parser->curr->type == '{') {
- bh_arr_push(parser->scope_flags, private_kind);
-
- expect_token(parser, '{');
- parse_top_level_statements_until(parser, '}');
- expect_token(parser, '}');
-
- bh_arr_pop(parser->scope_flags);
- return;
- }
- }
-
- AstBinding* binding = NULL;
-
- switch ((u16) parser->curr->type) {
- case Token_Type_Keyword_Use: {
- AstNode* use_node = parse_use_stmt(parser);
- if (use_node) ENTITY_SUBMIT(use_node);
- return;
- }
-
- case Token_Type_Symbol: {
- OnyxToken* symbol = expect_token(parser, Token_Type_Symbol);
- expect_token(parser, ':');
-
- if (parser->curr->type == ':') {
- binding = parse_top_level_binding(parser, symbol);
- if (binding != NULL) binding->flags |= private_kind;
-
- goto submit_binding_to_entities;
- }
-
- AstMemRes* memres = make_node(AstMemRes, Ast_Kind_Memres);
- memres->token = symbol;
-
- if (parser->curr->type != '=')
- memres->type_node = parse_type(parser);
-
- if (consume_token_if_next(parser, '='))
- memres->initial_value = parse_expression(parser, 1);
-
-
- ENTITY_SUBMIT(memres);
-
- binding = make_node(AstBinding, Ast_Kind_Binding);
- binding->token = symbol;
- binding->flags |= private_kind;
- binding->node = (AstNode *) memres;
-
- goto submit_binding_to_entities;
- }
-
- case '#': {
- if (next_tokens_are(parser, 2, '#', Token_Type_Keyword_If)) {
- AstIf* static_if = parse_static_if_stmt(parser, 0);
- ENTITY_SUBMIT(static_if);
- return;
- }
-
- OnyxToken* dir_token = parser->curr;
-
- if (parse_possible_directive(parser, "load")) {
- AstInclude* include = make_node(AstInclude, Ast_Kind_Load_File);
- include->token = dir_token;
-
- OnyxToken* str_token = expect_token(parser, Token_Type_Literal_String);
- if (str_token != NULL) {
- token_toggle_end(str_token);
- include->name = bh_strdup(parser->allocator, str_token->text);
- token_toggle_end(str_token);
- }
-
- ENTITY_SUBMIT(include);
- return;
- }
- else if (parse_possible_directive(parser, "load_path")) {
- AstInclude* include = make_node(AstInclude, Ast_Kind_Load_Path);
- include->token = dir_token;
-
- OnyxToken* str_token = expect_token(parser, Token_Type_Literal_String);
- if (str_token != NULL) {
- token_toggle_end(str_token);
- include->name = bh_strdup(parser->allocator, str_token->text);
- token_toggle_end(str_token);
- }
-
- ENTITY_SUBMIT(include);
- return;
- }
- else if (parse_possible_directive(parser, "error")) {
- AstDirectiveError *error = make_node(AstDirectiveError, Ast_Kind_Directive_Error);
- error->token = dir_token;
- error->error_msg = expect_token(parser, Token_Type_Literal_String);
-
- ENTITY_SUBMIT(error);
- return;
- }
- else if (parse_possible_directive(parser, "operator")) {
- AstDirectiveOperator *operator = make_node(AstDirectiveOperator, Ast_Kind_Directive_Operator);
- operator->token = dir_token;
-
- BinaryOp op = binary_op_from_token_type(parser->curr->type);
- consume_token(parser);
- if (op == Binary_Op_Subscript) expect_token(parser, ']'); // #operator [] ... needs to consume the other ']'
-
- if (op == Binary_Op_Count) {
- onyx_report_error(parser->curr->pos, "Invalid binary operator.");
- } else {
- operator->operator = op;
- }
-
- operator->overload = parse_expression(parser, 0);
-
- ENTITY_SUBMIT(operator);
- return;
- }
- else if (parse_possible_directive(parser, "add_overload") || parse_possible_directive(parser, "add_match")) {
- AstDirectiveAddOverload *add_overload = make_node(AstDirectiveAddOverload, Ast_Kind_Directive_Add_Overload);
- add_overload->token = dir_token;
- add_overload->overloaded_function = (AstNode *) parse_expression(parser, 0);
-
- expect_token(parser, ',');
-
- if (parse_possible_directive(parser, "precedence")) {
- AstNumLit* pre = parse_int_literal(parser);
- if (parser->hit_unexpected_token) return;
-
- add_overload->precedence = bh_max(pre->value.l, 0);
- } else {
- add_overload->precedence = 0;
- }
-
- add_overload->overload = parse_expression(parser, 0);
-
- ENTITY_SUBMIT(add_overload);
- return;
- }
- else if (parse_possible_directive(parser, "export")) {
- AstDirectiveExport *export = make_node(AstDirectiveExport, Ast_Kind_Directive_Export);
- export->token = dir_token;
- export->export_name = expect_token(parser, Token_Type_Literal_String);
-
- export->export = parse_expression(parser, 0);
-
- ENTITY_SUBMIT(export);
- return;
- }
- else {
- OnyxToken* directive_token = expect_token(parser, '#');
- OnyxToken* symbol_token = expect_token(parser, Token_Type_Symbol);
-
- onyx_report_error(directive_token->pos, "unknown directive '#%b'.", symbol_token->text, symbol_token->length);
- return;
- }
- }
-
- default: break;
- }
-
- expect_token(parser, ';');
- return;
-
-submit_binding_to_entities:
- {
- if (!binding) return;
-
- Scope* target_scope = parser->package->scope;
-
- if (binding->flags & Ast_Flag_Private_Package)
- target_scope = parser->package->private_scope;
- if (binding->flags & Ast_Flag_Private_File)
- target_scope = parser->file_scope;
-
- ENTITY_SUBMIT_IN_SCOPE(binding, target_scope);
- }
-}
-
-static AstPackage* parse_package_expression(OnyxParser* parser) {
- AstPackage* package_node = make_node(AstPackage, Ast_Kind_Package);
- package_node->token = expect_token(parser, Token_Type_Keyword_Package);
-
- bh_arr_new(global_heap_allocator, package_node->path, 2);
-
- while (parser->curr->type == Token_Type_Symbol) {
- if (parser->hit_unexpected_token) return package_node;
-
- OnyxToken* symbol = expect_token(parser, Token_Type_Symbol);
-
- bh_arr_push(package_node->path, symbol);
-
- if (consume_token_if_next(parser, '.'));
- else break;
- }
-
- i32 total_package_name_length = 0;
- bh_arr_each(OnyxToken *, token, package_node->path) {
- total_package_name_length += (*token)->length + 1;
- }
-
- char* package_name = bh_alloc_array(context.ast_alloc, char, total_package_name_length);
- *package_name = '\0';
-
- bh_arr_each(OnyxToken *, token, package_node->path) {
- token_toggle_end(*token);
- strncat(package_name, (*token)->text, total_package_name_length - 1);
- token_toggle_end(*token);
-
- if (token != &bh_arr_last(package_node->path)) {
- strncat(package_name, ".", total_package_name_length - 1);
- }
- }
-
- package_node->package_name = package_name;
- package_node->package = package_lookup(package_name);
-
- return package_node;
-}
-
-static Package* parse_file_package(OnyxParser* parser) {
- if (parser->curr->type != Token_Type_Keyword_Package) {
- return package_lookup_or_create("main", context.global_scope, parser->allocator);
- }
-
- AstPackage* package_node = parse_package_expression(parser);
-
- char aggregate_name[2048];
- aggregate_name[0] = '\0';
-
- Package* prevpackage = NULL;
-
- bh_arr_each(OnyxToken *, symbol, package_node->path) {
- token_toggle_end(*symbol);
-
- strncat(aggregate_name, (*symbol)->text, 2047);
- Package* newpackage = package_lookup_or_create(aggregate_name, context.global_scope, parser->allocator);
-
- AstPackage* pnode = make_node(AstPackage, Ast_Kind_Package);
- pnode->token = *symbol;
- pnode->package = newpackage;
- pnode->package_name = newpackage->name;
-
- if (prevpackage != NULL) {
- symbol_subpackage_introduce(prevpackage->scope, (*symbol)->text, pnode);
- package_reinsert_use_packages(prevpackage);
- }
-
- token_toggle_end(*symbol);
- strncat(aggregate_name, ".", 2047);
-
- prevpackage = newpackage;
- }
-
- package_node->package = prevpackage;
-
- return package_node->package;
-}
-
-static void parse_top_level_statements_until(OnyxParser* parser, TokenType tt) {
- while (parser->curr->type != tt) {
- if (parser->hit_unexpected_token) break;
- if (onyx_has_errors()) break;
- parse_top_level_statement(parser);
- }
-}
-
-
-// NOTE: This returns a void* so I don't need to cast it everytime I use it
-void* onyx_ast_node_new(bh_allocator alloc, i32 size, AstKind kind) {
- void* node = bh_alloc(alloc, size);
-
- memset(node, 0, size);
- *(AstKind *) node = kind;
-
- return node;
-}
-
-OnyxParser onyx_parser_create(bh_allocator alloc, OnyxTokenizer *tokenizer) {
- OnyxParser parser;
-
- parser.allocator = alloc;
- parser.tokenizer = tokenizer;
- parser.curr = tokenizer->tokens;
- parser.prev = NULL;
- parser.hit_unexpected_token = 0;
- parser.current_scope = NULL;
- parser.alternate_entity_placement_stack = NULL;
- parser.scope_flags = NULL;
-
- parser.polymorph_context = (PolymorphicContext) {
- .root_node = NULL,
- .poly_params = NULL,
- };
-
- bh_arr_new(global_heap_allocator, parser.alternate_entity_placement_stack, 4);
- bh_arr_new(global_heap_allocator, parser.scope_flags, 4);
-
- return parser;
-}
-
-void onyx_parser_free(OnyxParser* parser) {
-}
-
-void onyx_parse(OnyxParser *parser) {
- // NOTE: Skip comments at the beginning of the file
- while (consume_token_if_next(parser, Token_Type_Comment) || consume_token_if_next(parser, Token_Type_Note));
-
- parser->package = parse_file_package(parser);
- parser->file_scope = scope_create(parser->allocator, parser->package->private_scope, parser->tokenizer->tokens[0].pos);
- parser->current_scope = parser->file_scope;
-
- AstUse* implicit_use_builtin = make_node(AstUse, Ast_Kind_Use);
- AstPackage* implicit_builtin_package = make_node(AstPackage, Ast_Kind_Package);
- implicit_builtin_package->package_name = "builtin";
- implicit_use_builtin->expr = (AstTyped *) implicit_builtin_package;
- ENTITY_SUBMIT(implicit_use_builtin);
-
- parse_top_level_statements_until(parser, Token_Type_End_Stream);
-
- parser->current_scope = parser->current_scope->parent;
-}
+++ /dev/null
-#define BH_DEBUG
-#include "onyxparser.h"
-#include "onyxutils.h"
-#include "onyxastnodes.h"
-#include "onyxerrors.h"
-
-// Variables used during the symbol resolution phase.
-static Scope* curr_scope = NULL;
-static b32 report_unresolved_symbols = 1;
-
-// Everything related to waiting on is imcomplete at the moment.
-static Entity* waiting_on = NULL;
-
-#define SYMRES(kind, ...) do { \
- SymresStatus ss = symres_ ## kind (__VA_ARGS__); \
- if (ss > Symres_Errors_Start) return ss; \
- } while (0)
-
-typedef enum SymresStatus {
- Symres_Success,
- Symres_Complete,
-
- Symres_Errors_Start,
- Symres_Yield_Macro,
- Symres_Yield_Micro,
- Symres_Error,
-} SymresStatus;
-
-static SymresStatus symres_type(AstType** type);
-static SymresStatus symres_local(AstLocal** local);
-static SymresStatus symres_call(AstCall* call);
-static SymresStatus symres_size_of(AstSizeOf* so);
-static SymresStatus symres_align_of(AstAlignOf* so);
-static SymresStatus symres_field_access(AstFieldAccess** fa);
-static SymresStatus symres_compound(AstCompound* compound);
-static SymresStatus symres_expression(AstTyped** expr);
-static SymresStatus symres_return(AstReturn* ret);
-static SymresStatus symres_if(AstIfWhile* ifnode);
-static SymresStatus symres_while(AstIfWhile* whilenode);
-static SymresStatus symres_for(AstFor* fornode);
-static SymresStatus symres_switch(AstSwitch* switchnode);
-static SymresStatus symres_use(AstUse* use);
-static SymresStatus symres_directive_solidify(AstDirectiveSolidify** psolid);
-static SymresStatus symres_directive_defined(AstDirectiveDefined** pdefined);
-static SymresStatus symres_directive_insert(AstDirectiveInsert* insert);
-static SymresStatus symres_statement_chain(AstNode** walker);
-static SymresStatus symres_statement(AstNode** stmt, b32 *remove);
-static SymresStatus symres_block(AstBlock* block);
-static SymresStatus symres_function_header(AstFunction* func);
-static SymresStatus symres_function(AstFunction* func);
-static SymresStatus symres_global(AstGlobal* global);
-static SymresStatus symres_overloaded_function(AstOverloadedFunction* ofunc);
-static SymresStatus symres_package(AstPackage* package);
-static SymresStatus symres_enum(AstEnumType* enum_node);
-static SymresStatus symres_memres_type(AstMemRes** memres);
-static SymresStatus symres_memres(AstMemRes** memres);
-static SymresStatus symres_struct_defaults(AstType* st);
-static SymresStatus symres_static_if(AstIf* static_if);
-static SymresStatus symres_macro(AstMacro* macro);
-
-static void scope_enter(Scope* new_scope) {
- curr_scope = new_scope;
-}
-
-static void scope_leave() {
- curr_scope = curr_scope->parent;
-}
-
-static SymresStatus symres_symbol(AstNode** symbol_node) {
- OnyxToken* token = (*symbol_node)->token;
- AstNode* res = symbol_resolve(curr_scope, token);
-
- if (!res) { // :SymresStall
- if (report_unresolved_symbols) {
- onyx_report_error(token->pos,
- "Unable to resolve symbol '%b'",
- token->text,
- token->length);
-
- return Symres_Error;
- } else {
- return Symres_Yield_Macro;
- }
-
- } else {
- *symbol_node = res;
- }
-
- return Symres_Success;
-}
-
-static SymresStatus symres_struct_type(AstStructType* s_node) {
- if (s_node->flags & Ast_Flag_Type_Is_Resolved) return Symres_Success;
-
- s_node->flags |= Ast_Flag_Type_Is_Resolved;
-
- if (s_node->scope) {
- // FIX: This is probably wrong for the long term.
- s_node->scope->parent = curr_scope;
-
- scope_enter(s_node->scope);
- }
-
- fori (i, 0, bh_arr_length(s_node->members)) {
- AstStructMember *member = s_node->members[i];
-
- if (member->type_node) {
- SymresStatus ss = symres_type(&member->type_node);
- if (ss != Symres_Success) {
- s_node->flags &= ~Ast_Flag_Type_Is_Resolved;
- if (s_node->scope) scope_leave();
- return ss;
- }
-
- if (!node_is_type((AstNode *) member->type_node)) {
- onyx_report_error(member->token->pos, "Member type is not a type.");
- goto struct_symres_done;
- }
-
- if (member->flags & Ast_Flag_Struct_Mem_Used) {
- AstType *used = (AstType *) member->type_node;
-
- while (used->kind == Ast_Kind_Type_Alias) {
- used = ((AstTypeAlias *) used)->to;
- }
-
- b32 use_works = (used->kind == Ast_Kind_Struct_Type || used->kind == Ast_Kind_Poly_Call_Type);
-
- if (used->kind == Ast_Kind_Type_Raw_Alias) {
- AstTypeRawAlias* alias = (AstTypeRawAlias *) used;
- use_works = (alias->to->kind == Type_Kind_Struct);
- }
-
- if (!use_works) {
- onyx_report_error(member->token->pos,
- "Can only 'use' members of struct type, got '%s'.",
- onyx_ast_node_kind_string(used->kind));
- goto struct_symres_done;
- }
- }
- }
- }
-
-struct_symres_done:
- if (s_node->scope) scope_leave();
- return Symres_Success;
-}
-
-static SymresStatus symres_type(AstType** type) {
- // Don't make this kill all symbol resolution if the type is null.
- if (!type || !*type) return Symres_Success;
-
- switch ((*type)->kind) {
- case Ast_Kind_Symbol: SYMRES(symbol, (AstNode **) type); break;
- case Ast_Kind_Basic_Type: break;
- case Ast_Kind_Type_Alias: SYMRES(type, &((AstTypeAlias *) *type)->to); break;
- case Ast_Kind_Field_Access: {
- SYMRES(field_access, (AstFieldAccess **) type);
-
- if (!node_is_type((AstNode *) *type))
- onyx_report_error((*type)->token->pos, "Field access did not result in a type. (%s)", onyx_ast_node_kind_string((*type)->kind));
- break;
- }
-
- case Ast_Kind_Pointer_Type: SYMRES(type, &((AstPointerType *) *type)->elem); break;
- case Ast_Kind_Slice_Type: SYMRES(type, &((AstSliceType *) *type)->elem); break;
- case Ast_Kind_DynArr_Type: SYMRES(type, &((AstDynArrType *) *type)->elem); break;
- case Ast_Kind_VarArg_Type: SYMRES(type, &((AstVarArgType *) *type)->elem); break;
-
- case Ast_Kind_Function_Type: {
- AstFunctionType* ftype = (AstFunctionType *) *type;
-
- SYMRES(type, &ftype->return_type);
-
- if (ftype->param_count > 0) {
- fori (i, 0, (i64) ftype->param_count) {
- SYMRES(type, &ftype->params[i]);
- }
- }
- break;
- }
-
- case Ast_Kind_Struct_Type: SYMRES(struct_type, (AstStructType *) *type); break;
- case Ast_Kind_Array_Type: {
- AstArrayType* a_node = (AstArrayType *) *type;
-
- if (a_node->count_expr) SYMRES(expression, &a_node->count_expr);
- SYMRES(type, &a_node->elem);
- break;
- }
-
- case Ast_Kind_Enum_Type: break;
-
- case Ast_Kind_Poly_Struct_Type: {
- AstPolyStructType* pst_node = (AstPolyStructType *) *type;
- pst_node->scope = scope_create(context.ast_alloc, pst_node->entity->scope, pst_node->token->pos);
-
- bh_arr_each(AstPolyStructParam, param, pst_node->poly_params) {
- SYMRES(type, ¶m->type_node);
- param->type = type_build_from_ast(context.ast_alloc, param->type_node);
- }
- break;
- }
-
- case Ast_Kind_Poly_Call_Type: {
- AstPolyCallType* pc_node = (AstPolyCallType *) *type;
-
- SYMRES(type, &pc_node->callee);
-
- bh_arr_each(AstNode *, param, pc_node->params) {
- if (node_is_type(*param)) {
- SYMRES(type, (AstType **) param);
- } else {
- SYMRES(expression, (AstTyped **) param);
- }
- }
- break;
- }
-
- case Ast_Kind_Type_Compound: {
- AstCompoundType* ctype = (AstCompoundType *) *type;
-
- bh_arr_each(AstType *, type, ctype->types) SYMRES(type, type);
- break;
- }
-
- case Ast_Kind_Alias: {
- AstAlias* alias = (AstAlias *) *type;
- SYMRES(type, (AstType **) &alias->alias);
- break;
- }
-
- case Ast_Kind_Typeof: {
- AstTypeOf* type_of = (AstTypeOf *) *type;
- SYMRES(expression, &type_of->expr);
- break;
- }
- }
-
- return Symres_Success;
-}
-
-static SymresStatus symres_local(AstLocal** local) {
- SYMRES(type, &(*local)->type_node);
-
- if ((*local)->token != NULL)
- symbol_introduce(curr_scope, (*local)->token, (AstNode *) *local);
-
- return Symres_Success;
-}
-
-static SymresStatus symres_arguments(Arguments* args) {
- bh_arr_each(AstTyped *, arg, args->values)
- SYMRES(expression, arg);
-
- bh_arr_each(AstNamedValue *, named_arg, args->named_values)
- SYMRES(expression, &(*named_arg)->value);
-
- return Symres_Success;
-}
-
-static SymresStatus symres_call(AstCall* call) {
- SYMRES(expression, (AstTyped **) &call->callee);
- SYMRES(arguments, &call->args);
-
- return Symres_Success;
-}
-
-static SymresStatus symres_size_of(AstSizeOf* so) {
- SYMRES(type, &so->type_node);
- SYMRES(type, &so->so_ast_type);
-
- return Symres_Success;
-}
-
-static SymresStatus symres_align_of(AstAlignOf* ao) {
- SYMRES(type, &ao->type_node);
- SYMRES(type, &ao->ao_ast_type);
-
- return Symres_Success;
-}
-
-static SymresStatus symres_field_access(AstFieldAccess** fa) {
- if ((*fa)->expr == NULL) return Symres_Error;
- SYMRES(expression, &(*fa)->expr);
- if ((*fa)->expr == NULL) return Symres_Error;
-
- AstTyped* expr = (AstTyped *) strip_aliases((AstNode *) (*fa)->expr);
-
- AstNode* resolution = try_symbol_resolve_from_node((AstNode *) expr, (*fa)->token);
- if (resolution) *((AstNode **) fa) = resolution;
- else if (expr->kind == Ast_Kind_Package) {
- if (report_unresolved_symbols) {
- onyx_report_error((*fa)->token->pos, "'%b' was not found in package '%s'. Perhaps it is defined in a file that wasn't loaded?",
- (*fa)->token->text,
- (*fa)->token->length,
- ((AstPackage *) (*fa)->expr)->package->name);
- return Symres_Error;
- } else {
- return Symres_Yield_Macro;
- }
- }
-
- return Symres_Success;
-}
-
-static SymresStatus symres_compound(AstCompound* compound) {
- bh_arr_each(AstTyped *, expr, compound->exprs) {
- SYMRES(expression, expr);
- }
-
- return Symres_Success;
-}
-
-static SymresStatus symres_if_expression(AstIfExpression* if_expr) {
- SYMRES(expression, &if_expr->cond);
- SYMRES(expression, &if_expr->true_expr);
- SYMRES(expression, &if_expr->false_expr);
- return Symres_Success;
-}
-
-static SymresStatus symres_pipe(AstBinaryOp** pipe) {
- AstCall* call_node = (AstCall *) (*pipe)->right;
- SYMRES(expression, (AstTyped **) &call_node);
- SYMRES(expression, &(*pipe)->left);
-
- if (call_node->kind != Ast_Kind_Call) {
- onyx_report_error((*pipe)->token->pos, "Pipe operator expected call on right side.");
- return Symres_Error;
- }
-
- if ((*pipe)->left == NULL) return Symres_Error;
-
- bh_arr_insertn(call_node->args.values, 0, 1);
- call_node->args.values[0] = (AstTyped *) make_argument(context.ast_alloc, (*pipe)->left);
- call_node->next = (*pipe)->next;
-
- // NOTE: Not a BinaryOp node
- *pipe = (AstBinaryOp *) call_node;
-
- return Symres_Success;
-}
-
-// CLEANUP: This is an experimental feature and might be removed in the future.
-// I noticed a common pattern when writing in Onyx is something that looks like this:
-//
-// foo.member_function(^foo, ...)
-//
-// I decided it would be worth adding a bit of syntactic sugar for such as call. I
-// decided to use the '->' operator for this purpose. The snippet below is the exact
-// same as the snippet above (after the nodes have been processed by the function below)
-//
-// foo->member_function(...)
-static SymresStatus symres_method_call(AstBinaryOp** mcall) {
- AstCall* call_node = (AstCall *) (*mcall)->right;
- if (call_node->kind != Ast_Kind_Call) {
- onyx_report_error((*mcall)->token->pos, "'->' expected procedure call on right side.");
- return Symres_Error;
- }
-
- SYMRES(expression, &(*mcall)->left);
- if ((*mcall)->left == NULL) return Symres_Error;
-
- if (((*mcall)->flags & Ast_Flag_Has_Been_Symres) == 0) {
- AstFieldAccess* implicit_field_access = make_field_access(context.ast_alloc, (*mcall)->left, NULL);
- implicit_field_access->token = call_node->callee->token;
- call_node->callee = (AstTyped *) implicit_field_access;
- }
-
- SYMRES(expression, (AstTyped **) &call_node);
- (*mcall)->flags |= Ast_Flag_Has_Been_Symres;
-
- return Symres_Success;
-}
-
-static SymresStatus symres_unaryop(AstUnaryOp** unaryop) {
- if ((*unaryop)->operation == Unary_Op_Cast) {
- SYMRES(type, &(*unaryop)->type_node);
- }
-
- SYMRES(expression, &(*unaryop)->expr);
-
- return Symres_Success;
-}
-
-static SymresStatus symres_struct_literal(AstStructLiteral* sl) {
- if (sl->stnode != NULL) SYMRES(expression, &sl->stnode);
- SYMRES(type, (AstType **) &sl->stnode);
-
- sl->type_node = (AstType *) sl->stnode;
- while (sl->type_node && sl->type_node->kind == Ast_Kind_Type_Alias)
- sl->type_node = ((AstTypeAlias *) sl->type_node)->to;
-
- SYMRES(arguments, &sl->args);
-
- return Symres_Success;
-}
-
-static SymresStatus symres_array_literal(AstArrayLiteral* al) {
- if (al->atnode != NULL) SYMRES(expression, &al->atnode);
- SYMRES(type, (AstType **) &al->atnode);
-
- al->type_node = (AstType *) al->atnode;
- while (al->type_node && al->type_node->kind == Ast_Kind_Type_Alias)
- al->type_node = ((AstTypeAlias *) al->type_node)->to;
-
- bh_arr_each(AstTyped *, expr, al->values)
- SYMRES(expression, expr);
-
- return Symres_Success;
-}
-
-static SymresStatus symres_expression(AstTyped** expr) {
- if (node_is_type((AstNode *) *expr)) {
- SYMRES(type, (AstType **) expr);
- return Symres_Success;
- }
-
- switch ((*expr)->kind) {
- case Ast_Kind_Symbol: SYMRES(symbol, (AstNode **) expr); break;
-
- case Ast_Kind_Binary_Op:
- SYMRES(expression, &((AstBinaryOp *)(*expr))->left);
- SYMRES(expression, &((AstBinaryOp *)(*expr))->right);
- break;
-
- case Ast_Kind_Unary_Op: SYMRES(unaryop, (AstUnaryOp **) expr); break;
- case Ast_Kind_Call: SYMRES(call, (AstCall *) *expr); break;
- case Ast_Kind_Argument: SYMRES(expression, &((AstArgument *) *expr)->value); break;
- case Ast_Kind_Block: SYMRES(block, (AstBlock *) *expr); break;
- case Ast_Kind_Address_Of: SYMRES(expression, &((AstAddressOf *)(*expr))->expr); break;
- case Ast_Kind_Dereference: SYMRES(expression, &((AstDereference *)(*expr))->expr); break;
- case Ast_Kind_Field_Access: SYMRES(field_access, (AstFieldAccess **) expr); break;
- case Ast_Kind_Pipe: SYMRES(pipe, (AstBinaryOp **) expr); break;
- case Ast_Kind_Method_Call: SYMRES(method_call, (AstBinaryOp **) expr); break;
- case Ast_Kind_Size_Of: SYMRES(size_of, (AstSizeOf *)*expr); break;
- case Ast_Kind_Align_Of: SYMRES(align_of, (AstAlignOf *)*expr); break;
- case Ast_Kind_Alias: SYMRES(expression, &((AstAlias *) *expr)->alias); break;
-
- case Ast_Kind_Range_Literal:
- SYMRES(expression, &((AstRangeLiteral *)(*expr))->low);
- SYMRES(expression, &((AstRangeLiteral *)(*expr))->high);
-
- SYMRES(type, &builtin_range_type);
- (*expr)->type_node = builtin_range_type;
-
- // NOTE: This is a weird place to put this so maybe put it somewhere else eventually
- // - brendanfh 2020/09/04
- builtin_range_type_type = type_build_from_ast(context.ast_alloc, builtin_range_type);
- break;
-
- case Ast_Kind_Function:
- case Ast_Kind_NumLit:
- SYMRES(type, &(*expr)->type_node);
- break;
-
- case Ast_Kind_StrLit:
- SYMRES(type, &builtin_string_type);
- (*expr)->type_node = builtin_string_type;
- break;
-
- case Ast_Kind_Slice:
- case Ast_Kind_Subscript:
- SYMRES(expression, &((AstSubscript *)(*expr))->addr);
- SYMRES(expression, &((AstSubscript *)(*expr))->expr);
- break;
-
- case Ast_Kind_Struct_Literal:
- SYMRES(struct_literal, (AstStructLiteral *)(*expr));
- break;
-
- case Ast_Kind_Array_Literal:
- SYMRES(array_literal, (AstArrayLiteral *)(*expr));
- break;
-
- case Ast_Kind_Directive_Solidify:
- SYMRES(directive_solidify, (AstDirectiveSolidify **) expr);
- break;
-
- case Ast_Kind_Directive_Defined:
- SYMRES(directive_defined, (AstDirectiveDefined **) expr);
- break;
-
- case Ast_Kind_Compound:
- SYMRES(compound, (AstCompound *) *expr);
- break;
-
- case Ast_Kind_Package:
- SYMRES(package, (AstPackage *) *expr);
- break;
-
- case Ast_Kind_If_Expression:
- SYMRES(if_expression, (AstIfExpression *) *expr);
- break;
-
- case Ast_Kind_Directive_Insert:
- SYMRES(directive_insert, (AstDirectiveInsert *) *expr);
- break;
-
- case Ast_Kind_Do_Block:
- SYMRES(block, ((AstDoBlock *) *expr)->block);
- break;
-
- default: break;
- }
-
- return Symres_Success;
-}
-
-static SymresStatus symres_return(AstReturn* ret) {
- if (ret->expr)
- SYMRES(expression, &ret->expr);
-
- return Symres_Success;
-}
-
-static SymresStatus symres_if(AstIfWhile* ifnode) {
- if (ifnode->kind == Ast_Kind_Static_If) {
- if ((ifnode->flags & Ast_Flag_Static_If_Resolved) == 0) {
- return Symres_Yield_Macro;
- }
-
- if (static_if_resolution(ifnode)) {
- if (ifnode->true_stmt != NULL) SYMRES(statement, (AstNode **) &ifnode->true_stmt, NULL);
-
- } else {
- if (ifnode->false_stmt != NULL) SYMRES(statement, (AstNode **) &ifnode->false_stmt, NULL);
- }
-
- } else {
- if (ifnode->initialization != NULL) {
- ifnode->scope = scope_create(context.ast_alloc, curr_scope, ifnode->token->pos);
- scope_enter(ifnode->scope);
-
- SYMRES(statement_chain, &ifnode->initialization);
- }
-
- SYMRES(expression, &ifnode->cond);
-
- // NOTE: These are statements because "elseif" means the `false_stmt` has an if node.
- if (ifnode->true_stmt != NULL) SYMRES(statement, (AstNode **) &ifnode->true_stmt, NULL);
- if (ifnode->false_stmt != NULL) SYMRES(statement, (AstNode **) &ifnode->false_stmt, NULL);
-
- if (ifnode->initialization != NULL) scope_leave();
- }
-
- return Symres_Success;
-}
-
-static SymresStatus symres_while(AstIfWhile* whilenode) {
- if (whilenode->initialization != NULL) {
- whilenode->scope = scope_create(context.ast_alloc, curr_scope, whilenode->token->pos);
- scope_enter(whilenode->scope);
-
- SYMRES(statement_chain, &whilenode->initialization);
- }
-
- SYMRES(expression, &whilenode->cond);
-
- if (whilenode->true_stmt) SYMRES(block, whilenode->true_stmt);
- if (whilenode->false_stmt) SYMRES(block, whilenode->false_stmt);
-
- if (whilenode->initialization != NULL) scope_leave();
-
- return Symres_Success;
-}
-
-static SymresStatus symres_for(AstFor* fornode) {
- fornode->scope = scope_create(context.ast_alloc, curr_scope, fornode->token->pos);
- scope_enter(fornode->scope);
- SYMRES(expression, &fornode->iter);
- SYMRES(local, &fornode->var);
- SYMRES(block, fornode->stmt);
- scope_leave();
-
- return Symres_Success;
-}
-
-static SymresStatus symres_switch(AstSwitch* switchnode) {
- if (switchnode->initialization != NULL) {
- switchnode->scope = scope_create(context.ast_alloc, curr_scope, switchnode->token->pos);
- scope_enter(switchnode->scope);
-
- SYMRES(statement_chain, &switchnode->initialization);
- }
-
- SYMRES(expression, &switchnode->expr);
-
- bh_arr_each(AstSwitchCase, sc, switchnode->cases) {
- bh_arr_each(AstTyped *, value, sc->values)
- SYMRES(expression, value);
-
- SYMRES(block, sc->block);
- }
-
- if (switchnode->default_case)
- SYMRES(block, switchnode->default_case);
-
- if (switchnode->initialization != NULL) scope_leave();
-
- return Symres_Success;
-}
-
-// CLEANUP: A lot of duplication going on in this function. A proper
-// "namespace" concept would be useful to remove a lot of the fluff
-// code here. There already is try_resolve_symbol_from_node which
-// may be able to do what is needed here?
-static SymresStatus symres_use(AstUse* use) {
- SYMRES(expression, &use->expr);
-
- if (use->expr->kind == Ast_Kind_Package) {
- AstPackage* package = (AstPackage *) use->expr;
- SYMRES(package, package);
-
- if (package->package->scope == curr_scope) return Symres_Success;
-
- if (use->only == NULL) {
- OnyxFilePos pos = { 0 };
- if (use->token != NULL)
- pos = use->token->pos;
-
- scope_include(curr_scope, package->package->scope, pos);
-
- } else {
- bh_arr_each(QualifiedUse, qu, use->only) {
- AstNode* thing = symbol_resolve(package->package->scope, qu->symbol_name);
- if (thing == NULL) { // :SymresStall
- if (report_unresolved_symbols) {
- onyx_report_error(qu->symbol_name->pos,
- "The symbol '%b' was not found in this package.",
- qu->symbol_name->text, qu->symbol_name->length);
- return Symres_Error;
- } else {
- return Symres_Yield_Macro;
- }
- }
-
- symbol_introduce(curr_scope, qu->as_name, thing);
- }
- }
-
- package_track_use_package(package->package, use->entity);
-
- return Symres_Success;
- }
-
- if (use->expr->kind == Ast_Kind_Enum_Type) {
- AstEnumType* et = (AstEnumType *) use->expr;
-
- bh_arr_each(AstEnumValue *, ev, et->values)
- symbol_introduce(curr_scope, (*ev)->token, (AstNode *) *ev);
-
- return Symres_Success;
- }
-
- if (use->expr->kind == Ast_Kind_Struct_Type) {
- AstStructType* st = (AstStructType *) use->expr;
- if (!st->scope) return Symres_Success;
-
- if (use->only == NULL) {
- scope_include(curr_scope, st->scope, use->token->pos);
-
- } else {
- bh_arr_each(QualifiedUse, qu, use->only) {
- AstNode* thing = symbol_resolve(st->scope, qu->symbol_name);
- if (thing == NULL) {
- onyx_report_error(qu->symbol_name->pos,
- "The symbol '%b' was not found in this scope.",
- qu->symbol_name->text, qu->symbol_name->length);
- return Symres_Error;
- }
-
- symbol_introduce(curr_scope, qu->as_name, thing);
- }
- }
-
- return Symres_Success;
- }
-
- if (use->expr->type_node == NULL && use->expr->type == NULL) goto cannot_use;
-
- AstType* effective_type = use->expr->type_node;
- if (effective_type->kind == Ast_Kind_Pointer_Type)
- effective_type = ((AstPointerType *) effective_type)->elem;
-
- if (effective_type->kind == Ast_Kind_Struct_Type ||
- effective_type->kind == Ast_Kind_Poly_Call_Type) {
-
- if (use->expr->type == NULL)
- use->expr->type = type_build_from_ast(context.ast_alloc, use->expr->type_node);
- if (use->expr->type == NULL) goto cannot_use;
-
- Type* st = use->expr->type;
- if (st->kind == Type_Kind_Pointer)
- st = st->Pointer.elem;
-
- bh_table_each_start(StructMember, st->Struct.members);
- AstFieldAccess* fa = make_field_access(context.ast_alloc, use->expr, value.name);
- symbol_raw_introduce(curr_scope, value.name, use->token->pos, (AstNode *) fa);
- bh_table_each_end;
-
- return Symres_Success;
- }
-
-cannot_use:
- onyx_report_error(use->token->pos, "Cannot use this because its type is unknown.");
- return Symres_Error;
-}
-
-static SymresStatus symres_directive_solidify(AstDirectiveSolidify** psolid) {
- AstDirectiveSolidify* solid = *psolid;
- if (solid->resolved_proc != NULL) {
- *psolid = (AstDirectiveSolidify *) solid->resolved_proc;
- return Symres_Success;
- }
-
- SYMRES(expression, (AstTyped **) &solid->poly_proc);
- if (solid->poly_proc && solid->poly_proc->kind == Ast_Kind_Directive_Solidify) {
- AstPolyProc* potentially_resolved_proc = (AstPolyProc *) ((AstDirectiveSolidify *) solid->poly_proc)->resolved_proc;
- if (!potentially_resolved_proc) return Symres_Yield_Micro;
-
- solid->poly_proc = potentially_resolved_proc;
- }
-
- if (!solid->poly_proc || solid->poly_proc->kind != Ast_Kind_Polymorphic_Proc) {
- onyx_report_error(solid->token->pos, "Expected polymorphic procedure in #solidify directive.");
- return Symres_Error;
- }
-
- bh_arr_each(AstPolySolution, sln, solid->known_polyvars) {
- // HACK: This assumes that 'ast_type' and 'value' are at the same offset.
- SYMRES(expression, &sln->value);
- if (onyx_has_errors()) return Symres_Error;
-
- if (node_is_type((AstNode *) sln->value)) {
- sln->type = type_build_from_ast(context.ast_alloc, sln->ast_type);
- sln->kind = PSK_Type;
- } else {
- sln->kind = PSK_Value;
- }
-
- if (onyx_has_errors()) return Symres_Error;
- }
-
- solid->resolved_proc = polymorphic_proc_try_solidify(solid->poly_proc, solid->known_polyvars, solid->token);
-
- // NOTE: Not a DirectiveSolidify.
- *psolid = (AstDirectiveSolidify *) solid->resolved_proc;
- return Symres_Success;
-}
-
-static SymresStatus symres_directive_defined(AstDirectiveDefined** pdefined) {
- AstDirectiveDefined* defined = *pdefined;
-
- b32 use_package_count = (context.entities.type_count[Entity_Type_Use_Package] == 0);
- b32 old_report_unresolved_symbols = report_unresolved_symbols;
- report_unresolved_symbols = 0;
-
- SymresStatus ss = symres_expression(&defined->expr);
- if ((use_package_count || old_report_unresolved_symbols) && ss != Symres_Success) {
- // The symbol definitely was not found and there is no chance that it could be found.
- defined->is_defined = 0;
-
- } else {
- if (ss == Symres_Success) {
- defined->is_defined = 1;
-
- } else {
- report_unresolved_symbols = old_report_unresolved_symbols;
- return Symres_Yield_Macro;
- }
- }
-
- report_unresolved_symbols = old_report_unresolved_symbols;
- return Symres_Success;
-}
-
-static SymresStatus symres_directive_insert(AstDirectiveInsert* insert) {
- SYMRES(expression, &insert->code_expr);
- return Symres_Success;
-}
-
-static SymresStatus symres_statement(AstNode** stmt, b32 *remove) {
- if (remove) *remove = 0;
-
- switch ((*stmt)->kind) {
- case Ast_Kind_Return: SYMRES(return, (AstReturn *) *stmt); break;
- case Ast_Kind_If: SYMRES(if, (AstIfWhile *) *stmt); break;
- case Ast_Kind_Static_If: SYMRES(if, (AstIfWhile *) *stmt); break;
- case Ast_Kind_While: SYMRES(while, (AstIfWhile *) *stmt); break;
- case Ast_Kind_For: SYMRES(for, (AstFor *) *stmt); break;
- case Ast_Kind_Switch: SYMRES(switch, (AstSwitch *) *stmt); break;
- case Ast_Kind_Call: SYMRES(call, (AstCall *) *stmt); break;
- case Ast_Kind_Argument: SYMRES(expression, (AstTyped **) &((AstArgument *) *stmt)->value); break;
- case Ast_Kind_Block: SYMRES(block, (AstBlock *) *stmt); break;
- case Ast_Kind_Defer: SYMRES(statement, &((AstDefer *) *stmt)->stmt, NULL); break;
- case Ast_Kind_Jump: break;
-
- case Ast_Kind_Local:
- // if (remove) *remove = 1;
- SYMRES(local, (AstLocal **) stmt);
- break;
-
- case Ast_Kind_Use:
- if (remove) *remove = 1;
- SYMRES(use, (AstUse *) *stmt);
- break;
-
- default: SYMRES(expression, (AstTyped **) stmt); break;
- }
-
- return Symres_Success;
-}
-
-static SymresStatus symres_statement_chain(AstNode** walker) {
- b32 remove = 0;
-
- while (*walker) {
- SYMRES(statement, walker, &remove);
- if (remove) {
- remove = 0;
- AstNode* tmp = (*walker)->next;
- (*walker)->next = NULL;
- (*walker) = tmp;
-
- } else {
- walker = &(*walker)->next;
- }
- }
- return Symres_Success;
-}
-
-static SymresStatus symres_block(AstBlock* block) {
- if (block->rules & Block_Rule_New_Scope) {
- if (block->scope == NULL)
- block->scope = scope_create(context.ast_alloc, curr_scope, block->token->pos);
-
- scope_enter(block->scope);
- }
-
- if (block->binding_scope != NULL)
- scope_include(curr_scope, block->binding_scope, block->token->pos);
-
- if (block->body)
- SYMRES(statement_chain, &block->body);
-
- if (block->rules & Block_Rule_New_Scope)
- scope_leave();
-
- return Symres_Success;
-}
-
-SymresStatus symres_function_header(AstFunction* func) {
- func->flags |= Ast_Flag_Comptime;
-
- if (func->scope == NULL)
- func->scope = scope_create(context.ast_alloc, curr_scope, func->token->pos);
-
- scope_enter(func->scope);
-
- bh_arr_each(AstParam, param, func->params) {
- if (param->default_value != NULL) {
- SYMRES(expression, ¶m->default_value);
- if (onyx_has_errors()) return Symres_Error;
- }
- }
-
- if ((func->flags & Ast_Flag_Params_Introduced) == 0) {
- bh_arr_each(AstParam, param, func->params) {
- symbol_introduce(curr_scope, param->local->token, (AstNode *) param->local);
- }
-
- func->flags |= Ast_Flag_Params_Introduced;
- }
-
- bh_arr_each(AstParam, param, func->params) {
- if (param->local->type_node != NULL) {
- SYMRES(type, ¶m->local->type_node);
- }
- }
-
- SYMRES(type, &func->return_type);
- if (!node_is_type((AstNode *) func->return_type)) {
- AstType* return_type = (AstType *) strip_aliases((AstNode *) func->return_type);
- if (return_type->kind == Ast_Kind_Symbol) return Symres_Yield_Macro;
-
- onyx_report_error(func->token->pos, "Return type is not a type.");
- }
-
- scope_leave();
-
- return Symres_Success;
-}
-
-SymresStatus symres_function(AstFunction* func) {
- if (func->scope == NULL)
- func->scope = scope_create(context.ast_alloc, curr_scope, func->token->pos);
- if (func->entity_header && func->entity_header->state < Entity_State_Check_Types) return Symres_Yield_Macro;
-
- scope_enter(func->scope);
-
- if ((func->flags & Ast_Flag_Has_Been_Symres) == 0) {
- bh_arr_each(AstParam, param, func->params) {
- // CLEANUP: Currently, in order to 'use' parameters, the type must be completely
- // resolved and built. This is excessive because all that should need to be known
- // is the names of the members, since all that happens is implicit field accesses
- // are placed in the scope. So instead, there should be a way to just query all the
- // member names in the structure, without needing to know their type. This would be
- // easy if it were not for 'use' statements in structs. It is made even more complicated
- // by this situtation:
- //
- // Foo :: struct (T: type_expr) {
- // use t : T;
- //
- // something_else := 5 + 6 * 8;
- // }
- //
- // The 'use t : T' member requires completely knowing the type of T, to know which
- // members should be brought in. At the moment, that requires completely building the
- // type of Foo($T).
- if ((param->local->flags & Ast_Flag_Param_Use) != 0 && param->use_processed == 0) {
- if (param->local->type_node != NULL && param->local->type == NULL) {
- param->local->type = type_build_from_ast(context.ast_alloc, param->local->type_node);
-
- if (param->local->type == NULL) {
- // HACK HACK HACK
- scope_leave();
- return Symres_Yield_Macro;
- }
- }
-
- if (type_is_struct(param->local->type)) {
- Type* st;
- if (param->local->type->kind == Type_Kind_Struct) {
- st = param->local->type;
- } else {
- st = param->local->type->Pointer.elem;
- }
-
- bh_table_each_start(StructMember, st->Struct.members);
- AstFieldAccess* fa = make_field_access(context.ast_alloc, (AstTyped *) param->local, value.name);
- symbol_raw_introduce(curr_scope, value.name, param->local->token->pos, (AstNode *) fa);
- bh_table_each_end;
-
- param->use_processed = 1;
-
- } else if (param->local->type != NULL) {
- onyx_report_error(param->local->token->pos, "Can only 'use' structures or pointers to structures.");
-
- } else {
- // :ExplicitTyping
- onyx_report_error(param->local->token->pos, "Cannot deduce type of parameter '%b'; Try adding it explicitly.",
- param->local->token->text,
- param->local->token->length);
- }
- }
- }
-
- func->flags |= Ast_Flag_Has_Been_Symres;
- }
-
- SYMRES(block, func->body);
-
- scope_leave();
- return Symres_Success;
-}
-
-static SymresStatus symres_global(AstGlobal* global) {
- SYMRES(type, &global->type_node);
- return Symres_Success;
-}
-
-static SymresStatus symres_overloaded_function(AstOverloadedFunction* ofunc) {
- bh_arr_each(OverloadOption, overload, ofunc->overloads) {
- SYMRES(expression, &overload->option);
- }
- return Symres_Success;
-}
-
-static SymresStatus symres_package(AstPackage* package) {
- if (package->package == NULL) {
- if (!package->package_name) return Symres_Error;
-
- package->package = package_lookup(package->package_name);
- }
-
- if (package->package) {
- return Symres_Success;
- } else {
- if (report_unresolved_symbols) {
- onyx_report_error(package->token->pos,
- "Package '%s' not found in included source files.",
- package->package_name);
- return Symres_Error;
- } else {
- return Symres_Yield_Macro;
- }
- }
-}
-
-static SymresStatus symres_enum(AstEnumType* enum_node) {
- if (enum_node->backing->kind == Ast_Kind_Symbol) SYMRES(symbol, (AstNode **) &enum_node->backing);
- if (enum_node->backing == NULL) return Symres_Error;
-
- enum_node->backing_type = type_build_from_ast(context.ast_alloc, enum_node->backing);
- enum_node->scope = scope_create(context.ast_alloc, NULL, enum_node->token->pos);
-
- type_build_from_ast(context.ast_alloc, (AstType *) enum_node);
-
- u64 next_assign_value = (enum_node->flags & Ast_Flag_Enum_Is_Flags) ? 1 : 0;
- bh_arr_each(AstEnumValue *, value, enum_node->values) {
- symbol_introduce(enum_node->scope, (*value)->token, (AstNode *) *value);
- (*value)->type = enum_node->etcache;
-
- if ((*value)->value != NULL) {
- // HACK
- resolve_expression_type((AstTyped *) (*value)->value);
- if (type_is_small_integer((*value)->value->type)) {
- next_assign_value = (*value)->value->value.i;
- } else if (type_is_integer((*value)->value->type)) {
- next_assign_value = (*value)->value->value.l;
- } else {
- onyx_report_error((*value)->token->pos, "expected numeric integer literal for enum initialization");
- return Symres_Error;
- }
-
- (*value)->value->type = enum_node->etcache;
-
- } else {
- AstNumLit* num = make_int_literal(context.ast_alloc, next_assign_value);
- num->type = enum_node->etcache;
-
- (*value)->value = num;
- }
-
- (*value)->flags |= Ast_Flag_Comptime;
-
- if (enum_node->flags & Ast_Flag_Enum_Is_Flags) {
- next_assign_value <<= 1;
- } else {
- next_assign_value++;
- }
- }
- return Symres_Success;
-}
-
-static SymresStatus symres_memres_type(AstMemRes** memres) {
- SYMRES(type, &(*memres)->type_node);
- return Symres_Success;
-}
-
-static SymresStatus symres_memres(AstMemRes** memres) {
- if ((*memres)->initial_value != NULL) {
- SYMRES(expression, &(*memres)->initial_value);
- }
- return Symres_Success;
-}
-
-static SymresStatus symres_struct_defaults(AstType* t) {
- if (t->kind != Ast_Kind_Struct_Type) return Symres_Error;
-
- AstStructType* st = (AstStructType *) t;
- if (st->scope) scope_enter(st->scope);
-
- bh_arr_each(AstStructMember *, smem, st->members) {
- if ((*smem)->initial_value != NULL) {
- SYMRES(expression, &(*smem)->initial_value);
- }
- }
-
- if (st->scope) scope_leave();
- return Symres_Success;
-}
-
-static SymresStatus symres_polyproc(AstPolyProc* pp) {
- pp->flags |= Ast_Flag_Comptime;
- pp->poly_scope = curr_scope;
-
- bh_arr_each(AstPolyParam, param, pp->poly_params) {
- if (param->kind != PPK_Baked_Value) continue;
-
- // FIX: Looking up symbols immediately in the type of the baked value does not always work
- // because I think the following should be possible:
- //
- // baked_proc :: (x: $T, $f: (T) -> T) -> T ...
- //
- // The type of 'f' depends on resolving the value for the polyvar 'T'.
- SYMRES(type, ¶m->type_expr);
- }
-
- return Symres_Success;
-}
-
-static SymresStatus symres_static_if(AstIf* static_if) {
- SYMRES(expression, &static_if->cond);
- return Symres_Success;
-}
-
-static SymresStatus symres_process_directive(AstNode* directive) {
- switch (directive->kind) {
- case Ast_Kind_Directive_Add_Overload: {
- AstDirectiveAddOverload *add_overload = (AstDirectiveAddOverload *) directive;
-
- SYMRES(expression, (AstTyped **) &add_overload->overloaded_function);
- if (add_overload->overloaded_function == NULL) return Symres_Error; // NOTE: Error message will already be generated
-
- if (add_overload->overloaded_function->kind != Ast_Kind_Overloaded_Function) {
- onyx_report_error(add_overload->token->pos, "#add_overload directive did not resolve to an overloaded function.");
-
- } else {
- AstOverloadedFunction* ofunc = (AstOverloadedFunction *) add_overload->overloaded_function;
- SYMRES(expression, (AstTyped **) &add_overload->overload);
- add_overload_option(&ofunc->overloads, add_overload->precedence, add_overload->overload);
- }
-
- break;
- }
-
- case Ast_Kind_Directive_Operator: {
- AstDirectiveOperator *operator = (AstDirectiveOperator *) directive;
- SYMRES(expression, &operator->overload);
- if (!operator->overload) return Symres_Error;
-
- AstFunction* overload = get_function_from_node((AstNode *) operator->overload);
- if (overload == NULL) {
- onyx_report_error(operator->token->pos, "This cannot be used as an operator overload.");
- return Symres_Error;
- }
-
- if (bh_arr_length(overload->params) != 2) {
- onyx_report_error(operator->token->pos, "Expected 2 exactly arguments for binary operator overload.");
- return Symres_Error;
- }
-
- /*if (binop_is_assignment(operator->operator)) {
- onyx_report_error(overload->token->pos, "'%s' is not currently overloadable.", binaryop_string[operator->operator]);
- return Symres_Error;
- }*/
-
- add_overload_option(&operator_overloads[operator->operator], 0, operator->overload);
- break;
- }
-
- case Ast_Kind_Directive_Export: {
- AstDirectiveExport *export = (AstDirectiveExport *) directive;
- SYMRES(expression, &export->export);
-
- export->export->flags |= Ast_Flag_Exported;
-
- if (export->export->kind == Ast_Kind_Function) {
- AstFunction *func = (AstFunction *) export->export;
- func->exported_name = export->export_name;
-
- if ((func->flags & Ast_Flag_Exported) != 0) {
- if ((func->flags & Ast_Flag_Foreign) != 0) {
- onyx_report_error(export->token->pos, "exporting a foreign function");
- return Symres_Error;
- }
-
- if ((func->flags & Ast_Flag_Intrinsic) != 0) {
- onyx_report_error(export->token->pos, "exporting a intrinsic function");
- return Symres_Error;
- }
-
- // NOTE: This should never happen
- if (func->exported_name == NULL) {
- onyx_report_error(export->token->pos, "exporting function without a name");
- return Symres_Error;
- }
- }
- }
-
- break;
- }
- }
-
- return Symres_Success;
-}
-
-static SymresStatus symres_macro(AstMacro* macro) {
- if (macro->body->kind == Ast_Kind_Function) {
- SYMRES(function_header, (AstFunction *) macro->body);
- }
- else if (macro->body->kind == Ast_Kind_Polymorphic_Proc) {
- SYMRES(polyproc, (AstPolyProc *) macro->body);
- }
-
- return Symres_Success;
-}
-
-void symres_entity(Entity* ent) {
- Scope* old_scope = NULL;
- if (ent->scope) {
- old_scope = curr_scope;
- scope_enter(ent->scope);
- }
-
- report_unresolved_symbols = context.cycle_detected;
- //(context.entities.type_count[Entity_Type_Static_If] == 0 &&
- // context.entities.type_count[Entity_Type_Use_Package] == 0)
- //|| context.cycle_detected;
-
- SymresStatus ss = Symres_Success;
- EntityState next_state = Entity_State_Check_Types;
-
- switch (ent->type) {
- case Entity_Type_Binding: {
- symbol_introduce(curr_scope, ent->binding->token, ent->binding->node);
- package_reinsert_use_packages(ent->package);
- next_state = Entity_State_Finalized;
- break;
- }
-
- case Entity_Type_Static_If: ss = symres_static_if(ent->static_if); break;
-
- case Entity_Type_Foreign_Function_Header:
- case Entity_Type_Function_Header: ss = symres_function_header(ent->function); break;
- case Entity_Type_Function: ss = symres_function(ent->function); break;
-
- case Entity_Type_Foreign_Global_Header:
- case Entity_Type_Global_Header: ss = symres_global(ent->global); break;
-
- case Entity_Type_Use_Package:
- case Entity_Type_Use: ss = symres_use(ent->use);
- next_state = Entity_State_Finalized;
- break;
-
- case Entity_Type_Overloaded_Function: ss = symres_overloaded_function(ent->overloaded_function); break;
- case Entity_Type_Expression: ss = symres_expression(&ent->expr); break;
- case Entity_Type_Type_Alias: ss = symres_type(&ent->type_alias); break;
- case Entity_Type_Enum: ss = symres_enum(ent->enum_type); break;
- case Entity_Type_Memory_Reservation_Type: ss = symres_memres_type(&ent->mem_res); break;
- case Entity_Type_Memory_Reservation: ss = symres_memres(&ent->mem_res); break;
- case Entity_Type_Polymorphic_Proc: ss = symres_polyproc(ent->poly_proc); break;
- case Entity_Type_String_Literal: ss = symres_expression(&ent->expr); break;
- case Entity_Type_Struct_Member_Default: ss = symres_struct_defaults((AstType *) ent->type_alias); break;
- case Entity_Type_Process_Directive: ss = symres_process_directive((AstNode *) ent->expr); break;
- case Entity_Type_Macro: ss = symres_macro(ent->macro); break;
-
- default: break;
- }
-
- if (ss == Symres_Yield_Macro) ent->macro_attempts++;
- if (ss == Symres_Yield_Micro) ent->micro_attempts++;
- if (ss == Symres_Success) {
- ent->macro_attempts = 0;
- ent->micro_attempts = 0;
- ent->state = next_state;
- }
-
- if (ent->scope) curr_scope = old_scope;
-}
+++ /dev/null
-#define BH_DEBUG
-#include "onyxtypes.h"
-#include "onyxastnodes.h"
-#include "onyxutils.h"
-#include "onyxerrors.h"
-
-// NOTE: These have to be in the same order as Basic
-Type basic_types[] = {
- { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_void, { Basic_Kind_Void, 0, 0, 1, "void" } },
-
- { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_bool, { Basic_Kind_Bool, Basic_Flag_Boolean, 1, 1, "bool" } },
-
- { Type_Kind_Basic, 0, 0, NULL, { Basic_Kind_Int_Unsized, Basic_Flag_Integer, 0, 0, "unsized int" } },
- { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_i8, { Basic_Kind_I8, Basic_Flag_Integer, 1, 1, "i8" } },
- { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_u8, { Basic_Kind_U8, Basic_Flag_Integer | Basic_Flag_Unsigned, 1, 1, "u8" } },
- { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_i16, { Basic_Kind_I16, Basic_Flag_Integer, 2, 2, "i16" } },
- { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_u16, { Basic_Kind_U16, Basic_Flag_Integer | Basic_Flag_Unsigned, 2, 2, "u16" } },
- { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_i32, { Basic_Kind_I32, Basic_Flag_Integer, 4, 4, "i32" } },
- { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_u32, { Basic_Kind_U32, Basic_Flag_Integer | Basic_Flag_Unsigned, 4, 4, "u32" } },
- { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_i64, { Basic_Kind_I64, Basic_Flag_Integer, 8, 8, "i64" } },
- { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_u64, { Basic_Kind_U64, Basic_Flag_Integer | Basic_Flag_Unsigned, 8, 8, "u64" } },
-
- { Type_Kind_Basic, 0, 0, NULL, { Basic_Kind_Float_Unsized, Basic_Flag_Float, 0, 0, "unsized float" } },
- { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_f32, { Basic_Kind_F32, Basic_Flag_Float, 4, 4, "f32" } },
- { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_f64, { Basic_Kind_F64, Basic_Flag_Float, 8, 4, "f64" } },
-
- { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_rawptr, { Basic_Kind_Rawptr, Basic_Flag_Pointer, 8, 8, "rawptr" } },
-
- { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_i8x16, { Basic_Kind_I8X16, Basic_Flag_SIMD, 16, 16, "i8x16" } },
- { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_i16x8, { Basic_Kind_I16X8, Basic_Flag_SIMD, 16, 16, "i16x8" } },
- { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_i32x4, { Basic_Kind_I32X4, Basic_Flag_SIMD, 16, 16, "i32x4" } },
- { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_i64x2, { Basic_Kind_I64X2, Basic_Flag_SIMD, 16, 16, "i64x2" } },
- { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_f32x4, { Basic_Kind_F32X4, Basic_Flag_SIMD, 16, 16, "f32x4" } },
- { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_f64x2, { Basic_Kind_F64X2, Basic_Flag_SIMD, 16, 16, "f64x2" } },
- { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_v128, { Basic_Kind_V128, Basic_Flag_SIMD, 16, 16, "v128" } },
-
- { Type_Kind_Basic, 0, 0, NULL, { Basic_Kind_Type_Index, Basic_Flag_Type_Index, 4, 4, "type_expr" } },
-};
-
-// TODO: Document this!!
- bh_imap type_map;
-static bh_imap type_pointer_map;
-static bh_imap type_slice_map;
-static bh_imap type_dynarr_map;
-static bh_imap type_vararg_map;
-static bh_table(u64) type_func_map;
-
-static Type* type_create(TypeKind kind, bh_allocator a, u32 extra_type_pointer_count) {
- Type* type = bh_alloc(a, sizeof(Type) + sizeof(Type *) * extra_type_pointer_count);
- type->kind = kind;
- type->ast_type = NULL;
- return type;
-}
-
-static void type_register(Type* type) {
- static u32 next_unique_id = 1;
- type->id = next_unique_id++;
- if (type->ast_type) type->ast_type->type_id = type->id;
-
- bh_imap_put(&type_map, type->id, (u64) type);
-}
-
-void types_init() {
- bh_imap_init(&type_map, global_heap_allocator, 255);
- bh_imap_init(&type_pointer_map, global_heap_allocator, 255);
- bh_imap_init(&type_slice_map, global_heap_allocator, 255);
- bh_imap_init(&type_dynarr_map, global_heap_allocator, 255);
- bh_imap_init(&type_vararg_map, global_heap_allocator, 255);
- bh_table_init(global_heap_allocator, type_func_map, 64);
-
- fori (i, 0, Basic_Kind_Count) type_register(&basic_types[i]);
-}
-
-void types_dump_type_info() {
- bh_arr_each(bh__imap_entry, entry, type_map.entries) {
- bh_printf("%d -> %s\n", entry->key, type_get_name((Type *) entry->value));
- }
-}
-
-b32 types_are_compatible_(Type* t1, Type* t2, b32 recurse_pointers) {
- // NOTE: If they are pointing to the same thing,
- // it is safe to assume they are the same type
- if (t1 == t2) return 1;
- if (t1 == NULL || t2 == NULL) return 0;
- if (t1->id == t2->id) return 1;
-
- if (t1 == &type_auto_return || t2 == &type_auto_return) {
- return 0;
- }
-
- switch (t1->kind) {
- case Type_Kind_Basic:
- if (t2->kind == Type_Kind_Basic) {
- // Signedness of an integer doesn't matter.
- if ((t1->Basic.flags & Basic_Flag_Integer) && (t2->Basic.flags & Basic_Flag_Integer)) {
- return t1->Basic.size == t2->Basic.size;
- }
-
- if (t1->Basic.kind == Basic_Kind_V128 || t2->Basic.kind == Basic_Kind_V128) return 1;
- }
-
- if (t1->Basic.kind == Basic_Kind_Rawptr && type_is_pointer(t2)) {
- return 1;
- }
- break;
-
- case Type_Kind_Pointer: {
- if (t2->kind == Type_Kind_Pointer) {
- if (!recurse_pointers) return 1;
-
- if (types_are_compatible(t1->Pointer.elem, t2->Pointer.elem)) return 1;
-
- if (t1->Pointer.elem->kind == Type_Kind_Struct && t2->Pointer.elem->kind == Type_Kind_Struct) {
- Type* t1_struct = t1->Pointer.elem;
- Type* t2_struct = t2->Pointer.elem;
-
- if (t1_struct->Struct.memarr[0]->used)
- return types_are_compatible(t2_struct, t1_struct->Struct.memarr[0]->type);
- }
- }
-
- if (t2->kind == Type_Kind_Basic && t2->Basic.kind == Basic_Kind_Rawptr) return 1;
-
- break;
- }
-
- case Type_Kind_Array: {
- if (t2->kind != Type_Kind_Array) return 0;
-
- if (t1->Array.count != 0)
- if (t1->Array.count != t2->Array.count) return 0;
-
- return types_are_compatible(t1->Array.elem, t2->Array.elem);
- }
-
- case Type_Kind_Struct: {
- // NOTE: The check above for t1 == t2 would already catch this.
-
- // if (t2->kind != Type_Kind_Struct) return 0;
- // if (t1->Struct.unique_id != t2->Struct.unique_id) return 0;
- // if (t1->Struct.mem_count != t2->Struct.mem_count) return 0;
- return 0;
- }
-
- case Type_Kind_Enum: {
- // NOTE: The check above for t1 == t2 would already catch this.
- return 0;
- }
-
- case Type_Kind_Function: {
- if (t2->kind != Type_Kind_Function) return 0;
- if (t1->Function.param_count != t2->Function.param_count) return 0;
-
- if (!types_are_compatible(t1->Function.return_type, t2->Function.return_type)) return 0;
-
- if (t1->Function.param_count > 0) {
- fori (i, 0, t1->Function.param_count) {
- if (!types_are_compatible(t1->Function.params[i], t2->Function.params[i])) return 0;
- }
- }
-
- return 1;
- }
-
- case Type_Kind_Slice: {
- if (t2->kind != Type_Kind_Slice) return 0;
- return types_are_compatible(t1->Slice.ptr_to_data->Pointer.elem, t2->Slice.ptr_to_data->Pointer.elem);
- }
-
- case Type_Kind_VarArgs: {
- if (t2->kind != Type_Kind_VarArgs) return 0;
- return types_are_compatible(t1->VarArgs.ptr_to_data->Pointer.elem, t2->VarArgs.ptr_to_data->Pointer.elem);
- }
-
- case Type_Kind_DynArray: {
- if (t2->kind != Type_Kind_DynArray) return 0;
- return types_are_compatible(t1->DynArray.ptr_to_data->Pointer.elem, t2->DynArray.ptr_to_data->Pointer.elem);
- }
-
- case Type_Kind_Compound: {
- if (t2->kind != Type_Kind_Compound) return 0;
- if (t1->Compound.count != t2->Compound.count) return 0;
-
- fori (i, 0, (i64) t1->Compound.count) {
- if (!types_are_compatible(t1->Compound.types[i], t2->Compound.types[i])) return 0;
- }
-
- return 1;
- }
-
- default:
- assert(("Invalid type", 0));
- break;
- }
-
- return 0;
-}
-
-b32 types_are_compatible(Type* t1, Type* t2) {
- return types_are_compatible_(t1, t2, 1);
-}
-
-u32 type_size_of(Type* type) {
- if (type == NULL) return 0;
-
- switch (type->kind) {
- case Type_Kind_Basic: return type->Basic.size;
- case Type_Kind_Pointer: return 8;
- case Type_Kind_Function: return 4;
- case Type_Kind_Array: return type->Array.size;
- case Type_Kind_Struct: return type->Struct.size;
- case Type_Kind_Enum: return type_size_of(type->Enum.backing);
- case Type_Kind_Slice: return 16; // HACK: These should not have to be 16 bytes in size, they should only have to be 12,
- case Type_Kind_VarArgs: return 16; // but there are alignment issues right now with that so I decided to not fight it and just make them 16 bytes in size.
- case Type_Kind_DynArray: return 32; // data (8), count (4), capacity (4), allocator { func (4), ---(4), data (8) }
- case Type_Kind_Compound: return type->Compound.size;
- default: return 0;
- }
-}
-
-u32 type_alignment_of(Type* type) {
- if (type == NULL) return 1;
-
- switch (type->kind) {
- case Type_Kind_Basic: return type->Basic.alignment;
- case Type_Kind_Pointer: return 8;
- case Type_Kind_Function: return 4;
- case Type_Kind_Array: return type_alignment_of(type->Array.elem);
- case Type_Kind_Struct: return type->Struct.alignment;
- case Type_Kind_Enum: return type_alignment_of(type->Enum.backing);
- case Type_Kind_Slice: return 8;
- case Type_Kind_VarArgs: return 8;
- case Type_Kind_DynArray: return 8;
- case Type_Kind_Compound: return 4; // HACK
- default: return 1;
- }
-}
-
-Type* type_build_from_ast(bh_allocator alloc, AstType* type_node) {
- if (type_node == NULL) return NULL;
-
- switch (type_node->kind) {
- case Ast_Kind_Pointer_Type: {
- Type* ptr_type = type_make_pointer(alloc, type_build_from_ast(alloc, ((AstPointerType *) type_node)->elem));
- if (ptr_type) ptr_type->ast_type = type_node;
- return ptr_type;
- }
-
- case Ast_Kind_Function_Type: {
- AstFunctionType* ftype_node = (AstFunctionType *) type_node;
- u64 param_count = ftype_node->param_count;
-
- Type* return_type = type_build_from_ast(alloc, ftype_node->return_type);
- if (return_type == NULL) return NULL;
-
- Type* func_type = type_create(Type_Kind_Function, alloc, param_count);
- func_type->ast_type = type_node;
- func_type->Function.param_count = param_count;
- func_type->Function.needed_param_count = param_count;
- func_type->Function.vararg_arg_pos = -1;
- func_type->Function.return_type = return_type;
-
- if (param_count > 0)
- fori (i, 0, (i64) param_count) {
- func_type->Function.params[i] = type_build_from_ast(alloc, ftype_node->params[i]);
-
- // LEAK LEAK LEAK
- if (func_type->Function.params[i] == NULL) return NULL;
- }
-
- char* name = (char *) type_get_unique_name(func_type);
- if (bh_table_has(u64, type_func_map, name)) {
- u64 id = bh_table_get(u64, type_func_map, name);
- Type* existing_type = (Type *) bh_imap_get(&type_map, id);
-
- // LEAK LEAK LEAK the func_type that is created
- return existing_type;
- }
-
- type_register(func_type);
- bh_table_put(u64, type_func_map, name, func_type->id);
-
- return func_type;
- }
-
- case Ast_Kind_Array_Type: {
- AstArrayType* a_node = (AstArrayType *) type_node;
-
- Type* a_type = type_create(Type_Kind_Array, alloc, 0);
- a_type->Array.elem = type_build_from_ast(alloc, a_node->elem);
-
- u32 count = 0;
- if (a_node->count_expr) {
- if (a_node->count_expr->type == NULL)
- a_node->count_expr->type = type_build_from_ast(alloc, a_node->count_expr->type_node);
-
- if (node_is_auto_cast((AstNode *) a_node->count_expr)) {
- a_node->count_expr = ((AstUnaryOp *) a_node)->expr;
- }
-
- resolve_expression_type((AstTyped *) a_node->count_expr);
-
- // NOTE: Currently, the count_expr has to be an I32 literal
- if (a_node->count_expr->type->kind != Type_Kind_Basic
- || a_node->count_expr->type->Basic.kind != Basic_Kind_I32) {
- onyx_report_error(type_node->token->pos, "Array type expects type 'i32' for size, got '%s'.",
- type_get_name(a_node->count_expr->type));
- return NULL;
- }
-
- count = get_expression_integer_value((AstTyped *) a_node->count_expr);
- }
-
- a_type->Array.count = count;
- a_type->Array.size = type_size_of(a_type->Array.elem) * count;
-
- type_register(a_type);
- return a_type;
- }
-
- case Ast_Kind_Struct_Type: {
- AstStructType* s_node = (AstStructType *) type_node;
- if (s_node->stcache != NULL && s_node->stcache_is_valid) return s_node->stcache;
-
- Type* s_type;
- if (s_node->stcache == NULL) {
- s_type = type_create(Type_Kind_Struct, alloc, 0);
- s_node->stcache = s_type;
-
- s_type->ast_type = type_node;
- s_type->Struct.name = s_node->name;
- s_type->Struct.mem_count = bh_arr_length(s_node->members);
- type_register(s_type);
-
- s_type->Struct.memarr = NULL;
- bh_table_init(global_heap_allocator, s_type->Struct.members, s_type->Struct.mem_count + 1);
- bh_arr_new(global_heap_allocator, s_type->Struct.memarr, s_type->Struct.mem_count);
-
- } else {
- s_type = s_node->stcache;
- }
-
- s_type->Struct.poly_sln = NULL;
-
- bh_arr_clear(s_type->Struct.memarr);
- bh_table_clear(s_type->Struct.members);
-
- s_node->stcache_is_valid = 1;
-
- b32 is_union = (s_node->flags & Ast_Flag_Struct_Is_Union) != 0;
- u32 size = 0;
- u32 offset = 0;
- u32 alignment = 1, mem_alignment;
- u32 idx = 0;
- bh_arr_each(AstStructMember *, member, s_node->members) {
- if ((*member)->type == NULL)
- (*member)->type = type_build_from_ast(alloc, (*member)->type_node);
-
- if ((*member)->type == NULL) {
- s_node->stcache_is_valid = 0;
- return NULL;
- }
-
- mem_alignment = type_alignment_of((*member)->type);
- if (mem_alignment <= 0) {
- onyx_report_error((*member)->token->pos, "Invalid member type: %s. Has alignment %d", type_get_name((*member)->type), mem_alignment);
- return NULL;
- }
-
- if (mem_alignment > alignment) alignment = mem_alignment;
- if (offset % mem_alignment != 0) {
- offset += mem_alignment - (offset % mem_alignment);
- }
-
- token_toggle_end((*member)->token);
- StructMember smem = {
- .offset = offset,
- .type = (*member)->type,
- .idx = idx,
- .name = bh_strdup(alloc, (*member)->token->text),
- .initial_value = &(*member)->initial_value,
- .included_through_use = 0,
- .used = (((*member)->flags & Ast_Flag_Struct_Mem_Used) != 0),
- };
-
- if (bh_table_has(StructMember, s_type->Struct.members, (*member)->token->text)) {
- onyx_report_error((*member)->token->pos, "Duplicate struct member, '%s'.", (*member)->token->text);
- return NULL;
- }
- bh_table_put(StructMember, s_type->Struct.members, (*member)->token->text, smem);
- token_toggle_end((*member)->token);
-
- if (smem.used) {
- assert((*member)->type->kind == Type_Kind_Struct);
-
- bh_arr_each(StructMember*, psmem, (*member)->type->Struct.memarr) {
- StructMember new_smem = {
- .offset = offset + (*psmem)->offset,
- .type = (*psmem)->type,
- .idx = -1, // Dummy value because I don't think this is needed.
- .name = (*psmem)->name,
- .initial_value = (*psmem)->initial_value,
- .included_through_use = 1,
- .used = 0,
- };
-
- if (bh_table_has(StructMember, s_type->Struct.members, (*psmem)->name)) {
- onyx_report_error((*member)->token->pos, "Duplicate struct member, '%s'.", (*psmem)->name);
- return NULL;
- }
- bh_table_put(StructMember, s_type->Struct.members, (*psmem)->name, new_smem);
- }
- }
-
- u32 type_size = type_size_of((*member)->type);
-
- if (!is_union) offset += type_size;
- if (!is_union) size = offset;
- else size = bh_max(size, type_size);
-
- idx++;
- }
-
- // NOTE: Need to do a second pass because the references to the
- // elements of the table may change if the internal arrays of the
- // table need to be resized.
- bh_arr_each(AstStructMember *, member, s_node->members) {
- token_toggle_end((*member)->token);
- bh_arr_push(s_type->Struct.memarr, &bh_table_get(StructMember, s_type->Struct.members, (*member)->token->text));
- token_toggle_end((*member)->token);
- }
-
- alignment = bh_max(s_node->min_alignment, alignment);
- s_type->Struct.alignment = alignment;
-
- if (size % alignment != 0) {
- size += alignment - (size % alignment);
- }
-
- size = bh_max(s_node->min_size, size);
- s_type->Struct.size = size;
-
- s_type->Struct.linear_members = NULL;
- bh_arr_new(global_heap_allocator, s_type->Struct.linear_members, s_type->Struct.mem_count);
- build_linear_types_with_offset(s_type, &s_type->Struct.linear_members, 0);
-
- return s_type;
- }
-
- case Ast_Kind_Enum_Type: {
- AstEnumType* enum_node = (AstEnumType *) type_node;
- if (enum_node->etcache) return enum_node->etcache;
- if (enum_node->backing_type == NULL) return NULL;
-
- Type* enum_type = type_create(Type_Kind_Enum, alloc, 0);
- enum_node->etcache = enum_type;
-
- enum_type->ast_type = type_node;
- enum_type->Enum.backing = enum_node->backing_type;
- enum_type->Enum.name = enum_node->name;
- enum_type->Enum.is_flags = enum_node->flags & Ast_Flag_Enum_Is_Flags;
-
- type_register(enum_type);
- return enum_type;
- }
-
- case Ast_Kind_Slice_Type: {
- Type* slice_type = type_make_slice(alloc, type_build_from_ast(alloc, ((AstSliceType *) type_node)->elem));
- if (slice_type) slice_type->ast_type = type_node;
- return slice_type;
- }
-
- case Ast_Kind_DynArr_Type: {
- Type* dynarr_type = type_make_dynarray(alloc, type_build_from_ast(alloc, ((AstDynArrType *) type_node)->elem));
- if (dynarr_type) dynarr_type->ast_type = type_node;
- return dynarr_type;
- }
-
- case Ast_Kind_VarArg_Type: {
- Type* va_type = type_make_varargs(alloc, type_build_from_ast(alloc, ((AstVarArgType *) type_node)->elem));
- if (va_type) va_type->ast_type = type_node;
- return va_type;
- }
-
- case Ast_Kind_Basic_Type: {
- return ((AstBasicType *) type_node)->basic_type;
- }
-
- case Ast_Kind_Type_Alias: {
- Type* type = type_build_from_ast(alloc, ((AstTypeAlias *) type_node)->to);
- if (type && type->ast_type) type_node->type_id = type->id;
- return type;
- }
-
- case Ast_Kind_Type_Raw_Alias:
- return ((AstTypeRawAlias *) type_node)->to;
-
- case Ast_Kind_Poly_Struct_Type:
- //onyx_report_error(type_node->token->pos,
- // "This structure is polymorphic, which means you need to provide arguments to it to make it a concrete structure. "
- // "This error message is probably in the wrong place, so look through your code for uses of this struct.");
- return NULL;
- break;
-
- case Ast_Kind_Poly_Call_Type: {
- AstPolyCallType* pc_type = (AstPolyCallType *) type_node;
- pc_type->callee = (AstType *) strip_aliases((AstNode *) pc_type->callee);
-
- if (!(pc_type->callee && pc_type->callee->kind == Ast_Kind_Poly_Struct_Type)) {
- // If it is an unresolved field access or symbol, just return because an error will be printed elsewhere.
- if (pc_type->callee->kind == Ast_Kind_Field_Access || pc_type->callee->kind == Ast_Kind_Symbol) return NULL;
-
- onyx_report_error(pc_type->token->pos, "Cannot instantiate a concrete type off of a non-polymorphic type.");
- onyx_report_error(pc_type->callee->token->pos, "Here is the type trying to be instantiated. (%s)", onyx_ast_node_kind_string(pc_type->callee->kind));
- return NULL;
- }
-
- AstPolyStructType* ps_type = (AstPolyStructType *) pc_type->callee;
-
- bh_arr(AstPolySolution) slns = NULL;
- bh_arr_new(global_heap_allocator, slns, bh_arr_length(pc_type->params));
- bh_arr_each(AstNode *, given, pc_type->params) {
- if (node_is_type(*given)) {
- Type* param_type = type_build_from_ast(alloc, (AstType *) *given);
-
- // LEAK LEAK LEAK
- if (param_type == NULL) return NULL;
-
- bh_arr_push(slns, ((AstPolySolution) {
- .kind = PSK_Type,
- .type = param_type,
- }));
- } else {
- bh_arr_push(slns, ((AstPolySolution) {
- .kind = PSK_Value,
- .value = (AstTyped *) *given,
- }));
- }
- }
-
- AstStructType* concrete = polymorphic_struct_lookup(ps_type, slns, pc_type->token->pos);
-
- // This should be copied in the previous function.
- // CLEANUP: Maybe don't copy it and just use this one since it is allocated on the heap?
- bh_arr_free(slns);
-
- if (!concrete) return NULL;
- Type* struct_type = type_build_from_ast(alloc, (AstType *) concrete);
- struct_type->Struct.constructed_from = (AstType *) ps_type;
- return struct_type;
- }
-
- case Ast_Kind_Type_Compound: {
- AstCompoundType* ctype = (AstCompoundType *) type_node;
-
- i64 type_count = bh_arr_length(ctype->types);
-
- Type* comp_type = type_create(Type_Kind_Compound, alloc, type_count);
- comp_type->Compound.size = 0;
- comp_type->Compound.count = type_count;
-
- fori (i, 0, type_count) {
- assert(ctype->types[i] != NULL);
- comp_type->Compound.types[i] = type_build_from_ast(alloc, ctype->types[i]);
-
- // LEAK LEAK LEAK
- if (comp_type->Compound.types[i] == NULL) return NULL;
-
- comp_type->Compound.size += bh_max(type_size_of(comp_type->Compound.types[i]), 4);
- }
-
- bh_align(comp_type->Compound.size, 4);
-
- comp_type->Compound.linear_members = NULL;
- bh_arr_new(global_heap_allocator, comp_type->Compound.linear_members, comp_type->Compound.count);
- build_linear_types_with_offset(comp_type, &comp_type->Compound.linear_members, 0);
-
- type_register(comp_type);
- return comp_type;
- }
-
- case Ast_Kind_Alias: {
- AstAlias* alias = (AstAlias *) type_node;
- return type_build_from_ast(alloc, (AstType *) alias->alias);
- }
-
- case Ast_Kind_Typeof: {
- AstTypeOf* type_of = (AstTypeOf *) type_node;
- if (type_of->resolved_type != NULL) {
- return type_of->resolved_type;
- }
-
- return NULL;
- }
- }
-
- return NULL;
-}
-
-// CLEANUP: This needs to be merged with the very similar code from up above.
-Type* type_build_function_type(bh_allocator alloc, AstFunction* func) {
- u64 param_count = bh_arr_length(func->params);
-
- Type* return_type = type_build_from_ast(alloc, func->return_type);
- if (return_type == NULL) return NULL;
-
- Type* func_type = type_create(Type_Kind_Function, alloc, param_count);
- func_type->Function.param_count = param_count;
- func_type->Function.needed_param_count = 0;
- func_type->Function.vararg_arg_pos = -1;
- func_type->Function.return_type = return_type;
-
- if (param_count > 0) {
- i32 i = 0;
- bh_arr_each(AstParam, param, func->params) {
- if (param->default_value == NULL && param->vararg_kind == VA_Kind_Not_VA)
- func_type->Function.needed_param_count++;
-
- if (param->vararg_kind == VA_Kind_Untyped)
- func_type->Function.vararg_arg_pos = i;
-
- func_type->Function.params[i++] = param->local->type;
- }
- }
-
- // CopyPaste from above in type_build_from_ast
- char* name = (char *) type_get_unique_name(func_type);
- if (bh_table_has(u64, type_func_map, name)) {
- u64 id = bh_table_get(u64, type_func_map, name);
- Type* existing_type = (Type *) bh_imap_get(&type_map, id);
-
- // LEAK LEAK LEAK the func_type that is created
- return existing_type;
- }
-
- type_register(func_type);
- bh_table_put(u64, type_func_map, name, func_type->id);
-
- return func_type;
-}
-
-Type* type_build_compound_type(bh_allocator alloc, AstCompound* compound) {
- i64 expr_count = bh_arr_length(compound->exprs);
- fori (i, 0, expr_count) {
- if (compound->exprs[i]->type == NULL) return NULL;
- }
-
- Type* comp_type = type_create(Type_Kind_Compound, alloc, expr_count);
- comp_type->Compound.size = 0;
- comp_type->Compound.count = expr_count;
-
- fori (i, 0, expr_count) {
- assert(compound->exprs[i]->type != NULL);
- comp_type->Compound.types[i] = compound->exprs[i]->type;
- comp_type->Compound.size += bh_max(type_size_of(comp_type->Compound.types[i]), 4);
- }
-
- bh_align(comp_type->Compound.size, 4);
-
- comp_type->Compound.linear_members = NULL;
- bh_arr_new(global_heap_allocator, comp_type->Compound.linear_members, comp_type->Compound.count);
- build_linear_types_with_offset(comp_type, &comp_type->Compound.linear_members, 0);
-
- type_register(comp_type);
- return comp_type;
-}
-
-Type* type_make_pointer(bh_allocator alloc, Type* to) {
- if (to == NULL) return NULL;
-
- assert(to->id > 0);
- u64 ptr_id = bh_imap_get(&type_pointer_map, to->id);
- if (ptr_id > 0) {
- Type* ptr_type = (Type *) bh_imap_get(&type_map, ptr_id);
- return ptr_type;
-
- } else {
- Type* ptr_type = type_create(Type_Kind_Pointer, alloc, 0);
- ptr_type->Pointer.base.flags |= Basic_Flag_Pointer;
- ptr_type->Pointer.base.size = 8;
- ptr_type->Pointer.elem = to;
-
- type_register(ptr_type);
- bh_imap_put(&type_pointer_map, to->id, ptr_type->id);
-
- return ptr_type;
- }
-}
-
-Type* type_make_array(bh_allocator alloc, Type* to, u32 count) {
- if (to == NULL) return NULL;
-
- Type* arr_type = type_create(Type_Kind_Array, alloc, 0);
- arr_type->Array.count = count;
- arr_type->Array.elem = to;
- arr_type->Array.size = count * type_size_of(to);
-
- // :TypeCanBeDuplicated
- type_register(arr_type);
- return arr_type;
-}
-
-Type* type_make_slice(bh_allocator alloc, Type* of) {
- if (of == NULL) return NULL;
-
- assert(of->id > 0);
- u64 slice_id = bh_imap_get(&type_slice_map, of->id);
- if (slice_id > 0) {
- Type* slice_type = (Type *) bh_imap_get(&type_map, slice_id);
- return slice_type;
-
- } else {
- Type* slice_type = type_create(Type_Kind_Slice, alloc, 0);
- type_register(slice_type);
- bh_imap_put(&type_slice_map, of->id, slice_type->id);
-
- slice_type->Slice.ptr_to_data = type_make_pointer(alloc, of);
-
- return slice_type;
- }
-}
-
-Type* type_make_dynarray(bh_allocator alloc, Type* of) {
- if (of == NULL) return NULL;
-
- assert(of->id > 0);
- u64 dynarr_id = bh_imap_get(&type_dynarr_map, of->id);
- if (dynarr_id > 0) {
- Type* dynarr = (Type *) bh_imap_get(&type_map, dynarr_id);
- return dynarr;
-
- } else {
- Type* dynarr = type_create(Type_Kind_DynArray, alloc, 0);
- type_register(dynarr);
- bh_imap_put(&type_dynarr_map, of->id, dynarr->id);
-
- dynarr->DynArray.ptr_to_data = type_make_pointer(alloc, of);
-
- return dynarr;
- }
-}
-
-Type* type_make_varargs(bh_allocator alloc, Type* of) {
- if (of == NULL) return NULL;
-
- assert(of->id > 0);
- u64 vararg_id = bh_imap_get(&type_vararg_map, of->id);
- if (vararg_id > 0) {
- Type* va_type = (Type *) bh_imap_get(&type_map, vararg_id);
- return va_type;
-
- } else {
- Type* va_type = type_create(Type_Kind_VarArgs, alloc, 0);
- type_register(va_type);
- bh_imap_put(&type_vararg_map, of->id, va_type->id);
-
- va_type->VarArgs.ptr_to_data = type_make_pointer(alloc, of);
-
- return va_type;
- }
-}
-
-void build_linear_types_with_offset(Type* type, bh_arr(TypeWithOffset)* pdest, u32 offset) {
- if (type_is_structlike_strict(type)) {
- u32 mem_count = type_structlike_mem_count(type);
- StructMember smem = { 0 };
- fori (i, 0, mem_count) {
- type_lookup_member_by_idx(type, i, &smem);
- build_linear_types_with_offset(smem.type, pdest, offset + smem.offset);
- }
-
- } else if (type->kind == Type_Kind_Compound) {
- u32 elem_offset = 0;
- fori (i, 0, type->Compound.count) {
- build_linear_types_with_offset(type->Compound.types[i], pdest, offset + elem_offset);
- elem_offset += bh_max(type_size_of(type->Compound.types[i]), 4);
- }
-
- } else {
- bh_arr(TypeWithOffset) dest = *pdest;
-
- TypeWithOffset two;
- two.type = type;
- two.offset = offset;
- bh_arr_push(dest, two);
-
- *pdest = dest;
- }
-}
-
-const char* type_get_unique_name(Type* type) {
- if (type == NULL) return "unknown";
-
- switch (type->kind) {
- case Type_Kind_Basic: return type->Basic.name;
- case Type_Kind_Pointer: return bh_aprintf(global_scratch_allocator, "^%s", type_get_unique_name(type->Pointer.elem));
- case Type_Kind_Array: return bh_aprintf(global_scratch_allocator, "[%d] %s", type->Array.count, type_get_unique_name(type->Array.elem));
- case Type_Kind_Struct:
- if (type->Struct.name)
- return bh_aprintf(global_scratch_allocator, "%s@%l", type->Struct.name, type->id);
- else
- return bh_aprintf(global_scratch_allocator, "%s@%l", "<anonymous struct>", type->id);
- case Type_Kind_Enum:
- if (type->Enum.name)
- return bh_aprintf(global_scratch_allocator, "%s@%l", type->Enum.name, type->id);
- else
- return bh_aprintf(global_scratch_allocator, "%s@%l", "<anonymous enum>", type->id);
-
- case Type_Kind_Slice: return bh_aprintf(global_scratch_allocator, "[] %s", type_get_unique_name(type->Slice.ptr_to_data->Pointer.elem));
- case Type_Kind_VarArgs: return bh_aprintf(global_scratch_allocator, "..%s", type_get_unique_name(type->VarArgs.ptr_to_data->Pointer.elem));
- case Type_Kind_DynArray: return bh_aprintf(global_scratch_allocator, "[..] %s", type_get_unique_name(type->DynArray.ptr_to_data->Pointer.elem));
-
- case Type_Kind_Function: {
- char buf[1024];
- memset(buf, 0, 1024);
-
- strncat(buf, "(", 1023);
- fori (i, 0, type->Function.param_count) {
- strncat(buf, type_get_unique_name(type->Function.params[i]), 1023);
-
- if (i >= type->Function.needed_param_count)
- strncat(buf, "?", 1023);
-
- if (i != type->Function.param_count - 1)
- strncat(buf, ", ", 1023);
- }
-
- strncat(buf, ") -> ", 1023);
- strncat(buf, type_get_unique_name(type->Function.return_type), 1023);
-
- return bh_aprintf(global_scratch_allocator, "%s", buf);
- }
-
- case Type_Kind_Compound: {
- char buf[1024];
- memset(buf, 0, 1024);
-
- strncat(buf, "(", 1023);
- fori (i, 0, type->Compound.count) {
- strncat(buf, type_get_unique_name(type->Compound.types[i]), 1023);
- if (i != type->Compound.count - 1)
- strncat(buf, ", ", 1023);
- }
- strncat(buf, ")", 1023);
-
- return bh_aprintf(global_scratch_allocator, "%s", buf);
- }
-
-
- default: return "unknown";
- }
-}
-
-const char* type_get_name(Type* type) {
- if (type == NULL) return "unknown";
-
- switch (type->kind) {
- case Type_Kind_Basic: return type->Basic.name;
- case Type_Kind_Pointer: return bh_aprintf(global_scratch_allocator, "^%s", type_get_name(type->Pointer.elem));
- case Type_Kind_Array: return bh_aprintf(global_scratch_allocator, "[%d] %s", type->Array.count, type_get_name(type->Array.elem));
- case Type_Kind_Struct:
- if (type->Struct.name)
- return type->Struct.name;
- else
- return "<anonymous struct>";
- case Type_Kind_Enum:
- if (type->Enum.name)
- return type->Enum.name;
- else
- return "<anonymous enum>";
-
- case Type_Kind_Slice: return bh_aprintf(global_scratch_allocator, "[] %s", type_get_name(type->Slice.ptr_to_data->Pointer.elem));
- case Type_Kind_VarArgs: return bh_aprintf(global_scratch_allocator, "..%s", type_get_name(type->VarArgs.ptr_to_data->Pointer.elem));
- case Type_Kind_DynArray: return bh_aprintf(global_scratch_allocator, "[..] %s", type_get_name(type->DynArray.ptr_to_data->Pointer.elem));
-
- case Type_Kind_Function: {
- char buf[512];
- fori (i, 0, 512) buf[i] = 0;
-
- strncat(buf, "(", 511);
- fori (i, 0, type->Function.param_count) {
- strncat(buf, type_get_name(type->Function.params[i]), 511);
- if (i != type->Function.param_count - 1)
- strncat(buf, ", ", 511);
- }
-
- strncat(buf, ") -> ", 511);
- strncat(buf, type_get_name(type->Function.return_type), 511);
-
- return bh_aprintf(global_scratch_allocator, "%s", buf);
- }
-
- case Type_Kind_Compound: {
- char buf[512];
- fori (i, 0, 512) buf[i] = 0;
-
- strncat(buf, "(", 511);
- fori (i, 0, type->Compound.count) {
- strncat(buf, type_get_name(type->Compound.types[i]), 511);
- if (i != type->Compound.count - 1)
- strncat(buf, ", ", 511);
- }
- strncat(buf, ")", 511);
-
- return bh_aprintf(global_scratch_allocator, "%s", buf);
- }
-
- default: return "unknown";
- }
-}
-
-u32 type_get_alignment_log2(Type* type) {
- i32 store_size = type_alignment_of(type);
- if (store_size == 1) return 0;
- else if (store_size == 2) return 1;
- else if (store_size == 4) return 2;
- else if (store_size == 8) return 3;
- else if (store_size == 16) return 4;
- return 2;
-}
-
-static const StructMember slice_members[] = {
- { 0, 0, NULL, "data", NULL, 0, 0 },
- { 8, 1, &basic_types[Basic_Kind_U32], "count", NULL, 0, 0 },
-};
-
-static const StructMember array_members[] = {
- { 0, 0, NULL, "data", NULL, 0, 0 },
- { 8, 1, &basic_types[Basic_Kind_U32], "count", NULL, 0, 0 },
- { 12, 2, &basic_types[Basic_Kind_U32], "capacity", NULL, 0, 0 },
- { 16, 3, NULL, "allocator", NULL, 0, 0 },
-};
-
-b32 type_lookup_member(Type* type, char* member, StructMember* smem) {
- if (type->kind == Type_Kind_Pointer) type = type->Pointer.elem;
-
- switch (type->kind) {
- case Type_Kind_Struct: {
- TypeStruct* stype = &type->Struct;
-
- if (!bh_table_has(StructMember, stype->members, member)) return 0;
- *smem = bh_table_get(StructMember, stype->members, member);
- return 1;
- }
-
- case Type_Kind_VarArgs:
- case Type_Kind_Slice: {
- fori (i, 0, (i64) (sizeof(slice_members) / sizeof(StructMember))) {
- if (strcmp(slice_members[i].name, member) == 0) {
- *smem = slice_members[i];
- if (smem->idx == 0) smem->type = type->Slice.ptr_to_data;
-
- return 1;
- }
- }
- return 0;
- }
-
- case Type_Kind_DynArray: {
- fori (i, 0, (i64) (sizeof(array_members) / sizeof(StructMember))) {
- if (strcmp(array_members[i].name, member) == 0) {
- *smem = array_members[i];
- if (smem->idx == 0) smem->type = type->DynArray.ptr_to_data;
- if (smem->idx == 3) smem->type = type_build_from_ast(context.ast_alloc, builtin_allocator_type);
-
- return 1;
- }
- }
- return 0;
- }
-
- default: return 0;
- }
-}
-
-b32 type_lookup_member_by_idx(Type* type, i32 idx, StructMember* smem) {
- if (type->kind == Type_Kind_Pointer) type = type->Pointer.elem;
-
- switch (type->kind) {
- case Type_Kind_Struct: {
- TypeStruct* stype = &type->Struct;
-
- if (idx > stype->mem_count) return 0;
- *smem = *stype->memarr[idx];
- return 1;
- }
-
- // HACK: This relies on the fact that the structures for Slice and VarArgs
- // are identical. - brendanfh 2020/09/07
- case Type_Kind_VarArgs:
- case Type_Kind_Slice: {
- if (idx > 2) return 0;
-
- *smem = slice_members[idx];
- if (smem->idx == 0) smem->type = type->Slice.ptr_to_data;
-
- return 1;
- }
-
- case Type_Kind_DynArray: {
- if (idx > 4) return 0;
-
- *smem = array_members[idx];
- if (idx == 0) smem->type = type->DynArray.ptr_to_data;
- if (idx == 3) smem->type = type_build_from_ast(context.ast_alloc, builtin_allocator_type);
-
- return 1;
- }
-
- default: return 0;
- }
-}
-
-i32 type_linear_member_count(Type* type) {
- switch (type->kind) {
- case Type_Kind_Slice:
- case Type_Kind_VarArgs: return 2;
- case Type_Kind_DynArray: return 5;
- case Type_Kind_Compound: return bh_arr_length(type->Compound.linear_members);
- case Type_Kind_Struct: return bh_arr_length(type->Struct.linear_members);
- default: return 1;
- }
-}
-
-b32 type_linear_member_lookup(Type* type, i32 idx, TypeWithOffset* two) {
- switch (type->kind) {
- case Type_Kind_Slice:
- case Type_Kind_VarArgs: {
- if (idx == 0) {
- two->type = type->Slice.ptr_to_data;
- two->offset = 0;
- }
- if (idx == 1) {
- two->type = &basic_types[Basic_Kind_U32];
- two->offset = 8;
- }
-
- return 1;
- }
- case Type_Kind_DynArray: {
- if (idx == 0) {
- two->type = type->DynArray.ptr_to_data;
- two->offset = 0;
- }
- if (idx == 1) {
- two->type = &basic_types[Basic_Kind_U32];
- two->offset = 8;
- }
- if (idx == 2) {
- two->type = &basic_types[Basic_Kind_U32];
- two->offset = 12;
- }
- if (idx == 3 || idx == 4) {
- Type* allocator_type = type_build_from_ast(context.ast_alloc, builtin_allocator_type);
- type_linear_member_lookup(allocator_type, idx - 3, two);
- two->offset += 16;
- }
-
- return 1;
- }
- case Type_Kind_Compound: *two = type->Compound.linear_members[idx]; return 1;
- case Type_Kind_Struct: *two = type->Struct.linear_members[idx]; return 1;
- default: {
- if (idx > 0) return 0;
- two->offset = 0;
- two->type = type;
- return 1;
- }
- }
-}
-
-i32 type_get_idx_of_linear_member_with_offset(Type* type, u32 offset) {
- switch (type->kind) {
- case Type_Kind_Slice:
- case Type_Kind_VarArgs: {
- if (offset == 0) return 0;
- if (offset == 8) return 1;
- return -1;
- }
- case Type_Kind_DynArray: {
- if (offset == 0) return 0;
- if (offset == 8) return 1;
- if (offset == 12) return 2;
- if (offset == 16) return 3;
- if (offset == 24) return 4;
- return -1;
- }
- case Type_Kind_Compound: {
- i32 idx = 0;
- bh_arr_each(TypeWithOffset, two, type->Compound.linear_members) {
- if (two->offset == offset) return idx;
- idx++;
- }
-
- return -1;
- }
- case Type_Kind_Struct: {
- i32 idx = 0;
- bh_arr_each(TypeWithOffset, two, type->Struct.linear_members) {
- if (two->offset == offset) return idx;
- idx++;
- }
-
- return -1;
- }
- default: return -1;
- }
-}
-
-b32 type_struct_is_simple(Type* type) {
- if (type->kind != Type_Kind_Struct) return 0;
-
- b32 is_simple = 1;
- bh_arr_each(StructMember *, mem, type->Struct.memarr) {
- if (type_is_compound((*mem)->type)) {
- is_simple = 0;
- break;
- }
- }
-
- return is_simple;
-}
-
-b32 type_is_pointer(Type* type) {
- if (type == NULL) return 0;
- return type->kind == Type_Kind_Pointer;
-}
-
-b32 type_is_rawptr(Type* type) {
- if (type == NULL) return 0;
- return type->kind == Type_Kind_Basic && type->Basic.kind == Basic_Kind_Rawptr;
-}
-
-b32 type_is_array(Type* type) {
- if (type == NULL) return 0;
- return type->kind == Type_Kind_Array;
-}
-
-b32 type_is_struct(Type* type) {
- if (type == NULL) return 0;
- if (type->kind == Type_Kind_Struct) return 1;
- if (type->kind == Type_Kind_Pointer)
- if (type->Pointer.elem != NULL && type->Pointer.elem->kind == Type_Kind_Struct) return 1;
- return 0;
-}
-
-b32 type_is_bool(Type* type) {
- if (type == NULL) return 0;
- return type != NULL && type->kind == Type_Kind_Basic && type->Basic.kind == Basic_Kind_Bool;
-}
-
-b32 type_is_small_integer(Type* type) {
- if (type == NULL) return 0;
- if (type->kind != Type_Kind_Basic) return 0;
-
- return type->Basic.kind >= Basic_Kind_I8 && type->Basic.kind <= Basic_Kind_U32;
-}
-
-b32 type_is_integer(Type* type) {
- if (type == NULL) return 0;
- if (type->kind != Type_Kind_Basic) return 0;
-
- return (type->Basic.kind >= Basic_Kind_I8 && type->Basic.kind <= Basic_Kind_U64)
- || type->Basic.kind == Basic_Kind_Type_Index;
-}
-
-b32 type_is_numeric(Type* type) {
- if (type == NULL) return 0;
- if (type->kind == Type_Kind_Enum) return 1;
- if (type->kind != Type_Kind_Basic) return 0;
-
- return type->Basic.kind >= Basic_Kind_Int_Unsized && type->Basic.kind <= Basic_Kind_F64;
-}
-
-b32 type_is_compound(Type* type) {
- if (type == NULL) return 0;
- return type->kind != Type_Kind_Basic
- && type->kind != Type_Kind_Pointer
- && type->kind != Type_Kind_Enum
- && type->kind != Type_Kind_Function
- && type->kind != Type_Kind_Array;
-}
-
-b32 type_is_simd(Type* type) {
- if (type == NULL) return 0;
- if (type->kind != Type_Kind_Basic) return 0;
- return type->Basic.flags & Basic_Flag_SIMD;
-}
-
-b32 type_results_in_void(Type* type) {
- return (type == NULL)
- || (type->kind == Type_Kind_Basic && type->Basic.kind == Basic_Kind_Void)
- || ( (type->kind == Type_Kind_Function)
- && (type->Function.return_type->kind == Type_Kind_Basic)
- && (type->Function.return_type->Basic.kind == Basic_Kind_Void));
-}
-
-b32 type_is_array_accessible(Type* type) {
- if (type == NULL) return 0;
- if (type_is_pointer(type)) return 1;
- if (type->kind == Type_Kind_Array) return 1;
- if (type->kind == Type_Kind_Slice) return 1;
- if (type->kind == Type_Kind_DynArray) return 1;
- if (type->kind == Type_Kind_VarArgs) return 1;
- return 0;
-}
-
-b32 type_is_structlike(Type* type) {
- if (type == NULL) return 0;
- if (type->kind == Type_Kind_Struct) return 1;
- if (type->kind == Type_Kind_Slice) return 1;
- if (type->kind == Type_Kind_DynArray) return 1;
- if (type->kind == Type_Kind_VarArgs) return 1;
- if (type->kind == Type_Kind_Pointer) {
- if (type->Pointer.elem->kind == Type_Kind_Struct) return 1;
- if (type->Pointer.elem->kind == Type_Kind_Slice) return 1;
- if (type->Pointer.elem->kind == Type_Kind_DynArray) return 1;
- }
- return 0;
-}
-
-b32 type_is_structlike_strict(Type* type) {
- if (type == NULL) return 0;
- if (type->kind == Type_Kind_Struct) return 1;
- if (type->kind == Type_Kind_Slice) return 1;
- if (type->kind == Type_Kind_DynArray) return 1;
- if (type->kind == Type_Kind_VarArgs) return 1;
- return 0;
-}
-
-u32 type_structlike_mem_count(Type* type) {
- if (type == NULL) return 0;
- switch (type->kind) {
- case Type_Kind_Struct: return type->Struct.mem_count;
- case Type_Kind_Slice: return 2;
- case Type_Kind_VarArgs: return 2;
- case Type_Kind_DynArray: return 4;
- default: return 0;
- }
-}
-
-u32 type_structlike_is_simple(Type* type) {
- if (type == NULL) return 0;
- switch (type->kind) {
- case Type_Kind_Struct: return type_struct_is_simple(type);
- case Type_Kind_Slice: return 1;
- case Type_Kind_VarArgs: return 1;
- case Type_Kind_DynArray: return 0;
- default: return 0;
- }
-}
-
-b32 type_is_sl_constructable(Type* type) {
- if (type == NULL) return 0;
- switch (type->kind) {
- case Type_Kind_Struct: return 1;
- case Type_Kind_Slice: return 1;
- case Type_Kind_DynArray: return 1;
- default: return 0;
- }
-}
-
-b32 type_struct_constructed_from_poly_struct(Type* struct_type, struct AstType* from) {
- if (struct_type == NULL) return 0;
- if (struct_type->kind != Type_Kind_Struct) return 0;
-
- return struct_type->Struct.constructed_from == from;
-}
+++ /dev/null
-#define BH_DEBUG
-
-#include "onyxutils.h"
-#include "onyxlex.h"
-#include "onyxastnodes.h"
-#include "onyxerrors.h"
-#include "onyxparser.h"
-#include "onyxastnodes.h"
-
-bh_scratch global_scratch;
-bh_allocator global_scratch_allocator;
-
-bh_managed_heap global_heap;
-bh_allocator global_heap_allocator;
-
-//
-// Program info and packages
-//
-Package* package_lookup(char* package_name) {
- if (bh_table_has(Package *, context.packages, package_name)) {
- return bh_table_get(Package *, context.packages, package_name);
- } else {
- return NULL;
- }
-}
-
-Package* package_lookup_or_create(char* package_name, Scope* parent_scope, bh_allocator alloc) {
- if (bh_table_has(Package *, context.packages, package_name)) {
- return bh_table_get(Package *, context.packages, package_name);
-
- } else {
- Package* package = bh_alloc_item(alloc, Package);
-
- char* pac_name = bh_alloc_array(alloc, char, strlen(package_name) + 1);
- memcpy(pac_name, package_name, strlen(package_name) + 1);
- pac_name[strlen(package_name)] = '\0';
-
- package->name = pac_name;
- package->scope = scope_create(alloc, parent_scope, (OnyxFilePos) { 0 });
- package->private_scope = scope_create(alloc, package->scope, (OnyxFilePos) { 0 });
- package->use_package_entities = NULL;
-
- bh_table_put(Package *, context.packages, pac_name, package);
-
- return package;
- }
-}
-
-void package_track_use_package(Package* package, Entity* entity) {
- if (package->use_package_entities == NULL) {
- bh_arr_new(global_heap_allocator, package->use_package_entities, 4);
- }
-
- bh_arr_push(package->use_package_entities, entity);
-}
-
-void package_reinsert_use_packages(Package* package) {
- if (!package->use_package_entities) return;
-
- bh_arr_each(Entity *, use_package, package->use_package_entities) {
- (*use_package)->state = Entity_State_Resolve_Symbols;
- (*use_package)->macro_attempts = 0;
- entity_heap_insert_existing(&context.entities, *use_package);
- }
-
- bh_arr_set_length(package->use_package_entities, 0);
-}
-
-
-//
-// Scoping
-//
-static u64 next_scope_id = 1;
-
-Scope* scope_create(bh_allocator a, Scope* parent, OnyxFilePos created_at) {
- Scope* scope = bh_alloc_item(a, Scope);
- scope->id = next_scope_id++;
- scope->parent = parent;
- scope->created_at = created_at;
-
- scope->symbols = NULL;
- bh_table_init(global_heap_allocator, scope->symbols, 64);
-
- return scope;
-}
-
-void scope_include(Scope* target, Scope* source, OnyxFilePos pos) {
- bh_table_each_start(AstNode *, source->symbols);
- symbol_raw_introduce(target, (char *) key, pos, value);
- bh_table_each_end;
-}
-
-b32 symbol_introduce(Scope* scope, OnyxToken* tkn, AstNode* symbol) {
- token_toggle_end(tkn);
-
- b32 ret = symbol_raw_introduce(scope, tkn->text, tkn->pos, symbol);
-
- token_toggle_end(tkn);
- return ret;
-}
-
-b32 symbol_raw_introduce(Scope* scope, char* name, OnyxFilePos pos, AstNode* symbol) {
- if (strcmp(name, "_")) {
- if (bh_table_has(AstNode *, scope->symbols, name)) {
- if (bh_table_get(AstNode *, scope->symbols, name) != symbol) {
- onyx_report_error(pos, "Redeclaration of symbol '%s'.", name);
- return 0;
- }
- return 1;
- }
- }
-
- bh_table_put(AstNode *, scope->symbols, name, symbol);
- return 1;
-}
-
-void symbol_builtin_introduce(Scope* scope, char* sym, AstNode *node) {
- bh_table_put(AstNode *, scope->symbols, sym, node);
-}
-
-void symbol_subpackage_introduce(Scope* scope, char* sym, AstPackage* package) {
- if (bh_table_has(AstNode *, scope->symbols, sym)) {
- AstNode* maybe_package = bh_table_get(AstNode *, scope->symbols, sym);
-
- // CLEANUP: Make this assertion an actual error message.
- assert(maybe_package->kind == Ast_Kind_Package);
- } else {
- bh_table_put(AstNode *, scope->symbols, sym, (AstNode *) package);
- }
-}
-
-AstNode* symbol_raw_resolve(Scope* start_scope, char* sym) {
- AstNode* res = NULL;
- Scope* scope = start_scope;
-
- while (res == NULL && scope != NULL) {
- if (bh_table_has(AstNode *, scope->symbols, sym)) {
- res = bh_table_get(AstNode *, scope->symbols, sym);
- } else {
- scope = scope->parent;
- }
- }
-
- return res;
-}
-
-AstNode* symbol_resolve(Scope* start_scope, OnyxToken* tkn) {
- token_toggle_end(tkn);
- AstNode* res = symbol_raw_resolve(start_scope, tkn->text);
- token_toggle_end(tkn);
-
- return res;
-}
-
-AstNode* try_symbol_raw_resolve_from_node(AstNode* node, char* symbol) {
- b32 used_pointer = 0;
-
- while (1) {
- if (!node) return NULL;
-
- switch (node->kind) {
- case Ast_Kind_Type_Raw_Alias: node = (AstNode *) ((AstTypeRawAlias *) node)->to->ast_type; break;
- case Ast_Kind_Type_Alias: node = (AstNode *) ((AstTypeAlias *) node)->to; break;
- case Ast_Kind_Alias: node = (AstNode *) ((AstAlias *) node)->alias; break;
- case Ast_Kind_Pointer_Type: {
- if (used_pointer) goto all_types_peeled_off;
- used_pointer = 1;
-
- node = (AstNode *) ((AstPointerType *) node)->elem;
- break;
- }
-
- default: goto all_types_peeled_off;
- }
- }
-
-all_types_peeled_off:
- if (!node) return NULL;
-
- switch (node->kind) {
- case Ast_Kind_Package: {
- AstPackage* package = (AstPackage *) node;
-
- // CLEANUP
- if (package->package == NULL) {
- package->package = package_lookup(package->package_name);
- }
-
- if (package->package == NULL) {
- return NULL;
- }
-
- return symbol_raw_resolve(package->package->scope, symbol);
- }
-
- case Ast_Kind_Enum_Type: {
- AstEnumType* etype = (AstEnumType *) node;
- return symbol_raw_resolve(etype->scope, symbol);
- }
-
- case Ast_Kind_Struct_Type: {
- AstStructType* stype = (AstStructType *) node;
- AstNode* result = symbol_raw_resolve(stype->scope, symbol);
-
- if (result == NULL && stype->stcache != NULL) {
- Type* struct_type = stype->stcache;
- assert(struct_type->kind == Type_Kind_Struct);
-
- bh_arr_each(AstPolySolution, sln, struct_type->Struct.poly_sln) {
- if (token_text_equals(sln->poly_sym->token, symbol)) {
- if (sln->kind == PSK_Type) {
- result = (AstNode *) sln->type->ast_type;
- } else {
- result = (AstNode *) sln->value;
- }
- }
- }
- }
-
- return result;
- }
-
- case Ast_Kind_Poly_Struct_Type: {
- AstStructType* stype = ((AstPolyStructType *) node)->base_struct;
- return symbol_raw_resolve(stype->scope, symbol);
- }
- }
-
- return NULL;
-}
-
-AstNode* try_symbol_resolve_from_node(AstNode* node, OnyxToken* token) {
- token_toggle_end(token);
- AstNode* result = try_symbol_raw_resolve_from_node(node, token->text);
- token_toggle_end(token);
-
- return result;
-}
-
-void scope_clear(Scope* scope) {
- bh_table_clear(scope->symbols);
-}
-
-
-//
-// Polymorphic Procedures
-//
-
-// HACK HACK HACK nocheckin
-static b32 flag_to_yield = 0;
-
-AstTyped node_that_signals_a_yield = { Ast_Kind_Function, 0 };
-
-static void ensure_polyproc_cache_is_created(AstPolyProc* pp) {
- if (pp->concrete_funcs == NULL) {
- bh_table_init(global_heap_allocator, pp->concrete_funcs, 16);
- }
-}
-
-static void insert_poly_slns_into_scope(Scope* scope, bh_arr(AstPolySolution) slns) {
- bh_arr_each(AstPolySolution, sln, slns) {
- AstNode *node = NULL;
-
- switch (sln->kind) {
- case PSK_Type:
- node = onyx_ast_node_new(context.ast_alloc, sizeof(AstTypeRawAlias), Ast_Kind_Type_Raw_Alias);
- ((AstTypeRawAlias *) node)->token = sln->poly_sym->token;
- ((AstTypeRawAlias *) node)->to = sln->type;
- break;
-
- case PSK_Value:
- // CLEANUP: Maybe clone this?
- assert(sln->value->flags & Ast_Flag_Comptime);
- node = (AstNode *) sln->value;
- break;
- }
-
- symbol_introduce(scope, sln->poly_sym->token, node);
- }
-}
-
-// NOTE: This might return a volatile string. Do not store it without copying it.
-static char* build_poly_solution_key(AstPolySolution* sln) {
- static char buffer[128];
-
- if (sln->kind == PSK_Type) {
- return (char *) type_get_unique_name(sln->type);
- }
-
- else if (sln->kind == PSK_Value) {
- fori (i, 0, 128) buffer[i] = 0;
-
- if (sln->value->kind == Ast_Kind_NumLit) {
- strncat(buffer, "NUMLIT:", 127);
- strncat(buffer, bh_bprintf("%l", ((AstNumLit *) sln->value)->value.l), 127);
-
- } else {
- // HACK: For now, the value pointer is just used. This means that
- // sometimes, even through the solution is the same, it won't be
- // stored the same.
- bh_snprintf(buffer, 128, "%p", sln->value);
- }
-
- return buffer;
- }
-
- return NULL;
-}
-
-// NOTE: This returns a volatile string. Do not store it without copying it.
-static char* build_poly_slns_unique_key(bh_arr(AstPolySolution) slns) {
- static char key_buf[1024];
- fori (i, 0, 1024) key_buf[i] = 0;
-
- bh_arr_each(AstPolySolution, sln, slns) {
- token_toggle_end(sln->poly_sym->token);
-
- strncat(key_buf, sln->poly_sym->token->text, 1023);
- strncat(key_buf, "=", 1023);
- strncat(key_buf, build_poly_solution_key(sln), 1023);
- strncat(key_buf, ";", 1023);
-
- token_toggle_end(sln->poly_sym->token);
- }
-
- return key_buf;
-}
-
-// NOTE: This function adds a solidified function to the entity heap for it to be processed
-// later. It optionally can start the function header entity at the code generation state if
-// the header has already been processed.
-static b32 add_solidified_function_entities(AstSolidifiedFunction solidified_func, b32 header_already_processed) {
- solidified_func.func->flags |= Ast_Flag_Function_Used;
- solidified_func.func->flags |= Ast_Flag_From_Polymorphism;
-
- EntityState header_start_state = Entity_State_Resolve_Symbols;
- if (header_already_processed) header_start_state = Entity_State_Code_Gen;
-
- Entity func_header_entity = {
- .state = header_start_state,
- .type = Entity_Type_Function_Header,
- .function = solidified_func.func,
- .package = NULL,
- .scope = solidified_func.poly_scope,
- };
-
- entity_bring_to_state(&func_header_entity, Entity_State_Code_Gen);
- if (onyx_has_errors()) return 0;
-
- Entity func_entity = {
- .state = Entity_State_Resolve_Symbols,
- .type = Entity_Type_Function,
- .function = solidified_func.func,
- .package = NULL,
- .scope = solidified_func.poly_scope,
- };
-
- Entity* entity_header = entity_heap_insert(&context.entities, func_header_entity);
- Entity* entity_body = entity_heap_insert(&context.entities, func_entity);
-
- solidified_func.func->entity_header = entity_header;
- solidified_func.func->entity_body = entity_body;
-
- return 1;
-}
-
-// NOTE: This function is responsible for taking all of the information about generating
-// a new polymorphic variant, and producing a solidified function. It optionally can only
-// generate the header of the function, which is useful for cases such as checking if a
-// set of arguments works for a polymorphic overload option.
-static AstSolidifiedFunction generate_solidified_function(
- AstPolyProc* pp,
- bh_arr(AstPolySolution) slns,
- OnyxToken* tkn,
- b32 header_only) {
-
- AstSolidifiedFunction solidified_func;
- solidified_func.header_complete = 0;
-
- // NOTE: Use the position of token if one was provided, otherwise just use NULL.
- OnyxFilePos poly_scope_pos = { 0 };
- if (tkn) poly_scope_pos = tkn->pos;
-
- solidified_func.poly_scope = scope_create(context.ast_alloc, pp->poly_scope, poly_scope_pos);
- insert_poly_slns_into_scope(solidified_func.poly_scope, slns);
-
- if (header_only) {
- solidified_func.func = (AstFunction *) clone_function_header(context.ast_alloc, pp->base_func);
- solidified_func.func->flags |= Ast_Flag_Incomplete_Body;
-
- } else {
- solidified_func.func = (AstFunction *) ast_clone(context.ast_alloc, pp->base_func);
- }
-
- solidified_func.func->flags |= Ast_Flag_From_Polymorphism;
- solidified_func.func->generated_from = tkn;
-
- // HACK: Remove the baked parameters from the function defintion so they can be
- // resolved in the poly scope above the function. This does feel kinda of gross
- // and I would love an alternative to tell it to just "skip" the parameter, but
- // that is liable to breaking because it is one more thing to remember.
- // - brendanfh 2021/01/18
- u32 removed_params = 0;
- bh_arr_each(AstPolyParam, param, pp->poly_params) {
- if (param->kind != PPK_Baked_Value) continue;
-
- bh_arr_deleten(solidified_func.func->params, param->idx - removed_params, 1);
- removed_params++;
- }
-
- return solidified_func;
-}
-
-static void ensure_solidified_function_has_body(AstPolyProc* pp, AstSolidifiedFunction solidified_func) {
- if (solidified_func.func->flags & Ast_Flag_Incomplete_Body) {
- clone_function_body(context.ast_alloc, solidified_func.func, pp->base_func);
-
- // HACK: I'm asserting that this function should return without an error, because
- // the only case where it can return an error is if there was a problem with the
- // header. This should never be the case in this situation, since the header would
- // have to have successfully passed type checking before it would become a solidified
- // procedure.
- assert(add_solidified_function_entities(solidified_func, 1));
-
- solidified_func.func->flags &= ~Ast_Flag_Incomplete_Body;
- }
-}
-
-// NOTE: These are temporary data structures used to represent the pattern matching system
-// of polymorphic type resolution.
-typedef struct PolySolveResult {
- PolySolutionKind kind;
- union {
- AstTyped* value;
- Type* actual;
- };
-} PolySolveResult;
-
-typedef struct PolySolveElem {
- AstType* type_expr;
-
- PolySolutionKind kind;
- union {
- AstTyped* value;
- Type* actual;
- };
-} PolySolveElem;
-
-// NOTE: The job of this function is to solve for the type/value that belongs in a
-// polymorphic variable. This function takes in three arguments:
-// * The symbol node of the polymorphic parameter being searched for
-// * The type expression that should contain the symbol node it is some where
-// * The actual type to pattern match against
-//
-// This function utilizes a basic breadth-first search of the type_expr and actual type
-// trees, always moving along them in parallel, so when the target is reached (if it is
-// ever reached), the "actual" is the matched type/value.
-static PolySolveResult solve_poly_type(AstNode* target, AstType* type_expr, Type* actual) {
- bh_arr(PolySolveElem) elem_queue = NULL;
- bh_arr_new(global_heap_allocator, elem_queue, 4);
-
- PolySolveResult result = { PSK_Undefined, { NULL } };
-
- bh_arr_push(elem_queue, ((PolySolveElem) {
- .type_expr = type_expr,
- .kind = PSK_Type,
- .actual = actual
- }));
-
- while (!bh_arr_is_empty(elem_queue)) {
- PolySolveElem elem = elem_queue[0];
- bh_arr_deleten(elem_queue, 0, 1);
-
- if (elem.type_expr == (AstType *) target) {
- result.kind = elem.kind;
-
- assert(elem.kind != PSK_Undefined);
- if (result.kind == PSK_Type) result.actual = elem.actual;
- if (result.kind == PSK_Value) result.value = elem.value;
- break;
- }
-
- if (elem.kind != PSK_Type) continue;
-
- switch (elem.type_expr->kind) {
- case Ast_Kind_Pointer_Type: {
- if (elem.actual->kind != Type_Kind_Pointer) break;
-
- bh_arr_push(elem_queue, ((PolySolveElem) {
- .type_expr = ((AstPointerType *) elem.type_expr)->elem,
- .kind = PSK_Type,
- .actual = elem.actual->Pointer.elem,
- }));
- break;
- }
-
- case Ast_Kind_Array_Type: {
- if (elem.actual->kind != Type_Kind_Array) break;
-
- bh_arr_push(elem_queue, ((PolySolveElem) {
- .type_expr = (AstType*) ((AstArrayType *) elem.type_expr)->count_expr,
- .kind = PSK_Value,
-
- // CLEANUP: Making an integer literal every time is very very very gross. This should
- // at least be cached or something.
- .value = (AstTyped *) make_int_literal(context.ast_alloc, elem.actual->Array.count)
- }));
-
- bh_arr_push(elem_queue, ((PolySolveElem) {
- .type_expr = ((AstArrayType *) elem.type_expr)->elem,
- .kind = PSK_Type,
- .actual = elem.actual->Array.elem,
- }));
- break;
- }
-
- case Ast_Kind_Slice_Type: {
- if (elem.actual->kind != Type_Kind_Slice) break;
-
- bh_arr_push(elem_queue, ((PolySolveElem) {
- .type_expr = ((AstSliceType *) elem.type_expr)->elem,
- .kind = PSK_Type,
- .actual = elem.actual->Slice.ptr_to_data->Pointer.elem,
- }));
- break;
- }
-
- case Ast_Kind_DynArr_Type: {
- if (elem.actual->kind != Type_Kind_DynArray) break;
-
- bh_arr_push(elem_queue, ((PolySolveElem) {
- .type_expr = ((AstDynArrType *) elem.type_expr)->elem,
- .kind = PSK_Type,
- .actual = elem.actual->DynArray.ptr_to_data->Pointer.elem,
- }));
- break;
- }
-
- case Ast_Kind_VarArg_Type:
- bh_arr_push(elem_queue, ((PolySolveElem) {
- .type_expr = ((AstVarArgType *) elem.type_expr)->elem,
- .kind = PSK_Type,
- .actual = actual,
- }));
- break;
-
- case Ast_Kind_Function_Type: {
- if (elem.actual->kind != Type_Kind_Function) break;
-
- AstFunctionType* ft = (AstFunctionType *) elem.type_expr;
-
- fori (i, 0, (i64) ft->param_count) {
- bh_arr_push(elem_queue, ((PolySolveElem) {
- .type_expr = ft->params[i],
- .kind = PSK_Type,
- .actual = elem.actual->Function.params[i],
- }));
- }
-
- bh_arr_push(elem_queue, ((PolySolveElem) {
- .type_expr = ft->return_type,
- .kind = PSK_Type,
- .actual = elem.actual->Function.return_type,
- }));
-
- break;
- }
-
- case Ast_Kind_Poly_Call_Type: {
- if (elem.actual->kind != Type_Kind_Struct) break;
- if (bh_arr_length(elem.actual->Struct.poly_sln) != bh_arr_length(((AstPolyCallType *) elem.type_expr)->params)) break;
-
- AstPolyCallType* pt = (AstPolyCallType *) elem.type_expr;
-
- fori (i, 0, bh_arr_length(pt->params)) {
- PolySolutionKind kind = elem.actual->Struct.poly_sln[i].kind;
- if (kind == PSK_Type) {
- bh_arr_push(elem_queue, ((PolySolveElem) {
- .kind = kind,
- .type_expr = (AstType *) pt->params[i],
- .actual = elem.actual->Struct.poly_sln[i].type,
- }));
- } else {
- bh_arr_push(elem_queue, ((PolySolveElem) {
- .kind = kind,
- .type_expr = (AstType *) pt->params[i],
- .value = elem.actual->Struct.poly_sln[i].value,
- }));
- }
- }
-
- break;
- }
-
- case Ast_Kind_Type_Compound: {
- if (elem.actual->kind != Type_Kind_Compound) break;
- if (elem.actual->Compound.count != (u32) bh_arr_length(((AstCompoundType *) elem.type_expr)->types)) break;
-
- AstCompoundType* ct = (AstCompoundType *) elem.type_expr;
-
- fori (i, 0, bh_arr_length(ct->types)) {
- bh_arr_push(elem_queue, ((PolySolveElem) {
- .kind = PSK_Type,
- .type_expr = ct->types[i],
- .actual = elem.actual->Compound.types[i],
- }));
- }
-
- break;
- }
-
- default: break;
- }
- }
-
- bh_arr_free(elem_queue);
-
- return result;
-}
-
-// NOTE: The job of this function is to take a polymorphic parameter and a set of arguments
-// and solve for the argument that matches the parameter. This is needed because polymorphic
-// procedure resolution has to happen before the named arguments are placed in their correct
-// positions.
-static AstTyped* lookup_param_in_arguments(AstPolyProc* pp, AstPolyParam* param, Arguments* args, char** err_msg) {
- bh_arr(AstTyped *) arg_arr = args->values;
- bh_arr(AstNamedValue *) named_values = args->named_values;
-
- // NOTE: This check is safe because currently the arguments given without a name
- // always map to the beginning indidies of the argument array.
- if (param->idx >= (u64) bh_arr_length(arg_arr)) {
- OnyxToken* param_name = pp->base_func->params[param->idx].local->token;
-
- bh_arr_each(AstNamedValue *, named_value, named_values) {
- if (token_equals(param_name, (*named_value)->token)) {
- return (AstTyped *) (*named_value)->value;
- }
- }
-
- // CLEANUP
- if (err_msg) *err_msg = "Not enough arguments to polymorphic procedure. This error message may not be entirely right.";
-
- } else {
- return (AstTyped *) arg_arr[param->idx];
- }
-
- return NULL;
-}
-
-// NOTE: The job of this function is to solve for type of AstPolySolution using the provided
-// information. It is asssumed that the "param" is of kind PPK_Poly_Type. This function uses
-// either the arguments provided, or a function type to compare against to pattern match for
-// the type that the parameter but be.
-static void solve_for_polymorphic_param_type(PolySolveResult* resolved, AstPolyProc* pp, AstPolyParam* param, PolyProcLookupMethod pp_lookup, ptr actual, char** err_msg) {
- Type* actual_type = NULL;
-
- switch (pp_lookup) {
- case PPLM_By_Arguments: {
- Arguments* args = (Arguments *) actual;
-
- AstTyped* typed_param = lookup_param_in_arguments(pp, param, args, err_msg);
- if (typed_param == NULL) return;
-
- actual_type = resolve_expression_type(typed_param);
- if (actual_type == NULL) return;
-
- break;
- }
-
- case PPLM_By_Function_Type: {
- Type* ft = (Type *) actual;
- if (param->idx >= ft->Function.param_count) {
- if (err_msg) *err_msg = "Incompatible polymorphic argument to function parameter.";
- return;
- }
-
- actual_type = ft->Function.params[param->idx];
- break;
- }
-
- default: return;
- }
-
- *resolved = solve_poly_type(param->poly_sym, param->type_expr, actual_type);
-}
-
-
-// NOTE: The job of this function is to look through the arguments provided and find a matching
-// value that is to be baked into the polymorphic procedures poly-scope. It expected that param
-// will be of kind PPK_Baked_Value.
-// CLEANUP: This function is kind of gross at the moment, because it handles different cases for
-// the argument kind. When type expressions (type_expr) become first-class types in the type
-// system, this code should be able to be a lot cleaner.
-static void solve_for_polymorphic_param_value(PolySolveResult* resolved, AstPolyProc* pp, AstPolyParam* param, PolyProcLookupMethod pp_lookup, ptr actual, char** err_msg) {
- if (pp_lookup != PPLM_By_Arguments) {
- *err_msg = "Function type cannot be used to solved for baked parameter value.";
- return;
- }
-
- Arguments* args = (Arguments *) actual;
- AstTyped* value = lookup_param_in_arguments(pp, param, args, err_msg);
- if (value == NULL) return;
-
- // HACK: Storing the original value because if this was an AstArgument, we need to flag
- // it as baked if it is determined that the argument is of the correct kind and type.
- AstTyped* orig_value = value;
- if (value->kind == Ast_Kind_Argument) {
- ((AstArgument *) orig_value)->is_baked = 0;
- value = ((AstArgument *) value)->value;
- }
-
- if (param->type_expr == (AstType *) &basic_type_type_expr) {
- if (!node_is_type((AstNode *) value)) {
- if (err_msg) *err_msg = "Expected type expression.";
- return;
- }
-
- Type* resolved_type = type_build_from_ast(context.ast_alloc, (AstType *) value);
- if (resolved_type == NULL) flag_to_yield = 1;
-
- *resolved = ((PolySolveResult) { PSK_Type, .actual = resolved_type });
-
- } else {
- resolve_expression_type(value);
-
- if ((value->flags & Ast_Flag_Comptime) == 0) {
- if (err_msg) *err_msg = "Expected compile-time known argument.";
- return;
- }
-
- if (param->type == NULL)
- param->type = type_build_from_ast(context.ast_alloc, param->type_expr);
- assert(param->type);
-
- if (!type_check_or_auto_cast(&value, param->type)) {
- if (err_msg) *err_msg = bh_aprintf(global_scratch_allocator,
- "The procedure '%s' expects a value of type '%s' for baked %d%s parameter, got '%s'.",
- get_function_name(pp->base_func),
- type_get_name(param->type),
- param->idx + 1,
- bh_num_suffix(param->idx + 1),
- node_get_type_name(value));
- return;
- }
-
- *resolved = ((PolySolveResult) { PSK_Value, value });
- }
-
- if (orig_value->kind == Ast_Kind_Argument) {
- ((AstArgument *) orig_value)->is_baked = 1;
- }
-}
-
-// NOTE: The job of this function is to take a polymorphic procedure, as well as a method of
-// solving for the polymorphic variables, in order to return an array of the solutions for all
-// of the polymorphic variables.
-static bh_arr(AstPolySolution) find_polymorphic_slns(AstPolyProc* pp, PolyProcLookupMethod pp_lookup, ptr actual, char** err_msg) {
- bh_arr(AstPolySolution) slns = NULL;
- bh_arr_new(global_heap_allocator, slns, bh_arr_length(pp->poly_params));
-
- // NOTE: "known solutions" are given through a '#solidify' directive. If this polymorphic
- // procedure is the result of a partially applied solidification, this array will be non-
- // empty and these solutions will be used.
- bh_arr_each(AstPolySolution, known_sln, pp->known_slns) bh_arr_push(slns, *known_sln);
-
- bh_arr_each(AstPolyParam, param, pp->poly_params) {
-
- // NOTE: First check to see if this polymorphic variable was already specified in the
- // known solutions.
- b32 already_solved = 0;
- bh_arr_each(AstPolySolution, known_sln, pp->known_slns) {
- if (token_equals(param->poly_sym->token, known_sln->poly_sym->token)) {
- already_solved = 1;
- break;
- }
- }
- if (already_solved) continue;
-
- // NOTE: Solve for the polymorphic parameter's value
- PolySolveResult resolved = { PSK_Undefined };
- switch (param->kind) {
- case PPK_Poly_Type: solve_for_polymorphic_param_type (&resolved, pp, param, pp_lookup, actual, err_msg); break;
- case PPK_Baked_Value: solve_for_polymorphic_param_value(&resolved, pp, param, pp_lookup, actual, err_msg); break;
-
- default: if (err_msg) *err_msg = "Invalid polymorphic parameter kind. This is a compiler bug.";
- }
-
- if (flag_to_yield) goto sln_not_found;
-
- switch (resolved.kind) {
- case PSK_Undefined:
- // NOTE: If no error message has been assigned to why this polymorphic parameter
- // resolution was unsuccessful, provide a basic dummy one.
- if (err_msg && *err_msg == NULL)
- *err_msg = bh_aprintf(global_scratch_allocator,
- "Unable to solve for polymorphic variable '%b'.",
- param->poly_sym->token->text,
- param->poly_sym->token->length);
-
- goto sln_not_found;
-
- case PSK_Type:
- bh_arr_push(slns, ((AstPolySolution) {
- .kind = PSK_Type,
- .poly_sym = param->poly_sym,
- .type = resolved.actual,
- }));
- break;
-
- case PSK_Value:
- bh_arr_push(slns, ((AstPolySolution) {
- .kind = PSK_Value,
- .poly_sym = param->poly_sym,
- .value = resolved.value,
- }));
- break;
- }
- }
-
- return slns;
-
- sln_not_found:
- bh_arr_free(slns);
- return NULL;
-}
-
-// NOTE: The job of this function is to be a wrapper to other functions, providing an error
-// message if a solution could not be found. This can't be merged with polymorphic_proc_solidify
-// because polymorphic_proc_try_solidify uses the aforementioned function.
-AstFunction* polymorphic_proc_lookup(AstPolyProc* pp, PolyProcLookupMethod pp_lookup, ptr actual, OnyxToken* tkn) {
- ensure_polyproc_cache_is_created(pp);
-
- char *err_msg = NULL;
- bh_arr(AstPolySolution) slns = find_polymorphic_slns(pp, pp_lookup, actual, &err_msg);
- if (slns == NULL) {
- if (flag_to_yield) {
- flag_to_yield = 0;
- return (AstFunction *) &node_that_signals_a_yield;
- }
-
- if (err_msg != NULL) onyx_report_error(tkn->pos, err_msg);
- else onyx_report_error(tkn->pos, "Some kind of error occured when generating a polymorphic procedure. You hopefully will not see this");
-
- return NULL;
- }
-
- AstFunction* result = polymorphic_proc_solidify(pp, slns, tkn);
-
- bh_arr_free(slns);
- return result;
-}
-
-AstFunction* polymorphic_proc_solidify(AstPolyProc* pp, bh_arr(AstPolySolution) slns, OnyxToken* tkn) {
- ensure_polyproc_cache_is_created(pp);
-
- // NOTE: Check if a version of this polyproc has already been created.
- char* unique_key = build_poly_slns_unique_key(slns);
- if (bh_table_has(AstSolidifiedFunction, pp->concrete_funcs, unique_key)) {
- AstSolidifiedFunction solidified_func = bh_table_get(AstSolidifiedFunction, pp->concrete_funcs, unique_key);
-
- // NOTE: If this solution was originally created from a "build_only_header" call, then the body
- // will not have been or type checked, or anything. This ensures that the body is copied, the
- // entities are created and entered into the pipeline.
- ensure_solidified_function_has_body(pp, solidified_func);
-
- // NOTE: Again, if this came from a "build_only_header" call, then there was no known token and
- // the "generated_from" member will be null. It is best to set it here so errors reported in that
- // function can report where the polymorphic instantiation occurred.
- if (solidified_func.func->generated_from == NULL)
- solidified_func.func->generated_from = tkn;
-
- return solidified_func.func;
- }
-
- AstSolidifiedFunction solidified_func = generate_solidified_function(pp, slns, tkn, 0);
-
- // NOTE: Cache the function for later use, reducing duplicate functions.
- bh_table_put(AstSolidifiedFunction, pp->concrete_funcs, unique_key, solidified_func);
-
- if (!add_solidified_function_entities(solidified_func, 0)) {
- onyx_report_error(tkn->pos, "Error in polymorphic procedure header generated from this call site.");
- return NULL;
- }
-
- return solidified_func.func;
-}
-
-// NOTE: This can return either a AstFunction or an AstPolyProc, depending if enough parameters were
-// supplied to remove all the polymorphic variables from the function.
-AstNode* polymorphic_proc_try_solidify(AstPolyProc* pp, bh_arr(AstPolySolution) slns, OnyxToken* tkn) {
- i32 valid_argument_count = 0;
-
- bh_arr_each(AstPolySolution, sln, slns) {
- b32 found_match = 0;
-
- bh_arr_each(AstPolyParam, param, pp->poly_params) {
- if (token_equals(sln->poly_sym->token, param->poly_sym->token)) {
- found_match = 1;
- break;
- }
- }
-
- if (found_match) {
- valid_argument_count++;
- } else {
- onyx_report_error(tkn->pos, "'%b' is not a type variable of '%b'.",
- sln->poly_sym->token->text, sln->poly_sym->token->length,
- pp->token->text, pp->token->length);
- return (AstNode *) pp;
- }
- }
-
- if (valid_argument_count == bh_arr_length(pp->poly_params)) {
- return (AstNode *) polymorphic_proc_solidify(pp, slns, tkn);
-
- } else {
- // HACK: Some of these initializations assume that the entity for this polyproc has
- // made it through the symbol resolution phase.
- // - brendanfh 2020/12/25
- AstPolyProc* new_pp = onyx_ast_node_new(context.ast_alloc, sizeof(AstPolyProc), Ast_Kind_Polymorphic_Proc);
- new_pp->token = pp->token; // TODO: Change this to be the solidify->token
- new_pp->base_func = pp->base_func;
- new_pp->flags = pp->flags;
- new_pp->poly_params = pp->poly_params;
-
- ensure_polyproc_cache_is_created(pp);
- new_pp->concrete_funcs = pp->concrete_funcs;
-
- new_pp->known_slns = NULL;
- bh_arr_new(global_heap_allocator, new_pp->known_slns, bh_arr_length(pp->known_slns) + bh_arr_length(slns));
-
- bh_arr_each(AstPolySolution, sln, pp->known_slns) bh_arr_push(new_pp->known_slns, *sln);
- bh_arr_each(AstPolySolution, sln, slns) bh_arr_push(new_pp->known_slns, *sln);
-
- return (AstNode *) new_pp;
- }
-}
-
-AstFunction* polymorphic_proc_build_only_header(AstPolyProc* pp, PolyProcLookupMethod pp_lookup, ptr actual) {
- bh_arr(AstPolySolution) slns = find_polymorphic_slns(pp, pp_lookup, actual, NULL);
- if (slns == NULL) return NULL;
-
- ensure_polyproc_cache_is_created(pp);
-
- AstSolidifiedFunction solidified_func;
-
- char* unique_key = build_poly_slns_unique_key(slns);
- if (bh_table_has(AstSolidifiedFunction, pp->concrete_funcs, unique_key)) {
- solidified_func = bh_table_get(AstSolidifiedFunction, pp->concrete_funcs, unique_key);
-
- } else {
- // NOTE: This function is only going to have the header of it correctly created.
- // Nothing should happen to this function's body or else the original will be corrupted.
- // - brendanfh 2021/01/10
- solidified_func = generate_solidified_function(pp, slns, NULL, 1);
- }
-
- if (solidified_func.header_complete) return solidified_func.func;
-
- Entity func_header_entity = {
- .state = Entity_State_Resolve_Symbols,
- .type = Entity_Type_Function_Header,
- .function = solidified_func.func,
- .package = NULL,
- .scope = solidified_func.poly_scope,
- };
-
- b32 successful = entity_bring_to_state(&func_header_entity, Entity_State_Code_Gen);
- if (onyx_has_errors()) {
- onyx_errors_print();
- onyx_clear_errors();
- return NULL;
- }
-
- solidified_func.header_complete = successful;
-
- // NOTE: Cache the function for later use, only if it didn't have errors in its header.
- bh_table_put(AstSolidifiedFunction, pp->concrete_funcs, unique_key, solidified_func);
-
- return solidified_func.func;
-}
-
-//
-// Overloaded Procedures
-//
-
-//
-// @Cleanup: Everything having to do with overload resolving!
-// Things that need to be available:
-// * A copy of the arguments list that can be mutated
-// - The named_values do not need to be copied, because they are not modified when fill_in_arguments happens.
-// - Only values needs to have a copy available
-// - This copy needs to be reset after checking every option
-//
-// Steps needed to check if an overload option is "the one":
-// 1. Figure out what the overload is
-// a. If polymorphic, generate the header for the procedure only
-// 2. Place the arguments in the copy, according to the overload option
-// 3. Ensure the option has a type filled out
-// 4. For each argument
-// a. Ensure it has a place to go (not too many arguments)
-// b. Ensure it has a type
-// c. Ensure the types match (currently there could be a problem if an option is attempted and doesn't work all the way that polymorphic procedures as arguments could still be solidified)
-//
-// Additional features that this needs to account for:
-// * Resolving an overload from a list of parameter types
-// * Resolving an overload from a TypeFunction (so an overloaded procedure can be passed as a parameter)
-//
-
-void add_overload_option(bh_arr(OverloadOption)* poverloads, u64 precedence, AstTyped* overload) {
- bh_arr(OverloadOption) overloads = *poverloads;
-
- i32 index = -1;
- fori (i, 0, bh_arr_length(overloads)) {
- if (overloads[i].precedence > precedence) {
- index = i;
- break;
- }
- }
-
- if (index < 0) {
- bh_arr_push(overloads, ((OverloadOption) {
- .precedence = precedence,
- .option = overload,
- }));
-
- } else {
- bh_arr_insertn(overloads, index, 1);
- overloads[index].precedence = precedence;
- overloads[index].option = overload;
- }
-
- *poverloads = overloads;
-}
-
-// NOTE: The job of this function is to take a set of overloads, and traverse it to add all possible
-// overloads that are reachable. This is slightly more difficult than it may seem. In this language,
-// overloaded procedures have a strict ordering to their overloads, which determines how the correct
-// match will be found. This was not very complicated until overloaded procedures could be used as
-// overload options. This means that you could create an "infinite loop" of overloads like so:
-//
-// o1 :: { o2 :: {
-// (...) { ... }, (...) { ... },
-// o2 o1
-// } }
-//
-// Obviously, this is not really an infinite loop. It just means that all options are available if
-// o1 or o2 are called. The difference between o1 and o2 is the order that the overloads will be
-// searched. To build the the list of overloads, a hashmap is used to prevent the problem from being
-// O(n^2), even though n would (probably) be small. bh_imap has the useful property that it maintains
-// an "entries" array that, so long as nothing is ever removed from it, will maintain the order in
-// which entries were put into the map. This is useful because a simple recursive algorithm can
-// collect all the overloads into the map, and also use the map to provide a base case.
-void build_all_overload_options(bh_arr(OverloadOption) overloads, bh_imap* all_overloads) {
- bh_arr_each(OverloadOption, overload, overloads) {
- if (bh_imap_has(all_overloads, (u64) overload->option)) continue;
-
- bh_imap_put(all_overloads, (u64) overload->option, 1);
-
- if (overload->option->kind == Ast_Kind_Overloaded_Function) {
- AstOverloadedFunction* sub_overload = (AstOverloadedFunction *) overload->option;
- build_all_overload_options(sub_overload->overloads, all_overloads);
- }
- }
-}
-
-AstTyped* find_matching_overload_by_arguments(bh_arr(OverloadOption) overloads, Arguments* param_args, b32* should_yield) {
- Arguments args;
- arguments_clone(&args, param_args);
- arguments_ensure_length(&args, bh_arr_length(args.values) + bh_arr_length(args.named_values));
-
- // CLEANUP SPEED: This currently rebuilds the complete set of overloads every time one is looked up.
- // This should be cached in the AstOverloadedFunction or somewhere like that.
- bh_imap all_overloads;
- bh_imap_init(&all_overloads, global_heap_allocator, bh_arr_length(overloads) * 2);
- build_all_overload_options(overloads, &all_overloads);
-
- AstTyped *matched_overload = NULL;
-
- bh_arr_each(bh__imap_entry, entry, all_overloads.entries) {
- AstTyped* node = (AstTyped *) entry->key;
- arguments_copy(&args, param_args);
-
- AstFunction* overload = NULL;
- switch (node->kind) {
- case Ast_Kind_Function: overload = (AstFunction *) node; break;
- case Ast_Kind_Macro: overload = macro_resolve_header((AstMacro *) node, param_args, NULL); break;
- case Ast_Kind_Polymorphic_Proc: overload = polymorphic_proc_build_only_header((AstPolyProc *) node, PPLM_By_Arguments, param_args); break;
- }
-
- // NOTE: Overload is not something that is known to be overloadable.
- if (overload == NULL) continue;
- if (overload->kind != Ast_Kind_Function) continue;
- if (overload->type == NULL) {
- // If it was not possible to create the type for this procedure, tell the
- // caller that this should yield and try again later.
- if (should_yield) *should_yield = 1;
-
- // return and not continue because if the overload that didn't have a type will
- // work in the future, then it has to take precedence over the other options available.
- return NULL;
- }
- assert(overload->type->kind == Type_Kind_Function);
-
- // NOTE: If the arguments cannot be placed successfully in the parameters list
- if (!fill_in_arguments(&args, (AstNode *) overload, NULL)) continue;
-
- TypeFunction* ol_type = &overload->type->Function;
- if (bh_arr_length(args.values) < (i32) ol_type->needed_param_count) continue;
-
- b32 all_arguments_work = 1;
- fori (i, 0, bh_arr_length(args.values)) {
- if (i >= ol_type->param_count) {
- all_arguments_work = 0;
- break;
- }
-
- Type* type_to_match = ol_type->params[i];
- AstTyped** value = &args.values[i];
-
- if (type_to_match->kind == Type_Kind_VarArgs) type_to_match = type_to_match->VarArgs.ptr_to_data->Pointer.elem;
- if ((*value)->kind == Ast_Kind_Argument) {
- // :ArgumentResolvingIsComplicated
- if (((AstArgument *) (*value))->is_baked) continue;
- value = &((AstArgument *) *value)->value;
- }
-
- if (!type_check_or_auto_cast(value, type_to_match)) {
- all_arguments_work = 0;
- break;
- }
- }
-
- if (all_arguments_work) {
- matched_overload = node;
- break;
- }
- }
-
- bh_imap_free(&all_overloads);
- bh_arr_free(args.values);
- return matched_overload;
-}
-
-AstTyped* find_matching_overload_by_type(bh_arr(OverloadOption) overloads, Type* type) {
- if (type->kind != Type_Kind_Function) return NULL;
-
- bh_imap all_overloads;
- bh_imap_init(&all_overloads, global_heap_allocator, bh_arr_length(overloads) * 2);
- build_all_overload_options(overloads, &all_overloads);
-
- AstTyped *matched_overload = NULL;
-
- bh_arr_each(bh__imap_entry, entry, all_overloads.entries) {
- AstTyped* node = (AstTyped *) entry->key;
- if (node->kind == Ast_Kind_Overloaded_Function) continue;
-
- if (type_check_or_auto_cast(&node, type)) {
- matched_overload = node;
- break;
- }
- }
-
- bh_imap_free(&all_overloads);
- return matched_overload;
-}
-
-void report_unable_to_match_overload(AstCall* call) {
- char* arg_str = bh_alloc(global_scratch_allocator, 1024);
- arg_str[0] = '\0';
-
- bh_arr_each(AstTyped *, arg, call->args.values) {
- strncat(arg_str, node_get_type_name(*arg), 1023);
-
- if (arg != &bh_arr_last(call->args.values))
- strncat(arg_str, ", ", 1023);
- }
-
- if (bh_arr_length(call->args.named_values) > 0) {
- if (bh_arr_length(call->args.values) > 0) {
- strncat(arg_str, ", ", 1023);
- }
-
- bh_arr_each(AstNamedValue *, named_value, call->args.named_values) {
- token_toggle_end((*named_value)->token);
- strncat(arg_str, (*named_value)->token->text, 1023);
- token_toggle_end((*named_value)->token);
-
- strncat(arg_str, "=", 1023);
- strncat(arg_str, node_get_type_name((*named_value)->value), 1023); // CHECK: this might say 'unknown'.
-
- if (named_value != &bh_arr_last(call->args.named_values))
- strncat(arg_str, ", ", 1023);
- }
- }
-
- onyx_report_error(call->token->pos, "Unable to match overloaded function with provided argument types: (%s)", arg_str);
-
- bh_free(global_scratch_allocator, arg_str);
-}
-
-
-//
-// Macros
-//
-//
-// TODO: Write this documentation
-void expand_macro(AstCall** pcall, AstFunction* template) {
- AstCall* call = *pcall;
- AstMacro* macro = (AstMacro *) call->callee;
- assert(macro->kind == Ast_Kind_Macro);
-
- assert(template->kind == Ast_Kind_Function);
- assert(template->type != NULL);
- assert(template->type->kind == Type_Kind_Function);
-
- AstBlock* expansion = (AstBlock *) ast_clone(context.ast_alloc, template->body);
- expansion->rules = Block_Rule_Macro;
- expansion->scope = NULL;
- expansion->next = call->next;
-
- AstNode* subst = (AstNode *) expansion;
-
- if (template->type->Function.return_type != &basic_types[Basic_Kind_Void]) {
- AstDoBlock* doblock = (AstDoBlock *) onyx_ast_node_new(context.ast_alloc, sizeof(AstDoBlock), Ast_Kind_Do_Block);
- doblock->token = expansion->token;
- doblock->block = expansion;
- doblock->type = template->type->Function.return_type;
- doblock->next = expansion->next;
- expansion->next = NULL;
-
- subst = (AstNode *) doblock;
- }
-
- Scope* argument_scope = scope_create(context.ast_alloc, NULL, call->token->pos);
- if (expansion->binding_scope != NULL)
- scope_include(argument_scope, expansion->binding_scope, call->token->pos);
- expansion->binding_scope = argument_scope;
-
- // HACK HACK HACK This is probably very wrong. I don't know what guarentees that
- // the paramters and arguments are going to be in the same order exactly.
- fori (i, 0, bh_arr_length(call->args.values)) {
- symbol_introduce(argument_scope,
- template->params[i].local->token,
- (AstNode *) ((AstArgument *) call->args.values[i])->value);
- }
-
- if (template->flags & Ast_Flag_From_Polymorphism) {
- // SLOW DUMB HACKY WAY TO DO THIS!!!!! FIX IT!!!!!
-
- AstPolyProc* pp = (AstPolyProc *) macro->body;
- bh_table_each_start(AstSolidifiedFunction, pp->concrete_funcs);
-
- if (value.func == template) {
- scope_include(argument_scope, value.poly_scope, call->token->pos);
- break;
- }
-
- bh_table_each_end;
- }
-
- *(AstNode **) pcall = subst;
- return;
-}
-
-AstFunction* macro_resolve_header(AstMacro* macro, Arguments* args, OnyxToken* callsite) {
- switch (macro->body->kind) {
- case Ast_Kind_Function: return (AstFunction *) macro->body;
-
- case Ast_Kind_Polymorphic_Proc: {
- AstPolyProc* pp = (AstPolyProc *) macro->body;
- ensure_polyproc_cache_is_created(pp);
-
- char* err_msg=NULL;
- bh_arr(AstPolySolution) slns = find_polymorphic_slns(pp, PPLM_By_Arguments, args, &err_msg);
-
- if (slns == NULL) {
- if (flag_to_yield) {
- flag_to_yield = 0;
- return (AstFunction *) &node_that_signals_a_yield;
- }
-
- if (callsite) onyx_report_error(callsite->pos, err_msg);
-
- return NULL;
- }
-
- // CLEANUP Copy'n'pasted from polymorphic_proc_build_only_header
- AstSolidifiedFunction solidified_func;
-
- char* unique_key = build_poly_slns_unique_key(slns);
- if (bh_table_has(AstSolidifiedFunction, pp->concrete_funcs, unique_key)) {
- solidified_func = bh_table_get(AstSolidifiedFunction, pp->concrete_funcs, unique_key);
-
- } else {
- // NOTE: This function is only going to have the header of it correctly created.
- // Nothing should happen to this function's body or else the original will be corrupted.
- // - brendanfh 2021/01/10
- solidified_func = generate_solidified_function(pp, slns, callsite, 1);
- }
-
- if (solidified_func.header_complete) return solidified_func.func;
-
- Entity func_header_entity = {
- .state = Entity_State_Resolve_Symbols,
- .type = Entity_Type_Function_Header,
- .function = solidified_func.func,
- .package = NULL,
- .scope = solidified_func.poly_scope,
- };
-
- b32 successful = entity_bring_to_state(&func_header_entity, Entity_State_Code_Gen);
- if (onyx_has_errors()) return NULL;
-
- solidified_func.header_complete = successful;
-
- bh_table_put(AstSolidifiedFunction, pp->concrete_funcs, unique_key, solidified_func);
- if (!successful) return (AstFunction *) &node_that_signals_a_yield;
-
- return solidified_func.func;
- }
-
- default: assert(("Bad macro body type.", 0));
- }
-}
-
-//
-// Polymorphic Structures
-//
-//
-// Currently, I am not very happy about how polymorphic structure generation works. My biggest problem
-// with it is that it is very different from the polymorhic procedure generation. Also, it needs to
-// completely generate and check the structure right away, which means there is a lot of up-front work
-// done here that could probably be done elsewhere. This really relates to a large problem in the compiler
-// that types need to be known completely by the time symbol resolution is done, even though that
-// information shouldn't need to be known until right before the types are checked.
-//
-// The above documentation is very incorrect but I don't want to fix it right now. Basically, polymorphic
-// structures now have a delay instantiation phase and are not forced to be completed immediately.
-
-char* build_poly_struct_name(AstPolyStructType* ps_type, Type* cs_type) {
- char name_buf[256];
- fori (i, 0, 256) name_buf[i] = 0;
-
- strncat(name_buf, ps_type->name, 255);
- strncat(name_buf, "(", 255);
- bh_arr_each(AstPolySolution, ptype, cs_type->Struct.poly_sln) {
- if (ptype != cs_type->Struct.poly_sln)
- strncat(name_buf, ", ", 255);
-
- // This logic will have to be other places as well.
-
- switch (ptype->kind) {
- case PSK_Undefined: assert(0); break;
- case PSK_Type: strncat(name_buf, type_get_name(ptype->type), 255); break;
- case PSK_Value: {
- // FIX
- AstNode* value = strip_aliases((AstNode *) ptype->value);
-
- if (value->kind == Ast_Kind_NumLit) {
- AstNumLit* nl = (AstNumLit *) value;
- if (type_is_integer(nl->type)) {
- strncat(name_buf, bh_bprintf("%l", nl->value.l), 127);
- } else {
- strncat(name_buf, "numlit (FIX ME)", 127);
- }
- } else if (value->kind == Ast_Kind_Code_Block) {
- AstCodeBlock* code = (AstCodeBlock *) value;
- OnyxFilePos code_loc = code->token->pos;
- strncat(name_buf, bh_bprintf("code at %s:%d,%d", code_loc.filename, code_loc.line, code_loc.column), 127);
- } else {
- strncat(name_buf, "<expr>", 127);
- }
-
- break;
- }
- }
- }
- strncat(name_buf, ")", 255);
-
- return bh_aprintf(global_heap_allocator, "%s", name_buf);
-}
-
-AstStructType* polymorphic_struct_lookup(AstPolyStructType* ps_type, bh_arr(AstPolySolution) slns, OnyxFilePos pos) {
- // @Cleanup
- assert(ps_type->scope != NULL);
-
- if (ps_type->concrete_structs == NULL) {
- bh_table_init(global_heap_allocator, ps_type->concrete_structs, 16);
- }
-
- if (bh_arr_length(slns) != bh_arr_length(ps_type->poly_params)) {
- onyx_report_error(pos, "Wrong number of arguments for '%s'. Expected %d, got %d",
- ps_type->name,
- bh_arr_length(ps_type->poly_params),
- bh_arr_length(slns));
-
- return NULL;
- }
-
- i32 i = 0;
- bh_arr_each(AstPolySolution, sln, slns) {
- sln->poly_sym = (AstNode *) &ps_type->poly_params[i];
-
- PolySolutionKind expected_kind = PSK_Undefined;
- if ((AstNode *) ps_type->poly_params[i].type_node == (AstNode *) &basic_type_type_expr) {
- expected_kind = PSK_Type;
- } else {
- expected_kind = PSK_Value;
- }
-
- if (sln->kind != expected_kind) {
- if (expected_kind == PSK_Type)
- onyx_report_error(pos, "Expected type expression for %d%s argument.", i + 1, bh_num_suffix(i + 1));
-
- if (expected_kind == PSK_Value)
- onyx_report_error(pos, "Expected value expression of type '%s' for %d%s argument.",
- type_get_name(ps_type->poly_params[i].type),
- i + 1, bh_num_suffix(i + 1));
-
- return NULL;
- }
-
- if (sln->kind == PSK_Value) {
- resolve_expression_type(sln->value);
-
- if ((sln->value->flags & Ast_Flag_Comptime) == 0) {
- onyx_report_error(pos,
- "Expected compile-time known argument for '%b'.",
- sln->poly_sym->token->text,
- sln->poly_sym->token->length);
- return NULL;
- }
-
- if (!types_are_compatible(sln->value->type, ps_type->poly_params[i].type)) {
- onyx_report_error(pos, "Expected compile-time argument of type '%s', got '%s'.",
- type_get_name(ps_type->poly_params[i].type),
- type_get_name(sln->value->type));
- return NULL;
- }
- }
-
- i++;
- }
-
- char* unique_key = build_poly_slns_unique_key(slns);
- if (bh_table_has(AstStructType *, ps_type->concrete_structs, unique_key)) {
- AstStructType* concrete_struct = bh_table_get(AstStructType *, ps_type->concrete_structs, unique_key);
-
- if (concrete_struct->entity_type->state < Entity_State_Check_Types) {
- return NULL;
- }
-
- Type* cs_type = type_build_from_ast(context.ast_alloc, (AstType *) concrete_struct);
- if (!cs_type) return NULL;
-
- if (cs_type->Struct.poly_sln == NULL) cs_type->Struct.poly_sln = bh_arr_copy(global_heap_allocator, slns);
- if (cs_type->Struct.name == NULL) cs_type->Struct.name = build_poly_struct_name(ps_type, cs_type);
-
- return concrete_struct;
- }
-
- Scope* sln_scope = scope_create(context.ast_alloc, ps_type->scope, ps_type->token->pos);
- insert_poly_slns_into_scope(sln_scope, slns);
-
- AstStructType* concrete_struct = (AstStructType *) ast_clone(context.ast_alloc, ps_type->base_struct);
- bh_table_put(AstStructType *, ps_type->concrete_structs, unique_key, concrete_struct);
-
- add_entities_for_node(NULL, (AstNode *) concrete_struct, sln_scope, NULL);
- return NULL;
-}
-
-b32 entity_bring_to_state(Entity* ent, EntityState state) {
- EntityState last_state = ent->state;
-
- while (ent->state != state) {
- switch (ent->state) {
- case Entity_State_Resolve_Symbols: symres_entity(ent); break;
- case Entity_State_Check_Types: check_entity(ent); break;
- case Entity_State_Code_Gen: emit_entity(ent); break;
-
- default: return 0;
- }
-
- if (ent->state == last_state) return 0;
- last_state = ent->state;
-
- if (onyx_has_errors()) return 0;
- }
-
- return 1;
-}
-
-
-//
-// Arguments resolving
-//
-static i32 lookup_idx_by_name(AstNode* provider, char* name) {
- switch (provider->kind) {
- case Ast_Kind_Struct_Literal: {
- AstStructLiteral* sl = (AstStructLiteral *) provider;
- assert(sl->type);
-
- StructMember s;
- if (!type_lookup_member(sl->type, name, &s)) return -1;
- if (s.included_through_use) return -1;
-
- return s.idx;
- }
-
- case Ast_Kind_Function: {
- AstFunction* func = (AstFunction *) provider;
-
- i32 param_idx = -1;
- i32 idx = 0;
- bh_arr_each(AstParam, param, func->params) {
- if (token_text_equals(param->local->token, name)) {
- param_idx = idx;
- break;
- }
-
- idx++;
- }
-
- return param_idx;
- }
-
- default: return -1;
- }
-}
-
-static AstNode* lookup_default_value_by_idx(AstNode* provider, i32 idx) {
- switch (provider->kind) {
- case Ast_Kind_Struct_Literal: {
- AstStructLiteral* sl = (AstStructLiteral *) provider;
- assert(sl->type);
-
- if (sl->type->kind == Type_Kind_Struct) {
- bh_arr(StructMember *) memarr = sl->type->Struct.memarr;
- if (idx >= bh_arr_length(memarr)) return NULL;
-
- return (AstNode *) *memarr[idx]->initial_value;
- }
-
- return NULL;
- }
-
- case Ast_Kind_Function: {
- AstFunction* func = (AstFunction *) provider;
-
- AstTyped* default_value = func->params[idx].default_value;
- if (default_value == NULL) return NULL;
-
- AstArgument* arg = make_argument(context.ast_alloc, default_value);
- return (AstNode *) arg;
- }
-
- default: return NULL;
- }
-}
-
-static i32 maximum_argument_count(AstNode* provider) {
- switch (provider->kind) {
- case Ast_Kind_Struct_Literal: {
- AstStructLiteral* sl = (AstStructLiteral *) provider;
- assert(sl->type);
-
- return type_structlike_mem_count(sl->type);
- }
- }
-
- // NOTE: This returns int_max for anything other than struct literals because the
- // bounds checking on the arguments will be done elsewhere.
- return 0x7fffffff;
-}
-
-// NOTE: The values array can be partially filled out, and is the resulting array.
-// Returns if all the values were filled in.
-b32 fill_in_arguments(Arguments* args, AstNode* provider, char** err_msg) {
-
- { // Delete baked arguments
- // :ArgumentResolvingIsComplicated
- i32 max = bh_arr_length(args->values);
- fori (i, 0, max) {
- AstTyped* value = args->values[i];
- if (value == NULL) continue;
-
- if (value->kind == Ast_Kind_Argument) {
- if (((AstArgument *) value)->is_baked) {
- i--;
- max--;
- bh_arr_deleten(args->values, i, 1);
- }
- }
- }
- }
-
- if (args->named_values != NULL) {
- bh_arr_each(AstNamedValue *, p_named_value, args->named_values) {
- AstNamedValue* named_value = *p_named_value;
-
- if (named_value->value->kind == Ast_Kind_Argument) {
- if (((AstArgument *) named_value->value)->is_baked) {
- // :ArgumentResolvingIsComplicated
- bh_arr_set_length(args->values, bh_arr_length(args->values) - 1);
- continue;
- }
- }
-
- token_toggle_end(named_value->token);
- i32 idx = lookup_idx_by_name(provider, named_value->token->text);
- if (idx == -1) {
- if (err_msg) *err_msg = bh_aprintf(global_scratch_allocator, "'%s' is not a valid named parameter here.", named_value->token->text);
- token_toggle_end(named_value->token);
- return 0;
- }
-
- // assert(idx < bh_arr_length(args->values));
- if (idx >= bh_arr_length(args->values)) {
- token_toggle_end(named_value->token);
- return 0;
- }
-
- if (args->values[idx] != NULL && args->values[idx] != named_value->value) {
- if (err_msg) *err_msg = bh_aprintf(global_scratch_allocator, "Multiple values given for parameter named '%s'.", named_value->token->text);
- token_toggle_end(named_value->token);
- return 0;
- }
-
- args->values[idx] = named_value->value;
- token_toggle_end(named_value->token);
- }
- }
-
- b32 success = 1;
- fori (idx, 0, bh_arr_length(args->values)) {
- if (args->values[idx] == NULL) args->values[idx] = (AstTyped *) lookup_default_value_by_idx(provider, idx);
- if (args->values[idx] == NULL) {
- *err_msg = bh_aprintf(global_scratch_allocator, "No value given for %d%s argument.", idx + 1, bh_num_suffix(idx + 1));
- success = 0;
- }
- }
-
- i32 maximum_arguments = maximum_argument_count(provider);
- if (bh_arr_length(args->values) > maximum_arguments) {
- *err_msg = bh_aprintf(global_scratch_allocator, "Too many values provided. Expected at most %d.", maximum_arguments);
- success = 0;
- }
-
- return success;
-}
-
-//
-// String parsing helpers
-//
-u32 char_to_base16_value(char x) {
- if (x >= '0' && x <= '9') return (u32) (x - '0');
- if (x >= 'A' && x <= 'F') return (u32) (x - 'A' + 10);
- if (x >= 'a' && x <= 'f') return (u32) (x - 'a' + 10);
- return 0xffffffff;
-}
-
-i32 string_process_escape_seqs(char* dest, char* src, i32 len) {
- i32 total_len = 0;
- for (i32 i = 0; i < len; i++) {
- if (src[i] == '\\') {
- i++;
- switch (src[i]) {
- case '0': *dest++ = '\0'; total_len++; break;
- case 'a': *dest++ = '\a'; total_len++; break;
- case 'b': *dest++ = '\b'; total_len++; break;
- case 'f': *dest++ = '\f'; total_len++; break;
- case 'n': *dest++ = '\n'; total_len++; break;
- case 't': *dest++ = '\t'; total_len++; break;
- case 'r': *dest++ = '\r'; total_len++; break;
- case 'v': *dest++ = '\v'; total_len++; break;
- case 'e': *dest++ = '\e'; total_len++; break;
- case '"': *dest++ = '"'; total_len++; break;
- case '\\': *dest++ = '\\'; total_len++; break;
- case 'x': {
- u8 ch1 = src[i + 1];
- u8 ch2 = src[i + 2];
- *dest++ = (i8) (char_to_base16_value(ch1) << 4 | char_to_base16_value(ch2));
- total_len++;
- i += 2;
- break;
- }
- default: *dest++ = '\\';
- *dest++ = src[i];
- total_len += 2;
- }
- } else {
- *dest++ = src[i];
- total_len += 1;
- }
- }
-
- // NOTE: Gotta null terminate
- *dest = 0;
-
- return total_len;
-}
-
-char* lookup_included_file(char* filename, char* relative_to, b32 add_onyx_suffix, b32 search_included_folders) {
- assert(relative_to != NULL);
-
- static char path[256];
- fori (i, 0, 256) path[i] = 0;
-
- static char fn[128];
- fori (i, 0, 128) fn[i] = 0;
-
- if (!bh_str_ends_with(filename, ".onyx") && add_onyx_suffix) {
- bh_snprintf(fn, 128, "%s.onyx", filename);
- } else {
- bh_snprintf(fn, 128, "%s", filename);
- }
-
-#if defined(_BH_LINUX)
- #define DIR_SEPARATOR '/'
-#elif defined(_BH_WINDOWS)
- #define DIR_SEPARATOR '\\'
-#endif
-
- fori (i, 0, 128) if (fn[i] == '/') fn[i] = DIR_SEPARATOR;
-
- if (bh_str_starts_with(filename, "./")) {
- if (relative_to[strlen(relative_to) - 1] != DIR_SEPARATOR)
- bh_snprintf(path, 256, "%s%c%s", relative_to, DIR_SEPARATOR, fn + 2);
- else
- bh_snprintf(path, 256, "%s%s", relative_to, fn + 2);
-
- if (bh_file_exists(path)) return bh_path_get_full_name(path, global_scratch_allocator);
-
- return fn;
- }
-
- if (search_included_folders) {
- bh_arr_each(const char *, folder, context.options->included_folders) {
- if ((*folder)[strlen(*folder) - 1] != DIR_SEPARATOR)
- bh_snprintf(path, 256, "%s%c%s", *folder, DIR_SEPARATOR, fn);
- else
- bh_snprintf(path, 256, "%s%s", *folder, fn);
-
- if (bh_file_exists(path)) return bh_path_get_full_name(path, global_scratch_allocator);
- }
- }
-
- return fn;
-
-#undef DIR_SEPARATOR
-}
-
+++ /dev/null
-//
-// There are several things I'm seeing in this file that I want to clean up.
-// They are:
-// [x] remove the need to know if the stack is needed before generating the function.
-// Just leave 5 nops at the beginning because they will be automatically removed
-// by the WASM outputter.
-// [x] remove the need to have "allocate_exprs" on blocks and in functions. This will
-// be easy once the above is done.
-// [x] there should be a better way to emit pending deferred statements because there
-// is some code duplication between emit_return and emit_structured_jump.
-// [ ] Change the calling convention so it is easier to use from both JS and in the compiler.
-
-
-
-
-#define BH_DEBUG
-#include "onyxwasm.h"
-#include "onyxutils.h"
-
-#define WASM_TYPE_INT32 0x7F
-#define WASM_TYPE_INT64 0x7E
-#define WASM_TYPE_FLOAT32 0x7D
-#define WASM_TYPE_FLOAT64 0x7C
-#define WASM_TYPE_VAR128 0x7B
-#define WASM_TYPE_PTR WASM_TYPE_INT32
-#define WASM_TYPE_FUNC WASM_TYPE_INT32
-#define WASM_TYPE_VOID 0x00
-
-static WasmType onyx_type_to_wasm_type(Type* type) {
- if (type->kind == Type_Kind_Struct) {
- return WASM_TYPE_VOID;
- }
-
- if (type->kind == Type_Kind_Slice) {
- return WASM_TYPE_VOID;
- }
-
- if (type->kind == Type_Kind_Enum) {
- return onyx_type_to_wasm_type(type->Enum.backing);
- }
-
- if (type->kind == Type_Kind_Pointer) {
- return WASM_TYPE_PTR;
- }
-
- if (type->kind == Type_Kind_Array) {
- return WASM_TYPE_PTR;
- }
-
- if (type->kind == Type_Kind_Function) {
- return WASM_TYPE_FUNC;
- }
-
- if (type->kind == Type_Kind_Basic) {
- TypeBasic* basic = &type->Basic;
- if (basic->flags & Basic_Flag_Boolean) return WASM_TYPE_INT32;
- if (basic->flags & Basic_Flag_Integer) {
- if (basic->size <= 4) return WASM_TYPE_INT32;
- if (basic->size == 8) return WASM_TYPE_INT64;
- }
- if (basic->flags & Basic_Flag_Pointer) return WASM_TYPE_INT32;
- if (basic->flags & Basic_Flag_Float) {
- if (basic->size <= 4) return WASM_TYPE_FLOAT32;
- if (basic->size == 8) return WASM_TYPE_FLOAT64;
- }
- if (basic->flags & Basic_Flag_SIMD) return WASM_TYPE_VAR128;
- if (basic->flags & Basic_Flag_Type_Index) return WASM_TYPE_INT32;
- if (basic->size == 0) return WASM_TYPE_VOID;
- }
-
- return WASM_TYPE_VOID;
-}
-
-static i32 generate_type_idx(OnyxWasmModule* mod, Type* ft);
-static i32 get_element_idx(OnyxWasmModule* mod, AstFunction* func);
-
-#define LOCAL_I32 0x000000000
-#define LOCAL_I64 0x100000000
-#define LOCAL_F32 0x300000000
-#define LOCAL_F64 0x700000000
-#define LOCAL_V128 0xf00000000
-
-static b32 local_is_wasm_local(AstTyped* local) {
- if (local->kind == Ast_Kind_Local && local->flags & Ast_Flag_Address_Taken) return 0;
- if (local->type->kind == Type_Kind_Basic) return 1;
- if (local->type->kind == Type_Kind_Enum && local->type->Enum.backing->kind == Type_Kind_Basic) return 1;
- if (local->type->kind == Type_Kind_Pointer) return 1;
- return 0;
-}
-
-static u64 local_raw_allocate(LocalAllocator* la, WasmType wt) {
- i32 idx = 0;
- if (wt == WASM_TYPE_INT32) idx = 0;
- if (wt == WASM_TYPE_INT64) idx = 1;
- if (wt == WASM_TYPE_FLOAT32) idx = 2;
- if (wt == WASM_TYPE_FLOAT64) idx = 3;
- if (wt == WASM_TYPE_VAR128) idx = 4;
-
- u64 flag_bits = LOCAL_IS_WASM;
- if (wt == WASM_TYPE_INT32) flag_bits |= LOCAL_I32;
- if (wt == WASM_TYPE_INT64) flag_bits |= LOCAL_I64;
- if (wt == WASM_TYPE_FLOAT32) flag_bits |= LOCAL_F32;
- if (wt == WASM_TYPE_FLOAT64) flag_bits |= LOCAL_F64;
- if (wt == WASM_TYPE_VAR128) flag_bits |= LOCAL_V128;
-
- if (la->freed[idx] > 0) {
- la->freed[idx]--;
- return flag_bits | ((u64) (la->allocated[idx] - la->freed[idx] - 1 + la->param_count));
-
- } else {
- la->allocated[idx]++;
- return flag_bits | ((u64) (la->allocated[idx] - 1 + la->param_count));
- }
-}
-
-static void local_raw_free(LocalAllocator* la, WasmType wt) {
- i32 idx = 0;
-
- if (wt == WASM_TYPE_INT32) idx = 0;
- if (wt == WASM_TYPE_INT64) idx = 1;
- if (wt == WASM_TYPE_FLOAT32) idx = 2;
- if (wt == WASM_TYPE_FLOAT64) idx = 3;
- if (wt == WASM_TYPE_VAR128) idx = 4;
-
- assert(la->allocated[idx] > 0 && la->freed[idx] < la->allocated[idx]);
-
- la->freed[idx]++;
-}
-
-static u64 local_allocate(LocalAllocator* la, AstTyped* local) {
- if (local_is_wasm_local(local)) {
- WasmType wt = onyx_type_to_wasm_type(local->type);
- return local_raw_allocate(la, wt);
-
- } else {
- u32 size = type_size_of(local->type);
- u32 alignment = type_alignment_of(local->type);
-
- bh_align(la->curr_stack, alignment);
-
- if (la->max_stack < la->curr_stack)
- la->max_stack = la->curr_stack;
-
- bh_align(size, alignment);
-
- if (la->max_stack - la->curr_stack >= (i32) size) {
- la->curr_stack += size;
-
- } else {
- la->max_stack += size - (la->max_stack - la->curr_stack);
- la->curr_stack = la->max_stack;
- }
-
- return la->curr_stack - size;
- }
-}
-
-static void local_free(LocalAllocator* la, AstTyped* local) {
- if (local_is_wasm_local(local)) {
- WasmType wt = onyx_type_to_wasm_type(local->type);
- local_raw_free(la, wt);
-
- } else {
- u32 size = type_size_of(local->type);
- u32 alignment = type_alignment_of(local->type);
- bh_align(size, alignment);
-
- la->curr_stack -= size;
- }
-}
-
-static u64 local_lookup_idx(LocalAllocator* la, u64 value) {
- assert(value & LOCAL_IS_WASM);
-
- u32 idx = value & 0xFFFFFFFF;
- if (value & 0x100000000) idx += la->allocated[0];
- if (value & 0x200000000) idx += la->allocated[1];
- if (value & 0x400000000) idx += la->allocated[2];
- if (value & 0x800000000) idx += la->allocated[3];
-
- return (u64) idx;
-}
-
-
-typedef enum StructuredBlockType StructuredBlockType;
-enum StructuredBlockType {
- SBT_Basic_Block, // Cannot be targeted using jump
- SBT_Breakable_Block, // Targeted using break
- SBT_Continue_Block, // Targeted using continue
- SBT_Fallthrough_Block, // Targeted using fallthrough
- SBT_Return_Block, // Targeted using return, (used for expression macros)
-
- SBT_Basic_If, // Cannot be targeted using jump
- SBT_Breakable_If, // Targeted using break
-
- SBT_Basic_Loop, // Cannot be targeted using jump
- SBT_Continue_Loop, // Targeted using continue
-
- SBT_Count,
-};
-
-
-#define WI(instr) bh_arr_push(code, ((WasmInstruction){ instr, 0x00 }))
-#define WID(instr, data) bh_arr_push(code, ((WasmInstruction){ instr, data }))
-#define WIL(instr, data) bh_arr_push(code, ((WasmInstruction){ instr, { .l = data } }))
-#define WIP(instr, data) bh_arr_push(code, ((WasmInstruction){ instr, { .p = data } }))
-#define EMIT_FUNC(kind, ...) static void emit_ ## kind (OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, __VA_ARGS__)
-#define EMIT_FUNC_NO_ARGS(kind) static void emit_ ## kind (OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode)
-#define STACK_SWAP(type1, type2) { \
- u64 t0 = local_raw_allocate(mod->local_alloc, type1); \
- u64 t1 = local_raw_allocate(mod->local_alloc, type2); \
- \
- WIL(WI_LOCAL_SET, t0); \
- WIL(WI_LOCAL_SET, t1); \
- WIL(WI_LOCAL_GET, t0); \
- WIL(WI_LOCAL_GET, t1); \
- \
- local_raw_free(mod->local_alloc, type1); \
- local_raw_free(mod->local_alloc, type2); \
- }
-#define SUBMIT_PATCH(patch_arr, offset) bh_arr_push((patch_arr), ((PatchInfo) { bh_arr_length(code) - offset }))
-
-EMIT_FUNC(function_body, AstFunction* fd);
-EMIT_FUNC(block, AstBlock* block, b32 generate_block_headers);
-EMIT_FUNC(statement, AstNode* stmt);
-EMIT_FUNC(local_allocation, AstTyped* stmt);
-EMIT_FUNC_NO_ARGS(free_local_allocations);
-EMIT_FUNC(assignment, AstBinaryOp* assign);
-EMIT_FUNC(assignment_of_array, AstTyped* left, AstTyped* right);
-EMIT_FUNC(compound_assignment, AstBinaryOp* assign);
-EMIT_FUNC(store_instruction, Type* type, u32 offset);
-EMIT_FUNC(load_instruction, Type* type, u32 offset);
-EMIT_FUNC(if, AstIfWhile* if_node);
-EMIT_FUNC(while, AstIfWhile* while_node);
-EMIT_FUNC(for, AstFor* for_node);
-EMIT_FUNC(switch, AstSwitch* switch_node);
-EMIT_FUNC(defer, AstDefer* defer);
-EMIT_FUNC(defer_code, WasmInstruction* deferred_code, u32 code_count);
-EMIT_FUNC(deferred_stmt, DeferredStmt deferred_stmt);
-EMIT_FUNC_NO_ARGS(deferred_stmts);
-EMIT_FUNC(binop, AstBinaryOp* binop);
-EMIT_FUNC(unaryop, AstUnaryOp* unop);
-EMIT_FUNC(call, AstCall* call);
-EMIT_FUNC(intrinsic_call, AstCall* call);
-EMIT_FUNC(subscript_location, AstSubscript* sub, u64* offset_return);
-EMIT_FUNC(field_access_location, AstFieldAccess* field, u64* offset_return);
-EMIT_FUNC(local_location, AstLocal* local, u64* offset_return);
-EMIT_FUNC(memory_reservation_location, AstMemRes* memres);
-EMIT_FUNC(location_return_offset, AstTyped* expr, u64* offset_return);
-EMIT_FUNC(location, AstTyped* expr);
-EMIT_FUNC(compound_load, Type* type, u64 offset);
-EMIT_FUNC(struct_lval, AstTyped* lval);
-EMIT_FUNC(struct_literal, AstStructLiteral* sl);
-EMIT_FUNC(compound_store, Type* type, u64 offset, b32 location_first);
-EMIT_FUNC(array_store, Type* type, u32 offset);
-EMIT_FUNC(array_literal, AstArrayLiteral* al);
-EMIT_FUNC(range_literal, AstRangeLiteral* range);
-EMIT_FUNC(if_expression, AstIfExpression* if_expr);
-EMIT_FUNC(do_block, AstDoBlock* doblock);
-EMIT_FUNC(expression, AstTyped* expr);
-EMIT_FUNC(cast, AstUnaryOp* cast);
-EMIT_FUNC(return, AstReturn* ret);
-EMIT_FUNC(stack_enter, u64 stacksize);
-EMIT_FUNC(zero_value, WasmType wt);
-EMIT_FUNC(zero_value_for_type, Type* type, OnyxToken* where);
-
-EMIT_FUNC(enter_structured_block, StructuredBlockType sbt);
-EMIT_FUNC_NO_ARGS(leave_structured_block);
-
-static void emit_raw_data(OnyxWasmModule* mod, ptr data, AstTyped* node);
-static b32 emit_raw_data_(OnyxWasmModule* mod, ptr data, AstTyped* node);
-
-#include "onyxwasm_intrinsics.c"
-#include "onyxwasm_type_table.c"
-
-EMIT_FUNC(function_body, AstFunction* fd) {
- if (fd->body == NULL) return;
-
- emit_block(mod, pcode, fd->body, 0);
-}
-
-EMIT_FUNC(block, AstBlock* block, b32 generate_block_headers) {
- bh_arr(WasmInstruction) code = *pcode;
-
- generate_block_headers = generate_block_headers && (block->rules & Block_Rule_Emit_Instructions);
-
- if (generate_block_headers) {
- emit_enter_structured_block(mod, &code, (block->rules & Block_Rule_Override_Return)
- ? SBT_Return_Block
- : SBT_Breakable_Block);
- }
-
- forll (AstNode, stmt, block->body, next) {
- emit_statement(mod, &code, stmt);
- }
-
- // HACK: I'm not convinced this is the best way to handle this case. Essentially, if
- // a deferred statement uses a local that is freed from the a macro block, then the
- // local won't be valid anymore and could have garbage data. This ensures that the
- // freeing of the local variables and flushing of the deferred statements happen together,
- // so a deferred statement can never try to use a local that has been freed.
- // - brendanfh 2021/08/29
- if (block->rules & Block_Rule_Clear_Defer) {
- emit_deferred_stmts(mod, &code);
-
- // if (block->rules & Block_Rule_New_Scope)
- emit_free_local_allocations(mod, &code);
- }
-
- if (generate_block_headers)
- emit_leave_structured_block(mod, &code);
-
- *pcode = code;
-}
-
-EMIT_FUNC(enter_structured_block, StructuredBlockType sbt) {
- bh_arr(WasmInstruction) code = *pcode;
-
- static const StructuredBlockType jump_numbers[SBT_Count] = {
- /* SBT_Basic_Block */ 0,
- /* SBT_Breakable_Block */ 1,
- /* SBT_Continue_Block */ 2,
- /* SBT_Fallthrough_Block */ 3,
- /* SBT_Return_Block */ 4,
-
- /* SBT_Basic_If */ 0,
- /* SBT_Breakable_If */ 1,
-
- /* SBT_Basic_Loop */ 0,
- /* SBT_Continue_Loop */ 2,
- };
-
- static const WasmInstructionType block_instrs[SBT_Count] = {
- /* SBT_Basic_Block */ WI_BLOCK_START,
- /* SBT_Breakable_Block */ WI_BLOCK_START,
- /* SBT_Continue_Block */ WI_BLOCK_START,
- /* SBT_Fallthrough_Block */ WI_BLOCK_START,
- /* SBT_Return_Block */ WI_BLOCK_START,
-
- /* SBT_Basic_If */ WI_IF_START,
- /* SBT_Breakable_If */ WI_IF_START,
-
- /* SBT_Basic_Loop */ WI_LOOP_START,
- /* SBT_Continue_Loop */ WI_LOOP_START,
- };
-
-
- WID(block_instrs[sbt], 0x40);
- bh_arr_push(mod->structured_jump_target, jump_numbers[sbt]);
-
- *pcode = code;
-}
-
-EMIT_FUNC_NO_ARGS(leave_structured_block) {
- bh_arr(WasmInstruction) code = *pcode;
-
- WI(WI_BLOCK_END);
- bh_arr_pop(mod->structured_jump_target);
-
- *pcode = code;
-}
-
-i64 get_structured_jump_label(OnyxWasmModule* mod, JumpType jump_type, u32 jump_count) {
- // :CLEANUP These numbers should become constants because they are shared with
- // enter_structured_block's definitions.
- static const u8 wants[Jump_Type_Count] = { 1, 2, 3, 4 };
-
- i64 labelidx = 0;
- u8 wanted = wants[jump_type];
- b32 success = 0;
-
- i32 len = bh_arr_length(mod->structured_jump_target) - 1;
- for (u8* t = &bh_arr_last(mod->structured_jump_target); len >= 0; len--, t--) {
- if (*t == wanted) jump_count--;
- if (jump_count == 0) {
- success = 1;
- break;
- }
-
- labelidx++;
- }
-
- return (success == 0) ? -1 : labelidx;
-}
-
-EMIT_FUNC(structured_jump, AstJump* jump) {
- bh_arr(WasmInstruction) code = *pcode;
-
- i64 labelidx = get_structured_jump_label(mod, jump->jump, jump->count);
-
- if (bh_arr_length(mod->deferred_stmts) > 0) {
- i32 i = bh_arr_length(mod->deferred_stmts) - 1;
- i32 d = bh_arr_length(mod->structured_jump_target) - (labelidx + 1);
-
- while (i >= 0 && (i32) mod->deferred_stmts[i].depth > d) {
- emit_deferred_stmt(mod, &code, mod->deferred_stmts[i]);
- i--;
- }
- }
-
- if (labelidx >= 0) {
- // NOTE: If the previous instruction was a non conditional jump,
- // don't emit another jump since it will never be reached.
- if (bh_arr_last(code).type != WI_JUMP)
- WID(WI_JUMP, labelidx);
- } else {
- onyx_report_error(jump->token->pos, "Invalid structured jump.");
- }
-
- *pcode = code;
-}
-
-EMIT_FUNC(statement, AstNode* stmt) {
- bh_arr(WasmInstruction) code = *pcode;
-
- switch (stmt->kind) {
- case Ast_Kind_Return: emit_return(mod, &code, (AstReturn *) stmt); break;
- case Ast_Kind_If: emit_if(mod, &code, (AstIfWhile *) stmt); break;
- case Ast_Kind_Static_If: emit_if(mod, &code, (AstIfWhile *) stmt); break;
- case Ast_Kind_While: emit_while(mod, &code, (AstIfWhile *) stmt); break;
- case Ast_Kind_For: emit_for(mod, &code, (AstFor *) stmt); break;
- case Ast_Kind_Switch: emit_switch(mod, &code, (AstSwitch *) stmt); break;
- case Ast_Kind_Jump: emit_structured_jump(mod, &code, (AstJump *) stmt); break;
- case Ast_Kind_Block: emit_block(mod, &code, (AstBlock *) stmt, 1); break;
- case Ast_Kind_Defer: emit_defer(mod, &code, (AstDefer *) stmt); break;
- case Ast_Kind_Local: emit_local_allocation(mod, &code, (AstTyped *) stmt); break;
-
- case Ast_Kind_Directive_Insert: break;
-
- default: emit_expression(mod, &code, (AstTyped *) stmt); break;
- }
-
- *pcode = code;
-}
-
-EMIT_FUNC(local_allocation, AstTyped* stmt) {
- bh_imap_put(&mod->local_map, (u64) stmt, local_allocate(mod->local_alloc, stmt));
-
- bh_arr_push(mod->local_allocations, ((AllocatedSpace) {
- .depth = bh_arr_length(mod->structured_jump_target),
- .expr = stmt,
- }));
-}
-
-EMIT_FUNC_NO_ARGS(free_local_allocations) {
- if (bh_arr_length(mod->local_allocations) == 0) return;
-
- u64 depth = bh_arr_length(mod->structured_jump_target);
- while (bh_arr_length(mod->local_allocations) > 0 && bh_arr_last(mod->local_allocations).depth >= depth) {
- // CHECK: Not sure this next line is okay to be here...
- bh_imap_delete(&mod->local_map, (u64) bh_arr_last(mod->local_allocations).expr);
-
- local_free(mod->local_alloc, bh_arr_last(mod->local_allocations).expr);
- bh_arr_pop(mod->local_allocations);
- }
-}
-
-EMIT_FUNC(assignment, AstBinaryOp* assign) {
- bh_arr(WasmInstruction) code = *pcode;
-
- if (assign->right->type->kind == Type_Kind_Array) {
- emit_assignment_of_array(mod, &code, assign->left, assign->right);
- *pcode = code;
- return;
- }
-
- if (assign->right->type->kind == Type_Kind_Compound) {
- emit_compound_assignment(mod, &code, assign);
- *pcode = code;
- return;
- }
-
- AstTyped* lval = assign->left;
-
- if (lval->kind == Ast_Kind_Local || lval->kind == Ast_Kind_Param) {
- if (bh_imap_get(&mod->local_map, (u64) lval) & LOCAL_IS_WASM) {
- emit_expression(mod, &code, assign->right);
-
- u64 localidx = bh_imap_get(&mod->local_map, (u64) lval);
-
- if (lval->kind == Ast_Kind_Param && type_is_structlike_strict(lval->type)) {
- u32 mem_count = type_structlike_mem_count(lval->type);
- fori (i, 0, mem_count) WIL(WI_LOCAL_SET, localidx + i);
-
- } else {
- WIL(WI_LOCAL_SET, localidx);
- }
-
- *pcode = code;
- return;
- }
- }
-
- if (lval->kind == Ast_Kind_Global) {
- emit_expression(mod, &code, assign->right);
-
- i32 globalidx = (i32) bh_imap_get(&mod->index_map, (u64) lval);
- WID(WI_GLOBAL_SET, globalidx);
-
- *pcode = code;
- return;
- }
-
- u64 offset = 0;
- emit_location_return_offset(mod, &code, lval, &offset);
- emit_expression(mod, &code, assign->right);
- emit_store_instruction(mod, &code, lval->type, offset);
-
- *pcode = code;
-}
-
-EMIT_FUNC(assignment_of_array, AstTyped* left, AstTyped* right) {
- bh_arr(WasmInstruction) code = *pcode;
-
- Type* rtype = right->type;
- assert(rtype->kind == Type_Kind_Array);
-
- if (right->kind == Ast_Kind_Array_Literal) {
- Type* elem_type = rtype;
- u32 elem_count = 1;
- while (elem_type->kind == Type_Kind_Array) {
- elem_count *= elem_type->Array.count;
- elem_type = elem_type->Array.elem;
- }
- u32 elem_size = type_size_of(elem_type);
-
- u64 lptr_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
-
- emit_location(mod, &code, left);
- WIL(WI_LOCAL_SET, lptr_local);
-
- AstArrayLiteral* al = (AstArrayLiteral *) right;
- fori (i, 0, elem_count) {
- WIL(WI_LOCAL_GET, lptr_local);
- emit_expression(mod, &code, al->values[i]);
- emit_store_instruction(mod, &code, elem_type, i * elem_size);
- }
-
- local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
-
- } else {
- u64 offset = 0;
- emit_location_return_offset(mod, &code, left, &offset);
- emit_expression(mod, &code, right);
- emit_array_store(mod, &code, rtype, offset);
- }
-
- *pcode = code;
- return;
-}
-
-EMIT_FUNC(compound_assignment, AstBinaryOp* assign) {
- bh_arr(WasmInstruction) code = *pcode;
-
- emit_expression(mod, &code, assign->right);
-
- AstCompound* compound_lval = (AstCompound *) assign->left;
- bh_arr_rev_each(AstTyped *, plval, compound_lval->exprs) {
- AstTyped *lval = *plval;
-
- if (type_is_structlike_strict(lval->type)) {
- emit_struct_lval(mod, &code, lval);
- continue;
- }
-
- if (lval->kind == Ast_Kind_Local || lval->kind == Ast_Kind_Param) {
- if (bh_imap_get(&mod->local_map, (u64) lval) & LOCAL_IS_WASM) {
- u64 localidx = bh_imap_get(&mod->local_map, (u64) lval);
- WIL(WI_LOCAL_SET, localidx);
- continue;
- }
- }
-
- WasmType wt = onyx_type_to_wasm_type(lval->type);
- u64 expr_tmp = local_raw_allocate(mod->local_alloc, wt);
- WIL(WI_LOCAL_SET, expr_tmp);
- u64 offset = 0;
- emit_location_return_offset(mod, &code, lval, &offset);
- WIL(WI_LOCAL_GET, expr_tmp);
-
- local_raw_free(mod->local_alloc, wt);
- emit_store_instruction(mod, &code, lval->type, offset);
- }
-
- *pcode = code;
- return;
-}
-
-EMIT_FUNC(store_instruction, Type* type, u32 offset) {
- bh_arr(WasmInstruction) code = *pcode;
-
- if (type_is_compound(type)) {
- emit_compound_store(mod, pcode, type, offset, 0);
- return;
- }
-
- if (type->kind == Type_Kind_Array) {
- emit_array_store(mod, pcode, type, offset);
- return;
- }
-
- if (type->kind == Type_Kind_Enum) type = type->Enum.backing;
- if (type->kind == Type_Kind_Function) type = &basic_types[Basic_Kind_U32];
-
- u32 alignment = type_get_alignment_log2(type);
-
- i32 store_size = type_size_of(type);
- i32 is_basic = type->kind == Type_Kind_Basic || type->kind == Type_Kind_Pointer;
-
- if (is_basic && (type->Basic.flags & Basic_Flag_Pointer)) {
- WID(WI_I32_STORE, ((WasmInstructionData) { 2, offset }));
- } else if (is_basic && ((type->Basic.flags & Basic_Flag_Integer)
- || (type->Basic.flags & Basic_Flag_Boolean)
- || (type->Basic.flags & Basic_Flag_Type_Index))) {
- if (store_size == 1) WID(WI_I32_STORE_8, ((WasmInstructionData) { alignment, offset }));
- else if (store_size == 2) WID(WI_I32_STORE_16, ((WasmInstructionData) { alignment, offset }));
- else if (store_size == 4) WID(WI_I32_STORE, ((WasmInstructionData) { alignment, offset }));
- else if (store_size == 8) WID(WI_I64_STORE, ((WasmInstructionData) { alignment, offset }));
- } else if (is_basic && (type->Basic.flags & Basic_Flag_Float)) {
- if (store_size == 4) WID(WI_F32_STORE, ((WasmInstructionData) { alignment, offset }));
- else if (store_size == 8) WID(WI_F64_STORE, ((WasmInstructionData) { alignment, offset }));
- } else if (is_basic && (type->Basic.flags & Basic_Flag_SIMD)) {
- WID(WI_V128_STORE, ((WasmInstructionData) { alignment, offset }));
- } else {
- onyx_report_error((OnyxFilePos) { 0 },
- "Failed to generate store instruction for type '%s'.",
- type_get_name(type));
- }
-
- *pcode = code;
-}
-
-EMIT_FUNC(load_instruction, Type* type, u32 offset) {
- bh_arr(WasmInstruction) code = *pcode;
-
- if (type_is_compound(type)) {
- emit_compound_load(mod, pcode, type, offset);
- return;
- }
-
- if (type->kind == Type_Kind_Array) {
- if (offset != 0) {
- WID(WI_PTR_CONST, offset);
- WI(WI_PTR_ADD);
- }
-
- *pcode = code;
- return;
- }
-
- if (type->kind == Type_Kind_Enum) type = type->Enum.backing;
- if (type->kind == Type_Kind_Function) type = &basic_types[Basic_Kind_U32];
-
- i32 load_size = type_size_of(type);
- i32 is_basic = type->kind == Type_Kind_Basic || type->kind == Type_Kind_Pointer;
-
- WasmInstructionType instr = WI_NOP;
- i32 alignment = type_get_alignment_log2(type);
-
- if (is_basic && (type->Basic.flags & Basic_Flag_Pointer)) {
- instr = WI_I32_LOAD;
- alignment = 2;
- }
- else if (is_basic && ((type->Basic.flags & Basic_Flag_Integer)
- || (type->Basic.flags & Basic_Flag_Boolean)
- || (type->Basic.flags & Basic_Flag_Type_Index))) {
- if (load_size == 1) instr = WI_I32_LOAD_8_S;
- else if (load_size == 2) instr = WI_I32_LOAD_16_S;
- else if (load_size == 4) instr = WI_I32_LOAD;
- else if (load_size == 8) instr = WI_I64_LOAD;
-
- if (load_size < 4 && (type->Basic.flags & Basic_Flag_Unsigned)) instr += 1;
- }
- else if (is_basic && (type->Basic.flags & Basic_Flag_Float)) {
- if (load_size == 4) instr = WI_F32_LOAD;
- else if (load_size == 8) instr = WI_F64_LOAD;
- }
- else if (is_basic && (type->Basic.flags & Basic_Flag_SIMD)) {
- instr = WI_V128_LOAD;
- }
-
- WID(instr, ((WasmInstructionData) { alignment, offset }));
-
- if (instr == WI_NOP) {
- onyx_report_error((OnyxFilePos) { 0 },
- "Failed to generate load instruction for type '%s'.",
- type_get_name(type));
- }
-
- *pcode = code;
-}
-
-EMIT_FUNC(if, AstIfWhile* if_node) {
- bh_arr(WasmInstruction) code = *pcode;
-
- if (if_node->initialization != NULL) {
- // bh_imap_put(&mod->local_map, (u64) if_node->local, local_allocate(mod->local_alloc, (AstTyped *) if_node->local));
-
- forll (AstNode, stmt, if_node->initialization, next) {
- emit_statement(mod, &code, stmt);
- }
- }
-
- if (if_node->kind == Ast_Kind_Static_If) {
- if (static_if_resolution(if_node)) {
- if (if_node->true_stmt) emit_block(mod, &code, if_node->true_stmt, 1);
- } else {
- if (if_node->false_stmt) emit_block(mod, &code, if_node->false_stmt, 1);
- }
-
- *pcode = code;
- return;
- }
-
- emit_expression(mod, &code, if_node->cond);
-
- emit_enter_structured_block(mod, &code, SBT_Basic_If);
- if (if_node->true_stmt) emit_block(mod, &code, if_node->true_stmt, 0);
-
- if (if_node->false_stmt) {
- WI(WI_ELSE);
-
- if (if_node->false_stmt->kind == Ast_Kind_If) {
- emit_if(mod, &code, (AstIfWhile *) if_node->false_stmt);
- } else {
- emit_block(mod, &code, if_node->false_stmt, 0);
- }
- }
-
- emit_leave_structured_block(mod, &code);
-
- *pcode = code;
-}
-
-EMIT_FUNC(while, AstIfWhile* while_node) {
- bh_arr(WasmInstruction) code = *pcode;
-
- if (while_node->initialization != NULL) {
- forll (AstNode, stmt, while_node->initialization, next) {
- emit_statement(mod, &code, stmt);
- }
- }
-
- if (while_node->false_stmt == NULL) {
- emit_enter_structured_block(mod, &code, SBT_Breakable_Block);
- emit_enter_structured_block(mod, &code, SBT_Continue_Loop);
-
- emit_expression(mod, &code, while_node->cond);
- WI(WI_I32_EQZ);
- WID(WI_COND_JUMP, 0x01);
-
- emit_block(mod, &code, while_node->true_stmt, 0);
-
- if (bh_arr_last(code).type != WI_JUMP)
- WID(WI_JUMP, 0x00);
-
- emit_leave_structured_block(mod, &code);
- emit_leave_structured_block(mod, &code);
-
- } else {
- emit_expression(mod, &code, while_node->cond);
-
- emit_enter_structured_block(mod, &code, SBT_Breakable_If);
- emit_enter_structured_block(mod, &code, SBT_Continue_Loop);
-
- emit_block(mod, &code, while_node->true_stmt, 0);
-
- emit_expression(mod, &code, while_node->cond);
- WID(WI_COND_JUMP, 0x00);
-
- emit_leave_structured_block(mod, &code);
- WI(WI_ELSE);
-
- emit_block(mod, &code, while_node->false_stmt, 0);
-
- emit_leave_structured_block(mod, &code);
- }
-
- *pcode = code;
-}
-
-EMIT_FUNC(for_range, AstFor* for_node, u64 iter_local) {
- bh_arr(WasmInstruction) code = *pcode;
-
- // NOTE: There are some aspects of the code below that rely on the
- // low, high, and step members to be i32's. This restriction can be lifted,
- // but it is important to change the code here.
- // -brendanfh 2020/09/04
-
- AstLocal* var = for_node->var;
- u64 offset = 0;
-
- StructMember low_mem, high_mem, step_mem;
- type_lookup_member(builtin_range_type_type, "low", &low_mem);
- type_lookup_member(builtin_range_type_type, "high", &high_mem);
- type_lookup_member(builtin_range_type_type, "step", &step_mem);
- u64 low_local = local_raw_allocate(mod->local_alloc, onyx_type_to_wasm_type(low_mem.type));
- u64 high_local = local_raw_allocate(mod->local_alloc, onyx_type_to_wasm_type(high_mem.type));
- u64 step_local = local_raw_allocate(mod->local_alloc, onyx_type_to_wasm_type(step_mem.type));
-
- WIL(WI_LOCAL_SET, step_local);
- WIL(WI_LOCAL_SET, high_local);
- WIL(WI_LOCAL_TEE, low_local);
- WIL(WI_LOCAL_SET, iter_local);
-
- emit_enter_structured_block(mod, &code, SBT_Breakable_Block);
- emit_enter_structured_block(mod, &code, SBT_Basic_Loop);
- emit_enter_structured_block(mod, &code, SBT_Continue_Block);
-
- WIL(WI_LOCAL_GET, iter_local);
- WIL(WI_LOCAL_GET, high_local);
- WI(WI_I32_GE_S);
- WID(WI_COND_JUMP, 0x02);
-
- emit_block(mod, &code, for_node->stmt, 0);
-
- emit_leave_structured_block(mod, &code);
-
- WIL(WI_LOCAL_GET, iter_local);
- WIL(WI_LOCAL_GET, step_local);
- WI(WI_I32_ADD);
- WIL(WI_LOCAL_SET, iter_local);
-
- if (bh_arr_last(code).type != WI_JUMP)
- WID(WI_JUMP, 0x00);
-
- emit_leave_structured_block(mod, &code);
- emit_leave_structured_block(mod, &code);
-
- local_raw_free(mod->local_alloc, onyx_type_to_wasm_type(low_mem.type));
- local_raw_free(mod->local_alloc, onyx_type_to_wasm_type(high_mem.type));
- local_raw_free(mod->local_alloc, onyx_type_to_wasm_type(step_mem.type));
-
- *pcode = code;
-}
-
-EMIT_FUNC(for_array, AstFor* for_node, u64 iter_local) {
- bh_arr(WasmInstruction) code = *pcode;
-
- u64 end_ptr_local, ptr_local;
- end_ptr_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
-
- if (for_node->by_pointer) {
- ptr_local = iter_local;
- } else {
- ptr_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
- }
-
- AstLocal* var = for_node->var;
- b32 it_is_local = (b32) ((iter_local & LOCAL_IS_WASM) != 0);
- u64 offset = 0;
-
- u64 elem_size;
- if (for_node->by_pointer) elem_size = type_size_of(var->type->Pointer.elem);
- else elem_size = type_size_of(var->type);
-
- WIL(WI_LOCAL_TEE, ptr_local);
- WIL(WI_PTR_CONST, for_node->iter->type->Array.count * elem_size);
- WI(WI_PTR_ADD);
- WIL(WI_LOCAL_SET, end_ptr_local);
-
- emit_enter_structured_block(mod, &code, SBT_Breakable_Block);
- emit_enter_structured_block(mod, &code, SBT_Basic_Loop);
- emit_enter_structured_block(mod, &code, SBT_Continue_Block);
-
- WIL(WI_LOCAL_GET, ptr_local);
- WIL(WI_LOCAL_GET, end_ptr_local);
- WI(WI_PTR_GE);
- WID(WI_COND_JUMP, 0x02);
-
- if (!for_node->by_pointer) {
- if (!it_is_local) emit_local_location(mod, &code, var, &offset);
-
- WIL(WI_LOCAL_GET, ptr_local);
- emit_load_instruction(mod, &code, var->type, 0);
-
- if (!it_is_local) emit_store_instruction(mod, &code, var->type, offset);
- else WIL(WI_LOCAL_SET, iter_local);
- }
-
- emit_block(mod, &code, for_node->stmt, 0);
-
- emit_leave_structured_block(mod, &code);
-
- WIL(WI_LOCAL_GET, ptr_local);
- WIL(WI_PTR_CONST, elem_size);
- WI(WI_PTR_ADD);
- WIL(WI_LOCAL_SET, ptr_local);
-
- if (bh_arr_last(code).type != WI_JUMP)
- WID(WI_JUMP, 0x00);
-
- emit_leave_structured_block(mod, &code);
- emit_leave_structured_block(mod, &code);
-
- local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
- if (!for_node->by_pointer) local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
-
- *pcode = code;
-}
-
-EMIT_FUNC(for_slice, AstFor* for_node, u64 iter_local) {
- bh_arr(WasmInstruction) code = *pcode;
-
- u64 end_ptr_local, ptr_local;
- end_ptr_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
-
- if (for_node->by_pointer) {
- ptr_local = iter_local;
- } else {
- ptr_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
- }
-
- AstLocal* var = for_node->var;
- b32 it_is_local = (b32) ((iter_local & LOCAL_IS_WASM) != 0);
- u64 offset = 0;
-
- u64 elem_size;
- if (for_node->by_pointer) elem_size = type_size_of(var->type->Pointer.elem);
- else elem_size = type_size_of(var->type);
-
- WIL(WI_LOCAL_SET, end_ptr_local);
- WIL(WI_LOCAL_TEE, ptr_local);
- WIL(WI_LOCAL_GET, end_ptr_local);
- if (elem_size != 1) {
- WID(WI_PTR_CONST, elem_size);
- WI(WI_PTR_MUL);
- }
- WI(WI_PTR_ADD);
- WIL(WI_LOCAL_SET, end_ptr_local);
-
- emit_enter_structured_block(mod, &code, SBT_Breakable_Block);
- emit_enter_structured_block(mod, &code, SBT_Basic_Loop);
- emit_enter_structured_block(mod, &code, SBT_Continue_Block);
-
- WIL(WI_LOCAL_GET, ptr_local);
- WIL(WI_LOCAL_GET, end_ptr_local);
- WI(WI_PTR_GE);
- WID(WI_COND_JUMP, 0x02);
-
- if (!for_node->by_pointer) {
- if (!it_is_local) emit_local_location(mod, &code, var, &offset);
-
- WIL(WI_LOCAL_GET, ptr_local);
- emit_load_instruction(mod, &code, var->type, 0);
-
- if (!it_is_local) emit_store_instruction(mod, &code, var->type, offset);
- else WIL(WI_LOCAL_SET, iter_local);
- }
-
- emit_block(mod, &code, for_node->stmt, 0);
-
- emit_leave_structured_block(mod, &code);
-
- WIL(WI_LOCAL_GET, ptr_local);
- WIL(WI_PTR_CONST, elem_size);
- WI(WI_PTR_ADD);
- WIL(WI_LOCAL_SET, ptr_local);
-
- if (bh_arr_last(code).type != WI_JUMP)
- WID(WI_JUMP, 0x00);
-
- emit_leave_structured_block(mod, &code);
- emit_leave_structured_block(mod, &code);
-
- local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
- if (!for_node->by_pointer) local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
-
- *pcode = code;
-}
-
-EMIT_FUNC(for_iterator, AstFor* for_node, u64 iter_local) {
- bh_arr(WasmInstruction) code = *pcode;
-
- // Allocate temporaries for iterator contents
- u64 iterator_data_ptr = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
- u64 iterator_next_func = local_raw_allocate(mod->local_alloc, WASM_TYPE_FUNC);
- u64 iterator_close_func = local_raw_allocate(mod->local_alloc, WASM_TYPE_FUNC);
- u64 iterator_done_bool = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
- WIL(WI_LOCAL_SET, iterator_close_func);
- WIL(WI_LOCAL_SET, iterator_next_func);
- WIL(WI_LOCAL_SET, iterator_data_ptr);
-
- AstLocal* var = for_node->var;
- b32 it_is_local = (b32) ((iter_local & LOCAL_IS_WASM) != 0);
- u64 offset = 0;
-
- // Enter a deferred statement for the auto-close
- emit_enter_structured_block(mod, &code, SBT_Basic_Block);
-
- TypeWithOffset close_func_type;
- type_linear_member_lookup(for_node->iter->type, 2, &close_func_type);
- i32 close_type_idx = generate_type_idx(mod, close_func_type.type);
-
- WasmInstruction* close_instructions = bh_alloc_array(global_heap_allocator, WasmInstruction, 8);
- close_instructions[0] = (WasmInstruction) { WI_LOCAL_GET, { .l = iterator_close_func } };
- close_instructions[1] = (WasmInstruction) { WI_I32_CONST, { .l = mod->null_proc_func_idx } };
- close_instructions[2] = (WasmInstruction) { WI_I32_NE, { .l = 0x00 } };
- close_instructions[3] = (WasmInstruction) { WI_IF_START, { .l = 0x40 } };
- close_instructions[4] = (WasmInstruction) { WI_LOCAL_GET, { .l = iterator_data_ptr } };
- close_instructions[5] = (WasmInstruction) { WI_LOCAL_GET, { .l = iterator_close_func } };
- close_instructions[6] = (WasmInstruction) { WI_CALL_INDIRECT, { .l = close_type_idx } };
- close_instructions[7] = (WasmInstruction) { WI_IF_END, { .l = 0x00 } };
-
- emit_defer_code(mod, &code, close_instructions, 8);
-
- emit_enter_structured_block(mod, &code, SBT_Breakable_Block);
- emit_enter_structured_block(mod, &code, SBT_Continue_Loop);
-
- if (!it_is_local) emit_local_location(mod, &code, var, &offset);
-
- {
- WIL(WI_LOCAL_GET, iterator_data_ptr);
- WIL(WI_LOCAL_GET, iterator_next_func);
-
- // CLEANUP: Calling a function is way too f-ing complicated. FACTOR IT!!
- u64 stack_top_idx = bh_imap_get(&mod->index_map, (u64) &builtin_stack_top);
-
- TypeWithOffset next_func_type;
- type_linear_member_lookup(for_node->iter->type, 1, &next_func_type);
- Type* return_type = next_func_type.type->Function.return_type;
-
- u32 return_size = type_size_of(return_type);
- u32 return_align = type_alignment_of(return_type);
- bh_align(return_size, return_align);
-
- u64 reserve_size = return_size;
- bh_align(reserve_size, 16);
-
- WID(WI_GLOBAL_GET, stack_top_idx);
- WID(WI_PTR_CONST, reserve_size);
- WI(WI_PTR_ADD);
- WID(WI_GLOBAL_SET, stack_top_idx);
-
- i32 type_idx = generate_type_idx(mod, next_func_type.type);
- WID(WI_CALL_INDIRECT, ((WasmInstructionData) { type_idx, 0x00 }));
-
- WID(WI_GLOBAL_GET, stack_top_idx);
- WID(WI_PTR_CONST, reserve_size);
- WI(WI_PTR_SUB);
- WID(WI_GLOBAL_SET, stack_top_idx);
-
- WID(WI_GLOBAL_GET, stack_top_idx);
- emit_load_instruction(mod, &code, return_type, reserve_size - return_size);
- }
-
- WIL(WI_LOCAL_SET, iterator_done_bool);
-
- if (!it_is_local) emit_store_instruction(mod, &code, var->type, offset);
- else WIL(WI_LOCAL_SET, iter_local);
-
- WIL(WI_LOCAL_GET, iterator_done_bool);
- WI(WI_I32_EQZ);
- WID(WI_COND_JUMP, 0x01);
-
- emit_block(mod, &code, for_node->stmt, 0);
- WID(WI_JUMP, 0x00);
-
- emit_leave_structured_block(mod, &code);
- emit_leave_structured_block(mod, &code);
-
- emit_deferred_stmts(mod, &code);
- emit_leave_structured_block(mod, &code);
-
- local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
- local_raw_free(mod->local_alloc, WASM_TYPE_FUNC);
- local_raw_free(mod->local_alloc, WASM_TYPE_FUNC);
- local_raw_free(mod->local_alloc, WASM_TYPE_INT32);
- *pcode = code;
-}
-
-EMIT_FUNC(for, AstFor* for_node) {
- bh_arr(WasmInstruction) code = *pcode;
-
- AstLocal* var = for_node->var;
- u64 iter_local = local_allocate(mod->local_alloc, (AstTyped *) var);
- bh_imap_put(&mod->local_map, (u64) var, iter_local);
-
- emit_expression(mod, &code, for_node->iter);
-
- switch (for_node->loop_type) {
- case For_Loop_Range: emit_for_range(mod, &code, for_node, iter_local); break;
- case For_Loop_Array: emit_for_array(mod, &code, for_node, iter_local); break;
- // NOTE: A dynamic array is just a slice with a capacity and allocator on the end.
- // Just dropping the extra fields will mean we can just use the slice implementation.
- // - brendanfh 2020/09/04
- // - brendanfh 2021/04/13
- case For_Loop_DynArr: WI(WI_DROP); WI(WI_DROP); WI(WI_DROP);
- case For_Loop_Slice: emit_for_slice(mod, &code, for_node, iter_local); break;
- case For_Loop_Iterator: emit_for_iterator(mod, &code, for_node, iter_local); break;
- default: onyx_report_error(for_node->token->pos, "Invalid for loop type. You should probably not be seeing this...");
- }
-
- local_free(mod->local_alloc, (AstTyped *) var);
-
- *pcode = code;
-}
-
-EMIT_FUNC(switch, AstSwitch* switch_node) {
- bh_arr(WasmInstruction) code = *pcode;
-
- bh_imap block_map;
- bh_imap_init(&block_map, global_heap_allocator, bh_arr_length(switch_node->cases));
-
- if (switch_node->initialization != NULL) {
- forll (AstNode, stmt, switch_node->initialization, next) {
- emit_statement(mod, &code, stmt);
- }
- }
-
- emit_enter_structured_block(mod, &code, SBT_Breakable_Block);
-
- u64 block_num = 0;
- bh_arr_each(AstSwitchCase, sc, switch_node->cases) {
- if (bh_imap_has(&block_map, (u64) sc->block)) continue;
-
- emit_enter_structured_block(mod, &code, SBT_Fallthrough_Block);
-
- bh_imap_put(&block_map, (u64) sc->block, block_num);
- block_num++;
- }
-
- u64 count = switch_node->max_case + 1 - switch_node->min_case;
- BranchTable* bt = bh_alloc(mod->extended_instr_alloc, sizeof(BranchTable) + sizeof(u32) * count);
- bt->count = count;
- bt->default_case = block_num;
- fori (i, 0, bt->count) bt->cases[i] = bt->default_case;
-
- bh_arr_each(bh__imap_entry, sc, switch_node->case_map.entries) {
- bt->cases[sc->key - switch_node->min_case] = bh_imap_get(&block_map, (u64) sc->value);
- }
-
- // CLEANUP: We enter a new block here in order to setup the correct
- // indicies for the jump targets in the branch table. For example,
- //
- // <expr>
- // jump_table
- // label0:
- // ...
- // label1:
- // ...
- //
- // If we didn't enter a new block, then jumping to label 0, would jump
- // to the second block, and so on.
- WID(WI_BLOCK_START, 0x40);
- emit_expression(mod, &code, switch_node->expr);
- if (switch_node->min_case != 0) {
- WID(WI_I32_CONST, switch_node->min_case);
- WI(WI_I32_SUB);
- }
- WIP(WI_JUMP_TABLE, bt);
- WI(WI_BLOCK_END);
-
- bh_arr_each(AstSwitchCase, sc, switch_node->cases) {
- if (bh_imap_get(&block_map, (u64) sc->block) == 0xdeadbeef) continue;
-
- u64 bn = bh_imap_get(&block_map, (u64) sc->block);
-
- emit_block(mod, &code, sc->block, 0);
-
- if (bh_arr_last(code).type != WI_JUMP)
- WID(WI_JUMP, block_num - bn);
-
- emit_leave_structured_block(mod, &code);
-
- bh_imap_put(&block_map, (u64) sc->block, 0xdeadbeef);
- }
-
- if (switch_node->default_case != NULL) {
- emit_block(mod, &code, switch_node->default_case, 0);
- }
-
- emit_leave_structured_block(mod, &code);
-
- bh_imap_free(&block_map);
- *pcode = code;
-}
-
-EMIT_FUNC(defer, AstDefer* defer) {
- bh_arr_push(mod->deferred_stmts, ((DeferredStmt) {
- .type = Deferred_Stmt_Node,
- .depth = bh_arr_length(mod->structured_jump_target),
- .stmt = defer->stmt,
- }));
-}
-
-EMIT_FUNC(defer_code, WasmInstruction* deferred_code, u32 code_count) {
- bh_arr_push(mod->deferred_stmts, ((DeferredStmt) {
- .type = Deferred_Stmt_Code,
- .depth = bh_arr_length(mod->structured_jump_target),
- .instructions = deferred_code,
- .instruction_count = code_count,
- }));
-}
-
-EMIT_FUNC(deferred_stmt, DeferredStmt deferred_stmt) {
- bh_arr(WasmInstruction) code = *pcode;
-
- switch (deferred_stmt.type) {
- case Deferred_Stmt_Node: emit_statement(mod, &code, deferred_stmt.stmt); break;
- case Deferred_Stmt_Code: {
- fori (i, 0, deferred_stmt.instruction_count) {
- bh_arr_push(code, deferred_stmt.instructions[i]);
- }
- break;
- }
- }
-
- *pcode = code;
-}
-
-EMIT_FUNC_NO_ARGS(deferred_stmts) {
- if (bh_arr_length(mod->deferred_stmts) <= 0) return;
-
- u64 depth = bh_arr_length(mod->structured_jump_target);
-
- while (bh_arr_length(mod->deferred_stmts) > 0 && bh_arr_last(mod->deferred_stmts).depth >= depth) {
- DeferredStmt stmt = bh_arr_pop(mod->deferred_stmts);
- emit_deferred_stmt(mod, pcode, stmt);
- }
-}
-
-// NOTE: These need to be in the same order as
-// the OnyxBinaryOp enum
-static const WasmInstructionType binop_map[][4] = {
- // I32 I64 F32 F64
- /* ADD */ { WI_I32_ADD, WI_I64_ADD, WI_F32_ADD, WI_F64_ADD },
- /* SUB */ { WI_I32_SUB, WI_I64_SUB, WI_F32_SUB, WI_F64_SUB },
- /* MUL */ { WI_I32_MUL, WI_I64_MUL, WI_F32_MUL, WI_F64_MUL },
- /* DIV */ { WI_I32_DIV_S, WI_I64_DIV_S, WI_F32_DIV, WI_F64_DIV },
- /* REM */ { WI_I32_REM_S, WI_I64_REM_S, WI_NOP, WI_NOP },
-
- /* EQ */ { WI_I32_EQ, WI_I64_EQ, WI_F32_EQ, WI_F64_EQ },
- /* NEQ */ { WI_I32_NE, WI_I64_NE, WI_F32_NE , WI_F64_NE },
- /* LT */ { WI_I32_LT_S, WI_I64_LT_S, WI_F32_LT, WI_F64_LT },
- /* LTE */ { WI_I32_LE_S, WI_I64_LE_S, WI_F32_LE, WI_F64_LE },
- /* GT */ { WI_I32_GT_S, WI_I64_GT_S, WI_F32_GT, WI_F64_GT },
- /* GTE */ { WI_I32_GE_S, WI_I64_GE_S, WI_F32_GE, WI_F64_GE },
-
- /* AND */ { WI_I32_AND, WI_I64_AND, WI_NOP, WI_NOP },
- /* OR */ { WI_I32_OR, WI_I64_OR, WI_NOP, WI_NOP },
- /* XOR */ { WI_I32_XOR, WI_I64_XOR, WI_NOP, WI_NOP },
- /* SHL */ { WI_I32_SHL, WI_I64_SHL, WI_NOP, WI_NOP },
- /* SHR */ { WI_I32_SHR_U, WI_I64_SHR_U, WI_NOP, WI_NOP },
- /* SAR */ { WI_I32_SHR_S, WI_I64_SHR_S, WI_NOP, WI_NOP },
-
- /* BAND */ { WI_I32_AND, WI_I64_AND, WI_NOP, WI_NOP },
- /* BOR */ { WI_I32_OR, WI_I64_OR, WI_NOP, WI_NOP },
-};
-
-EMIT_FUNC(binop, AstBinaryOp* binop) {
- bh_arr(WasmInstruction) code = *pcode;
-
- if (binop_is_assignment(binop->operation)) {
- emit_assignment(mod, &code, binop);
- *pcode = code;
- return;
- }
-
- b32 is_sign_significant = 0;
- switch (binop->operation) {
- case Binary_Op_Divide: case Binary_Op_Modulus:
- case Binary_Op_Less: case Binary_Op_Less_Equal:
- case Binary_Op_Greater: case Binary_Op_Greater_Equal:
- is_sign_significant = 1;
- }
-
- WasmType operator_type = onyx_type_to_wasm_type(binop->left->type);
- i32 optype = 0;
- if (operator_type == WASM_TYPE_INT32) optype = 0;
- else if (operator_type == WASM_TYPE_INT64) optype = 1;
- else if (operator_type == WASM_TYPE_FLOAT32) optype = 2;
- else if (operator_type == WASM_TYPE_FLOAT64) optype = 3;
-
- WasmInstructionType binop_instr = binop_map[(i32) binop->operation][optype];
-
- assert(binop_instr != WI_NOP);
-
- // NOTE: Use unsigned variant if needed
- // Unsigned instructions are always right after
- // the signed equivalent
- if (is_sign_significant) {
- if (binop->left->type->Basic.flags & Basic_Flag_Unsigned) {
- binop_instr = (WasmInstructionType) ((i32) binop_instr + 1);
- }
- }
-
- emit_expression(mod, &code, binop->left);
- emit_expression(mod, &code, binop->right);
-
- WI(binop_instr);
-
- *pcode = code;
-}
-
-EMIT_FUNC(unaryop, AstUnaryOp* unop) {
- bh_arr(WasmInstruction) code = *pcode;
-
- switch (unop->operation) {
- case Unary_Op_Negate: {
- TypeBasic* type = &unop->type->Basic;
-
- if (type->kind == Basic_Kind_I32
- || type->kind == Basic_Kind_I16
- || type->kind == Basic_Kind_I8) {
- WID(WI_I32_CONST, 0x00);
- emit_expression(mod, &code, unop->expr);
- WI(WI_I32_SUB);
-
- }
- else if (type->kind == Basic_Kind_I64) {
- WID(WI_I64_CONST, 0x00);
- emit_expression(mod, &code, unop->expr);
- WI(WI_I64_SUB);
-
- }
- else {
- emit_expression(mod, &code, unop->expr);
-
- if (type->kind == Basic_Kind_F32) WI(WI_F32_NEG);
- if (type->kind == Basic_Kind_F64) WI(WI_F64_NEG);
- }
-
- break;
- }
-
- case Unary_Op_Not:
- emit_expression(mod, &code, unop->expr);
-
- WI(WI_I32_EQZ);
- break;
-
- case Unary_Op_Bitwise_Not: {
- emit_expression(mod, &code, unop->expr);
-
- TypeBasic* type = &unop->type->Basic;
-
- if (type->kind == Basic_Kind_I8 || type->kind == Basic_Kind_U8) {
- WID(WI_I32_CONST, 0xff);
- WI(WI_I32_XOR);
- }
- else if (type->kind == Basic_Kind_I16 || type->kind == Basic_Kind_U16) {
- WID(WI_I32_CONST, 0xffff);
- WI(WI_I32_XOR);
- }
- else if (type->kind == Basic_Kind_I32 || type->kind == Basic_Kind_U32) {
- WID(WI_I32_CONST, 0xffffffff);
- WI(WI_I32_XOR);
- }
- else if (type->kind == Basic_Kind_I64 || type->kind == Basic_Kind_U64) {
- WIL(WI_I64_CONST, 0xffffffffffffffff);
- WI(WI_I64_XOR);
- }
-
- break;
- }
-
- case Unary_Op_Auto_Cast:
- case Unary_Op_Cast: emit_cast(mod, &code, unop); break;
- }
-
- *pcode = code;
-}
-
-// Calling a procedure in Onyx.
-//
-// This documentation should be placed elsewhere, but for right now I'm going to write it in the relevant
-// piece of code. Calling a procedure is relatively simple, at least compared to other calling conventions
-// out there, mostly due the fact that this is WebAssembly, where registers are "infinite" and there's no
-// really need to use stack canaries for security.
-//
-// The biggest piece to understand is how the stack gets laid out for the called procedure. To be confusing,
-// there are two stacks at play: the WASM expression stack, and the linear memory stack. Here is the general
-// lay out for calling a procedure with the following signature.
-//
-// foo :: (x: i32, y: str, z: [..] i32, va: ..i32) -> (i32, i32)
-//
-// WASM stack: top is last pushed
-//
-// vararg count
-// vararg pointer to variadic arguments in the linear memory
-// pointer to struct-like arguments (z)
-// simple structs/primitives (y, x)
-//
-// Linear memory stack:
-//
-// ... | struct-like arguments (z) | variadic arguments (va) | return space | ...
-//
-// The interesting part from above is the fact that 'y' gets passed on the WASM stack, not the linear memory
-// stack, even though a 'str' in Onyx is 2-component structure. This is because so-called "simple" structures,
-// i.e. structures that are completely flat, with no sub-structures, are passed as multiple primitives. I do
-// this because many times for interoperability, it is nicer to get two primitive values for the pointer and
-// count of a slice, instead of a pointer.
-EMIT_FUNC(call, AstCall* call) {
- bh_arr(WasmInstruction) code = *pcode;
-
- u64 stack_top_idx = bh_imap_get(&mod->index_map, (u64) &builtin_stack_top);
- u64 stack_top_store_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
-
- // Because it would be inefficient to increment and decrement the global stack pointer for every argument,
- // a simple set of instructions increments it once to the size it will need to be. However, because it is
- // impossible to know what size the reserved memory will be, a location patch is taken in order to fill it
- // in later.
- u32 reserve_space_patch = bh_arr_length(code);
- WID(WI_GLOBAL_GET, stack_top_idx);
- WIL(WI_LOCAL_TEE, stack_top_store_local);
- WID(WI_PTR_CONST, 0); // This will be filled in later.
- WI(WI_PTR_ADD);
- WID(WI_GLOBAL_SET, stack_top_idx);
-
- u32 reserve_size = 0;
- u32 vararg_count = 0;
- i32 vararg_offset = -1;
-
- u32* vararg_any_offsets=NULL;
- u32* vararg_any_types=NULL;
- if (call->va_kind == VA_Kind_Any) {
- vararg_any_offsets = bh_alloc_array(global_scratch_allocator, u32, bh_arr_length(call->args.values));
- vararg_any_types = bh_alloc_array(global_scratch_allocator, u32, bh_arr_length(call->args.values));
- }
-
- bh_arr_each(AstTyped *, parg, call->args.values) {
- AstArgument* arg = (AstArgument *) *parg;
- if (arg->is_baked) continue;
-
- b32 place_on_stack = 0;
-
- if (type_get_param_pass(arg->value->type) == Param_Pass_By_Implicit_Pointer) {
- // This arguments needs to be written to the stack because it is not a simple structure.
- place_on_stack = 1;
- }
-
- if (arg->va_kind != VA_Kind_Not_VA) {
- // This is a variadic argument and needs to be written to the stack. If the starting
- // location of the vararg array hasn't been noted, note it.
- if (vararg_offset < 0) vararg_offset = reserve_size;
-
- place_on_stack = 1;
- vararg_count += 1;
- }
-
- if (place_on_stack) WIL(WI_LOCAL_GET, stack_top_store_local);
-
- emit_expression(mod, &code, arg->value);
-
- if (place_on_stack) {
- emit_store_instruction(mod, &code, arg->value->type, reserve_size);
-
- if (arg->va_kind == VA_Kind_Not_VA) {
- // Non-variadic arguments on the stack need a pointer to them placed on the WASM stack.
- WIL(WI_LOCAL_GET, stack_top_store_local);
- WID(WI_PTR_CONST, reserve_size);
- WI(WI_PTR_ADD);
- }
-
- if (arg->va_kind == VA_Kind_Any) {
- vararg_any_offsets[vararg_count - 1] = reserve_size;
- vararg_any_types[vararg_count - 1] = arg->value->type->id;
- }
-
- reserve_size += type_size_of(arg->value->type);
- }
- }
-
- switch (call->va_kind) {
- case VA_Kind_Any: {
- vararg_offset = reserve_size;
-
- i32 any_size = type_size_of(type_build_from_ast(context.ast_alloc, builtin_any_type));
-
- fori (i, 0, vararg_count) {
- WIL(WI_LOCAL_GET, stack_top_store_local);
- WIL(WI_LOCAL_GET, stack_top_store_local);
- WID(WI_PTR_CONST, vararg_any_offsets[i]);
- WI(WI_PTR_ADD);
- emit_store_instruction(mod, &code, &basic_types[Basic_Kind_Rawptr], vararg_offset + i * any_size);
-
- WIL(WI_LOCAL_GET, stack_top_store_local);
- WID(WI_I32_CONST, vararg_any_types[i]);
- emit_store_instruction(mod, &code, &basic_types[Basic_Kind_Type_Index], vararg_offset + i * any_size + 8);
-
- reserve_size += any_size;
- }
-
- // fallthrough
- }
-
- case VA_Kind_Typed: {
- WIL(WI_LOCAL_GET, stack_top_store_local);
- if (vararg_offset > 0) {
- WID(WI_PTR_CONST, vararg_offset);
- WI(WI_PTR_ADD);
- }
- WID(WI_I32_CONST, vararg_count);
- break;
- }
-
- case VA_Kind_Untyped: {
- WIL(WI_LOCAL_GET, stack_top_store_local);
- WIL(WI_LOCAL_GET, stack_top_store_local);
- if (vararg_offset > 0) {
- WID(WI_PTR_CONST, vararg_offset);
- WI(WI_PTR_ADD);
- }
- emit_store_instruction(mod, &code, &basic_types[Basic_Kind_Rawptr], reserve_size);
-
- // NOTE: There will be 4 uninitialized bytes here, because pointers are only 4 bytes in WASM.
-
- WIL(WI_LOCAL_GET, stack_top_store_local);
- WID(WI_I32_CONST, vararg_count);
- emit_store_instruction(mod, &code, &basic_types[Basic_Kind_I32], reserve_size + 8);
-
- WIL(WI_LOCAL_GET, stack_top_store_local);
- if (reserve_size > 0) {
- WID(WI_PTR_CONST, reserve_size);
- WI(WI_PTR_ADD);
- }
-
- reserve_size += 12;
- break;
- }
- }
-
- CallingConvention cc = type_function_get_cc(call->callee->type);
- assert(cc != CC_Undefined);
-
- Type* return_type = call->callee->type->Function.return_type;
- u32 return_size = type_size_of(return_type);
- assert(return_size % type_alignment_of(return_type) == 0);
-
- if (cc == CC_Return_Stack) reserve_size += return_size;
-
- if (call->callee->kind == Ast_Kind_Function) {
- i32 func_idx = (i32) bh_imap_get(&mod->index_map, (u64) call->callee);
- bh_arr_push(code, ((WasmInstruction){ WI_CALL, func_idx }));
-
- } else {
- emit_expression(mod, &code, call->callee);
-
- i32 type_idx = generate_type_idx(mod, call->callee->type);
- WID(WI_CALL_INDIRECT, ((WasmInstructionData) { type_idx, 0x00 }));
- }
-
- if (reserve_size > 0) {
- WIL(WI_LOCAL_GET, stack_top_store_local);
- WID(WI_GLOBAL_SET, stack_top_idx);
-
- bh_align(reserve_size, 16);
- code[reserve_space_patch + 2].data.l = reserve_size;
-
- } else {
- fori (i, 0, 5) code[reserve_space_patch + i].type = WI_NOP;
- }
-
- if (cc == CC_Return_Stack) {
- WID(WI_GLOBAL_GET, stack_top_idx);
- emit_load_instruction(mod, &code, return_type, reserve_size - return_size);
- }
-
- local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
- *pcode = code;
-}
-
-// BUG: This implementation assumes that the host system C's implementation is using
-// little endian integers.
-#define SIMD_INT_CONST_INTRINSIC(type, count) { \
- type* byte_buffer = bh_alloc(mod->extended_instr_alloc, 16); \
- bh_arr(AstArgument *) arg_arr = (bh_arr(AstArgument *)) call->args.values; \
- fori (i, 0, count) { \
- if (arg_arr[i]->value->kind != Ast_Kind_NumLit) { \
- onyx_report_error(arg_arr[i]->token->pos, \
- "SIMD constants expect compile time constants as parameters. The %d%s parameter was not.", \
- i, bh_num_suffix(i)); \
- *pcode = code; \
- return; \
- } \
- byte_buffer[i] = (type) ((AstNumLit *) arg_arr[i]->value)->value.l; \
- } \
- WIP(WI_V128_CONST, byte_buffer); \
- }
-
-#define SIMD_EXTRACT_LANE_INSTR(instr, arg_arr) \
- emit_expression(mod, &code, arg_arr[0]->value);\
- if (arg_arr[1]->value->kind != Ast_Kind_NumLit) { \
- onyx_report_error(arg_arr[1]->token->pos, "SIMD lane instructions expect a compile time lane number."); \
- *pcode = code; \
- return; \
- } \
- WID(instr, (u8) ((AstNumLit *) arg_arr[1]->value)->value.i);
-
-#define SIMD_REPLACE_LANE_INSTR(instr, arg_arr) { \
- emit_expression(mod, &code, arg_arr[0]->value);\
- if (arg_arr[1]->value->kind != Ast_Kind_NumLit) { \
- onyx_report_error(arg_arr[1]->token->pos, "SIMD lane instructions expect a compile time lane number."); \
- *pcode = code; \
- return; \
- } \
- u8 lane = (u8) ((AstNumLit *) arg_arr[1]->value)->value.i; \
- emit_expression(mod, &code, arg_arr[2]->value); \
- WID(instr, lane); \
-}
-
-
-EMIT_FUNC(intrinsic_call, AstCall* call) {
- bh_arr(WasmInstruction) code = *pcode;
-
- b32 place_arguments_normally = 1;
-
- switch (call->intrinsic) {
- case ONYX_INTRINSIC_V128_CONST:
- case ONYX_INTRINSIC_I8X16_CONST: case ONYX_INTRINSIC_I16X8_CONST:
- case ONYX_INTRINSIC_I32X4_CONST: case ONYX_INTRINSIC_I64X2_CONST:
- case ONYX_INTRINSIC_F32X4_CONST: case ONYX_INTRINSIC_F64X2_CONST:
- case ONYX_INTRINSIC_I8X16_EXTRACT_LANE_S: case ONYX_INTRINSIC_I8X16_EXTRACT_LANE_U:
- case ONYX_INTRINSIC_I16X8_EXTRACT_LANE_S: case ONYX_INTRINSIC_I16X8_EXTRACT_LANE_U:
- case ONYX_INTRINSIC_I32X4_EXTRACT_LANE: case ONYX_INTRINSIC_I64X2_EXTRACT_LANE:
- case ONYX_INTRINSIC_F32X4_EXTRACT_LANE: case ONYX_INTRINSIC_F64X2_EXTRACT_LANE:
- case ONYX_INTRINSIC_I8X16_REPLACE_LANE: case ONYX_INTRINSIC_I16X8_REPLACE_LANE:
- case ONYX_INTRINSIC_I32X4_REPLACE_LANE: case ONYX_INTRINSIC_I64X2_REPLACE_LANE:
- case ONYX_INTRINSIC_F32X4_REPLACE_LANE: case ONYX_INTRINSIC_F64X2_REPLACE_LANE:
- case ONYX_INTRINSIC_I8X16_SHUFFLE:
- place_arguments_normally = 0;
-
- default: break;
- }
-
- if (place_arguments_normally) {
- bh_arr_each(AstTyped *, arg, call->args.values) {
- emit_expression(mod, &code, *arg);
- }
- }
-
- switch (call->intrinsic) {
- case ONYX_INTRINSIC_MEMORY_SIZE: WID(WI_MEMORY_SIZE, 0x00); break;
- case ONYX_INTRINSIC_MEMORY_GROW: WID(WI_MEMORY_GROW, 0x00); break;
- case ONYX_INTRINSIC_MEMORY_COPY:
- if (context.options->use_post_mvp_features) {
- WIL(WI_MEMORY_COPY, 0x00);
- } else {
- emit_intrinsic_memory_copy(mod, &code);
- }
- break;
- case ONYX_INTRINSIC_MEMORY_FILL:
- if (context.options->use_post_mvp_features) {
- WIL(WI_MEMORY_FILL, 0x00);
- } else {
- emit_intrinsic_memory_fill(mod, &code);
- }
- break;
-
- case ONYX_INTRINSIC_INITIALIZE: {
- Type* type_to_initialize = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
- emit_initialize_type(mod, &code, type_to_initialize, call->token);
- break;
- }
-
- case ONYX_INTRINSIC_ZERO_VALUE: {
- // NOTE: This probably will not have to make an allocation.
- Type* zero_type = type_build_from_ast(context.ast_alloc, (AstType *) ((AstArgument *) call->original_args.values[0])->value);
- emit_zero_value_for_type(mod, &code, zero_type, call->token);
- break;
- }
-
- case ONYX_INTRINSIC_I32_CLZ: WI(WI_I32_CLZ); break;
- case ONYX_INTRINSIC_I32_CTZ: WI(WI_I32_CTZ); break;
- case ONYX_INTRINSIC_I32_POPCNT: WI(WI_I32_POPCNT); break;
- case ONYX_INTRINSIC_I32_AND: WI(WI_I32_AND); break;
- case ONYX_INTRINSIC_I32_OR: WI(WI_I32_OR); break;
- case ONYX_INTRINSIC_I32_XOR: WI(WI_I32_XOR); break;
- case ONYX_INTRINSIC_I32_SHL: WI(WI_I32_SHL); break;
- case ONYX_INTRINSIC_I32_SLR: WI(WI_I32_SHR_U); break;
- case ONYX_INTRINSIC_I32_SAR: WI(WI_I32_SHR_S); break;
- case ONYX_INTRINSIC_I32_ROTL: WI(WI_I32_ROTL); break;
- case ONYX_INTRINSIC_I32_ROTR: WI(WI_I32_ROTR); break;
-
- case ONYX_INTRINSIC_I64_CLZ: WI(WI_I64_CLZ); break;
- case ONYX_INTRINSIC_I64_CTZ: WI(WI_I64_CTZ); break;
- case ONYX_INTRINSIC_I64_POPCNT: WI(WI_I64_POPCNT); break;
- case ONYX_INTRINSIC_I64_AND: WI(WI_I64_AND); break;
- case ONYX_INTRINSIC_I64_OR: WI(WI_I64_OR); break;
- case ONYX_INTRINSIC_I64_XOR: WI(WI_I64_XOR); break;
- case ONYX_INTRINSIC_I64_SHL: WI(WI_I64_SHL); break;
- case ONYX_INTRINSIC_I64_SLR: WI(WI_I64_SHR_U); break;
- case ONYX_INTRINSIC_I64_SAR: WI(WI_I64_SHR_S); break;
- case ONYX_INTRINSIC_I64_ROTL: WI(WI_I64_ROTL); break;
- case ONYX_INTRINSIC_I64_ROTR: WI(WI_I64_ROTR); break;
-
- case ONYX_INTRINSIC_F32_ABS: WI(WI_F32_ABS); break;
- case ONYX_INTRINSIC_F32_CEIL: WI(WI_F32_CEIL); break;
- case ONYX_INTRINSIC_F32_FLOOR: WI(WI_F32_FLOOR); break;
- case ONYX_INTRINSIC_F32_TRUNC: WI(WI_F32_TRUNC); break;
- case ONYX_INTRINSIC_F32_NEAREST: WI(WI_F32_NEAREST); break;
- case ONYX_INTRINSIC_F32_SQRT: WI(WI_F32_SQRT); break;
- case ONYX_INTRINSIC_F32_MIN: WI(WI_F32_MIN); break;
- case ONYX_INTRINSIC_F32_MAX: WI(WI_F32_MAX); break;
- case ONYX_INTRINSIC_F32_COPYSIGN: WI(WI_F32_COPYSIGN); break;
-
- case ONYX_INTRINSIC_F64_ABS: WI(WI_F64_ABS); break;
- case ONYX_INTRINSIC_F64_CEIL: WI(WI_F64_CEIL); break;
- case ONYX_INTRINSIC_F64_FLOOR: WI(WI_F64_FLOOR); break;
- case ONYX_INTRINSIC_F64_TRUNC: WI(WI_F64_TRUNC); break;
- case ONYX_INTRINSIC_F64_NEAREST: WI(WI_F64_NEAREST); break;
- case ONYX_INTRINSIC_F64_SQRT: WI(WI_F64_SQRT); break;
- case ONYX_INTRINSIC_F64_MIN: WI(WI_F64_MIN); break;
- case ONYX_INTRINSIC_F64_MAX: WI(WI_F64_MAX); break;
- case ONYX_INTRINSIC_F64_COPYSIGN: WI(WI_F64_COPYSIGN); break;
-
- case ONYX_INTRINSIC_I8X16_CONST:
- case ONYX_INTRINSIC_V128_CONST: SIMD_INT_CONST_INTRINSIC(u8, 16); break;
- case ONYX_INTRINSIC_I16X8_CONST: SIMD_INT_CONST_INTRINSIC(u16, 8); break;
- case ONYX_INTRINSIC_I32X4_CONST: SIMD_INT_CONST_INTRINSIC(u32, 4); break;
- case ONYX_INTRINSIC_I64X2_CONST: SIMD_INT_CONST_INTRINSIC(u64, 2); break;
- case ONYX_INTRINSIC_F32X4_CONST: {
- f32* byte_buffer = bh_alloc(mod->extended_instr_alloc, 16);
- bh_arr(AstArgument *) arg_arr = (bh_arr(AstArgument *)) call->args.values;
- fori (i, 0, 4) {
- if (arg_arr[i]->value->kind != Ast_Kind_NumLit) {
- onyx_report_error(arg_arr[i]->token->pos,
- "SIMD constants expect compile time constants as parameters. The %d%s parameter was not.",
- i, bh_num_suffix(i));
- *pcode = code;
- return;
- }
- byte_buffer[i] = (f32) ((AstNumLit *) arg_arr[i]->value)->value.f;
- }
- WIP(WI_V128_CONST, byte_buffer);
- break;
- }
-
- case ONYX_INTRINSIC_F64X2_CONST: {
- f64* byte_buffer = bh_alloc(mod->extended_instr_alloc, 16);
- bh_arr(AstArgument *) arg_arr = (bh_arr(AstArgument *)) call->args.values;
- fori (i, 0, 2) {
- if (arg_arr[i]->value->kind != Ast_Kind_NumLit) {
- onyx_report_error(arg_arr[i]->token->pos,
- "SIMD constants expect compile time constants as parameters. The %d%s parameter was not.",
- i, bh_num_suffix(i));
- *pcode = code;
- return;
- }
- byte_buffer[i] = (f64) ((AstNumLit *) arg_arr[i]->value)->value.d;
- }
- WIP(WI_V128_CONST, byte_buffer);
- break;
- }
-
- case ONYX_INTRINSIC_I8X16_SHUFFLE: {
- u8* byte_buffer = bh_alloc(mod->extended_instr_alloc, 16);
- bh_arr(AstArgument *) arg_arr = (bh_arr(AstArgument *)) call->args.values;
-
- // NOTE: There are two parameters that have to be outputted before
- // the immediate bytes
- emit_expression(mod, &code, arg_arr[0]->value);
- emit_expression(mod, &code, arg_arr[1]->value);
-
- fori (i, 0, 16) {
- if (arg_arr[i + 2]->value->kind != Ast_Kind_NumLit) {
- onyx_report_error(arg_arr[i + 2]->token->pos,
- "SIMD constants expect compile time constants as parameters. The %d%s parameter was not.",
- i, bh_num_suffix(i));
- *pcode = code;
- return;
- }
- byte_buffer[i] = (u8) ((AstNumLit *) arg_arr[i + 2]->value)->value.i;
- }
- WIP(WI_I8X16_SHUFFLE, byte_buffer);
- break;
- }
-
- // CLEANUP ALL OF THIS
- case ONYX_INTRINSIC_I8X16_EXTRACT_LANE_S: SIMD_EXTRACT_LANE_INSTR(WI_I8X16_EXTRACT_LANE_S, ((bh_arr(AstArgument *)) call->args.values)); break;
- case ONYX_INTRINSIC_I8X16_EXTRACT_LANE_U: SIMD_EXTRACT_LANE_INSTR(WI_I8X16_EXTRACT_LANE_U, ((bh_arr(AstArgument *)) call->args.values)); break;
- case ONYX_INTRINSIC_I8X16_REPLACE_LANE: SIMD_REPLACE_LANE_INSTR(WI_I8X16_REPLACE_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
- case ONYX_INTRINSIC_I16X8_EXTRACT_LANE_S: SIMD_EXTRACT_LANE_INSTR(WI_I16X8_EXTRACT_LANE_S, ((bh_arr(AstArgument *)) call->args.values)); break;
- case ONYX_INTRINSIC_I16X8_EXTRACT_LANE_U: SIMD_EXTRACT_LANE_INSTR(WI_I16X8_EXTRACT_LANE_U, ((bh_arr(AstArgument *)) call->args.values)); break;
- case ONYX_INTRINSIC_I16X8_REPLACE_LANE: SIMD_REPLACE_LANE_INSTR(WI_I16X8_REPLACE_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
- case ONYX_INTRINSIC_I32X4_EXTRACT_LANE: SIMD_EXTRACT_LANE_INSTR(WI_I32X4_EXTRACT_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
- case ONYX_INTRINSIC_I32X4_REPLACE_LANE: SIMD_REPLACE_LANE_INSTR(WI_I32X4_REPLACE_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
- case ONYX_INTRINSIC_I64X2_EXTRACT_LANE: SIMD_EXTRACT_LANE_INSTR(WI_I64X2_EXTRACT_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
- case ONYX_INTRINSIC_I64X2_REPLACE_LANE: SIMD_REPLACE_LANE_INSTR(WI_I64X2_REPLACE_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
- case ONYX_INTRINSIC_F32X4_EXTRACT_LANE: SIMD_EXTRACT_LANE_INSTR(WI_F32X4_EXTRACT_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
- case ONYX_INTRINSIC_F32X4_REPLACE_LANE: SIMD_REPLACE_LANE_INSTR(WI_F32X4_REPLACE_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
- case ONYX_INTRINSIC_F64X2_EXTRACT_LANE: SIMD_EXTRACT_LANE_INSTR(WI_F64X2_EXTRACT_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
- case ONYX_INTRINSIC_F64X2_REPLACE_LANE: SIMD_REPLACE_LANE_INSTR(WI_F64X2_REPLACE_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
-
- case ONYX_INTRINSIC_I8X16_SWIZZLE: WI(WI_I8X16_SWIZZLE); break;
- case ONYX_INTRINSIC_I8X16_SPLAT: WI(WI_I8X16_SPLAT); break;
- case ONYX_INTRINSIC_I16X8_SPLAT: WI(WI_I16X8_SPLAT); break;
- case ONYX_INTRINSIC_I32X4_SPLAT: WI(WI_I32X4_SPLAT); break;
- case ONYX_INTRINSIC_I64X2_SPLAT: WI(WI_I64X2_SPLAT); break;
- case ONYX_INTRINSIC_F32X4_SPLAT: WI(WI_F32X4_SPLAT); break;
- case ONYX_INTRINSIC_F64X2_SPLAT: WI(WI_F64X2_SPLAT); break;
-
- case ONYX_INTRINSIC_I8X16_EQ: WI(WI_I8X16_EQ); break;
- case ONYX_INTRINSIC_I8X16_NEQ: WI(WI_I8X16_NEQ); break;
- case ONYX_INTRINSIC_I8X16_LT_S: WI(WI_I8X16_LT_S); break;
- case ONYX_INTRINSIC_I8X16_LT_U: WI(WI_I8X16_LT_U); break;
- case ONYX_INTRINSIC_I8X16_GT_S: WI(WI_I8X16_GT_S); break;
- case ONYX_INTRINSIC_I8X16_GT_U: WI(WI_I8X16_GT_U); break;
- case ONYX_INTRINSIC_I8X16_LE_S: WI(WI_I8X16_LE_S); break;
- case ONYX_INTRINSIC_I8X16_LE_U: WI(WI_I8X16_LE_U); break;
- case ONYX_INTRINSIC_I8X16_GE_S: WI(WI_I8X16_GE_S); break;
- case ONYX_INTRINSIC_I8X16_GE_U: WI(WI_I8X16_GE_U); break;
-
- case ONYX_INTRINSIC_I16X8_EQ: WI(WI_I16X8_EQ); break;
- case ONYX_INTRINSIC_I16X8_NEQ: WI(WI_I16X8_NEQ); break;
- case ONYX_INTRINSIC_I16X8_LT_S: WI(WI_I16X8_LT_S); break;
- case ONYX_INTRINSIC_I16X8_LT_U: WI(WI_I16X8_LT_U); break;
- case ONYX_INTRINSIC_I16X8_GT_S: WI(WI_I16X8_GT_S); break;
- case ONYX_INTRINSIC_I16X8_GT_U: WI(WI_I16X8_GT_U); break;
- case ONYX_INTRINSIC_I16X8_LE_S: WI(WI_I16X8_LE_S); break;
- case ONYX_INTRINSIC_I16X8_LE_U: WI(WI_I16X8_LE_U); break;
- case ONYX_INTRINSIC_I16X8_GE_S: WI(WI_I16X8_GE_S); break;
- case ONYX_INTRINSIC_I16X8_GE_U: WI(WI_I16X8_GE_U); break;
-
- case ONYX_INTRINSIC_I32X4_EQ: WI(WI_I32X4_EQ); break;
- case ONYX_INTRINSIC_I32X4_NEQ: WI(WI_I32X4_NEQ); break;
- case ONYX_INTRINSIC_I32X4_LT_S: WI(WI_I32X4_LT_S); break;
- case ONYX_INTRINSIC_I32X4_LT_U: WI(WI_I32X4_LT_U); break;
- case ONYX_INTRINSIC_I32X4_GT_S: WI(WI_I32X4_GT_S); break;
- case ONYX_INTRINSIC_I32X4_GT_U: WI(WI_I32X4_GT_U); break;
- case ONYX_INTRINSIC_I32X4_LE_S: WI(WI_I32X4_LE_S); break;
- case ONYX_INTRINSIC_I32X4_LE_U: WI(WI_I32X4_LE_U); break;
- case ONYX_INTRINSIC_I32X4_GE_S: WI(WI_I32X4_GE_S); break;
- case ONYX_INTRINSIC_I32X4_GE_U: WI(WI_I32X4_GE_U); break;
-
- case ONYX_INTRINSIC_F32X4_EQ: WI(WI_F32X4_EQ); break;
- case ONYX_INTRINSIC_F32X4_NEQ: WI(WI_F32X4_NEQ); break;
- case ONYX_INTRINSIC_F32X4_LT: WI(WI_F32X4_LT); break;
- case ONYX_INTRINSIC_F32X4_GT: WI(WI_F32X4_GT); break;
- case ONYX_INTRINSIC_F32X4_LE: WI(WI_F32X4_LE); break;
- case ONYX_INTRINSIC_F32X4_GE: WI(WI_F32X4_GE); break;
-
- case ONYX_INTRINSIC_F64X2_EQ: WI(WI_F64X2_EQ); break;
- case ONYX_INTRINSIC_F64X2_NEQ: WI(WI_F64X2_NEQ); break;
- case ONYX_INTRINSIC_F64X2_LT: WI(WI_F64X2_LT); break;
- case ONYX_INTRINSIC_F64X2_GT: WI(WI_F64X2_GT); break;
- case ONYX_INTRINSIC_F64X2_LE: WI(WI_F64X2_LE); break;
- case ONYX_INTRINSIC_F64X2_GE: WI(WI_F64X2_GE); break;
-
- case ONYX_INTRINSIC_V128_NOT: WI(WI_V128_NOT); break;
- case ONYX_INTRINSIC_V128_AND: WI(WI_V128_AND); break;
- case ONYX_INTRINSIC_V128_ANDNOT: WI(WI_V128_ANDNOT); break;
- case ONYX_INTRINSIC_V128_OR: WI(WI_V128_OR); break;
- case ONYX_INTRINSIC_V128_XOR: WI(WI_V128_XOR); break;
- case ONYX_INTRINSIC_V128_BITSELECT: WI(WI_V128_BITSELECT); break;
-
- case ONYX_INTRINSIC_I8X16_ABS: WI(WI_I8X16_ABS); break;
- case ONYX_INTRINSIC_I8X16_NEG: WI(WI_I8X16_NEG); break;
- case ONYX_INTRINSIC_I8X16_ANY_TRUE: WI(WI_I8X16_ANY_TRUE); break;
- case ONYX_INTRINSIC_I8X16_ALL_TRUE: WI(WI_I8X16_ALL_TRUE); break;
- case ONYX_INTRINSIC_I8X16_BITMASK: WI(WI_I8X16_BITMASK); break;
- case ONYX_INTRINSIC_I8X16_NARROW_I16X8_S: WI(WI_I8X16_NARROW_I16X8_S); break;
- case ONYX_INTRINSIC_I8X16_NARROW_I16X8_U: WI(WI_I8X16_NARROW_I16X8_U); break;
- case ONYX_INTRINSIC_I8X16_SHL: WI(WI_I8X16_SHL); break;
- case ONYX_INTRINSIC_I8X16_SHR_S: WI(WI_I8X16_SHR_S); break;
- case ONYX_INTRINSIC_I8X16_SHR_U: WI(WI_I8X16_SHR_U); break;
- case ONYX_INTRINSIC_I8X16_ADD: WI(WI_I8X16_ADD); break;
- case ONYX_INTRINSIC_I8X16_ADD_SAT_S: WI(WI_I8X16_ADD_SAT_S); break;
- case ONYX_INTRINSIC_I8X16_ADD_SAT_U: WI(WI_I8X16_ADD_SAT_U); break;
- case ONYX_INTRINSIC_I8X16_SUB: WI(WI_I8X16_SUB); break;
- case ONYX_INTRINSIC_I8X16_SUB_SAT_S: WI(WI_I8X16_SUB_SAT_S); break;
- case ONYX_INTRINSIC_I8X16_SUB_SAT_U: WI(WI_I8X16_SUB_SAT_U); break;
- case ONYX_INTRINSIC_I8X16_MIN_S: WI(WI_I8X16_MIN_S); break;
- case ONYX_INTRINSIC_I8X16_MIN_U: WI(WI_I8X16_MIN_U); break;
- case ONYX_INTRINSIC_I8X16_MAX_S: WI(WI_I8X16_MAX_S); break;
- case ONYX_INTRINSIC_I8X16_MAX_U: WI(WI_I8X16_MAX_U); break;
- case ONYX_INTRINSIC_I8X16_AVGR_U: WI(WI_I8X16_AVGR_U); break;
-
- case ONYX_INTRINSIC_I16X8_ABS: WI(WI_I16X8_ABS); break;
- case ONYX_INTRINSIC_I16X8_NEG: WI(WI_I16X8_NEG); break;
- case ONYX_INTRINSIC_I16X8_ANY_TRUE: WI(WI_I16X8_ANY_TRUE); break;
- case ONYX_INTRINSIC_I16X8_ALL_TRUE: WI(WI_I16X8_ALL_TRUE); break;
- case ONYX_INTRINSIC_I16X8_BITMASK: WI(WI_I16X8_BITMASK); break;
- case ONYX_INTRINSIC_I16X8_NARROW_I32X4_S: WI(WI_I16X8_NARROW_I32X4_S); break;
- case ONYX_INTRINSIC_I16X8_NARROW_I32X4_U: WI(WI_I16X8_NARROW_I32X4_U); break;
- case ONYX_INTRINSIC_I16X8_WIDEN_LOW_I8X16_S: WI(WI_I16X8_WIDEN_LOW_I8X16_S); break;
- case ONYX_INTRINSIC_I16X8_WIDEN_HIGH_I8X16_S: WI(WI_I16X8_WIDEN_HIGH_I8X16_S); break;
- case ONYX_INTRINSIC_I16X8_WIDEN_LOW_I8X16_U: WI(WI_I16X8_WIDEN_LOW_I8X16_U); break;
- case ONYX_INTRINSIC_I16X8_WIDEN_HIGH_I8X16_U: WI(WI_I16X8_WIDEN_HIGH_I8X16_U); break;
- case ONYX_INTRINSIC_I16X8_SHL: WI(WI_I16X8_SHL); break;
- case ONYX_INTRINSIC_I16X8_SHR_S: WI(WI_I16X8_SHR_S); break;
- case ONYX_INTRINSIC_I16X8_SHR_U: WI(WI_I16X8_SHR_U); break;
- case ONYX_INTRINSIC_I16X8_ADD: WI(WI_I16X8_ADD); break;
- case ONYX_INTRINSIC_I16X8_ADD_SAT_S: WI(WI_I16X8_ADD_SAT_S); break;
- case ONYX_INTRINSIC_I16X8_ADD_SAT_U: WI(WI_I16X8_ADD_SAT_U); break;
- case ONYX_INTRINSIC_I16X8_SUB: WI(WI_I16X8_SUB); break;
- case ONYX_INTRINSIC_I16X8_SUB_SAT_S: WI(WI_I16X8_SUB_SAT_S); break;
- case ONYX_INTRINSIC_I16X8_SUB_SAT_U: WI(WI_I16X8_SUB_SAT_U); break;
- case ONYX_INTRINSIC_I16X8_MUL: WI(WI_I16X8_MUL); break;
- case ONYX_INTRINSIC_I16X8_MIN_S: WI(WI_I16X8_MIN_S); break;
- case ONYX_INTRINSIC_I16X8_MIN_U: WI(WI_I16X8_MIN_U); break;
- case ONYX_INTRINSIC_I16X8_MAX_S: WI(WI_I16X8_MAX_S); break;
- case ONYX_INTRINSIC_I16X8_MAX_U: WI(WI_I16X8_MAX_U); break;
- case ONYX_INTRINSIC_I16X8_AVGR_U: WI(WI_I16X8_AVGR_U); break;
-
- case ONYX_INTRINSIC_I32X4_ABS: WI(WI_I32X4_ABS); break;
- case ONYX_INTRINSIC_I32X4_NEG: WI(WI_I32X4_NEG); break;
- case ONYX_INTRINSIC_I32X4_ANY_TRUE: WI(WI_I32X4_ANY_TRUE); break;
- case ONYX_INTRINSIC_I32X4_ALL_TRUE: WI(WI_I32X4_ALL_TRUE); break;
- case ONYX_INTRINSIC_I32X4_BITMASK: WI(WI_I32X4_BITMASK); break;
- case ONYX_INTRINSIC_I32X4_WIDEN_LOW_I16X8_S: WI(WI_I32X4_WIDEN_LOW_I16X8_S); break;
- case ONYX_INTRINSIC_I32X4_WIDEN_HIGH_I16X8_S: WI(WI_I32X4_WIDEN_HIGH_I16X8_S); break;
- case ONYX_INTRINSIC_I32X4_WIDEN_LOW_I16X8_U: WI(WI_I32X4_WIDEN_LOW_I16X8_U); break;
- case ONYX_INTRINSIC_I32X4_WIDEN_HIGH_I16X8_U: WI(WI_I32X4_WIDEN_HIGH_I16X8_U); break;
- case ONYX_INTRINSIC_I32X4_SHL: WI(WI_I32X4_SHL); break;
- case ONYX_INTRINSIC_I32X4_SHR_S: WI(WI_I32X4_SHR_S); break;
- case ONYX_INTRINSIC_I32X4_SHR_U: WI(WI_I32X4_SHR_U); break;
- case ONYX_INTRINSIC_I32X4_ADD: WI(WI_I32X4_ADD); break;
- case ONYX_INTRINSIC_I32X4_SUB: WI(WI_I32X4_SUB); break;
- case ONYX_INTRINSIC_I32X4_MUL: WI(WI_I32X4_MUL); break;
- case ONYX_INTRINSIC_I32X4_MIN_S: WI(WI_I32X4_MIN_S); break;
- case ONYX_INTRINSIC_I32X4_MIN_U: WI(WI_I32X4_MIN_U); break;
- case ONYX_INTRINSIC_I32X4_MAX_S: WI(WI_I32X4_MAX_S); break;
- case ONYX_INTRINSIC_I32X4_MAX_U: WI(WI_I32X4_MAX_U); break;
-
- case ONYX_INTRINSIC_I64X2_NEG: WI(WI_I64X2_NEG); break;
- case ONYX_INTRINSIC_I64X2_SHL: WI(WI_I64X2_SHL); break;
- case ONYX_INTRINSIC_I64X2_SHR_S: WI(WI_I64X2_SHR_S); break;
- case ONYX_INTRINSIC_I64X2_SHR_U: WI(WI_I64X2_SHR_U); break;
- case ONYX_INTRINSIC_I64X2_ADD: WI(WI_I64X2_ADD); break;
- case ONYX_INTRINSIC_I64X2_SUB: WI(WI_I64X2_SUB); break;
- case ONYX_INTRINSIC_I64X2_MUL: WI(WI_I64X2_MUL); break;
-
- case ONYX_INTRINSIC_F32X4_ABS: WI(WI_F32X4_ABS); break;
- case ONYX_INTRINSIC_F32X4_NEG: WI(WI_F32X4_NEG); break;
- case ONYX_INTRINSIC_F32X4_SQRT: WI(WI_F32X4_SQRT); break;
- case ONYX_INTRINSIC_F32X4_ADD: WI(WI_F32X4_ADD); break;
- case ONYX_INTRINSIC_F32X4_SUB: WI(WI_F32X4_SUB); break;
- case ONYX_INTRINSIC_F32X4_MUL: WI(WI_F32X4_MUL); break;
- case ONYX_INTRINSIC_F32X4_DIV: WI(WI_F32X4_DIV); break;
- case ONYX_INTRINSIC_F32X4_MIN: WI(WI_F32X4_MIN); break;
- case ONYX_INTRINSIC_F32X4_MAX: WI(WI_F32X4_MAX); break;
-
- case ONYX_INTRINSIC_F64X2_ABS: WI(WI_F64X2_ABS); break;
- case ONYX_INTRINSIC_F64X2_NEG: WI(WI_F64X2_NEG); break;
- case ONYX_INTRINSIC_F64X2_SQRT: WI(WI_F64X2_SQRT); break;
- case ONYX_INTRINSIC_F64X2_ADD: WI(WI_F64X2_ADD); break;
- case ONYX_INTRINSIC_F64X2_SUB: WI(WI_F64X2_SUB); break;
- case ONYX_INTRINSIC_F64X2_MUL: WI(WI_F64X2_MUL); break;
- case ONYX_INTRINSIC_F64X2_DIV: WI(WI_F64X2_DIV); break;
- case ONYX_INTRINSIC_F64X2_MIN: WI(WI_F64X2_MIN); break;
- case ONYX_INTRINSIC_F64X2_MAX: WI(WI_F64X2_MAX); break;
-
- case ONYX_INTRINSIC_I32X4_TRUNC_SAT_F32X4_S: WI(WI_I32X4_TRUNC_SAT_F32X4_S); break;
- case ONYX_INTRINSIC_I32X4_TRUNC_SAT_F32X4_U: WI(WI_I32X4_TRUNC_SAT_F32X4_U); break;
- case ONYX_INTRINSIC_F32X4_CONVERT_I32X4_S: WI(WI_F32X4_CONVERT_I32X4_S); break;
- case ONYX_INTRINSIC_F32X4_CONVERT_I32X4_U: WI(WI_F32X4_CONVERT_I32X4_U); break;
-
- default: assert(("Unsupported intrinsic", 0));
- }
-
- *pcode = code;
-}
-
-EMIT_FUNC(subscript_location, AstSubscript* sub, u64* offset_return) {
- bh_arr(WasmInstruction) code = *pcode;
-
- emit_expression(mod, &code, sub->expr);
- if (sub->elem_size != 1) {
- WID(WI_PTR_CONST, sub->elem_size);
- WI(WI_PTR_MUL);
- }
-
- // CLEANUP: This is one dense clusterf**k of code...
- u64 offset = 0;
- if (sub->addr->kind == Ast_Kind_Subscript
- && sub->addr->type->kind == Type_Kind_Array) {
- emit_subscript_location(mod, &code, (AstSubscript *) sub->addr, &offset);
- } else if (sub->addr->kind == Ast_Kind_Field_Access
- && sub->addr->type->kind == Type_Kind_Array) {
- emit_field_access_location(mod, &code, (AstFieldAccess *) sub->addr, &offset);
- } else if ((sub->addr->kind == Ast_Kind_Local || sub->addr->kind == Ast_Kind_Param)
- && sub->addr->type->kind == Type_Kind_Array) {
- emit_local_location(mod, &code, (AstLocal *) sub->addr, &offset);
- } else if (sub->addr->kind == Ast_Kind_Memres
- && sub->addr->type->kind != Type_Kind_Array) {
- emit_memory_reservation_location(mod, &code, (AstMemRes *) sub->addr);
- } else {
- emit_expression(mod, &code, sub->addr);
- }
-
- WI(WI_PTR_ADD);
-
- *offset_return += offset;
-
- *pcode = code;
-}
-
-EMIT_FUNC(field_access_location, AstFieldAccess* field, u64* offset_return) {
- bh_arr(WasmInstruction) code = *pcode;
-
- u64 offset = field->offset;
- AstTyped* source_expr = field->expr;
- while (source_expr->kind == Ast_Kind_Field_Access
- && type_is_structlike_strict(source_expr->type)) {
- offset += ((AstFieldAccess *) source_expr)->offset;
- source_expr = (AstTyped *) ((AstFieldAccess *) source_expr)->expr;
- }
-
- if (source_expr->kind == Ast_Kind_Subscript
- && source_expr->type->kind != Type_Kind_Pointer) {
- u64 o2 = 0;
- emit_subscript_location(mod, &code, (AstSubscript *) source_expr, &o2);
- offset += o2;
-
- } else if ((source_expr->kind == Ast_Kind_Local || source_expr->kind == Ast_Kind_Param)
- && source_expr->type->kind != Type_Kind_Pointer) {
- u64 o2 = 0;
- emit_local_location(mod, &code, (AstLocal *) source_expr, &o2);
- offset += o2;
-
- } else if (source_expr->kind == Ast_Kind_Memres
- && source_expr->type->kind != Type_Kind_Pointer) {
- emit_memory_reservation_location(mod, &code, (AstMemRes *) source_expr);
-
- } else {
- emit_expression(mod, &code, source_expr);
- }
-
- *offset_return = offset;
-
- *pcode = code;
-}
-
-EMIT_FUNC(memory_reservation_location, AstMemRes* memres) {
- bh_arr(WasmInstruction) code = *pcode;
- WID(WI_PTR_CONST, memres->addr);
- *pcode = code;
-}
-
-EMIT_FUNC(local_location, AstLocal* local, u64* offset_return) {
- bh_arr(WasmInstruction) code = *pcode;
-
- u64 local_offset = (u64) bh_imap_get(&mod->local_map, (u64) local);
-
- if (local_offset & LOCAL_IS_WASM) {
- // This is a weird condition but it is relied on in a couple places including
- // passing non-simple structs by value. -brendanfh 2020/09/18
- WIL(WI_LOCAL_GET, local_offset);
-
- } else {
- WIL(WI_LOCAL_GET, mod->stack_base_idx);
-
- *offset_return += local_offset;
- }
-
- *pcode = code;
-}
-
-EMIT_FUNC(struct_lval, AstTyped* lval) {
- bh_arr(WasmInstruction) code = *pcode;
-
- assert(type_is_structlike_strict(lval->type));
-
- u64 offset = 0;
- emit_location_return_offset(mod, &code, lval, &offset);
- emit_compound_store(mod, &code, lval->type, offset, 1);
-
- *pcode = code;
-}
-
-EMIT_FUNC(compound_load, Type* type, u64 offset) {
- bh_arr(WasmInstruction) code = *pcode;
- i32 mem_count = type_linear_member_count(type);
- TypeWithOffset two;
-
- if (mem_count == 1) {
- type_linear_member_lookup(type, 0, &two);
- emit_load_instruction(mod, &code, two.type, offset + two.offset); // two.offset should be 0
- } else {
- u64 tmp_idx = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
- WIL(WI_LOCAL_TEE, tmp_idx);
-
- fori (i, 0, mem_count) {
- type_linear_member_lookup(type, i, &two);
- if (i != 0) WIL(WI_LOCAL_GET, tmp_idx);
- emit_load_instruction(mod, &code, two.type, offset + two.offset);
- }
-
- local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
- }
-
- *pcode = code;
- return;
-}
-
-EMIT_FUNC(compound_store, Type* type, u64 offset, b32 location_first) {
- bh_arr(WasmInstruction) code = *pcode;
-
- TypeWithOffset two;
-
- u64 loc_idx = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
- if (location_first) WIL(WI_LOCAL_SET, loc_idx);
-
- i32 elem_count = type_linear_member_count(type);
- u64 *temp_locals = bh_alloc_array(global_scratch_allocator, u64, elem_count);
-
- forir (i, elem_count - 1, 0) {
- type_linear_member_lookup(type, i, &two);
-
- WasmType wt = onyx_type_to_wasm_type(two.type);
- temp_locals[i] = local_raw_allocate(mod->local_alloc, wt);
- WIL(WI_LOCAL_SET, temp_locals[i]);
- }
-
- if (!location_first) WIL(WI_LOCAL_SET, loc_idx);
-
- fori (i, 0, elem_count) {
- type_linear_member_lookup(type, i, &two);
-
- u64 tmp_idx = temp_locals[i];
- WIL(WI_LOCAL_GET, loc_idx);
- WIL(WI_LOCAL_GET, tmp_idx);
- emit_store_instruction(mod, &code, two.type, offset + two.offset);
-
- WasmType wt = onyx_type_to_wasm_type(two.type);
- local_raw_free(mod->local_alloc, wt);
- }
-
- local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
-
- // This shouldn't be necessary because the scratch allocator doesn't free.
- bh_free(global_scratch_allocator, temp_locals);
-
- *pcode = code;
-}
-
-EMIT_FUNC(struct_literal, AstStructLiteral* sl) {
- bh_arr(WasmInstruction) code = *pcode;
-
- bh_arr_each(AstTyped *, val, sl->args.values) {
- emit_expression(mod, &code, *val);
- }
-
- *pcode = code;
-}
-
-EMIT_FUNC(array_store, Type* type, u32 offset) {
- assert(type->kind == Type_Kind_Array);
- bh_arr(WasmInstruction) code = *pcode;
-
- Type* elem_type = type;
- u32 elem_count = 1;
- while (elem_type->kind == Type_Kind_Array) {
- elem_count *= elem_type->Array.count;
- elem_type = elem_type->Array.elem;
- }
- u32 elem_size = type_size_of(elem_type);
-
- u64 lptr_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
- u64 rptr_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
- WIL(WI_LOCAL_SET, rptr_local);
- WIL(WI_LOCAL_SET, lptr_local);
-
- if (elem_count <= 2) {
- // Inline copying for a small number of elements. It still may be faster to do this in a tight loop.
-
- fori (i, 0, elem_count) {
- if (bh_arr_last(code).type == WI_LOCAL_SET && (u64) bh_arr_last(code).data.l == lptr_local)
- bh_arr_last(code).type = WI_LOCAL_TEE;
- else
- WIL(WI_LOCAL_GET, lptr_local);
-
- WIL(WI_LOCAL_GET, rptr_local);
- emit_load_instruction(mod, &code, elem_type, i * elem_size);
-
- emit_store_instruction(mod, &code, elem_type, i * elem_size + offset);
- }
-
- } else if (context.options->use_post_mvp_features) {
- // Use a simple memory copy if it is available. This may be what happens in the case below too at a later time.
-
- if (bh_arr_last(code).type == WI_LOCAL_SET && (u64) bh_arr_last(code).data.l == lptr_local)
- bh_arr_last(code).type = WI_LOCAL_TEE;
- else
- WIL(WI_LOCAL_GET, lptr_local);
- WIL(WI_PTR_CONST, offset);
- WI(WI_PTR_ADD);
-
- WIL(WI_LOCAL_GET, rptr_local);
- WIL(WI_I32_CONST, elem_count * elem_size);
- WI(WI_MEMORY_COPY);
-
- } else {
- // Emit a loop that copies the memory. This could be switched to a tight loop that just copies word per word.
-
- u64 offset_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
- WIL(WI_PTR_CONST, 0);
- WIL(WI_LOCAL_SET, offset_local);
-
- WID(WI_BLOCK_START, 0x40);
- WID(WI_LOOP_START, 0x40);
- WIL(WI_LOCAL_GET, offset_local);
- WIL(WI_LOCAL_GET, lptr_local);
- WI(WI_PTR_ADD);
-
- WIL(WI_LOCAL_GET, offset_local);
- WIL(WI_LOCAL_GET, rptr_local);
- WI(WI_PTR_ADD);
-
- emit_load_instruction(mod, &code, elem_type, 0);
- emit_store_instruction(mod, &code, elem_type, offset);
-
- WIL(WI_LOCAL_GET, offset_local);
- WIL(WI_PTR_CONST, elem_size);
- WI(WI_PTR_ADD);
- WIL(WI_LOCAL_TEE, offset_local);
-
- WIL(WI_PTR_CONST, elem_count * elem_size);
- WI(WI_PTR_GE);
- WID(WI_COND_JUMP, 0x01);
-
- WID(WI_JUMP, 0x00);
-
- WI(WI_LOOP_END);
- WI(WI_BLOCK_END);
-
- local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
- }
-
- local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
- local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
-
- *pcode = code;
- return;
-}
-
-EMIT_FUNC(array_literal, AstArrayLiteral* al) {
- bh_arr(WasmInstruction) code = *pcode;
-
- u64 local_offset = (u64) bh_imap_get(&mod->local_map, (u64) al);
- assert((local_offset & LOCAL_IS_WASM) == 0);
-
- assert(al->type->kind == Type_Kind_Array);
-
- u32 elem_size = type_size_of(al->type->Array.elem);
-
- fori (i, 0, al->type->Array.count) {
- WIL(WI_LOCAL_GET, mod->stack_base_idx);
- emit_expression(mod, &code, al->values[i]);
- emit_store_instruction(mod, &code, al->type->Array.elem, local_offset + i * elem_size);
- }
-
- WIL(WI_LOCAL_GET, mod->stack_base_idx);
- WIL(WI_PTR_CONST, local_offset);
- WI(WI_PTR_ADD);
-
- *pcode = code;
-}
-
-EMIT_FUNC(range_literal, AstRangeLiteral* range) {
- bh_arr(WasmInstruction) code = *pcode;
-
- emit_expression(mod, &code, range->low);
- emit_expression(mod, &code, range->high);
- emit_expression(mod, &code, range->step);
-
- *pcode = code;
-}
-
-EMIT_FUNC(if_expression, AstIfExpression* if_expr) {
- bh_arr(WasmInstruction) code = *pcode;
-
- u64 offset = 0;
- u64 result_local = local_allocate(mod->local_alloc, (AstTyped *) if_expr);
- b32 result_is_local = (b32) ((result_local & LOCAL_IS_WASM) != 0);
- bh_imap_put(&mod->local_map, (u64) if_expr, result_local);
-
- emit_expression(mod, &code, if_expr->cond);
-
- emit_enter_structured_block(mod, &code, SBT_Basic_If);
- if (!result_is_local) emit_local_location(mod, &code, (AstLocal *) if_expr, &offset);
-
- emit_expression(mod, &code, if_expr->true_expr);
-
- if (!result_is_local) emit_store_instruction(mod, &code, if_expr->type, offset);
- else WIL(WI_LOCAL_SET, result_local);
-
- offset = 0;
- WI(WI_ELSE);
- if (!result_is_local) emit_local_location(mod, &code, (AstLocal *) if_expr, &offset);
-
- emit_expression(mod, &code, if_expr->false_expr);
-
- if (!result_is_local) emit_store_instruction(mod, &code, if_expr->type, offset);
- else WIL(WI_LOCAL_SET, result_local);
-
- emit_leave_structured_block(mod, &code);
-
- offset = 0;
- if (!result_is_local) {
- emit_local_location(mod, &code, (AstLocal *) if_expr, &offset);
- emit_load_instruction(mod, &code, if_expr->type, offset);
-
- } else {
- WIL(WI_LOCAL_GET, result_local);
- }
-
- local_free(mod->local_alloc, (AstTyped *) if_expr);
-
- *pcode = code;
-}
-
-EMIT_FUNC(do_block, AstDoBlock* doblock) {
- bh_arr(WasmInstruction) code = *pcode;
-
- u64 result_local = local_allocate(mod->local_alloc, (AstTyped *) doblock);
- b32 result_is_local = (b32) ((result_local & LOCAL_IS_WASM) != 0);
-
- bh_imap_put(&mod->local_map, (u64) doblock, result_local);
- bh_arr_push(mod->return_location_stack, (AstLocal *) doblock);
-
- emit_block(mod, &code, doblock->block, 1);
-
- u64 offset = 0;
- if (!result_is_local) {
- emit_local_location(mod, &code, (AstLocal *) doblock, &offset);
- emit_load_instruction(mod, &code, doblock->type, offset);
-
- } else {
- WIL(WI_LOCAL_GET, result_local);
- }
-
- bh_arr_pop(mod->return_location_stack);
- local_free(mod->local_alloc, (AstTyped *) doblock);
-
- *pcode = code;
-}
-
-EMIT_FUNC(location_return_offset, AstTyped* expr, u64* offset_return) {
- bh_arr(WasmInstruction) code = *pcode;
-
- switch (expr->kind) {
- case Ast_Kind_Param:
- case Ast_Kind_Local: {
- emit_local_location(mod, &code, (AstLocal *) expr, offset_return);
- break;
- }
-
- case Ast_Kind_Dereference: {
- emit_expression(mod, &code, ((AstDereference *) expr)->expr);
- *offset_return = 0;
- break;
- }
-
- case Ast_Kind_Subscript: {
- AstSubscript* sub = (AstSubscript *) expr;
- emit_subscript_location(mod, &code, sub, offset_return);
- break;
- }
-
- case Ast_Kind_Field_Access: {
- AstFieldAccess* field = (AstFieldAccess *) expr;
- emit_field_access_location(mod, &code, field, offset_return);
- break;
- }
-
- case Ast_Kind_Memres: {
- AstMemRes* memres = (AstMemRes *) expr;
- WID(WI_PTR_CONST, memres->addr);
- *offset_return = 0;
- break;
- }
-
- default: {
- onyx_report_error(expr->token->pos, "Unable to generate locate for '%s'.", onyx_ast_node_kind_string(expr->kind));
- break;
- }
- }
-
- *pcode = code;
-}
-
-EMIT_FUNC(location, AstTyped* expr) {
- bh_arr(WasmInstruction) code = *pcode;
-
- u64 offset = 0;
- emit_location_return_offset(mod, &code, expr, &offset);
- if (offset != 0) {
- WID(WI_PTR_CONST, offset);
- WI(WI_PTR_ADD);
- }
-
- *pcode = code;
-}
-
-EMIT_FUNC(expression, AstTyped* expr) {
- bh_arr(WasmInstruction) code = *pcode;
-
- if (node_is_type((AstNode *) expr)) {
- AstType* type = (AstType *) expr;
- if (type->type_id != 0) {
- WID(WI_I32_CONST, ((AstType *) expr)->type_id);
- } else {
- Type* t = type_build_from_ast(context.ast_alloc, type);
- WID(WI_I32_CONST, t->id);
- }
-
- *pcode = code;
- return;
- }
-
- switch (expr->kind) {
- case Ast_Kind_Param: {
- AstLocal* param = (AstLocal *) expr;
- u64 localidx = bh_imap_get(&mod->local_map, (u64) param);
-
- switch (type_get_param_pass(param->type)) {
- case Param_Pass_By_Value: {
- if (type_is_structlike_strict(expr->type)) {
- u32 mem_count = type_structlike_mem_count(expr->type);
- fori (idx, 0, mem_count) WIL(WI_LOCAL_GET, localidx + idx);
-
- } else {
- WIL(WI_LOCAL_GET, localidx);
- }
- break;
- }
-
- case Param_Pass_By_Implicit_Pointer: {
- WIL(WI_LOCAL_GET, localidx);
- emit_load_instruction(mod, &code, expr->type, 0);
- break;
- }
-
- default: assert(0);
- }
-
- break;
- }
-
- case Ast_Kind_Local: {
- u64 tmp = bh_imap_get(&mod->local_map, (u64) expr);
-
- if (tmp & LOCAL_IS_WASM) {
- if (bh_arr_last(code).type == WI_LOCAL_SET && (u64) bh_arr_last(code).data.l == tmp) {
- bh_arr_last(code).type = WI_LOCAL_TEE;
- } else {
- WIL(WI_LOCAL_GET, tmp);
- }
-
- } else {
- u64 offset = 0;
- emit_local_location(mod, &code, (AstLocal *) expr, &offset);
-
- if (expr->type->kind != Type_Kind_Array) {
- emit_load_instruction(mod, &code, expr->type, offset);
- } else if (offset != 0) {
- WID(WI_PTR_CONST, offset);
- WI(WI_PTR_ADD);
- }
- }
-
- break;
- }
-
- case Ast_Kind_Global: {
- i32 globalidx = (i32) bh_imap_get(&mod->index_map, (u64) expr);
-
- WID(WI_GLOBAL_GET, globalidx);
- break;
- }
-
- case Ast_Kind_NumLit: {
- AstNumLit* lit = (AstNumLit *) expr;
- WasmType lit_type = onyx_type_to_wasm_type(lit->type);
- WasmInstruction instr = { WI_NOP, 0 };
-
- if (lit_type == WASM_TYPE_INT32) {
- instr.type = WI_I32_CONST;
- instr.data.i1 = lit->value.i;
- } else if (lit_type == WASM_TYPE_INT64) {
- instr.type = WI_I64_CONST;
- instr.data.l = lit->value.l;
- } else if (lit_type == WASM_TYPE_FLOAT32) {
- instr.type = WI_F32_CONST;
- instr.data.f = lit->value.f;
- } else if (lit_type == WASM_TYPE_FLOAT64) {
- instr.type = WI_F64_CONST;
- instr.data.d = lit->value.d;
- }
-
- bh_arr_push(code, instr);
- break;
- }
-
- case Ast_Kind_StrLit: {
- WID(WI_PTR_CONST, ((AstStrLit *) expr)->addr);
- WID(WI_I32_CONST, ((AstStrLit *) expr)->length);
- break;
- }
-
- case Ast_Kind_Struct_Literal: {
- emit_struct_literal(mod, &code, (AstStructLiteral *) expr);
- break;
- }
-
- case Ast_Kind_Array_Literal: {
- emit_local_allocation(mod, &code, expr);
- emit_array_literal(mod, &code, (AstArrayLiteral *) expr);
- break;
- }
-
- case Ast_Kind_Range_Literal: {
- emit_range_literal(mod, &code, (AstRangeLiteral *) expr);
- break;
- }
-
- case Ast_Kind_Function: {
- i32 elemidx = get_element_idx(mod, (AstFunction *) expr);
-
- WID(WI_I32_CONST, elemidx);
- break;
- }
-
- case Ast_Kind_Block: emit_block(mod, &code, (AstBlock *) expr, 1); break;
- case Ast_Kind_Do_Block: emit_do_block(mod, &code, (AstDoBlock *) expr); break;
- case Ast_Kind_Call: emit_call(mod, &code, (AstCall *) expr); break;
- case Ast_Kind_Argument: emit_expression(mod, &code, ((AstArgument *) expr)->value); break;
- case Ast_Kind_Intrinsic_Call: emit_intrinsic_call(mod, &code, (AstCall *) expr); break;
- case Ast_Kind_Binary_Op: emit_binop(mod, &code, (AstBinaryOp *) expr); break;
- case Ast_Kind_Unary_Op: emit_unaryop(mod, &code, (AstUnaryOp *) expr); break;
- case Ast_Kind_Alias: emit_expression(mod, &code, ((AstAlias *) expr)->alias); break;
-
- case Ast_Kind_Address_Of: {
- AstAddressOf* aof = (AstAddressOf *) expr;
- emit_location(mod, &code, aof->expr);
- break;
- }
-
- case Ast_Kind_Dereference: {
- AstDereference* deref = (AstDereference *) expr;
- emit_expression(mod, &code, deref->expr);
- emit_load_instruction(mod, &code, deref->type, 0);
- break;
- }
-
- case Ast_Kind_Subscript: {
- AstSubscript* sub = (AstSubscript *) expr;
- u64 offset = 0;
- emit_subscript_location(mod, &code, sub, &offset);
- emit_load_instruction(mod, &code, sub->type, offset);
- break;
- }
-
- case Ast_Kind_Field_Access: {
- AstFieldAccess* field = (AstFieldAccess* ) expr;
-
- if (field->expr->kind == Ast_Kind_Param) {
- if (type_get_param_pass(field->expr->type) == Param_Pass_By_Value && !type_is_pointer(field->expr->type)) {
- u64 localidx = bh_imap_get(&mod->local_map, (u64) field->expr) + field->idx;
- WIL(WI_LOCAL_GET, localidx);
- break;
- }
- }
-
- if (is_lval((AstNode *) field->expr) || type_is_pointer(field->expr->type)) {
- u64 offset = 0;
- emit_field_access_location(mod, &code, field, &offset);
- emit_load_instruction(mod, &code, field->type, offset);
-
- } else {
- emit_expression(mod, &code, field->expr);
-
- i32 idx = type_get_idx_of_linear_member_with_offset(field->expr->type, field->offset);
- i32 field_linear_members = type_linear_member_count(field->type);
- i32 total_linear_members = type_linear_member_count(field->expr->type);
-
- if (idx == 0) {
- // Easy case: the member is the first one and all other members just have to be dropped.
- fori (i, 0, total_linear_members - field_linear_members) WI(WI_DROP);
-
- } else {
- // Tough case: Stack shuffling to make the member the only thing on the stack.
- // This is very similar to the compound_load/compound_store procedures but it is different enough
- // that I cannot find a good way to factor them all without just introducing a ton of complexity.
- fori (i, 0, total_linear_members - idx - field_linear_members) WI(WI_DROP);
-
- u64 *temporaries = bh_alloc_array(global_scratch_allocator, u64, field_linear_members);
- fori (i, 0, field_linear_members) temporaries[i] = 0;
-
- TypeWithOffset two = { 0 };
- forir (i, field_linear_members - 1, 0) {
- type_linear_member_lookup(field->type, i, &two);
-
- WasmType wt = onyx_type_to_wasm_type(two.type);
- temporaries[i] = local_raw_allocate(mod->local_alloc, wt);
- WIL(WI_LOCAL_SET, temporaries[i]);
- }
-
- fori (i, 0, idx) WI(WI_DROP);
-
- fori (i, 0, field_linear_members) {
- type_linear_member_lookup(field->type, i, &two);
-
- WIL(WI_LOCAL_GET, temporaries[i]);
-
- WasmType wt = onyx_type_to_wasm_type(two.type);
- local_raw_free(mod->local_alloc, wt);
- }
-
- bh_free(global_scratch_allocator, temporaries);
- }
- }
-
- break;
- }
-
- case Ast_Kind_Slice: {
- AstSubscript* sl = (AstSubscript *) expr;
-
- emit_expression(mod, &code, sl->expr);
-
- u64 lo_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
- u64 hi_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
-
- WI(WI_DROP);
- WIL(WI_LOCAL_SET, hi_local);
- WIL(WI_LOCAL_TEE, lo_local);
- if (sl->elem_size != 1) {
- WID(WI_I32_CONST, sl->elem_size);
- WI(WI_I32_MUL);
- }
- emit_expression(mod, &code, sl->addr);
- WI(WI_I32_ADD);
- WIL(WI_LOCAL_GET, hi_local);
- WIL(WI_LOCAL_GET, lo_local);
- WI(WI_I32_SUB);
-
- local_raw_free(mod->local_alloc, lo_local);
- local_raw_free(mod->local_alloc, hi_local);
- break;
- }
-
- case Ast_Kind_Size_Of: {
- AstSizeOf* so = (AstSizeOf *) expr;
- WID(WI_I32_CONST, so->size);
- break;
- }
-
- case Ast_Kind_Align_Of: {
- AstAlignOf* ao = (AstAlignOf *) expr;
- WID(WI_I32_CONST, ao->alignment);
- break;
- }
-
- case Ast_Kind_Enum_Value: {
- AstEnumValue* ev = (AstEnumValue *) expr;
- WasmType backing_type = onyx_type_to_wasm_type(ev->type);
- if (backing_type == WASM_TYPE_INT32) WID(WI_I32_CONST, ev->value->value.i);
- else if (backing_type == WASM_TYPE_INT64) WID(WI_I64_CONST, ev->value->value.l);
- else onyx_report_error(ev->token->pos, "Invalid backing type for enum.");
- break;
- }
-
- case Ast_Kind_Memres: {
- AstMemRes* memres = (AstMemRes *) expr;
- WID(WI_I32_CONST, memres->addr);
- emit_load_instruction(mod, &code, memres->type, 0);
- break;
- }
-
- case Ast_Kind_File_Contents: {
- AstFileContents* fc = (AstFileContents *) expr;
-
- assert(fc->addr > 0);
- assert(fc->size > 0);
-
- WID(WI_PTR_CONST, fc->addr);
- WID(WI_I32_CONST, fc->size);
- break;
- }
-
- case Ast_Kind_Compound: {
- AstCompound* compound = (AstCompound *) expr;
-
- bh_arr_each(AstTyped *, expr, compound->exprs) {
- emit_expression(mod, &code, *expr);
- }
- break;
- }
-
- case Ast_Kind_Call_Site: {
- AstCallSite* callsite = (AstCallSite *) expr;
-
- emit_expression(mod, &code, (AstTyped *) callsite->filename);
- emit_expression(mod, &code, (AstTyped *) callsite->line);
- emit_expression(mod, &code, (AstTyped *) callsite->column);
- break;
- }
-
- case Ast_Kind_If_Expression: {
- AstIfExpression* if_expr = (AstIfExpression *) expr;
- emit_if_expression(mod, &code, if_expr);
- break;
- }
-
- default:
- bh_printf("Unhandled case: %d\n", expr->kind);
- DEBUG_HERE;
- assert(0);
- }
-
- if ((expr->flags & Ast_Flag_Expr_Ignored) != 0 && !type_results_in_void(expr->type)) {
- i32 mem_count = 1;
- if (type_is_compound(expr->type)) mem_count = type_linear_member_count(expr->type);
- fori (i, 0, mem_count) WI(WI_DROP);
- }
-
- *pcode = code;
-}
-
-static const WasmInstructionType cast_map[][12] = {
- // I8 U8 I16 U16 I32 U32 I64 U64 F32 F64 PTR
- /* I8 */ { WI_NOP, WI_NOP, WI_I32_EXTEND_8_S, WI_NOP, WI_I32_EXTEND_8_S, WI_NOP, WI_I64_FROM_I32_S, WI_I64_FROM_I32_S, WI_UNREACHABLE, WI_UNREACHABLE, WI_UNREACHABLE, WI_UNREACHABLE },
- /* U8 */ { WI_NOP, WI_NOP, WI_NOP, WI_NOP, WI_NOP, WI_NOP, WI_I64_FROM_I32_U, WI_I64_FROM_I32_U, WI_UNREACHABLE, WI_UNREACHABLE, WI_UNREACHABLE, WI_UNREACHABLE },
- /* I16 */ { WI_NOP, WI_NOP, WI_NOP, WI_NOP, WI_I32_EXTEND_16_S, WI_NOP, WI_I64_FROM_I32_S, WI_I64_FROM_I32_S, WI_UNREACHABLE, WI_UNREACHABLE, WI_UNREACHABLE, WI_UNREACHABLE },
- /* U16 */ { WI_NOP, WI_NOP, WI_NOP, WI_NOP, WI_NOP, WI_NOP, WI_I64_FROM_I32_U, WI_I64_FROM_I32_U, WI_UNREACHABLE, WI_UNREACHABLE, WI_UNREACHABLE, WI_UNREACHABLE },
- /* I32 */ { WI_NOP, WI_NOP, WI_NOP, WI_NOP, WI_NOP, WI_NOP, WI_I64_FROM_I32_S, WI_I64_FROM_I32_S, WI_F32_FROM_I32_S, WI_F64_FROM_I32_S, WI_NOP, WI_NOP },
- /* U32 */ { WI_NOP, WI_NOP, WI_NOP, WI_NOP, WI_NOP, WI_NOP, WI_I64_FROM_I32_U, WI_I64_FROM_I32_U, WI_F32_FROM_I32_U, WI_F64_FROM_I32_U, WI_NOP, WI_NOP },
- /* I64 */ { WI_NOP, WI_I32_FROM_I64, WI_I32_FROM_I64, WI_I32_FROM_I64, WI_I32_FROM_I64, WI_I32_FROM_I64, WI_NOP, WI_NOP, WI_F32_FROM_I64_S, WI_F64_FROM_I64_S, WI_I32_FROM_I64, WI_UNREACHABLE },
- /* U64 */ { WI_NOP, WI_I32_FROM_I64, WI_I32_FROM_I64, WI_I32_FROM_I64, WI_I32_FROM_I64, WI_I32_FROM_I64, WI_NOP, WI_NOP, WI_F32_FROM_I64_U, WI_F64_FROM_I64_U, WI_I32_FROM_I64, WI_UNREACHABLE },
- /* F32 */ { WI_UNREACHABLE, WI_UNREACHABLE, WI_UNREACHABLE, WI_UNREACHABLE, WI_I32_FROM_F32_S, WI_I32_FROM_F32_U, WI_I64_FROM_F32_S, WI_I64_FROM_F32_U, WI_NOP, WI_F64_FROM_F32, WI_UNREACHABLE, WI_UNREACHABLE },
- /* F64 */ { WI_UNREACHABLE, WI_UNREACHABLE, WI_UNREACHABLE, WI_UNREACHABLE, WI_I32_FROM_F64_S, WI_I32_FROM_F64_U, WI_I64_FROM_F64_S, WI_I64_FROM_F64_U, WI_F32_FROM_F64, WI_NOP, WI_UNREACHABLE, WI_UNREACHABLE },
- /* PTR */ { WI_UNREACHABLE, WI_UNREACHABLE, WI_UNREACHABLE, WI_UNREACHABLE, WI_NOP, WI_NOP, WI_I64_FROM_I32_U, WI_I64_FROM_I32_U, WI_UNREACHABLE, WI_UNREACHABLE, WI_NOP, WI_UNREACHABLE },
- /* TYP */ { WI_UNREACHABLE, WI_UNREACHABLE, WI_UNREACHABLE, WI_UNREACHABLE, WI_NOP, WI_NOP, WI_UNREACHABLE, WI_UNREACHABLE, WI_UNREACHABLE, WI_UNREACHABLE, WI_UNREACHABLE, WI_NOP },
-};
-
-EMIT_FUNC(cast, AstUnaryOp* cast) {
- bh_arr(WasmInstruction) code = *pcode;
-
- emit_expression(mod, &code, cast->expr);
-
- Type* from = cast->expr->type;
- Type* to = cast->type;
- if (from->kind == Type_Kind_Enum) from = from->Enum.backing;
- if (to->kind == Type_Kind_Enum) to = to->Enum.backing;
-
- if (type_is_simd(from) && type_is_simd(to)) {
- *pcode = code;
- return;
- }
-
- if (to->kind == Type_Kind_Basic && to->Basic.kind == Basic_Kind_Void) {
- WI(WI_DROP);
- *pcode = code;
- return;
- }
-
- if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_Array) {
- WID(WI_I32_CONST, from->Array.count);
- *pcode = code;
- return;
- }
-
- if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_VarArgs) {
- // Nothing needs to be done because they are identical
- *pcode = code;
- return;
- }
-
- i32 fromidx = -1, toidx = -1;
- if (from->Basic.flags & Basic_Flag_Pointer || from->kind == Type_Kind_Array) {
- fromidx = 10;
- }
- else if (from->Basic.flags & Basic_Flag_Integer) {
- b32 unsign = (from->Basic.flags & Basic_Flag_Unsigned) != 0;
-
- fromidx = log2_dumb(from->Basic.size) * 2 + unsign;
- }
- else if (from->Basic.flags & Basic_Flag_Float) {
- if (from->Basic.size == 4) fromidx = 8;
- else if (from->Basic.size == 8) fromidx = 9;
- }
- else if (from->Basic.flags & Basic_Flag_Boolean) {
- fromidx = 0;
- }
- else if (from->Basic.flags & Basic_Flag_Type_Index) {
- fromidx = 11;
- }
-
- if (to->Basic.flags & Basic_Flag_Pointer || to->kind == Type_Kind_Array) {
- toidx = 10;
- }
- else if (to->Basic.flags & Basic_Flag_Integer) {
- b32 unsign = (to->Basic.flags & Basic_Flag_Unsigned) != 0;
-
- toidx = log2_dumb(to->Basic.size) * 2 + unsign;
- }
- else if (to->Basic.flags & Basic_Flag_Float) {
- if (to->Basic.size == 4) toidx = 8;
- else if (to->Basic.size == 8) toidx = 9;
- }
- else if (to->Basic.flags & Basic_Flag_Boolean) {
- toidx = 0;
- }
- else if (to->Basic.flags & Basic_Flag_Type_Index) {
- toidx = 11;
- }
-
- if (fromidx != -1 && toidx != -1) {
- WasmInstructionType cast_op = cast_map[fromidx][toidx];
- assert(cast_op != WI_UNREACHABLE);
-
- if (cast_op != WI_NOP) {
- WI(cast_op);
- }
- }
-
- *pcode = code;
-}
-
-EMIT_FUNC(return, AstReturn* ret) {
- bh_arr(WasmInstruction) code = *pcode;
-
- // If we have an expression to return, we see if it should be placed on the linear memory stack, or the WASM stack.
- if (ret->expr) {
- if (bh_arr_length(mod->return_location_stack) > 0) {
- AstLocal* dest = bh_arr_last(mod->return_location_stack);
- u64 dest_loc = bh_imap_get(&mod->local_map, (u64) dest);
- b32 dest_is_local = (b32) ((dest_loc & LOCAL_IS_WASM) != 0);
-
- u64 offset = 0;
- if (!dest_is_local) emit_local_location(mod, &code, dest, &offset);
-
- emit_expression(mod, &code, ret->expr);
-
- if (!dest_is_local) emit_store_instruction(mod, &code, dest->type, offset);
- else WIL(WI_LOCAL_SET, dest_loc);
-
- } else if (mod->curr_cc == CC_Return_Stack) {
- WIL(WI_LOCAL_GET, mod->stack_base_idx);
- WID(WI_I32_CONST, type_size_of(ret->expr->type));
- WI(WI_I32_SUB);
-
- emit_expression(mod, &code, ret->expr);
- emit_store_instruction(mod, &code, ret->expr->type, 0);
-
- } else {
- emit_expression(mod, &code, ret->expr);
- }
- }
-
- // Clear the normal deferred statements
- emit_deferred_stmts(mod, &code);
-
- // Clear the rest of the deferred statements
- if (bh_arr_length(mod->deferred_stmts) > 0) {
- i32 i = bh_arr_length(mod->deferred_stmts) - 1;
- while (i >= 0) {
- emit_deferred_stmt(mod, &code, mod->deferred_stmts[i]);
- i--;
- }
- }
-
- i64 jump_label = get_structured_jump_label(mod, Jump_Type_Return, 1);
- if (jump_label >= 0) {
- WIL(WI_JUMP, jump_label);
-
- } else {
- // Make a patch for the two instructions needed to restore the stack pointer
- SUBMIT_PATCH(mod->stack_leave_patches, 0);
- WI(WI_NOP);
- WI(WI_NOP);
-
- WI(WI_RETURN);
- }
-
- *pcode = code;
-}
-
-EMIT_FUNC(stack_enter, u64 stacksize) {
- bh_arr(WasmInstruction) code = *pcode;
-
- bh_align(stacksize, 16);
-
- u64 stack_top_idx = bh_imap_get(&mod->index_map, (u64) &builtin_stack_top);
-
- // HACK: slightly... There will be space for 5 instructions
- if (stacksize == 0) {
- code[0] = (WasmInstruction) { WI_GLOBAL_GET, { .l = stack_top_idx } };
- code[1] = (WasmInstruction) { WI_LOCAL_SET, { .l = mod->stack_base_idx} };
- code[2] = (WasmInstruction) { WI_NOP, 0 };
- code[3] = (WasmInstruction) { WI_NOP, 0 };
- code[4] = (WasmInstruction) { WI_NOP, 0 };
- } else {
- code[0] = (WasmInstruction) { WI_GLOBAL_GET, { .l = stack_top_idx } };
- code[1] = (WasmInstruction) { WI_LOCAL_TEE, { .l = mod->stack_base_idx} };
- code[2] = (WasmInstruction) { WI_I32_CONST, { .l = stacksize } };
- code[3] = (WasmInstruction) { WI_I32_ADD, 0 };
- code[4] = (WasmInstruction) { WI_GLOBAL_SET, { .l = stack_top_idx } };
- }
-
- *pcode = code;
-}
-
-EMIT_FUNC(zero_value, WasmType wt) {
- bh_arr(WasmInstruction) code = *pcode;
-
- switch (wt) {
- case WASM_TYPE_INT32: WIL(WI_I32_CONST, 0); break;
- case WASM_TYPE_INT64: WIL(WI_I64_CONST, 0); break;
- case WASM_TYPE_FLOAT32: WIL(WI_F32_CONST, 0); break;
- case WASM_TYPE_FLOAT64: WIL(WI_F64_CONST, 0); break;
- case WASM_TYPE_VAR128: {
- static u8 zero_v128[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
- WIP(WI_V128_CONST, &zero_v128);
- break;
- }
- }
-
- *pcode = code;
-}
-
-EMIT_FUNC(zero_value_for_type, Type* type, OnyxToken* where) {
- bh_arr(WasmInstruction) code = *pcode;
-
- if (type_is_structlike_strict(type)) {
- i32 mem_count = type_linear_member_count(type);
- TypeWithOffset two;
-
- fori (i, 0, mem_count) {
- type_linear_member_lookup(type, i, &two);
- emit_zero_value_for_type(mod, &code, two.type, where);
- }
-
- }
- else if (type->kind == Type_Kind_Function) {
- WID(WI_I32_CONST, mod->null_proc_func_idx);
- }
- else {
- WasmType wt = onyx_type_to_wasm_type(type);
- if (wt == WASM_TYPE_VOID) {
- onyx_report_error(where->pos, "Cannot produce a zero-value for this type.");
- }
- emit_zero_value(mod, &code, wt);
- }
-
- *pcode = code;
-}
-
-static i32 generate_type_idx(OnyxWasmModule* mod, Type* ft) {
- if (ft->kind != Type_Kind_Function) return -1;
-
- static char type_repr_buf[128];
- char* t = type_repr_buf;
-
- Type** param_type = ft->Function.params;
- i32 param_count = ft->Function.param_count;
- i32 params_left = param_count;
-
- while (params_left-- > 0) {
- if (type_get_param_pass(*param_type) == Param_Pass_By_Implicit_Pointer) {
- *(t++) = (char) onyx_type_to_wasm_type(&basic_types[Basic_Kind_Rawptr]);
-
- }
- else if (type_is_structlike_strict(*param_type)) {
- u32 mem_count = type_structlike_mem_count(*param_type);
- StructMember smem;
-
- fori (i, 0, mem_count) {
- type_lookup_member_by_idx(*param_type, i, &smem);
- *(t++) = (char) onyx_type_to_wasm_type(smem.type);
- }
-
- param_count += mem_count - 1;
-
- } else {
- *(t++) = (char) onyx_type_to_wasm_type(*param_type);
- }
-
- param_type++;
- }
- *(t++) = ':';
-
- // HACK: Slightly: the wasm type for structs has to be 0x00
- WasmType return_type = onyx_type_to_wasm_type(ft->Function.return_type);
- *(t++) = (char) return_type;
- *t = '\0';
-
- i32 type_idx = 0;
- if (bh_table_has(i32, mod->type_map, type_repr_buf)) {
- type_idx = bh_table_get(i32, mod->type_map, type_repr_buf);
- } else {
- // NOTE: Make a new type
- WasmFuncType* type = (WasmFuncType*) bh_alloc(mod->allocator, sizeof(WasmFuncType) + sizeof(WasmType) * param_count);
- type->return_type = return_type;
- type->param_count = param_count;
-
- // HACK ish thing
- memcpy(type->param_types, type_repr_buf, type->param_count);
-
- bh_arr_push(mod->types, type);
-
- bh_table_put(i32, mod->type_map, type_repr_buf, mod->next_type_idx);
- type_idx = mod->next_type_idx;
- mod->next_type_idx++;
- }
-
- return type_idx;
-}
-
-static i32 get_element_idx(OnyxWasmModule* mod, AstFunction* func) {
- if (bh_imap_has(&mod->elem_map, (u64) func)) {
- return bh_imap_get(&mod->elem_map, (u64) func);
- } else {
- i32 idx = mod->next_elem_idx;
- bh_imap_put(&mod->elem_map, (u64) func, idx);
-
- i32 func_idx = bh_imap_get(&mod->index_map, (u64) func);
- bh_arr_push(mod->elems, func_idx);
-
- mod->next_elem_idx++;
-
- return idx;
- }
-}
-
-static inline b32 should_emit_function(AstFunction* fd) {
- // NOTE: Don't output intrinsic functions
- if (fd->flags & Ast_Flag_Intrinsic) return 0;
-
- // NOTE: Don't output functions that are not used, only if
- // they are also not exported.
- if ((fd->flags & Ast_Flag_Function_Used) == 0) {
- if (fd->flags & Ast_Flag_Exported) {
- return 1;
- } else {
- return 0;
- }
- }
-
- return 1;
-}
-
-
-static void emit_function(OnyxWasmModule* mod, AstFunction* fd) {
- if (!should_emit_function(fd)) return;
-
- i32 type_idx = generate_type_idx(mod, fd->type);
-
- WasmFunc wasm_func = {
- .type_idx = type_idx,
- .locals = {
- .param_count = 0,
-
- .allocated = { 0 },
- .freed = { 0 },
-
- .max_stack = 0,
- .curr_stack = 0,
- },
- .code = NULL,
- };
-
- bh_arr_new(mod->allocator, wasm_func.code, 4);
-
- i32 func_idx = (i32) bh_imap_get(&mod->index_map, (u64) fd);
-
- // If there is no body then don't process the code
- if (fd->body != NULL) {
- // NOTE: Generate the local map
- u64 localidx = 0;
- bh_arr_each(AstParam, param, fd->params) {
- switch (type_get_param_pass(param->local->type)) {
- case Param_Pass_By_Value: {
- if (type_is_structlike_strict(param->local->type)) {
- bh_imap_put(&mod->local_map, (u64) param->local, localidx | LOCAL_IS_WASM);
- localidx += type_structlike_mem_count(param->local->type);
-
- break;
- }
- // fallthrough
- }
-
- case Param_Pass_By_Implicit_Pointer: {
- bh_imap_put(&mod->local_map, (u64) param->local, localidx++ | LOCAL_IS_WASM);
- break;
- }
-
- default: assert(0);
- }
- }
-
- mod->local_alloc = &wasm_func.locals;
- mod->local_alloc->param_count = localidx;
-
- mod->curr_cc = type_function_get_cc(fd->type);
- assert(mod->curr_cc != CC_Undefined);
-
- bh_arr_clear(mod->stack_leave_patches);
-
- bh_arr_insert_end(wasm_func.code, 5);
- fori (i, 0, 5) wasm_func.code[i] = (WasmInstruction) { WI_NOP, 0 };
-
- mod->stack_base_idx = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
-
- // Generate code
- emit_function_body(mod, &wasm_func.code, fd);
-
- if (mod->local_alloc->max_stack > 0 || mod->curr_cc == CC_Return_Stack) {
- emit_stack_enter(mod, &wasm_func.code, mod->local_alloc->max_stack);
-
- // Place all stack leaves in patch locations. These will (probably) all be
- // right before a "return" instruction.
- u64 stack_top_idx = bh_imap_get(&mod->index_map, (u64) &builtin_stack_top);
- bh_arr_push(wasm_func.code, ((WasmInstruction) { WI_LOCAL_GET, { .l = mod->stack_base_idx } }));
- bh_arr_push(wasm_func.code, ((WasmInstruction) { WI_GLOBAL_SET, { .l = stack_top_idx } }));
-
- bh_arr_each(PatchInfo, patch, mod->stack_leave_patches) {
- wasm_func.code[patch->instruction_index + 0] = (WasmInstruction) { WI_LOCAL_GET, { .l = mod->stack_base_idx } };
- wasm_func.code[patch->instruction_index + 1] = (WasmInstruction) { WI_GLOBAL_SET, { .l = stack_top_idx } };
- }
- }
- }
-
- WasmFuncType* ft = mod->types[type_idx];
- emit_zero_value(mod, &wasm_func.code, ft->return_type);
-
- bh_arr_push(wasm_func.code, ((WasmInstruction){ WI_BLOCK_END, 0x00 }));
-
- bh_arr_set_at(mod->funcs, func_idx - mod->foreign_function_count, wasm_func);
-
- // NOTE: Clear the local map on exit of generating this function
- bh_imap_clear(&mod->local_map);
-}
-
-static void emit_foreign_function(OnyxWasmModule* mod, AstFunction* fd) {
- if (!should_emit_function(fd)) return;
-
- i32 type_idx = generate_type_idx(mod, fd->type);
-
- WasmImport import = {
- .kind = WASM_FOREIGN_FUNCTION,
- .idx = type_idx,
- .mod = fd->foreign_module,
- .name = fd->foreign_name,
- };
-
- bh_arr_push(mod->imports, import);
- return;
-}
-
-static void emit_export_directive(OnyxWasmModule* mod, AstDirectiveExport* export) {
- assert(export->export_name);
- assert(export->export);
-
- token_toggle_end(export->export_name);
-
- i64 idx = bh_imap_get(&mod->index_map, (u64) export->export);
-
- WasmExport wasm_export;
- wasm_export.idx = (i32) idx;
-
- switch (export->export->kind) {
- case Ast_Kind_Function: wasm_export.kind = WASM_FOREIGN_FUNCTION;
- break;
-
- case Ast_Kind_Global: wasm_export.kind = WASM_FOREIGN_GLOBAL;
- break;
- }
-
- bh_table_put(WasmExport, mod->exports, export->export_name->text, wasm_export);
- mod->export_count++;
-
- token_toggle_end(export->export_name);
- return;
-}
-
-static void emit_global(OnyxWasmModule* module, AstGlobal* global) {
- WasmType global_type = onyx_type_to_wasm_type(global->type);
-
- WasmGlobal glob = {
- .type = global_type,
- .mutable = (global->flags & Ast_Flag_Const) == 0,
- .initial_value = NULL,
- };
-
- i32 global_idx = (i32) bh_imap_get(&module->index_map, (u64) global);
-
- bh_arr_new(global_heap_allocator, glob.initial_value, 1);
-
- switch (global_type) {
- case WASM_TYPE_INT32: bh_arr_push(glob.initial_value, ((WasmInstruction) { WI_I32_CONST, 0 })); break;
- case WASM_TYPE_INT64: bh_arr_push(glob.initial_value, ((WasmInstruction) { WI_I64_CONST, 0 })); break;
- case WASM_TYPE_FLOAT32: bh_arr_push(glob.initial_value, ((WasmInstruction) { WI_F32_CONST, 0 })); break;
- case WASM_TYPE_FLOAT64: bh_arr_push(glob.initial_value, ((WasmInstruction) { WI_F64_CONST, 0 })); break;
-
- default: assert(("Invalid global type", 0)); break;
- }
-
- bh_arr_set_at(module->globals, global_idx - module->foreign_global_count, glob);
-
- if (global->flags & Ast_Flag_Global_Stack_Top)
- module->stack_top_ptr = &module->globals[global_idx - module->foreign_global_count].initial_value[0].data.i1;
-}
-
-static void emit_foreign_global(OnyxWasmModule* module, AstGlobal* global) {
- WasmType global_type = onyx_type_to_wasm_type(global->type);
-
- if (global->flags & Ast_Flag_Foreign) {
- WasmImport import = {
- .kind = WASM_FOREIGN_GLOBAL,
- .idx = global_type,
- .mod = global->foreign_module,
- .name = global->foreign_name,
- };
-
- bh_arr_push(module->imports, import);
- }
-}
-
-static void emit_string_literal(OnyxWasmModule* mod, AstStrLit* strlit) {
-
- // NOTE: Allocating more than necessary, but there are no cases
- // in a string literal that create more bytes than already
- // existed. You can create less however ('\n' => 0x0a).
- i8* strdata = bh_alloc_array(global_heap_allocator, i8, strlit->token->length + 1);
- i32 length = string_process_escape_seqs(strdata, strlit->token->text, strlit->token->length);
-
- // Warning for having '%' in a string literal (because that probably is being used for a old print format)
- /*
- if (charset_contains((const char *) strdata, '%')) {
- onyx_report_warning(strlit->token->pos, "Found string literal with '%%'");
- }
- */
-
- if (bh_table_has(StrLitInfo, mod->string_literals, (char *) strdata)) {
- StrLitInfo sti = bh_table_get(StrLitInfo, mod->string_literals, (char *) strdata);
- strlit->addr = sti.addr;
- strlit->length = sti.len;
-
- bh_free(global_heap_allocator, strdata);
- return;
- }
-
- WasmDatum datum = {
- .offset = mod->next_datum_offset,
- .length = length,
- .data = strdata,
- };
-
- strlit->addr = (u32) mod->next_datum_offset,
- strlit->length = length;
- mod->next_datum_offset += length;
-
- bh_table_put(StrLitInfo, mod->string_literals, (char *) strdata, ((StrLitInfo) { strlit->addr, strlit->length }));
-
- bh_arr_push(mod->data, datum);
-}
-
-static void emit_raw_data(OnyxWasmModule* mod, ptr data, AstTyped* node) {
- if (!emit_raw_data_(mod, data, node)) {
- onyx_report_error(node->token->pos,
- "Cannot generate constant data for '%s'.",
- onyx_ast_node_kind_string(node->kind));
- }
-}
-
-static b32 emit_raw_data_(OnyxWasmModule* mod, ptr data, AstTyped* node) {
- b32 retval = 1;
-
- switch (node->kind) {
- case Ast_Kind_Array_Literal: {
- AstArrayLiteral* al = (AstArrayLiteral *) node;
-
- i32 i = 0;
- i32 elem_size = type_size_of(al->type->Array.elem);
-
- bh_arr_each(AstTyped *, expr, al->values) {
- retval &= emit_raw_data_(mod, bh_pointer_add(data, i * elem_size), *expr);
- i++;
- }
-
- break;
- }
-
- case Ast_Kind_Struct_Literal: {
- AstStructLiteral* sl = (AstStructLiteral *) node;
-
- Type* sl_type = sl->type;
- // ROBUSTNESS: Handle cases for slices and dynamic arrays
- if (sl_type->kind != Type_Kind_Struct && sl_type->kind != Type_Kind_Slice && sl_type->kind != Type_Kind_DynArray) {
- retval = 0;
- break;
- }
-
- i32 mem_count = type_structlike_mem_count(sl_type);
- StructMember smem;
-
- fori (i, 0, mem_count) {
- type_lookup_member_by_idx(sl_type, i, &smem);
- retval &= emit_raw_data_(mod, bh_pointer_add(data, smem.offset), sl->args.values[i]);
- }
-
- break;
- }
-
- case Ast_Kind_StrLit: {
- AstStrLit* sl = (AstStrLit *) node;
-
- // NOTE: This assumes the address and the length fields have been filled out
- // by emit_string_literal.
- u32* sdata = (u32 *) data;
- sdata[0] = sl->addr;
- sdata[1] = 0x00;
- sdata[2] = sl->length;
- break;
- }
-
- case Ast_Kind_Enum_Value: {
- AstEnumValue* ev = (AstEnumValue *) node;
- retval &= emit_raw_data_(mod, data, (AstTyped *) ev->value);
- break;
- }
-
- case Ast_Kind_Function: {
- AstFunction* func = (AstFunction *) node;
- *((u32 *) data) = get_element_idx(mod, func);
- break;
- }
-
- case Ast_Kind_NumLit: {
- // NOTE: This makes a big assumption that we are running on a
- // little endian machine, since WebAssembly is little endian
- // by specification. This is probably a safe assumption, but
- // should probably be fixed at some point.
- // - brendanfh 2020/12/15
-
- Type* effective_type = node->type;
-
- if (effective_type->kind == Type_Kind_Enum)
- effective_type = effective_type->Enum.backing;
-
- switch (effective_type->Basic.kind) {
- case Basic_Kind_Bool:
- case Basic_Kind_I8:
- case Basic_Kind_U8:
- *((i8 *) data) = (i8) ((AstNumLit *) node)->value.i;
- return retval;
-
- case Basic_Kind_I16:
- case Basic_Kind_U16:
- *((i16 *) data) = (i16) ((AstNumLit *) node)->value.i;
- return retval;
-
- case Basic_Kind_I32:
- case Basic_Kind_U32:
- *((i32 *) data) = ((AstNumLit *) node)->value.i;
- return retval;
-
- case Basic_Kind_I64:
- case Basic_Kind_U64:
- case Basic_Kind_Rawptr:
- *((i64 *) data) = ((AstNumLit *) node)->value.l;
- return retval;
-
- case Basic_Kind_F32:
- *((f32 *) data) = ((AstNumLit *) node)->value.f;
- return retval;
-
- case Basic_Kind_F64:
- *((f64 *) data) = ((AstNumLit *) node)->value.d;
- return retval;
-
- default: break;
- }
-
- //fallthrough
- }
-
- case Ast_Kind_Code_Block: break;
-
- default: retval = 0;
- }
-
- return retval;
-}
-
-static void emit_memory_reservation(OnyxWasmModule* mod, AstMemRes* memres) {
- Type* effective_type = memres->type;
-
- u64 alignment = type_alignment_of(effective_type);
- u64 size = type_size_of(effective_type);
-
- if (type_table_node != NULL && (AstMemRes *) type_table_node == memres) {
- u64 table_location = build_type_table(mod);
- memres->addr = table_location;
-
- return;
- }
-
- u32 offset = mod->next_datum_offset;
- bh_align(offset, alignment);
-
- if (memres->initial_value != NULL) {
- u8* data = bh_alloc(global_heap_allocator, size);
- emit_raw_data(mod, data, memres->initial_value);
-
- WasmDatum datum = {
- .offset = offset,
- .length = size,
- .data = data,
- };
-
- bh_arr_push(mod->data, datum);
- }
-
- memres->addr = offset;
- mod->next_datum_offset = offset + size;
-}
-
-static void emit_file_contents(OnyxWasmModule* mod, AstFileContents* fc) {
- token_toggle_end(fc->filename_token);
-
- // INVESTIGATE: I think that filename should always be NULL when this function starts because
- // a file contents entity is only processed once and therefore should only go through this step
- // once. But somehow filename isn't NULL occasionally so I have to check for that...
- // - brendanfh 2021/05/23
- if (fc->filename == NULL) {
- const char* parent_file = fc->token->pos.filename;
- if (parent_file == NULL) parent_file = ".";
-
- char* parent_folder = bh_path_get_parent(parent_file, global_scratch_allocator);
-
- char* temp_fn = bh_alloc_array(global_scratch_allocator, char, fc->filename_token->length);
- i32 temp_fn_len = string_process_escape_seqs(temp_fn, fc->filename_token->text, fc->filename_token->length);
- char* filename = lookup_included_file(temp_fn, parent_folder, 0, 0);
- fc->filename = bh_strdup(global_heap_allocator, filename);
- }
-
- token_toggle_end(fc->filename_token);
-
- if (bh_table_has(StrLitInfo, mod->loaded_file_info, fc->filename)) {
- StrLitInfo info = bh_table_get(StrLitInfo, mod->loaded_file_info, fc->filename);
- fc->addr = info.addr;
- fc->size = info.len;
- return;
- }
-
- u32 offset = mod->next_datum_offset;
- bh_align(offset, 16);
-
- if (!bh_file_exists(fc->filename)) {
- onyx_report_error(fc->filename_token->pos,
- "Unable to open file for reading, '%s'.",
- fc->filename);
- return;
- }
-
- // :RelativeFiles This should be relative to the current directory by default; However,
- // if the filename is prefixed with a './' or '.\\' then it should be relative to the
- // file in which is was inclded. The loaded file info above should probably use the full
- // file path in order to avoid duplicates.
- bh_file_contents contents = bh_file_read_contents(global_heap_allocator, fc->filename);
- u8* actual_data = bh_alloc(global_heap_allocator, contents.length + 1);
- u32 length = contents.length + 1;
- memcpy(actual_data, contents.data, contents.length);
- actual_data[contents.length] = 0;
- bh_file_contents_free(&contents);
-
- bh_table_put(StrLitInfo, mod->loaded_file_info, fc->filename, ((StrLitInfo) {
- .addr = offset,
- .len = length - 1,
- }));
-
- fc->addr = offset;
- fc->size = length - 1;
-
- WasmDatum datum = {
- .offset = offset,
- .length = length,
- .data = actual_data,
- };
-
- bh_arr_push(mod->data, datum);
-
- mod->next_datum_offset = offset + length;
-}
-
-OnyxWasmModule onyx_wasm_module_create(bh_allocator alloc) {
- OnyxWasmModule module = {
- .allocator = alloc,
-
- .type_map = NULL,
- .next_type_idx = 0,
- .types = NULL,
-
- .funcs = NULL,
- .next_func_idx = 0,
-
- .exports = NULL,
- .export_count = 0,
-
- .imports = NULL,
-
- .globals = NULL,
- .next_global_idx = 0,
-
- .data = NULL,
- .next_datum_offset = 32, // Starting offset so null pointers don't immediately
- // break constant data. - brendanfh 2020/12/16
-
- .elems = NULL,
- .next_elem_idx = 0,
-
- .structured_jump_target = NULL,
- .return_location_stack = NULL,
- .local_allocations = NULL,
- .stack_leave_patches = NULL,
- .deferred_stmts = NULL,
-
- .stack_top_ptr = NULL,
- .stack_base_idx = 0,
-
- .foreign_function_count = 0,
- .foreign_global_count = 0,
-
- .null_proc_func_idx = -1,
- };
-
- bh_arena* eid = bh_alloc(global_heap_allocator, sizeof(bh_arena));
- bh_arena_init(eid, global_heap_allocator, 8196);
- module.extended_instr_data = eid;
- module.extended_instr_alloc = bh_arena_allocator(eid);
-
- bh_arr_new(alloc, module.types, 4);
- bh_arr_new(alloc, module.funcs, 4);
- bh_arr_new(alloc, module.imports, 4);
- bh_arr_new(alloc, module.globals, 4);
- bh_arr_new(alloc, module.data, 4);
- bh_arr_new(alloc, module.elems, 4);
-
- bh_arr_new(global_heap_allocator, module.return_location_stack, 4);
- bh_arr_new(global_heap_allocator, module.structured_jump_target, 16);
- bh_arr_set_length(module.structured_jump_target, 0);
-
- bh_table_init(global_heap_allocator, module.type_map, 61);
- bh_table_init(global_heap_allocator, module.exports, 61);
- bh_table_init(global_heap_allocator, module.loaded_file_info, 7);
- bh_table_init(global_heap_allocator, module.string_literals, 16);
-
- bh_imap_init(&module.index_map, global_heap_allocator, 128);
- bh_imap_init(&module.local_map, global_heap_allocator, 16);
- bh_imap_init(&module.elem_map, global_heap_allocator, 16);
-
- bh_arr_new(global_heap_allocator, module.deferred_stmts, 4);
- bh_arr_new(global_heap_allocator, module.local_allocations, 4);
- bh_arr_new(global_heap_allocator, module.stack_leave_patches, 4);
-
- WasmExport mem_export = {
- .kind = WASM_FOREIGN_MEMORY,
- .idx = 0,
- };
- // :ArbitraryConstant
- // :WasmMemory
- bh_table_put(WasmExport, module.exports, "memory", mem_export);
- module.export_count++;
-
- return module;
-}
-
-void emit_entity(Entity* ent) {
- OnyxWasmModule* module = context.wasm_module;
-
- if (module->stack_top_ptr) {
- *module->stack_top_ptr = module->next_datum_offset;
-
- if (*module->stack_top_ptr % 16 != 0) {
- *module->stack_top_ptr += 16 - (*module->stack_top_ptr % 16);
- }
-
- builtin_heap_start.value.i = *module->stack_top_ptr + (1 << 20);
- if (builtin_heap_start.value.i % 16 != 0) {
- builtin_heap_start.value.i += 16 - (builtin_heap_start.value.i % 16);
- }
- }
-
- switch (ent->type) {
- case Entity_Type_Foreign_Function_Header:
- if (!should_emit_function(ent->function)) break;
-
- module->foreign_function_count++;
- emit_foreign_function(module, ent->function);
- // fallthrough
-
- case Entity_Type_Function_Header:
- if (!should_emit_function(ent->function)) break;
-
- if (context.options->print_function_mappings) {
- bh_printf("%d -> %s:%d:%d\n",
- module->next_func_idx,
- ent->expr->token->pos.filename,
- ent->expr->token->pos.line,
- ent->expr->token->pos.column);
- }
-
- bh_imap_put(&module->index_map, (u64) ent->function, module->next_func_idx++);
-
- if (ent->function->flags & Ast_Flag_Proc_Is_Null) {
- if (module->null_proc_func_idx == -1) module->null_proc_func_idx = get_element_idx(module, ent->function);
- }
- break;
-
- case Entity_Type_Foreign_Global_Header:
- module->foreign_global_count++;
- emit_foreign_global(module, ent->global);
- // fallthrough
-
- case Entity_Type_Global_Header:
- bh_imap_put(&module->index_map, (u64) ent->global, module->next_global_idx++);
- break;
-
- case Entity_Type_String_Literal: {
- emit_string_literal(module, (AstStrLit *) ent->strlit);
- break;
- }
-
- case Entity_Type_File_Contents: {
- emit_file_contents(module, (AstFileContents *) ent->file_contents);
- break;
- }
-
- case Entity_Type_Memory_Reservation: {
- emit_memory_reservation(module, (AstMemRes *) ent->mem_res);
- break;
- }
-
- case Entity_Type_Process_Directive: {
- if (ent->expr->kind == Ast_Kind_Directive_Export) {
- emit_export_directive(module, (AstDirectiveExport *) ent->expr);
- }
- break;
- }
-
- case Entity_Type_Function: emit_function(module, ent->function); break;
- case Entity_Type_Global: emit_global(module, ent->global); break;
-
- // Cleanup: Maybe these should be printed elsewhere?
- // Also, they should be sorted? Or have that ability?
- case Entity_Type_Note: {
- if (!context.options->print_notes) break;
-
- AstNote* note = (AstNote *) ent->expr;
- OnyxFilePos pos = note->token->pos;
-
- bh_printf("Note: %b %s:%d:%d\n", note->token->text, note->token->length, pos.filename, pos.line, pos.column);
-
- break;
- }
-
- default: break;
- }
-
- ent->state = Entity_State_Finalized;
-}
-
-void onyx_wasm_module_free(OnyxWasmModule* module) {
- if (module->extended_instr_data != NULL)
- bh_arena_free(module->extended_instr_data);
-
- bh_arr_free(module->types);
- bh_arr_free(module->funcs);
- bh_imap_free(&module->local_map);
- bh_imap_free(&module->index_map);
- bh_table_free(module->type_map);
- bh_table_free(module->exports);
-}
-
-
-#include "onyxwasm_output.c"
+++ /dev/null
-// This file is directly included in src/onxywasm.c
-// It is here purely to decrease the amount of clutter in the main file.
-
-
-// IMPROVE: This implementation assumes that the source and destination buffers do not overlap.
-// The specification for memory.copy in WASM does work even if the buffers overlap.
-// Also, this implementation copies byte-by-byte, which is terrible. It should copy
-// quad word by quad word, and then the additional bytes if the count was not divisible by 8.
-// :32BitPointers
-EMIT_FUNC_NO_ARGS(intrinsic_memory_copy) {
- bh_arr(WasmInstruction) code = *pcode;
-
- // The stack should look like this:
- // <count>
- // <source>
- // <dest>
-
- u64 count_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
- u64 source_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
- u64 dest_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
-
- WIL(WI_LOCAL_SET, count_local);
- WIL(WI_LOCAL_SET, source_local);
- WIL(WI_LOCAL_SET, dest_local);
-
- // count is greater than 0
- WIL(WI_LOCAL_GET, count_local);
- WID(WI_I32_CONST, 0);
- WI(WI_I32_GT_S);
-
- WID(WI_IF_START, 0x40);
- WID(WI_LOOP_START, 0x40);
-
- WIL(WI_LOCAL_GET, count_local);
- WID(WI_I32_CONST, 1);
- WI(WI_I32_SUB);
- WIL(WI_LOCAL_SET, count_local);
-
- WIL(WI_LOCAL_GET, dest_local);
- WIL(WI_LOCAL_GET, count_local);
- WI(WI_PTR_ADD);
-
- WIL(WI_LOCAL_GET, source_local);
- WIL(WI_LOCAL_GET, count_local);
- WI(WI_PTR_ADD);
-
- WID(WI_I32_LOAD_8_U, ((WasmInstructionData) { 0, 0 }));
- WID(WI_I32_STORE_8, ((WasmInstructionData) { 0, 0 }));
-
- WIL(WI_LOCAL_GET, count_local);
- WID(WI_I32_CONST, 0);
- WI(WI_I32_GT_S);
- WID(WI_COND_JUMP, 0x00);
-
- WI(WI_LOOP_END);
- WI(WI_IF_END);
-
- local_raw_free(mod->local_alloc, WASM_TYPE_INT32);
- local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
- local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
-
- *pcode = code;
-}
-
-EMIT_FUNC_NO_ARGS(intrinsic_memory_fill) {
- bh_arr(WasmInstruction) code = *pcode;
-
- // The stack should look like this:
- // <count>
- // <byte>
- // <dest>
-
- u64 count_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
- u64 byte_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
- u64 dest_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
-
- WIL(WI_LOCAL_SET, count_local);
- WIL(WI_LOCAL_SET, byte_local);
- WIL(WI_LOCAL_SET, dest_local);
-
- // count is greater than 0
- WIL(WI_LOCAL_GET, count_local);
- WID(WI_I32_CONST, 0);
- WI(WI_I32_GT_S);
-
- WID(WI_IF_START, 0x40);
- WID(WI_LOOP_START, 0x40);
-
- WIL(WI_LOCAL_GET, count_local);
- WID(WI_I32_CONST, 1);
- WI(WI_I32_SUB);
- WIL(WI_LOCAL_SET, count_local);
-
- WIL(WI_LOCAL_GET, dest_local);
- WIL(WI_LOCAL_GET, count_local);
- WI(WI_PTR_ADD);
-
- WIL(WI_LOCAL_GET, byte_local);
- WID(WI_I32_STORE_8, ((WasmInstructionData) { 0, 0 }));
-
- WIL(WI_LOCAL_GET, count_local);
- WID(WI_I32_CONST, 0);
- WI(WI_I32_GT_S);
- WID(WI_COND_JUMP, 0x00);
-
- WI(WI_LOOP_END);
- WI(WI_IF_END);
-
- local_raw_free(mod->local_alloc, WASM_TYPE_INT32);
- local_raw_free(mod->local_alloc, WASM_TYPE_INT32);
- local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
-
- *pcode = code;
-}
-
-EMIT_FUNC(initialize_type, Type* type, OnyxToken* where) {
- bh_arr(WasmInstruction) code = *pcode;
-
- switch (type->kind) {
- case Type_Kind_Pointer:
- case Type_Kind_Basic: {
- WasmType basic_type = onyx_type_to_wasm_type(type);
- emit_zero_value(mod, &code, basic_type);
- emit_store_instruction(mod, &code, type, 0);
- break;
- }
-
- case Type_Kind_Struct: {
- u64 value_ptr = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
- WIL(WI_LOCAL_SET, value_ptr);
-
- bh_arr_each(StructMember *, psmem, type->Struct.memarr) {
- StructMember* smem = *psmem;
- if (smem->initial_value == NULL || *smem->initial_value == NULL) continue;
-
- WIL(WI_LOCAL_GET, value_ptr);
- emit_expression(mod, &code, *smem->initial_value);
- emit_store_instruction(mod, &code, smem->type, smem->offset);
- }
-
- local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
- break;
- }
-
- default:
- onyx_report_error(where->pos,
- "Unable to initialize type, '%s'. The reason for this is largely due to the compiler not knowing what the initial value should be.",
- type_get_name(type));
- break;
- }
-
- *pcode = code;
-}
+++ /dev/null
-// This file is included in src/onyxwasm.c.
-// It is separated because of its fundamentally different goals.
-
-//-------------------------------------------------
-// BINARY OUPUT
-//-------------------------------------------------
-
-#define WASM_SECTION_ID_TYPE 1
-#define WASM_SECTION_ID_IMPORT 2
-#define WASM_SECTION_ID_FUNCTION 3
-#define WASM_SECTION_ID_TABLE 4
-#define WASM_SECTION_ID_MEMORY 5
-#define WASM_SECTION_ID_GLOBAL 6
-#define WASM_SECTION_ID_EXPORT 7
-#define WASM_SECTION_ID_START 8
-#define WASM_SECTION_ID_ELEMENT 9
-#define WASM_SECTION_ID_CODE 10
-#define WASM_SECTION_ID_DATA 11
-
-typedef i32 vector_func(void*, bh_buffer*);
-
-static const u8 WASM_MAGIC_STRING[] = { 0x00, 0x61, 0x73, 0x6D };
-static const u8 WASM_VERSION[] = { 0x01, 0x00, 0x00, 0x00 };
-
-static void output_instruction(WasmFunc* func, WasmInstruction* instr, bh_buffer* buff);
-
-static i32 output_vector(void** arr, i32 stride, i32 arrlen, vector_func elem, bh_buffer* vec_buff) {
- i32 len;
- u8* leb = uint_to_uleb128((u64) arrlen, &len);
- bh_buffer_append(vec_buff, leb, len);
-
- i32 i = 0;
- while (i < arrlen) {
- elem(*arr, vec_buff);
- arr = bh_pointer_add(arr, stride);
- i++;
- }
-
- return vec_buff->length;
-}
-
-static i32 output_name(const char* start, i32 length, bh_buffer* buff) {
- i32 leb_len, prev_len = buff->length;
- u8* leb = uint_to_uleb128((u64) length, &leb_len);
- bh_buffer_append(buff, leb, leb_len);
- bh_buffer_append(buff, start, length);
- return buff->length - prev_len;
-}
-
-static i32 output_limits(i32 min, i32 max, bh_buffer* buff) {
- i32 leb_len, prev_len = buff->length;
- u8* leb;
-
- bh_buffer_write_byte(buff, (max >= 0) ? 0x01 : 0x00);
-
- leb = uint_to_uleb128((u64) min, &leb_len);
- bh_buffer_append(buff, leb, leb_len);
-
- if (max >= 0) {
- leb = uint_to_uleb128((u64) max, &leb_len);
- bh_buffer_append(buff, leb, leb_len);
- }
-
- return buff->length - prev_len;
-}
-
-static i32 output_functype(WasmFuncType* type, bh_buffer* buff) {
- i32 prev_len = buff->length;
-
- bh_buffer_write_byte(buff, 0x60);
-
- i32 len;
- u8* leb_buff = uint_to_uleb128(type->param_count, &len);
- bh_buffer_append(buff, leb_buff, len);
- bh_buffer_append(buff, type->param_types, type->param_count);
-
- if (type->return_type != WASM_TYPE_VOID) {
- bh_buffer_write_byte(buff, 0x01);
- bh_buffer_write_byte(buff, type->return_type);
- } else {
- bh_buffer_write_byte(buff, 0x00);
- }
-
- return buff->length - prev_len;
-}
-
-static i32 output_typesection(OnyxWasmModule* module, bh_buffer* buff) {
- i32 prev_len = buff->length;
- bh_buffer_write_byte(buff, 0x01);
-
- bh_buffer vec_buff;
- bh_buffer_init(&vec_buff, buff->allocator, 128);
-
- i32 vec_len = output_vector(
- (void**) module->types,
- sizeof(WasmFuncType*),
- bh_arr_length(module->types),
- (vector_func *) output_functype,
- &vec_buff);
-
- i32 leb_len;
- u8* leb = uint_to_uleb128((u64) vec_len, &leb_len);
- bh_buffer_append(buff, leb, leb_len);
-
- bh_buffer_concat(buff, vec_buff);
- bh_buffer_free(&vec_buff);
-
- return buff->length - prev_len;
-}
-
-static i32 output_funcsection(OnyxWasmModule* module, bh_buffer* buff) {
- i32 prev_len = buff->length;
- bh_buffer_write_byte(buff, WASM_SECTION_ID_FUNCTION);
-
- bh_buffer vec_buff;
- bh_buffer_init(&vec_buff, buff->allocator, 128);
-
- i32 leb_len;
- u8* leb = uint_to_uleb128((u64) (bh_arr_length(module->funcs)), &leb_len);
- bh_buffer_append(&vec_buff, leb, leb_len);
-
- bh_arr_each(WasmFunc, func, module->funcs) {
- leb = uint_to_uleb128((u64) (func->type_idx), &leb_len);
- bh_buffer_append(&vec_buff, leb, leb_len);
- }
-
- leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
- bh_buffer_append(buff, leb, leb_len);
-
- bh_buffer_concat(buff, vec_buff);
- bh_buffer_free(&vec_buff);
-
- return buff->length - prev_len;
-}
-
-static i32 output_tablesection(OnyxWasmModule* module, bh_buffer* buff) {
- if (bh_arr_length(module->elems) == 0) return 0;
-
- i32 prev_len = buff->length;
- bh_buffer_write_byte(buff, WASM_SECTION_ID_TABLE);
-
- bh_buffer vec_buff;
- bh_buffer_init(&vec_buff, buff->allocator, 128);
-
- i32 leb_len;
- u8* leb = uint_to_uleb128((u64) 1, &leb_len);
- bh_buffer_append(&vec_buff, leb, leb_len);
-
- // NOTE: funcrefs are the only valid table element type
- bh_buffer_write_byte(&vec_buff, 0x70);
- output_limits(bh_arr_length(module->elems), -1, &vec_buff);
-
- leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
- bh_buffer_append(buff, leb, leb_len);
-
- bh_buffer_concat(buff, vec_buff);
- bh_buffer_free(&vec_buff);
-
- return buff->length - prev_len;
-}
-
-static i32 output_memorysection(OnyxWasmModule* module, bh_buffer* buff) {
- i32 prev_len = buff->length;
- bh_buffer_write_byte(buff, WASM_SECTION_ID_MEMORY);
-
- bh_buffer vec_buff;
- bh_buffer_init(&vec_buff, buff->allocator, 128);
-
- i32 leb_len;
- u8* leb = uint_to_uleb128((u64) 1, &leb_len);
- bh_buffer_append(&vec_buff, leb, leb_len);
-
- // FIXME: This needs to be dynamically chosen depending on the size of
- // the data section and stack size pre-requeseted.
- // :WasmMemory
- output_limits(1024, -1, &vec_buff);
-
- leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
- bh_buffer_append(buff, leb, leb_len);
-
- bh_buffer_concat(buff, vec_buff);
- bh_buffer_free(&vec_buff);
-
- return buff->length - prev_len;
-}
-
-static i32 output_globalsection(OnyxWasmModule* module, bh_buffer* buff) {
- i32 prev_len = buff->length;
- bh_buffer_write_byte(buff, WASM_SECTION_ID_GLOBAL);
-
- bh_buffer vec_buff;
- bh_buffer_init(&vec_buff, buff->allocator, 128);
-
- i32 leb_len;
- u8* leb = uint_to_uleb128((u64) (bh_arr_length(module->globals)), &leb_len);
- bh_buffer_append(&vec_buff, leb, leb_len);
-
- bh_arr_each(WasmGlobal, global, module->globals) {
- bh_buffer_write_byte(&vec_buff, global->type);
- bh_buffer_write_byte(&vec_buff, 0x01);
-
- bh_arr_each(WasmInstruction, instr, global->initial_value)
- output_instruction(NULL, instr, &vec_buff);
-
- // NOTE: Initial value expression terminator
- bh_buffer_write_byte(&vec_buff, (u8) WI_BLOCK_END);
- }
-
- leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
- bh_buffer_append(buff, leb, leb_len);
-
- bh_buffer_concat(buff, vec_buff);
- bh_buffer_free(&vec_buff);
-
- return buff->length - prev_len;
-}
-
-static i32 output_importsection(OnyxWasmModule* module, bh_buffer* buff) {
- i32 prev_len = buff->length;
- bh_buffer_write_byte(buff, WASM_SECTION_ID_IMPORT);
-
- bh_buffer vec_buff;
- bh_buffer_init(&vec_buff, buff->allocator, 128);
-
- i32 leb_len;
- u8* leb = uint_to_uleb128((u64) (bh_arr_length(module->imports)), &leb_len);
- bh_buffer_append(&vec_buff, leb, leb_len);
-
- bh_arr_each(WasmImport, import, module->imports) {
- output_name(import->mod->text, import->mod->length, &vec_buff);
- output_name(import->name->text, import->name->length, &vec_buff);
- bh_buffer_write_byte(&vec_buff, (u8) import->kind);
-
- leb = uint_to_uleb128((u64) import->idx, &leb_len);
- bh_buffer_append(&vec_buff, leb, leb_len);
-
- if (import->kind == WASM_FOREIGN_GLOBAL) {
- // NOTE: All foreign globals are mutable
- bh_buffer_write_byte(&vec_buff, 0x01);
- }
- }
-
- leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
- bh_buffer_append(buff, leb, leb_len);
-
- bh_buffer_concat(buff, vec_buff);
- bh_buffer_free(&vec_buff);
-
- return buff->length - prev_len;
-}
-
-static i32 output_exportsection(OnyxWasmModule* module, bh_buffer* buff) {
- i32 prev_len = buff->length;
- bh_buffer_write_byte(buff, WASM_SECTION_ID_EXPORT);
-
- bh_buffer vec_buff;
- bh_buffer_init(&vec_buff, buff->allocator, 128);
-
- i32 leb_len;
- u8* leb = uint_to_uleb128((u64) (module->export_count), &leb_len);
- bh_buffer_append(&vec_buff, leb, leb_len);
-
- i32 key_len = 0;
- bh_table_each_start(WasmExport, module->exports);
- key_len = strlen(key);
- output_name(key, key_len, &vec_buff);
-
- bh_buffer_write_byte(&vec_buff, (u8) (value.kind));
- leb = uint_to_uleb128((u64) value.idx, &leb_len);
- bh_buffer_append(&vec_buff, leb, leb_len);
- bh_table_each_end;
-
- leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
- bh_buffer_append(buff, leb, leb_len);
-
- bh_buffer_concat(buff, vec_buff);
- bh_buffer_free(&vec_buff);
-
- return buff->length - prev_len;
-}
-
-static i32 output_startsection(OnyxWasmModule* module, bh_buffer* buff) {
- i32 prev_len = buff->length;
-
- i32 start_idx = -1;
- bh_table_each_start(WasmExport, module->exports) {
- if (value.kind == WASM_FOREIGN_FUNCTION) {
- if (strncmp("main", key, 5) == 0) {
- start_idx = value.idx;
- break;
- }
- }
- } bh_table_each_end;
-
- if (start_idx != -1) {
- bh_buffer_write_byte(buff, WASM_SECTION_ID_START);
-
- i32 start_leb_len, section_leb_len;
- uint_to_uleb128((u64) start_idx, &start_leb_len);
- u8* section_leb = uint_to_uleb128((u64) start_leb_len, §ion_leb_len);
- bh_buffer_append(buff, section_leb, section_leb_len);
-
- u8* start_leb = uint_to_uleb128((u64) start_idx, &start_leb_len);
- bh_buffer_append(buff, start_leb, start_leb_len);
- }
-
- return buff->length - prev_len;
-}
-
-static i32 output_elemsection(OnyxWasmModule* module, bh_buffer* buff) {
- if (bh_arr_length(module->elems) == 0) return 0;
-
- i32 prev_len = buff->length;
-
- bh_buffer_write_byte(buff, WASM_SECTION_ID_ELEMENT);
-
- bh_buffer vec_buff;
- bh_buffer_init(&vec_buff, buff->allocator, 128);
-
- i32 leb_len;
- u8* leb;
-
- // NOTE: 0x01 count of elems
- bh_buffer_write_byte(&vec_buff, 0x01);
-
- // NOTE: 0x00 table index
- bh_buffer_write_byte(&vec_buff, 0x00);
-
- bh_buffer_write_byte(&vec_buff, WI_I32_CONST);
- bh_buffer_write_byte(&vec_buff, 0x00);
- bh_buffer_write_byte(&vec_buff, WI_BLOCK_END);
-
- leb = uint_to_uleb128((u64) bh_arr_length(module->elems), &leb_len);
- bh_buffer_append(&vec_buff, leb, leb_len);
-
- bh_arr_each(i32, elem, module->elems) {
- leb = uint_to_uleb128((u64) *elem, &leb_len);
- bh_buffer_append(&vec_buff, leb, leb_len);
- }
-
- leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
- bh_buffer_append(buff, leb, leb_len);
-
- bh_buffer_concat(buff, vec_buff);
- bh_buffer_free(&vec_buff);
-
- return buff->length - prev_len;
-}
-
-static i32 output_locals(WasmFunc* func, bh_buffer* buff) {
- i32 prev_len = buff->length;
-
- // NOTE: Output vector length
- i32 total_locals =
- (i32) (func->locals.allocated[0] != 0) +
- (i32) (func->locals.allocated[1] != 0) +
- (i32) (func->locals.allocated[2] != 0) +
- (i32) (func->locals.allocated[3] != 0) +
- (i32) (func->locals.allocated[4] != 0);
-
- i32 leb_len;
- u8* leb = uint_to_uleb128((u64) total_locals, &leb_len);
- bh_buffer_append(buff, leb, leb_len);
-
- if (func->locals.allocated[0] != 0) {
- leb = uint_to_uleb128((u64) func->locals.allocated[0], &leb_len);
- bh_buffer_append(buff, leb, leb_len);
- bh_buffer_write_byte(buff, WASM_TYPE_INT32);
- }
- if (func->locals.allocated[1] != 0) {
- leb = uint_to_uleb128((u64) func->locals.allocated[1], &leb_len);
- bh_buffer_append(buff, leb, leb_len);
- bh_buffer_write_byte(buff, WASM_TYPE_INT64);
- }
- if (func->locals.allocated[2] != 0) {
- leb = uint_to_uleb128((u64) func->locals.allocated[2], &leb_len);
- bh_buffer_append(buff, leb, leb_len);
- bh_buffer_write_byte(buff, WASM_TYPE_FLOAT32);
- }
- if (func->locals.allocated[3] != 0) {
- leb = uint_to_uleb128((u64) func->locals.allocated[3], &leb_len);
- bh_buffer_append(buff, leb, leb_len);
- bh_buffer_write_byte(buff, WASM_TYPE_FLOAT64);
- }
- if (func->locals.allocated[4] != 0) {
- leb = uint_to_uleb128((u64) func->locals.allocated[4], &leb_len);
- bh_buffer_append(buff, leb, leb_len);
- bh_buffer_write_byte(buff, WASM_TYPE_VAR128);
- }
-
- return buff->length - prev_len;
-}
-
-static void output_instruction(WasmFunc* func, WasmInstruction* instr, bh_buffer* buff) {
- i32 leb_len;
- u8* leb;
-
- if (instr->type == WI_NOP) return;
-
- if (instr->type & SIMD_INSTR_MASK) {
- bh_buffer_write_byte(buff, 0xFD);
- leb = uint_to_uleb128((u64) (instr->type &~ SIMD_INSTR_MASK), &leb_len);
- bh_buffer_append(buff, leb, leb_len);
-
- } else if (instr->type & EXT_INSTR_MASK) {
- bh_buffer_write_byte(buff, 0xFC);
- leb = uint_to_uleb128((u64) (instr->type &~ EXT_INSTR_MASK), &leb_len);
- bh_buffer_append(buff, leb, leb_len);
-
- } else {
- bh_buffer_write_byte(buff, (u8) instr->type);
- }
-
- switch (instr->type) {
- case WI_LOCAL_GET:
- case WI_LOCAL_SET:
- case WI_LOCAL_TEE: {
- u64 actual_idx = local_lookup_idx(&func->locals, instr->data.l);
- leb = uint_to_uleb128(actual_idx, &leb_len);
- bh_buffer_append(buff, leb, leb_len);
- break;
- }
-
- case WI_GLOBAL_GET:
- case WI_GLOBAL_SET:
- case WI_CALL:
- case WI_BLOCK_START:
- case WI_LOOP_START:
- case WI_JUMP:
- case WI_COND_JUMP:
- case WI_IF_START:
- case WI_MEMORY_SIZE:
- case WI_MEMORY_GROW:
- case WI_MEMORY_FILL:
- leb = uint_to_uleb128((u64) instr->data.i1, &leb_len);
- bh_buffer_append(buff, leb, leb_len);
- break;
-
- case WI_MEMORY_COPY:
- leb = uint_to_uleb128((u64) instr->data.i1, &leb_len);
- bh_buffer_append(buff, leb, leb_len);
-
- leb = uint_to_uleb128((u64) instr->data.i2, &leb_len);
- bh_buffer_append(buff, leb, leb_len);
- break;
-
- case WI_JUMP_TABLE: {
- BranchTable* bt = (BranchTable *) instr->data.p;
-
- leb = uint_to_uleb128((u64) bt->count, &leb_len);
- bh_buffer_append(buff, leb, leb_len);
-
- fori (i, 0, bt->count) {
- leb = uint_to_uleb128((u64) bt->cases[i], &leb_len);
- bh_buffer_append(buff, leb, leb_len);
- }
-
- leb = uint_to_uleb128((u64) bt->default_case, &leb_len);
- bh_buffer_append(buff, leb, leb_len);
- break;
- }
-
-
- case WI_CALL_INDIRECT:
- case WI_I32_STORE: case WI_I32_STORE_8: case WI_I32_STORE_16:
- case WI_I64_STORE: case WI_I64_STORE_8: case WI_I64_STORE_16: case WI_I64_STORE_32:
- case WI_F32_STORE: case WI_F64_STORE:
- case WI_V128_STORE:
- case WI_I32_LOAD:
- case WI_I32_LOAD_8_S: case WI_I32_LOAD_8_U:
- case WI_I32_LOAD_16_S: case WI_I32_LOAD_16_U:
- case WI_I64_LOAD:
- case WI_I64_LOAD_8_S: case WI_I64_LOAD_8_U:
- case WI_I64_LOAD_16_S: case WI_I64_LOAD_16_U:
- case WI_I64_LOAD_32_S: case WI_I64_LOAD_32_U:
- case WI_F32_LOAD: case WI_F64_LOAD:
- case WI_V128_LOAD:
- leb = uint_to_uleb128((u64) instr->data.i1, &leb_len);
- bh_buffer_append(buff, leb, leb_len);
- leb = uint_to_uleb128((u64) instr->data.i2, &leb_len);
- bh_buffer_append(buff, leb, leb_len);
- break;
-
- case WI_I32_CONST:
- leb = int_to_leb128((i64) instr->data.i1, &leb_len);
- bh_buffer_append(buff, leb, leb_len);
- break;
- case WI_I64_CONST:
- leb = int_to_leb128((i64) instr->data.l, &leb_len);
- bh_buffer_append(buff, leb, leb_len);
- break;
- case WI_F32_CONST:
- leb = float_to_ieee754(instr->data.f, 0);
- bh_buffer_append(buff, leb, 4);
- break;
- case WI_F64_CONST:
- leb = double_to_ieee754(instr->data.d, 0);
- bh_buffer_append(buff, leb, 8);
- break;
-
- case WI_V128_CONST:
- case WI_I8X16_SHUFFLE:
- fori (i, 0, 16) bh_buffer_write_byte(buff, ((u8*) instr->data.p)[i]);
- break;
-
- case WI_I8X16_EXTRACT_LANE_S: case WI_I8X16_EXTRACT_LANE_U: case WI_I8X16_REPLACE_LANE:
- case WI_I16X8_EXTRACT_LANE_S: case WI_I16X8_EXTRACT_LANE_U: case WI_I16X8_REPLACE_LANE:
- case WI_I32X4_EXTRACT_LANE: case WI_I32X4_REPLACE_LANE:
- case WI_I64X2_EXTRACT_LANE: case WI_I64X2_REPLACE_LANE:
- case WI_F32X4_EXTRACT_LANE: case WI_F32X4_REPLACE_LANE:
- case WI_F64X2_EXTRACT_LANE: case WI_F64X2_REPLACE_LANE:
- bh_buffer_write_byte(buff, (u8) instr->data.i1);
- break;
-
- default: break;
- }
-}
-
-static i32 output_code(WasmFunc* func, bh_buffer* buff) {
-
- bh_buffer code_buff;
- bh_buffer_init(&code_buff, buff->allocator, 128);
-
- // Output locals
- output_locals(func, &code_buff);
-
- // Output code
- bh_arr_each(WasmInstruction, instr, func->code) output_instruction(func, instr, &code_buff);
-
- i32 leb_len;
- u8* leb = uint_to_uleb128((u64) code_buff.length, &leb_len);
- bh_buffer_append(buff, leb, leb_len);
-
- bh_buffer_concat(buff, code_buff);
- bh_buffer_free(&code_buff);
-
- return 0;
-}
-
-static i32 output_codesection(OnyxWasmModule* module, bh_buffer* buff) {
- i32 prev_len = buff->length;
-
- bh_buffer_write_byte(buff, WASM_SECTION_ID_CODE);
-
- bh_buffer vec_buff;
- bh_buffer_init(&vec_buff, buff->allocator, 128);
-
- i32 leb_len;
- u8* leb = uint_to_uleb128((u64) bh_arr_length(module->funcs), &leb_len);
- bh_buffer_append(&vec_buff, leb, leb_len);
-
- // DEBUG_HERE;
-
- bh_arr_each(WasmFunc, func, module->funcs) output_code(func, &vec_buff);
-
- leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
- bh_buffer_append(buff, leb, leb_len);
-
- bh_buffer_concat(buff, vec_buff);
- bh_buffer_free(&vec_buff);
-
- return buff->length - prev_len;
-}
-
-static i32 output_datasection(OnyxWasmModule* module, bh_buffer* buff) {
- i32 prev_len = buff->length;
-
- bh_buffer_write_byte(buff, WASM_SECTION_ID_DATA);
-
- bh_buffer vec_buff;
- bh_buffer_init(&vec_buff, buff->allocator, 128);
-
- i32 leb_len;
- u8* leb = uint_to_uleb128((u64) bh_arr_length(module->data), &leb_len);
- bh_buffer_append(&vec_buff, leb, leb_len);
-
- bh_arr_each(WasmDatum, datum, module->data) {
- if (datum->data == NULL) continue;
-
- // NOTE: 0x00 memory index
- bh_buffer_write_byte(&vec_buff, 0x00);
-
- bh_buffer_write_byte(&vec_buff, WI_I32_CONST);
- leb = int_to_leb128((i64) datum->offset, &leb_len);
- bh_buffer_append(&vec_buff, leb, leb_len);
- bh_buffer_write_byte(&vec_buff, WI_BLOCK_END);
-
- leb = uint_to_uleb128((u64) datum->length, &leb_len);
- bh_buffer_append(&vec_buff, leb, leb_len);
- fori (i, 0, datum->length) bh_buffer_write_byte(&vec_buff, ((u8 *) datum->data)[i]);
- }
-
- leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
- bh_buffer_append(buff, leb, leb_len);
-
- bh_buffer_concat(buff, vec_buff);
- bh_buffer_free(&vec_buff);
-
- return buff->length - prev_len;
-}
-
-void onyx_wasm_module_write_to_file(OnyxWasmModule* module, bh_file file) {
- bh_buffer master_buffer;
- bh_buffer_init(&master_buffer, global_heap_allocator, 128);
- bh_buffer_append(&master_buffer, WASM_MAGIC_STRING, 4);
- bh_buffer_append(&master_buffer, WASM_VERSION, 4);
-
- output_typesection(module, &master_buffer);
- output_importsection(module, &master_buffer);
- output_funcsection(module, &master_buffer);
- output_tablesection(module, &master_buffer);
- output_memorysection(module, &master_buffer);
- output_globalsection(module, &master_buffer);
- output_exportsection(module, &master_buffer);
- output_startsection(module, &master_buffer);
- output_elemsection(module, &master_buffer);
- output_codesection(module, &master_buffer);
- output_datasection(module, &master_buffer);
-
- bh_file_write(&file, master_buffer.data, master_buffer.length);
-}
+++ /dev/null
-// This file is directly included in src/onxywasm.c
-// It is here purely to decrease the amount of clutter in the main file.
-
-
-u64 build_type_table(OnyxWasmModule* module) {
-
- bh_arr(u32) base_patch_locations=NULL;
- bh_arr_new(global_heap_allocator, base_patch_locations, 256);
-
-#define PATCH (bh_arr_push(base_patch_locations, table_buffer.length))
-
- // This is the data behind the "type_table" slice in type_info.onyx
- u32 type_count = bh_arr_length(type_map.entries) + 1;
- u64* table_info = bh_alloc_array(global_heap_allocator, u64, type_count); // HACK
- memset(table_info, 0, type_count * sizeof(u64));
-
- bh_buffer table_buffer;
- bh_buffer_init(&table_buffer, global_heap_allocator, 4096);
-
- // Write a "NULL" at the beginning so nothing will have to point to the first byte of the buffer.
- bh_buffer_write_u64(&table_buffer, 0);
-
- bh_arr_each(bh__imap_entry, type_entry, type_map.entries) {
- u64 type_idx = type_entry->key;
- Type* type = (Type *) type_entry->value;
-
- switch (type->kind) {
- case Type_Kind_Basic: {
- table_info[type_idx] = table_buffer.length;
- bh_buffer_write_u32(&table_buffer, type->kind);
- bh_buffer_write_u32(&table_buffer, type_size_of(type));
- bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
- bh_buffer_write_u32(&table_buffer, type->Basic.kind);
- break;
- }
-
- case Type_Kind_Pointer: {
- table_info[type_idx] = table_buffer.length;
- bh_buffer_write_u32(&table_buffer, type->kind);
- bh_buffer_write_u32(&table_buffer, type_size_of(type));
- bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
- bh_buffer_write_u32(&table_buffer, type->Pointer.elem->id);
- break;
- }
-
- case Type_Kind_Array: {
- table_info[type_idx] = table_buffer.length;
- bh_buffer_write_u32(&table_buffer, type->kind);
- bh_buffer_write_u32(&table_buffer, type_size_of(type));
- bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
- bh_buffer_write_u32(&table_buffer, type->Array.elem->id);
- bh_buffer_write_u32(&table_buffer, type->Array.count);
- break;
- }
-
- case Type_Kind_Slice: {
- table_info[type_idx] = table_buffer.length;
- bh_buffer_write_u32(&table_buffer, type->kind);
- bh_buffer_write_u32(&table_buffer, type_size_of(type));
- bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
- bh_buffer_write_u32(&table_buffer, type->Slice.ptr_to_data->Pointer.elem->id);
- break;
- }
-
- case Type_Kind_DynArray: {
- table_info[type_idx] = table_buffer.length;
- bh_buffer_write_u32(&table_buffer, type->kind);
- bh_buffer_write_u32(&table_buffer, type_size_of(type));
- bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
- bh_buffer_write_u32(&table_buffer, type->DynArray.ptr_to_data->Pointer.elem->id);
- break;
- }
-
- case Type_Kind_VarArgs: {
- table_info[type_idx] = table_buffer.length;
- bh_buffer_write_u32(&table_buffer, type->kind);
- bh_buffer_write_u32(&table_buffer, type_size_of(type));
- bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
- bh_buffer_write_u32(&table_buffer, type->VarArgs.ptr_to_data->Pointer.elem->id);
- break;
- }
-
- case Type_Kind_Compound: {
- u32 components_base = table_buffer.length;
-
- u32 components_count = type->Compound.count;
- fori (i, 0, components_count) {
- u32 type_idx = type->Compound.types[i]->id;
- bh_buffer_write_u32(&table_buffer, type_idx);
- }
-
- bh_buffer_align(&table_buffer, 8);
- table_info[type_idx] = table_buffer.length;
- bh_buffer_write_u32(&table_buffer, type->kind);
- bh_buffer_write_u32(&table_buffer, type_size_of(type));
- bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
- bh_buffer_align(&table_buffer, 8);
- PATCH;
- bh_buffer_write_u64(&table_buffer, components_base);
- bh_buffer_write_u64(&table_buffer, components_count);
- break;
- }
-
- case Type_Kind_Function: {
- u32 parameters_base = table_buffer.length;
-
- u32 parameters_count = type->Function.param_count;
- fori (i, 0, parameters_count) {
- u32 type_idx = type->Function.params[i]->id;
- bh_buffer_write_u32(&table_buffer, type_idx);
- }
-
- table_info[type_idx] = table_buffer.length;
- bh_buffer_write_u32(&table_buffer, type->kind);
- bh_buffer_write_u32(&table_buffer, type_size_of(type));
- bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
- bh_buffer_write_u32(&table_buffer, type->Function.return_type->id);
-
- PATCH;
- bh_buffer_write_u64(&table_buffer, parameters_base);
- bh_buffer_write_u64(&table_buffer, parameters_count);
-
- bh_buffer_write_u32(&table_buffer, type->Function.vararg_arg_pos > 0 ? 1 : 0);
- break;
- }
-
- case Type_Kind_Enum: {
- AstEnumType* ast_enum = (AstEnumType *) type->ast_type;
- u32 member_count = bh_arr_length(ast_enum->values);
- u32* name_locations = bh_alloc_array(global_scratch_allocator, u32, member_count);
-
- u32 i = 0;
- bh_arr_each(AstEnumValue *, value, ast_enum->values) {
- name_locations[i++] = table_buffer.length;
-
- bh_buffer_append(&table_buffer, (*value)->token->text, (*value)->token->length);
- }
- bh_buffer_align(&table_buffer, 8);
-
- u32 member_base = table_buffer.length;
- i = 0;
- bh_arr_each(AstEnumValue *, value, ast_enum->values) {
- u32 name_loc = name_locations[i++];
-
- bh_buffer_align(&table_buffer, 8);
- PATCH;
- bh_buffer_write_u64(&table_buffer, name_loc);
- bh_buffer_write_u64(&table_buffer, (*value)->token->length);
- bh_buffer_write_u64(&table_buffer, (*value)->value->value.l);
- }
-
- u32 name_base = table_buffer.length;
- u32 name_length = strlen(type->Enum.name);
- bh_buffer_append(&table_buffer, type->Enum.name, name_length);
- bh_buffer_align(&table_buffer, 8);
-
- table_info[type_idx] = table_buffer.length;
- bh_buffer_write_u32(&table_buffer, type->kind);
- bh_buffer_write_u32(&table_buffer, type_size_of(type));
- bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
- bh_buffer_write_u32(&table_buffer, type->Enum.backing->id);
- PATCH;
- bh_buffer_write_u64(&table_buffer, name_base);
- bh_buffer_write_u64(&table_buffer, name_length);
- PATCH;
- bh_buffer_write_u64(&table_buffer, member_base);
- bh_buffer_write_u64(&table_buffer, member_count);
- bh_buffer_write_u32(&table_buffer, type->Enum.is_flags ? 1 : 0);
- break;
- }
-
- case Type_Kind_Struct: {
- TypeStruct* s = &type->Struct;
- u32* name_locations = bh_alloc_array(global_scratch_allocator, u32, s->mem_count);
- u32* param_locations = bh_alloc_array(global_scratch_allocator, u32, bh_arr_length(s->poly_sln));
- u32* value_locations = bh_alloc_array(global_scratch_allocator, u32, s->mem_count);
- memset(value_locations, 0, s->mem_count * sizeof(u32));
-
- u32 i = 0;
- bh_arr_each(StructMember*, pmem, s->memarr) {
- StructMember* mem = *pmem;
-
- name_locations[i++] = table_buffer.length;
- bh_buffer_append(&table_buffer, mem->name, strlen(mem->name));
- }
-
- bh_buffer_align(&table_buffer, 8);
-
- i = 0;
- bh_arr_each(AstPolySolution, sln, s->poly_sln) {
- bh_buffer_align(&table_buffer, 8);
- param_locations[i++] = table_buffer.length;
-
- switch (sln->kind) {
- case PSK_Type: {
- // NOTE: This assumes a little endian compiler (which is assumed in other part of the code too)
- bh_buffer_append(&table_buffer, &sln->type->id, 4);
- break;
- }
-
- case PSK_Value: {
- assert(sln->value->type);
- u32 size = type_size_of(sln->value->type);
-
- bh_buffer_grow(&table_buffer, table_buffer.length + size);
- u8* buffer = table_buffer.data + table_buffer.length;
- emit_raw_data(module, buffer, sln->value);
- table_buffer.length += size;
- break;
- }
-
- default: {
- // Set to null if this is not known how to encode
- param_locations[i-1] = 0;
- break;
- }
- }
- }
-
- bh_buffer_align(&table_buffer, 8);
-
- i = 0;
- bh_arr_each(StructMember*, pmem, s->memarr) {
- StructMember* mem = *pmem;
-
- if (mem->initial_value == NULL || *mem->initial_value == NULL) {
- i++;
- continue;
- }
-
- AstTyped* value = *mem->initial_value;
- assert(value->type);
-
- if ((value->flags & Ast_Flag_Comptime) == 0) {
- // onyx_report_warning(value->token->pos, "Warning: skipping generating default value for '%s' in '%s' because it is not compile-time known.\n", mem->name, s->name);
- i++;
- continue;
- }
-
- u32 size = type_size_of(value->type);
- bh_buffer_align(&table_buffer, type_alignment_of(value->type));
-
- bh_buffer_grow(&table_buffer, table_buffer.length + size);
- u8* buffer = table_buffer.data + table_buffer.length;
-
- if (!emit_raw_data_(module, buffer, value)) {
- // Failed to generate raw data
- // onyx_report_warning(value->token->pos, "Warning: failed to generate default value for '%s' in '%s'.\n", mem->name, s->name);
- value_locations[i++] = 0;
-
- } else {
- // Success
- value_locations[i++] = table_buffer.length;
- table_buffer.length += size;
- }
- }
-
- bh_buffer_align(&table_buffer, 8);
- u32 members_base = table_buffer.length;
-
- i = 0;
- bh_arr_each(StructMember*, pmem, s->memarr) {
- StructMember* mem = *pmem;
-
- u32 name_loc = name_locations[i];
- u32 value_loc = value_locations[i++];
-
- bh_buffer_align(&table_buffer, 8);
- PATCH;
- bh_buffer_write_u64(&table_buffer, name_loc);
- bh_buffer_write_u64(&table_buffer, strlen(mem->name));
- bh_buffer_write_u32(&table_buffer, mem->offset);
- bh_buffer_write_u32(&table_buffer, mem->type->id);
- bh_buffer_write_byte(&table_buffer, mem->used ? 1 : 0);
-
- bh_buffer_align(&table_buffer, 8);
- PATCH;
- bh_buffer_write_u64(&table_buffer, value_loc);
- }
-
- bh_buffer_align(&table_buffer, 8);
- u32 params_base = table_buffer.length;
-
- i = 0;
- bh_arr_each(AstPolySolution, sln, s->poly_sln) {
- bh_buffer_align(&table_buffer, 8);
- PATCH;
- bh_buffer_write_u64(&table_buffer, param_locations[i++]);
-
- if (sln->kind == PSK_Type) bh_buffer_write_u32(&table_buffer, basic_types[Basic_Kind_Type_Index].id);
- else bh_buffer_write_u32(&table_buffer, sln->value->type->id);
- }
-
- u32 name_base = 0;
- u32 name_length = 0;
- if (s->name) {
- name_length = strlen(s->name);
- name_base = table_buffer.length;
- bh_buffer_append(&table_buffer, s->name, name_length);
- }
-
- bh_buffer_align(&table_buffer, 8);
- table_info[type_idx] = table_buffer.length;
- bh_buffer_write_u32(&table_buffer, type->kind);
- bh_buffer_write_u32(&table_buffer, type_size_of(type));
- bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
- bh_buffer_write_u32(&table_buffer, 0);
- PATCH;
- bh_buffer_write_u64(&table_buffer, name_base);
- bh_buffer_write_u64(&table_buffer, name_length);
- PATCH;
- bh_buffer_write_u64(&table_buffer, members_base);
- bh_buffer_write_u64(&table_buffer, s->mem_count);
- PATCH;
- bh_buffer_write_u64(&table_buffer, params_base);
- bh_buffer_write_u64(&table_buffer, bh_arr_length(s->poly_sln));
-
- break;
- }
- }
- }
-
- u32 offset = module->next_datum_offset;
- bh_align(offset, 8);
-
- u64 type_table_location = offset;
-
- WasmDatum type_table_data = {
- .offset = offset,
- .length = type_count * 8,
- .data = table_info,
- };
- bh_arr_push(module->data, type_table_data);
-
- offset += type_table_data.length;
-
- fori (i, 0, type_count) {
- table_info[i] += offset;
- }
-
- bh_arr_each(u32, patch_loc, base_patch_locations) {
- u64* loc = bh_pointer_add(table_buffer.data, *patch_loc);
- if (*loc == 0) continue;
-
- *loc += offset;
- }
-
- WasmDatum type_info_data = {
- .offset = offset,
- .length = table_buffer.length,
- .data = table_buffer.data,
- };
- bh_arr_push(module->data, type_info_data);
- offset += type_info_data.length;
-
- u64 global_data_ptr = offset;
-
- u64* tmp_data = bh_alloc(global_heap_allocator, 16);
- tmp_data[0] = type_table_location;
- tmp_data[1] = type_count;
- WasmDatum type_table_global_data = {
- .offset = offset,
- .length = 16,
- .data = tmp_data,
- };
- bh_arr_push(module->data, type_table_global_data);
- offset += type_table_global_data.length;
-
- module->next_datum_offset = offset;
-
- return global_data_ptr;
-
-#undef PATCH
-}
--- /dev/null
+// The sole job of the parser for Onyx is to submit nodes to the
+// entity heap for further processing. These nodes include things
+// such as procedure definitions, string literals, struct definitions
+// and declarations to be introduced into scopes.
+
+#include "lex.h"
+#include "errors.h"
+#include "parser.h"
+#include "utils.h"
+
+#define make_node(nclass, kind) onyx_ast_node_new(parser->allocator, sizeof(nclass), kind)
+// :LinearTokenDependent
+#define peek_token(ahead) (parser->curr + ahead)
+
+static AstNode error_node = { Ast_Kind_Error, 0, NULL, NULL };
+
+#define ENTITY_SUBMIT(node) (submit_entity_in_scope(parser, (AstNode *) (node), parser->current_scope, parser->package))
+#define ENTITY_SUBMIT_IN_SCOPE(node, scope) (submit_entity_in_scope(parser, (AstNode *) (node), scope, parser->package))
+
+void submit_entity_in_scope(OnyxParser* parser, AstNode* node, Scope* scope, Package* package) {
+ if (bh_arr_length(parser->alternate_entity_placement_stack) == 0) {
+ add_entities_for_node(NULL, node, scope, package);
+
+ } else {
+ bh_arr(Entity *) *entity_array = bh_arr_last(parser->alternate_entity_placement_stack);
+ add_entities_for_node(entity_array, node, scope, package);
+ }
+}
+
+// Parsing Utilities
+static void consume_token(OnyxParser* parser);
+static OnyxToken* expect_token(OnyxParser* parser, TokenType token_type);
+static b32 consume_token_if_next(OnyxParser* parser, TokenType token_type);
+static b32 next_tokens_are(OnyxParser* parser, i32 n, ...);
+static OnyxToken* find_matching_paren(OnyxToken* paren);
+
+static AstNumLit* parse_int_literal(OnyxParser* parser);
+static AstNumLit* parse_float_literal(OnyxParser* parser);
+static b32 parse_possible_struct_literal(OnyxParser* parser, AstTyped* left, AstTyped** ret);
+static b32 parse_possible_array_literal(OnyxParser* parser, AstTyped* left, AstTyped** ret);
+static b32 parse_possible_unary_field_access(OnyxParser* parser, AstTyped** ret);
+static void parse_arguments(OnyxParser* parser, TokenType end_token, Arguments* args);
+static AstTyped* parse_factor(OnyxParser* parser);
+static AstTyped* parse_compound_assignment(OnyxParser* parser, AstTyped* lhs);
+static AstTyped* parse_compound_expression(OnyxParser* parser, b32 assignment_allowed);
+static AstTyped* parse_expression(OnyxParser* parser, b32 assignment_allowed);
+static AstIfWhile* parse_if_stmt(OnyxParser* parser);
+static AstIfWhile* parse_while_stmt(OnyxParser* parser);
+static AstFor* parse_for_stmt(OnyxParser* parser);
+static AstSwitch* parse_switch_stmt(OnyxParser* parser);
+static i32 parse_possible_symbol_declaration(OnyxParser* parser, AstNode** ret);
+static AstReturn* parse_return_stmt(OnyxParser* parser);
+static AstNode* parse_use_stmt(OnyxParser* parser);
+static AstBlock* parse_block(OnyxParser* parser, b32 make_a_new_scope);
+static AstNode* parse_statement(OnyxParser* parser);
+static AstType* parse_type(OnyxParser* parser);
+static AstTypeOf* parse_typeof(OnyxParser* parser);
+static AstStructType* parse_struct(OnyxParser* parser);
+static void parse_function_params(OnyxParser* parser, AstFunction* func);
+static b32 parse_possible_directive(OnyxParser* parser, const char* dir);
+static b32 parse_possible_function_definition(OnyxParser* parser, AstTyped** ret);
+static b32 parse_possible_quick_function_definition(OnyxParser* parser, AstTyped** ret);
+static AstFunction* parse_function_definition(OnyxParser* parser, OnyxToken* token);
+static AstTyped* parse_global_declaration(OnyxParser* parser);
+static AstEnumType* parse_enum_declaration(OnyxParser* parser);
+static AstMacro* parse_macro(OnyxParser* parser);
+static AstIf* parse_static_if_stmt(OnyxParser* parser, b32 parse_block_as_statements);
+static AstTyped* parse_top_level_expression(OnyxParser* parser);
+static AstBinding* parse_top_level_binding(OnyxParser* parser, OnyxToken* symbol);
+static void parse_top_level_statement(OnyxParser* parser);
+static AstPackage* parse_package_expression(OnyxParser* parser);
+static void parse_top_level_statements_until(OnyxParser* parser, TokenType tt);
+
+static void consume_token(OnyxParser* parser) {
+ if (parser->hit_unexpected_token) return;
+
+ parser->prev = parser->curr;
+ // :LinearTokenDependent
+ parser->curr++;
+ while (parser->curr->type == Token_Type_Comment || parser->curr->type == Token_Type_Note) {
+ if (parser->curr->type == Token_Type_Note) {
+ AstNote* note = make_node(AstNode, Ast_Kind_Note);
+ note->token = parser->curr;
+ ENTITY_SUBMIT(note);
+ }
+
+ parser->curr++;
+ }
+}
+
+static OnyxToken* find_matching_paren(OnyxToken* paren) {
+ TokenType match = 0;
+ switch ((u16) paren->type) {
+ case '(': match = ')'; break;
+ case '[': match = ']'; break;
+ case '{': match = '}'; break;
+ case '<': match = '>'; break;
+ default: return NULL;
+ }
+
+ i32 paren_count = 1;
+ i32 i = 1;
+ while (paren_count > 0) {
+ // :LinearTokenDependent
+ TokenType type = (paren + i)->type;
+ if (type == Token_Type_End_Stream) return NULL;
+
+ if (type == paren->type) paren_count++;
+ else if (type == match) paren_count--;
+
+ i++;
+ }
+
+ // :LinearTokenDependent
+ return paren + (i - 1);
+}
+
+// NOTE: This advances to next token no matter what
+static OnyxToken* expect_token(OnyxParser* parser, TokenType token_type) {
+ if (parser->hit_unexpected_token) return NULL;
+
+ OnyxToken* token = parser->curr;
+ consume_token(parser);
+
+ if (token->type != token_type) {
+ onyx_report_error(token->pos, "expected token '%s', got '%s'.", token_name(token_type), token_name(token->type));
+ parser->hit_unexpected_token = 1;
+ // :LinearTokenDependent
+ parser->curr = &parser->tokenizer->tokens[bh_arr_length(parser->tokenizer->tokens) - 1];
+ return NULL;
+ }
+
+ return token;
+}
+
+static b32 consume_token_if_next(OnyxParser* parser, TokenType token_type) {
+ if (parser->hit_unexpected_token) return 0;
+
+ if (parser->curr->type == token_type) {
+ consume_token(parser);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static void consume_tokens(OnyxParser* parser, i32 n) {
+ fori (i, 0, n) consume_token(parser);
+}
+
+static b32 next_tokens_are(OnyxParser* parser, i32 n, ...) {
+ va_list va;
+ va_start(va, n);
+
+ i32 matched = 1;
+
+ // BUG: This does not take into consideration comments and notes that can occur between any tokens.
+ fori (i, 0, n) {
+ TokenType expected_type = va_arg(va, TokenType);
+ if (peek_token(i)->type != expected_type) {
+ matched = 0;
+ break;
+ }
+ }
+
+ va_end(va);
+ return matched;
+}
+
+
+// TODO: Make parsing numeric literals not rely on the C standard libary.
+static AstNumLit* parse_int_literal(OnyxParser* parser) {
+ AstNumLit* int_node = make_node(AstNumLit, Ast_Kind_NumLit);
+ int_node->token = expect_token(parser, Token_Type_Literal_Integer);
+ int_node->flags |= Ast_Flag_Comptime;
+ int_node->value.l = 0ll;
+
+ token_toggle_end(int_node->token);
+
+ char* first_invalid = NULL;
+ i64 value = strtoll(int_node->token->text, &first_invalid, 0);
+
+ int_node->value.l = value;
+
+ // NOTE: Hex literals are unsigned.
+ if (int_node->token->length >= 2 && int_node->token->text[1] == 'x') {
+ if ((u64) value >= ((u64) 1 << 32))
+ int_node->type_node = (AstType *) &basic_type_u64;
+ else
+ int_node->type_node = (AstType *) &basic_type_u32;
+ } else {
+ int_node->type_node = (AstType *) &basic_type_int_unsized;
+ }
+
+ token_toggle_end(int_node->token);
+ return int_node;
+}
+
+static AstNumLit* parse_float_literal(OnyxParser* parser) {
+ AstNumLit* float_node = make_node(AstNumLit, Ast_Kind_NumLit);
+ float_node->token = expect_token(parser, Token_Type_Literal_Float);
+ float_node->flags |= Ast_Flag_Comptime;
+ float_node->value.d = 0.0;
+
+ AstType* type = (AstType *) &basic_type_float_unsized;
+ token_toggle_end(float_node->token);
+
+ if (float_node->token->text[float_node->token->length - 1] == 'f') {
+ type = (AstType *) &basic_type_f32;
+ float_node->value.f = strtof(float_node->token->text, NULL);
+ } else {
+ float_node->value.d = strtod(float_node->token->text, NULL);
+ }
+
+ float_node->type_node = type;
+
+ token_toggle_end(float_node->token);
+ return float_node;
+}
+
+static b32 parse_possible_directive(OnyxParser* parser, const char* dir) {
+ if (!next_tokens_are(parser, 2, '#', Token_Type_Symbol)) return 0;
+
+ OnyxToken* sym = peek_token(1);
+
+ b32 match = (strlen(dir) == (u64) sym->length) && (strncmp(dir, sym->text, sym->length) == 0);
+ if (match) consume_tokens(parser, 2);
+
+ return match;
+}
+
+static b32 parse_possible_struct_literal(OnyxParser* parser, AstTyped* left, AstTyped** ret) {
+ if (!next_tokens_are(parser, 2, '.', '{')) return 0;
+
+ AstStructLiteral* sl = make_node(AstStructLiteral, Ast_Kind_Struct_Literal);
+ sl->token = parser->curr;
+ sl->stnode = left;
+
+ arguments_initialize(&sl->args);
+
+ expect_token(parser, '.');
+ expect_token(parser, '{');
+
+ parse_arguments(parser, '}', &sl->args);
+
+ *ret = (AstTyped *) sl;
+ return 1;
+}
+
+static b32 parse_possible_array_literal(OnyxParser* parser, AstTyped* left, AstTyped** ret) {
+ if (!next_tokens_are(parser, 2, '.', '[')) return 0;
+
+ AstArrayLiteral* al = make_node(AstArrayLiteral, Ast_Kind_Array_Literal);
+ al->token = parser->curr;
+ al->atnode = left;
+
+ bh_arr_new(global_heap_allocator, al->values, 4);
+ fori (i, 0, 4) al->values[i] = NULL;
+
+ expect_token(parser, '.');
+ expect_token(parser, '[');
+ while (!consume_token_if_next(parser, ']')) {
+ if (parser->hit_unexpected_token) return 1;
+
+ AstTyped* value = parse_expression(parser, 0);
+ bh_arr_push(al->values, value);
+
+ if (parser->curr->type != ']')
+ expect_token(parser, ',');
+ }
+
+ *ret = (AstTyped *) al;
+ return 1;
+}
+
+static b32 parse_possible_unary_field_access(OnyxParser* parser, AstTyped** ret) {
+ if (!next_tokens_are(parser, 2, '.', Token_Type_Symbol)) return 0;
+
+ AstUnaryFieldAccess* ufl = make_node(AstUnaryFieldAccess, Ast_Kind_Unary_Field_Access);
+ expect_token(parser, '.');
+ ufl->token = expect_token(parser, Token_Type_Symbol);
+
+ *ret = (AstTyped *) ufl;
+ return 1;
+}
+
+static void parse_arguments(OnyxParser* parser, TokenType end_token, Arguments* args) {
+ while (!consume_token_if_next(parser, end_token)) {
+ if (parser->hit_unexpected_token) return;
+
+ if (next_tokens_are(parser, 2, Token_Type_Symbol, '=')) {
+ OnyxToken* name = expect_token(parser, Token_Type_Symbol);
+ expect_token(parser, '=');
+
+ AstNamedValue* named_value = make_node(AstNamedValue, Ast_Kind_Named_Value);
+ named_value->token = name;
+ named_value->value = parse_expression(parser, 0);
+
+ bh_arr_push(args->named_values, named_value);
+
+ } else {
+ AstTyped* value = parse_expression(parser, 0);
+ bh_arr_push(args->values, value);
+ }
+
+ if (parser->curr->type != end_token)
+ expect_token(parser, ',');
+ }
+}
+
+static AstTyped* parse_factor(OnyxParser* parser) {
+ AstTyped* retval = NULL;
+
+ switch ((u16) parser->curr->type) {
+ case '(': {
+ if (parse_possible_function_definition(parser, &retval)) break;
+ if (parse_possible_quick_function_definition(parser, &retval)) break;
+
+ consume_token(parser);
+ retval = parse_compound_expression(parser, 0);
+ expect_token(parser, ')');
+ break;
+ }
+
+ case '-': {
+ AstUnaryOp* negate_node = make_node(AstUnaryOp, Ast_Kind_Unary_Op);
+ negate_node->operation = Unary_Op_Negate;
+ negate_node->token = expect_token(parser, '-');
+ negate_node->expr = parse_factor(parser);
+
+ retval = (AstTyped *) negate_node;
+ break;
+ }
+
+ case '!': {
+ AstUnaryOp* not_node = make_node(AstUnaryOp, Ast_Kind_Unary_Op);
+ not_node->operation = Unary_Op_Not;
+ not_node->token = expect_token(parser, '!');
+ not_node->expr = parse_factor(parser);
+
+ retval = (AstTyped *) not_node;
+ break;
+ }
+
+ case '~': {
+ AstUnaryOp* not_node = make_node(AstUnaryOp, Ast_Kind_Unary_Op);
+ not_node->operation = Unary_Op_Bitwise_Not;
+ not_node->token = expect_token(parser, '~');
+ not_node->expr = parse_factor(parser);
+
+ retval = (AstTyped *) not_node;
+ break;
+ }
+
+ case '*': {
+ AstDereference* deref_node = make_node(AstDereference, Ast_Kind_Dereference);
+ deref_node->token = expect_token(parser, '*');
+ deref_node->expr = parse_factor(parser);
+
+ retval = (AstTyped *) deref_node;
+ break;
+ }
+
+ case '^': {
+ AstAddressOf* aof_node = make_node(AstAddressOf, Ast_Kind_Address_Of);
+ aof_node->token = expect_token(parser, '^');
+ aof_node->expr = parse_factor(parser);
+
+ retval = (AstTyped *) aof_node;
+ break;
+ }
+
+ case '.': {
+ if (parse_possible_struct_literal(parser, NULL, &retval)) return retval;
+ if (parse_possible_array_literal(parser, NULL, &retval)) return retval;
+ if (parse_possible_unary_field_access(parser, &retval)) return retval;
+ goto no_match;
+ }
+
+ case Token_Type_Tilde_Tilde: {
+ AstUnaryOp* ac_node = make_node(AstUnaryOp, Ast_Kind_Unary_Op);
+ ac_node->operation = Unary_Op_Auto_Cast;
+ ac_node->token = expect_token(parser, Token_Type_Tilde_Tilde);
+ ac_node->expr = parse_factor(parser);
+
+ retval = (AstTyped *) ac_node;
+ break;
+ }
+
+ case Token_Type_Keyword_Cast: {
+ AstUnaryOp* cast_node = make_node(AstUnaryOp, Ast_Kind_Unary_Op);
+ cast_node->operation = Unary_Op_Cast;
+ cast_node->token = expect_token(parser, Token_Type_Keyword_Cast);
+
+ expect_token(parser, '(');
+ cast_node->type_node = parse_type(parser);
+ expect_token(parser, ')');
+
+ cast_node->expr = parse_factor(parser);
+
+ retval = (AstTyped *) cast_node;
+ break;
+ }
+
+ case Token_Type_Keyword_Sizeof: {
+ AstSizeOf* so_node = make_node(AstSizeOf, Ast_Kind_Size_Of);
+ so_node->token = expect_token(parser, Token_Type_Keyword_Sizeof);
+ so_node->so_ast_type = (AstType *) parse_type(parser);
+ so_node->type_node = (AstType *) &basic_type_i32;
+
+ retval = (AstTyped *) so_node;
+ break;
+ }
+
+ case Token_Type_Keyword_Alignof: {
+ AstAlignOf* ao_node = make_node(AstAlignOf, Ast_Kind_Align_Of);
+ ao_node->token = expect_token(parser, Token_Type_Keyword_Alignof);
+ ao_node->ao_ast_type = (AstType *) parse_type(parser);
+ ao_node->type_node = (AstType *) &basic_type_i32;
+
+ retval = (AstTyped *) ao_node;
+ break;
+ }
+
+ case Token_Type_Keyword_Typeof: {
+ retval = (AstTyped *) parse_typeof(parser);
+ break;
+ }
+
+ case Token_Type_Symbol: {
+ OnyxToken* sym_token = expect_token(parser, Token_Type_Symbol);
+ AstTyped* sym_node = make_node(AstTyped, Ast_Kind_Symbol);
+ sym_node->token = sym_token;
+
+ retval = sym_node;
+ break;
+ }
+
+ case Token_Type_Literal_Integer:
+ retval = (AstTyped *) parse_int_literal(parser);
+ break;
+
+ case Token_Type_Literal_Float:
+ retval = (AstTyped *) parse_float_literal(parser);
+ break;
+
+ case Token_Type_Literal_String: {
+ AstStrLit* str_node = make_node(AstStrLit, Ast_Kind_StrLit);
+ str_node->token = expect_token(parser, Token_Type_Literal_String);
+ str_node->addr = 0;
+ str_node->flags |= Ast_Flag_Comptime;
+
+ ENTITY_SUBMIT(str_node);
+
+ retval = (AstTyped *) str_node;
+ break;
+ }
+
+ case Token_Type_Literal_True: {
+ AstNumLit* bool_node = make_node(AstNumLit, Ast_Kind_NumLit);
+ bool_node->type_node = (AstType *) &basic_type_bool;
+ bool_node->token = expect_token(parser, Token_Type_Literal_True);
+ bool_node->value.i = 1;
+ bool_node->flags |= Ast_Flag_Comptime;
+ retval = (AstTyped *) bool_node;
+ break;
+ }
+
+ case Token_Type_Literal_False: {
+ AstNumLit* bool_node = make_node(AstNumLit, Ast_Kind_NumLit);
+ bool_node->type_node = (AstType *) &basic_type_bool;
+ bool_node->token = expect_token(parser, Token_Type_Literal_False);
+ bool_node->value.i = 0;
+ bool_node->flags |= Ast_Flag_Comptime;
+ retval = (AstTyped *) bool_node;
+ break;
+ }
+
+ case Token_Type_Keyword_Package: {
+ retval = (AstTyped *) parse_package_expression(parser);
+ break;
+ }
+
+ case Token_Type_Keyword_Macro: {
+ retval = (AstTyped *) parse_macro(parser);
+ break;
+ }
+
+ case Token_Type_Keyword_Do: {
+ OnyxToken* do_token = expect_token(parser, Token_Type_Keyword_Do);
+ AstDoBlock* do_block = make_node(AstDoBlock, Ast_Kind_Do_Block);
+ do_block->token = do_token;
+ do_block->type_node = (AstType *) &basic_type_auto_return;
+
+ if (parser->curr->type != '{') {
+ onyx_report_error(parser->curr->pos, "Expected '{' after 'do', got '%s'.", token_name(parser->curr->type));
+ retval = NULL;
+ break;
+ }
+
+ do_block->block = parse_block(parser, 1);
+
+ retval = (AstTyped *) do_block;
+ break;
+ }
+
+ // :TypeValueInterchange
+ case '<': {
+ AstTypeAlias* alias = make_node(AstTypeAlias, Ast_Kind_Type_Alias);
+ alias->token = expect_token(parser, '<');
+ alias->to = parse_type(parser);
+ expect_token(parser, '>');
+
+ retval = (AstTyped *) alias;
+ break;
+ }
+
+ case '#': {
+ if (parse_possible_directive(parser, "file_contents")) {
+ AstFileContents* fc = make_node(AstFileContents, Ast_Kind_File_Contents);
+ fc->token = parser->prev - 1;
+ fc->filename_token = expect_token(parser, Token_Type_Literal_String);
+ fc->type = type_make_slice(parser->allocator, &basic_types[Basic_Kind_U8]);
+
+ ENTITY_SUBMIT(fc);
+
+ retval = (AstTyped *) fc;
+ break;
+ }
+ else if (parse_possible_directive(parser, "file")) {
+ OnyxToken* dir_token = parser->curr - 2;
+
+ OnyxToken* str_token = bh_alloc(parser->allocator, sizeof(OnyxToken));
+ str_token->text = bh_strdup(global_heap_allocator, (char *) dir_token->pos.filename);
+ str_token->length = strlen(dir_token->pos.filename);
+ str_token->pos = dir_token->pos;
+ str_token->type = Token_Type_Literal_String;
+
+ AstStrLit* filename = make_node(AstStrLit, Ast_Kind_StrLit);
+ filename->token = str_token;
+ filename->addr = 0;
+
+ ENTITY_SUBMIT(filename);
+ retval = (AstTyped *) filename;
+ break;
+ }
+ else if (parse_possible_directive(parser, "line")) {
+ OnyxToken* dir_token = parser->curr - 2;
+
+ AstNumLit* line_num = make_int_literal(parser->allocator, dir_token->pos.line);
+ retval = (AstTyped *) line_num;
+ break;
+ }
+ else if (parse_possible_directive(parser, "column")) {
+ OnyxToken* dir_token = parser->curr - 2;
+
+ AstNumLit* col_num = make_int_literal(parser->allocator, dir_token->pos.column);
+ retval = (AstTyped *) col_num;
+ break;
+ }
+ else if (parse_possible_directive(parser, "char")) {
+ AstNumLit* char_lit = make_node(AstNumLit, Ast_Kind_NumLit);
+ char_lit->flags |= Ast_Flag_Comptime;
+ char_lit->type_node = (AstType *) &basic_type_int_unsized;
+ char_lit->token = expect_token(parser, Token_Type_Literal_String);
+
+ i8 dest = '\0';
+ i32 length = string_process_escape_seqs((char *) &dest, char_lit->token->text, 1);
+ char_lit->value.i = (u32) dest;
+
+ if (length != 1) {
+ onyx_report_error(char_lit->token->pos, "Expected only a single character in character literal.");
+ }
+
+ retval = (AstTyped *) char_lit;
+ break;
+ }
+ else if (parse_possible_directive(parser, "type")) {
+ AstTypeAlias* alias = make_node(AstTypeAlias, Ast_Kind_Type_Alias);
+ alias->token = parser->curr - 2;
+ alias->to = parse_type(parser);
+ retval = (AstTyped *) alias;
+ break;
+ }
+ else if (parse_possible_directive(parser, "solidify")) {
+ AstDirectiveSolidify* solid = make_node(AstDirectiveSolidify, Ast_Kind_Directive_Solidify);
+ // :LinearTokenDependent
+ solid->token = parser->curr - 1;
+
+ solid->poly_proc = (AstPolyProc *) parse_factor(parser);
+
+ solid->known_polyvars = NULL;
+ bh_arr_new(global_heap_allocator, solid->known_polyvars, 2);
+
+ expect_token(parser, '{');
+ while (!consume_token_if_next(parser, '}')) {
+ if (parser->hit_unexpected_token) break;
+
+ AstNode* poly_var = make_node(AstNode, Ast_Kind_Symbol);
+ poly_var->token = expect_token(parser, Token_Type_Symbol);
+
+ expect_token(parser, '=');
+ AstType* poly_type = parse_type(parser);
+
+ bh_arr_push(solid->known_polyvars, ((AstPolySolution) {
+ .kind = PSK_Undefined,
+ .poly_sym = poly_var,
+ .ast_type = poly_type,
+ .type = NULL
+ }));
+
+ if (parser->curr->type != '}')
+ expect_token(parser, ',');
+ }
+
+ retval = (AstTyped *) solid;
+ break;
+ }
+ else if (parse_possible_directive(parser, "defined")) {
+ AstDirectiveDefined* defined = make_node(AstDirectiveDefined, Ast_Kind_Directive_Defined);
+ // :LinearTokenDependent
+ defined->token = parser->curr - 1;
+ defined->type_node = (AstType *) &basic_type_bool;
+
+ expect_token(parser, '(');
+ defined->expr = parse_expression(parser, 0);
+ expect_token(parser, ')');
+
+ retval = (AstTyped *) defined;
+ break;
+ }
+ else if (parse_possible_directive(parser, "code")) {
+ OnyxToken* code_token = parser->curr - 1;
+
+ AstCodeBlock* code_block = make_node(AstCodeBlock, Ast_Kind_Code_Block);
+ code_block->token = code_token;
+
+ assert(builtin_code_type != NULL);
+ code_block->type_node = builtin_code_type;
+
+ if (parser->curr->type == '{') {
+ code_block->code = (AstNode *) parse_block(parser, 1);
+ ((AstBlock *) code_block->code)->rules = Block_Rule_Code_Block;
+
+ } else {
+ code_block->code = (AstNode *) parse_expression(parser, 0);
+ }
+
+ retval = (AstTyped *) code_block;
+ break;
+ }
+ else if (parse_possible_directive(parser, "insert")) {
+ AstDirectiveInsert* insert = make_node(AstDirectiveInsert, Ast_Kind_Directive_Insert);
+ insert->token = parser->curr - 1;
+ insert->code_expr = parse_expression(parser, 0);
+
+ retval = (AstTyped *) insert;
+ break;
+ }
+
+ onyx_report_error(parser->curr->pos, "Invalid directive in expression.");
+ return NULL;
+ }
+
+ default:
+ no_match:
+ onyx_report_error(parser->curr->pos, "Unexpected token '%s'.", token_name(parser->curr->type));
+ return NULL;
+ }
+
+ while (1) {
+ if (parser->hit_unexpected_token) return retval;
+
+ switch ((u16) parser->curr->type) {
+ case '[': {
+ OnyxToken *open_bracket = expect_token(parser, '[');
+ AstTyped *expr = parse_compound_expression(parser, 0);
+
+ AstSubscript *sub_node = make_node(AstSubscript, Ast_Kind_Subscript);
+ sub_node->token = open_bracket;
+ sub_node->addr = retval;
+ sub_node->expr = expr;
+ sub_node->__unused_operation = Binary_Op_Subscript;
+
+ retval = (AstTyped *) sub_node;
+ expect_token(parser, ']');
+ break;
+ }
+
+ case '.': {
+ if (parse_possible_struct_literal(parser, retval, &retval)) return retval;
+ if (parse_possible_array_literal(parser, retval, &retval)) return retval;
+
+ consume_token(parser);
+ AstFieldAccess* field = make_node(AstFieldAccess, Ast_Kind_Field_Access);
+ field->token = expect_token(parser, Token_Type_Symbol);
+ field->expr = retval;
+
+ retval = (AstTyped *) field;
+ break;
+ }
+
+ case '(': {
+ AstCall* call_node = make_node(AstCall, Ast_Kind_Call);
+ call_node->token = expect_token(parser, '(');
+ call_node->callee = retval;
+
+ arguments_initialize(&call_node->args);
+
+ parse_arguments(parser, ')', &call_node->args);
+
+ // Wrap expressions in AstArgument
+ bh_arr_each(AstTyped *, arg, call_node->args.values) {
+ if ((*arg) == NULL) continue;
+ *arg = (AstTyped *) make_argument(parser->allocator, *arg);
+ }
+
+ bh_arr_each(AstNamedValue *, named_value, call_node->args.named_values) {
+ if ((*named_value)->value == NULL) continue;
+ (*named_value)->value = (AstTyped *) make_argument(parser->allocator, (AstTyped *) (*named_value)->value);
+ }
+
+ retval = (AstTyped *) call_node;
+ break;
+ }
+
+ case Token_Type_Right_Arrow: {
+ AstBinaryOp* method_call = make_node(AstBinaryOp, Ast_Kind_Method_Call);
+ method_call->token = expect_token(parser, Token_Type_Right_Arrow);
+ method_call->left = retval;
+ method_call->right = parse_factor(parser);
+
+ retval = (AstTyped *) method_call;
+ break;
+ }
+
+ case Token_Type_Keyword_If: {
+ AstIfExpression* if_expression = make_node(AstIfExpression, Ast_Kind_If_Expression);
+ if_expression->token = expect_token(parser, Token_Type_Keyword_If);
+
+ if_expression->true_expr = retval;
+ if_expression->cond = parse_expression(parser, 0);
+ expect_token(parser, Token_Type_Keyword_Else);
+ if_expression->false_expr = parse_expression(parser, 0);
+
+ retval = (AstTyped *) if_expression;
+
+ // nocheckin This should maybe be goto factor_parsed; ??
+ break;
+ }
+
+ default: goto factor_parsed;
+ }
+ }
+
+factor_parsed:
+
+ return retval;
+}
+
+static inline i32 get_precedence(BinaryOp kind) {
+ switch (kind) {
+ case Binary_Op_Assign: return 1;
+ case Binary_Op_Assign_Add: return 1;
+ case Binary_Op_Assign_Minus: return 1;
+ case Binary_Op_Assign_Multiply: return 1;
+ case Binary_Op_Assign_Divide: return 1;
+ case Binary_Op_Assign_Modulus: return 1;
+ case Binary_Op_Assign_And: return 1;
+ case Binary_Op_Assign_Or: return 1;
+ case Binary_Op_Assign_Xor: return 1;
+ case Binary_Op_Assign_Shl: return 1;
+ case Binary_Op_Assign_Shr: return 1;
+ case Binary_Op_Assign_Sar: return 1;
+
+ case Binary_Op_Pipe: return 2;
+ case Binary_Op_Range: return 2;
+
+ case Binary_Op_Bool_And: return 3;
+ case Binary_Op_Bool_Or: return 3;
+
+ case Binary_Op_Equal: return 4;
+ case Binary_Op_Not_Equal: return 4;
+
+ case Binary_Op_Less_Equal: return 5;
+ case Binary_Op_Less: return 5;
+ case Binary_Op_Greater_Equal: return 5;
+ case Binary_Op_Greater: return 5;
+
+ case Binary_Op_And: return 6;
+ case Binary_Op_Or: return 6;
+ case Binary_Op_Xor: return 6;
+ case Binary_Op_Shl: return 6;
+ case Binary_Op_Shr: return 6;
+ case Binary_Op_Sar: return 6;
+
+ case Binary_Op_Add: return 7;
+ case Binary_Op_Minus: return 7;
+
+ case Binary_Op_Multiply: return 8;
+ case Binary_Op_Divide: return 8;
+
+ case Binary_Op_Modulus: return 9;
+
+ default: return -1;
+ }
+}
+
+static BinaryOp binary_op_from_token_type(TokenType t) {
+ switch ((u16) t) {
+ case Token_Type_Equal_Equal: return Binary_Op_Equal;
+ case Token_Type_Not_Equal: return Binary_Op_Not_Equal;
+ case Token_Type_Less_Equal: return Binary_Op_Less_Equal;
+ case Token_Type_Greater_Equal: return Binary_Op_Greater_Equal;
+ case '<': return Binary_Op_Less;
+ case '>': return Binary_Op_Greater;
+
+ case '+': return Binary_Op_Add;
+ case '-': return Binary_Op_Minus;
+ case '*': return Binary_Op_Multiply;
+ case '/': return Binary_Op_Divide;
+ case '%': return Binary_Op_Modulus;
+
+ case '&': return Binary_Op_And;
+ case '|': return Binary_Op_Or;
+ case '^': return Binary_Op_Xor;
+ case Token_Type_Shift_Left: return Binary_Op_Shl;
+ case Token_Type_Shift_Right: return Binary_Op_Shr;
+ case Token_Type_Shift_Arith_Right: return Binary_Op_Sar;
+
+ case Token_Type_And_And: return Binary_Op_Bool_And;
+ case Token_Type_Or_Or: return Binary_Op_Bool_Or;
+
+ case '=': return Binary_Op_Assign;
+ case Token_Type_Plus_Equal: return Binary_Op_Assign_Add;
+ case Token_Type_Minus_Equal: return Binary_Op_Assign_Minus;
+ case Token_Type_Star_Equal: return Binary_Op_Assign_Multiply;
+ case Token_Type_Fslash_Equal: return Binary_Op_Assign_Divide;
+ case Token_Type_Percent_Equal: return Binary_Op_Assign_Modulus;
+ case Token_Type_And_Equal: return Binary_Op_Assign_And;
+ case Token_Type_Or_Equal: return Binary_Op_Assign_Or;
+ case Token_Type_Xor_Equal: return Binary_Op_Assign_Xor;
+ case Token_Type_Shl_Equal: return Binary_Op_Assign_Shl;
+ case Token_Type_Shr_Equal: return Binary_Op_Assign_Shr;
+ case Token_Type_Sar_Equal: return Binary_Op_Assign_Sar;
+
+ case Token_Type_Pipe: return Binary_Op_Pipe;
+ case Token_Type_Dot_Dot: return Binary_Op_Range;
+ case '[': return Binary_Op_Subscript;
+ default: return Binary_Op_Count;
+ }
+}
+
+static AstTyped* parse_compound_assignment(OnyxParser* parser, AstTyped* lhs) {
+ if (parser->curr->type != '=') return lhs;
+
+ AstBinaryOp* assignment = make_node(AstBinaryOp, Ast_Kind_Binary_Op);
+ assignment->token = expect_token(parser, '=');
+ assignment->operation = Binary_Op_Assign;
+ assignment->left = lhs;
+ assignment->right = parse_compound_expression(parser, 0);
+
+ return (AstTyped *) assignment;
+}
+
+static AstTyped* parse_compound_expression(OnyxParser* parser, b32 assignment_allowed) {
+ AstTyped* first = parse_expression(parser, assignment_allowed);
+
+ if (parser->curr->type == ',') {
+ AstCompound* compound = make_node(AstCompound, Ast_Kind_Compound);
+ compound->token = parser->curr;
+
+ bh_arr_new(global_heap_allocator, compound->exprs, 2);
+ bh_arr_push(compound->exprs, first);
+
+ while (consume_token_if_next(parser, ',')) {
+ if (parser->hit_unexpected_token) return (AstTyped *) compound;
+
+ AstTyped* expr = parse_expression(parser, 0);
+ bh_arr_push(compound->exprs, expr);
+
+ if (assignment_allowed && parser->curr->type == '=') {
+ return parse_compound_assignment(parser, (AstTyped *) compound);
+ }
+ }
+
+ return (AstTyped *) compound;
+
+ } else {
+ return first;
+ }
+}
+
+static AstTyped* parse_expression(OnyxParser* parser, b32 assignment_allowed) {
+ bh_arr(AstBinaryOp*) tree_stack = NULL;
+ bh_arr_new(global_heap_allocator, tree_stack, 4);
+ bh_arr_set_length(tree_stack, 0);
+
+ AstTyped* left = parse_factor(parser);
+ AstTyped* right;
+ AstTyped* root = left;
+
+ BinaryOp bin_op_kind;
+ OnyxToken* bin_op_tok;
+
+ while (1) {
+ if (parser->hit_unexpected_token) return root;
+
+ bin_op_kind = binary_op_from_token_type(parser->curr->type);
+ if (bin_op_kind == Binary_Op_Count) goto expression_done;
+ if (binop_is_assignment(bin_op_kind) && !assignment_allowed) goto expression_done;
+ if (bin_op_kind == Binary_Op_Subscript) goto expression_done;
+
+ bin_op_tok = parser->curr;
+ consume_token(parser);
+
+ AstBinaryOp* bin_op;
+ if (bin_op_kind == Binary_Op_Pipe) bin_op = make_node(AstBinaryOp, Ast_Kind_Pipe);
+ else if (bin_op_kind == Binary_Op_Range) bin_op = (AstBinaryOp *) make_node(AstRangeLiteral, Ast_Kind_Range_Literal);
+ else bin_op = make_node(AstBinaryOp, Ast_Kind_Binary_Op);
+
+ bin_op->token = bin_op_tok;
+ bin_op->operation = bin_op_kind;
+
+ while ( !bh_arr_is_empty(tree_stack) &&
+ get_precedence(bh_arr_last(tree_stack)->operation) >= get_precedence(bin_op_kind))
+ bh_arr_pop(tree_stack);
+
+ if (bh_arr_is_empty(tree_stack)) {
+ // NOTE: new is now the root node
+ bin_op->left = root;
+ root = (AstTyped *) bin_op;
+
+ } else {
+ bin_op->left = bh_arr_last(tree_stack)->right;
+ bh_arr_last(tree_stack)->right = (AstTyped *) bin_op;
+ }
+
+ bh_arr_push(tree_stack, bin_op);
+
+ right = parse_factor(parser);
+ bin_op->right = right;
+ }
+
+expression_done:
+ bh_arr_free(tree_stack);
+ return root;
+}
+
+static AstIfWhile* parse_if_stmt(OnyxParser* parser) {
+ AstIfWhile* if_node = make_node(AstIfWhile, Ast_Kind_If);
+ if_node->token = expect_token(parser, Token_Type_Keyword_If);
+
+ AstIfWhile* root_if = if_node;
+ AstTyped* cond;
+ AstNode* initialization_or_cond=NULL;
+ b32 had_initialization = 0;
+ if (parse_possible_symbol_declaration(parser, &initialization_or_cond)) {
+ had_initialization = 1;
+
+ } else {
+ // NOTE: Assignment is allowed here because instead of not parsing correctly,
+ // an error is reported in the typechecking, saying that assignment isn't allowed
+ // here, which is better than an unexpected token error.
+ initialization_or_cond = (AstNode *) parse_compound_expression(parser, 1);
+ }
+
+ if (had_initialization || parser->curr->type == ';') {
+ expect_token(parser, ';');
+ cond = parse_expression(parser, 1);
+ } else {
+ cond = (AstTyped *) initialization_or_cond;
+ initialization_or_cond = NULL;
+ }
+
+ AstBlock* true_stmt = parse_block(parser, 1);
+
+ if_node->initialization = initialization_or_cond;
+ if_node->cond = cond;
+ if (true_stmt != NULL)
+ if_node->true_stmt = true_stmt;
+
+ while (consume_token_if_next(parser, Token_Type_Keyword_Elseif)) {
+ if (parser->hit_unexpected_token) return root_if;
+
+ AstIfWhile* elseif_node = make_node(AstIfWhile, Ast_Kind_If);
+ elseif_node->token = parser->curr - 1;
+
+ cond = parse_expression(parser, 1);
+ true_stmt = parse_block(parser, 1);
+
+ elseif_node->cond = cond;
+ if (true_stmt != NULL)
+ elseif_node->true_stmt = true_stmt;
+
+ if_node->false_stmt = (AstBlock *) elseif_node;
+ if_node = elseif_node;
+ }
+
+ if (consume_token_if_next(parser, Token_Type_Keyword_Else)) {
+ AstBlock* false_stmt = parse_block(parser, 1);
+ if (false_stmt != NULL)
+ if_node->false_stmt = false_stmt;
+ }
+
+ return root_if;
+}
+
+static AstIfWhile* parse_while_stmt(OnyxParser* parser) {
+ OnyxToken* while_token = expect_token(parser, Token_Type_Keyword_While);
+ AstIfWhile* while_node = make_node(AstIfWhile, Ast_Kind_While);
+ while_node->token = while_token;
+
+ AstTyped* cond;
+ AstNode* initialization_or_cond=NULL;
+ b32 had_initialization = 0;
+ if (parse_possible_symbol_declaration(parser, &initialization_or_cond)) {
+ had_initialization = 1;
+
+ } else {
+ // NOTE: Assignment is allowed here because instead of not parsing correctly,
+ // an error is reported in the typechecking, saying that assignment isn't allowed
+ // here, which is better than an unexpected token error.
+ initialization_or_cond = (AstNode *) parse_compound_expression(parser, 1);
+ }
+
+ if (had_initialization || parser->curr->type == ';') {
+ expect_token(parser, ';');
+ cond = parse_expression(parser, 1);
+ } else {
+ cond = (AstTyped *) initialization_or_cond;
+ initialization_or_cond = NULL;
+ }
+
+ while_node->initialization = initialization_or_cond;
+ while_node->cond = cond;
+ while_node->true_stmt = parse_block(parser, 1);
+
+ if (consume_token_if_next(parser, Token_Type_Keyword_Else)) {
+ while_node->false_stmt = parse_block(parser, 1);
+ }
+
+ return while_node;
+}
+
+static AstFor* parse_for_stmt(OnyxParser* parser) {
+ AstFor* for_node = make_node(AstFor, Ast_Kind_For);
+ for_node->token = expect_token(parser, Token_Type_Keyword_For);
+
+ if (consume_token_if_next(parser, '^')) {
+ for_node->by_pointer = 1;
+ }
+
+ OnyxToken* local_sym = expect_token(parser, Token_Type_Symbol);
+ AstLocal* var_node = make_local(parser->allocator, local_sym, NULL);
+
+ for_node->var = var_node;
+
+ expect_token(parser, ':');
+ for_node->iter = parse_expression(parser, 1);
+ for_node->stmt = parse_block(parser, 1);
+
+ return for_node;
+}
+
+static AstSwitch* parse_switch_stmt(OnyxParser* parser) {
+ AstSwitch* switch_node = make_node(AstSwitch, Ast_Kind_Switch);
+ switch_node->token = expect_token(parser, Token_Type_Keyword_Switch);
+
+ bh_arr_new(global_heap_allocator, switch_node->cases, 4);
+
+ AstTyped* expr;
+ AstNode* initialization_or_expr=NULL;
+ b32 had_initialization = 0;
+ if (parse_possible_symbol_declaration(parser, &initialization_or_expr)) {
+ had_initialization = 1;
+
+ } else {
+ // NOTE: Assignment is allowed here because instead of not parsing correctly,
+ // an error is reported in the typechecking, saying that assignment isn't allowed
+ // here, which is better than an unexpected token error.
+ initialization_or_expr = (AstNode *) parse_compound_expression(parser, 1);
+ }
+
+ if (had_initialization || parser->curr->type == ';') {
+ expect_token(parser, ';');
+ expr = parse_expression(parser, 1);
+
+ } else {
+ expr = (AstTyped *) initialization_or_expr;
+ initialization_or_expr = NULL;
+ }
+
+ switch_node->initialization = initialization_or_expr;
+ switch_node->expr = expr;
+
+ expect_token(parser, '{');
+
+ while (consume_token_if_next(parser, Token_Type_Keyword_Case)) {
+ if (parser->hit_unexpected_token) return switch_node;
+
+ bh_arr(AstTyped *) case_values = NULL;
+ bh_arr_new(global_heap_allocator, case_values, 1);
+
+ if (parse_possible_directive(parser, "default")) {
+ switch_node->default_case = parse_block(parser, 1);
+
+ if (parser->curr->type != '}') {
+ onyx_report_error(parser->curr->pos, "The #default case must be the last case in a switch statement.\n");
+ }
+ break;
+ }
+
+ AstTyped* value = parse_expression(parser, 1);
+ bh_arr_push(case_values, value);
+ while (consume_token_if_next(parser, ',')) {
+ if (parser->hit_unexpected_token) return switch_node;
+
+ value = parse_expression(parser, 1);
+ bh_arr_push(case_values, value);
+ }
+
+ AstBlock* block = parse_block(parser, 1);
+
+ AstSwitchCase sc_node;
+ sc_node.block = block;
+ sc_node.values = case_values;
+
+ bh_arr_push(switch_node->cases, sc_node);
+ }
+
+ expect_token(parser, '}');
+ return switch_node;
+}
+
+static i32 parse_possible_compound_symbol_declaration(OnyxParser* parser, AstNode** ret) {
+ u32 token_offset = 0;
+ while (peek_token(token_offset)->type == Token_Type_Symbol) {
+ token_offset += 1;
+
+ if (peek_token(token_offset)->type != ',') break;
+ token_offset += 1;
+ }
+
+ if (peek_token(token_offset)->type != ':') return 0;
+
+ // At this point, we are sure it is a compound declaration.
+ AstCompound* local_compound = make_node(AstCompound, Ast_Kind_Compound);
+ bh_arr_new(global_heap_allocator, local_compound->exprs, token_offset / 2);
+
+ AstLocal* first_local = NULL;
+ AstLocal* prev_local = NULL;
+
+ while (parser->curr->type == Token_Type_Symbol) {
+ if (parser->hit_unexpected_token) return 1;
+
+ OnyxToken* local_sym = expect_token(parser, Token_Type_Symbol);
+ AstLocal* new_local = make_local(parser->allocator, local_sym, NULL);
+
+ if (prev_local == NULL) {
+ first_local = new_local;
+ } else {
+ prev_local->next = (AstNode *) new_local;
+ }
+ prev_local = new_local;
+
+ AstNode* sym_node = make_symbol(parser->allocator, local_sym);
+ bh_arr_push(local_compound->exprs, (AstTyped *) sym_node);
+
+ consume_token_if_next(parser, ',');
+ }
+
+ expect_token(parser, ':');
+
+ if (parser->curr->type == '=') {
+ AstBinaryOp* assignment = make_binary_op(parser->allocator, Binary_Op_Assign, (AstTyped *) local_compound, NULL);
+ assignment->token = expect_token(parser, '=');
+ assignment->right = parse_compound_expression(parser, 0);
+
+ prev_local->next = (AstNode *) assignment;
+
+ } else {
+ AstType* type_for_all = parse_type(parser);
+ forll (AstLocal, local, first_local, next) {
+ local->type_node = type_for_all;
+ }
+ }
+
+ *ret = (AstNode *) first_local;
+ return 1;
+}
+
+// Returns:
+// 0 - if this was not a symbol declaration.
+// 1 - if this was a local declaration.
+// 2 - if this was binding declaration.
+// ret is set to the statement to insert
+static i32 parse_possible_symbol_declaration(OnyxParser* parser, AstNode** ret) {
+ // Has to start with a symbol to be a declaration
+ if (parser->curr->type != Token_Type_Symbol) return 0;
+
+ // If the token after the symbol is a comma, assume this is a compound declaration.
+ if (peek_token(1)->type == ',') {
+ return parse_possible_compound_symbol_declaration(parser, ret);
+ }
+
+ if (peek_token(1)->type != ':') return 0;
+
+ OnyxToken* symbol = expect_token(parser, Token_Type_Symbol);
+ expect_token(parser, ':');
+
+ if (parser->curr->type == ':') {
+ AstBinding* binding = parse_top_level_binding(parser, symbol);
+ if (parser->hit_unexpected_token) return 2;
+
+ ENTITY_SUBMIT(binding);
+ return 2;
+ }
+
+ AstType* type_node = NULL;
+ if (parser->curr->type != '=') {
+ type_node = parse_type(parser);
+ }
+
+ AstLocal* local = make_local(parser->allocator, symbol, type_node);
+ *ret = (AstNode *) local;
+
+ if (parser->curr->type == '=') {
+ AstBinaryOp* assignment = make_node(AstBinaryOp, Ast_Kind_Binary_Op);
+ assignment->operation = Binary_Op_Assign;
+ assignment->token = expect_token(parser, '=');
+ local->next = (AstNode *) assignment;
+
+ AstTyped* expr = parse_expression(parser, 1);
+ if (expr == NULL) return 1;
+ assignment->right = expr;
+
+ // INVESTIGATE: I don't know why, but appearantly, this has to be a
+ // symbol node, not a direct link to the local. There is an error about
+ // being unable to resolve the type of the local if it is immediately set.
+ AstNode* left_symbol = make_node(AstNode, Ast_Kind_Symbol);
+ left_symbol->token = symbol;
+ assignment->left = (AstTyped *) left_symbol;
+ }
+
+ return 1;
+}
+
+static AstReturn* parse_return_stmt(OnyxParser* parser) {
+ AstReturn* return_node = make_node(AstReturn, Ast_Kind_Return);
+ return_node->token = expect_token(parser, Token_Type_Keyword_Return);
+
+ AstTyped* expr = NULL;
+
+ if (parser->curr->type != ';') {
+ expr = parse_compound_expression(parser, 0);
+
+ if (expr == NULL || expr == (AstTyped *) &error_node) {
+ return (AstReturn *) &error_node;
+ } else {
+ return_node->expr = expr;
+ }
+ }
+
+ return return_node;
+}
+
+static AstNode* parse_use_stmt(OnyxParser* parser) {
+ OnyxToken* use_token = expect_token(parser, Token_Type_Keyword_Use);
+ AstUse* use_node = make_node(AstUse, Ast_Kind_Use);
+ use_node->token = use_token;
+ use_node->expr = parse_expression(parser, 1);
+
+ if (consume_token_if_next(parser, '{')) {
+ bh_arr_new(global_heap_allocator, use_node->only, 4);
+
+ while (!consume_token_if_next(parser, '}')) {
+ if (parser->hit_unexpected_token) return NULL;
+
+ QualifiedUse qu;
+ qu.symbol_name = expect_token(parser, Token_Type_Symbol);
+
+ if (consume_token_if_next(parser, Token_Type_Keyword_As))
+ qu.as_name = expect_token(parser, Token_Type_Symbol);
+ else
+ qu.as_name = qu.symbol_name;
+
+ bh_arr_push(use_node->only, qu);
+
+ if (parser->curr->type != '}')
+ expect_token(parser, ',');
+ }
+ }
+
+ if (use_node->expr->kind == Ast_Kind_Package) {
+ ENTITY_SUBMIT(use_node);
+ return NULL;
+
+ } else {
+ return (AstNode *) use_node;
+ }
+}
+
+static AstNode* parse_jump_stmt(OnyxParser* parser, TokenType token_type, JumpType jump_type) {
+ AstJump* jnode = make_node(AstJump, Ast_Kind_Jump);
+ jnode->token = expect_token(parser, token_type);
+ jnode->jump = jump_type;
+
+ u64 count = 1;
+ while (consume_token_if_next(parser, token_type)) count++;
+ jnode->count = count;
+
+ return (AstNode *) jnode;
+}
+
+static AstNode* parse_statement(OnyxParser* parser) {
+ b32 needs_semicolon = 1;
+ AstNode* retval = NULL;
+
+ switch ((u16) parser->curr->type) {
+ case Token_Type_Keyword_Return:
+ retval = (AstNode *) parse_return_stmt(parser);
+ break;
+
+ case '{':
+ case Token_Type_Empty_Block:
+ case Token_Type_Keyword_Do:
+ needs_semicolon = 0;
+ retval = (AstNode *) parse_block(parser, 1);
+ break;
+
+ case Token_Type_Symbol: {
+ i32 symbol_res = parse_possible_symbol_declaration(parser, &retval);
+ if (symbol_res == 2) needs_semicolon = 0;
+ if (symbol_res != 0) break;
+
+ // fallthrough
+ }
+
+ case '(': case '+': case '-': case '!': case '*': case '^':
+ case Token_Type_Literal_Integer:
+ case Token_Type_Literal_Float:
+ case Token_Type_Literal_String:
+ retval = (AstNode *) parse_compound_expression(parser, 1);
+ break;
+
+ case Token_Type_Keyword_If:
+ needs_semicolon = 0;
+ retval = (AstNode *) parse_if_stmt(parser);
+ break;
+
+ case Token_Type_Keyword_While:
+ needs_semicolon = 0;
+ retval = (AstNode *) parse_while_stmt(parser);
+ break;
+
+ case Token_Type_Keyword_For:
+ needs_semicolon = 0;
+ retval = (AstNode *) parse_for_stmt(parser);
+ break;
+
+ case Token_Type_Keyword_Switch:
+ needs_semicolon = 0;
+ retval = (AstNode *) parse_switch_stmt(parser);
+ break;
+
+ case Token_Type_Keyword_Break:
+ retval = parse_jump_stmt(parser, Token_Type_Keyword_Break, Jump_Type_Break);
+ break;
+
+ case Token_Type_Keyword_Continue:
+ retval = parse_jump_stmt(parser, Token_Type_Keyword_Continue, Jump_Type_Continue);
+ break;
+
+ case Token_Type_Keyword_Fallthrough:
+ retval = parse_jump_stmt(parser, Token_Type_Keyword_Fallthrough, Jump_Type_Fallthrough);
+ break;
+
+ case Token_Type_Keyword_Defer: {
+ needs_semicolon = 0;
+
+ AstDefer* defer = make_node(AstDefer, Ast_Kind_Defer);
+ defer->token = expect_token(parser, Token_Type_Keyword_Defer);
+ defer->stmt = parse_statement(parser);
+
+ retval = (AstNode *) defer;
+ break;
+ }
+
+ case Token_Type_Keyword_Use: {
+ needs_semicolon = 0;
+
+ retval = (AstNode *) parse_use_stmt(parser);
+ break;
+ }
+
+ case '#': {
+ if (parse_possible_directive(parser, "context_scope")) {
+ AstLocal* context_tmp = make_local(parser->allocator, NULL, builtin_context_variable->type_node);
+
+ AstBinaryOp* assignment = make_node(AstBinaryOp, Ast_Kind_Binary_Op);
+ assignment->operation = Binary_Op_Assign;
+ assignment->left = (AstTyped *) context_tmp;
+ assignment->right = builtin_context_variable;
+ context_tmp->next = (AstNode *) assignment;
+
+ AstBinaryOp* assignment2 = make_node(AstBinaryOp, Ast_Kind_Binary_Op);
+ assignment2->operation = Binary_Op_Assign;
+ assignment2->left = builtin_context_variable;
+ assignment2->right = (AstTyped *) context_tmp;
+
+ AstBlock* context_block = parse_block(parser, 1);
+ assignment->next = (AstNode *) context_block;
+
+ AstDefer* defer_node = make_node(AstDefer, Ast_Kind_Defer);
+ defer_node->stmt = (AstNode *) assignment2;
+ defer_node->next = context_block->body;
+ context_block->body = (AstNode *) defer_node;
+
+ needs_semicolon = 0;
+ retval = (AstNode *) context_tmp;
+ break;
+ }
+
+ if (next_tokens_are(parser, 2, '#', Token_Type_Keyword_If)) {
+ AstIf* static_if = parse_static_if_stmt(parser, 1);
+ ENTITY_SUBMIT(static_if);
+
+ needs_semicolon = 0;
+ retval = (AstNode *) static_if;
+ break;
+ }
+
+ if (parse_possible_directive(parser, "persist")) {
+ // :Duplicated from parse_top_level_statement
+ AstMemRes* memres = make_node(AstMemRes, Ast_Kind_Memres);
+ memres->token = expect_token(parser, Token_Type_Symbol);
+ expect_token(parser, ':');
+
+ if (parser->curr->type != '=')
+ memres->type_node = parse_type(parser);
+
+ if (consume_token_if_next(parser, '='))
+ memres->initial_value = parse_expression(parser, 1);
+
+
+ ENTITY_SUBMIT(memres);
+
+ AstBinding* binding = make_node(AstBinding, Ast_Kind_Binding);
+ binding->token = memres->token;
+ binding->node = (AstNode *) memres;
+ ENTITY_SUBMIT(binding);
+ break;
+ }
+
+ if (next_tokens_are(parser, 2, '#', Token_Type_Symbol)) {
+ retval = (AstNode *) parse_factor(parser);
+ break;
+ }
+ }
+
+ default:
+ break;
+ }
+
+ if (needs_semicolon) expect_token(parser, ';');
+
+ return retval;
+}
+
+static AstBlock* parse_block(OnyxParser* parser, b32 make_a_new_scope) {
+ AstBlock* block = make_node(AstBlock, Ast_Kind_Block);
+ block->rules = Block_Rule_Normal;
+
+ // NOTE: --- is for an empty block
+ if (parser->curr->type == Token_Type_Empty_Block) {
+ block->token = expect_token(parser, Token_Type_Empty_Block);
+ return block;
+ }
+
+ if (make_a_new_scope) {
+ block->binding_scope = scope_create(parser->allocator, parser->current_scope, parser->curr->pos);
+ parser->current_scope = block->binding_scope;
+ }
+
+ if (parser->curr->type == Token_Type_Keyword_Do) {
+ block->token = expect_token(parser, Token_Type_Keyword_Do);
+ block->body = parse_statement(parser);
+ if (make_a_new_scope) parser->current_scope = parser->current_scope->parent;
+ return block;
+ }
+
+ block->token = expect_token(parser, '{');
+
+ AstNode** next = &block->body;
+ AstNode* stmt = NULL;
+ while (!consume_token_if_next(parser, '}')) {
+ if (parser->hit_unexpected_token) {
+ if (make_a_new_scope) parser->current_scope = parser->current_scope->parent;
+ return block;
+ }
+
+ stmt = parse_statement(parser);
+
+ if (stmt != NULL && stmt->kind != Ast_Kind_Error) {
+ *next = stmt;
+
+ while (stmt->next != NULL) stmt = stmt->next;
+ next = &stmt->next;
+ }
+ }
+
+ if (make_a_new_scope) parser->current_scope = parser->current_scope->parent;
+ return block;
+}
+
+static void parse_polymorphic_variable(OnyxParser* parser, AstType*** next_insertion) {
+ bh_arr(AstPolyParam) pv = NULL;
+
+ if (parser->polymorph_context.poly_params == NULL)
+ onyx_report_error(parser->curr->pos, "Polymorphic variable not valid here.");
+ else
+ pv = *parser->polymorph_context.poly_params;
+
+ consume_token(parser);
+
+ AstNode* symbol_node = make_node(AstNode, Ast_Kind_Symbol);
+ symbol_node->token = expect_token(parser, Token_Type_Symbol);
+
+ **next_insertion = (AstType *) symbol_node;
+ *next_insertion = NULL;
+
+ if (pv != NULL) {
+ bh_arr_push(pv, ((AstPolyParam) {
+ .kind = PPK_Poly_Type,
+ .poly_sym = symbol_node,
+
+ // These will be filled out by function_params()
+ .type_expr = NULL,
+ .idx = -1,
+ }));
+
+ *parser->polymorph_context.poly_params = pv;
+ }
+}
+
+static AstType* parse_compound_type(OnyxParser* parser) {
+ // CLEANUP this is little weird having this here because it means that this parses:
+ //
+ // foo :: (x: (something_here: i32)) -> void ---
+ //
+ if (next_tokens_are(parser, 2, Token_Type_Symbol, ':')) {
+ consume_tokens(parser, 2);
+ }
+
+ AstType* first = parse_type(parser);
+
+ if (parser->curr->type == ',') {
+ AstCompoundType* ctype = make_node(AstCompoundType, Ast_Kind_Type_Compound);
+ ctype->token = parser->curr;
+
+ bh_arr_new(global_heap_allocator, ctype->types, 2);
+ bh_arr_push(ctype->types, first);
+
+ while (consume_token_if_next(parser, ',')) {
+ if (parser->hit_unexpected_token) return (AstType *) ctype;
+
+ if (next_tokens_are(parser, 2, Token_Type_Symbol, ':')) {
+ consume_tokens(parser, 2);
+ }
+
+ bh_arr_push(ctype->types, parse_type(parser));
+ }
+
+ return (AstType *) ctype;
+
+ } else {
+ return first;
+ }
+}
+
+static AstType* parse_function_type(OnyxParser* parser, OnyxToken* proc_token) {
+ bh_arr(AstType *) params = NULL;
+ bh_arr_new(global_scratch_allocator, params, 4);
+ bh_arr_set_length(params, 0);
+
+ expect_token(parser, '(');
+ while (!consume_token_if_next(parser, ')')) {
+ if (parser->hit_unexpected_token) return NULL;
+
+ // NOTE: Allows for names to be put in the function types, just for readability.
+ if (next_tokens_are(parser, 2, Token_Type_Symbol, ':')) consume_tokens(parser, 2);
+
+ AstType* param_type = parse_type(parser);
+ bh_arr_push(params, param_type);
+
+ if (parser->curr->type != ')')
+ expect_token(parser, ',');
+ }
+
+ AstType* return_type = (AstType *) &basic_type_void;
+ if (consume_token_if_next(parser, Token_Type_Right_Arrow))
+ return_type = parse_type(parser);
+
+ i64 param_count = bh_arr_length(params);
+ AstFunctionType* new = onyx_ast_node_new(parser->allocator,
+ sizeof(AstFunctionType) + sizeof(AstType*) * param_count,
+ Ast_Kind_Function_Type);
+ new->token = proc_token;
+ new->param_count = param_count;
+ new->return_type = return_type;
+
+ if (param_count > 0)
+ fori (i, 0, param_count) new->params[i] = params[i];
+
+ return (AstType *) new;
+}
+
+static AstType* parse_type(OnyxParser* parser) {
+ AstType* root = NULL;
+ AstType** next_insertion = &root;
+
+ while (1) {
+ if (parser->hit_unexpected_token) return root;
+
+ switch ((u16) parser->curr->type) {
+ case '^': {
+ AstPointerType* new = make_node(AstPointerType, Ast_Kind_Pointer_Type);
+ new->flags |= Basic_Flag_Pointer;
+ new->token = expect_token(parser, '^');
+
+ *next_insertion = (AstType *) new;
+ next_insertion = &new->elem;
+ break;
+ }
+
+ case '[': {
+ AstType *new;
+ OnyxToken *open_bracket = expect_token(parser, '[');
+
+ if (parser->curr->type == ']') {
+ new = make_node(AstSliceType, Ast_Kind_Slice_Type);
+ new->token = open_bracket;
+
+ } else if (parser->curr->type == Token_Type_Dot_Dot) {
+ new = make_node(AstDynArrType, Ast_Kind_DynArr_Type);
+ new->token = open_bracket;
+ consume_token(parser);
+
+ } else {
+ new = make_node(AstArrayType, Ast_Kind_Array_Type);
+ new->token = open_bracket;
+
+ if (parser->curr->type == '$') {
+ AstType** insertion = (AstType **) &((AstArrayType *) new)->count_expr;
+ parse_polymorphic_variable(parser, &insertion);
+ } else {
+ ((AstArrayType *) new)->count_expr = parse_expression(parser, 0);
+ }
+ }
+
+ expect_token(parser, ']');
+ *next_insertion = (AstType *) new;
+ next_insertion = &((AstSliceType *) new)->elem;
+ break;
+ }
+
+ case Token_Type_Keyword_Proc: {
+ OnyxToken* proc_token = expect_token(parser, Token_Type_Keyword_Proc);
+ onyx_report_warning(proc_token->pos, "Warning: 'proc' is a deprecated keyword.");
+ *next_insertion = parse_function_type(parser, proc_token);
+ next_insertion = NULL;
+ break;
+ }
+
+ case '$': {
+ parse_polymorphic_variable(parser, &next_insertion);
+ break;
+ }
+
+ case Token_Type_Symbol: {
+ AstNode* symbol_node = make_node(AstNode, Ast_Kind_Symbol);
+ symbol_node->token = expect_token(parser, Token_Type_Symbol);
+
+ *next_insertion = (AstType *) symbol_node;
+
+ while (consume_token_if_next(parser, '.')) {
+ AstFieldAccess* field = make_node(AstFieldAccess, Ast_Kind_Field_Access);
+ field->token = expect_token(parser, Token_Type_Symbol);
+ field->expr = (AstTyped *) *next_insertion;
+
+ *next_insertion = (AstType *) field;
+ }
+
+ if (parser->curr->type == '(') {
+ OnyxToken* paren_token = expect_token(parser, '(');
+
+ bh_arr(AstNode *) params = NULL;
+ bh_arr_new(global_heap_allocator, params, 2);
+
+ while (!consume_token_if_next(parser, ')')) {
+ if (parser->hit_unexpected_token) break;
+
+ AstNode* t = (AstNode *) parse_type(parser);
+ bh_arr_push(params, t);
+
+ if (parser->curr->type != ')')
+ expect_token(parser, ',');
+ }
+
+ AstPolyCallType* pc_type = make_node(AstPolyCallType, Ast_Kind_Poly_Call_Type);
+ pc_type->token = paren_token;
+ pc_type->callee = *next_insertion;
+ pc_type->params = params;
+
+ *next_insertion = (AstType *) pc_type;
+ }
+
+ next_insertion = NULL;
+ break;
+ }
+
+ case Token_Type_Keyword_Struct: {
+ AstStructType* s_node = parse_struct(parser);
+ *next_insertion = (AstType *) s_node;
+ next_insertion = NULL;
+ break;
+ }
+
+ case '#': {
+ // :ValueDirectiveHack
+ if (parse_possible_directive(parser, "value")) {
+ // It is very weird to put these here.
+ case Token_Type_Literal_Integer:
+ case Token_Type_Literal_String:
+ case Token_Type_Literal_Float:
+ case Token_Type_Literal_True:
+ case Token_Type_Literal_False:
+ *next_insertion = (AstType *) parse_expression(parser, 0);
+ next_insertion = NULL;
+ break;
+ }
+
+ next_insertion = NULL;
+ break;
+ }
+
+ case '(': {
+ OnyxToken* matching = find_matching_paren(parser->curr);
+
+ // :LinearTokenDependent
+ if ((matching + 1)->type == Token_Type_Right_Arrow) {
+ *next_insertion = parse_function_type(parser, parser->curr);
+
+ } else {
+ expect_token(parser, '(');
+ *next_insertion = parse_compound_type(parser);
+ expect_token(parser, ')');
+ }
+
+ next_insertion = NULL;
+ break;
+ }
+
+ case Token_Type_Keyword_Typeof: {
+ *next_insertion = (AstType *) parse_typeof(parser);
+ next_insertion = NULL;
+ break;
+ }
+
+ default:
+ onyx_report_error(parser->curr->pos, "unexpected token '%b'.", parser->curr->text, parser->curr->length);
+ consume_token(parser);
+ break;
+ }
+
+ if (next_insertion == NULL) break;
+ }
+
+ return root;
+}
+
+static AstTypeOf* parse_typeof(OnyxParser* parser) {
+ OnyxToken* token = expect_token(parser, Token_Type_Keyword_Typeof);
+
+ AstTypeOf* type_of = make_node(AstTypeOf, Ast_Kind_Typeof);
+ type_of->token = token;
+ type_of->expr = parse_expression(parser, 0);
+ type_of->resolved_type = NULL;
+
+ return type_of;
+}
+
+static AstStructType* parse_struct(OnyxParser* parser) {
+ OnyxToken *s_token = expect_token(parser, Token_Type_Keyword_Struct);
+
+ AstStructType* s_node;
+ AstPolyStructType* poly_struct = NULL;
+
+ s_node = make_node(AstStructType, Ast_Kind_Struct_Type);
+ s_node->token = s_token;
+
+ if (consume_token_if_next(parser, '(')) {
+ bh_arr(AstPolyStructParam) poly_params = NULL;
+ bh_arr_new(global_heap_allocator, poly_params, 1);
+
+ while (!consume_token_if_next(parser, ')')) {
+ if (parser->hit_unexpected_token) return NULL;
+
+ OnyxToken* sym_token = expect_token(parser, Token_Type_Symbol);
+ expect_token(parser, ':');
+
+ AstType* param_type = parse_type(parser);
+
+ bh_arr_push(poly_params, ((AstPolyStructParam) {
+ .token = sym_token,
+ .type_node = param_type,
+ .type = NULL,
+ }));
+
+ if (parser->curr->type != ')')
+ expect_token(parser, ',');
+ }
+
+ poly_struct = make_node(AstPolyStructType, Ast_Kind_Poly_Struct_Type);
+ poly_struct->token = s_token;
+ poly_struct->poly_params = poly_params;
+ poly_struct->base_struct = s_node;
+ }
+
+ bh_arr_new(global_heap_allocator, s_node->members, 4);
+
+ while (parser->curr->type == '#') {
+ if (parser->hit_unexpected_token) return NULL;
+
+ if (parse_possible_directive(parser, "union")) {
+ s_node->flags |= Ast_Flag_Struct_Is_Union;
+ }
+
+ else if (parse_possible_directive(parser, "align")) {
+ AstNumLit* numlit = parse_int_literal(parser);
+ if (numlit == NULL) return NULL;
+
+ s_node->min_alignment = numlit->value.i;
+ }
+
+ else if (parse_possible_directive(parser, "size")) {
+ AstNumLit* numlit = parse_int_literal(parser);
+ if (numlit == NULL) return NULL;
+
+ s_node->min_size = numlit->value.i;
+ }
+
+ else {
+ OnyxToken* directive_token = expect_token(parser, '#');
+ OnyxToken* symbol_token = expect_token(parser, Token_Type_Symbol);
+
+ onyx_report_error(directive_token->pos, "unknown directive '#%b'.", symbol_token->text, symbol_token->length);
+ }
+ }
+
+ expect_token(parser, '{');
+
+ b32 member_is_used = 0;
+ bh_arr(OnyxToken *) member_list_temp = NULL;
+ bh_arr_new(global_heap_allocator, member_list_temp, 4);
+
+ while (!consume_token_if_next(parser, '}')) {
+ if (parser->hit_unexpected_token) return s_node;
+
+ member_is_used = consume_token_if_next(parser, Token_Type_Keyword_Use);
+
+ if (next_tokens_are(parser, 3, Token_Type_Symbol, ':', ':')) {
+ if (!s_node->scope) {
+ s_node->scope = scope_create(context.ast_alloc, parser->current_scope, s_node->token->pos);
+ parser->current_scope = s_node->scope;
+ }
+
+ OnyxToken* binding_name = expect_token(parser, Token_Type_Symbol);
+ consume_token(parser);
+
+ AstBinding* binding = parse_top_level_binding(parser, binding_name);
+ if (binding) ENTITY_SUBMIT(binding);
+
+ consume_token_if_next(parser, ';');
+
+ } else {
+ bh_arr_clear(member_list_temp);
+ while (!consume_token_if_next(parser, ':')) {
+ if (parser->hit_unexpected_token) return NULL;
+ bh_arr_push(member_list_temp, expect_token(parser, Token_Type_Symbol));
+
+ if (parser->curr->type != ':')
+ expect_token(parser, ',');
+ }
+
+ AstType* member_type = NULL;
+ if (parser->curr->type != '=')
+ member_type = parse_type(parser);
+
+ AstTyped* initial_value = NULL;
+ if (consume_token_if_next(parser, '='))
+ initial_value = parse_expression(parser, 0);
+
+ // RECONSIDER: There are seamingly arbitrary limitations put in place here which do two things:
+ // 1. Prevent multiple struct members being used in the same declaration.
+ // This makes sense because the members will be of the same type, which means
+ // they have the same members. Using both of the members would immediately result
+ // in name collisions.
+ //
+ // 2. Prevent multiple struct members having an initializer set for them.
+ // I think the semantics could be confusing either way, so I'm deciding to leave
+ // them out of discussion for now. Initialized members should be treated special and
+ // deserve their own line.
+ if (bh_arr_length(member_list_temp) > 1) {
+ if (member_is_used) onyx_report_error((member_list_temp[0] - 1)->pos, "'use' is only allowed for a single struct member declaration. Try splitting this compound declaration into multiple lines.");
+ if (initial_value) onyx_report_error(initial_value->token->pos, "Intialized values are only allowed on single struct member declarations. Try splitting this compound initializer into multiple lines.");
+ }
+
+ bh_arr_each(OnyxToken *, member_name, member_list_temp) {
+ AstStructMember* mem = make_node(AstStructMember, Ast_Kind_Struct_Member);
+ mem->token = *member_name;
+ mem->type_node = member_type;
+ mem->initial_value = initial_value;
+
+ if (member_is_used) mem->flags |= Ast_Flag_Struct_Mem_Used;
+
+ bh_arr_push(s_node->members, mem);
+ }
+
+ expect_token(parser, ';');
+ }
+ }
+
+ if (s_node->scope) parser->current_scope = parser->current_scope->parent;
+
+ bh_arr_free(member_list_temp);
+
+ if (poly_struct != NULL) {
+ // NOTE: Not a StructType
+ return (AstStructType *) poly_struct;
+
+ } else {
+ return s_node;
+ }
+}
+
+static void parse_function_params(OnyxParser* parser, AstFunction* func) {
+ expect_token(parser, '(');
+
+ if (consume_token_if_next(parser, ')')) return;
+
+ u32 param_idx = 0;
+ assert(parser->polymorph_context.poly_params != NULL);
+
+ b32 param_use = 0;
+ b32 param_is_baked = 0;
+ OnyxToken* symbol;
+ while (!consume_token_if_next(parser, ')')) {
+ if (parser->hit_unexpected_token) return;
+
+ AstParam curr_param = { 0 };
+
+ if (consume_token_if_next(parser, Token_Type_Keyword_Use)) param_use = 1;
+ if (consume_token_if_next(parser, '$')) param_is_baked = 1;
+
+ symbol = expect_token(parser, Token_Type_Symbol);
+ expect_token(parser, ':');
+
+ curr_param.vararg_kind = VA_Kind_Not_VA;
+ curr_param.local = make_local(parser->allocator, symbol, NULL);
+ curr_param.local->kind = Ast_Kind_Param;
+
+ if (param_use) {
+ curr_param.local->flags |= Ast_Flag_Param_Use;
+ param_use = 0;
+ }
+
+ if (parser->curr->type != '=') {
+ if (consume_token_if_next(parser, Token_Type_Dot_Dot)) {
+ if (consume_token_if_next(parser, '.')) curr_param.vararg_kind = VA_Kind_Untyped;
+ else curr_param.vararg_kind = VA_Kind_Typed;
+ }
+
+ if (curr_param.vararg_kind != VA_Kind_Untyped) {
+ // CLEANUP: This is mess and it is hard to follow what is going on here.
+ // I think with recent rewrites, this should be easier to do.
+ i32 old_len = bh_arr_length(*parser->polymorph_context.poly_params);
+ curr_param.local->type_node = parse_type(parser);
+ i32 new_len = bh_arr_length(*parser->polymorph_context.poly_params);
+
+ if (curr_param.vararg_kind == VA_Kind_Typed) {
+ AstVarArgType* va_type = make_node(AstVarArgType, Ast_Kind_VarArg_Type);
+ va_type->elem = curr_param.local->type_node;
+ va_type->token = curr_param.local->type_node->token;
+ curr_param.local->type_node = (AstType *) va_type;
+ }
+
+ fori (i, 0, new_len - old_len) {
+ (*parser->polymorph_context.poly_params)[old_len + i].type_expr = curr_param.local->type_node;
+ (*parser->polymorph_context.poly_params)[old_len + i].idx = param_idx;
+ }
+ }
+ }
+
+ if (curr_param.vararg_kind == VA_Kind_Not_VA && consume_token_if_next(parser, '=')) {
+ OnyxToken* directive_token = parser->curr;
+
+ // :Callsite currently #callsite is only valid as a default value for a funciton parameter.
+ if (parse_possible_directive(parser, "callsite")) {
+ AstCallSite* cs = make_node(AstCallSite, Ast_Kind_Call_Site);
+ cs->token = directive_token;
+ curr_param.default_value = (AstTyped *) cs;
+
+ } else {
+ curr_param.default_value = parse_expression(parser, 0);
+ }
+ }
+
+ if (param_is_baked) {
+ param_is_baked = 0;
+
+ bh_arr(AstPolyParam) pv = *parser->polymorph_context.poly_params;
+ bh_arr_push(pv, ((AstPolyParam) {
+ .kind = PPK_Baked_Value,
+ .idx = param_idx,
+
+ .poly_sym = (AstNode *) curr_param.local,
+ .type_expr = curr_param.local->type_node,
+ }));
+
+ *parser->polymorph_context.poly_params = pv;
+ }
+
+ bh_arr_push(func->params, curr_param);
+ param_idx++;
+
+ if (parser->curr->type != ')')
+ expect_token(parser, ',');
+ }
+ return;
+}
+
+static AstOverloadedFunction* parse_overloaded_function(OnyxParser* parser, OnyxToken* token) {
+ expect_token(parser, '{');
+
+ AstOverloadedFunction* ofunc = make_node(AstOverloadedFunction, Ast_Kind_Overloaded_Function);
+ ofunc->token = token;
+ ofunc->flags |= Ast_Flag_Comptime;
+
+ bh_arr_new(global_heap_allocator, ofunc->overloads, 4);
+
+ u64 precedence = 0;
+ while (!consume_token_if_next(parser, '}')) {
+ if (parser->hit_unexpected_token) return ofunc;
+
+ if (parse_possible_directive(parser, "precedence")) {
+ AstNumLit* pre = parse_int_literal(parser);
+ if (parser->hit_unexpected_token) return ofunc;
+
+ precedence = bh_max(pre->value.l, 0);
+ }
+
+ AstTyped* option = parse_expression(parser, 0);
+ add_overload_option(&ofunc->overloads, precedence++, option);
+
+ if (parser->curr->type != '}')
+ expect_token(parser, ',');
+ }
+
+ ENTITY_SUBMIT(ofunc);
+ return ofunc;
+}
+
+static AstFunction* parse_function_definition(OnyxParser* parser, OnyxToken* token) {
+ AstFunction* func_def = make_node(AstFunction, Ast_Kind_Function);
+ func_def->token = token;
+
+ bh_arr_new(global_heap_allocator, func_def->params, 4);
+
+ bh_arr(AstPolyParam) polymorphic_vars = NULL;
+ bh_arr_new(global_heap_allocator, polymorphic_vars, 4);
+
+ parser->polymorph_context.poly_params = &polymorphic_vars;
+ parse_function_params(parser, func_def);
+ parser->polymorph_context.poly_params = NULL;
+
+ func_def->return_type = (AstType *) &basic_type_void;
+ if (consume_token_if_next(parser, Token_Type_Right_Arrow)) {
+ if (parse_possible_directive(parser, "auto")) {
+ func_def->return_type = (AstType *) &basic_type_auto_return;
+ } else {
+ func_def->return_type = parse_type(parser);
+ }
+ }
+
+ while (parser->curr->type == '#') {
+ if (parse_possible_directive(parser, "intrinsic")) {
+ func_def->flags |= Ast_Flag_Intrinsic;
+
+ if (parser->curr->type == Token_Type_Literal_String) {
+ func_def->intrinsic_name = expect_token(parser, Token_Type_Literal_String);
+ }
+ }
+
+ else if (parse_possible_directive(parser, "foreign")) {
+ func_def->foreign_module = expect_token(parser, Token_Type_Literal_String);
+ func_def->foreign_name = expect_token(parser, Token_Type_Literal_String);
+
+ func_def->flags |= Ast_Flag_Foreign;
+ }
+
+ // HACK: NullProcHack
+ else if (parse_possible_directive(parser, "null")) {
+ func_def->flags |= Ast_Flag_Proc_Is_Null;
+ }
+
+ else {
+ OnyxToken* directive_token = expect_token(parser, '#');
+ OnyxToken* symbol_token = expect_token(parser, Token_Type_Symbol);
+
+ onyx_report_error(directive_token->pos, "unknown directive '#%b'.", symbol_token->text, symbol_token->length);
+ }
+ }
+
+ func_def->body = parse_block(parser, 1);
+
+ if (bh_arr_length(polymorphic_vars) > 0) {
+ AstPolyProc* pp = make_node(AstPolyProc, Ast_Kind_Polymorphic_Proc);
+ pp->token = func_def->token;
+ pp->poly_params = polymorphic_vars;
+ pp->base_func = func_def;
+
+ return (AstFunction *) pp;
+
+ } else {
+ bh_arr_free(polymorphic_vars);
+ return func_def;
+ }
+}
+
+static b32 parse_possible_function_definition(OnyxParser* parser, AstTyped** ret) {
+ if (parser->curr->type == '(') {
+ OnyxToken* matching_paren = find_matching_paren(parser->curr);
+ if (matching_paren == NULL) return 0;
+
+ // :LinearTokenDependent
+ OnyxToken* token_after_paren = matching_paren + 1;
+ if (token_after_paren->type != Token_Type_Right_Arrow
+ && token_after_paren->type != '{'
+ && token_after_paren->type != Token_Type_Keyword_Do
+ && token_after_paren->type != Token_Type_Empty_Block)
+ return 0;
+
+ // :LinearTokenDependent
+ b32 is_params = (parser->curr + 1) == matching_paren;
+ OnyxToken* tmp_token = parser->curr;
+ while (!is_params && tmp_token < matching_paren) {
+ if (tmp_token->type == ':') is_params = 1;
+
+ tmp_token++;
+ }
+
+ if (!is_params) return 0;
+
+ OnyxToken* proc_token = parser->curr;
+ AstFunction* func_node = parse_function_definition(parser, proc_token);
+ ENTITY_SUBMIT(func_node);
+ *ret = (AstTyped *) func_node;
+ return 1;
+ }
+
+ return 0;
+}
+
+static b32 parse_possible_quick_function_definition(OnyxParser* parser, AstTyped** ret) {
+ if (parser->curr->type != '(') return 0;
+
+ OnyxToken* matching_paren = find_matching_paren(parser->curr);
+ if (matching_paren == NULL) return 0;
+
+ // :LinearTokenDependent
+ OnyxToken* token_after_paren = matching_paren + 1;
+ if (token_after_paren->type != '=' || (token_after_paren + 1)->type != '>')
+ return 0;
+
+ OnyxToken* proc_token = expect_token(parser, '(');
+
+ bh_arr(OnyxToken*) params=NULL;
+ bh_arr_new(global_heap_allocator, params, 4);
+
+ while (parser->curr->type != ')') {
+ if (parser->hit_unexpected_token) return 0;
+
+ bh_arr_push(params, expect_token(parser, Token_Type_Symbol));
+
+ if (parser->curr->type != ')') {
+ expect_token(parser, ',');
+ }
+ }
+
+ expect_token(parser, ')');
+ expect_token(parser, '=');
+ expect_token(parser, '>');
+
+ bh_arr(AstNode*) poly_params=NULL;
+ bh_arr_new(global_heap_allocator, poly_params, bh_arr_length(params));
+ bh_arr_each(OnyxToken*, param, params) {
+ char text[512];
+ memset(text, 0, 512);
+ strncat(text, "__type_", 511);
+ token_toggle_end(*param);
+ strncat(text, (*param)->text, 511);
+ token_toggle_end(*param);
+
+ OnyxToken* new_token = bh_alloc(parser->allocator, sizeof(OnyxToken));
+ new_token->type = Token_Type_Symbol;
+ new_token->length = 7 + (*param)->length;
+ new_token->text = bh_strdup(parser->allocator, text);
+ new_token->pos = (*param)->pos;
+
+ AstNode* type_node = make_symbol(parser->allocator, new_token);
+ bh_arr_push(poly_params, type_node);
+ }
+
+ AstFunction* func_node = make_node(AstFunction, Ast_Kind_Function);
+ AstPolyProc* poly_proc = make_node(AstPolyProc, Ast_Kind_Polymorphic_Proc);
+
+ bh_arr_new(global_heap_allocator, func_node->params, bh_arr_length(params));
+ fori (i, 0, bh_arr_length(params)) {
+ AstLocal* param_local = make_local(parser->allocator, params[i], (AstType *) poly_params[i]);
+ param_local->kind = Ast_Kind_Param;
+
+ bh_arr_push(func_node->params, ((AstParam) {
+ .local = param_local,
+ .default_value = NULL,
+
+ .vararg_kind = 0,
+ .use_processed = 0,
+ }));
+ }
+
+ AstBlock* body_block;
+ AstType* return_type;
+
+ if (parser->curr->type == '{') {
+ body_block = parse_block(parser, 1);
+ return_type = (AstType *) &basic_type_auto_return;
+
+ } else {
+ AstTyped* body = parse_compound_expression(parser, 0);
+
+ AstReturn* return_node = make_node(AstReturn, Ast_Kind_Return);
+ return_node->token = body->token;
+ return_node->expr = body;
+
+ body_block = make_node(AstBlock, Ast_Kind_Block);
+ body_block->token = body->token;
+ body_block->body = (AstNode *) return_node;
+
+ AstTypeOf* return_type_of = make_node(AstTypeOf, Ast_Kind_Typeof);
+ return_type_of->token = body->token;
+ return_type_of->expr = body;
+ return_type = (AstType *) return_type_of;
+ }
+
+ func_node->token = proc_token;
+ func_node->body = body_block;
+ func_node->return_type = (AstType *) return_type;
+
+ poly_proc->token = proc_token;
+ bh_arr_new(global_heap_allocator, poly_proc->poly_params, bh_arr_length(params));
+ fori (i, 0, bh_arr_length(params)) {
+ bh_arr_push(poly_proc->poly_params, ((AstPolyParam) {
+ .kind = PSK_Type,
+ .idx = i,
+ .poly_sym = poly_params[i],
+ .type_expr = (AstType *) poly_params[i],
+ .type = NULL,
+ }));
+ }
+ poly_proc->base_func = func_node;
+
+ ENTITY_SUBMIT(poly_proc);
+ *ret = (AstTyped *) poly_proc;
+
+ bh_arr_free(params);
+ bh_arr_free(poly_params);
+ return 1;
+}
+
+static AstTyped* parse_global_declaration(OnyxParser* parser) {
+ AstGlobal* global_node = make_node(AstGlobal, Ast_Kind_Global);
+ global_node->token = expect_token(parser, Token_Type_Keyword_Global);
+
+ while (parser->curr->type == '#') {
+ if (parse_possible_directive(parser, "foreign")) {
+ global_node->foreign_module = expect_token(parser, Token_Type_Literal_String);
+ global_node->foreign_name = expect_token(parser, Token_Type_Literal_String);
+
+ global_node->flags |= Ast_Flag_Foreign;
+ }
+
+ else {
+ OnyxToken* directive_token = expect_token(parser, '#');
+ OnyxToken* symbol_token = expect_token(parser, Token_Type_Symbol);
+
+ onyx_report_error(directive_token->pos, "unknown directive '#%b'.", symbol_token->text, symbol_token->length);
+ }
+ }
+
+ global_node->type_node = parse_type(parser);
+
+ ENTITY_SUBMIT(global_node);
+
+ return (AstTyped *) global_node;
+}
+
+static AstEnumType* parse_enum_declaration(OnyxParser* parser) {
+ AstEnumType* enum_node = make_node(AstEnumType, Ast_Kind_Enum_Type);
+ enum_node->token = expect_token(parser, Token_Type_Keyword_Enum);
+
+ bh_arr_new(global_heap_allocator, enum_node->values, 4);
+
+ while (parser->curr->type == '#') {
+ if (parse_possible_directive(parser, "flags")) {
+ enum_node->flags |= Ast_Flag_Enum_Is_Flags;
+ } else {
+ OnyxToken* directive_token = expect_token(parser, '#');
+ OnyxToken* symbol_token = expect_token(parser, Token_Type_Symbol);
+
+ onyx_report_error(directive_token->pos, "unknown directive '#%b'.", symbol_token->text, symbol_token->length);
+ }
+ }
+
+ AstType* backing = (AstType *) &basic_type_u32;
+ if (consume_token_if_next(parser, '(')) {
+ AstNode* backing_sym = make_node(AstNode, Ast_Kind_Symbol);
+ backing_sym->token = expect_token(parser, Token_Type_Symbol);
+ backing = (AstType *) backing_sym;
+
+ expect_token(parser, ')');
+ }
+ enum_node->backing = backing;
+
+ expect_token(parser, '{');
+
+ while (!consume_token_if_next(parser, '}')) {
+ if (parser->hit_unexpected_token) return enum_node;
+
+ AstEnumValue* evalue = make_node(AstEnumValue, Ast_Kind_Enum_Value);
+ evalue->token = expect_token(parser, Token_Type_Symbol);
+ evalue->type_node = (AstType *) enum_node;
+
+ if (consume_token_if_next(parser, ':')) {
+ expect_token(parser, ':');
+
+ // TODO: Make this work for any expression.
+ evalue->value = parse_int_literal(parser);
+ }
+
+ expect_token(parser, ';');
+
+ bh_arr_push(enum_node->values, evalue);
+ }
+
+ return enum_node;
+}
+
+static AstIf* parse_static_if_stmt(OnyxParser* parser, b32 parse_block_as_statements) {
+ AstIf* static_if_node = make_node(AstIf, Ast_Kind_Static_If);
+ static_if_node->token = expect_token(parser, '#');
+ expect_token(parser, Token_Type_Keyword_If);
+
+ static_if_node->cond = parse_expression(parser, 0);
+
+ bh_arr_new(global_heap_allocator, static_if_node->true_entities, 2);
+ bh_arr_push(parser->alternate_entity_placement_stack, &static_if_node->true_entities);
+
+ if (parse_block_as_statements) {
+ static_if_node->true_stmt = parse_block(parser, 0);
+
+ } else {
+ expect_token(parser, '{');
+ while (!consume_token_if_next(parser, '}')) {
+ if (parser->hit_unexpected_token) return static_if_node;
+
+ parse_top_level_statement(parser);
+ }
+ }
+
+ bh_arr_pop(parser->alternate_entity_placement_stack);
+
+ if (consume_token_if_next(parser, Token_Type_Keyword_Else)) {
+ bh_arr_new(global_heap_allocator, static_if_node->false_entities, 2);
+ bh_arr_push(parser->alternate_entity_placement_stack, &static_if_node->false_entities);
+
+ if (parse_block_as_statements) {
+ static_if_node->false_stmt = parse_block(parser, 0);
+
+ } else {
+ expect_token(parser, '{');
+ while (!consume_token_if_next(parser, '}')) {
+ if (parser->hit_unexpected_token) return static_if_node;
+
+ parse_top_level_statement(parser);
+ }
+ }
+
+ bh_arr_pop(parser->alternate_entity_placement_stack);
+ }
+
+ return static_if_node;
+}
+
+static AstMacro* parse_macro(OnyxParser* parser) {
+ AstMacro* macro = make_node(AstMacro, Ast_Kind_Macro);
+ macro->token = expect_token(parser, Token_Type_Keyword_Macro);
+
+ // First try quick function
+ if (!parse_possible_quick_function_definition(parser, ¯o->body)) {
+ // Otherwise, do a normal function
+ macro->body = (AstTyped *) parse_function_definition(parser, macro->token);
+ }
+
+ ENTITY_SUBMIT(macro);
+ return macro;
+}
+
+static AstTyped* parse_top_level_expression(OnyxParser* parser) {
+ if (parser->curr->type == Token_Type_Keyword_Global) return parse_global_declaration(parser);
+ if (parser->curr->type == Token_Type_Keyword_Struct) return (AstTyped *) parse_struct(parser);
+ if (parser->curr->type == Token_Type_Keyword_Enum) return (AstTyped *) parse_enum_declaration(parser);
+ if (parser->curr->type == Token_Type_Keyword_Macro) return (AstTyped *) parse_macro(parser);
+
+ if (parse_possible_directive(parser, "type")) {
+ AstTypeAlias* alias = make_node(AstTypeAlias, Ast_Kind_Type_Alias);
+ alias->to = parse_type(parser);
+ return (AstTyped *) alias;
+ }
+
+ if (parse_possible_directive(parser, "match")) {
+ // :LinearTokenDependent
+ OnyxToken* directive_token = parser->curr - 2;
+ AstOverloadedFunction* ofunc = parse_overloaded_function(parser, directive_token);
+ return (AstTyped *) ofunc;
+ }
+
+ return parse_expression(parser, 1);
+}
+
+static AstBinding* parse_top_level_binding(OnyxParser* parser, OnyxToken* symbol) {
+ expect_token(parser, ':');
+
+ AstTyped* node = parse_top_level_expression(parser);
+ if (parser->hit_unexpected_token || node == NULL)
+ return NULL;
+
+ // CLEANUP
+ if (node->kind == Ast_Kind_Function) {
+ AstFunction* func = (AstFunction *) node;
+
+ if (func->intrinsic_name == NULL)
+ func->intrinsic_name = symbol;
+
+ func->name = symbol;
+
+ } else if (node->kind == Ast_Kind_Polymorphic_Proc) {
+ AstPolyProc* proc = (AstPolyProc *) node;
+
+ if (proc->base_func->intrinsic_name == NULL)
+ proc->base_func->intrinsic_name = symbol;
+
+ proc->base_func->name = symbol;
+
+ } else if (node->kind == Ast_Kind_Macro) {
+ AstMacro* macro = (AstMacro *) node;
+
+ AstFunction* func = (AstFunction *) macro->body;
+ if (func->kind == Ast_Kind_Polymorphic_Proc)
+ func = (AstFunction *) ((AstPolyProc *) func)->base_func;
+
+ func->name = symbol;
+
+ } else if (node->kind == Ast_Kind_Global) {
+ AstGlobal* global = (AstGlobal *) node;
+
+ global->name = symbol;
+
+ } else if (node->kind != Ast_Kind_Overloaded_Function
+ && node->kind != Ast_Kind_StrLit) {
+
+ if (node->kind == Ast_Kind_Struct_Type
+ || node->kind == Ast_Kind_Enum_Type
+ || node->kind == Ast_Kind_Poly_Struct_Type) {
+ ((AstStructType *)node)->name = bh_aprintf(global_heap_allocator,
+ "%b", symbol->text, symbol->length);
+ }
+
+ if (node->kind == Ast_Kind_Type_Alias) node->token = symbol;
+ else if (node_is_type((AstNode *) node));
+ else if (node->kind == Ast_Kind_Package);
+ else if (node->kind == Ast_Kind_NumLit);
+ else {
+ AstAlias* alias = make_node(AstAlias, Ast_Kind_Alias);
+ alias->token = node->token;
+ alias->alias = node;
+ node = (AstTyped *) alias;
+ }
+
+ // HACK: This should maybe be entered elsewhere?
+ ENTITY_SUBMIT(node);
+ }
+
+ AstBinding* binding = make_node(AstBinding, Ast_Kind_Binding);
+ binding->token = symbol;
+ binding->node = (AstNode *) node;
+
+ return binding;
+}
+
+static void parse_top_level_statement(OnyxParser* parser) {
+ AstFlags private_kind = 0;
+ if (bh_arr_length(parser->scope_flags) > 0)
+ private_kind = bh_arr_last(parser->scope_flags);
+
+ // :CLEANUP this very repetetive code...
+ if (parse_possible_directive(parser, "private")) {
+ private_kind = Ast_Flag_Private_Package;
+ if (parser->curr->type == '{') {
+ bh_arr_push(parser->scope_flags, private_kind);
+
+ expect_token(parser, '{');
+ parse_top_level_statements_until(parser, '}');
+ expect_token(parser, '}');
+
+ bh_arr_pop(parser->scope_flags);
+ return;
+ }
+ }
+ else if (parse_possible_directive(parser, "private_file")) {
+ private_kind = Ast_Flag_Private_File;
+ if (parser->curr->type == '{') {
+ bh_arr_push(parser->scope_flags, private_kind);
+
+ expect_token(parser, '{');
+ parse_top_level_statements_until(parser, '}');
+ expect_token(parser, '}');
+
+ bh_arr_pop(parser->scope_flags);
+ return;
+ }
+ }
+
+ AstBinding* binding = NULL;
+
+ switch ((u16) parser->curr->type) {
+ case Token_Type_Keyword_Use: {
+ AstNode* use_node = parse_use_stmt(parser);
+ if (use_node) ENTITY_SUBMIT(use_node);
+ return;
+ }
+
+ case Token_Type_Symbol: {
+ OnyxToken* symbol = expect_token(parser, Token_Type_Symbol);
+ expect_token(parser, ':');
+
+ if (parser->curr->type == ':') {
+ binding = parse_top_level_binding(parser, symbol);
+ if (binding != NULL) binding->flags |= private_kind;
+
+ goto submit_binding_to_entities;
+ }
+
+ AstMemRes* memres = make_node(AstMemRes, Ast_Kind_Memres);
+ memres->token = symbol;
+
+ if (parser->curr->type != '=')
+ memres->type_node = parse_type(parser);
+
+ if (consume_token_if_next(parser, '='))
+ memres->initial_value = parse_expression(parser, 1);
+
+
+ ENTITY_SUBMIT(memres);
+
+ binding = make_node(AstBinding, Ast_Kind_Binding);
+ binding->token = symbol;
+ binding->flags |= private_kind;
+ binding->node = (AstNode *) memres;
+
+ goto submit_binding_to_entities;
+ }
+
+ case '#': {
+ if (next_tokens_are(parser, 2, '#', Token_Type_Keyword_If)) {
+ AstIf* static_if = parse_static_if_stmt(parser, 0);
+ ENTITY_SUBMIT(static_if);
+ return;
+ }
+
+ OnyxToken* dir_token = parser->curr;
+
+ if (parse_possible_directive(parser, "load")) {
+ AstInclude* include = make_node(AstInclude, Ast_Kind_Load_File);
+ include->token = dir_token;
+
+ OnyxToken* str_token = expect_token(parser, Token_Type_Literal_String);
+ if (str_token != NULL) {
+ token_toggle_end(str_token);
+ include->name = bh_strdup(parser->allocator, str_token->text);
+ token_toggle_end(str_token);
+ }
+
+ ENTITY_SUBMIT(include);
+ return;
+ }
+ else if (parse_possible_directive(parser, "load_path")) {
+ AstInclude* include = make_node(AstInclude, Ast_Kind_Load_Path);
+ include->token = dir_token;
+
+ OnyxToken* str_token = expect_token(parser, Token_Type_Literal_String);
+ if (str_token != NULL) {
+ token_toggle_end(str_token);
+ include->name = bh_strdup(parser->allocator, str_token->text);
+ token_toggle_end(str_token);
+ }
+
+ ENTITY_SUBMIT(include);
+ return;
+ }
+ else if (parse_possible_directive(parser, "error")) {
+ AstDirectiveError *error = make_node(AstDirectiveError, Ast_Kind_Directive_Error);
+ error->token = dir_token;
+ error->error_msg = expect_token(parser, Token_Type_Literal_String);
+
+ ENTITY_SUBMIT(error);
+ return;
+ }
+ else if (parse_possible_directive(parser, "operator")) {
+ AstDirectiveOperator *operator = make_node(AstDirectiveOperator, Ast_Kind_Directive_Operator);
+ operator->token = dir_token;
+
+ BinaryOp op = binary_op_from_token_type(parser->curr->type);
+ consume_token(parser);
+ if (op == Binary_Op_Subscript) expect_token(parser, ']'); // #operator [] ... needs to consume the other ']'
+
+ if (op == Binary_Op_Count) {
+ onyx_report_error(parser->curr->pos, "Invalid binary operator.");
+ } else {
+ operator->operator = op;
+ }
+
+ operator->overload = parse_expression(parser, 0);
+
+ ENTITY_SUBMIT(operator);
+ return;
+ }
+ else if (parse_possible_directive(parser, "add_overload") || parse_possible_directive(parser, "add_match")) {
+ AstDirectiveAddOverload *add_overload = make_node(AstDirectiveAddOverload, Ast_Kind_Directive_Add_Overload);
+ add_overload->token = dir_token;
+ add_overload->overloaded_function = (AstNode *) parse_expression(parser, 0);
+
+ expect_token(parser, ',');
+
+ if (parse_possible_directive(parser, "precedence")) {
+ AstNumLit* pre = parse_int_literal(parser);
+ if (parser->hit_unexpected_token) return;
+
+ add_overload->precedence = bh_max(pre->value.l, 0);
+ } else {
+ add_overload->precedence = 0;
+ }
+
+ add_overload->overload = parse_expression(parser, 0);
+
+ ENTITY_SUBMIT(add_overload);
+ return;
+ }
+ else if (parse_possible_directive(parser, "export")) {
+ AstDirectiveExport *export = make_node(AstDirectiveExport, Ast_Kind_Directive_Export);
+ export->token = dir_token;
+ export->export_name = expect_token(parser, Token_Type_Literal_String);
+
+ export->export = parse_expression(parser, 0);
+
+ ENTITY_SUBMIT(export);
+ return;
+ }
+ else {
+ OnyxToken* directive_token = expect_token(parser, '#');
+ OnyxToken* symbol_token = expect_token(parser, Token_Type_Symbol);
+
+ onyx_report_error(directive_token->pos, "unknown directive '#%b'.", symbol_token->text, symbol_token->length);
+ return;
+ }
+ }
+
+ default: break;
+ }
+
+ expect_token(parser, ';');
+ return;
+
+submit_binding_to_entities:
+ {
+ if (!binding) return;
+
+ Scope* target_scope = parser->package->scope;
+
+ if (binding->flags & Ast_Flag_Private_Package)
+ target_scope = parser->package->private_scope;
+ if (binding->flags & Ast_Flag_Private_File)
+ target_scope = parser->file_scope;
+
+ ENTITY_SUBMIT_IN_SCOPE(binding, target_scope);
+ }
+}
+
+static AstPackage* parse_package_expression(OnyxParser* parser) {
+ AstPackage* package_node = make_node(AstPackage, Ast_Kind_Package);
+ package_node->token = expect_token(parser, Token_Type_Keyword_Package);
+
+ bh_arr_new(global_heap_allocator, package_node->path, 2);
+
+ while (parser->curr->type == Token_Type_Symbol) {
+ if (parser->hit_unexpected_token) return package_node;
+
+ OnyxToken* symbol = expect_token(parser, Token_Type_Symbol);
+
+ bh_arr_push(package_node->path, symbol);
+
+ if (consume_token_if_next(parser, '.'));
+ else break;
+ }
+
+ i32 total_package_name_length = 0;
+ bh_arr_each(OnyxToken *, token, package_node->path) {
+ total_package_name_length += (*token)->length + 1;
+ }
+
+ char* package_name = bh_alloc_array(context.ast_alloc, char, total_package_name_length);
+ *package_name = '\0';
+
+ bh_arr_each(OnyxToken *, token, package_node->path) {
+ token_toggle_end(*token);
+ strncat(package_name, (*token)->text, total_package_name_length - 1);
+ token_toggle_end(*token);
+
+ if (token != &bh_arr_last(package_node->path)) {
+ strncat(package_name, ".", total_package_name_length - 1);
+ }
+ }
+
+ package_node->package_name = package_name;
+ package_node->package = package_lookup(package_name);
+
+ return package_node;
+}
+
+static Package* parse_file_package(OnyxParser* parser) {
+ if (parser->curr->type != Token_Type_Keyword_Package) {
+ return package_lookup_or_create("main", context.global_scope, parser->allocator);
+ }
+
+ AstPackage* package_node = parse_package_expression(parser);
+
+ char aggregate_name[2048];
+ aggregate_name[0] = '\0';
+
+ Package* prevpackage = NULL;
+
+ bh_arr_each(OnyxToken *, symbol, package_node->path) {
+ token_toggle_end(*symbol);
+
+ strncat(aggregate_name, (*symbol)->text, 2047);
+ Package* newpackage = package_lookup_or_create(aggregate_name, context.global_scope, parser->allocator);
+
+ AstPackage* pnode = make_node(AstPackage, Ast_Kind_Package);
+ pnode->token = *symbol;
+ pnode->package = newpackage;
+ pnode->package_name = newpackage->name;
+
+ if (prevpackage != NULL) {
+ symbol_subpackage_introduce(prevpackage->scope, (*symbol)->text, pnode);
+ package_reinsert_use_packages(prevpackage);
+ }
+
+ token_toggle_end(*symbol);
+ strncat(aggregate_name, ".", 2047);
+
+ prevpackage = newpackage;
+ }
+
+ package_node->package = prevpackage;
+
+ return package_node->package;
+}
+
+static void parse_top_level_statements_until(OnyxParser* parser, TokenType tt) {
+ while (parser->curr->type != tt) {
+ if (parser->hit_unexpected_token) break;
+ if (onyx_has_errors()) break;
+ parse_top_level_statement(parser);
+ }
+}
+
+
+// NOTE: This returns a void* so I don't need to cast it everytime I use it
+void* onyx_ast_node_new(bh_allocator alloc, i32 size, AstKind kind) {
+ void* node = bh_alloc(alloc, size);
+
+ memset(node, 0, size);
+ *(AstKind *) node = kind;
+
+ return node;
+}
+
+OnyxParser onyx_parser_create(bh_allocator alloc, OnyxTokenizer *tokenizer) {
+ OnyxParser parser;
+
+ parser.allocator = alloc;
+ parser.tokenizer = tokenizer;
+ parser.curr = tokenizer->tokens;
+ parser.prev = NULL;
+ parser.hit_unexpected_token = 0;
+ parser.current_scope = NULL;
+ parser.alternate_entity_placement_stack = NULL;
+ parser.scope_flags = NULL;
+
+ parser.polymorph_context = (PolymorphicContext) {
+ .root_node = NULL,
+ .poly_params = NULL,
+ };
+
+ bh_arr_new(global_heap_allocator, parser.alternate_entity_placement_stack, 4);
+ bh_arr_new(global_heap_allocator, parser.scope_flags, 4);
+
+ return parser;
+}
+
+void onyx_parser_free(OnyxParser* parser) {
+}
+
+void onyx_parse(OnyxParser *parser) {
+ // NOTE: Skip comments at the beginning of the file
+ while (consume_token_if_next(parser, Token_Type_Comment) || consume_token_if_next(parser, Token_Type_Note));
+
+ parser->package = parse_file_package(parser);
+ parser->file_scope = scope_create(parser->allocator, parser->package->private_scope, parser->tokenizer->tokens[0].pos);
+ parser->current_scope = parser->file_scope;
+
+ AstUse* implicit_use_builtin = make_node(AstUse, Ast_Kind_Use);
+ AstPackage* implicit_builtin_package = make_node(AstPackage, Ast_Kind_Package);
+ implicit_builtin_package->package_name = "builtin";
+ implicit_use_builtin->expr = (AstTyped *) implicit_builtin_package;
+ ENTITY_SUBMIT(implicit_use_builtin);
+
+ parse_top_level_statements_until(parser, Token_Type_End_Stream);
+
+ parser->current_scope = parser->current_scope->parent;
+}
--- /dev/null
+
+//
+// Polymorphic Procedures
+//
+
+// This flag is used by some of the procedures that try working with polymorphic things,
+// but need to wait until more information is known. Instead of passing a out parameter
+// into each of these procedures, a single global variable is used instead. If the type
+// checker ever gets multi-threaded, this would have to become a threadlocal variable.
+static b32 flag_to_yield = 0;
+
+// The name is pretty self-descriptive, but this is a node that is returned from things
+// like polymorphic_proc_lookup when it is determined that everything works so far, but
+// the caller must yield in order to finish checking this polymorphic procedure.
+AstTyped node_that_signals_a_yield = { Ast_Kind_Function, 0 };
+
+static void ensure_polyproc_cache_is_created(AstPolyProc* pp) {
+ if (pp->concrete_funcs == NULL) {
+ bh_table_init(global_heap_allocator, pp->concrete_funcs, 16);
+ }
+}
+
+static void insert_poly_slns_into_scope(Scope* scope, bh_arr(AstPolySolution) slns) {
+ bh_arr_each(AstPolySolution, sln, slns) {
+ AstNode *node = NULL;
+
+ switch (sln->kind) {
+ case PSK_Type:
+ node = onyx_ast_node_new(context.ast_alloc, sizeof(AstTypeRawAlias), Ast_Kind_Type_Raw_Alias);
+ ((AstTypeRawAlias *) node)->token = sln->poly_sym->token;
+ ((AstTypeRawAlias *) node)->to = sln->type;
+ break;
+
+ case PSK_Value:
+ // CLEANUP: Maybe clone this?
+ assert(sln->value->flags & Ast_Flag_Comptime);
+ node = (AstNode *) sln->value;
+ break;
+ }
+
+ symbol_introduce(scope, sln->poly_sym->token, node);
+ }
+}
+
+// NOTE: This might return a volatile string. Do not store it without copying it.
+static char* build_poly_solution_key(AstPolySolution* sln) {
+ if (sln->kind == PSK_Type) {
+ return (char *) type_get_unique_name(sln->type);
+
+ } else if (sln->kind == PSK_Value) {
+ static char buffer[256];
+
+ fori (i, 0, 256) buffer[i] = 0;
+
+ if (sln->value->kind == Ast_Kind_NumLit) {
+ strncat(buffer, "NUMLIT:", 127);
+ strncat(buffer, bh_bprintf("%l", ((AstNumLit *) sln->value)->value.l), 127);
+
+ } else {
+ // HACK: For now, the value pointer is just used. This means that
+ // sometimes, even through the solution is the same, it won't be
+ // stored the same.
+ bh_snprintf(buffer, 128, "%p", sln->value);
+ }
+
+ return buffer;
+ }
+
+ return NULL;
+}
+
+// NOTE: This returns a volatile string. Do not store it without copying it.
+static char* build_poly_slns_unique_key(bh_arr(AstPolySolution) slns) {
+ static char key_buf[1024];
+ fori (i, 0, 1024) key_buf[i] = 0;
+
+ bh_arr_each(AstPolySolution, sln, slns) {
+ token_toggle_end(sln->poly_sym->token);
+
+ strncat(key_buf, sln->poly_sym->token->text, 1023);
+ strncat(key_buf, "=", 1023);
+ strncat(key_buf, build_poly_solution_key(sln), 1023);
+ strncat(key_buf, ";", 1023);
+
+ token_toggle_end(sln->poly_sym->token);
+ }
+
+ return key_buf;
+}
+
+// NOTE: This function adds a solidified function to the entity heap for it to be processed
+// later. It optionally can start the function header entity at the code generation state if
+// the header has already been processed.
+static b32 add_solidified_function_entities(AstSolidifiedFunction solidified_func, b32 header_already_processed) {
+ solidified_func.func->flags |= Ast_Flag_Function_Used;
+ solidified_func.func->flags |= Ast_Flag_From_Polymorphism;
+
+ EntityState header_start_state = Entity_State_Resolve_Symbols;
+ if (header_already_processed) header_start_state = Entity_State_Code_Gen;
+
+ Entity func_header_entity = {
+ .state = header_start_state,
+ .type = Entity_Type_Function_Header,
+ .function = solidified_func.func,
+ .package = NULL,
+ .scope = solidified_func.poly_scope,
+ };
+
+ entity_bring_to_state(&func_header_entity, Entity_State_Code_Gen);
+ if (onyx_has_errors()) return 0;
+
+ Entity func_entity = {
+ .state = Entity_State_Resolve_Symbols,
+ .type = Entity_Type_Function,
+ .function = solidified_func.func,
+ .package = NULL,
+ .scope = solidified_func.poly_scope,
+ };
+
+ Entity* entity_header = entity_heap_insert(&context.entities, func_header_entity);
+ Entity* entity_body = entity_heap_insert(&context.entities, func_entity);
+
+ solidified_func.func->entity_header = entity_header;
+ solidified_func.func->entity_body = entity_body;
+
+ return 1;
+}
+
+// NOTE: This function is responsible for taking all of the information about generating
+// a new polymorphic variant, and producing a solidified function. It optionally can only
+// generate the header of the function, which is useful for cases such as checking if a
+// set of arguments works for a polymorphic overload option.
+static AstSolidifiedFunction generate_solidified_function(
+ AstPolyProc* pp,
+ bh_arr(AstPolySolution) slns,
+ OnyxToken* tkn,
+ b32 header_only) {
+
+ AstSolidifiedFunction solidified_func;
+ solidified_func.header_complete = 0;
+
+ // NOTE: Use the position of token if one was provided, otherwise just use NULL.
+ OnyxFilePos poly_scope_pos = { 0 };
+ if (tkn) poly_scope_pos = tkn->pos;
+
+ solidified_func.poly_scope = scope_create(context.ast_alloc, pp->poly_scope, poly_scope_pos);
+ insert_poly_slns_into_scope(solidified_func.poly_scope, slns);
+
+ if (header_only) {
+ solidified_func.func = (AstFunction *) clone_function_header(context.ast_alloc, pp->base_func);
+ solidified_func.func->flags |= Ast_Flag_Incomplete_Body;
+
+ } else {
+ solidified_func.func = (AstFunction *) ast_clone(context.ast_alloc, pp->base_func);
+ }
+
+ solidified_func.func->flags |= Ast_Flag_From_Polymorphism;
+ solidified_func.func->generated_from = tkn;
+
+ // HACK: Remove the baked parameters from the function defintion so they can be
+ // resolved in the poly scope above the function. This does feel kinda of gross
+ // and I would love an alternative to tell it to just "skip" the parameter, but
+ // that is liable to breaking because it is one more thing to remember.
+ // - brendanfh 2021/01/18
+ u32 removed_params = 0;
+ bh_arr_each(AstPolyParam, param, pp->poly_params) {
+ if (param->kind != PPK_Baked_Value) continue;
+
+ bh_arr_deleten(solidified_func.func->params, param->idx - removed_params, 1);
+ removed_params++;
+ }
+
+ return solidified_func;
+}
+
+static void ensure_solidified_function_has_body(AstPolyProc* pp, AstSolidifiedFunction solidified_func) {
+ if (solidified_func.func->flags & Ast_Flag_Incomplete_Body) {
+ clone_function_body(context.ast_alloc, solidified_func.func, pp->base_func);
+
+ // HACK: I'm asserting that this function should return without an error, because
+ // the only case where it can return an error is if there was a problem with the
+ // header. This should never be the case in this situation, since the header would
+ // have to have successfully passed type checking before it would become a solidified
+ // procedure.
+ assert(add_solidified_function_entities(solidified_func, 1));
+
+ solidified_func.func->flags &= ~Ast_Flag_Incomplete_Body;
+ }
+}
+
+// NOTE: These are temporary data structures used to represent the pattern matching system
+// of polymorphic type resolution.
+typedef struct PolySolveResult {
+ PolySolutionKind kind;
+ union {
+ AstTyped* value;
+ Type* actual;
+ };
+} PolySolveResult;
+
+typedef struct PolySolveElem {
+ AstType* type_expr;
+
+ PolySolutionKind kind;
+ union {
+ AstTyped* value;
+ Type* actual;
+ };
+} PolySolveElem;
+
+// NOTE: The job of this function is to solve for the type/value that belongs in a
+// polymorphic variable. This function takes in three arguments:
+// * The symbol node of the polymorphic parameter being searched for
+// * The type expression that should contain the symbol node it is some where
+// * The actual type to pattern match against
+//
+// This function utilizes a basic breadth-first search of the type_expr and actual type
+// trees, always moving along them in parallel, so when the target is reached (if it is
+// ever reached), the "actual" is the matched type/value.
+static PolySolveResult solve_poly_type(AstNode* target, AstType* type_expr, Type* actual) {
+ bh_arr(PolySolveElem) elem_queue = NULL;
+ bh_arr_new(global_heap_allocator, elem_queue, 4);
+
+ PolySolveResult result = { PSK_Undefined, { NULL } };
+
+ bh_arr_push(elem_queue, ((PolySolveElem) {
+ .type_expr = type_expr,
+ .kind = PSK_Type,
+ .actual = actual
+ }));
+
+ while (!bh_arr_is_empty(elem_queue)) {
+ PolySolveElem elem = elem_queue[0];
+ bh_arr_deleten(elem_queue, 0, 1);
+
+ if (elem.type_expr == (AstType *) target) {
+ result.kind = elem.kind;
+
+ assert(elem.kind != PSK_Undefined);
+ if (result.kind == PSK_Type) result.actual = elem.actual;
+ if (result.kind == PSK_Value) result.value = elem.value;
+ break;
+ }
+
+ if (elem.kind != PSK_Type) continue;
+
+ switch (elem.type_expr->kind) {
+ case Ast_Kind_Pointer_Type: {
+ if (elem.actual->kind != Type_Kind_Pointer) break;
+
+ bh_arr_push(elem_queue, ((PolySolveElem) {
+ .type_expr = ((AstPointerType *) elem.type_expr)->elem,
+ .kind = PSK_Type,
+ .actual = elem.actual->Pointer.elem,
+ }));
+ break;
+ }
+
+ case Ast_Kind_Array_Type: {
+ if (elem.actual->kind != Type_Kind_Array) break;
+
+ bh_arr_push(elem_queue, ((PolySolveElem) {
+ .type_expr = (AstType*) ((AstArrayType *) elem.type_expr)->count_expr,
+ .kind = PSK_Value,
+
+ // CLEANUP: Making an integer literal every time is very very very gross. This should
+ // at least be cached or something.
+ .value = (AstTyped *) make_int_literal(context.ast_alloc, elem.actual->Array.count)
+ }));
+
+ bh_arr_push(elem_queue, ((PolySolveElem) {
+ .type_expr = ((AstArrayType *) elem.type_expr)->elem,
+ .kind = PSK_Type,
+ .actual = elem.actual->Array.elem,
+ }));
+ break;
+ }
+
+ case Ast_Kind_Slice_Type: {
+ if (elem.actual->kind != Type_Kind_Slice) break;
+
+ bh_arr_push(elem_queue, ((PolySolveElem) {
+ .type_expr = ((AstSliceType *) elem.type_expr)->elem,
+ .kind = PSK_Type,
+ .actual = elem.actual->Slice.ptr_to_data->Pointer.elem,
+ }));
+ break;
+ }
+
+ case Ast_Kind_DynArr_Type: {
+ if (elem.actual->kind != Type_Kind_DynArray) break;
+
+ bh_arr_push(elem_queue, ((PolySolveElem) {
+ .type_expr = ((AstDynArrType *) elem.type_expr)->elem,
+ .kind = PSK_Type,
+ .actual = elem.actual->DynArray.ptr_to_data->Pointer.elem,
+ }));
+ break;
+ }
+
+ case Ast_Kind_VarArg_Type:
+ bh_arr_push(elem_queue, ((PolySolveElem) {
+ .type_expr = ((AstVarArgType *) elem.type_expr)->elem,
+ .kind = PSK_Type,
+ .actual = actual,
+ }));
+ break;
+
+ case Ast_Kind_Function_Type: {
+ if (elem.actual->kind != Type_Kind_Function) break;
+
+ AstFunctionType* ft = (AstFunctionType *) elem.type_expr;
+
+ fori (i, 0, (i64) ft->param_count) {
+ bh_arr_push(elem_queue, ((PolySolveElem) {
+ .type_expr = ft->params[i],
+ .kind = PSK_Type,
+ .actual = elem.actual->Function.params[i],
+ }));
+ }
+
+ bh_arr_push(elem_queue, ((PolySolveElem) {
+ .type_expr = ft->return_type,
+ .kind = PSK_Type,
+ .actual = elem.actual->Function.return_type,
+ }));
+
+ break;
+ }
+
+ case Ast_Kind_Poly_Call_Type: {
+ if (elem.actual->kind != Type_Kind_Struct) break;
+ if (bh_arr_length(elem.actual->Struct.poly_sln) != bh_arr_length(((AstPolyCallType *) elem.type_expr)->params)) break;
+
+ AstPolyCallType* pt = (AstPolyCallType *) elem.type_expr;
+
+ fori (i, 0, bh_arr_length(pt->params)) {
+ PolySolutionKind kind = elem.actual->Struct.poly_sln[i].kind;
+ if (kind == PSK_Type) {
+ bh_arr_push(elem_queue, ((PolySolveElem) {
+ .kind = kind,
+ .type_expr = (AstType *) pt->params[i],
+ .actual = elem.actual->Struct.poly_sln[i].type,
+ }));
+ } else {
+ bh_arr_push(elem_queue, ((PolySolveElem) {
+ .kind = kind,
+ .type_expr = (AstType *) pt->params[i],
+ .value = elem.actual->Struct.poly_sln[i].value,
+ }));
+ }
+ }
+
+ break;
+ }
+
+ case Ast_Kind_Type_Compound: {
+ if (elem.actual->kind != Type_Kind_Compound) break;
+ if (elem.actual->Compound.count != (u32) bh_arr_length(((AstCompoundType *) elem.type_expr)->types)) break;
+
+ AstCompoundType* ct = (AstCompoundType *) elem.type_expr;
+
+ fori (i, 0, bh_arr_length(ct->types)) {
+ bh_arr_push(elem_queue, ((PolySolveElem) {
+ .kind = PSK_Type,
+ .type_expr = ct->types[i],
+ .actual = elem.actual->Compound.types[i],
+ }));
+ }
+
+ break;
+ }
+
+ default: break;
+ }
+ }
+
+ bh_arr_free(elem_queue);
+
+ return result;
+}
+
+// NOTE: The job of this function is to take a polymorphic parameter and a set of arguments
+// and solve for the argument that matches the parameter. This is needed because polymorphic
+// procedure resolution has to happen before the named arguments are placed in their correct
+// positions.
+static AstTyped* lookup_param_in_arguments(AstPolyProc* pp, AstPolyParam* param, Arguments* args, char** err_msg) {
+ bh_arr(AstTyped *) arg_arr = args->values;
+ bh_arr(AstNamedValue *) named_values = args->named_values;
+
+ // NOTE: This check is safe because currently the arguments given without a name
+ // always map to the beginning indidies of the argument array.
+ if (param->idx >= (u64) bh_arr_length(arg_arr)) {
+ OnyxToken* param_name = pp->base_func->params[param->idx].local->token;
+
+ bh_arr_each(AstNamedValue *, named_value, named_values) {
+ if (token_equals(param_name, (*named_value)->token)) {
+ return (AstTyped *) (*named_value)->value;
+ }
+ }
+
+ // CLEANUP
+ if (err_msg) *err_msg = "Not enough arguments to polymorphic procedure. This error message may not be entirely right.";
+
+ } else {
+ return (AstTyped *) arg_arr[param->idx];
+ }
+
+ return NULL;
+}
+
+// NOTE: The job of this function is to solve for type of AstPolySolution using the provided
+// information. It is asssumed that the "param" is of kind PPK_Poly_Type. This function uses
+// either the arguments provided, or a function type to compare against to pattern match for
+// the type that the parameter but be.
+static void solve_for_polymorphic_param_type(PolySolveResult* resolved, AstPolyProc* pp, AstPolyParam* param, PolyProcLookupMethod pp_lookup, ptr actual, char** err_msg) {
+ Type* actual_type = NULL;
+
+ switch (pp_lookup) {
+ case PPLM_By_Arguments: {
+ Arguments* args = (Arguments *) actual;
+
+ AstTyped* typed_param = lookup_param_in_arguments(pp, param, args, err_msg);
+ if (typed_param == NULL) return;
+
+ actual_type = resolve_expression_type(typed_param);
+ if (actual_type == NULL) return;
+
+ break;
+ }
+
+ case PPLM_By_Function_Type: {
+ Type* ft = (Type *) actual;
+ if (param->idx >= ft->Function.param_count) {
+ if (err_msg) *err_msg = "Incompatible polymorphic argument to function parameter.";
+ return;
+ }
+
+ actual_type = ft->Function.params[param->idx];
+ break;
+ }
+
+ default: return;
+ }
+
+ *resolved = solve_poly_type(param->poly_sym, param->type_expr, actual_type);
+}
+
+
+// NOTE: The job of this function is to look through the arguments provided and find a matching
+// value that is to be baked into the polymorphic procedures poly-scope. It expected that param
+// will be of kind PPK_Baked_Value.
+// CLEANUP: This function is kind of gross at the moment, because it handles different cases for
+// the argument kind. When type expressions (type_expr) become first-class types in the type
+// system, this code should be able to be a lot cleaner.
+static void solve_for_polymorphic_param_value(PolySolveResult* resolved, AstPolyProc* pp, AstPolyParam* param, PolyProcLookupMethod pp_lookup, ptr actual, char** err_msg) {
+ if (pp_lookup != PPLM_By_Arguments) {
+ *err_msg = "Function type cannot be used to solved for baked parameter value.";
+ return;
+ }
+
+ Arguments* args = (Arguments *) actual;
+ AstTyped* value = lookup_param_in_arguments(pp, param, args, err_msg);
+ if (value == NULL) return;
+
+ // HACK: Storing the original value because if this was an AstArgument, we need to flag
+ // it as baked if it is determined that the argument is of the correct kind and type.
+ AstTyped* orig_value = value;
+ if (value->kind == Ast_Kind_Argument) {
+ ((AstArgument *) orig_value)->is_baked = 0;
+ value = ((AstArgument *) value)->value;
+ }
+
+ if (param->type_expr == (AstType *) &basic_type_type_expr) {
+ if (!node_is_type((AstNode *) value)) {
+ if (err_msg) *err_msg = "Expected type expression.";
+ return;
+ }
+
+ Type* resolved_type = type_build_from_ast(context.ast_alloc, (AstType *) value);
+ if (resolved_type == NULL) flag_to_yield = 1;
+
+ *resolved = ((PolySolveResult) { PSK_Type, .actual = resolved_type });
+
+ } else {
+ resolve_expression_type(value);
+
+ if ((value->flags & Ast_Flag_Comptime) == 0) {
+ if (err_msg) *err_msg = "Expected compile-time known argument.";
+ return;
+ }
+
+ if (param->type == NULL)
+ param->type = type_build_from_ast(context.ast_alloc, param->type_expr);
+ assert(param->type);
+
+ if (!type_check_or_auto_cast(&value, param->type)) {
+ if (err_msg) *err_msg = bh_aprintf(global_scratch_allocator,
+ "The procedure '%s' expects a value of type '%s' for baked %d%s parameter, got '%s'.",
+ get_function_name(pp->base_func),
+ type_get_name(param->type),
+ param->idx + 1,
+ bh_num_suffix(param->idx + 1),
+ node_get_type_name(value));
+ return;
+ }
+
+ *resolved = ((PolySolveResult) { PSK_Value, value });
+ }
+
+ if (orig_value->kind == Ast_Kind_Argument) {
+ ((AstArgument *) orig_value)->is_baked = 1;
+ }
+}
+
+// NOTE: The job of this function is to take a polymorphic procedure, as well as a method of
+// solving for the polymorphic variables, in order to return an array of the solutions for all
+// of the polymorphic variables.
+static bh_arr(AstPolySolution) find_polymorphic_slns(AstPolyProc* pp, PolyProcLookupMethod pp_lookup, ptr actual, char** err_msg) {
+ bh_arr(AstPolySolution) slns = NULL;
+ bh_arr_new(global_heap_allocator, slns, bh_arr_length(pp->poly_params));
+
+ // NOTE: "known solutions" are given through a '#solidify' directive. If this polymorphic
+ // procedure is the result of a partially applied solidification, this array will be non-
+ // empty and these solutions will be used.
+ bh_arr_each(AstPolySolution, known_sln, pp->known_slns) bh_arr_push(slns, *known_sln);
+
+ bh_arr_each(AstPolyParam, param, pp->poly_params) {
+
+ // NOTE: First check to see if this polymorphic variable was already specified in the
+ // known solutions.
+ b32 already_solved = 0;
+ bh_arr_each(AstPolySolution, known_sln, pp->known_slns) {
+ if (token_equals(param->poly_sym->token, known_sln->poly_sym->token)) {
+ already_solved = 1;
+ break;
+ }
+ }
+ if (already_solved) continue;
+
+ // NOTE: Solve for the polymorphic parameter's value
+ PolySolveResult resolved = { PSK_Undefined };
+ switch (param->kind) {
+ case PPK_Poly_Type: solve_for_polymorphic_param_type (&resolved, pp, param, pp_lookup, actual, err_msg); break;
+ case PPK_Baked_Value: solve_for_polymorphic_param_value(&resolved, pp, param, pp_lookup, actual, err_msg); break;
+
+ default: if (err_msg) *err_msg = "Invalid polymorphic parameter kind. This is a compiler bug.";
+ }
+
+ if (flag_to_yield) goto sln_not_found;
+
+ switch (resolved.kind) {
+ case PSK_Undefined:
+ // NOTE: If no error message has been assigned to why this polymorphic parameter
+ // resolution was unsuccessful, provide a basic dummy one.
+ if (err_msg && *err_msg == NULL)
+ *err_msg = bh_aprintf(global_scratch_allocator,
+ "Unable to solve for polymorphic variable '%b'.",
+ param->poly_sym->token->text,
+ param->poly_sym->token->length);
+
+ goto sln_not_found;
+
+ case PSK_Type:
+ bh_arr_push(slns, ((AstPolySolution) {
+ .kind = PSK_Type,
+ .poly_sym = param->poly_sym,
+ .type = resolved.actual,
+ }));
+ break;
+
+ case PSK_Value:
+ bh_arr_push(slns, ((AstPolySolution) {
+ .kind = PSK_Value,
+ .poly_sym = param->poly_sym,
+ .value = resolved.value,
+ }));
+ break;
+ }
+ }
+
+ return slns;
+
+ sln_not_found:
+ bh_arr_free(slns);
+ return NULL;
+}
+
+// NOTE: The job of this function is to be a wrapper to other functions, providing an error
+// message if a solution could not be found. This can't be merged with polymorphic_proc_solidify
+// because polymorphic_proc_try_solidify uses the aforementioned function.
+AstFunction* polymorphic_proc_lookup(AstPolyProc* pp, PolyProcLookupMethod pp_lookup, ptr actual, OnyxToken* tkn) {
+ ensure_polyproc_cache_is_created(pp);
+
+ char *err_msg = NULL;
+ bh_arr(AstPolySolution) slns = find_polymorphic_slns(pp, pp_lookup, actual, &err_msg);
+ if (slns == NULL) {
+ if (flag_to_yield) {
+ flag_to_yield = 0;
+ return (AstFunction *) &node_that_signals_a_yield;
+ }
+
+ if (err_msg != NULL) onyx_report_error(tkn->pos, err_msg);
+ else onyx_report_error(tkn->pos, "Some kind of error occured when generating a polymorphic procedure. You hopefully will not see this");
+
+ return NULL;
+ }
+
+ AstFunction* result = polymorphic_proc_solidify(pp, slns, tkn);
+
+ bh_arr_free(slns);
+ return result;
+}
+
+AstFunction* polymorphic_proc_solidify(AstPolyProc* pp, bh_arr(AstPolySolution) slns, OnyxToken* tkn) {
+ ensure_polyproc_cache_is_created(pp);
+
+ // NOTE: Check if a version of this polyproc has already been created.
+ char* unique_key = build_poly_slns_unique_key(slns);
+ if (bh_table_has(AstSolidifiedFunction, pp->concrete_funcs, unique_key)) {
+ AstSolidifiedFunction solidified_func = bh_table_get(AstSolidifiedFunction, pp->concrete_funcs, unique_key);
+
+ // NOTE: If this solution was originally created from a "build_only_header" call, then the body
+ // will not have been or type checked, or anything. This ensures that the body is copied, the
+ // entities are created and entered into the pipeline.
+ ensure_solidified_function_has_body(pp, solidified_func);
+
+ // NOTE: Again, if this came from a "build_only_header" call, then there was no known token and
+ // the "generated_from" member will be null. It is best to set it here so errors reported in that
+ // function can report where the polymorphic instantiation occurred.
+ if (solidified_func.func->generated_from == NULL)
+ solidified_func.func->generated_from = tkn;
+
+ return solidified_func.func;
+ }
+
+ AstSolidifiedFunction solidified_func = generate_solidified_function(pp, slns, tkn, 0);
+
+ // NOTE: Cache the function for later use, reducing duplicate functions.
+ bh_table_put(AstSolidifiedFunction, pp->concrete_funcs, unique_key, solidified_func);
+
+ if (!add_solidified_function_entities(solidified_func, 0)) {
+ onyx_report_error(tkn->pos, "Error in polymorphic procedure header generated from this call site.");
+ return NULL;
+ }
+
+ return solidified_func.func;
+}
+
+// NOTE: This can return either a AstFunction or an AstPolyProc, depending if enough parameters were
+// supplied to remove all the polymorphic variables from the function.
+AstNode* polymorphic_proc_try_solidify(AstPolyProc* pp, bh_arr(AstPolySolution) slns, OnyxToken* tkn) {
+ i32 valid_argument_count = 0;
+
+ bh_arr_each(AstPolySolution, sln, slns) {
+ b32 found_match = 0;
+
+ bh_arr_each(AstPolyParam, param, pp->poly_params) {
+ if (token_equals(sln->poly_sym->token, param->poly_sym->token)) {
+ found_match = 1;
+ break;
+ }
+ }
+
+ if (found_match) {
+ valid_argument_count++;
+ } else {
+ onyx_report_error(tkn->pos, "'%b' is not a type variable of '%b'.",
+ sln->poly_sym->token->text, sln->poly_sym->token->length,
+ pp->token->text, pp->token->length);
+ return (AstNode *) pp;
+ }
+ }
+
+ if (valid_argument_count == bh_arr_length(pp->poly_params)) {
+ return (AstNode *) polymorphic_proc_solidify(pp, slns, tkn);
+
+ } else {
+ // HACK: Some of these initializations assume that the entity for this polyproc has
+ // made it through the symbol resolution phase.
+ // - brendanfh 2020/12/25
+ AstPolyProc* new_pp = onyx_ast_node_new(context.ast_alloc, sizeof(AstPolyProc), Ast_Kind_Polymorphic_Proc);
+ new_pp->token = pp->token; // TODO: Change this to be the solidify->token
+ new_pp->base_func = pp->base_func;
+ new_pp->flags = pp->flags;
+ new_pp->poly_params = pp->poly_params;
+
+ ensure_polyproc_cache_is_created(pp);
+ new_pp->concrete_funcs = pp->concrete_funcs;
+
+ new_pp->known_slns = NULL;
+ bh_arr_new(global_heap_allocator, new_pp->known_slns, bh_arr_length(pp->known_slns) + bh_arr_length(slns));
+
+ bh_arr_each(AstPolySolution, sln, pp->known_slns) bh_arr_push(new_pp->known_slns, *sln);
+ bh_arr_each(AstPolySolution, sln, slns) bh_arr_push(new_pp->known_slns, *sln);
+
+ return (AstNode *) new_pp;
+ }
+}
+
+AstFunction* polymorphic_proc_build_only_header(AstPolyProc* pp, PolyProcLookupMethod pp_lookup, ptr actual) {
+ bh_arr(AstPolySolution) slns = find_polymorphic_slns(pp, pp_lookup, actual, NULL);
+ if (slns == NULL) return NULL;
+
+ ensure_polyproc_cache_is_created(pp);
+
+ AstSolidifiedFunction solidified_func;
+
+ char* unique_key = build_poly_slns_unique_key(slns);
+ if (bh_table_has(AstSolidifiedFunction, pp->concrete_funcs, unique_key)) {
+ solidified_func = bh_table_get(AstSolidifiedFunction, pp->concrete_funcs, unique_key);
+
+ } else {
+ // NOTE: This function is only going to have the header of it correctly created.
+ // Nothing should happen to this function's body or else the original will be corrupted.
+ // - brendanfh 2021/01/10
+ solidified_func = generate_solidified_function(pp, slns, NULL, 1);
+ }
+
+ if (solidified_func.header_complete) return solidified_func.func;
+
+ Entity func_header_entity = {
+ .state = Entity_State_Resolve_Symbols,
+ .type = Entity_Type_Function_Header,
+ .function = solidified_func.func,
+ .package = NULL,
+ .scope = solidified_func.poly_scope,
+ };
+
+ // HACK: Everything with entity_bring_to_state is a big hack...
+ // The idea is that instead of making the caller yield for a simple procedure header,
+ // do a single pass of trying to solve for the parameter types and return types.
+ // The big missing piece of the entity pumping system is that once an entity is
+ // entered into the system, it cannot be removed and it is assumed it is going to
+ // be used in the final binary. This is obviously not always the case, but that
+ // is how the system currently works.
+ b32 successful = entity_bring_to_state(&func_header_entity, Entity_State_Code_Gen);
+ if (onyx_has_errors()) {
+ onyx_errors_print();
+ onyx_clear_errors();
+ return NULL;
+ }
+
+ solidified_func.header_complete = successful;
+
+ // NOTE: Cache the function for later use, only if it didn't have errors in its header.
+ bh_table_put(AstSolidifiedFunction, pp->concrete_funcs, unique_key, solidified_func);
+
+ return solidified_func.func;
+}
+
+//
+// Polymorphic Structures
+//
+//
+// Currently, I am not very happy about how polymorphic structure generation works. My biggest problem
+// with it is that it is very different from the polymorhic procedure generation. Also, it needs to
+// completely generate and check the structure right away, which means there is a lot of up-front work
+// done here that could probably be done elsewhere. This really relates to a large problem in the compiler
+// that types need to be known completely by the time symbol resolution is done, even though that
+// information shouldn't need to be known until right before the types are checked.
+//
+// The above documentation is very incorrect but I don't want to fix it right now. Basically, polymorphic
+// structures now have a delay instantiation phase and are not forced to be completed immediately.
+
+char* build_poly_struct_name(AstPolyStructType* ps_type, Type* cs_type) {
+ char name_buf[256];
+ fori (i, 0, 256) name_buf[i] = 0;
+
+ strncat(name_buf, ps_type->name, 255);
+ strncat(name_buf, "(", 255);
+ bh_arr_each(AstPolySolution, ptype, cs_type->Struct.poly_sln) {
+ if (ptype != cs_type->Struct.poly_sln)
+ strncat(name_buf, ", ", 255);
+
+ // This logic will have to be other places as well.
+
+ switch (ptype->kind) {
+ case PSK_Undefined: assert(0); break;
+ case PSK_Type: strncat(name_buf, type_get_name(ptype->type), 255); break;
+ case PSK_Value: {
+ // FIX
+ AstNode* value = strip_aliases((AstNode *) ptype->value);
+
+ if (value->kind == Ast_Kind_NumLit) {
+ AstNumLit* nl = (AstNumLit *) value;
+ if (type_is_integer(nl->type)) {
+ strncat(name_buf, bh_bprintf("%l", nl->value.l), 127);
+ } else {
+ strncat(name_buf, "numlit (FIX ME)", 127);
+ }
+ } else if (value->kind == Ast_Kind_Code_Block) {
+ AstCodeBlock* code = (AstCodeBlock *) value;
+ OnyxFilePos code_loc = code->token->pos;
+ strncat(name_buf, bh_bprintf("code at %s:%d,%d", code_loc.filename, code_loc.line, code_loc.column), 127);
+ } else {
+ strncat(name_buf, "<expr>", 127);
+ }
+
+ break;
+ }
+ }
+ }
+ strncat(name_buf, ")", 255);
+
+ return bh_aprintf(global_heap_allocator, "%s", name_buf);
+}
+
+AstStructType* polymorphic_struct_lookup(AstPolyStructType* ps_type, bh_arr(AstPolySolution) slns, OnyxFilePos pos) {
+ // @Cleanup
+ assert(ps_type->scope != NULL);
+
+ if (ps_type->concrete_structs == NULL) {
+ bh_table_init(global_heap_allocator, ps_type->concrete_structs, 16);
+ }
+
+ if (bh_arr_length(slns) != bh_arr_length(ps_type->poly_params)) {
+ onyx_report_error(pos, "Wrong number of arguments for '%s'. Expected %d, got %d",
+ ps_type->name,
+ bh_arr_length(ps_type->poly_params),
+ bh_arr_length(slns));
+
+ return NULL;
+ }
+
+ i32 i = 0;
+ bh_arr_each(AstPolySolution, sln, slns) {
+ sln->poly_sym = (AstNode *) &ps_type->poly_params[i];
+
+ PolySolutionKind expected_kind = PSK_Undefined;
+ if ((AstNode *) ps_type->poly_params[i].type_node == (AstNode *) &basic_type_type_expr) {
+ expected_kind = PSK_Type;
+ } else {
+ expected_kind = PSK_Value;
+ }
+
+ if (sln->kind != expected_kind) {
+ if (expected_kind == PSK_Type)
+ onyx_report_error(pos, "Expected type expression for %d%s argument.", i + 1, bh_num_suffix(i + 1));
+
+ if (expected_kind == PSK_Value)
+ onyx_report_error(pos, "Expected value expression of type '%s' for %d%s argument.",
+ type_get_name(ps_type->poly_params[i].type),
+ i + 1, bh_num_suffix(i + 1));
+
+ return NULL;
+ }
+
+ if (sln->kind == PSK_Value) {
+ resolve_expression_type(sln->value);
+
+ if ((sln->value->flags & Ast_Flag_Comptime) == 0) {
+ onyx_report_error(pos,
+ "Expected compile-time known argument for '%b'.",
+ sln->poly_sym->token->text,
+ sln->poly_sym->token->length);
+ return NULL;
+ }
+
+ if (!types_are_compatible(sln->value->type, ps_type->poly_params[i].type)) {
+ onyx_report_error(pos, "Expected compile-time argument of type '%s', got '%s'.",
+ type_get_name(ps_type->poly_params[i].type),
+ type_get_name(sln->value->type));
+ return NULL;
+ }
+ }
+
+ i++;
+ }
+
+ char* unique_key = build_poly_slns_unique_key(slns);
+ if (bh_table_has(AstStructType *, ps_type->concrete_structs, unique_key)) {
+ AstStructType* concrete_struct = bh_table_get(AstStructType *, ps_type->concrete_structs, unique_key);
+
+ if (concrete_struct->entity_type->state < Entity_State_Check_Types) {
+ return NULL;
+ }
+
+ Type* cs_type = type_build_from_ast(context.ast_alloc, (AstType *) concrete_struct);
+ if (!cs_type) return NULL;
+
+ if (cs_type->Struct.poly_sln == NULL) cs_type->Struct.poly_sln = bh_arr_copy(global_heap_allocator, slns);
+ if (cs_type->Struct.name == NULL) cs_type->Struct.name = build_poly_struct_name(ps_type, cs_type);
+
+ return concrete_struct;
+ }
+
+ Scope* sln_scope = scope_create(context.ast_alloc, ps_type->scope, ps_type->token->pos);
+ insert_poly_slns_into_scope(sln_scope, slns);
+
+ AstStructType* concrete_struct = (AstStructType *) ast_clone(context.ast_alloc, ps_type->base_struct);
+ bh_table_put(AstStructType *, ps_type->concrete_structs, unique_key, concrete_struct);
+
+ add_entities_for_node(NULL, (AstNode *) concrete_struct, sln_scope, NULL);
+ return NULL;
+}
--- /dev/null
+#define BH_DEBUG
+#include "parser.h"
+#include "utils.h"
+#include "astnodes.h"
+#include "errors.h"
+
+// Variables used during the symbol resolution phase.
+static Scope* curr_scope = NULL;
+static b32 report_unresolved_symbols = 1;
+
+// Everything related to waiting on is imcomplete at the moment.
+static Entity* waiting_on = NULL;
+
+#define SYMRES(kind, ...) do { \
+ SymresStatus ss = symres_ ## kind (__VA_ARGS__); \
+ if (ss > Symres_Errors_Start) return ss; \
+ } while (0)
+
+typedef enum SymresStatus {
+ Symres_Success,
+ Symres_Complete,
+
+ Symres_Errors_Start,
+ Symres_Yield_Macro,
+ Symres_Yield_Micro,
+ Symres_Error,
+} SymresStatus;
+
+static SymresStatus symres_type(AstType** type);
+static SymresStatus symres_local(AstLocal** local);
+static SymresStatus symres_call(AstCall* call);
+static SymresStatus symres_size_of(AstSizeOf* so);
+static SymresStatus symres_align_of(AstAlignOf* so);
+static SymresStatus symres_field_access(AstFieldAccess** fa);
+static SymresStatus symres_compound(AstCompound* compound);
+static SymresStatus symres_expression(AstTyped** expr);
+static SymresStatus symres_return(AstReturn* ret);
+static SymresStatus symres_if(AstIfWhile* ifnode);
+static SymresStatus symres_while(AstIfWhile* whilenode);
+static SymresStatus symres_for(AstFor* fornode);
+static SymresStatus symres_switch(AstSwitch* switchnode);
+static SymresStatus symres_use(AstUse* use);
+static SymresStatus symres_directive_solidify(AstDirectiveSolidify** psolid);
+static SymresStatus symres_directive_defined(AstDirectiveDefined** pdefined);
+static SymresStatus symres_directive_insert(AstDirectiveInsert* insert);
+static SymresStatus symres_statement_chain(AstNode** walker);
+static SymresStatus symres_statement(AstNode** stmt, b32 *remove);
+static SymresStatus symres_block(AstBlock* block);
+static SymresStatus symres_function_header(AstFunction* func);
+static SymresStatus symres_function(AstFunction* func);
+static SymresStatus symres_global(AstGlobal* global);
+static SymresStatus symres_overloaded_function(AstOverloadedFunction* ofunc);
+static SymresStatus symres_package(AstPackage* package);
+static SymresStatus symres_enum(AstEnumType* enum_node);
+static SymresStatus symres_memres_type(AstMemRes** memres);
+static SymresStatus symres_memres(AstMemRes** memres);
+static SymresStatus symres_struct_defaults(AstType* st);
+static SymresStatus symres_static_if(AstIf* static_if);
+static SymresStatus symres_macro(AstMacro* macro);
+
+static void scope_enter(Scope* new_scope) {
+ curr_scope = new_scope;
+}
+
+static void scope_leave() {
+ curr_scope = curr_scope->parent;
+}
+
+static SymresStatus symres_symbol(AstNode** symbol_node) {
+ OnyxToken* token = (*symbol_node)->token;
+ AstNode* res = symbol_resolve(curr_scope, token);
+
+ if (!res) { // :SymresStall
+ if (report_unresolved_symbols) {
+ onyx_report_error(token->pos,
+ "Unable to resolve symbol '%b'",
+ token->text,
+ token->length);
+
+ return Symres_Error;
+ } else {
+ return Symres_Yield_Macro;
+ }
+
+ } else {
+ *symbol_node = res;
+ }
+
+ return Symres_Success;
+}
+
+static SymresStatus symres_struct_type(AstStructType* s_node) {
+ if (s_node->flags & Ast_Flag_Type_Is_Resolved) return Symres_Success;
+
+ s_node->flags |= Ast_Flag_Type_Is_Resolved;
+
+ if (s_node->scope) {
+ // FIX: This is probably wrong for the long term.
+ s_node->scope->parent = curr_scope;
+
+ scope_enter(s_node->scope);
+ }
+
+ fori (i, 0, bh_arr_length(s_node->members)) {
+ AstStructMember *member = s_node->members[i];
+
+ if (member->type_node) {
+ SymresStatus ss = symres_type(&member->type_node);
+ if (ss != Symres_Success) {
+ s_node->flags &= ~Ast_Flag_Type_Is_Resolved;
+ if (s_node->scope) scope_leave();
+ return ss;
+ }
+
+ if (!node_is_type((AstNode *) member->type_node)) {
+ onyx_report_error(member->token->pos, "Member type is not a type.");
+ goto struct_symres_done;
+ }
+
+ if (member->flags & Ast_Flag_Struct_Mem_Used) {
+ AstType *used = (AstType *) member->type_node;
+
+ while (used->kind == Ast_Kind_Type_Alias) {
+ used = ((AstTypeAlias *) used)->to;
+ }
+
+ b32 use_works = (used->kind == Ast_Kind_Struct_Type || used->kind == Ast_Kind_Poly_Call_Type);
+
+ if (used->kind == Ast_Kind_Type_Raw_Alias) {
+ AstTypeRawAlias* alias = (AstTypeRawAlias *) used;
+ use_works = (alias->to->kind == Type_Kind_Struct);
+ }
+
+ if (!use_works) {
+ onyx_report_error(member->token->pos,
+ "Can only 'use' members of struct type, got '%s'.",
+ onyx_ast_node_kind_string(used->kind));
+ goto struct_symres_done;
+ }
+ }
+ }
+ }
+
+struct_symres_done:
+ if (s_node->scope) scope_leave();
+ return Symres_Success;
+}
+
+static SymresStatus symres_type(AstType** type) {
+ // Don't make this kill all symbol resolution if the type is null.
+ if (!type || !*type) return Symres_Success;
+
+ switch ((*type)->kind) {
+ case Ast_Kind_Symbol: SYMRES(symbol, (AstNode **) type); break;
+ case Ast_Kind_Basic_Type: break;
+ case Ast_Kind_Type_Alias: SYMRES(type, &((AstTypeAlias *) *type)->to); break;
+ case Ast_Kind_Field_Access: {
+ SYMRES(field_access, (AstFieldAccess **) type);
+
+ if (!node_is_type((AstNode *) *type))
+ onyx_report_error((*type)->token->pos, "Field access did not result in a type. (%s)", onyx_ast_node_kind_string((*type)->kind));
+ break;
+ }
+
+ case Ast_Kind_Pointer_Type: SYMRES(type, &((AstPointerType *) *type)->elem); break;
+ case Ast_Kind_Slice_Type: SYMRES(type, &((AstSliceType *) *type)->elem); break;
+ case Ast_Kind_DynArr_Type: SYMRES(type, &((AstDynArrType *) *type)->elem); break;
+ case Ast_Kind_VarArg_Type: SYMRES(type, &((AstVarArgType *) *type)->elem); break;
+
+ case Ast_Kind_Function_Type: {
+ AstFunctionType* ftype = (AstFunctionType *) *type;
+
+ SYMRES(type, &ftype->return_type);
+
+ if (ftype->param_count > 0) {
+ fori (i, 0, (i64) ftype->param_count) {
+ SYMRES(type, &ftype->params[i]);
+ }
+ }
+ break;
+ }
+
+ case Ast_Kind_Struct_Type: SYMRES(struct_type, (AstStructType *) *type); break;
+ case Ast_Kind_Array_Type: {
+ AstArrayType* a_node = (AstArrayType *) *type;
+
+ if (a_node->count_expr) SYMRES(expression, &a_node->count_expr);
+ SYMRES(type, &a_node->elem);
+ break;
+ }
+
+ case Ast_Kind_Enum_Type: break;
+
+ case Ast_Kind_Poly_Struct_Type: {
+ AstPolyStructType* pst_node = (AstPolyStructType *) *type;
+ pst_node->scope = scope_create(context.ast_alloc, pst_node->entity->scope, pst_node->token->pos);
+
+ bh_arr_each(AstPolyStructParam, param, pst_node->poly_params) {
+ SYMRES(type, ¶m->type_node);
+ param->type = type_build_from_ast(context.ast_alloc, param->type_node);
+ }
+ break;
+ }
+
+ case Ast_Kind_Poly_Call_Type: {
+ AstPolyCallType* pc_node = (AstPolyCallType *) *type;
+
+ SYMRES(type, &pc_node->callee);
+
+ bh_arr_each(AstNode *, param, pc_node->params) {
+ if (node_is_type(*param)) {
+ SYMRES(type, (AstType **) param);
+ } else {
+ SYMRES(expression, (AstTyped **) param);
+ }
+ }
+ break;
+ }
+
+ case Ast_Kind_Type_Compound: {
+ AstCompoundType* ctype = (AstCompoundType *) *type;
+
+ bh_arr_each(AstType *, type, ctype->types) SYMRES(type, type);
+ break;
+ }
+
+ case Ast_Kind_Alias: {
+ AstAlias* alias = (AstAlias *) *type;
+ SYMRES(type, (AstType **) &alias->alias);
+ break;
+ }
+
+ case Ast_Kind_Typeof: {
+ AstTypeOf* type_of = (AstTypeOf *) *type;
+ SYMRES(expression, &type_of->expr);
+ break;
+ }
+ }
+
+ return Symres_Success;
+}
+
+static SymresStatus symres_local(AstLocal** local) {
+ SYMRES(type, &(*local)->type_node);
+
+ if ((*local)->token != NULL)
+ symbol_introduce(curr_scope, (*local)->token, (AstNode *) *local);
+
+ return Symres_Success;
+}
+
+static SymresStatus symres_arguments(Arguments* args) {
+ bh_arr_each(AstTyped *, arg, args->values)
+ SYMRES(expression, arg);
+
+ bh_arr_each(AstNamedValue *, named_arg, args->named_values)
+ SYMRES(expression, &(*named_arg)->value);
+
+ return Symres_Success;
+}
+
+static SymresStatus symres_call(AstCall* call) {
+ SYMRES(expression, (AstTyped **) &call->callee);
+ SYMRES(arguments, &call->args);
+
+ return Symres_Success;
+}
+
+static SymresStatus symres_size_of(AstSizeOf* so) {
+ SYMRES(type, &so->type_node);
+ SYMRES(type, &so->so_ast_type);
+
+ return Symres_Success;
+}
+
+static SymresStatus symres_align_of(AstAlignOf* ao) {
+ SYMRES(type, &ao->type_node);
+ SYMRES(type, &ao->ao_ast_type);
+
+ return Symres_Success;
+}
+
+static SymresStatus symres_field_access(AstFieldAccess** fa) {
+ if ((*fa)->expr == NULL) return Symres_Error;
+ SYMRES(expression, &(*fa)->expr);
+ if ((*fa)->expr == NULL) return Symres_Error;
+
+ AstTyped* expr = (AstTyped *) strip_aliases((AstNode *) (*fa)->expr);
+
+ AstNode* resolution = try_symbol_resolve_from_node((AstNode *) expr, (*fa)->token);
+ if (resolution) *((AstNode **) fa) = resolution;
+ else if (expr->kind == Ast_Kind_Package) {
+ if (report_unresolved_symbols) {
+ onyx_report_error((*fa)->token->pos, "'%b' was not found in package '%s'. Perhaps it is defined in a file that wasn't loaded?",
+ (*fa)->token->text,
+ (*fa)->token->length,
+ ((AstPackage *) (*fa)->expr)->package->name);
+ return Symres_Error;
+ } else {
+ return Symres_Yield_Macro;
+ }
+ }
+
+ return Symres_Success;
+}
+
+static SymresStatus symres_compound(AstCompound* compound) {
+ bh_arr_each(AstTyped *, expr, compound->exprs) {
+ SYMRES(expression, expr);
+ }
+
+ return Symres_Success;
+}
+
+static SymresStatus symres_if_expression(AstIfExpression* if_expr) {
+ SYMRES(expression, &if_expr->cond);
+ SYMRES(expression, &if_expr->true_expr);
+ SYMRES(expression, &if_expr->false_expr);
+ return Symres_Success;
+}
+
+static SymresStatus symres_pipe(AstBinaryOp** pipe) {
+ AstCall* call_node = (AstCall *) (*pipe)->right;
+ SYMRES(expression, (AstTyped **) &call_node);
+ SYMRES(expression, &(*pipe)->left);
+
+ if (call_node->kind != Ast_Kind_Call) {
+ onyx_report_error((*pipe)->token->pos, "Pipe operator expected call on right side.");
+ return Symres_Error;
+ }
+
+ if ((*pipe)->left == NULL) return Symres_Error;
+
+ bh_arr_insertn(call_node->args.values, 0, 1);
+ call_node->args.values[0] = (AstTyped *) make_argument(context.ast_alloc, (*pipe)->left);
+ call_node->next = (*pipe)->next;
+
+ // NOTE: Not a BinaryOp node
+ *pipe = (AstBinaryOp *) call_node;
+
+ return Symres_Success;
+}
+
+// CLEANUP: This is an experimental feature and might be removed in the future.
+// I noticed a common pattern when writing in Onyx is something that looks like this:
+//
+// foo.member_function(^foo, ...)
+//
+// I decided it would be worth adding a bit of syntactic sugar for such as call. I
+// decided to use the '->' operator for this purpose. The snippet below is the exact
+// same as the snippet above (after the nodes have been processed by the function below)
+//
+// foo->member_function(...)
+static SymresStatus symres_method_call(AstBinaryOp** mcall) {
+ AstCall* call_node = (AstCall *) (*mcall)->right;
+ if (call_node->kind != Ast_Kind_Call) {
+ onyx_report_error((*mcall)->token->pos, "'->' expected procedure call on right side.");
+ return Symres_Error;
+ }
+
+ SYMRES(expression, &(*mcall)->left);
+ if ((*mcall)->left == NULL) return Symres_Error;
+
+ if (((*mcall)->flags & Ast_Flag_Has_Been_Symres) == 0) {
+ AstFieldAccess* implicit_field_access = make_field_access(context.ast_alloc, (*mcall)->left, NULL);
+ implicit_field_access->token = call_node->callee->token;
+ call_node->callee = (AstTyped *) implicit_field_access;
+ }
+
+ SYMRES(expression, (AstTyped **) &call_node);
+ (*mcall)->flags |= Ast_Flag_Has_Been_Symres;
+
+ return Symres_Success;
+}
+
+static SymresStatus symres_unaryop(AstUnaryOp** unaryop) {
+ if ((*unaryop)->operation == Unary_Op_Cast) {
+ SYMRES(type, &(*unaryop)->type_node);
+ }
+
+ SYMRES(expression, &(*unaryop)->expr);
+
+ return Symres_Success;
+}
+
+static SymresStatus symres_struct_literal(AstStructLiteral* sl) {
+ if (sl->stnode != NULL) SYMRES(expression, &sl->stnode);
+ SYMRES(type, (AstType **) &sl->stnode);
+
+ sl->type_node = (AstType *) sl->stnode;
+ while (sl->type_node && sl->type_node->kind == Ast_Kind_Type_Alias)
+ sl->type_node = ((AstTypeAlias *) sl->type_node)->to;
+
+ SYMRES(arguments, &sl->args);
+
+ return Symres_Success;
+}
+
+static SymresStatus symres_array_literal(AstArrayLiteral* al) {
+ if (al->atnode != NULL) SYMRES(expression, &al->atnode);
+ SYMRES(type, (AstType **) &al->atnode);
+
+ al->type_node = (AstType *) al->atnode;
+ while (al->type_node && al->type_node->kind == Ast_Kind_Type_Alias)
+ al->type_node = ((AstTypeAlias *) al->type_node)->to;
+
+ bh_arr_each(AstTyped *, expr, al->values)
+ SYMRES(expression, expr);
+
+ return Symres_Success;
+}
+
+static SymresStatus symres_expression(AstTyped** expr) {
+ if (node_is_type((AstNode *) *expr)) {
+ SYMRES(type, (AstType **) expr);
+ return Symres_Success;
+ }
+
+ switch ((*expr)->kind) {
+ case Ast_Kind_Symbol: SYMRES(symbol, (AstNode **) expr); break;
+
+ case Ast_Kind_Binary_Op:
+ SYMRES(expression, &((AstBinaryOp *)(*expr))->left);
+ SYMRES(expression, &((AstBinaryOp *)(*expr))->right);
+ break;
+
+ case Ast_Kind_Unary_Op: SYMRES(unaryop, (AstUnaryOp **) expr); break;
+ case Ast_Kind_Call: SYMRES(call, (AstCall *) *expr); break;
+ case Ast_Kind_Argument: SYMRES(expression, &((AstArgument *) *expr)->value); break;
+ case Ast_Kind_Block: SYMRES(block, (AstBlock *) *expr); break;
+ case Ast_Kind_Address_Of: SYMRES(expression, &((AstAddressOf *)(*expr))->expr); break;
+ case Ast_Kind_Dereference: SYMRES(expression, &((AstDereference *)(*expr))->expr); break;
+ case Ast_Kind_Field_Access: SYMRES(field_access, (AstFieldAccess **) expr); break;
+ case Ast_Kind_Pipe: SYMRES(pipe, (AstBinaryOp **) expr); break;
+ case Ast_Kind_Method_Call: SYMRES(method_call, (AstBinaryOp **) expr); break;
+ case Ast_Kind_Size_Of: SYMRES(size_of, (AstSizeOf *)*expr); break;
+ case Ast_Kind_Align_Of: SYMRES(align_of, (AstAlignOf *)*expr); break;
+ case Ast_Kind_Alias: SYMRES(expression, &((AstAlias *) *expr)->alias); break;
+
+ case Ast_Kind_Range_Literal:
+ SYMRES(expression, &((AstRangeLiteral *)(*expr))->low);
+ SYMRES(expression, &((AstRangeLiteral *)(*expr))->high);
+
+ SYMRES(type, &builtin_range_type);
+ (*expr)->type_node = builtin_range_type;
+
+ // NOTE: This is a weird place to put this so maybe put it somewhere else eventually
+ // - brendanfh 2020/09/04
+ builtin_range_type_type = type_build_from_ast(context.ast_alloc, builtin_range_type);
+ break;
+
+ case Ast_Kind_Function:
+ case Ast_Kind_NumLit:
+ SYMRES(type, &(*expr)->type_node);
+ break;
+
+ case Ast_Kind_StrLit:
+ SYMRES(type, &builtin_string_type);
+ (*expr)->type_node = builtin_string_type;
+ break;
+
+ case Ast_Kind_Slice:
+ case Ast_Kind_Subscript:
+ SYMRES(expression, &((AstSubscript *)(*expr))->addr);
+ SYMRES(expression, &((AstSubscript *)(*expr))->expr);
+ break;
+
+ case Ast_Kind_Struct_Literal:
+ SYMRES(struct_literal, (AstStructLiteral *)(*expr));
+ break;
+
+ case Ast_Kind_Array_Literal:
+ SYMRES(array_literal, (AstArrayLiteral *)(*expr));
+ break;
+
+ case Ast_Kind_Directive_Solidify:
+ SYMRES(directive_solidify, (AstDirectiveSolidify **) expr);
+ break;
+
+ case Ast_Kind_Directive_Defined:
+ SYMRES(directive_defined, (AstDirectiveDefined **) expr);
+ break;
+
+ case Ast_Kind_Compound:
+ SYMRES(compound, (AstCompound *) *expr);
+ break;
+
+ case Ast_Kind_Package:
+ SYMRES(package, (AstPackage *) *expr);
+ break;
+
+ case Ast_Kind_If_Expression:
+ SYMRES(if_expression, (AstIfExpression *) *expr);
+ break;
+
+ case Ast_Kind_Directive_Insert:
+ SYMRES(directive_insert, (AstDirectiveInsert *) *expr);
+ break;
+
+ case Ast_Kind_Do_Block:
+ SYMRES(block, ((AstDoBlock *) *expr)->block);
+ break;
+
+ default: break;
+ }
+
+ return Symres_Success;
+}
+
+static SymresStatus symres_return(AstReturn* ret) {
+ if (ret->expr)
+ SYMRES(expression, &ret->expr);
+
+ return Symres_Success;
+}
+
+static SymresStatus symres_if(AstIfWhile* ifnode) {
+ if (ifnode->kind == Ast_Kind_Static_If) {
+ if ((ifnode->flags & Ast_Flag_Static_If_Resolved) == 0) {
+ return Symres_Yield_Macro;
+ }
+
+ if (static_if_resolution(ifnode)) {
+ if (ifnode->true_stmt != NULL) SYMRES(statement, (AstNode **) &ifnode->true_stmt, NULL);
+
+ } else {
+ if (ifnode->false_stmt != NULL) SYMRES(statement, (AstNode **) &ifnode->false_stmt, NULL);
+ }
+
+ } else {
+ if (ifnode->initialization != NULL) {
+ ifnode->scope = scope_create(context.ast_alloc, curr_scope, ifnode->token->pos);
+ scope_enter(ifnode->scope);
+
+ SYMRES(statement_chain, &ifnode->initialization);
+ }
+
+ SYMRES(expression, &ifnode->cond);
+
+ // NOTE: These are statements because "elseif" means the `false_stmt` has an if node.
+ if (ifnode->true_stmt != NULL) SYMRES(statement, (AstNode **) &ifnode->true_stmt, NULL);
+ if (ifnode->false_stmt != NULL) SYMRES(statement, (AstNode **) &ifnode->false_stmt, NULL);
+
+ if (ifnode->initialization != NULL) scope_leave();
+ }
+
+ return Symres_Success;
+}
+
+static SymresStatus symres_while(AstIfWhile* whilenode) {
+ if (whilenode->initialization != NULL) {
+ whilenode->scope = scope_create(context.ast_alloc, curr_scope, whilenode->token->pos);
+ scope_enter(whilenode->scope);
+
+ SYMRES(statement_chain, &whilenode->initialization);
+ }
+
+ SYMRES(expression, &whilenode->cond);
+
+ if (whilenode->true_stmt) SYMRES(block, whilenode->true_stmt);
+ if (whilenode->false_stmt) SYMRES(block, whilenode->false_stmt);
+
+ if (whilenode->initialization != NULL) scope_leave();
+
+ return Symres_Success;
+}
+
+static SymresStatus symres_for(AstFor* fornode) {
+ fornode->scope = scope_create(context.ast_alloc, curr_scope, fornode->token->pos);
+ scope_enter(fornode->scope);
+ SYMRES(expression, &fornode->iter);
+ SYMRES(local, &fornode->var);
+ SYMRES(block, fornode->stmt);
+ scope_leave();
+
+ return Symres_Success;
+}
+
+static SymresStatus symres_switch(AstSwitch* switchnode) {
+ if (switchnode->initialization != NULL) {
+ switchnode->scope = scope_create(context.ast_alloc, curr_scope, switchnode->token->pos);
+ scope_enter(switchnode->scope);
+
+ SYMRES(statement_chain, &switchnode->initialization);
+ }
+
+ SYMRES(expression, &switchnode->expr);
+
+ bh_arr_each(AstSwitchCase, sc, switchnode->cases) {
+ bh_arr_each(AstTyped *, value, sc->values)
+ SYMRES(expression, value);
+
+ SYMRES(block, sc->block);
+ }
+
+ if (switchnode->default_case)
+ SYMRES(block, switchnode->default_case);
+
+ if (switchnode->initialization != NULL) scope_leave();
+
+ return Symres_Success;
+}
+
+// CLEANUP: A lot of duplication going on in this function. A proper
+// "namespace" concept would be useful to remove a lot of the fluff
+// code here. There already is try_resolve_symbol_from_node which
+// may be able to do what is needed here?
+static SymresStatus symres_use(AstUse* use) {
+ SYMRES(expression, &use->expr);
+
+ if (use->expr->kind == Ast_Kind_Package) {
+ AstPackage* package = (AstPackage *) use->expr;
+ SYMRES(package, package);
+
+ if (package->package->scope == curr_scope) return Symres_Success;
+
+ if (use->only == NULL) {
+ OnyxFilePos pos = { 0 };
+ if (use->token != NULL)
+ pos = use->token->pos;
+
+ scope_include(curr_scope, package->package->scope, pos);
+
+ } else {
+ bh_arr_each(QualifiedUse, qu, use->only) {
+ AstNode* thing = symbol_resolve(package->package->scope, qu->symbol_name);
+ if (thing == NULL) { // :SymresStall
+ if (report_unresolved_symbols) {
+ onyx_report_error(qu->symbol_name->pos,
+ "The symbol '%b' was not found in this package.",
+ qu->symbol_name->text, qu->symbol_name->length);
+ return Symres_Error;
+ } else {
+ return Symres_Yield_Macro;
+ }
+ }
+
+ symbol_introduce(curr_scope, qu->as_name, thing);
+ }
+ }
+
+ package_track_use_package(package->package, use->entity);
+
+ return Symres_Success;
+ }
+
+ if (use->expr->kind == Ast_Kind_Enum_Type) {
+ AstEnumType* et = (AstEnumType *) use->expr;
+
+ bh_arr_each(AstEnumValue *, ev, et->values)
+ symbol_introduce(curr_scope, (*ev)->token, (AstNode *) *ev);
+
+ return Symres_Success;
+ }
+
+ if (use->expr->kind == Ast_Kind_Struct_Type) {
+ AstStructType* st = (AstStructType *) use->expr;
+ if (!st->scope) return Symres_Success;
+
+ if (use->only == NULL) {
+ scope_include(curr_scope, st->scope, use->token->pos);
+
+ } else {
+ bh_arr_each(QualifiedUse, qu, use->only) {
+ AstNode* thing = symbol_resolve(st->scope, qu->symbol_name);
+ if (thing == NULL) {
+ onyx_report_error(qu->symbol_name->pos,
+ "The symbol '%b' was not found in this scope.",
+ qu->symbol_name->text, qu->symbol_name->length);
+ return Symres_Error;
+ }
+
+ symbol_introduce(curr_scope, qu->as_name, thing);
+ }
+ }
+
+ return Symres_Success;
+ }
+
+ if (use->expr->type_node == NULL && use->expr->type == NULL) goto cannot_use;
+
+ AstType* effective_type = use->expr->type_node;
+ if (effective_type->kind == Ast_Kind_Pointer_Type)
+ effective_type = ((AstPointerType *) effective_type)->elem;
+
+ if (effective_type->kind == Ast_Kind_Struct_Type ||
+ effective_type->kind == Ast_Kind_Poly_Call_Type) {
+
+ if (use->expr->type == NULL)
+ use->expr->type = type_build_from_ast(context.ast_alloc, use->expr->type_node);
+ if (use->expr->type == NULL) goto cannot_use;
+
+ Type* st = use->expr->type;
+ if (st->kind == Type_Kind_Pointer)
+ st = st->Pointer.elem;
+
+ bh_table_each_start(StructMember, st->Struct.members);
+ AstFieldAccess* fa = make_field_access(context.ast_alloc, use->expr, value.name);
+ symbol_raw_introduce(curr_scope, value.name, use->token->pos, (AstNode *) fa);
+ bh_table_each_end;
+
+ return Symres_Success;
+ }
+
+cannot_use:
+ onyx_report_error(use->token->pos, "Cannot use this because its type is unknown.");
+ return Symres_Error;
+}
+
+static SymresStatus symres_directive_solidify(AstDirectiveSolidify** psolid) {
+ AstDirectiveSolidify* solid = *psolid;
+ if (solid->resolved_proc != NULL) {
+ *psolid = (AstDirectiveSolidify *) solid->resolved_proc;
+ return Symres_Success;
+ }
+
+ SYMRES(expression, (AstTyped **) &solid->poly_proc);
+ if (solid->poly_proc && solid->poly_proc->kind == Ast_Kind_Directive_Solidify) {
+ AstPolyProc* potentially_resolved_proc = (AstPolyProc *) ((AstDirectiveSolidify *) solid->poly_proc)->resolved_proc;
+ if (!potentially_resolved_proc) return Symres_Yield_Micro;
+
+ solid->poly_proc = potentially_resolved_proc;
+ }
+
+ if (!solid->poly_proc || solid->poly_proc->kind != Ast_Kind_Polymorphic_Proc) {
+ onyx_report_error(solid->token->pos, "Expected polymorphic procedure in #solidify directive.");
+ return Symres_Error;
+ }
+
+ bh_arr_each(AstPolySolution, sln, solid->known_polyvars) {
+ // HACK: This assumes that 'ast_type' and 'value' are at the same offset.
+ SYMRES(expression, &sln->value);
+ if (onyx_has_errors()) return Symres_Error;
+
+ if (node_is_type((AstNode *) sln->value)) {
+ sln->type = type_build_from_ast(context.ast_alloc, sln->ast_type);
+ sln->kind = PSK_Type;
+ } else {
+ sln->kind = PSK_Value;
+ }
+
+ if (onyx_has_errors()) return Symres_Error;
+ }
+
+ solid->resolved_proc = polymorphic_proc_try_solidify(solid->poly_proc, solid->known_polyvars, solid->token);
+
+ // NOTE: Not a DirectiveSolidify.
+ *psolid = (AstDirectiveSolidify *) solid->resolved_proc;
+ return Symres_Success;
+}
+
+static SymresStatus symres_directive_defined(AstDirectiveDefined** pdefined) {
+ AstDirectiveDefined* defined = *pdefined;
+
+ b32 use_package_count = (context.entities.type_count[Entity_Type_Use_Package] == 0);
+ b32 old_report_unresolved_symbols = report_unresolved_symbols;
+ report_unresolved_symbols = 0;
+
+ SymresStatus ss = symres_expression(&defined->expr);
+ if ((use_package_count || old_report_unresolved_symbols) && ss != Symres_Success) {
+ // The symbol definitely was not found and there is no chance that it could be found.
+ defined->is_defined = 0;
+
+ } else {
+ if (ss == Symres_Success) {
+ defined->is_defined = 1;
+
+ } else {
+ report_unresolved_symbols = old_report_unresolved_symbols;
+ return Symres_Yield_Macro;
+ }
+ }
+
+ report_unresolved_symbols = old_report_unresolved_symbols;
+ return Symres_Success;
+}
+
+static SymresStatus symres_directive_insert(AstDirectiveInsert* insert) {
+ SYMRES(expression, &insert->code_expr);
+ return Symres_Success;
+}
+
+static SymresStatus symres_statement(AstNode** stmt, b32 *remove) {
+ if (remove) *remove = 0;
+
+ switch ((*stmt)->kind) {
+ case Ast_Kind_Return: SYMRES(return, (AstReturn *) *stmt); break;
+ case Ast_Kind_If: SYMRES(if, (AstIfWhile *) *stmt); break;
+ case Ast_Kind_Static_If: SYMRES(if, (AstIfWhile *) *stmt); break;
+ case Ast_Kind_While: SYMRES(while, (AstIfWhile *) *stmt); break;
+ case Ast_Kind_For: SYMRES(for, (AstFor *) *stmt); break;
+ case Ast_Kind_Switch: SYMRES(switch, (AstSwitch *) *stmt); break;
+ case Ast_Kind_Call: SYMRES(call, (AstCall *) *stmt); break;
+ case Ast_Kind_Argument: SYMRES(expression, (AstTyped **) &((AstArgument *) *stmt)->value); break;
+ case Ast_Kind_Block: SYMRES(block, (AstBlock *) *stmt); break;
+ case Ast_Kind_Defer: SYMRES(statement, &((AstDefer *) *stmt)->stmt, NULL); break;
+ case Ast_Kind_Jump: break;
+
+ case Ast_Kind_Local:
+ // if (remove) *remove = 1;
+ SYMRES(local, (AstLocal **) stmt);
+ break;
+
+ case Ast_Kind_Use:
+ if (remove) *remove = 1;
+ SYMRES(use, (AstUse *) *stmt);
+ break;
+
+ default: SYMRES(expression, (AstTyped **) stmt); break;
+ }
+
+ return Symres_Success;
+}
+
+static SymresStatus symres_statement_chain(AstNode** walker) {
+ b32 remove = 0;
+
+ while (*walker) {
+ SYMRES(statement, walker, &remove);
+ if (remove) {
+ remove = 0;
+ AstNode* tmp = (*walker)->next;
+ (*walker)->next = NULL;
+ (*walker) = tmp;
+
+ } else {
+ walker = &(*walker)->next;
+ }
+ }
+ return Symres_Success;
+}
+
+static SymresStatus symres_block(AstBlock* block) {
+ if (block->rules & Block_Rule_New_Scope) {
+ if (block->scope == NULL)
+ block->scope = scope_create(context.ast_alloc, curr_scope, block->token->pos);
+
+ scope_enter(block->scope);
+ }
+
+ if (block->binding_scope != NULL)
+ scope_include(curr_scope, block->binding_scope, block->token->pos);
+
+ if (block->body)
+ SYMRES(statement_chain, &block->body);
+
+ if (block->rules & Block_Rule_New_Scope)
+ scope_leave();
+
+ return Symres_Success;
+}
+
+SymresStatus symres_function_header(AstFunction* func) {
+ func->flags |= Ast_Flag_Comptime;
+
+ if (func->scope == NULL)
+ func->scope = scope_create(context.ast_alloc, curr_scope, func->token->pos);
+
+ scope_enter(func->scope);
+
+ bh_arr_each(AstParam, param, func->params) {
+ if (param->default_value != NULL) {
+ SYMRES(expression, ¶m->default_value);
+ if (onyx_has_errors()) return Symres_Error;
+ }
+ }
+
+ if ((func->flags & Ast_Flag_Params_Introduced) == 0) {
+ bh_arr_each(AstParam, param, func->params) {
+ symbol_introduce(curr_scope, param->local->token, (AstNode *) param->local);
+ }
+
+ func->flags |= Ast_Flag_Params_Introduced;
+ }
+
+ bh_arr_each(AstParam, param, func->params) {
+ if (param->local->type_node != NULL) {
+ SYMRES(type, ¶m->local->type_node);
+ }
+ }
+
+ SYMRES(type, &func->return_type);
+ if (!node_is_type((AstNode *) func->return_type)) {
+ AstType* return_type = (AstType *) strip_aliases((AstNode *) func->return_type);
+ if (return_type->kind == Ast_Kind_Symbol) return Symres_Yield_Macro;
+
+ onyx_report_error(func->token->pos, "Return type is not a type.");
+ }
+
+ scope_leave();
+
+ return Symres_Success;
+}
+
+SymresStatus symres_function(AstFunction* func) {
+ if (func->scope == NULL)
+ func->scope = scope_create(context.ast_alloc, curr_scope, func->token->pos);
+ if (func->entity_header && func->entity_header->state < Entity_State_Check_Types) return Symres_Yield_Macro;
+
+ scope_enter(func->scope);
+
+ if ((func->flags & Ast_Flag_Has_Been_Symres) == 0) {
+ bh_arr_each(AstParam, param, func->params) {
+ // CLEANUP: Currently, in order to 'use' parameters, the type must be completely
+ // resolved and built. This is excessive because all that should need to be known
+ // is the names of the members, since all that happens is implicit field accesses
+ // are placed in the scope. So instead, there should be a way to just query all the
+ // member names in the structure, without needing to know their type. This would be
+ // easy if it were not for 'use' statements in structs. It is made even more complicated
+ // by this situtation:
+ //
+ // Foo :: struct (T: type_expr) {
+ // use t : T;
+ //
+ // something_else := 5 + 6 * 8;
+ // }
+ //
+ // The 'use t : T' member requires completely knowing the type of T, to know which
+ // members should be brought in. At the moment, that requires completely building the
+ // type of Foo($T).
+ if ((param->local->flags & Ast_Flag_Param_Use) != 0 && param->use_processed == 0) {
+ if (param->local->type_node != NULL && param->local->type == NULL) {
+ param->local->type = type_build_from_ast(context.ast_alloc, param->local->type_node);
+
+ if (param->local->type == NULL) {
+ // HACK HACK HACK
+ scope_leave();
+ return Symres_Yield_Macro;
+ }
+ }
+
+ if (type_is_struct(param->local->type)) {
+ Type* st;
+ if (param->local->type->kind == Type_Kind_Struct) {
+ st = param->local->type;
+ } else {
+ st = param->local->type->Pointer.elem;
+ }
+
+ bh_table_each_start(StructMember, st->Struct.members);
+ AstFieldAccess* fa = make_field_access(context.ast_alloc, (AstTyped *) param->local, value.name);
+ symbol_raw_introduce(curr_scope, value.name, param->local->token->pos, (AstNode *) fa);
+ bh_table_each_end;
+
+ param->use_processed = 1;
+
+ } else if (param->local->type != NULL) {
+ onyx_report_error(param->local->token->pos, "Can only 'use' structures or pointers to structures.");
+
+ } else {
+ // :ExplicitTyping
+ onyx_report_error(param->local->token->pos, "Cannot deduce type of parameter '%b'; Try adding it explicitly.",
+ param->local->token->text,
+ param->local->token->length);
+ }
+ }
+ }
+
+ func->flags |= Ast_Flag_Has_Been_Symres;
+ }
+
+ SYMRES(block, func->body);
+
+ scope_leave();
+ return Symres_Success;
+}
+
+static SymresStatus symres_global(AstGlobal* global) {
+ SYMRES(type, &global->type_node);
+ return Symres_Success;
+}
+
+static SymresStatus symres_overloaded_function(AstOverloadedFunction* ofunc) {
+ bh_arr_each(OverloadOption, overload, ofunc->overloads) {
+ SYMRES(expression, &overload->option);
+ }
+ return Symres_Success;
+}
+
+static SymresStatus symres_package(AstPackage* package) {
+ if (package->package == NULL) {
+ if (!package->package_name) return Symres_Error;
+
+ package->package = package_lookup(package->package_name);
+ }
+
+ if (package->package) {
+ return Symres_Success;
+ } else {
+ if (report_unresolved_symbols) {
+ onyx_report_error(package->token->pos,
+ "Package '%s' not found in included source files.",
+ package->package_name);
+ return Symres_Error;
+ } else {
+ return Symres_Yield_Macro;
+ }
+ }
+}
+
+static SymresStatus symres_enum(AstEnumType* enum_node) {
+ if (enum_node->backing->kind == Ast_Kind_Symbol) SYMRES(symbol, (AstNode **) &enum_node->backing);
+ if (enum_node->backing == NULL) return Symres_Error;
+
+ enum_node->backing_type = type_build_from_ast(context.ast_alloc, enum_node->backing);
+ enum_node->scope = scope_create(context.ast_alloc, NULL, enum_node->token->pos);
+
+ type_build_from_ast(context.ast_alloc, (AstType *) enum_node);
+
+ u64 next_assign_value = (enum_node->flags & Ast_Flag_Enum_Is_Flags) ? 1 : 0;
+ bh_arr_each(AstEnumValue *, value, enum_node->values) {
+ symbol_introduce(enum_node->scope, (*value)->token, (AstNode *) *value);
+ (*value)->type = enum_node->etcache;
+
+ if ((*value)->value != NULL) {
+ // HACK
+ resolve_expression_type((AstTyped *) (*value)->value);
+ if (type_is_small_integer((*value)->value->type)) {
+ next_assign_value = (*value)->value->value.i;
+ } else if (type_is_integer((*value)->value->type)) {
+ next_assign_value = (*value)->value->value.l;
+ } else {
+ onyx_report_error((*value)->token->pos, "expected numeric integer literal for enum initialization");
+ return Symres_Error;
+ }
+
+ (*value)->value->type = enum_node->etcache;
+
+ } else {
+ AstNumLit* num = make_int_literal(context.ast_alloc, next_assign_value);
+ num->type = enum_node->etcache;
+
+ (*value)->value = num;
+ }
+
+ (*value)->flags |= Ast_Flag_Comptime;
+
+ if (enum_node->flags & Ast_Flag_Enum_Is_Flags) {
+ next_assign_value <<= 1;
+ } else {
+ next_assign_value++;
+ }
+ }
+ return Symres_Success;
+}
+
+static SymresStatus symres_memres_type(AstMemRes** memres) {
+ SYMRES(type, &(*memres)->type_node);
+ return Symres_Success;
+}
+
+static SymresStatus symres_memres(AstMemRes** memres) {
+ if ((*memres)->initial_value != NULL) {
+ SYMRES(expression, &(*memres)->initial_value);
+ }
+ return Symres_Success;
+}
+
+static SymresStatus symres_struct_defaults(AstType* t) {
+ if (t->kind != Ast_Kind_Struct_Type) return Symres_Error;
+
+ AstStructType* st = (AstStructType *) t;
+ if (st->scope) scope_enter(st->scope);
+
+ bh_arr_each(AstStructMember *, smem, st->members) {
+ if ((*smem)->initial_value != NULL) {
+ SYMRES(expression, &(*smem)->initial_value);
+ }
+ }
+
+ if (st->scope) scope_leave();
+ return Symres_Success;
+}
+
+static SymresStatus symres_polyproc(AstPolyProc* pp) {
+ pp->flags |= Ast_Flag_Comptime;
+ pp->poly_scope = curr_scope;
+
+ bh_arr_each(AstPolyParam, param, pp->poly_params) {
+ if (param->kind != PPK_Baked_Value) continue;
+
+ // FIX: Looking up symbols immediately in the type of the baked value does not always work
+ // because I think the following should be possible:
+ //
+ // baked_proc :: (x: $T, $f: (T) -> T) -> T ...
+ //
+ // The type of 'f' depends on resolving the value for the polyvar 'T'.
+ SYMRES(type, ¶m->type_expr);
+ }
+
+ return Symres_Success;
+}
+
+static SymresStatus symres_static_if(AstIf* static_if) {
+ SYMRES(expression, &static_if->cond);
+ return Symres_Success;
+}
+
+static SymresStatus symres_process_directive(AstNode* directive) {
+ switch (directive->kind) {
+ case Ast_Kind_Directive_Add_Overload: {
+ AstDirectiveAddOverload *add_overload = (AstDirectiveAddOverload *) directive;
+
+ SYMRES(expression, (AstTyped **) &add_overload->overloaded_function);
+ if (add_overload->overloaded_function == NULL) return Symres_Error; // NOTE: Error message will already be generated
+
+ if (add_overload->overloaded_function->kind != Ast_Kind_Overloaded_Function) {
+ onyx_report_error(add_overload->token->pos, "#add_overload directive did not resolve to an overloaded function.");
+
+ } else {
+ AstOverloadedFunction* ofunc = (AstOverloadedFunction *) add_overload->overloaded_function;
+ SYMRES(expression, (AstTyped **) &add_overload->overload);
+ add_overload_option(&ofunc->overloads, add_overload->precedence, add_overload->overload);
+ }
+
+ break;
+ }
+
+ case Ast_Kind_Directive_Operator: {
+ AstDirectiveOperator *operator = (AstDirectiveOperator *) directive;
+ SYMRES(expression, &operator->overload);
+ if (!operator->overload) return Symres_Error;
+
+ AstFunction* overload = get_function_from_node((AstNode *) operator->overload);
+ if (overload == NULL) {
+ onyx_report_error(operator->token->pos, "This cannot be used as an operator overload.");
+ return Symres_Error;
+ }
+
+ if (bh_arr_length(overload->params) != 2) {
+ onyx_report_error(operator->token->pos, "Expected 2 exactly arguments for binary operator overload.");
+ return Symres_Error;
+ }
+
+ /*if (binop_is_assignment(operator->operator)) {
+ onyx_report_error(overload->token->pos, "'%s' is not currently overloadable.", binaryop_string[operator->operator]);
+ return Symres_Error;
+ }*/
+
+ add_overload_option(&operator_overloads[operator->operator], 0, operator->overload);
+ break;
+ }
+
+ case Ast_Kind_Directive_Export: {
+ AstDirectiveExport *export = (AstDirectiveExport *) directive;
+ SYMRES(expression, &export->export);
+
+ export->export->flags |= Ast_Flag_Exported;
+
+ if (export->export->kind == Ast_Kind_Function) {
+ AstFunction *func = (AstFunction *) export->export;
+ func->exported_name = export->export_name;
+
+ if ((func->flags & Ast_Flag_Exported) != 0) {
+ if ((func->flags & Ast_Flag_Foreign) != 0) {
+ onyx_report_error(export->token->pos, "exporting a foreign function");
+ return Symres_Error;
+ }
+
+ if ((func->flags & Ast_Flag_Intrinsic) != 0) {
+ onyx_report_error(export->token->pos, "exporting a intrinsic function");
+ return Symres_Error;
+ }
+
+ // NOTE: This should never happen
+ if (func->exported_name == NULL) {
+ onyx_report_error(export->token->pos, "exporting function without a name");
+ return Symres_Error;
+ }
+ }
+ }
+
+ break;
+ }
+ }
+
+ return Symres_Success;
+}
+
+static SymresStatus symres_macro(AstMacro* macro) {
+ if (macro->body->kind == Ast_Kind_Function) {
+ SYMRES(function_header, (AstFunction *) macro->body);
+ }
+ else if (macro->body->kind == Ast_Kind_Polymorphic_Proc) {
+ SYMRES(polyproc, (AstPolyProc *) macro->body);
+ }
+
+ return Symres_Success;
+}
+
+void symres_entity(Entity* ent) {
+ Scope* old_scope = NULL;
+ if (ent->scope) {
+ old_scope = curr_scope;
+ scope_enter(ent->scope);
+ }
+
+ report_unresolved_symbols = context.cycle_detected;
+ //(context.entities.type_count[Entity_Type_Static_If] == 0 &&
+ // context.entities.type_count[Entity_Type_Use_Package] == 0)
+ //|| context.cycle_detected;
+
+ SymresStatus ss = Symres_Success;
+ EntityState next_state = Entity_State_Check_Types;
+
+ switch (ent->type) {
+ case Entity_Type_Binding: {
+ symbol_introduce(curr_scope, ent->binding->token, ent->binding->node);
+ package_reinsert_use_packages(ent->package);
+ next_state = Entity_State_Finalized;
+ break;
+ }
+
+ case Entity_Type_Static_If: ss = symres_static_if(ent->static_if); break;
+
+ case Entity_Type_Foreign_Function_Header:
+ case Entity_Type_Function_Header: ss = symres_function_header(ent->function); break;
+ case Entity_Type_Function: ss = symres_function(ent->function); break;
+
+ case Entity_Type_Foreign_Global_Header:
+ case Entity_Type_Global_Header: ss = symres_global(ent->global); break;
+
+ case Entity_Type_Use_Package:
+ case Entity_Type_Use: ss = symres_use(ent->use);
+ next_state = Entity_State_Finalized;
+ break;
+
+ case Entity_Type_Overloaded_Function: ss = symres_overloaded_function(ent->overloaded_function); break;
+ case Entity_Type_Expression: ss = symres_expression(&ent->expr); break;
+ case Entity_Type_Type_Alias: ss = symres_type(&ent->type_alias); break;
+ case Entity_Type_Enum: ss = symres_enum(ent->enum_type); break;
+ case Entity_Type_Memory_Reservation_Type: ss = symres_memres_type(&ent->mem_res); break;
+ case Entity_Type_Memory_Reservation: ss = symres_memres(&ent->mem_res); break;
+ case Entity_Type_Polymorphic_Proc: ss = symres_polyproc(ent->poly_proc); break;
+ case Entity_Type_String_Literal: ss = symres_expression(&ent->expr); break;
+ case Entity_Type_Struct_Member_Default: ss = symres_struct_defaults((AstType *) ent->type_alias); break;
+ case Entity_Type_Process_Directive: ss = symres_process_directive((AstNode *) ent->expr); break;
+ case Entity_Type_Macro: ss = symres_macro(ent->macro); break;
+
+ default: break;
+ }
+
+ if (ss == Symres_Yield_Macro) ent->macro_attempts++;
+ if (ss == Symres_Yield_Micro) ent->micro_attempts++;
+ if (ss == Symres_Success) {
+ ent->macro_attempts = 0;
+ ent->micro_attempts = 0;
+ ent->state = next_state;
+ }
+
+ if (ent->scope) curr_scope = old_scope;
+}
--- /dev/null
+#define BH_DEBUG
+#include "types.h"
+#include "astnodes.h"
+#include "utils.h"
+#include "errors.h"
+
+// NOTE: These have to be in the same order as Basic
+Type basic_types[] = {
+ { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_void, { Basic_Kind_Void, 0, 0, 1, "void" } },
+
+ { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_bool, { Basic_Kind_Bool, Basic_Flag_Boolean, 1, 1, "bool" } },
+
+ { Type_Kind_Basic, 0, 0, NULL, { Basic_Kind_Int_Unsized, Basic_Flag_Integer, 0, 0, "unsized int" } },
+ { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_i8, { Basic_Kind_I8, Basic_Flag_Integer, 1, 1, "i8" } },
+ { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_u8, { Basic_Kind_U8, Basic_Flag_Integer | Basic_Flag_Unsigned, 1, 1, "u8" } },
+ { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_i16, { Basic_Kind_I16, Basic_Flag_Integer, 2, 2, "i16" } },
+ { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_u16, { Basic_Kind_U16, Basic_Flag_Integer | Basic_Flag_Unsigned, 2, 2, "u16" } },
+ { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_i32, { Basic_Kind_I32, Basic_Flag_Integer, 4, 4, "i32" } },
+ { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_u32, { Basic_Kind_U32, Basic_Flag_Integer | Basic_Flag_Unsigned, 4, 4, "u32" } },
+ { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_i64, { Basic_Kind_I64, Basic_Flag_Integer, 8, 8, "i64" } },
+ { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_u64, { Basic_Kind_U64, Basic_Flag_Integer | Basic_Flag_Unsigned, 8, 8, "u64" } },
+
+ { Type_Kind_Basic, 0, 0, NULL, { Basic_Kind_Float_Unsized, Basic_Flag_Float, 0, 0, "unsized float" } },
+ { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_f32, { Basic_Kind_F32, Basic_Flag_Float, 4, 4, "f32" } },
+ { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_f64, { Basic_Kind_F64, Basic_Flag_Float, 8, 4, "f64" } },
+
+ { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_rawptr, { Basic_Kind_Rawptr, Basic_Flag_Pointer, 8, 8, "rawptr" } },
+
+ { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_i8x16, { Basic_Kind_I8X16, Basic_Flag_SIMD, 16, 16, "i8x16" } },
+ { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_i16x8, { Basic_Kind_I16X8, Basic_Flag_SIMD, 16, 16, "i16x8" } },
+ { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_i32x4, { Basic_Kind_I32X4, Basic_Flag_SIMD, 16, 16, "i32x4" } },
+ { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_i64x2, { Basic_Kind_I64X2, Basic_Flag_SIMD, 16, 16, "i64x2" } },
+ { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_f32x4, { Basic_Kind_F32X4, Basic_Flag_SIMD, 16, 16, "f32x4" } },
+ { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_f64x2, { Basic_Kind_F64X2, Basic_Flag_SIMD, 16, 16, "f64x2" } },
+ { Type_Kind_Basic, 0, 0, (AstType *) &basic_type_v128, { Basic_Kind_V128, Basic_Flag_SIMD, 16, 16, "v128" } },
+
+ { Type_Kind_Basic, 0, 0, NULL, { Basic_Kind_Type_Index, Basic_Flag_Type_Index, 4, 4, "type_expr" } },
+};
+
+// TODO: Document this!!
+ bh_imap type_map;
+static bh_imap type_pointer_map;
+static bh_imap type_slice_map;
+static bh_imap type_dynarr_map;
+static bh_imap type_vararg_map;
+static bh_table(u64) type_func_map;
+
+static Type* type_create(TypeKind kind, bh_allocator a, u32 extra_type_pointer_count) {
+ Type* type = bh_alloc(a, sizeof(Type) + sizeof(Type *) * extra_type_pointer_count);
+ type->kind = kind;
+ type->ast_type = NULL;
+ return type;
+}
+
+static void type_register(Type* type) {
+ static u32 next_unique_id = 1;
+ type->id = next_unique_id++;
+ if (type->ast_type) type->ast_type->type_id = type->id;
+
+ bh_imap_put(&type_map, type->id, (u64) type);
+}
+
+void types_init() {
+ bh_imap_init(&type_map, global_heap_allocator, 255);
+ bh_imap_init(&type_pointer_map, global_heap_allocator, 255);
+ bh_imap_init(&type_slice_map, global_heap_allocator, 255);
+ bh_imap_init(&type_dynarr_map, global_heap_allocator, 255);
+ bh_imap_init(&type_vararg_map, global_heap_allocator, 255);
+ bh_table_init(global_heap_allocator, type_func_map, 64);
+
+ fori (i, 0, Basic_Kind_Count) type_register(&basic_types[i]);
+}
+
+void types_dump_type_info() {
+ bh_arr_each(bh__imap_entry, entry, type_map.entries) {
+ bh_printf("%d -> %s\n", entry->key, type_get_name((Type *) entry->value));
+ }
+}
+
+b32 types_are_compatible_(Type* t1, Type* t2, b32 recurse_pointers) {
+ // NOTE: If they are pointing to the same thing,
+ // it is safe to assume they are the same type
+ if (t1 == t2) return 1;
+ if (t1 == NULL || t2 == NULL) return 0;
+ if (t1->id == t2->id) return 1;
+
+ if (t1 == &type_auto_return || t2 == &type_auto_return) {
+ return 0;
+ }
+
+ switch (t1->kind) {
+ case Type_Kind_Basic:
+ if (t2->kind == Type_Kind_Basic) {
+ // Signedness of an integer doesn't matter.
+ if ((t1->Basic.flags & Basic_Flag_Integer) && (t2->Basic.flags & Basic_Flag_Integer)) {
+ return t1->Basic.size == t2->Basic.size;
+ }
+
+ if (t1->Basic.kind == Basic_Kind_V128 || t2->Basic.kind == Basic_Kind_V128) return 1;
+ }
+
+ if (t1->Basic.kind == Basic_Kind_Rawptr && type_is_pointer(t2)) {
+ return 1;
+ }
+ break;
+
+ case Type_Kind_Pointer: {
+ if (t2->kind == Type_Kind_Pointer) {
+ if (!recurse_pointers) return 1;
+
+ if (types_are_compatible(t1->Pointer.elem, t2->Pointer.elem)) return 1;
+
+ if (t1->Pointer.elem->kind == Type_Kind_Struct && t2->Pointer.elem->kind == Type_Kind_Struct) {
+ Type* t1_struct = t1->Pointer.elem;
+ Type* t2_struct = t2->Pointer.elem;
+
+ if (t1_struct->Struct.memarr[0]->used)
+ return types_are_compatible(t2_struct, t1_struct->Struct.memarr[0]->type);
+ }
+ }
+
+ if (t2->kind == Type_Kind_Basic && t2->Basic.kind == Basic_Kind_Rawptr) return 1;
+
+ break;
+ }
+
+ case Type_Kind_Array: {
+ if (t2->kind != Type_Kind_Array) return 0;
+
+ if (t1->Array.count != 0)
+ if (t1->Array.count != t2->Array.count) return 0;
+
+ return types_are_compatible(t1->Array.elem, t2->Array.elem);
+ }
+
+ case Type_Kind_Struct: {
+ // NOTE: The check above for t1 == t2 would already catch this.
+
+ // if (t2->kind != Type_Kind_Struct) return 0;
+ // if (t1->Struct.unique_id != t2->Struct.unique_id) return 0;
+ // if (t1->Struct.mem_count != t2->Struct.mem_count) return 0;
+ return 0;
+ }
+
+ case Type_Kind_Enum: {
+ // NOTE: The check above for t1 == t2 would already catch this.
+ return 0;
+ }
+
+ case Type_Kind_Function: {
+ if (t2->kind != Type_Kind_Function) return 0;
+ if (t1->Function.param_count != t2->Function.param_count) return 0;
+
+ if (!types_are_compatible(t1->Function.return_type, t2->Function.return_type)) return 0;
+
+ if (t1->Function.param_count > 0) {
+ fori (i, 0, t1->Function.param_count) {
+ if (!types_are_compatible(t1->Function.params[i], t2->Function.params[i])) return 0;
+ }
+ }
+
+ return 1;
+ }
+
+ case Type_Kind_Slice: {
+ if (t2->kind != Type_Kind_Slice) return 0;
+ return types_are_compatible(t1->Slice.ptr_to_data->Pointer.elem, t2->Slice.ptr_to_data->Pointer.elem);
+ }
+
+ case Type_Kind_VarArgs: {
+ if (t2->kind != Type_Kind_VarArgs) return 0;
+ return types_are_compatible(t1->VarArgs.ptr_to_data->Pointer.elem, t2->VarArgs.ptr_to_data->Pointer.elem);
+ }
+
+ case Type_Kind_DynArray: {
+ if (t2->kind != Type_Kind_DynArray) return 0;
+ return types_are_compatible(t1->DynArray.ptr_to_data->Pointer.elem, t2->DynArray.ptr_to_data->Pointer.elem);
+ }
+
+ case Type_Kind_Compound: {
+ if (t2->kind != Type_Kind_Compound) return 0;
+ if (t1->Compound.count != t2->Compound.count) return 0;
+
+ fori (i, 0, (i64) t1->Compound.count) {
+ if (!types_are_compatible(t1->Compound.types[i], t2->Compound.types[i])) return 0;
+ }
+
+ return 1;
+ }
+
+ default:
+ assert(("Invalid type", 0));
+ break;
+ }
+
+ return 0;
+}
+
+b32 types_are_compatible(Type* t1, Type* t2) {
+ return types_are_compatible_(t1, t2, 1);
+}
+
+u32 type_size_of(Type* type) {
+ if (type == NULL) return 0;
+
+ switch (type->kind) {
+ case Type_Kind_Basic: return type->Basic.size;
+ case Type_Kind_Pointer: return 8;
+ case Type_Kind_Function: return 4;
+ case Type_Kind_Array: return type->Array.size;
+ case Type_Kind_Struct: return type->Struct.size;
+ case Type_Kind_Enum: return type_size_of(type->Enum.backing);
+ case Type_Kind_Slice: return 16; // HACK: These should not have to be 16 bytes in size, they should only have to be 12,
+ case Type_Kind_VarArgs: return 16; // but there are alignment issues right now with that so I decided to not fight it and just make them 16 bytes in size.
+ case Type_Kind_DynArray: return 32; // data (8), count (4), capacity (4), allocator { func (4), ---(4), data (8) }
+ case Type_Kind_Compound: return type->Compound.size;
+ default: return 0;
+ }
+}
+
+u32 type_alignment_of(Type* type) {
+ if (type == NULL) return 1;
+
+ switch (type->kind) {
+ case Type_Kind_Basic: return type->Basic.alignment;
+ case Type_Kind_Pointer: return 8;
+ case Type_Kind_Function: return 4;
+ case Type_Kind_Array: return type_alignment_of(type->Array.elem);
+ case Type_Kind_Struct: return type->Struct.alignment;
+ case Type_Kind_Enum: return type_alignment_of(type->Enum.backing);
+ case Type_Kind_Slice: return 8;
+ case Type_Kind_VarArgs: return 8;
+ case Type_Kind_DynArray: return 8;
+ case Type_Kind_Compound: return 4; // HACK
+ default: return 1;
+ }
+}
+
+Type* type_build_from_ast(bh_allocator alloc, AstType* type_node) {
+ if (type_node == NULL) return NULL;
+
+ switch (type_node->kind) {
+ case Ast_Kind_Pointer_Type: {
+ Type* ptr_type = type_make_pointer(alloc, type_build_from_ast(alloc, ((AstPointerType *) type_node)->elem));
+ if (ptr_type) ptr_type->ast_type = type_node;
+ return ptr_type;
+ }
+
+ case Ast_Kind_Function_Type: {
+ AstFunctionType* ftype_node = (AstFunctionType *) type_node;
+ u64 param_count = ftype_node->param_count;
+
+ Type* return_type = type_build_from_ast(alloc, ftype_node->return_type);
+ if (return_type == NULL) return NULL;
+
+ Type* func_type = type_create(Type_Kind_Function, alloc, param_count);
+ func_type->ast_type = type_node;
+ func_type->Function.param_count = param_count;
+ func_type->Function.needed_param_count = param_count;
+ func_type->Function.vararg_arg_pos = -1;
+ func_type->Function.return_type = return_type;
+
+ if (param_count > 0)
+ fori (i, 0, (i64) param_count) {
+ func_type->Function.params[i] = type_build_from_ast(alloc, ftype_node->params[i]);
+
+ // LEAK LEAK LEAK
+ if (func_type->Function.params[i] == NULL) return NULL;
+ }
+
+ char* name = (char *) type_get_unique_name(func_type);
+ if (bh_table_has(u64, type_func_map, name)) {
+ u64 id = bh_table_get(u64, type_func_map, name);
+ Type* existing_type = (Type *) bh_imap_get(&type_map, id);
+
+ // LEAK LEAK LEAK the func_type that is created
+ return existing_type;
+ }
+
+ type_register(func_type);
+ bh_table_put(u64, type_func_map, name, func_type->id);
+
+ return func_type;
+ }
+
+ case Ast_Kind_Array_Type: {
+ AstArrayType* a_node = (AstArrayType *) type_node;
+
+ Type* a_type = type_create(Type_Kind_Array, alloc, 0);
+ a_type->Array.elem = type_build_from_ast(alloc, a_node->elem);
+
+ u32 count = 0;
+ if (a_node->count_expr) {
+ if (a_node->count_expr->type == NULL)
+ a_node->count_expr->type = type_build_from_ast(alloc, a_node->count_expr->type_node);
+
+ if (node_is_auto_cast((AstNode *) a_node->count_expr)) {
+ a_node->count_expr = ((AstUnaryOp *) a_node)->expr;
+ }
+
+ resolve_expression_type((AstTyped *) a_node->count_expr);
+
+ // NOTE: Currently, the count_expr has to be an I32 literal
+ if (a_node->count_expr->type->kind != Type_Kind_Basic
+ || a_node->count_expr->type->Basic.kind != Basic_Kind_I32) {
+ onyx_report_error(type_node->token->pos, "Array type expects type 'i32' for size, got '%s'.",
+ type_get_name(a_node->count_expr->type));
+ return NULL;
+ }
+
+ count = get_expression_integer_value((AstTyped *) a_node->count_expr);
+ }
+
+ a_type->Array.count = count;
+ a_type->Array.size = type_size_of(a_type->Array.elem) * count;
+
+ type_register(a_type);
+ return a_type;
+ }
+
+ case Ast_Kind_Struct_Type: {
+ AstStructType* s_node = (AstStructType *) type_node;
+ if (s_node->stcache != NULL && s_node->stcache_is_valid) return s_node->stcache;
+
+ Type* s_type;
+ if (s_node->stcache == NULL) {
+ s_type = type_create(Type_Kind_Struct, alloc, 0);
+ s_node->stcache = s_type;
+
+ s_type->ast_type = type_node;
+ s_type->Struct.name = s_node->name;
+ s_type->Struct.mem_count = bh_arr_length(s_node->members);
+ type_register(s_type);
+
+ s_type->Struct.memarr = NULL;
+ bh_table_init(global_heap_allocator, s_type->Struct.members, s_type->Struct.mem_count + 1);
+ bh_arr_new(global_heap_allocator, s_type->Struct.memarr, s_type->Struct.mem_count);
+
+ } else {
+ s_type = s_node->stcache;
+ }
+
+ s_type->Struct.poly_sln = NULL;
+
+ bh_arr_clear(s_type->Struct.memarr);
+ bh_table_clear(s_type->Struct.members);
+
+ s_node->stcache_is_valid = 1;
+
+ b32 is_union = (s_node->flags & Ast_Flag_Struct_Is_Union) != 0;
+ u32 size = 0;
+ u32 offset = 0;
+ u32 alignment = 1, mem_alignment;
+ u32 idx = 0;
+ bh_arr_each(AstStructMember *, member, s_node->members) {
+ if ((*member)->type == NULL)
+ (*member)->type = type_build_from_ast(alloc, (*member)->type_node);
+
+ if ((*member)->type == NULL) {
+ s_node->stcache_is_valid = 0;
+ return NULL;
+ }
+
+ mem_alignment = type_alignment_of((*member)->type);
+ if (mem_alignment <= 0) {
+ onyx_report_error((*member)->token->pos, "Invalid member type: %s. Has alignment %d", type_get_name((*member)->type), mem_alignment);
+ return NULL;
+ }
+
+ if (mem_alignment > alignment) alignment = mem_alignment;
+ if (offset % mem_alignment != 0) {
+ offset += mem_alignment - (offset % mem_alignment);
+ }
+
+ token_toggle_end((*member)->token);
+ StructMember smem = {
+ .offset = offset,
+ .type = (*member)->type,
+ .idx = idx,
+ .name = bh_strdup(alloc, (*member)->token->text),
+ .initial_value = &(*member)->initial_value,
+ .included_through_use = 0,
+ .used = (((*member)->flags & Ast_Flag_Struct_Mem_Used) != 0),
+ };
+
+ if (bh_table_has(StructMember, s_type->Struct.members, (*member)->token->text)) {
+ onyx_report_error((*member)->token->pos, "Duplicate struct member, '%s'.", (*member)->token->text);
+ return NULL;
+ }
+ bh_table_put(StructMember, s_type->Struct.members, (*member)->token->text, smem);
+ token_toggle_end((*member)->token);
+
+ if (smem.used) {
+ assert((*member)->type->kind == Type_Kind_Struct);
+
+ bh_arr_each(StructMember*, psmem, (*member)->type->Struct.memarr) {
+ StructMember new_smem = {
+ .offset = offset + (*psmem)->offset,
+ .type = (*psmem)->type,
+ .idx = -1, // Dummy value because I don't think this is needed.
+ .name = (*psmem)->name,
+ .initial_value = (*psmem)->initial_value,
+ .included_through_use = 1,
+ .used = 0,
+ };
+
+ if (bh_table_has(StructMember, s_type->Struct.members, (*psmem)->name)) {
+ onyx_report_error((*member)->token->pos, "Duplicate struct member, '%s'.", (*psmem)->name);
+ return NULL;
+ }
+ bh_table_put(StructMember, s_type->Struct.members, (*psmem)->name, new_smem);
+ }
+ }
+
+ u32 type_size = type_size_of((*member)->type);
+
+ if (!is_union) offset += type_size;
+ if (!is_union) size = offset;
+ else size = bh_max(size, type_size);
+
+ idx++;
+ }
+
+ // NOTE: Need to do a second pass because the references to the
+ // elements of the table may change if the internal arrays of the
+ // table need to be resized.
+ bh_arr_each(AstStructMember *, member, s_node->members) {
+ token_toggle_end((*member)->token);
+ bh_arr_push(s_type->Struct.memarr, &bh_table_get(StructMember, s_type->Struct.members, (*member)->token->text));
+ token_toggle_end((*member)->token);
+ }
+
+ alignment = bh_max(s_node->min_alignment, alignment);
+ s_type->Struct.alignment = alignment;
+
+ if (size % alignment != 0) {
+ size += alignment - (size % alignment);
+ }
+
+ size = bh_max(s_node->min_size, size);
+ s_type->Struct.size = size;
+
+ s_type->Struct.linear_members = NULL;
+ bh_arr_new(global_heap_allocator, s_type->Struct.linear_members, s_type->Struct.mem_count);
+ build_linear_types_with_offset(s_type, &s_type->Struct.linear_members, 0);
+
+ return s_type;
+ }
+
+ case Ast_Kind_Enum_Type: {
+ AstEnumType* enum_node = (AstEnumType *) type_node;
+ if (enum_node->etcache) return enum_node->etcache;
+ if (enum_node->backing_type == NULL) return NULL;
+
+ Type* enum_type = type_create(Type_Kind_Enum, alloc, 0);
+ enum_node->etcache = enum_type;
+
+ enum_type->ast_type = type_node;
+ enum_type->Enum.backing = enum_node->backing_type;
+ enum_type->Enum.name = enum_node->name;
+ enum_type->Enum.is_flags = enum_node->flags & Ast_Flag_Enum_Is_Flags;
+
+ type_register(enum_type);
+ return enum_type;
+ }
+
+ case Ast_Kind_Slice_Type: {
+ Type* slice_type = type_make_slice(alloc, type_build_from_ast(alloc, ((AstSliceType *) type_node)->elem));
+ if (slice_type) slice_type->ast_type = type_node;
+ return slice_type;
+ }
+
+ case Ast_Kind_DynArr_Type: {
+ Type* dynarr_type = type_make_dynarray(alloc, type_build_from_ast(alloc, ((AstDynArrType *) type_node)->elem));
+ if (dynarr_type) dynarr_type->ast_type = type_node;
+ return dynarr_type;
+ }
+
+ case Ast_Kind_VarArg_Type: {
+ Type* va_type = type_make_varargs(alloc, type_build_from_ast(alloc, ((AstVarArgType *) type_node)->elem));
+ if (va_type) va_type->ast_type = type_node;
+ return va_type;
+ }
+
+ case Ast_Kind_Basic_Type: {
+ return ((AstBasicType *) type_node)->basic_type;
+ }
+
+ case Ast_Kind_Type_Alias: {
+ Type* type = type_build_from_ast(alloc, ((AstTypeAlias *) type_node)->to);
+ if (type && type->ast_type) type_node->type_id = type->id;
+ return type;
+ }
+
+ case Ast_Kind_Type_Raw_Alias:
+ return ((AstTypeRawAlias *) type_node)->to;
+
+ case Ast_Kind_Poly_Struct_Type:
+ //onyx_report_error(type_node->token->pos,
+ // "This structure is polymorphic, which means you need to provide arguments to it to make it a concrete structure. "
+ // "This error message is probably in the wrong place, so look through your code for uses of this struct.");
+ return NULL;
+ break;
+
+ case Ast_Kind_Poly_Call_Type: {
+ AstPolyCallType* pc_type = (AstPolyCallType *) type_node;
+ pc_type->callee = (AstType *) strip_aliases((AstNode *) pc_type->callee);
+
+ if (!(pc_type->callee && pc_type->callee->kind == Ast_Kind_Poly_Struct_Type)) {
+ // If it is an unresolved field access or symbol, just return because an error will be printed elsewhere.
+ if (pc_type->callee->kind == Ast_Kind_Field_Access || pc_type->callee->kind == Ast_Kind_Symbol) return NULL;
+
+ onyx_report_error(pc_type->token->pos, "Cannot instantiate a concrete type off of a non-polymorphic type.");
+ onyx_report_error(pc_type->callee->token->pos, "Here is the type trying to be instantiated. (%s)", onyx_ast_node_kind_string(pc_type->callee->kind));
+ return NULL;
+ }
+
+ AstPolyStructType* ps_type = (AstPolyStructType *) pc_type->callee;
+
+ bh_arr(AstPolySolution) slns = NULL;
+ bh_arr_new(global_heap_allocator, slns, bh_arr_length(pc_type->params));
+ bh_arr_each(AstNode *, given, pc_type->params) {
+ if (node_is_type(*given)) {
+ Type* param_type = type_build_from_ast(alloc, (AstType *) *given);
+
+ // LEAK LEAK LEAK
+ if (param_type == NULL) return NULL;
+
+ bh_arr_push(slns, ((AstPolySolution) {
+ .kind = PSK_Type,
+ .type = param_type,
+ }));
+ } else {
+ bh_arr_push(slns, ((AstPolySolution) {
+ .kind = PSK_Value,
+ .value = (AstTyped *) *given,
+ }));
+ }
+ }
+
+ AstStructType* concrete = polymorphic_struct_lookup(ps_type, slns, pc_type->token->pos);
+
+ // This should be copied in the previous function.
+ // CLEANUP: Maybe don't copy it and just use this one since it is allocated on the heap?
+ bh_arr_free(slns);
+
+ if (!concrete) return NULL;
+ Type* struct_type = type_build_from_ast(alloc, (AstType *) concrete);
+ struct_type->Struct.constructed_from = (AstType *) ps_type;
+ return struct_type;
+ }
+
+ case Ast_Kind_Type_Compound: {
+ AstCompoundType* ctype = (AstCompoundType *) type_node;
+
+ i64 type_count = bh_arr_length(ctype->types);
+
+ Type* comp_type = type_create(Type_Kind_Compound, alloc, type_count);
+ comp_type->Compound.size = 0;
+ comp_type->Compound.count = type_count;
+
+ fori (i, 0, type_count) {
+ assert(ctype->types[i] != NULL);
+ comp_type->Compound.types[i] = type_build_from_ast(alloc, ctype->types[i]);
+
+ // LEAK LEAK LEAK
+ if (comp_type->Compound.types[i] == NULL) return NULL;
+
+ comp_type->Compound.size += bh_max(type_size_of(comp_type->Compound.types[i]), 4);
+ }
+
+ bh_align(comp_type->Compound.size, 4);
+
+ comp_type->Compound.linear_members = NULL;
+ bh_arr_new(global_heap_allocator, comp_type->Compound.linear_members, comp_type->Compound.count);
+ build_linear_types_with_offset(comp_type, &comp_type->Compound.linear_members, 0);
+
+ type_register(comp_type);
+ return comp_type;
+ }
+
+ case Ast_Kind_Alias: {
+ AstAlias* alias = (AstAlias *) type_node;
+ return type_build_from_ast(alloc, (AstType *) alias->alias);
+ }
+
+ case Ast_Kind_Typeof: {
+ AstTypeOf* type_of = (AstTypeOf *) type_node;
+ if (type_of->resolved_type != NULL) {
+ return type_of->resolved_type;
+ }
+
+ return NULL;
+ }
+ }
+
+ return NULL;
+}
+
+// CLEANUP: This needs to be merged with the very similar code from up above.
+Type* type_build_function_type(bh_allocator alloc, AstFunction* func) {
+ u64 param_count = bh_arr_length(func->params);
+
+ Type* return_type = type_build_from_ast(alloc, func->return_type);
+ if (return_type == NULL) return NULL;
+
+ Type* func_type = type_create(Type_Kind_Function, alloc, param_count);
+ func_type->Function.param_count = param_count;
+ func_type->Function.needed_param_count = 0;
+ func_type->Function.vararg_arg_pos = -1;
+ func_type->Function.return_type = return_type;
+
+ if (param_count > 0) {
+ i32 i = 0;
+ bh_arr_each(AstParam, param, func->params) {
+ if (param->default_value == NULL && param->vararg_kind == VA_Kind_Not_VA)
+ func_type->Function.needed_param_count++;
+
+ if (param->vararg_kind == VA_Kind_Untyped)
+ func_type->Function.vararg_arg_pos = i;
+
+ func_type->Function.params[i++] = param->local->type;
+ }
+ }
+
+ // CopyPaste from above in type_build_from_ast
+ char* name = (char *) type_get_unique_name(func_type);
+ if (bh_table_has(u64, type_func_map, name)) {
+ u64 id = bh_table_get(u64, type_func_map, name);
+ Type* existing_type = (Type *) bh_imap_get(&type_map, id);
+
+ // LEAK LEAK LEAK the func_type that is created
+ return existing_type;
+ }
+
+ type_register(func_type);
+ bh_table_put(u64, type_func_map, name, func_type->id);
+
+ return func_type;
+}
+
+Type* type_build_compound_type(bh_allocator alloc, AstCompound* compound) {
+ i64 expr_count = bh_arr_length(compound->exprs);
+ fori (i, 0, expr_count) {
+ if (compound->exprs[i]->type == NULL) return NULL;
+ }
+
+ Type* comp_type = type_create(Type_Kind_Compound, alloc, expr_count);
+ comp_type->Compound.size = 0;
+ comp_type->Compound.count = expr_count;
+
+ fori (i, 0, expr_count) {
+ assert(compound->exprs[i]->type != NULL);
+ comp_type->Compound.types[i] = compound->exprs[i]->type;
+ comp_type->Compound.size += bh_max(type_size_of(comp_type->Compound.types[i]), 4);
+ }
+
+ bh_align(comp_type->Compound.size, 4);
+
+ comp_type->Compound.linear_members = NULL;
+ bh_arr_new(global_heap_allocator, comp_type->Compound.linear_members, comp_type->Compound.count);
+ build_linear_types_with_offset(comp_type, &comp_type->Compound.linear_members, 0);
+
+ type_register(comp_type);
+ return comp_type;
+}
+
+Type* type_make_pointer(bh_allocator alloc, Type* to) {
+ if (to == NULL) return NULL;
+
+ assert(to->id > 0);
+ u64 ptr_id = bh_imap_get(&type_pointer_map, to->id);
+ if (ptr_id > 0) {
+ Type* ptr_type = (Type *) bh_imap_get(&type_map, ptr_id);
+ return ptr_type;
+
+ } else {
+ Type* ptr_type = type_create(Type_Kind_Pointer, alloc, 0);
+ ptr_type->Pointer.base.flags |= Basic_Flag_Pointer;
+ ptr_type->Pointer.base.size = 8;
+ ptr_type->Pointer.elem = to;
+
+ type_register(ptr_type);
+ bh_imap_put(&type_pointer_map, to->id, ptr_type->id);
+
+ return ptr_type;
+ }
+}
+
+Type* type_make_array(bh_allocator alloc, Type* to, u32 count) {
+ if (to == NULL) return NULL;
+
+ Type* arr_type = type_create(Type_Kind_Array, alloc, 0);
+ arr_type->Array.count = count;
+ arr_type->Array.elem = to;
+ arr_type->Array.size = count * type_size_of(to);
+
+ // :TypeCanBeDuplicated
+ type_register(arr_type);
+ return arr_type;
+}
+
+Type* type_make_slice(bh_allocator alloc, Type* of) {
+ if (of == NULL) return NULL;
+
+ assert(of->id > 0);
+ u64 slice_id = bh_imap_get(&type_slice_map, of->id);
+ if (slice_id > 0) {
+ Type* slice_type = (Type *) bh_imap_get(&type_map, slice_id);
+ return slice_type;
+
+ } else {
+ Type* slice_type = type_create(Type_Kind_Slice, alloc, 0);
+ type_register(slice_type);
+ bh_imap_put(&type_slice_map, of->id, slice_type->id);
+
+ slice_type->Slice.ptr_to_data = type_make_pointer(alloc, of);
+
+ return slice_type;
+ }
+}
+
+Type* type_make_dynarray(bh_allocator alloc, Type* of) {
+ if (of == NULL) return NULL;
+
+ assert(of->id > 0);
+ u64 dynarr_id = bh_imap_get(&type_dynarr_map, of->id);
+ if (dynarr_id > 0) {
+ Type* dynarr = (Type *) bh_imap_get(&type_map, dynarr_id);
+ return dynarr;
+
+ } else {
+ Type* dynarr = type_create(Type_Kind_DynArray, alloc, 0);
+ type_register(dynarr);
+ bh_imap_put(&type_dynarr_map, of->id, dynarr->id);
+
+ dynarr->DynArray.ptr_to_data = type_make_pointer(alloc, of);
+
+ return dynarr;
+ }
+}
+
+Type* type_make_varargs(bh_allocator alloc, Type* of) {
+ if (of == NULL) return NULL;
+
+ assert(of->id > 0);
+ u64 vararg_id = bh_imap_get(&type_vararg_map, of->id);
+ if (vararg_id > 0) {
+ Type* va_type = (Type *) bh_imap_get(&type_map, vararg_id);
+ return va_type;
+
+ } else {
+ Type* va_type = type_create(Type_Kind_VarArgs, alloc, 0);
+ type_register(va_type);
+ bh_imap_put(&type_vararg_map, of->id, va_type->id);
+
+ va_type->VarArgs.ptr_to_data = type_make_pointer(alloc, of);
+
+ return va_type;
+ }
+}
+
+void build_linear_types_with_offset(Type* type, bh_arr(TypeWithOffset)* pdest, u32 offset) {
+ if (type_is_structlike_strict(type)) {
+ u32 mem_count = type_structlike_mem_count(type);
+ StructMember smem = { 0 };
+ fori (i, 0, mem_count) {
+ type_lookup_member_by_idx(type, i, &smem);
+ build_linear_types_with_offset(smem.type, pdest, offset + smem.offset);
+ }
+
+ } else if (type->kind == Type_Kind_Compound) {
+ u32 elem_offset = 0;
+ fori (i, 0, type->Compound.count) {
+ build_linear_types_with_offset(type->Compound.types[i], pdest, offset + elem_offset);
+ elem_offset += bh_max(type_size_of(type->Compound.types[i]), 4);
+ }
+
+ } else {
+ bh_arr(TypeWithOffset) dest = *pdest;
+
+ TypeWithOffset two;
+ two.type = type;
+ two.offset = offset;
+ bh_arr_push(dest, two);
+
+ *pdest = dest;
+ }
+}
+
+const char* type_get_unique_name(Type* type) {
+ if (type == NULL) return "unknown";
+
+ switch (type->kind) {
+ case Type_Kind_Basic: return type->Basic.name;
+ case Type_Kind_Pointer: return bh_aprintf(global_scratch_allocator, "^%s", type_get_unique_name(type->Pointer.elem));
+ case Type_Kind_Array: return bh_aprintf(global_scratch_allocator, "[%d] %s", type->Array.count, type_get_unique_name(type->Array.elem));
+ case Type_Kind_Struct:
+ if (type->Struct.name)
+ return bh_aprintf(global_scratch_allocator, "%s@%l", type->Struct.name, type->id);
+ else
+ return bh_aprintf(global_scratch_allocator, "%s@%l", "<anonymous struct>", type->id);
+ case Type_Kind_Enum:
+ if (type->Enum.name)
+ return bh_aprintf(global_scratch_allocator, "%s@%l", type->Enum.name, type->id);
+ else
+ return bh_aprintf(global_scratch_allocator, "%s@%l", "<anonymous enum>", type->id);
+
+ case Type_Kind_Slice: return bh_aprintf(global_scratch_allocator, "[] %s", type_get_unique_name(type->Slice.ptr_to_data->Pointer.elem));
+ case Type_Kind_VarArgs: return bh_aprintf(global_scratch_allocator, "..%s", type_get_unique_name(type->VarArgs.ptr_to_data->Pointer.elem));
+ case Type_Kind_DynArray: return bh_aprintf(global_scratch_allocator, "[..] %s", type_get_unique_name(type->DynArray.ptr_to_data->Pointer.elem));
+
+ case Type_Kind_Function: {
+ char buf[1024];
+ memset(buf, 0, 1024);
+
+ strncat(buf, "(", 1023);
+ fori (i, 0, type->Function.param_count) {
+ strncat(buf, type_get_unique_name(type->Function.params[i]), 1023);
+
+ if (i >= type->Function.needed_param_count)
+ strncat(buf, "?", 1023);
+
+ if (i != type->Function.param_count - 1)
+ strncat(buf, ", ", 1023);
+ }
+
+ strncat(buf, ") -> ", 1023);
+ strncat(buf, type_get_unique_name(type->Function.return_type), 1023);
+
+ return bh_aprintf(global_scratch_allocator, "%s", buf);
+ }
+
+ case Type_Kind_Compound: {
+ char buf[1024];
+ memset(buf, 0, 1024);
+
+ strncat(buf, "(", 1023);
+ fori (i, 0, type->Compound.count) {
+ strncat(buf, type_get_unique_name(type->Compound.types[i]), 1023);
+ if (i != type->Compound.count - 1)
+ strncat(buf, ", ", 1023);
+ }
+ strncat(buf, ")", 1023);
+
+ return bh_aprintf(global_scratch_allocator, "%s", buf);
+ }
+
+
+ default: return "unknown";
+ }
+}
+
+const char* type_get_name(Type* type) {
+ if (type == NULL) return "unknown";
+
+ switch (type->kind) {
+ case Type_Kind_Basic: return type->Basic.name;
+ case Type_Kind_Pointer: return bh_aprintf(global_scratch_allocator, "^%s", type_get_name(type->Pointer.elem));
+ case Type_Kind_Array: return bh_aprintf(global_scratch_allocator, "[%d] %s", type->Array.count, type_get_name(type->Array.elem));
+ case Type_Kind_Struct:
+ if (type->Struct.name)
+ return type->Struct.name;
+ else
+ return "<anonymous struct>";
+ case Type_Kind_Enum:
+ if (type->Enum.name)
+ return type->Enum.name;
+ else
+ return "<anonymous enum>";
+
+ case Type_Kind_Slice: return bh_aprintf(global_scratch_allocator, "[] %s", type_get_name(type->Slice.ptr_to_data->Pointer.elem));
+ case Type_Kind_VarArgs: return bh_aprintf(global_scratch_allocator, "..%s", type_get_name(type->VarArgs.ptr_to_data->Pointer.elem));
+ case Type_Kind_DynArray: return bh_aprintf(global_scratch_allocator, "[..] %s", type_get_name(type->DynArray.ptr_to_data->Pointer.elem));
+
+ case Type_Kind_Function: {
+ char buf[512];
+ fori (i, 0, 512) buf[i] = 0;
+
+ strncat(buf, "(", 511);
+ fori (i, 0, type->Function.param_count) {
+ strncat(buf, type_get_name(type->Function.params[i]), 511);
+ if (i != type->Function.param_count - 1)
+ strncat(buf, ", ", 511);
+ }
+
+ strncat(buf, ") -> ", 511);
+ strncat(buf, type_get_name(type->Function.return_type), 511);
+
+ return bh_aprintf(global_scratch_allocator, "%s", buf);
+ }
+
+ case Type_Kind_Compound: {
+ char buf[512];
+ fori (i, 0, 512) buf[i] = 0;
+
+ strncat(buf, "(", 511);
+ fori (i, 0, type->Compound.count) {
+ strncat(buf, type_get_name(type->Compound.types[i]), 511);
+ if (i != type->Compound.count - 1)
+ strncat(buf, ", ", 511);
+ }
+ strncat(buf, ")", 511);
+
+ return bh_aprintf(global_scratch_allocator, "%s", buf);
+ }
+
+ default: return "unknown";
+ }
+}
+
+u32 type_get_alignment_log2(Type* type) {
+ i32 store_size = type_alignment_of(type);
+ if (store_size == 1) return 0;
+ else if (store_size == 2) return 1;
+ else if (store_size == 4) return 2;
+ else if (store_size == 8) return 3;
+ else if (store_size == 16) return 4;
+ return 2;
+}
+
+static const StructMember slice_members[] = {
+ { 0, 0, NULL, "data", NULL, 0, 0 },
+ { 8, 1, &basic_types[Basic_Kind_U32], "count", NULL, 0, 0 },
+};
+
+static const StructMember array_members[] = {
+ { 0, 0, NULL, "data", NULL, 0, 0 },
+ { 8, 1, &basic_types[Basic_Kind_U32], "count", NULL, 0, 0 },
+ { 12, 2, &basic_types[Basic_Kind_U32], "capacity", NULL, 0, 0 },
+ { 16, 3, NULL, "allocator", NULL, 0, 0 },
+};
+
+b32 type_lookup_member(Type* type, char* member, StructMember* smem) {
+ if (type->kind == Type_Kind_Pointer) type = type->Pointer.elem;
+
+ switch (type->kind) {
+ case Type_Kind_Struct: {
+ TypeStruct* stype = &type->Struct;
+
+ if (!bh_table_has(StructMember, stype->members, member)) return 0;
+ *smem = bh_table_get(StructMember, stype->members, member);
+ return 1;
+ }
+
+ case Type_Kind_VarArgs:
+ case Type_Kind_Slice: {
+ fori (i, 0, (i64) (sizeof(slice_members) / sizeof(StructMember))) {
+ if (strcmp(slice_members[i].name, member) == 0) {
+ *smem = slice_members[i];
+ if (smem->idx == 0) smem->type = type->Slice.ptr_to_data;
+
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ case Type_Kind_DynArray: {
+ fori (i, 0, (i64) (sizeof(array_members) / sizeof(StructMember))) {
+ if (strcmp(array_members[i].name, member) == 0) {
+ *smem = array_members[i];
+ if (smem->idx == 0) smem->type = type->DynArray.ptr_to_data;
+ if (smem->idx == 3) smem->type = type_build_from_ast(context.ast_alloc, builtin_allocator_type);
+
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ default: return 0;
+ }
+}
+
+b32 type_lookup_member_by_idx(Type* type, i32 idx, StructMember* smem) {
+ if (type->kind == Type_Kind_Pointer) type = type->Pointer.elem;
+
+ switch (type->kind) {
+ case Type_Kind_Struct: {
+ TypeStruct* stype = &type->Struct;
+
+ if (idx > stype->mem_count) return 0;
+ *smem = *stype->memarr[idx];
+ return 1;
+ }
+
+ // HACK: This relies on the fact that the structures for Slice and VarArgs
+ // are identical. - brendanfh 2020/09/07
+ case Type_Kind_VarArgs:
+ case Type_Kind_Slice: {
+ if (idx > 2) return 0;
+
+ *smem = slice_members[idx];
+ if (smem->idx == 0) smem->type = type->Slice.ptr_to_data;
+
+ return 1;
+ }
+
+ case Type_Kind_DynArray: {
+ if (idx > 4) return 0;
+
+ *smem = array_members[idx];
+ if (idx == 0) smem->type = type->DynArray.ptr_to_data;
+ if (idx == 3) smem->type = type_build_from_ast(context.ast_alloc, builtin_allocator_type);
+
+ return 1;
+ }
+
+ default: return 0;
+ }
+}
+
+i32 type_linear_member_count(Type* type) {
+ switch (type->kind) {
+ case Type_Kind_Slice:
+ case Type_Kind_VarArgs: return 2;
+ case Type_Kind_DynArray: return 5;
+ case Type_Kind_Compound: return bh_arr_length(type->Compound.linear_members);
+ case Type_Kind_Struct: return bh_arr_length(type->Struct.linear_members);
+ default: return 1;
+ }
+}
+
+b32 type_linear_member_lookup(Type* type, i32 idx, TypeWithOffset* two) {
+ switch (type->kind) {
+ case Type_Kind_Slice:
+ case Type_Kind_VarArgs: {
+ if (idx == 0) {
+ two->type = type->Slice.ptr_to_data;
+ two->offset = 0;
+ }
+ if (idx == 1) {
+ two->type = &basic_types[Basic_Kind_U32];
+ two->offset = 8;
+ }
+
+ return 1;
+ }
+ case Type_Kind_DynArray: {
+ if (idx == 0) {
+ two->type = type->DynArray.ptr_to_data;
+ two->offset = 0;
+ }
+ if (idx == 1) {
+ two->type = &basic_types[Basic_Kind_U32];
+ two->offset = 8;
+ }
+ if (idx == 2) {
+ two->type = &basic_types[Basic_Kind_U32];
+ two->offset = 12;
+ }
+ if (idx == 3 || idx == 4) {
+ Type* allocator_type = type_build_from_ast(context.ast_alloc, builtin_allocator_type);
+ type_linear_member_lookup(allocator_type, idx - 3, two);
+ two->offset += 16;
+ }
+
+ return 1;
+ }
+ case Type_Kind_Compound: *two = type->Compound.linear_members[idx]; return 1;
+ case Type_Kind_Struct: *two = type->Struct.linear_members[idx]; return 1;
+ default: {
+ if (idx > 0) return 0;
+ two->offset = 0;
+ two->type = type;
+ return 1;
+ }
+ }
+}
+
+i32 type_get_idx_of_linear_member_with_offset(Type* type, u32 offset) {
+ switch (type->kind) {
+ case Type_Kind_Slice:
+ case Type_Kind_VarArgs: {
+ if (offset == 0) return 0;
+ if (offset == 8) return 1;
+ return -1;
+ }
+ case Type_Kind_DynArray: {
+ if (offset == 0) return 0;
+ if (offset == 8) return 1;
+ if (offset == 12) return 2;
+ if (offset == 16) return 3;
+ if (offset == 24) return 4;
+ return -1;
+ }
+ case Type_Kind_Compound: {
+ i32 idx = 0;
+ bh_arr_each(TypeWithOffset, two, type->Compound.linear_members) {
+ if (two->offset == offset) return idx;
+ idx++;
+ }
+
+ return -1;
+ }
+ case Type_Kind_Struct: {
+ i32 idx = 0;
+ bh_arr_each(TypeWithOffset, two, type->Struct.linear_members) {
+ if (two->offset == offset) return idx;
+ idx++;
+ }
+
+ return -1;
+ }
+ default: return -1;
+ }
+}
+
+b32 type_struct_is_simple(Type* type) {
+ if (type->kind != Type_Kind_Struct) return 0;
+
+ b32 is_simple = 1;
+ bh_arr_each(StructMember *, mem, type->Struct.memarr) {
+ if (type_is_compound((*mem)->type)) {
+ is_simple = 0;
+ break;
+ }
+ }
+
+ return is_simple;
+}
+
+b32 type_is_pointer(Type* type) {
+ if (type == NULL) return 0;
+ return type->kind == Type_Kind_Pointer;
+}
+
+b32 type_is_rawptr(Type* type) {
+ if (type == NULL) return 0;
+ return type->kind == Type_Kind_Basic && type->Basic.kind == Basic_Kind_Rawptr;
+}
+
+b32 type_is_array(Type* type) {
+ if (type == NULL) return 0;
+ return type->kind == Type_Kind_Array;
+}
+
+b32 type_is_struct(Type* type) {
+ if (type == NULL) return 0;
+ if (type->kind == Type_Kind_Struct) return 1;
+ if (type->kind == Type_Kind_Pointer)
+ if (type->Pointer.elem != NULL && type->Pointer.elem->kind == Type_Kind_Struct) return 1;
+ return 0;
+}
+
+b32 type_is_bool(Type* type) {
+ if (type == NULL) return 0;
+ return type != NULL && type->kind == Type_Kind_Basic && type->Basic.kind == Basic_Kind_Bool;
+}
+
+b32 type_is_small_integer(Type* type) {
+ if (type == NULL) return 0;
+ if (type->kind != Type_Kind_Basic) return 0;
+
+ return type->Basic.kind >= Basic_Kind_I8 && type->Basic.kind <= Basic_Kind_U32;
+}
+
+b32 type_is_integer(Type* type) {
+ if (type == NULL) return 0;
+ if (type->kind != Type_Kind_Basic) return 0;
+
+ return (type->Basic.kind >= Basic_Kind_I8 && type->Basic.kind <= Basic_Kind_U64)
+ || type->Basic.kind == Basic_Kind_Type_Index;
+}
+
+b32 type_is_numeric(Type* type) {
+ if (type == NULL) return 0;
+ if (type->kind == Type_Kind_Enum) return 1;
+ if (type->kind != Type_Kind_Basic) return 0;
+
+ return type->Basic.kind >= Basic_Kind_Int_Unsized && type->Basic.kind <= Basic_Kind_F64;
+}
+
+b32 type_is_compound(Type* type) {
+ if (type == NULL) return 0;
+ return type->kind != Type_Kind_Basic
+ && type->kind != Type_Kind_Pointer
+ && type->kind != Type_Kind_Enum
+ && type->kind != Type_Kind_Function
+ && type->kind != Type_Kind_Array;
+}
+
+b32 type_is_simd(Type* type) {
+ if (type == NULL) return 0;
+ if (type->kind != Type_Kind_Basic) return 0;
+ return type->Basic.flags & Basic_Flag_SIMD;
+}
+
+b32 type_results_in_void(Type* type) {
+ return (type == NULL)
+ || (type->kind == Type_Kind_Basic && type->Basic.kind == Basic_Kind_Void)
+ || ( (type->kind == Type_Kind_Function)
+ && (type->Function.return_type->kind == Type_Kind_Basic)
+ && (type->Function.return_type->Basic.kind == Basic_Kind_Void));
+}
+
+b32 type_is_array_accessible(Type* type) {
+ if (type == NULL) return 0;
+ if (type_is_pointer(type)) return 1;
+ if (type->kind == Type_Kind_Array) return 1;
+ if (type->kind == Type_Kind_Slice) return 1;
+ if (type->kind == Type_Kind_DynArray) return 1;
+ if (type->kind == Type_Kind_VarArgs) return 1;
+ return 0;
+}
+
+b32 type_is_structlike(Type* type) {
+ if (type == NULL) return 0;
+ if (type->kind == Type_Kind_Struct) return 1;
+ if (type->kind == Type_Kind_Slice) return 1;
+ if (type->kind == Type_Kind_DynArray) return 1;
+ if (type->kind == Type_Kind_VarArgs) return 1;
+ if (type->kind == Type_Kind_Pointer) {
+ if (type->Pointer.elem->kind == Type_Kind_Struct) return 1;
+ if (type->Pointer.elem->kind == Type_Kind_Slice) return 1;
+ if (type->Pointer.elem->kind == Type_Kind_DynArray) return 1;
+ }
+ return 0;
+}
+
+b32 type_is_structlike_strict(Type* type) {
+ if (type == NULL) return 0;
+ if (type->kind == Type_Kind_Struct) return 1;
+ if (type->kind == Type_Kind_Slice) return 1;
+ if (type->kind == Type_Kind_DynArray) return 1;
+ if (type->kind == Type_Kind_VarArgs) return 1;
+ return 0;
+}
+
+u32 type_structlike_mem_count(Type* type) {
+ if (type == NULL) return 0;
+ switch (type->kind) {
+ case Type_Kind_Struct: return type->Struct.mem_count;
+ case Type_Kind_Slice: return 2;
+ case Type_Kind_VarArgs: return 2;
+ case Type_Kind_DynArray: return 4;
+ default: return 0;
+ }
+}
+
+u32 type_structlike_is_simple(Type* type) {
+ if (type == NULL) return 0;
+ switch (type->kind) {
+ case Type_Kind_Struct: return type_struct_is_simple(type);
+ case Type_Kind_Slice: return 1;
+ case Type_Kind_VarArgs: return 1;
+ case Type_Kind_DynArray: return 0;
+ default: return 0;
+ }
+}
+
+b32 type_is_sl_constructable(Type* type) {
+ if (type == NULL) return 0;
+ switch (type->kind) {
+ case Type_Kind_Struct: return 1;
+ case Type_Kind_Slice: return 1;
+ case Type_Kind_DynArray: return 1;
+ default: return 0;
+ }
+}
+
+b32 type_struct_constructed_from_poly_struct(Type* struct_type, struct AstType* from) {
+ if (struct_type == NULL) return 0;
+ if (struct_type->kind != Type_Kind_Struct) return 0;
+
+ return struct_type->Struct.constructed_from == from;
+}
--- /dev/null
+#define BH_DEBUG
+
+#include "utils.h"
+#include "lex.h"
+#include "astnodes.h"
+#include "errors.h"
+#include "parser.h"
+#include "astnodes.h"
+
+bh_scratch global_scratch;
+bh_allocator global_scratch_allocator;
+
+bh_managed_heap global_heap;
+bh_allocator global_heap_allocator;
+
+//
+// Program info and packages
+//
+Package* package_lookup(char* package_name) {
+ if (bh_table_has(Package *, context.packages, package_name)) {
+ return bh_table_get(Package *, context.packages, package_name);
+ } else {
+ return NULL;
+ }
+}
+
+Package* package_lookup_or_create(char* package_name, Scope* parent_scope, bh_allocator alloc) {
+ if (bh_table_has(Package *, context.packages, package_name)) {
+ return bh_table_get(Package *, context.packages, package_name);
+
+ } else {
+ Package* package = bh_alloc_item(alloc, Package);
+
+ char* pac_name = bh_alloc_array(alloc, char, strlen(package_name) + 1);
+ memcpy(pac_name, package_name, strlen(package_name) + 1);
+ pac_name[strlen(package_name)] = '\0';
+
+ package->name = pac_name;
+ package->scope = scope_create(alloc, parent_scope, (OnyxFilePos) { 0 });
+ package->private_scope = scope_create(alloc, package->scope, (OnyxFilePos) { 0 });
+ package->use_package_entities = NULL;
+
+ bh_table_put(Package *, context.packages, pac_name, package);
+
+ return package;
+ }
+}
+
+void package_track_use_package(Package* package, Entity* entity) {
+ if (package->use_package_entities == NULL) {
+ bh_arr_new(global_heap_allocator, package->use_package_entities, 4);
+ }
+
+ bh_arr_push(package->use_package_entities, entity);
+}
+
+void package_reinsert_use_packages(Package* package) {
+ if (!package->use_package_entities) return;
+
+ bh_arr_each(Entity *, use_package, package->use_package_entities) {
+ (*use_package)->state = Entity_State_Resolve_Symbols;
+ (*use_package)->macro_attempts = 0;
+ entity_heap_insert_existing(&context.entities, *use_package);
+ }
+
+ bh_arr_set_length(package->use_package_entities, 0);
+}
+
+
+//
+// Scoping
+//
+static u64 next_scope_id = 1;
+
+Scope* scope_create(bh_allocator a, Scope* parent, OnyxFilePos created_at) {
+ Scope* scope = bh_alloc_item(a, Scope);
+ scope->id = next_scope_id++;
+ scope->parent = parent;
+ scope->created_at = created_at;
+
+ scope->symbols = NULL;
+ bh_table_init(global_heap_allocator, scope->symbols, 64);
+
+ return scope;
+}
+
+void scope_include(Scope* target, Scope* source, OnyxFilePos pos) {
+ bh_table_each_start(AstNode *, source->symbols);
+ symbol_raw_introduce(target, (char *) key, pos, value);
+ bh_table_each_end;
+}
+
+b32 symbol_introduce(Scope* scope, OnyxToken* tkn, AstNode* symbol) {
+ token_toggle_end(tkn);
+
+ b32 ret = symbol_raw_introduce(scope, tkn->text, tkn->pos, symbol);
+
+ token_toggle_end(tkn);
+ return ret;
+}
+
+b32 symbol_raw_introduce(Scope* scope, char* name, OnyxFilePos pos, AstNode* symbol) {
+ if (strcmp(name, "_")) {
+ if (bh_table_has(AstNode *, scope->symbols, name)) {
+ if (bh_table_get(AstNode *, scope->symbols, name) != symbol) {
+ onyx_report_error(pos, "Redeclaration of symbol '%s'.", name);
+ return 0;
+ }
+ return 1;
+ }
+ }
+
+ bh_table_put(AstNode *, scope->symbols, name, symbol);
+ return 1;
+}
+
+void symbol_builtin_introduce(Scope* scope, char* sym, AstNode *node) {
+ bh_table_put(AstNode *, scope->symbols, sym, node);
+}
+
+void symbol_subpackage_introduce(Scope* scope, char* sym, AstPackage* package) {
+ if (bh_table_has(AstNode *, scope->symbols, sym)) {
+ AstNode* maybe_package = bh_table_get(AstNode *, scope->symbols, sym);
+
+ // CLEANUP: Make this assertion an actual error message.
+ assert(maybe_package->kind == Ast_Kind_Package);
+ } else {
+ bh_table_put(AstNode *, scope->symbols, sym, (AstNode *) package);
+ }
+}
+
+AstNode* symbol_raw_resolve(Scope* start_scope, char* sym) {
+ AstNode* res = NULL;
+ Scope* scope = start_scope;
+
+ while (res == NULL && scope != NULL) {
+ if (bh_table_has(AstNode *, scope->symbols, sym)) {
+ res = bh_table_get(AstNode *, scope->symbols, sym);
+ } else {
+ scope = scope->parent;
+ }
+ }
+
+ return res;
+}
+
+AstNode* symbol_resolve(Scope* start_scope, OnyxToken* tkn) {
+ token_toggle_end(tkn);
+ AstNode* res = symbol_raw_resolve(start_scope, tkn->text);
+ token_toggle_end(tkn);
+
+ return res;
+}
+
+AstNode* try_symbol_raw_resolve_from_node(AstNode* node, char* symbol) {
+ b32 used_pointer = 0;
+
+ while (1) {
+ if (!node) return NULL;
+
+ switch (node->kind) {
+ case Ast_Kind_Type_Raw_Alias: node = (AstNode *) ((AstTypeRawAlias *) node)->to->ast_type; break;
+ case Ast_Kind_Type_Alias: node = (AstNode *) ((AstTypeAlias *) node)->to; break;
+ case Ast_Kind_Alias: node = (AstNode *) ((AstAlias *) node)->alias; break;
+ case Ast_Kind_Pointer_Type: {
+ if (used_pointer) goto all_types_peeled_off;
+ used_pointer = 1;
+
+ node = (AstNode *) ((AstPointerType *) node)->elem;
+ break;
+ }
+
+ default: goto all_types_peeled_off;
+ }
+ }
+
+all_types_peeled_off:
+ if (!node) return NULL;
+
+ switch (node->kind) {
+ case Ast_Kind_Package: {
+ AstPackage* package = (AstPackage *) node;
+
+ // CLEANUP
+ if (package->package == NULL) {
+ package->package = package_lookup(package->package_name);
+ }
+
+ if (package->package == NULL) {
+ return NULL;
+ }
+
+ return symbol_raw_resolve(package->package->scope, symbol);
+ }
+
+ case Ast_Kind_Enum_Type: {
+ AstEnumType* etype = (AstEnumType *) node;
+ return symbol_raw_resolve(etype->scope, symbol);
+ }
+
+ case Ast_Kind_Struct_Type: {
+ AstStructType* stype = (AstStructType *) node;
+ AstNode* result = symbol_raw_resolve(stype->scope, symbol);
+
+ if (result == NULL && stype->stcache != NULL) {
+ Type* struct_type = stype->stcache;
+ assert(struct_type->kind == Type_Kind_Struct);
+
+ bh_arr_each(AstPolySolution, sln, struct_type->Struct.poly_sln) {
+ if (token_text_equals(sln->poly_sym->token, symbol)) {
+ if (sln->kind == PSK_Type) {
+ result = (AstNode *) sln->type->ast_type;
+ } else {
+ result = (AstNode *) sln->value;
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+ case Ast_Kind_Poly_Struct_Type: {
+ AstStructType* stype = ((AstPolyStructType *) node)->base_struct;
+ return symbol_raw_resolve(stype->scope, symbol);
+ }
+ }
+
+ return NULL;
+}
+
+AstNode* try_symbol_resolve_from_node(AstNode* node, OnyxToken* token) {
+ token_toggle_end(token);
+ AstNode* result = try_symbol_raw_resolve_from_node(node, token->text);
+ token_toggle_end(token);
+
+ return result;
+}
+
+void scope_clear(Scope* scope) {
+ bh_table_clear(scope->symbols);
+}
+
+// Polymorphic procedures are in their own file to clean up this file.
+#include "polymorph.c"
+
+//
+// Overloaded Procedures
+//
+
+//
+// @Cleanup: Everything having to do with overload resolving!
+// Things that need to be available:
+// * A copy of the arguments list that can be mutated
+// - The named_values do not need to be copied, because they are not modified when fill_in_arguments happens.
+// - Only values needs to have a copy available
+// - This copy needs to be reset after checking every option
+//
+// Steps needed to check if an overload option is "the one":
+// 1. Figure out what the overload is
+// a. If polymorphic, generate the header for the procedure only
+// 2. Place the arguments in the copy, according to the overload option
+// 3. Ensure the option has a type filled out
+// 4. For each argument
+// a. Ensure it has a place to go (not too many arguments)
+// b. Ensure it has a type
+// c. Ensure the types match (currently there could be a problem if an option is attempted and doesn't work all the way that polymorphic procedures as arguments could still be solidified)
+//
+// Additional features that this needs to account for:
+// * Resolving an overload from a list of parameter types
+// * Resolving an overload from a TypeFunction (so an overloaded procedure can be passed as a parameter)
+//
+
+void add_overload_option(bh_arr(OverloadOption)* poverloads, u64 precedence, AstTyped* overload) {
+ bh_arr(OverloadOption) overloads = *poverloads;
+
+ i32 index = -1;
+ fori (i, 0, bh_arr_length(overloads)) {
+ if (overloads[i].precedence > precedence) {
+ index = i;
+ break;
+ }
+ }
+
+ if (index < 0) {
+ bh_arr_push(overloads, ((OverloadOption) {
+ .precedence = precedence,
+ .option = overload,
+ }));
+
+ } else {
+ bh_arr_insertn(overloads, index, 1);
+ overloads[index].precedence = precedence;
+ overloads[index].option = overload;
+ }
+
+ *poverloads = overloads;
+}
+
+// NOTE: The job of this function is to take a set of overloads, and traverse it to add all possible
+// overloads that are reachable. This is slightly more difficult than it may seem. In this language,
+// overloaded procedures have a strict ordering to their overloads, which determines how the correct
+// match will be found. This was not very complicated until overloaded procedures could be used as
+// overload options. This means that you could create an "infinite loop" of overloads like so:
+//
+// o1 :: { o2 :: {
+// (...) { ... }, (...) { ... },
+// o2 o1
+// } }
+//
+// Obviously, this is not really an infinite loop. It just means that all options are available if
+// o1 or o2 are called. The difference between o1 and o2 is the order that the overloads will be
+// searched. To build the the list of overloads, a hashmap is used to prevent the problem from being
+// O(n^2), even though n would (probably) be small. bh_imap has the useful property that it maintains
+// an "entries" array that, so long as nothing is ever removed from it, will maintain the order in
+// which entries were put into the map. This is useful because a simple recursive algorithm can
+// collect all the overloads into the map, and also use the map to provide a base case.
+void build_all_overload_options(bh_arr(OverloadOption) overloads, bh_imap* all_overloads) {
+ bh_arr_each(OverloadOption, overload, overloads) {
+ if (bh_imap_has(all_overloads, (u64) overload->option)) continue;
+
+ bh_imap_put(all_overloads, (u64) overload->option, 1);
+
+ if (overload->option->kind == Ast_Kind_Overloaded_Function) {
+ AstOverloadedFunction* sub_overload = (AstOverloadedFunction *) overload->option;
+ build_all_overload_options(sub_overload->overloads, all_overloads);
+ }
+ }
+}
+
+AstTyped* find_matching_overload_by_arguments(bh_arr(OverloadOption) overloads, Arguments* param_args, b32* should_yield) {
+ Arguments args;
+ arguments_clone(&args, param_args);
+ arguments_ensure_length(&args, bh_arr_length(args.values) + bh_arr_length(args.named_values));
+
+ // CLEANUP SPEED: This currently rebuilds the complete set of overloads every time one is looked up.
+ // This should be cached in the AstOverloadedFunction or somewhere like that.
+ bh_imap all_overloads;
+ bh_imap_init(&all_overloads, global_heap_allocator, bh_arr_length(overloads) * 2);
+ build_all_overload_options(overloads, &all_overloads);
+
+ AstTyped *matched_overload = NULL;
+
+ bh_arr_each(bh__imap_entry, entry, all_overloads.entries) {
+ AstTyped* node = (AstTyped *) entry->key;
+ arguments_copy(&args, param_args);
+
+ AstFunction* overload = NULL;
+ switch (node->kind) {
+ case Ast_Kind_Function: overload = (AstFunction *) node; break;
+ case Ast_Kind_Macro: overload = macro_resolve_header((AstMacro *) node, param_args, NULL); break;
+ case Ast_Kind_Polymorphic_Proc: overload = polymorphic_proc_build_only_header((AstPolyProc *) node, PPLM_By_Arguments, param_args); break;
+ }
+
+ // NOTE: Overload is not something that is known to be overloadable.
+ if (overload == NULL) continue;
+ if (overload->kind != Ast_Kind_Function) continue;
+ if (overload->type == NULL) {
+ // If it was not possible to create the type for this procedure, tell the
+ // caller that this should yield and try again later.
+ if (should_yield) *should_yield = 1;
+
+ // return and not continue because if the overload that didn't have a type will
+ // work in the future, then it has to take precedence over the other options available.
+ return NULL;
+ }
+ assert(overload->type->kind == Type_Kind_Function);
+
+ // NOTE: If the arguments cannot be placed successfully in the parameters list
+ if (!fill_in_arguments(&args, (AstNode *) overload, NULL)) continue;
+
+ TypeFunction* ol_type = &overload->type->Function;
+ if (bh_arr_length(args.values) < (i32) ol_type->needed_param_count) continue;
+
+ b32 all_arguments_work = 1;
+ fori (i, 0, bh_arr_length(args.values)) {
+ if (i >= ol_type->param_count) {
+ all_arguments_work = 0;
+ break;
+ }
+
+ Type* type_to_match = ol_type->params[i];
+ AstTyped** value = &args.values[i];
+
+ if (type_to_match->kind == Type_Kind_VarArgs) type_to_match = type_to_match->VarArgs.ptr_to_data->Pointer.elem;
+ if ((*value)->kind == Ast_Kind_Argument) {
+ // :ArgumentResolvingIsComplicated
+ if (((AstArgument *) (*value))->is_baked) continue;
+ value = &((AstArgument *) *value)->value;
+ }
+
+ if (!type_check_or_auto_cast(value, type_to_match)) {
+ all_arguments_work = 0;
+ break;
+ }
+ }
+
+ if (all_arguments_work) {
+ matched_overload = node;
+ break;
+ }
+ }
+
+ bh_imap_free(&all_overloads);
+ bh_arr_free(args.values);
+ return matched_overload;
+}
+
+AstTyped* find_matching_overload_by_type(bh_arr(OverloadOption) overloads, Type* type) {
+ if (type->kind != Type_Kind_Function) return NULL;
+
+ bh_imap all_overloads;
+ bh_imap_init(&all_overloads, global_heap_allocator, bh_arr_length(overloads) * 2);
+ build_all_overload_options(overloads, &all_overloads);
+
+ AstTyped *matched_overload = NULL;
+
+ bh_arr_each(bh__imap_entry, entry, all_overloads.entries) {
+ AstTyped* node = (AstTyped *) entry->key;
+ if (node->kind == Ast_Kind_Overloaded_Function) continue;
+
+ if (type_check_or_auto_cast(&node, type)) {
+ matched_overload = node;
+ break;
+ }
+ }
+
+ bh_imap_free(&all_overloads);
+ return matched_overload;
+}
+
+void report_unable_to_match_overload(AstCall* call) {
+ char* arg_str = bh_alloc(global_scratch_allocator, 1024);
+ arg_str[0] = '\0';
+
+ bh_arr_each(AstTyped *, arg, call->args.values) {
+ strncat(arg_str, node_get_type_name(*arg), 1023);
+
+ if (arg != &bh_arr_last(call->args.values))
+ strncat(arg_str, ", ", 1023);
+ }
+
+ if (bh_arr_length(call->args.named_values) > 0) {
+ if (bh_arr_length(call->args.values) > 0) {
+ strncat(arg_str, ", ", 1023);
+ }
+
+ bh_arr_each(AstNamedValue *, named_value, call->args.named_values) {
+ token_toggle_end((*named_value)->token);
+ strncat(arg_str, (*named_value)->token->text, 1023);
+ token_toggle_end((*named_value)->token);
+
+ strncat(arg_str, "=", 1023);
+ strncat(arg_str, node_get_type_name((*named_value)->value), 1023); // CHECK: this might say 'unknown'.
+
+ if (named_value != &bh_arr_last(call->args.named_values))
+ strncat(arg_str, ", ", 1023);
+ }
+ }
+
+ onyx_report_error(call->token->pos, "Unable to match overloaded function with provided argument types: (%s)", arg_str);
+
+ bh_free(global_scratch_allocator, arg_str);
+}
+
+
+//
+// Macros
+//
+//
+// TODO: Write this documentation
+void expand_macro(AstCall** pcall, AstFunction* template) {
+ AstCall* call = *pcall;
+ AstMacro* macro = (AstMacro *) call->callee;
+ assert(macro->kind == Ast_Kind_Macro);
+
+ assert(template->kind == Ast_Kind_Function);
+ assert(template->type != NULL);
+ assert(template->type->kind == Type_Kind_Function);
+
+ AstBlock* expansion = (AstBlock *) ast_clone(context.ast_alloc, template->body);
+ expansion->rules = Block_Rule_Macro;
+ expansion->scope = NULL;
+ expansion->next = call->next;
+
+ AstNode* subst = (AstNode *) expansion;
+
+ if (template->type->Function.return_type != &basic_types[Basic_Kind_Void]) {
+ AstDoBlock* doblock = (AstDoBlock *) onyx_ast_node_new(context.ast_alloc, sizeof(AstDoBlock), Ast_Kind_Do_Block);
+ doblock->token = expansion->token;
+ doblock->block = expansion;
+ doblock->type = template->type->Function.return_type;
+ doblock->next = expansion->next;
+ expansion->next = NULL;
+
+ subst = (AstNode *) doblock;
+ }
+
+ Scope* argument_scope = scope_create(context.ast_alloc, NULL, call->token->pos);
+ if (expansion->binding_scope != NULL)
+ scope_include(argument_scope, expansion->binding_scope, call->token->pos);
+ expansion->binding_scope = argument_scope;
+
+ // HACK HACK HACK This is probably very wrong. I don't know what guarentees that
+ // the paramters and arguments are going to be in the same order exactly.
+ fori (i, 0, bh_arr_length(call->args.values)) {
+ symbol_introduce(argument_scope,
+ template->params[i].local->token,
+ (AstNode *) ((AstArgument *) call->args.values[i])->value);
+ }
+
+ if (template->flags & Ast_Flag_From_Polymorphism) {
+ // SLOW DUMB HACKY WAY TO DO THIS!!!!! FIX IT!!!!!
+
+ AstPolyProc* pp = (AstPolyProc *) macro->body;
+ bh_table_each_start(AstSolidifiedFunction, pp->concrete_funcs);
+
+ if (value.func == template) {
+ scope_include(argument_scope, value.poly_scope, call->token->pos);
+ break;
+ }
+
+ bh_table_each_end;
+ }
+
+ *(AstNode **) pcall = subst;
+ return;
+}
+
+AstFunction* macro_resolve_header(AstMacro* macro, Arguments* args, OnyxToken* callsite) {
+ switch (macro->body->kind) {
+ case Ast_Kind_Function: return (AstFunction *) macro->body;
+
+ case Ast_Kind_Polymorphic_Proc: {
+ AstPolyProc* pp = (AstPolyProc *) macro->body;
+ ensure_polyproc_cache_is_created(pp);
+
+ char* err_msg=NULL;
+ bh_arr(AstPolySolution) slns = find_polymorphic_slns(pp, PPLM_By_Arguments, args, &err_msg);
+
+ if (slns == NULL) {
+ if (flag_to_yield) {
+ flag_to_yield = 0;
+ return (AstFunction *) &node_that_signals_a_yield;
+ }
+
+ if (callsite) onyx_report_error(callsite->pos, err_msg);
+
+ return NULL;
+ }
+
+ // CLEANUP Copy'n'pasted from polymorphic_proc_build_only_header
+ AstSolidifiedFunction solidified_func;
+
+ char* unique_key = build_poly_slns_unique_key(slns);
+ if (bh_table_has(AstSolidifiedFunction, pp->concrete_funcs, unique_key)) {
+ solidified_func = bh_table_get(AstSolidifiedFunction, pp->concrete_funcs, unique_key);
+
+ } else {
+ // NOTE: This function is only going to have the header of it correctly created.
+ // Nothing should happen to this function's body or else the original will be corrupted.
+ // - brendanfh 2021/01/10
+ solidified_func = generate_solidified_function(pp, slns, callsite, 1);
+ }
+
+ if (solidified_func.header_complete) return solidified_func.func;
+
+ Entity func_header_entity = {
+ .state = Entity_State_Resolve_Symbols,
+ .type = Entity_Type_Function_Header,
+ .function = solidified_func.func,
+ .package = NULL,
+ .scope = solidified_func.poly_scope,
+ };
+
+ b32 successful = entity_bring_to_state(&func_header_entity, Entity_State_Code_Gen);
+ if (onyx_has_errors()) return NULL;
+
+ solidified_func.header_complete = successful;
+
+ bh_table_put(AstSolidifiedFunction, pp->concrete_funcs, unique_key, solidified_func);
+ if (!successful) return (AstFunction *) &node_that_signals_a_yield;
+
+ return solidified_func.func;
+ }
+
+ default: assert(("Bad macro body type.", 0));
+ }
+}
+
+b32 entity_bring_to_state(Entity* ent, EntityState state) {
+ EntityState last_state = ent->state;
+
+ while (ent->state != state) {
+ switch (ent->state) {
+ case Entity_State_Resolve_Symbols: symres_entity(ent); break;
+ case Entity_State_Check_Types: check_entity(ent); break;
+ case Entity_State_Code_Gen: emit_entity(ent); break;
+
+ default: return 0;
+ }
+
+ if (ent->state == last_state) return 0;
+ last_state = ent->state;
+
+ if (onyx_has_errors()) return 0;
+ }
+
+ return 1;
+}
+
+
+//
+// Arguments resolving
+//
+static i32 lookup_idx_by_name(AstNode* provider, char* name) {
+ switch (provider->kind) {
+ case Ast_Kind_Struct_Literal: {
+ AstStructLiteral* sl = (AstStructLiteral *) provider;
+ assert(sl->type);
+
+ StructMember s;
+ if (!type_lookup_member(sl->type, name, &s)) return -1;
+ if (s.included_through_use) return -1;
+
+ return s.idx;
+ }
+
+ case Ast_Kind_Function: {
+ AstFunction* func = (AstFunction *) provider;
+
+ i32 param_idx = -1;
+ i32 idx = 0;
+ bh_arr_each(AstParam, param, func->params) {
+ if (token_text_equals(param->local->token, name)) {
+ param_idx = idx;
+ break;
+ }
+
+ idx++;
+ }
+
+ return param_idx;
+ }
+
+ default: return -1;
+ }
+}
+
+static AstNode* lookup_default_value_by_idx(AstNode* provider, i32 idx) {
+ switch (provider->kind) {
+ case Ast_Kind_Struct_Literal: {
+ AstStructLiteral* sl = (AstStructLiteral *) provider;
+ assert(sl->type);
+
+ if (sl->type->kind == Type_Kind_Struct) {
+ bh_arr(StructMember *) memarr = sl->type->Struct.memarr;
+ if (idx >= bh_arr_length(memarr)) return NULL;
+
+ return (AstNode *) *memarr[idx]->initial_value;
+ }
+
+ return NULL;
+ }
+
+ case Ast_Kind_Function: {
+ AstFunction* func = (AstFunction *) provider;
+
+ AstTyped* default_value = func->params[idx].default_value;
+ if (default_value == NULL) return NULL;
+
+ AstArgument* arg = make_argument(context.ast_alloc, default_value);
+ return (AstNode *) arg;
+ }
+
+ default: return NULL;
+ }
+}
+
+static i32 maximum_argument_count(AstNode* provider) {
+ switch (provider->kind) {
+ case Ast_Kind_Struct_Literal: {
+ AstStructLiteral* sl = (AstStructLiteral *) provider;
+ assert(sl->type);
+
+ return type_structlike_mem_count(sl->type);
+ }
+ }
+
+ // NOTE: This returns int_max for anything other than struct literals because the
+ // bounds checking on the arguments will be done elsewhere.
+ return 0x7fffffff;
+}
+
+// NOTE: The values array can be partially filled out, and is the resulting array.
+// Returns if all the values were filled in.
+b32 fill_in_arguments(Arguments* args, AstNode* provider, char** err_msg) {
+
+ { // Delete baked arguments
+ // :ArgumentResolvingIsComplicated
+ i32 max = bh_arr_length(args->values);
+ fori (i, 0, max) {
+ AstTyped* value = args->values[i];
+ if (value == NULL) continue;
+
+ if (value->kind == Ast_Kind_Argument) {
+ if (((AstArgument *) value)->is_baked) {
+ i--;
+ max--;
+ bh_arr_deleten(args->values, i, 1);
+ }
+ }
+ }
+ }
+
+ if (args->named_values != NULL) {
+ bh_arr_each(AstNamedValue *, p_named_value, args->named_values) {
+ AstNamedValue* named_value = *p_named_value;
+
+ if (named_value->value->kind == Ast_Kind_Argument) {
+ if (((AstArgument *) named_value->value)->is_baked) {
+ // :ArgumentResolvingIsComplicated
+ bh_arr_set_length(args->values, bh_arr_length(args->values) - 1);
+ continue;
+ }
+ }
+
+ token_toggle_end(named_value->token);
+ i32 idx = lookup_idx_by_name(provider, named_value->token->text);
+ if (idx == -1) {
+ if (err_msg) *err_msg = bh_aprintf(global_scratch_allocator, "'%s' is not a valid named parameter here.", named_value->token->text);
+ token_toggle_end(named_value->token);
+ return 0;
+ }
+
+ // assert(idx < bh_arr_length(args->values));
+ if (idx >= bh_arr_length(args->values)) {
+ token_toggle_end(named_value->token);
+ return 0;
+ }
+
+ if (args->values[idx] != NULL && args->values[idx] != named_value->value) {
+ if (err_msg) *err_msg = bh_aprintf(global_scratch_allocator, "Multiple values given for parameter named '%s'.", named_value->token->text);
+ token_toggle_end(named_value->token);
+ return 0;
+ }
+
+ args->values[idx] = named_value->value;
+ token_toggle_end(named_value->token);
+ }
+ }
+
+ b32 success = 1;
+ fori (idx, 0, bh_arr_length(args->values)) {
+ if (args->values[idx] == NULL) args->values[idx] = (AstTyped *) lookup_default_value_by_idx(provider, idx);
+ if (args->values[idx] == NULL) {
+ *err_msg = bh_aprintf(global_scratch_allocator, "No value given for %d%s argument.", idx + 1, bh_num_suffix(idx + 1));
+ success = 0;
+ }
+ }
+
+ i32 maximum_arguments = maximum_argument_count(provider);
+ if (bh_arr_length(args->values) > maximum_arguments) {
+ *err_msg = bh_aprintf(global_scratch_allocator, "Too many values provided. Expected at most %d.", maximum_arguments);
+ success = 0;
+ }
+
+ return success;
+}
+
+//
+// String parsing helpers
+//
+u32 char_to_base16_value(char x) {
+ if (x >= '0' && x <= '9') return (u32) (x - '0');
+ if (x >= 'A' && x <= 'F') return (u32) (x - 'A' + 10);
+ if (x >= 'a' && x <= 'f') return (u32) (x - 'a' + 10);
+ return 0xffffffff;
+}
+
+i32 string_process_escape_seqs(char* dest, char* src, i32 len) {
+ i32 total_len = 0;
+ for (i32 i = 0; i < len; i++) {
+ if (src[i] == '\\') {
+ i++;
+ switch (src[i]) {
+ case '0': *dest++ = '\0'; total_len++; break;
+ case 'a': *dest++ = '\a'; total_len++; break;
+ case 'b': *dest++ = '\b'; total_len++; break;
+ case 'f': *dest++ = '\f'; total_len++; break;
+ case 'n': *dest++ = '\n'; total_len++; break;
+ case 't': *dest++ = '\t'; total_len++; break;
+ case 'r': *dest++ = '\r'; total_len++; break;
+ case 'v': *dest++ = '\v'; total_len++; break;
+ case 'e': *dest++ = '\e'; total_len++; break;
+ case '"': *dest++ = '"'; total_len++; break;
+ case '\\': *dest++ = '\\'; total_len++; break;
+ case 'x': {
+ u8 ch1 = src[i + 1];
+ u8 ch2 = src[i + 2];
+ *dest++ = (i8) (char_to_base16_value(ch1) << 4 | char_to_base16_value(ch2));
+ total_len++;
+ i += 2;
+ break;
+ }
+ default: *dest++ = '\\';
+ *dest++ = src[i];
+ total_len += 2;
+ }
+ } else {
+ *dest++ = src[i];
+ total_len += 1;
+ }
+ }
+
+ // NOTE: Gotta null terminate
+ *dest = 0;
+
+ return total_len;
+}
+
+char* lookup_included_file(char* filename, char* relative_to, b32 add_onyx_suffix, b32 search_included_folders) {
+ assert(relative_to != NULL);
+
+ static char path[256];
+ fori (i, 0, 256) path[i] = 0;
+
+ static char fn[128];
+ fori (i, 0, 128) fn[i] = 0;
+
+ if (!bh_str_ends_with(filename, ".onyx") && add_onyx_suffix) {
+ bh_snprintf(fn, 128, "%s.onyx", filename);
+ } else {
+ bh_snprintf(fn, 128, "%s", filename);
+ }
+
+#if defined(_BH_LINUX)
+ #define DIR_SEPARATOR '/'
+#elif defined(_BH_WINDOWS)
+ #define DIR_SEPARATOR '\\'
+#endif
+
+ fori (i, 0, 128) if (fn[i] == '/') fn[i] = DIR_SEPARATOR;
+
+ if (bh_str_starts_with(filename, "./")) {
+ if (relative_to[strlen(relative_to) - 1] != DIR_SEPARATOR)
+ bh_snprintf(path, 256, "%s%c%s", relative_to, DIR_SEPARATOR, fn + 2);
+ else
+ bh_snprintf(path, 256, "%s%s", relative_to, fn + 2);
+
+ if (bh_file_exists(path)) return bh_path_get_full_name(path, global_scratch_allocator);
+
+ return fn;
+ }
+
+ if (search_included_folders) {
+ bh_arr_each(const char *, folder, context.options->included_folders) {
+ if ((*folder)[strlen(*folder) - 1] != DIR_SEPARATOR)
+ bh_snprintf(path, 256, "%s%c%s", *folder, DIR_SEPARATOR, fn);
+ else
+ bh_snprintf(path, 256, "%s%s", *folder, fn);
+
+ if (bh_file_exists(path)) return bh_path_get_full_name(path, global_scratch_allocator);
+ }
+ }
+
+ return fn;
+
+#undef DIR_SEPARATOR
+}
+
--- /dev/null
+//
+// There are several things I'm seeing in this file that I want to clean up.
+// They are:
+// [x] remove the need to know if the stack is needed before generating the function.
+// Just leave 5 nops at the beginning because they will be automatically removed
+// by the WASM outputter.
+// [x] remove the need to have "allocate_exprs" on blocks and in functions. This will
+// be easy once the above is done.
+// [x] there should be a better way to emit pending deferred statements because there
+// is some code duplication between emit_return and emit_structured_jump.
+// [ ] Change the calling convention so it is easier to use from both JS and in the compiler.
+
+
+
+
+#define BH_DEBUG
+#include "wasm.h"
+#include "utils.h"
+
+#define WASM_TYPE_INT32 0x7F
+#define WASM_TYPE_INT64 0x7E
+#define WASM_TYPE_FLOAT32 0x7D
+#define WASM_TYPE_FLOAT64 0x7C
+#define WASM_TYPE_VAR128 0x7B
+#define WASM_TYPE_PTR WASM_TYPE_INT32
+#define WASM_TYPE_FUNC WASM_TYPE_INT32
+#define WASM_TYPE_VOID 0x00
+
+static WasmType onyx_type_to_wasm_type(Type* type) {
+ if (type->kind == Type_Kind_Struct) {
+ return WASM_TYPE_VOID;
+ }
+
+ if (type->kind == Type_Kind_Slice) {
+ return WASM_TYPE_VOID;
+ }
+
+ if (type->kind == Type_Kind_Enum) {
+ return onyx_type_to_wasm_type(type->Enum.backing);
+ }
+
+ if (type->kind == Type_Kind_Pointer) {
+ return WASM_TYPE_PTR;
+ }
+
+ if (type->kind == Type_Kind_Array) {
+ return WASM_TYPE_PTR;
+ }
+
+ if (type->kind == Type_Kind_Function) {
+ return WASM_TYPE_FUNC;
+ }
+
+ if (type->kind == Type_Kind_Basic) {
+ TypeBasic* basic = &type->Basic;
+ if (basic->flags & Basic_Flag_Boolean) return WASM_TYPE_INT32;
+ if (basic->flags & Basic_Flag_Integer) {
+ if (basic->size <= 4) return WASM_TYPE_INT32;
+ if (basic->size == 8) return WASM_TYPE_INT64;
+ }
+ if (basic->flags & Basic_Flag_Pointer) return WASM_TYPE_INT32;
+ if (basic->flags & Basic_Flag_Float) {
+ if (basic->size <= 4) return WASM_TYPE_FLOAT32;
+ if (basic->size == 8) return WASM_TYPE_FLOAT64;
+ }
+ if (basic->flags & Basic_Flag_SIMD) return WASM_TYPE_VAR128;
+ if (basic->flags & Basic_Flag_Type_Index) return WASM_TYPE_INT32;
+ if (basic->size == 0) return WASM_TYPE_VOID;
+ }
+
+ return WASM_TYPE_VOID;
+}
+
+static i32 generate_type_idx(OnyxWasmModule* mod, Type* ft);
+static i32 get_element_idx(OnyxWasmModule* mod, AstFunction* func);
+
+#define LOCAL_I32 0x000000000
+#define LOCAL_I64 0x100000000
+#define LOCAL_F32 0x300000000
+#define LOCAL_F64 0x700000000
+#define LOCAL_V128 0xf00000000
+
+static b32 local_is_wasm_local(AstTyped* local) {
+ if (local->kind == Ast_Kind_Local && local->flags & Ast_Flag_Address_Taken) return 0;
+ if (local->type->kind == Type_Kind_Basic) return 1;
+ if (local->type->kind == Type_Kind_Enum && local->type->Enum.backing->kind == Type_Kind_Basic) return 1;
+ if (local->type->kind == Type_Kind_Pointer) return 1;
+ return 0;
+}
+
+static u64 local_raw_allocate(LocalAllocator* la, WasmType wt) {
+ i32 idx = 0;
+ if (wt == WASM_TYPE_INT32) idx = 0;
+ if (wt == WASM_TYPE_INT64) idx = 1;
+ if (wt == WASM_TYPE_FLOAT32) idx = 2;
+ if (wt == WASM_TYPE_FLOAT64) idx = 3;
+ if (wt == WASM_TYPE_VAR128) idx = 4;
+
+ u64 flag_bits = LOCAL_IS_WASM;
+ if (wt == WASM_TYPE_INT32) flag_bits |= LOCAL_I32;
+ if (wt == WASM_TYPE_INT64) flag_bits |= LOCAL_I64;
+ if (wt == WASM_TYPE_FLOAT32) flag_bits |= LOCAL_F32;
+ if (wt == WASM_TYPE_FLOAT64) flag_bits |= LOCAL_F64;
+ if (wt == WASM_TYPE_VAR128) flag_bits |= LOCAL_V128;
+
+ if (la->freed[idx] > 0) {
+ la->freed[idx]--;
+ return flag_bits | ((u64) (la->allocated[idx] - la->freed[idx] - 1 + la->param_count));
+
+ } else {
+ la->allocated[idx]++;
+ return flag_bits | ((u64) (la->allocated[idx] - 1 + la->param_count));
+ }
+}
+
+static void local_raw_free(LocalAllocator* la, WasmType wt) {
+ i32 idx = 0;
+
+ if (wt == WASM_TYPE_INT32) idx = 0;
+ if (wt == WASM_TYPE_INT64) idx = 1;
+ if (wt == WASM_TYPE_FLOAT32) idx = 2;
+ if (wt == WASM_TYPE_FLOAT64) idx = 3;
+ if (wt == WASM_TYPE_VAR128) idx = 4;
+
+ assert(la->allocated[idx] > 0 && la->freed[idx] < la->allocated[idx]);
+
+ la->freed[idx]++;
+}
+
+static u64 local_allocate(LocalAllocator* la, AstTyped* local) {
+ if (local_is_wasm_local(local)) {
+ WasmType wt = onyx_type_to_wasm_type(local->type);
+ return local_raw_allocate(la, wt);
+
+ } else {
+ u32 size = type_size_of(local->type);
+ u32 alignment = type_alignment_of(local->type);
+
+ bh_align(la->curr_stack, alignment);
+
+ if (la->max_stack < la->curr_stack)
+ la->max_stack = la->curr_stack;
+
+ bh_align(size, alignment);
+
+ if (la->max_stack - la->curr_stack >= (i32) size) {
+ la->curr_stack += size;
+
+ } else {
+ la->max_stack += size - (la->max_stack - la->curr_stack);
+ la->curr_stack = la->max_stack;
+ }
+
+ return la->curr_stack - size;
+ }
+}
+
+static void local_free(LocalAllocator* la, AstTyped* local) {
+ if (local_is_wasm_local(local)) {
+ WasmType wt = onyx_type_to_wasm_type(local->type);
+ local_raw_free(la, wt);
+
+ } else {
+ u32 size = type_size_of(local->type);
+ u32 alignment = type_alignment_of(local->type);
+ bh_align(size, alignment);
+
+ la->curr_stack -= size;
+ }
+}
+
+static u64 local_lookup_idx(LocalAllocator* la, u64 value) {
+ assert(value & LOCAL_IS_WASM);
+
+ u32 idx = value & 0xFFFFFFFF;
+ if (value & 0x100000000) idx += la->allocated[0];
+ if (value & 0x200000000) idx += la->allocated[1];
+ if (value & 0x400000000) idx += la->allocated[2];
+ if (value & 0x800000000) idx += la->allocated[3];
+
+ return (u64) idx;
+}
+
+
+typedef enum StructuredBlockType StructuredBlockType;
+enum StructuredBlockType {
+ SBT_Basic_Block, // Cannot be targeted using jump
+ SBT_Breakable_Block, // Targeted using break
+ SBT_Continue_Block, // Targeted using continue
+ SBT_Fallthrough_Block, // Targeted using fallthrough
+ SBT_Return_Block, // Targeted using return, (used for expression macros)
+
+ SBT_Basic_If, // Cannot be targeted using jump
+ SBT_Breakable_If, // Targeted using break
+
+ SBT_Basic_Loop, // Cannot be targeted using jump
+ SBT_Continue_Loop, // Targeted using continue
+
+ SBT_Count,
+};
+
+
+#define WI(instr) bh_arr_push(code, ((WasmInstruction){ instr, 0x00 }))
+#define WID(instr, data) bh_arr_push(code, ((WasmInstruction){ instr, data }))
+#define WIL(instr, data) bh_arr_push(code, ((WasmInstruction){ instr, { .l = data } }))
+#define WIP(instr, data) bh_arr_push(code, ((WasmInstruction){ instr, { .p = data } }))
+#define EMIT_FUNC(kind, ...) static void emit_ ## kind (OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, __VA_ARGS__)
+#define EMIT_FUNC_NO_ARGS(kind) static void emit_ ## kind (OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode)
+#define STACK_SWAP(type1, type2) { \
+ u64 t0 = local_raw_allocate(mod->local_alloc, type1); \
+ u64 t1 = local_raw_allocate(mod->local_alloc, type2); \
+ \
+ WIL(WI_LOCAL_SET, t0); \
+ WIL(WI_LOCAL_SET, t1); \
+ WIL(WI_LOCAL_GET, t0); \
+ WIL(WI_LOCAL_GET, t1); \
+ \
+ local_raw_free(mod->local_alloc, type1); \
+ local_raw_free(mod->local_alloc, type2); \
+ }
+#define SUBMIT_PATCH(patch_arr, offset) bh_arr_push((patch_arr), ((PatchInfo) { bh_arr_length(code) - offset }))
+
+EMIT_FUNC(function_body, AstFunction* fd);
+EMIT_FUNC(block, AstBlock* block, b32 generate_block_headers);
+EMIT_FUNC(statement, AstNode* stmt);
+EMIT_FUNC(local_allocation, AstTyped* stmt);
+EMIT_FUNC_NO_ARGS(free_local_allocations);
+EMIT_FUNC(assignment, AstBinaryOp* assign);
+EMIT_FUNC(assignment_of_array, AstTyped* left, AstTyped* right);
+EMIT_FUNC(compound_assignment, AstBinaryOp* assign);
+EMIT_FUNC(store_instruction, Type* type, u32 offset);
+EMIT_FUNC(load_instruction, Type* type, u32 offset);
+EMIT_FUNC(if, AstIfWhile* if_node);
+EMIT_FUNC(while, AstIfWhile* while_node);
+EMIT_FUNC(for, AstFor* for_node);
+EMIT_FUNC(switch, AstSwitch* switch_node);
+EMIT_FUNC(defer, AstDefer* defer);
+EMIT_FUNC(defer_code, WasmInstruction* deferred_code, u32 code_count);
+EMIT_FUNC(deferred_stmt, DeferredStmt deferred_stmt);
+EMIT_FUNC_NO_ARGS(deferred_stmts);
+EMIT_FUNC(binop, AstBinaryOp* binop);
+EMIT_FUNC(unaryop, AstUnaryOp* unop);
+EMIT_FUNC(call, AstCall* call);
+EMIT_FUNC(intrinsic_call, AstCall* call);
+EMIT_FUNC(subscript_location, AstSubscript* sub, u64* offset_return);
+EMIT_FUNC(field_access_location, AstFieldAccess* field, u64* offset_return);
+EMIT_FUNC(local_location, AstLocal* local, u64* offset_return);
+EMIT_FUNC(memory_reservation_location, AstMemRes* memres);
+EMIT_FUNC(location_return_offset, AstTyped* expr, u64* offset_return);
+EMIT_FUNC(location, AstTyped* expr);
+EMIT_FUNC(compound_load, Type* type, u64 offset);
+EMIT_FUNC(struct_lval, AstTyped* lval);
+EMIT_FUNC(struct_literal, AstStructLiteral* sl);
+EMIT_FUNC(compound_store, Type* type, u64 offset, b32 location_first);
+EMIT_FUNC(array_store, Type* type, u32 offset);
+EMIT_FUNC(array_literal, AstArrayLiteral* al);
+EMIT_FUNC(range_literal, AstRangeLiteral* range);
+EMIT_FUNC(if_expression, AstIfExpression* if_expr);
+EMIT_FUNC(do_block, AstDoBlock* doblock);
+EMIT_FUNC(expression, AstTyped* expr);
+EMIT_FUNC(cast, AstUnaryOp* cast);
+EMIT_FUNC(return, AstReturn* ret);
+EMIT_FUNC(stack_enter, u64 stacksize);
+EMIT_FUNC(zero_value, WasmType wt);
+EMIT_FUNC(zero_value_for_type, Type* type, OnyxToken* where);
+
+EMIT_FUNC(enter_structured_block, StructuredBlockType sbt);
+EMIT_FUNC_NO_ARGS(leave_structured_block);
+
+static void emit_raw_data(OnyxWasmModule* mod, ptr data, AstTyped* node);
+static b32 emit_raw_data_(OnyxWasmModule* mod, ptr data, AstTyped* node);
+
+#include "wasm_intrinsics.c"
+#include "wasm_type_table.c"
+
+EMIT_FUNC(function_body, AstFunction* fd) {
+ if (fd->body == NULL) return;
+
+ emit_block(mod, pcode, fd->body, 0);
+}
+
+EMIT_FUNC(block, AstBlock* block, b32 generate_block_headers) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ generate_block_headers = generate_block_headers && (block->rules & Block_Rule_Emit_Instructions);
+
+ if (generate_block_headers) {
+ emit_enter_structured_block(mod, &code, (block->rules & Block_Rule_Override_Return)
+ ? SBT_Return_Block
+ : SBT_Breakable_Block);
+ }
+
+ forll (AstNode, stmt, block->body, next) {
+ emit_statement(mod, &code, stmt);
+ }
+
+ // HACK: I'm not convinced this is the best way to handle this case. Essentially, if
+ // a deferred statement uses a local that is freed from the a macro block, then the
+ // local won't be valid anymore and could have garbage data. This ensures that the
+ // freeing of the local variables and flushing of the deferred statements happen together,
+ // so a deferred statement can never try to use a local that has been freed.
+ // - brendanfh 2021/08/29
+ if (block->rules & Block_Rule_Clear_Defer) {
+ emit_deferred_stmts(mod, &code);
+
+ // if (block->rules & Block_Rule_New_Scope)
+ emit_free_local_allocations(mod, &code);
+ }
+
+ if (generate_block_headers)
+ emit_leave_structured_block(mod, &code);
+
+ *pcode = code;
+}
+
+EMIT_FUNC(enter_structured_block, StructuredBlockType sbt) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ static const StructuredBlockType jump_numbers[SBT_Count] = {
+ /* SBT_Basic_Block */ 0,
+ /* SBT_Breakable_Block */ 1,
+ /* SBT_Continue_Block */ 2,
+ /* SBT_Fallthrough_Block */ 3,
+ /* SBT_Return_Block */ 4,
+
+ /* SBT_Basic_If */ 0,
+ /* SBT_Breakable_If */ 1,
+
+ /* SBT_Basic_Loop */ 0,
+ /* SBT_Continue_Loop */ 2,
+ };
+
+ static const WasmInstructionType block_instrs[SBT_Count] = {
+ /* SBT_Basic_Block */ WI_BLOCK_START,
+ /* SBT_Breakable_Block */ WI_BLOCK_START,
+ /* SBT_Continue_Block */ WI_BLOCK_START,
+ /* SBT_Fallthrough_Block */ WI_BLOCK_START,
+ /* SBT_Return_Block */ WI_BLOCK_START,
+
+ /* SBT_Basic_If */ WI_IF_START,
+ /* SBT_Breakable_If */ WI_IF_START,
+
+ /* SBT_Basic_Loop */ WI_LOOP_START,
+ /* SBT_Continue_Loop */ WI_LOOP_START,
+ };
+
+
+ WID(block_instrs[sbt], 0x40);
+ bh_arr_push(mod->structured_jump_target, jump_numbers[sbt]);
+
+ *pcode = code;
+}
+
+EMIT_FUNC_NO_ARGS(leave_structured_block) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ WI(WI_BLOCK_END);
+ bh_arr_pop(mod->structured_jump_target);
+
+ *pcode = code;
+}
+
+i64 get_structured_jump_label(OnyxWasmModule* mod, JumpType jump_type, u32 jump_count) {
+ // :CLEANUP These numbers should become constants because they are shared with
+ // enter_structured_block's definitions.
+ static const u8 wants[Jump_Type_Count] = { 1, 2, 3, 4 };
+
+ i64 labelidx = 0;
+ u8 wanted = wants[jump_type];
+ b32 success = 0;
+
+ i32 len = bh_arr_length(mod->structured_jump_target) - 1;
+ for (u8* t = &bh_arr_last(mod->structured_jump_target); len >= 0; len--, t--) {
+ if (*t == wanted) jump_count--;
+ if (jump_count == 0) {
+ success = 1;
+ break;
+ }
+
+ labelidx++;
+ }
+
+ return (success == 0) ? -1 : labelidx;
+}
+
+EMIT_FUNC(structured_jump, AstJump* jump) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ i64 labelidx = get_structured_jump_label(mod, jump->jump, jump->count);
+
+ if (bh_arr_length(mod->deferred_stmts) > 0) {
+ i32 i = bh_arr_length(mod->deferred_stmts) - 1;
+ i32 d = bh_arr_length(mod->structured_jump_target) - (labelidx + 1);
+
+ while (i >= 0 && (i32) mod->deferred_stmts[i].depth > d) {
+ emit_deferred_stmt(mod, &code, mod->deferred_stmts[i]);
+ i--;
+ }
+ }
+
+ if (labelidx >= 0) {
+ // NOTE: If the previous instruction was a non conditional jump,
+ // don't emit another jump since it will never be reached.
+ if (bh_arr_last(code).type != WI_JUMP)
+ WID(WI_JUMP, labelidx);
+ } else {
+ onyx_report_error(jump->token->pos, "Invalid structured jump.");
+ }
+
+ *pcode = code;
+}
+
+EMIT_FUNC(statement, AstNode* stmt) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ switch (stmt->kind) {
+ case Ast_Kind_Return: emit_return(mod, &code, (AstReturn *) stmt); break;
+ case Ast_Kind_If: emit_if(mod, &code, (AstIfWhile *) stmt); break;
+ case Ast_Kind_Static_If: emit_if(mod, &code, (AstIfWhile *) stmt); break;
+ case Ast_Kind_While: emit_while(mod, &code, (AstIfWhile *) stmt); break;
+ case Ast_Kind_For: emit_for(mod, &code, (AstFor *) stmt); break;
+ case Ast_Kind_Switch: emit_switch(mod, &code, (AstSwitch *) stmt); break;
+ case Ast_Kind_Jump: emit_structured_jump(mod, &code, (AstJump *) stmt); break;
+ case Ast_Kind_Block: emit_block(mod, &code, (AstBlock *) stmt, 1); break;
+ case Ast_Kind_Defer: emit_defer(mod, &code, (AstDefer *) stmt); break;
+ case Ast_Kind_Local: emit_local_allocation(mod, &code, (AstTyped *) stmt); break;
+
+ case Ast_Kind_Directive_Insert: break;
+
+ default: emit_expression(mod, &code, (AstTyped *) stmt); break;
+ }
+
+ *pcode = code;
+}
+
+EMIT_FUNC(local_allocation, AstTyped* stmt) {
+ bh_imap_put(&mod->local_map, (u64) stmt, local_allocate(mod->local_alloc, stmt));
+
+ bh_arr_push(mod->local_allocations, ((AllocatedSpace) {
+ .depth = bh_arr_length(mod->structured_jump_target),
+ .expr = stmt,
+ }));
+}
+
+EMIT_FUNC_NO_ARGS(free_local_allocations) {
+ if (bh_arr_length(mod->local_allocations) == 0) return;
+
+ u64 depth = bh_arr_length(mod->structured_jump_target);
+ while (bh_arr_length(mod->local_allocations) > 0 && bh_arr_last(mod->local_allocations).depth >= depth) {
+ // CHECK: Not sure this next line is okay to be here...
+ bh_imap_delete(&mod->local_map, (u64) bh_arr_last(mod->local_allocations).expr);
+
+ local_free(mod->local_alloc, bh_arr_last(mod->local_allocations).expr);
+ bh_arr_pop(mod->local_allocations);
+ }
+}
+
+EMIT_FUNC(assignment, AstBinaryOp* assign) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ if (assign->right->type->kind == Type_Kind_Array) {
+ emit_assignment_of_array(mod, &code, assign->left, assign->right);
+ *pcode = code;
+ return;
+ }
+
+ if (assign->right->type->kind == Type_Kind_Compound) {
+ emit_compound_assignment(mod, &code, assign);
+ *pcode = code;
+ return;
+ }
+
+ AstTyped* lval = assign->left;
+
+ if (lval->kind == Ast_Kind_Local || lval->kind == Ast_Kind_Param) {
+ if (bh_imap_get(&mod->local_map, (u64) lval) & LOCAL_IS_WASM) {
+ emit_expression(mod, &code, assign->right);
+
+ u64 localidx = bh_imap_get(&mod->local_map, (u64) lval);
+
+ if (lval->kind == Ast_Kind_Param && type_is_structlike_strict(lval->type)) {
+ u32 mem_count = type_structlike_mem_count(lval->type);
+ fori (i, 0, mem_count) WIL(WI_LOCAL_SET, localidx + i);
+
+ } else {
+ WIL(WI_LOCAL_SET, localidx);
+ }
+
+ *pcode = code;
+ return;
+ }
+ }
+
+ if (lval->kind == Ast_Kind_Global) {
+ emit_expression(mod, &code, assign->right);
+
+ i32 globalidx = (i32) bh_imap_get(&mod->index_map, (u64) lval);
+ WID(WI_GLOBAL_SET, globalidx);
+
+ *pcode = code;
+ return;
+ }
+
+ u64 offset = 0;
+ emit_location_return_offset(mod, &code, lval, &offset);
+ emit_expression(mod, &code, assign->right);
+ emit_store_instruction(mod, &code, lval->type, offset);
+
+ *pcode = code;
+}
+
+EMIT_FUNC(assignment_of_array, AstTyped* left, AstTyped* right) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ Type* rtype = right->type;
+ assert(rtype->kind == Type_Kind_Array);
+
+ if (right->kind == Ast_Kind_Array_Literal) {
+ Type* elem_type = rtype;
+ u32 elem_count = 1;
+ while (elem_type->kind == Type_Kind_Array) {
+ elem_count *= elem_type->Array.count;
+ elem_type = elem_type->Array.elem;
+ }
+ u32 elem_size = type_size_of(elem_type);
+
+ u64 lptr_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+
+ emit_location(mod, &code, left);
+ WIL(WI_LOCAL_SET, lptr_local);
+
+ AstArrayLiteral* al = (AstArrayLiteral *) right;
+ fori (i, 0, elem_count) {
+ WIL(WI_LOCAL_GET, lptr_local);
+ emit_expression(mod, &code, al->values[i]);
+ emit_store_instruction(mod, &code, elem_type, i * elem_size);
+ }
+
+ local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+
+ } else {
+ u64 offset = 0;
+ emit_location_return_offset(mod, &code, left, &offset);
+ emit_expression(mod, &code, right);
+ emit_array_store(mod, &code, rtype, offset);
+ }
+
+ *pcode = code;
+ return;
+}
+
+EMIT_FUNC(compound_assignment, AstBinaryOp* assign) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ emit_expression(mod, &code, assign->right);
+
+ AstCompound* compound_lval = (AstCompound *) assign->left;
+ bh_arr_rev_each(AstTyped *, plval, compound_lval->exprs) {
+ AstTyped *lval = *plval;
+
+ if (type_is_structlike_strict(lval->type)) {
+ emit_struct_lval(mod, &code, lval);
+ continue;
+ }
+
+ if (lval->kind == Ast_Kind_Local || lval->kind == Ast_Kind_Param) {
+ if (bh_imap_get(&mod->local_map, (u64) lval) & LOCAL_IS_WASM) {
+ u64 localidx = bh_imap_get(&mod->local_map, (u64) lval);
+ WIL(WI_LOCAL_SET, localidx);
+ continue;
+ }
+ }
+
+ WasmType wt = onyx_type_to_wasm_type(lval->type);
+ u64 expr_tmp = local_raw_allocate(mod->local_alloc, wt);
+ WIL(WI_LOCAL_SET, expr_tmp);
+ u64 offset = 0;
+ emit_location_return_offset(mod, &code, lval, &offset);
+ WIL(WI_LOCAL_GET, expr_tmp);
+
+ local_raw_free(mod->local_alloc, wt);
+ emit_store_instruction(mod, &code, lval->type, offset);
+ }
+
+ *pcode = code;
+ return;
+}
+
+EMIT_FUNC(store_instruction, Type* type, u32 offset) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ if (type_is_compound(type)) {
+ emit_compound_store(mod, pcode, type, offset, 0);
+ return;
+ }
+
+ if (type->kind == Type_Kind_Array) {
+ emit_array_store(mod, pcode, type, offset);
+ return;
+ }
+
+ if (type->kind == Type_Kind_Enum) type = type->Enum.backing;
+ if (type->kind == Type_Kind_Function) type = &basic_types[Basic_Kind_U32];
+
+ u32 alignment = type_get_alignment_log2(type);
+
+ i32 store_size = type_size_of(type);
+ i32 is_basic = type->kind == Type_Kind_Basic || type->kind == Type_Kind_Pointer;
+
+ if (is_basic && (type->Basic.flags & Basic_Flag_Pointer)) {
+ WID(WI_I32_STORE, ((WasmInstructionData) { 2, offset }));
+ } else if (is_basic && ((type->Basic.flags & Basic_Flag_Integer)
+ || (type->Basic.flags & Basic_Flag_Boolean)
+ || (type->Basic.flags & Basic_Flag_Type_Index))) {
+ if (store_size == 1) WID(WI_I32_STORE_8, ((WasmInstructionData) { alignment, offset }));
+ else if (store_size == 2) WID(WI_I32_STORE_16, ((WasmInstructionData) { alignment, offset }));
+ else if (store_size == 4) WID(WI_I32_STORE, ((WasmInstructionData) { alignment, offset }));
+ else if (store_size == 8) WID(WI_I64_STORE, ((WasmInstructionData) { alignment, offset }));
+ } else if (is_basic && (type->Basic.flags & Basic_Flag_Float)) {
+ if (store_size == 4) WID(WI_F32_STORE, ((WasmInstructionData) { alignment, offset }));
+ else if (store_size == 8) WID(WI_F64_STORE, ((WasmInstructionData) { alignment, offset }));
+ } else if (is_basic && (type->Basic.flags & Basic_Flag_SIMD)) {
+ WID(WI_V128_STORE, ((WasmInstructionData) { alignment, offset }));
+ } else {
+ onyx_report_error((OnyxFilePos) { 0 },
+ "Failed to generate store instruction for type '%s'.",
+ type_get_name(type));
+ }
+
+ *pcode = code;
+}
+
+EMIT_FUNC(load_instruction, Type* type, u32 offset) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ if (type_is_compound(type)) {
+ emit_compound_load(mod, pcode, type, offset);
+ return;
+ }
+
+ if (type->kind == Type_Kind_Array) {
+ if (offset != 0) {
+ WID(WI_PTR_CONST, offset);
+ WI(WI_PTR_ADD);
+ }
+
+ *pcode = code;
+ return;
+ }
+
+ if (type->kind == Type_Kind_Enum) type = type->Enum.backing;
+ if (type->kind == Type_Kind_Function) type = &basic_types[Basic_Kind_U32];
+
+ i32 load_size = type_size_of(type);
+ i32 is_basic = type->kind == Type_Kind_Basic || type->kind == Type_Kind_Pointer;
+
+ WasmInstructionType instr = WI_NOP;
+ i32 alignment = type_get_alignment_log2(type);
+
+ if (is_basic && (type->Basic.flags & Basic_Flag_Pointer)) {
+ instr = WI_I32_LOAD;
+ alignment = 2;
+ }
+ else if (is_basic && ((type->Basic.flags & Basic_Flag_Integer)
+ || (type->Basic.flags & Basic_Flag_Boolean)
+ || (type->Basic.flags & Basic_Flag_Type_Index))) {
+ if (load_size == 1) instr = WI_I32_LOAD_8_S;
+ else if (load_size == 2) instr = WI_I32_LOAD_16_S;
+ else if (load_size == 4) instr = WI_I32_LOAD;
+ else if (load_size == 8) instr = WI_I64_LOAD;
+
+ if (load_size < 4 && (type->Basic.flags & Basic_Flag_Unsigned)) instr += 1;
+ }
+ else if (is_basic && (type->Basic.flags & Basic_Flag_Float)) {
+ if (load_size == 4) instr = WI_F32_LOAD;
+ else if (load_size == 8) instr = WI_F64_LOAD;
+ }
+ else if (is_basic && (type->Basic.flags & Basic_Flag_SIMD)) {
+ instr = WI_V128_LOAD;
+ }
+
+ WID(instr, ((WasmInstructionData) { alignment, offset }));
+
+ if (instr == WI_NOP) {
+ onyx_report_error((OnyxFilePos) { 0 },
+ "Failed to generate load instruction for type '%s'.",
+ type_get_name(type));
+ }
+
+ *pcode = code;
+}
+
+EMIT_FUNC(if, AstIfWhile* if_node) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ if (if_node->initialization != NULL) {
+ // bh_imap_put(&mod->local_map, (u64) if_node->local, local_allocate(mod->local_alloc, (AstTyped *) if_node->local));
+
+ forll (AstNode, stmt, if_node->initialization, next) {
+ emit_statement(mod, &code, stmt);
+ }
+ }
+
+ if (if_node->kind == Ast_Kind_Static_If) {
+ if (static_if_resolution(if_node)) {
+ if (if_node->true_stmt) emit_block(mod, &code, if_node->true_stmt, 1);
+ } else {
+ if (if_node->false_stmt) emit_block(mod, &code, if_node->false_stmt, 1);
+ }
+
+ *pcode = code;
+ return;
+ }
+
+ emit_expression(mod, &code, if_node->cond);
+
+ emit_enter_structured_block(mod, &code, SBT_Basic_If);
+ if (if_node->true_stmt) emit_block(mod, &code, if_node->true_stmt, 0);
+
+ if (if_node->false_stmt) {
+ WI(WI_ELSE);
+
+ if (if_node->false_stmt->kind == Ast_Kind_If) {
+ emit_if(mod, &code, (AstIfWhile *) if_node->false_stmt);
+ } else {
+ emit_block(mod, &code, if_node->false_stmt, 0);
+ }
+ }
+
+ emit_leave_structured_block(mod, &code);
+
+ *pcode = code;
+}
+
+EMIT_FUNC(while, AstIfWhile* while_node) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ if (while_node->initialization != NULL) {
+ forll (AstNode, stmt, while_node->initialization, next) {
+ emit_statement(mod, &code, stmt);
+ }
+ }
+
+ if (while_node->false_stmt == NULL) {
+ emit_enter_structured_block(mod, &code, SBT_Breakable_Block);
+ emit_enter_structured_block(mod, &code, SBT_Continue_Loop);
+
+ emit_expression(mod, &code, while_node->cond);
+ WI(WI_I32_EQZ);
+ WID(WI_COND_JUMP, 0x01);
+
+ emit_block(mod, &code, while_node->true_stmt, 0);
+
+ if (bh_arr_last(code).type != WI_JUMP)
+ WID(WI_JUMP, 0x00);
+
+ emit_leave_structured_block(mod, &code);
+ emit_leave_structured_block(mod, &code);
+
+ } else {
+ emit_expression(mod, &code, while_node->cond);
+
+ emit_enter_structured_block(mod, &code, SBT_Breakable_If);
+ emit_enter_structured_block(mod, &code, SBT_Continue_Loop);
+
+ emit_block(mod, &code, while_node->true_stmt, 0);
+
+ emit_expression(mod, &code, while_node->cond);
+ WID(WI_COND_JUMP, 0x00);
+
+ emit_leave_structured_block(mod, &code);
+ WI(WI_ELSE);
+
+ emit_block(mod, &code, while_node->false_stmt, 0);
+
+ emit_leave_structured_block(mod, &code);
+ }
+
+ *pcode = code;
+}
+
+EMIT_FUNC(for_range, AstFor* for_node, u64 iter_local) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ // NOTE: There are some aspects of the code below that rely on the
+ // low, high, and step members to be i32's. This restriction can be lifted,
+ // but it is important to change the code here.
+ // -brendanfh 2020/09/04
+
+ AstLocal* var = for_node->var;
+ u64 offset = 0;
+
+ StructMember low_mem, high_mem, step_mem;
+ type_lookup_member(builtin_range_type_type, "low", &low_mem);
+ type_lookup_member(builtin_range_type_type, "high", &high_mem);
+ type_lookup_member(builtin_range_type_type, "step", &step_mem);
+ u64 low_local = local_raw_allocate(mod->local_alloc, onyx_type_to_wasm_type(low_mem.type));
+ u64 high_local = local_raw_allocate(mod->local_alloc, onyx_type_to_wasm_type(high_mem.type));
+ u64 step_local = local_raw_allocate(mod->local_alloc, onyx_type_to_wasm_type(step_mem.type));
+
+ WIL(WI_LOCAL_SET, step_local);
+ WIL(WI_LOCAL_SET, high_local);
+ WIL(WI_LOCAL_TEE, low_local);
+ WIL(WI_LOCAL_SET, iter_local);
+
+ emit_enter_structured_block(mod, &code, SBT_Breakable_Block);
+ emit_enter_structured_block(mod, &code, SBT_Basic_Loop);
+ emit_enter_structured_block(mod, &code, SBT_Continue_Block);
+
+ WIL(WI_LOCAL_GET, iter_local);
+ WIL(WI_LOCAL_GET, high_local);
+ WI(WI_I32_GE_S);
+ WID(WI_COND_JUMP, 0x02);
+
+ emit_block(mod, &code, for_node->stmt, 0);
+
+ emit_leave_structured_block(mod, &code);
+
+ WIL(WI_LOCAL_GET, iter_local);
+ WIL(WI_LOCAL_GET, step_local);
+ WI(WI_I32_ADD);
+ WIL(WI_LOCAL_SET, iter_local);
+
+ if (bh_arr_last(code).type != WI_JUMP)
+ WID(WI_JUMP, 0x00);
+
+ emit_leave_structured_block(mod, &code);
+ emit_leave_structured_block(mod, &code);
+
+ local_raw_free(mod->local_alloc, onyx_type_to_wasm_type(low_mem.type));
+ local_raw_free(mod->local_alloc, onyx_type_to_wasm_type(high_mem.type));
+ local_raw_free(mod->local_alloc, onyx_type_to_wasm_type(step_mem.type));
+
+ *pcode = code;
+}
+
+EMIT_FUNC(for_array, AstFor* for_node, u64 iter_local) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ u64 end_ptr_local, ptr_local;
+ end_ptr_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+
+ if (for_node->by_pointer) {
+ ptr_local = iter_local;
+ } else {
+ ptr_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+ }
+
+ AstLocal* var = for_node->var;
+ b32 it_is_local = (b32) ((iter_local & LOCAL_IS_WASM) != 0);
+ u64 offset = 0;
+
+ u64 elem_size;
+ if (for_node->by_pointer) elem_size = type_size_of(var->type->Pointer.elem);
+ else elem_size = type_size_of(var->type);
+
+ WIL(WI_LOCAL_TEE, ptr_local);
+ WIL(WI_PTR_CONST, for_node->iter->type->Array.count * elem_size);
+ WI(WI_PTR_ADD);
+ WIL(WI_LOCAL_SET, end_ptr_local);
+
+ emit_enter_structured_block(mod, &code, SBT_Breakable_Block);
+ emit_enter_structured_block(mod, &code, SBT_Basic_Loop);
+ emit_enter_structured_block(mod, &code, SBT_Continue_Block);
+
+ WIL(WI_LOCAL_GET, ptr_local);
+ WIL(WI_LOCAL_GET, end_ptr_local);
+ WI(WI_PTR_GE);
+ WID(WI_COND_JUMP, 0x02);
+
+ if (!for_node->by_pointer) {
+ if (!it_is_local) emit_local_location(mod, &code, var, &offset);
+
+ WIL(WI_LOCAL_GET, ptr_local);
+ emit_load_instruction(mod, &code, var->type, 0);
+
+ if (!it_is_local) emit_store_instruction(mod, &code, var->type, offset);
+ else WIL(WI_LOCAL_SET, iter_local);
+ }
+
+ emit_block(mod, &code, for_node->stmt, 0);
+
+ emit_leave_structured_block(mod, &code);
+
+ WIL(WI_LOCAL_GET, ptr_local);
+ WIL(WI_PTR_CONST, elem_size);
+ WI(WI_PTR_ADD);
+ WIL(WI_LOCAL_SET, ptr_local);
+
+ if (bh_arr_last(code).type != WI_JUMP)
+ WID(WI_JUMP, 0x00);
+
+ emit_leave_structured_block(mod, &code);
+ emit_leave_structured_block(mod, &code);
+
+ local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+ if (!for_node->by_pointer) local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+
+ *pcode = code;
+}
+
+EMIT_FUNC(for_slice, AstFor* for_node, u64 iter_local) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ u64 end_ptr_local, ptr_local;
+ end_ptr_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+
+ if (for_node->by_pointer) {
+ ptr_local = iter_local;
+ } else {
+ ptr_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+ }
+
+ AstLocal* var = for_node->var;
+ b32 it_is_local = (b32) ((iter_local & LOCAL_IS_WASM) != 0);
+ u64 offset = 0;
+
+ u64 elem_size;
+ if (for_node->by_pointer) elem_size = type_size_of(var->type->Pointer.elem);
+ else elem_size = type_size_of(var->type);
+
+ WIL(WI_LOCAL_SET, end_ptr_local);
+ WIL(WI_LOCAL_TEE, ptr_local);
+ WIL(WI_LOCAL_GET, end_ptr_local);
+ if (elem_size != 1) {
+ WID(WI_PTR_CONST, elem_size);
+ WI(WI_PTR_MUL);
+ }
+ WI(WI_PTR_ADD);
+ WIL(WI_LOCAL_SET, end_ptr_local);
+
+ emit_enter_structured_block(mod, &code, SBT_Breakable_Block);
+ emit_enter_structured_block(mod, &code, SBT_Basic_Loop);
+ emit_enter_structured_block(mod, &code, SBT_Continue_Block);
+
+ WIL(WI_LOCAL_GET, ptr_local);
+ WIL(WI_LOCAL_GET, end_ptr_local);
+ WI(WI_PTR_GE);
+ WID(WI_COND_JUMP, 0x02);
+
+ if (!for_node->by_pointer) {
+ if (!it_is_local) emit_local_location(mod, &code, var, &offset);
+
+ WIL(WI_LOCAL_GET, ptr_local);
+ emit_load_instruction(mod, &code, var->type, 0);
+
+ if (!it_is_local) emit_store_instruction(mod, &code, var->type, offset);
+ else WIL(WI_LOCAL_SET, iter_local);
+ }
+
+ emit_block(mod, &code, for_node->stmt, 0);
+
+ emit_leave_structured_block(mod, &code);
+
+ WIL(WI_LOCAL_GET, ptr_local);
+ WIL(WI_PTR_CONST, elem_size);
+ WI(WI_PTR_ADD);
+ WIL(WI_LOCAL_SET, ptr_local);
+
+ if (bh_arr_last(code).type != WI_JUMP)
+ WID(WI_JUMP, 0x00);
+
+ emit_leave_structured_block(mod, &code);
+ emit_leave_structured_block(mod, &code);
+
+ local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+ if (!for_node->by_pointer) local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+
+ *pcode = code;
+}
+
+EMIT_FUNC(for_iterator, AstFor* for_node, u64 iter_local) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ // Allocate temporaries for iterator contents
+ u64 iterator_data_ptr = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+ u64 iterator_next_func = local_raw_allocate(mod->local_alloc, WASM_TYPE_FUNC);
+ u64 iterator_close_func = local_raw_allocate(mod->local_alloc, WASM_TYPE_FUNC);
+ u64 iterator_done_bool = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
+ WIL(WI_LOCAL_SET, iterator_close_func);
+ WIL(WI_LOCAL_SET, iterator_next_func);
+ WIL(WI_LOCAL_SET, iterator_data_ptr);
+
+ AstLocal* var = for_node->var;
+ b32 it_is_local = (b32) ((iter_local & LOCAL_IS_WASM) != 0);
+ u64 offset = 0;
+
+ // Enter a deferred statement for the auto-close
+ emit_enter_structured_block(mod, &code, SBT_Basic_Block);
+
+ TypeWithOffset close_func_type;
+ type_linear_member_lookup(for_node->iter->type, 2, &close_func_type);
+ i32 close_type_idx = generate_type_idx(mod, close_func_type.type);
+
+ WasmInstruction* close_instructions = bh_alloc_array(global_heap_allocator, WasmInstruction, 8);
+ close_instructions[0] = (WasmInstruction) { WI_LOCAL_GET, { .l = iterator_close_func } };
+ close_instructions[1] = (WasmInstruction) { WI_I32_CONST, { .l = mod->null_proc_func_idx } };
+ close_instructions[2] = (WasmInstruction) { WI_I32_NE, { .l = 0x00 } };
+ close_instructions[3] = (WasmInstruction) { WI_IF_START, { .l = 0x40 } };
+ close_instructions[4] = (WasmInstruction) { WI_LOCAL_GET, { .l = iterator_data_ptr } };
+ close_instructions[5] = (WasmInstruction) { WI_LOCAL_GET, { .l = iterator_close_func } };
+ close_instructions[6] = (WasmInstruction) { WI_CALL_INDIRECT, { .l = close_type_idx } };
+ close_instructions[7] = (WasmInstruction) { WI_IF_END, { .l = 0x00 } };
+
+ emit_defer_code(mod, &code, close_instructions, 8);
+
+ emit_enter_structured_block(mod, &code, SBT_Breakable_Block);
+ emit_enter_structured_block(mod, &code, SBT_Continue_Loop);
+
+ if (!it_is_local) emit_local_location(mod, &code, var, &offset);
+
+ {
+ WIL(WI_LOCAL_GET, iterator_data_ptr);
+ WIL(WI_LOCAL_GET, iterator_next_func);
+
+ // CLEANUP: Calling a function is way too f-ing complicated. FACTOR IT!!
+ u64 stack_top_idx = bh_imap_get(&mod->index_map, (u64) &builtin_stack_top);
+
+ TypeWithOffset next_func_type;
+ type_linear_member_lookup(for_node->iter->type, 1, &next_func_type);
+ Type* return_type = next_func_type.type->Function.return_type;
+
+ u32 return_size = type_size_of(return_type);
+ u32 return_align = type_alignment_of(return_type);
+ bh_align(return_size, return_align);
+
+ u64 reserve_size = return_size;
+ bh_align(reserve_size, 16);
+
+ WID(WI_GLOBAL_GET, stack_top_idx);
+ WID(WI_PTR_CONST, reserve_size);
+ WI(WI_PTR_ADD);
+ WID(WI_GLOBAL_SET, stack_top_idx);
+
+ i32 type_idx = generate_type_idx(mod, next_func_type.type);
+ WID(WI_CALL_INDIRECT, ((WasmInstructionData) { type_idx, 0x00 }));
+
+ WID(WI_GLOBAL_GET, stack_top_idx);
+ WID(WI_PTR_CONST, reserve_size);
+ WI(WI_PTR_SUB);
+ WID(WI_GLOBAL_SET, stack_top_idx);
+
+ WID(WI_GLOBAL_GET, stack_top_idx);
+ emit_load_instruction(mod, &code, return_type, reserve_size - return_size);
+ }
+
+ WIL(WI_LOCAL_SET, iterator_done_bool);
+
+ if (!it_is_local) emit_store_instruction(mod, &code, var->type, offset);
+ else WIL(WI_LOCAL_SET, iter_local);
+
+ WIL(WI_LOCAL_GET, iterator_done_bool);
+ WI(WI_I32_EQZ);
+ WID(WI_COND_JUMP, 0x01);
+
+ emit_block(mod, &code, for_node->stmt, 0);
+ WID(WI_JUMP, 0x00);
+
+ emit_leave_structured_block(mod, &code);
+ emit_leave_structured_block(mod, &code);
+
+ emit_deferred_stmts(mod, &code);
+ emit_leave_structured_block(mod, &code);
+
+ local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+ local_raw_free(mod->local_alloc, WASM_TYPE_FUNC);
+ local_raw_free(mod->local_alloc, WASM_TYPE_FUNC);
+ local_raw_free(mod->local_alloc, WASM_TYPE_INT32);
+ *pcode = code;
+}
+
+EMIT_FUNC(for, AstFor* for_node) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ AstLocal* var = for_node->var;
+ u64 iter_local = local_allocate(mod->local_alloc, (AstTyped *) var);
+ bh_imap_put(&mod->local_map, (u64) var, iter_local);
+
+ emit_expression(mod, &code, for_node->iter);
+
+ switch (for_node->loop_type) {
+ case For_Loop_Range: emit_for_range(mod, &code, for_node, iter_local); break;
+ case For_Loop_Array: emit_for_array(mod, &code, for_node, iter_local); break;
+ // NOTE: A dynamic array is just a slice with a capacity and allocator on the end.
+ // Just dropping the extra fields will mean we can just use the slice implementation.
+ // - brendanfh 2020/09/04
+ // - brendanfh 2021/04/13
+ case For_Loop_DynArr: WI(WI_DROP); WI(WI_DROP); WI(WI_DROP);
+ case For_Loop_Slice: emit_for_slice(mod, &code, for_node, iter_local); break;
+ case For_Loop_Iterator: emit_for_iterator(mod, &code, for_node, iter_local); break;
+ default: onyx_report_error(for_node->token->pos, "Invalid for loop type. You should probably not be seeing this...");
+ }
+
+ local_free(mod->local_alloc, (AstTyped *) var);
+
+ *pcode = code;
+}
+
+EMIT_FUNC(switch, AstSwitch* switch_node) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ bh_imap block_map;
+ bh_imap_init(&block_map, global_heap_allocator, bh_arr_length(switch_node->cases));
+
+ if (switch_node->initialization != NULL) {
+ forll (AstNode, stmt, switch_node->initialization, next) {
+ emit_statement(mod, &code, stmt);
+ }
+ }
+
+ emit_enter_structured_block(mod, &code, SBT_Breakable_Block);
+
+ u64 block_num = 0;
+ bh_arr_each(AstSwitchCase, sc, switch_node->cases) {
+ if (bh_imap_has(&block_map, (u64) sc->block)) continue;
+
+ emit_enter_structured_block(mod, &code, SBT_Fallthrough_Block);
+
+ bh_imap_put(&block_map, (u64) sc->block, block_num);
+ block_num++;
+ }
+
+ u64 count = switch_node->max_case + 1 - switch_node->min_case;
+ BranchTable* bt = bh_alloc(mod->extended_instr_alloc, sizeof(BranchTable) + sizeof(u32) * count);
+ bt->count = count;
+ bt->default_case = block_num;
+ fori (i, 0, bt->count) bt->cases[i] = bt->default_case;
+
+ bh_arr_each(bh__imap_entry, sc, switch_node->case_map.entries) {
+ bt->cases[sc->key - switch_node->min_case] = bh_imap_get(&block_map, (u64) sc->value);
+ }
+
+ // CLEANUP: We enter a new block here in order to setup the correct
+ // indicies for the jump targets in the branch table. For example,
+ //
+ // <expr>
+ // jump_table
+ // label0:
+ // ...
+ // label1:
+ // ...
+ //
+ // If we didn't enter a new block, then jumping to label 0, would jump
+ // to the second block, and so on.
+ WID(WI_BLOCK_START, 0x40);
+ emit_expression(mod, &code, switch_node->expr);
+ if (switch_node->min_case != 0) {
+ WID(WI_I32_CONST, switch_node->min_case);
+ WI(WI_I32_SUB);
+ }
+ WIP(WI_JUMP_TABLE, bt);
+ WI(WI_BLOCK_END);
+
+ bh_arr_each(AstSwitchCase, sc, switch_node->cases) {
+ if (bh_imap_get(&block_map, (u64) sc->block) == 0xdeadbeef) continue;
+
+ u64 bn = bh_imap_get(&block_map, (u64) sc->block);
+
+ emit_block(mod, &code, sc->block, 0);
+
+ if (bh_arr_last(code).type != WI_JUMP)
+ WID(WI_JUMP, block_num - bn);
+
+ emit_leave_structured_block(mod, &code);
+
+ bh_imap_put(&block_map, (u64) sc->block, 0xdeadbeef);
+ }
+
+ if (switch_node->default_case != NULL) {
+ emit_block(mod, &code, switch_node->default_case, 0);
+ }
+
+ emit_leave_structured_block(mod, &code);
+
+ bh_imap_free(&block_map);
+ *pcode = code;
+}
+
+EMIT_FUNC(defer, AstDefer* defer) {
+ bh_arr_push(mod->deferred_stmts, ((DeferredStmt) {
+ .type = Deferred_Stmt_Node,
+ .depth = bh_arr_length(mod->structured_jump_target),
+ .stmt = defer->stmt,
+ }));
+}
+
+EMIT_FUNC(defer_code, WasmInstruction* deferred_code, u32 code_count) {
+ bh_arr_push(mod->deferred_stmts, ((DeferredStmt) {
+ .type = Deferred_Stmt_Code,
+ .depth = bh_arr_length(mod->structured_jump_target),
+ .instructions = deferred_code,
+ .instruction_count = code_count,
+ }));
+}
+
+EMIT_FUNC(deferred_stmt, DeferredStmt deferred_stmt) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ switch (deferred_stmt.type) {
+ case Deferred_Stmt_Node: emit_statement(mod, &code, deferred_stmt.stmt); break;
+ case Deferred_Stmt_Code: {
+ fori (i, 0, deferred_stmt.instruction_count) {
+ bh_arr_push(code, deferred_stmt.instructions[i]);
+ }
+ break;
+ }
+ }
+
+ *pcode = code;
+}
+
+EMIT_FUNC_NO_ARGS(deferred_stmts) {
+ if (bh_arr_length(mod->deferred_stmts) <= 0) return;
+
+ u64 depth = bh_arr_length(mod->structured_jump_target);
+
+ while (bh_arr_length(mod->deferred_stmts) > 0 && bh_arr_last(mod->deferred_stmts).depth >= depth) {
+ DeferredStmt stmt = bh_arr_pop(mod->deferred_stmts);
+ emit_deferred_stmt(mod, pcode, stmt);
+ }
+}
+
+// NOTE: These need to be in the same order as
+// the OnyxBinaryOp enum
+static const WasmInstructionType binop_map[][4] = {
+ // I32 I64 F32 F64
+ /* ADD */ { WI_I32_ADD, WI_I64_ADD, WI_F32_ADD, WI_F64_ADD },
+ /* SUB */ { WI_I32_SUB, WI_I64_SUB, WI_F32_SUB, WI_F64_SUB },
+ /* MUL */ { WI_I32_MUL, WI_I64_MUL, WI_F32_MUL, WI_F64_MUL },
+ /* DIV */ { WI_I32_DIV_S, WI_I64_DIV_S, WI_F32_DIV, WI_F64_DIV },
+ /* REM */ { WI_I32_REM_S, WI_I64_REM_S, WI_NOP, WI_NOP },
+
+ /* EQ */ { WI_I32_EQ, WI_I64_EQ, WI_F32_EQ, WI_F64_EQ },
+ /* NEQ */ { WI_I32_NE, WI_I64_NE, WI_F32_NE , WI_F64_NE },
+ /* LT */ { WI_I32_LT_S, WI_I64_LT_S, WI_F32_LT, WI_F64_LT },
+ /* LTE */ { WI_I32_LE_S, WI_I64_LE_S, WI_F32_LE, WI_F64_LE },
+ /* GT */ { WI_I32_GT_S, WI_I64_GT_S, WI_F32_GT, WI_F64_GT },
+ /* GTE */ { WI_I32_GE_S, WI_I64_GE_S, WI_F32_GE, WI_F64_GE },
+
+ /* AND */ { WI_I32_AND, WI_I64_AND, WI_NOP, WI_NOP },
+ /* OR */ { WI_I32_OR, WI_I64_OR, WI_NOP, WI_NOP },
+ /* XOR */ { WI_I32_XOR, WI_I64_XOR, WI_NOP, WI_NOP },
+ /* SHL */ { WI_I32_SHL, WI_I64_SHL, WI_NOP, WI_NOP },
+ /* SHR */ { WI_I32_SHR_U, WI_I64_SHR_U, WI_NOP, WI_NOP },
+ /* SAR */ { WI_I32_SHR_S, WI_I64_SHR_S, WI_NOP, WI_NOP },
+
+ /* BAND */ { WI_I32_AND, WI_I64_AND, WI_NOP, WI_NOP },
+ /* BOR */ { WI_I32_OR, WI_I64_OR, WI_NOP, WI_NOP },
+};
+
+EMIT_FUNC(binop, AstBinaryOp* binop) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ if (binop_is_assignment(binop->operation)) {
+ emit_assignment(mod, &code, binop);
+ *pcode = code;
+ return;
+ }
+
+ b32 is_sign_significant = 0;
+ switch (binop->operation) {
+ case Binary_Op_Divide: case Binary_Op_Modulus:
+ case Binary_Op_Less: case Binary_Op_Less_Equal:
+ case Binary_Op_Greater: case Binary_Op_Greater_Equal:
+ is_sign_significant = 1;
+ }
+
+ WasmType operator_type = onyx_type_to_wasm_type(binop->left->type);
+ i32 optype = 0;
+ if (operator_type == WASM_TYPE_INT32) optype = 0;
+ else if (operator_type == WASM_TYPE_INT64) optype = 1;
+ else if (operator_type == WASM_TYPE_FLOAT32) optype = 2;
+ else if (operator_type == WASM_TYPE_FLOAT64) optype = 3;
+
+ WasmInstructionType binop_instr = binop_map[(i32) binop->operation][optype];
+
+ assert(binop_instr != WI_NOP);
+
+ // NOTE: Use unsigned variant if needed
+ // Unsigned instructions are always right after
+ // the signed equivalent
+ if (is_sign_significant) {
+ if (binop->left->type->Basic.flags & Basic_Flag_Unsigned) {
+ binop_instr = (WasmInstructionType) ((i32) binop_instr + 1);
+ }
+ }
+
+ emit_expression(mod, &code, binop->left);
+ emit_expression(mod, &code, binop->right);
+
+ WI(binop_instr);
+
+ *pcode = code;
+}
+
+EMIT_FUNC(unaryop, AstUnaryOp* unop) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ switch (unop->operation) {
+ case Unary_Op_Negate: {
+ TypeBasic* type = &unop->type->Basic;
+
+ if (type->kind == Basic_Kind_I32
+ || type->kind == Basic_Kind_I16
+ || type->kind == Basic_Kind_I8) {
+ WID(WI_I32_CONST, 0x00);
+ emit_expression(mod, &code, unop->expr);
+ WI(WI_I32_SUB);
+
+ }
+ else if (type->kind == Basic_Kind_I64) {
+ WID(WI_I64_CONST, 0x00);
+ emit_expression(mod, &code, unop->expr);
+ WI(WI_I64_SUB);
+
+ }
+ else {
+ emit_expression(mod, &code, unop->expr);
+
+ if (type->kind == Basic_Kind_F32) WI(WI_F32_NEG);
+ if (type->kind == Basic_Kind_F64) WI(WI_F64_NEG);
+ }
+
+ break;
+ }
+
+ case Unary_Op_Not:
+ emit_expression(mod, &code, unop->expr);
+
+ WI(WI_I32_EQZ);
+ break;
+
+ case Unary_Op_Bitwise_Not: {
+ emit_expression(mod, &code, unop->expr);
+
+ TypeBasic* type = &unop->type->Basic;
+
+ if (type->kind == Basic_Kind_I8 || type->kind == Basic_Kind_U8) {
+ WID(WI_I32_CONST, 0xff);
+ WI(WI_I32_XOR);
+ }
+ else if (type->kind == Basic_Kind_I16 || type->kind == Basic_Kind_U16) {
+ WID(WI_I32_CONST, 0xffff);
+ WI(WI_I32_XOR);
+ }
+ else if (type->kind == Basic_Kind_I32 || type->kind == Basic_Kind_U32) {
+ WID(WI_I32_CONST, 0xffffffff);
+ WI(WI_I32_XOR);
+ }
+ else if (type->kind == Basic_Kind_I64 || type->kind == Basic_Kind_U64) {
+ WIL(WI_I64_CONST, 0xffffffffffffffff);
+ WI(WI_I64_XOR);
+ }
+
+ break;
+ }
+
+ case Unary_Op_Auto_Cast:
+ case Unary_Op_Cast: emit_cast(mod, &code, unop); break;
+ }
+
+ *pcode = code;
+}
+
+// Calling a procedure in Onyx.
+//
+// This documentation should be placed elsewhere, but for right now I'm going to write it in the relevant
+// piece of code. Calling a procedure is relatively simple, at least compared to other calling conventions
+// out there, mostly due the fact that this is WebAssembly, where registers are "infinite" and there's no
+// really need to use stack canaries for security.
+//
+// The biggest piece to understand is how the stack gets laid out for the called procedure. To be confusing,
+// there are two stacks at play: the WASM expression stack, and the linear memory stack. Here is the general
+// lay out for calling a procedure with the following signature.
+//
+// foo :: (x: i32, y: str, z: [..] i32, va: ..i32) -> (i32, i32)
+//
+// WASM stack: top is last pushed
+//
+// vararg count
+// vararg pointer to variadic arguments in the linear memory
+// pointer to struct-like arguments (z)
+// simple structs/primitives (y, x)
+//
+// Linear memory stack:
+//
+// ... | struct-like arguments (z) | variadic arguments (va) | return space | ...
+//
+// The interesting part from above is the fact that 'y' gets passed on the WASM stack, not the linear memory
+// stack, even though a 'str' in Onyx is 2-component structure. This is because so-called "simple" structures,
+// i.e. structures that are completely flat, with no sub-structures, are passed as multiple primitives. I do
+// this because many times for interoperability, it is nicer to get two primitive values for the pointer and
+// count of a slice, instead of a pointer.
+EMIT_FUNC(call, AstCall* call) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ u64 stack_top_idx = bh_imap_get(&mod->index_map, (u64) &builtin_stack_top);
+ u64 stack_top_store_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+
+ // Because it would be inefficient to increment and decrement the global stack pointer for every argument,
+ // a simple set of instructions increments it once to the size it will need to be. However, because it is
+ // impossible to know what size the reserved memory will be, a location patch is taken in order to fill it
+ // in later.
+ u32 reserve_space_patch = bh_arr_length(code);
+ WID(WI_GLOBAL_GET, stack_top_idx);
+ WIL(WI_LOCAL_TEE, stack_top_store_local);
+ WID(WI_PTR_CONST, 0); // This will be filled in later.
+ WI(WI_PTR_ADD);
+ WID(WI_GLOBAL_SET, stack_top_idx);
+
+ u32 reserve_size = 0;
+ u32 vararg_count = 0;
+ i32 vararg_offset = -1;
+
+ u32* vararg_any_offsets=NULL;
+ u32* vararg_any_types=NULL;
+ if (call->va_kind == VA_Kind_Any) {
+ vararg_any_offsets = bh_alloc_array(global_scratch_allocator, u32, bh_arr_length(call->args.values));
+ vararg_any_types = bh_alloc_array(global_scratch_allocator, u32, bh_arr_length(call->args.values));
+ }
+
+ bh_arr_each(AstTyped *, parg, call->args.values) {
+ AstArgument* arg = (AstArgument *) *parg;
+ if (arg->is_baked) continue;
+
+ b32 place_on_stack = 0;
+
+ if (type_get_param_pass(arg->value->type) == Param_Pass_By_Implicit_Pointer) {
+ // This arguments needs to be written to the stack because it is not a simple structure.
+ place_on_stack = 1;
+ }
+
+ if (arg->va_kind != VA_Kind_Not_VA) {
+ // This is a variadic argument and needs to be written to the stack. If the starting
+ // location of the vararg array hasn't been noted, note it.
+ if (vararg_offset < 0) vararg_offset = reserve_size;
+
+ place_on_stack = 1;
+ vararg_count += 1;
+ }
+
+ if (place_on_stack) WIL(WI_LOCAL_GET, stack_top_store_local);
+
+ emit_expression(mod, &code, arg->value);
+
+ if (place_on_stack) {
+ emit_store_instruction(mod, &code, arg->value->type, reserve_size);
+
+ if (arg->va_kind == VA_Kind_Not_VA) {
+ // Non-variadic arguments on the stack need a pointer to them placed on the WASM stack.
+ WIL(WI_LOCAL_GET, stack_top_store_local);
+ WID(WI_PTR_CONST, reserve_size);
+ WI(WI_PTR_ADD);
+ }
+
+ if (arg->va_kind == VA_Kind_Any) {
+ vararg_any_offsets[vararg_count - 1] = reserve_size;
+ vararg_any_types[vararg_count - 1] = arg->value->type->id;
+ }
+
+ reserve_size += type_size_of(arg->value->type);
+ }
+ }
+
+ switch (call->va_kind) {
+ case VA_Kind_Any: {
+ vararg_offset = reserve_size;
+
+ i32 any_size = type_size_of(type_build_from_ast(context.ast_alloc, builtin_any_type));
+
+ fori (i, 0, vararg_count) {
+ WIL(WI_LOCAL_GET, stack_top_store_local);
+ WIL(WI_LOCAL_GET, stack_top_store_local);
+ WID(WI_PTR_CONST, vararg_any_offsets[i]);
+ WI(WI_PTR_ADD);
+ emit_store_instruction(mod, &code, &basic_types[Basic_Kind_Rawptr], vararg_offset + i * any_size);
+
+ WIL(WI_LOCAL_GET, stack_top_store_local);
+ WID(WI_I32_CONST, vararg_any_types[i]);
+ emit_store_instruction(mod, &code, &basic_types[Basic_Kind_Type_Index], vararg_offset + i * any_size + 8);
+
+ reserve_size += any_size;
+ }
+
+ // fallthrough
+ }
+
+ case VA_Kind_Typed: {
+ WIL(WI_LOCAL_GET, stack_top_store_local);
+ if (vararg_offset > 0) {
+ WID(WI_PTR_CONST, vararg_offset);
+ WI(WI_PTR_ADD);
+ }
+ WID(WI_I32_CONST, vararg_count);
+ break;
+ }
+
+ case VA_Kind_Untyped: {
+ WIL(WI_LOCAL_GET, stack_top_store_local);
+ WIL(WI_LOCAL_GET, stack_top_store_local);
+ if (vararg_offset > 0) {
+ WID(WI_PTR_CONST, vararg_offset);
+ WI(WI_PTR_ADD);
+ }
+ emit_store_instruction(mod, &code, &basic_types[Basic_Kind_Rawptr], reserve_size);
+
+ // NOTE: There will be 4 uninitialized bytes here, because pointers are only 4 bytes in WASM.
+
+ WIL(WI_LOCAL_GET, stack_top_store_local);
+ WID(WI_I32_CONST, vararg_count);
+ emit_store_instruction(mod, &code, &basic_types[Basic_Kind_I32], reserve_size + 8);
+
+ WIL(WI_LOCAL_GET, stack_top_store_local);
+ if (reserve_size > 0) {
+ WID(WI_PTR_CONST, reserve_size);
+ WI(WI_PTR_ADD);
+ }
+
+ reserve_size += 12;
+ break;
+ }
+ }
+
+ CallingConvention cc = type_function_get_cc(call->callee->type);
+ assert(cc != CC_Undefined);
+
+ Type* return_type = call->callee->type->Function.return_type;
+ u32 return_size = type_size_of(return_type);
+ assert(return_size % type_alignment_of(return_type) == 0);
+
+ if (cc == CC_Return_Stack) reserve_size += return_size;
+
+ if (call->callee->kind == Ast_Kind_Function) {
+ i32 func_idx = (i32) bh_imap_get(&mod->index_map, (u64) call->callee);
+ bh_arr_push(code, ((WasmInstruction){ WI_CALL, func_idx }));
+
+ } else {
+ emit_expression(mod, &code, call->callee);
+
+ i32 type_idx = generate_type_idx(mod, call->callee->type);
+ WID(WI_CALL_INDIRECT, ((WasmInstructionData) { type_idx, 0x00 }));
+ }
+
+ if (reserve_size > 0) {
+ WIL(WI_LOCAL_GET, stack_top_store_local);
+ WID(WI_GLOBAL_SET, stack_top_idx);
+
+ bh_align(reserve_size, 16);
+ code[reserve_space_patch + 2].data.l = reserve_size;
+
+ } else {
+ fori (i, 0, 5) code[reserve_space_patch + i].type = WI_NOP;
+ }
+
+ if (cc == CC_Return_Stack) {
+ WID(WI_GLOBAL_GET, stack_top_idx);
+ emit_load_instruction(mod, &code, return_type, reserve_size - return_size);
+ }
+
+ local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+ *pcode = code;
+}
+
+// BUG: This implementation assumes that the host system C's implementation is using
+// little endian integers.
+#define SIMD_INT_CONST_INTRINSIC(type, count) { \
+ type* byte_buffer = bh_alloc(mod->extended_instr_alloc, 16); \
+ bh_arr(AstArgument *) arg_arr = (bh_arr(AstArgument *)) call->args.values; \
+ fori (i, 0, count) { \
+ if (arg_arr[i]->value->kind != Ast_Kind_NumLit) { \
+ onyx_report_error(arg_arr[i]->token->pos, \
+ "SIMD constants expect compile time constants as parameters. The %d%s parameter was not.", \
+ i, bh_num_suffix(i)); \
+ *pcode = code; \
+ return; \
+ } \
+ byte_buffer[i] = (type) ((AstNumLit *) arg_arr[i]->value)->value.l; \
+ } \
+ WIP(WI_V128_CONST, byte_buffer); \
+ }
+
+#define SIMD_EXTRACT_LANE_INSTR(instr, arg_arr) \
+ emit_expression(mod, &code, arg_arr[0]->value);\
+ if (arg_arr[1]->value->kind != Ast_Kind_NumLit) { \
+ onyx_report_error(arg_arr[1]->token->pos, "SIMD lane instructions expect a compile time lane number."); \
+ *pcode = code; \
+ return; \
+ } \
+ WID(instr, (u8) ((AstNumLit *) arg_arr[1]->value)->value.i);
+
+#define SIMD_REPLACE_LANE_INSTR(instr, arg_arr) { \
+ emit_expression(mod, &code, arg_arr[0]->value);\
+ if (arg_arr[1]->value->kind != Ast_Kind_NumLit) { \
+ onyx_report_error(arg_arr[1]->token->pos, "SIMD lane instructions expect a compile time lane number."); \
+ *pcode = code; \
+ return; \
+ } \
+ u8 lane = (u8) ((AstNumLit *) arg_arr[1]->value)->value.i; \
+ emit_expression(mod, &code, arg_arr[2]->value); \
+ WID(instr, lane); \
+}
+
+
+EMIT_FUNC(intrinsic_call, AstCall* call) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ b32 place_arguments_normally = 1;
+
+ switch (call->intrinsic) {
+ case ONYX_INTRINSIC_V128_CONST:
+ case ONYX_INTRINSIC_I8X16_CONST: case ONYX_INTRINSIC_I16X8_CONST:
+ case ONYX_INTRINSIC_I32X4_CONST: case ONYX_INTRINSIC_I64X2_CONST:
+ case ONYX_INTRINSIC_F32X4_CONST: case ONYX_INTRINSIC_F64X2_CONST:
+ case ONYX_INTRINSIC_I8X16_EXTRACT_LANE_S: case ONYX_INTRINSIC_I8X16_EXTRACT_LANE_U:
+ case ONYX_INTRINSIC_I16X8_EXTRACT_LANE_S: case ONYX_INTRINSIC_I16X8_EXTRACT_LANE_U:
+ case ONYX_INTRINSIC_I32X4_EXTRACT_LANE: case ONYX_INTRINSIC_I64X2_EXTRACT_LANE:
+ case ONYX_INTRINSIC_F32X4_EXTRACT_LANE: case ONYX_INTRINSIC_F64X2_EXTRACT_LANE:
+ case ONYX_INTRINSIC_I8X16_REPLACE_LANE: case ONYX_INTRINSIC_I16X8_REPLACE_LANE:
+ case ONYX_INTRINSIC_I32X4_REPLACE_LANE: case ONYX_INTRINSIC_I64X2_REPLACE_LANE:
+ case ONYX_INTRINSIC_F32X4_REPLACE_LANE: case ONYX_INTRINSIC_F64X2_REPLACE_LANE:
+ case ONYX_INTRINSIC_I8X16_SHUFFLE:
+ place_arguments_normally = 0;
+
+ default: break;
+ }
+
+ if (place_arguments_normally) {
+ bh_arr_each(AstTyped *, arg, call->args.values) {
+ emit_expression(mod, &code, *arg);
+ }
+ }
+
+ switch (call->intrinsic) {
+ case ONYX_INTRINSIC_MEMORY_SIZE: WID(WI_MEMORY_SIZE, 0x00); break;
+ case ONYX_INTRINSIC_MEMORY_GROW: WID(WI_MEMORY_GROW, 0x00); break;
+ case ONYX_INTRINSIC_MEMORY_COPY:
+ if (context.options->use_post_mvp_features) {
+ WIL(WI_MEMORY_COPY, 0x00);
+ } else {
+ emit_intrinsic_memory_copy(mod, &code);
+ }
+ break;
+ case ONYX_INTRINSIC_MEMORY_FILL:
+ if (context.options->use_post_mvp_features) {
+ WIL(WI_MEMORY_FILL, 0x00);
+ } else {
+ emit_intrinsic_memory_fill(mod, &code);
+ }
+ break;
+
+ case ONYX_INTRINSIC_INITIALIZE: {
+ Type* type_to_initialize = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem;
+ emit_initialize_type(mod, &code, type_to_initialize, call->token);
+ break;
+ }
+
+ case ONYX_INTRINSIC_ZERO_VALUE: {
+ // NOTE: This probably will not have to make an allocation.
+ Type* zero_type = type_build_from_ast(context.ast_alloc, (AstType *) ((AstArgument *) call->original_args.values[0])->value);
+ emit_zero_value_for_type(mod, &code, zero_type, call->token);
+ break;
+ }
+
+ case ONYX_INTRINSIC_I32_CLZ: WI(WI_I32_CLZ); break;
+ case ONYX_INTRINSIC_I32_CTZ: WI(WI_I32_CTZ); break;
+ case ONYX_INTRINSIC_I32_POPCNT: WI(WI_I32_POPCNT); break;
+ case ONYX_INTRINSIC_I32_AND: WI(WI_I32_AND); break;
+ case ONYX_INTRINSIC_I32_OR: WI(WI_I32_OR); break;
+ case ONYX_INTRINSIC_I32_XOR: WI(WI_I32_XOR); break;
+ case ONYX_INTRINSIC_I32_SHL: WI(WI_I32_SHL); break;
+ case ONYX_INTRINSIC_I32_SLR: WI(WI_I32_SHR_U); break;
+ case ONYX_INTRINSIC_I32_SAR: WI(WI_I32_SHR_S); break;
+ case ONYX_INTRINSIC_I32_ROTL: WI(WI_I32_ROTL); break;
+ case ONYX_INTRINSIC_I32_ROTR: WI(WI_I32_ROTR); break;
+
+ case ONYX_INTRINSIC_I64_CLZ: WI(WI_I64_CLZ); break;
+ case ONYX_INTRINSIC_I64_CTZ: WI(WI_I64_CTZ); break;
+ case ONYX_INTRINSIC_I64_POPCNT: WI(WI_I64_POPCNT); break;
+ case ONYX_INTRINSIC_I64_AND: WI(WI_I64_AND); break;
+ case ONYX_INTRINSIC_I64_OR: WI(WI_I64_OR); break;
+ case ONYX_INTRINSIC_I64_XOR: WI(WI_I64_XOR); break;
+ case ONYX_INTRINSIC_I64_SHL: WI(WI_I64_SHL); break;
+ case ONYX_INTRINSIC_I64_SLR: WI(WI_I64_SHR_U); break;
+ case ONYX_INTRINSIC_I64_SAR: WI(WI_I64_SHR_S); break;
+ case ONYX_INTRINSIC_I64_ROTL: WI(WI_I64_ROTL); break;
+ case ONYX_INTRINSIC_I64_ROTR: WI(WI_I64_ROTR); break;
+
+ case ONYX_INTRINSIC_F32_ABS: WI(WI_F32_ABS); break;
+ case ONYX_INTRINSIC_F32_CEIL: WI(WI_F32_CEIL); break;
+ case ONYX_INTRINSIC_F32_FLOOR: WI(WI_F32_FLOOR); break;
+ case ONYX_INTRINSIC_F32_TRUNC: WI(WI_F32_TRUNC); break;
+ case ONYX_INTRINSIC_F32_NEAREST: WI(WI_F32_NEAREST); break;
+ case ONYX_INTRINSIC_F32_SQRT: WI(WI_F32_SQRT); break;
+ case ONYX_INTRINSIC_F32_MIN: WI(WI_F32_MIN); break;
+ case ONYX_INTRINSIC_F32_MAX: WI(WI_F32_MAX); break;
+ case ONYX_INTRINSIC_F32_COPYSIGN: WI(WI_F32_COPYSIGN); break;
+
+ case ONYX_INTRINSIC_F64_ABS: WI(WI_F64_ABS); break;
+ case ONYX_INTRINSIC_F64_CEIL: WI(WI_F64_CEIL); break;
+ case ONYX_INTRINSIC_F64_FLOOR: WI(WI_F64_FLOOR); break;
+ case ONYX_INTRINSIC_F64_TRUNC: WI(WI_F64_TRUNC); break;
+ case ONYX_INTRINSIC_F64_NEAREST: WI(WI_F64_NEAREST); break;
+ case ONYX_INTRINSIC_F64_SQRT: WI(WI_F64_SQRT); break;
+ case ONYX_INTRINSIC_F64_MIN: WI(WI_F64_MIN); break;
+ case ONYX_INTRINSIC_F64_MAX: WI(WI_F64_MAX); break;
+ case ONYX_INTRINSIC_F64_COPYSIGN: WI(WI_F64_COPYSIGN); break;
+
+ case ONYX_INTRINSIC_I8X16_CONST:
+ case ONYX_INTRINSIC_V128_CONST: SIMD_INT_CONST_INTRINSIC(u8, 16); break;
+ case ONYX_INTRINSIC_I16X8_CONST: SIMD_INT_CONST_INTRINSIC(u16, 8); break;
+ case ONYX_INTRINSIC_I32X4_CONST: SIMD_INT_CONST_INTRINSIC(u32, 4); break;
+ case ONYX_INTRINSIC_I64X2_CONST: SIMD_INT_CONST_INTRINSIC(u64, 2); break;
+ case ONYX_INTRINSIC_F32X4_CONST: {
+ f32* byte_buffer = bh_alloc(mod->extended_instr_alloc, 16);
+ bh_arr(AstArgument *) arg_arr = (bh_arr(AstArgument *)) call->args.values;
+ fori (i, 0, 4) {
+ if (arg_arr[i]->value->kind != Ast_Kind_NumLit) {
+ onyx_report_error(arg_arr[i]->token->pos,
+ "SIMD constants expect compile time constants as parameters. The %d%s parameter was not.",
+ i, bh_num_suffix(i));
+ *pcode = code;
+ return;
+ }
+ byte_buffer[i] = (f32) ((AstNumLit *) arg_arr[i]->value)->value.f;
+ }
+ WIP(WI_V128_CONST, byte_buffer);
+ break;
+ }
+
+ case ONYX_INTRINSIC_F64X2_CONST: {
+ f64* byte_buffer = bh_alloc(mod->extended_instr_alloc, 16);
+ bh_arr(AstArgument *) arg_arr = (bh_arr(AstArgument *)) call->args.values;
+ fori (i, 0, 2) {
+ if (arg_arr[i]->value->kind != Ast_Kind_NumLit) {
+ onyx_report_error(arg_arr[i]->token->pos,
+ "SIMD constants expect compile time constants as parameters. The %d%s parameter was not.",
+ i, bh_num_suffix(i));
+ *pcode = code;
+ return;
+ }
+ byte_buffer[i] = (f64) ((AstNumLit *) arg_arr[i]->value)->value.d;
+ }
+ WIP(WI_V128_CONST, byte_buffer);
+ break;
+ }
+
+ case ONYX_INTRINSIC_I8X16_SHUFFLE: {
+ u8* byte_buffer = bh_alloc(mod->extended_instr_alloc, 16);
+ bh_arr(AstArgument *) arg_arr = (bh_arr(AstArgument *)) call->args.values;
+
+ // NOTE: There are two parameters that have to be outputted before
+ // the immediate bytes
+ emit_expression(mod, &code, arg_arr[0]->value);
+ emit_expression(mod, &code, arg_arr[1]->value);
+
+ fori (i, 0, 16) {
+ if (arg_arr[i + 2]->value->kind != Ast_Kind_NumLit) {
+ onyx_report_error(arg_arr[i + 2]->token->pos,
+ "SIMD constants expect compile time constants as parameters. The %d%s parameter was not.",
+ i, bh_num_suffix(i));
+ *pcode = code;
+ return;
+ }
+ byte_buffer[i] = (u8) ((AstNumLit *) arg_arr[i + 2]->value)->value.i;
+ }
+ WIP(WI_I8X16_SHUFFLE, byte_buffer);
+ break;
+ }
+
+ // CLEANUP ALL OF THIS
+ case ONYX_INTRINSIC_I8X16_EXTRACT_LANE_S: SIMD_EXTRACT_LANE_INSTR(WI_I8X16_EXTRACT_LANE_S, ((bh_arr(AstArgument *)) call->args.values)); break;
+ case ONYX_INTRINSIC_I8X16_EXTRACT_LANE_U: SIMD_EXTRACT_LANE_INSTR(WI_I8X16_EXTRACT_LANE_U, ((bh_arr(AstArgument *)) call->args.values)); break;
+ case ONYX_INTRINSIC_I8X16_REPLACE_LANE: SIMD_REPLACE_LANE_INSTR(WI_I8X16_REPLACE_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
+ case ONYX_INTRINSIC_I16X8_EXTRACT_LANE_S: SIMD_EXTRACT_LANE_INSTR(WI_I16X8_EXTRACT_LANE_S, ((bh_arr(AstArgument *)) call->args.values)); break;
+ case ONYX_INTRINSIC_I16X8_EXTRACT_LANE_U: SIMD_EXTRACT_LANE_INSTR(WI_I16X8_EXTRACT_LANE_U, ((bh_arr(AstArgument *)) call->args.values)); break;
+ case ONYX_INTRINSIC_I16X8_REPLACE_LANE: SIMD_REPLACE_LANE_INSTR(WI_I16X8_REPLACE_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
+ case ONYX_INTRINSIC_I32X4_EXTRACT_LANE: SIMD_EXTRACT_LANE_INSTR(WI_I32X4_EXTRACT_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
+ case ONYX_INTRINSIC_I32X4_REPLACE_LANE: SIMD_REPLACE_LANE_INSTR(WI_I32X4_REPLACE_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
+ case ONYX_INTRINSIC_I64X2_EXTRACT_LANE: SIMD_EXTRACT_LANE_INSTR(WI_I64X2_EXTRACT_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
+ case ONYX_INTRINSIC_I64X2_REPLACE_LANE: SIMD_REPLACE_LANE_INSTR(WI_I64X2_REPLACE_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
+ case ONYX_INTRINSIC_F32X4_EXTRACT_LANE: SIMD_EXTRACT_LANE_INSTR(WI_F32X4_EXTRACT_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
+ case ONYX_INTRINSIC_F32X4_REPLACE_LANE: SIMD_REPLACE_LANE_INSTR(WI_F32X4_REPLACE_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
+ case ONYX_INTRINSIC_F64X2_EXTRACT_LANE: SIMD_EXTRACT_LANE_INSTR(WI_F64X2_EXTRACT_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
+ case ONYX_INTRINSIC_F64X2_REPLACE_LANE: SIMD_REPLACE_LANE_INSTR(WI_F64X2_REPLACE_LANE, ((bh_arr(AstArgument *)) call->args.values)); break;
+
+ case ONYX_INTRINSIC_I8X16_SWIZZLE: WI(WI_I8X16_SWIZZLE); break;
+ case ONYX_INTRINSIC_I8X16_SPLAT: WI(WI_I8X16_SPLAT); break;
+ case ONYX_INTRINSIC_I16X8_SPLAT: WI(WI_I16X8_SPLAT); break;
+ case ONYX_INTRINSIC_I32X4_SPLAT: WI(WI_I32X4_SPLAT); break;
+ case ONYX_INTRINSIC_I64X2_SPLAT: WI(WI_I64X2_SPLAT); break;
+ case ONYX_INTRINSIC_F32X4_SPLAT: WI(WI_F32X4_SPLAT); break;
+ case ONYX_INTRINSIC_F64X2_SPLAT: WI(WI_F64X2_SPLAT); break;
+
+ case ONYX_INTRINSIC_I8X16_EQ: WI(WI_I8X16_EQ); break;
+ case ONYX_INTRINSIC_I8X16_NEQ: WI(WI_I8X16_NEQ); break;
+ case ONYX_INTRINSIC_I8X16_LT_S: WI(WI_I8X16_LT_S); break;
+ case ONYX_INTRINSIC_I8X16_LT_U: WI(WI_I8X16_LT_U); break;
+ case ONYX_INTRINSIC_I8X16_GT_S: WI(WI_I8X16_GT_S); break;
+ case ONYX_INTRINSIC_I8X16_GT_U: WI(WI_I8X16_GT_U); break;
+ case ONYX_INTRINSIC_I8X16_LE_S: WI(WI_I8X16_LE_S); break;
+ case ONYX_INTRINSIC_I8X16_LE_U: WI(WI_I8X16_LE_U); break;
+ case ONYX_INTRINSIC_I8X16_GE_S: WI(WI_I8X16_GE_S); break;
+ case ONYX_INTRINSIC_I8X16_GE_U: WI(WI_I8X16_GE_U); break;
+
+ case ONYX_INTRINSIC_I16X8_EQ: WI(WI_I16X8_EQ); break;
+ case ONYX_INTRINSIC_I16X8_NEQ: WI(WI_I16X8_NEQ); break;
+ case ONYX_INTRINSIC_I16X8_LT_S: WI(WI_I16X8_LT_S); break;
+ case ONYX_INTRINSIC_I16X8_LT_U: WI(WI_I16X8_LT_U); break;
+ case ONYX_INTRINSIC_I16X8_GT_S: WI(WI_I16X8_GT_S); break;
+ case ONYX_INTRINSIC_I16X8_GT_U: WI(WI_I16X8_GT_U); break;
+ case ONYX_INTRINSIC_I16X8_LE_S: WI(WI_I16X8_LE_S); break;
+ case ONYX_INTRINSIC_I16X8_LE_U: WI(WI_I16X8_LE_U); break;
+ case ONYX_INTRINSIC_I16X8_GE_S: WI(WI_I16X8_GE_S); break;
+ case ONYX_INTRINSIC_I16X8_GE_U: WI(WI_I16X8_GE_U); break;
+
+ case ONYX_INTRINSIC_I32X4_EQ: WI(WI_I32X4_EQ); break;
+ case ONYX_INTRINSIC_I32X4_NEQ: WI(WI_I32X4_NEQ); break;
+ case ONYX_INTRINSIC_I32X4_LT_S: WI(WI_I32X4_LT_S); break;
+ case ONYX_INTRINSIC_I32X4_LT_U: WI(WI_I32X4_LT_U); break;
+ case ONYX_INTRINSIC_I32X4_GT_S: WI(WI_I32X4_GT_S); break;
+ case ONYX_INTRINSIC_I32X4_GT_U: WI(WI_I32X4_GT_U); break;
+ case ONYX_INTRINSIC_I32X4_LE_S: WI(WI_I32X4_LE_S); break;
+ case ONYX_INTRINSIC_I32X4_LE_U: WI(WI_I32X4_LE_U); break;
+ case ONYX_INTRINSIC_I32X4_GE_S: WI(WI_I32X4_GE_S); break;
+ case ONYX_INTRINSIC_I32X4_GE_U: WI(WI_I32X4_GE_U); break;
+
+ case ONYX_INTRINSIC_F32X4_EQ: WI(WI_F32X4_EQ); break;
+ case ONYX_INTRINSIC_F32X4_NEQ: WI(WI_F32X4_NEQ); break;
+ case ONYX_INTRINSIC_F32X4_LT: WI(WI_F32X4_LT); break;
+ case ONYX_INTRINSIC_F32X4_GT: WI(WI_F32X4_GT); break;
+ case ONYX_INTRINSIC_F32X4_LE: WI(WI_F32X4_LE); break;
+ case ONYX_INTRINSIC_F32X4_GE: WI(WI_F32X4_GE); break;
+
+ case ONYX_INTRINSIC_F64X2_EQ: WI(WI_F64X2_EQ); break;
+ case ONYX_INTRINSIC_F64X2_NEQ: WI(WI_F64X2_NEQ); break;
+ case ONYX_INTRINSIC_F64X2_LT: WI(WI_F64X2_LT); break;
+ case ONYX_INTRINSIC_F64X2_GT: WI(WI_F64X2_GT); break;
+ case ONYX_INTRINSIC_F64X2_LE: WI(WI_F64X2_LE); break;
+ case ONYX_INTRINSIC_F64X2_GE: WI(WI_F64X2_GE); break;
+
+ case ONYX_INTRINSIC_V128_NOT: WI(WI_V128_NOT); break;
+ case ONYX_INTRINSIC_V128_AND: WI(WI_V128_AND); break;
+ case ONYX_INTRINSIC_V128_ANDNOT: WI(WI_V128_ANDNOT); break;
+ case ONYX_INTRINSIC_V128_OR: WI(WI_V128_OR); break;
+ case ONYX_INTRINSIC_V128_XOR: WI(WI_V128_XOR); break;
+ case ONYX_INTRINSIC_V128_BITSELECT: WI(WI_V128_BITSELECT); break;
+
+ case ONYX_INTRINSIC_I8X16_ABS: WI(WI_I8X16_ABS); break;
+ case ONYX_INTRINSIC_I8X16_NEG: WI(WI_I8X16_NEG); break;
+ case ONYX_INTRINSIC_I8X16_ANY_TRUE: WI(WI_I8X16_ANY_TRUE); break;
+ case ONYX_INTRINSIC_I8X16_ALL_TRUE: WI(WI_I8X16_ALL_TRUE); break;
+ case ONYX_INTRINSIC_I8X16_BITMASK: WI(WI_I8X16_BITMASK); break;
+ case ONYX_INTRINSIC_I8X16_NARROW_I16X8_S: WI(WI_I8X16_NARROW_I16X8_S); break;
+ case ONYX_INTRINSIC_I8X16_NARROW_I16X8_U: WI(WI_I8X16_NARROW_I16X8_U); break;
+ case ONYX_INTRINSIC_I8X16_SHL: WI(WI_I8X16_SHL); break;
+ case ONYX_INTRINSIC_I8X16_SHR_S: WI(WI_I8X16_SHR_S); break;
+ case ONYX_INTRINSIC_I8X16_SHR_U: WI(WI_I8X16_SHR_U); break;
+ case ONYX_INTRINSIC_I8X16_ADD: WI(WI_I8X16_ADD); break;
+ case ONYX_INTRINSIC_I8X16_ADD_SAT_S: WI(WI_I8X16_ADD_SAT_S); break;
+ case ONYX_INTRINSIC_I8X16_ADD_SAT_U: WI(WI_I8X16_ADD_SAT_U); break;
+ case ONYX_INTRINSIC_I8X16_SUB: WI(WI_I8X16_SUB); break;
+ case ONYX_INTRINSIC_I8X16_SUB_SAT_S: WI(WI_I8X16_SUB_SAT_S); break;
+ case ONYX_INTRINSIC_I8X16_SUB_SAT_U: WI(WI_I8X16_SUB_SAT_U); break;
+ case ONYX_INTRINSIC_I8X16_MIN_S: WI(WI_I8X16_MIN_S); break;
+ case ONYX_INTRINSIC_I8X16_MIN_U: WI(WI_I8X16_MIN_U); break;
+ case ONYX_INTRINSIC_I8X16_MAX_S: WI(WI_I8X16_MAX_S); break;
+ case ONYX_INTRINSIC_I8X16_MAX_U: WI(WI_I8X16_MAX_U); break;
+ case ONYX_INTRINSIC_I8X16_AVGR_U: WI(WI_I8X16_AVGR_U); break;
+
+ case ONYX_INTRINSIC_I16X8_ABS: WI(WI_I16X8_ABS); break;
+ case ONYX_INTRINSIC_I16X8_NEG: WI(WI_I16X8_NEG); break;
+ case ONYX_INTRINSIC_I16X8_ANY_TRUE: WI(WI_I16X8_ANY_TRUE); break;
+ case ONYX_INTRINSIC_I16X8_ALL_TRUE: WI(WI_I16X8_ALL_TRUE); break;
+ case ONYX_INTRINSIC_I16X8_BITMASK: WI(WI_I16X8_BITMASK); break;
+ case ONYX_INTRINSIC_I16X8_NARROW_I32X4_S: WI(WI_I16X8_NARROW_I32X4_S); break;
+ case ONYX_INTRINSIC_I16X8_NARROW_I32X4_U: WI(WI_I16X8_NARROW_I32X4_U); break;
+ case ONYX_INTRINSIC_I16X8_WIDEN_LOW_I8X16_S: WI(WI_I16X8_WIDEN_LOW_I8X16_S); break;
+ case ONYX_INTRINSIC_I16X8_WIDEN_HIGH_I8X16_S: WI(WI_I16X8_WIDEN_HIGH_I8X16_S); break;
+ case ONYX_INTRINSIC_I16X8_WIDEN_LOW_I8X16_U: WI(WI_I16X8_WIDEN_LOW_I8X16_U); break;
+ case ONYX_INTRINSIC_I16X8_WIDEN_HIGH_I8X16_U: WI(WI_I16X8_WIDEN_HIGH_I8X16_U); break;
+ case ONYX_INTRINSIC_I16X8_SHL: WI(WI_I16X8_SHL); break;
+ case ONYX_INTRINSIC_I16X8_SHR_S: WI(WI_I16X8_SHR_S); break;
+ case ONYX_INTRINSIC_I16X8_SHR_U: WI(WI_I16X8_SHR_U); break;
+ case ONYX_INTRINSIC_I16X8_ADD: WI(WI_I16X8_ADD); break;
+ case ONYX_INTRINSIC_I16X8_ADD_SAT_S: WI(WI_I16X8_ADD_SAT_S); break;
+ case ONYX_INTRINSIC_I16X8_ADD_SAT_U: WI(WI_I16X8_ADD_SAT_U); break;
+ case ONYX_INTRINSIC_I16X8_SUB: WI(WI_I16X8_SUB); break;
+ case ONYX_INTRINSIC_I16X8_SUB_SAT_S: WI(WI_I16X8_SUB_SAT_S); break;
+ case ONYX_INTRINSIC_I16X8_SUB_SAT_U: WI(WI_I16X8_SUB_SAT_U); break;
+ case ONYX_INTRINSIC_I16X8_MUL: WI(WI_I16X8_MUL); break;
+ case ONYX_INTRINSIC_I16X8_MIN_S: WI(WI_I16X8_MIN_S); break;
+ case ONYX_INTRINSIC_I16X8_MIN_U: WI(WI_I16X8_MIN_U); break;
+ case ONYX_INTRINSIC_I16X8_MAX_S: WI(WI_I16X8_MAX_S); break;
+ case ONYX_INTRINSIC_I16X8_MAX_U: WI(WI_I16X8_MAX_U); break;
+ case ONYX_INTRINSIC_I16X8_AVGR_U: WI(WI_I16X8_AVGR_U); break;
+
+ case ONYX_INTRINSIC_I32X4_ABS: WI(WI_I32X4_ABS); break;
+ case ONYX_INTRINSIC_I32X4_NEG: WI(WI_I32X4_NEG); break;
+ case ONYX_INTRINSIC_I32X4_ANY_TRUE: WI(WI_I32X4_ANY_TRUE); break;
+ case ONYX_INTRINSIC_I32X4_ALL_TRUE: WI(WI_I32X4_ALL_TRUE); break;
+ case ONYX_INTRINSIC_I32X4_BITMASK: WI(WI_I32X4_BITMASK); break;
+ case ONYX_INTRINSIC_I32X4_WIDEN_LOW_I16X8_S: WI(WI_I32X4_WIDEN_LOW_I16X8_S); break;
+ case ONYX_INTRINSIC_I32X4_WIDEN_HIGH_I16X8_S: WI(WI_I32X4_WIDEN_HIGH_I16X8_S); break;
+ case ONYX_INTRINSIC_I32X4_WIDEN_LOW_I16X8_U: WI(WI_I32X4_WIDEN_LOW_I16X8_U); break;
+ case ONYX_INTRINSIC_I32X4_WIDEN_HIGH_I16X8_U: WI(WI_I32X4_WIDEN_HIGH_I16X8_U); break;
+ case ONYX_INTRINSIC_I32X4_SHL: WI(WI_I32X4_SHL); break;
+ case ONYX_INTRINSIC_I32X4_SHR_S: WI(WI_I32X4_SHR_S); break;
+ case ONYX_INTRINSIC_I32X4_SHR_U: WI(WI_I32X4_SHR_U); break;
+ case ONYX_INTRINSIC_I32X4_ADD: WI(WI_I32X4_ADD); break;
+ case ONYX_INTRINSIC_I32X4_SUB: WI(WI_I32X4_SUB); break;
+ case ONYX_INTRINSIC_I32X4_MUL: WI(WI_I32X4_MUL); break;
+ case ONYX_INTRINSIC_I32X4_MIN_S: WI(WI_I32X4_MIN_S); break;
+ case ONYX_INTRINSIC_I32X4_MIN_U: WI(WI_I32X4_MIN_U); break;
+ case ONYX_INTRINSIC_I32X4_MAX_S: WI(WI_I32X4_MAX_S); break;
+ case ONYX_INTRINSIC_I32X4_MAX_U: WI(WI_I32X4_MAX_U); break;
+
+ case ONYX_INTRINSIC_I64X2_NEG: WI(WI_I64X2_NEG); break;
+ case ONYX_INTRINSIC_I64X2_SHL: WI(WI_I64X2_SHL); break;
+ case ONYX_INTRINSIC_I64X2_SHR_S: WI(WI_I64X2_SHR_S); break;
+ case ONYX_INTRINSIC_I64X2_SHR_U: WI(WI_I64X2_SHR_U); break;
+ case ONYX_INTRINSIC_I64X2_ADD: WI(WI_I64X2_ADD); break;
+ case ONYX_INTRINSIC_I64X2_SUB: WI(WI_I64X2_SUB); break;
+ case ONYX_INTRINSIC_I64X2_MUL: WI(WI_I64X2_MUL); break;
+
+ case ONYX_INTRINSIC_F32X4_ABS: WI(WI_F32X4_ABS); break;
+ case ONYX_INTRINSIC_F32X4_NEG: WI(WI_F32X4_NEG); break;
+ case ONYX_INTRINSIC_F32X4_SQRT: WI(WI_F32X4_SQRT); break;
+ case ONYX_INTRINSIC_F32X4_ADD: WI(WI_F32X4_ADD); break;
+ case ONYX_INTRINSIC_F32X4_SUB: WI(WI_F32X4_SUB); break;
+ case ONYX_INTRINSIC_F32X4_MUL: WI(WI_F32X4_MUL); break;
+ case ONYX_INTRINSIC_F32X4_DIV: WI(WI_F32X4_DIV); break;
+ case ONYX_INTRINSIC_F32X4_MIN: WI(WI_F32X4_MIN); break;
+ case ONYX_INTRINSIC_F32X4_MAX: WI(WI_F32X4_MAX); break;
+
+ case ONYX_INTRINSIC_F64X2_ABS: WI(WI_F64X2_ABS); break;
+ case ONYX_INTRINSIC_F64X2_NEG: WI(WI_F64X2_NEG); break;
+ case ONYX_INTRINSIC_F64X2_SQRT: WI(WI_F64X2_SQRT); break;
+ case ONYX_INTRINSIC_F64X2_ADD: WI(WI_F64X2_ADD); break;
+ case ONYX_INTRINSIC_F64X2_SUB: WI(WI_F64X2_SUB); break;
+ case ONYX_INTRINSIC_F64X2_MUL: WI(WI_F64X2_MUL); break;
+ case ONYX_INTRINSIC_F64X2_DIV: WI(WI_F64X2_DIV); break;
+ case ONYX_INTRINSIC_F64X2_MIN: WI(WI_F64X2_MIN); break;
+ case ONYX_INTRINSIC_F64X2_MAX: WI(WI_F64X2_MAX); break;
+
+ case ONYX_INTRINSIC_I32X4_TRUNC_SAT_F32X4_S: WI(WI_I32X4_TRUNC_SAT_F32X4_S); break;
+ case ONYX_INTRINSIC_I32X4_TRUNC_SAT_F32X4_U: WI(WI_I32X4_TRUNC_SAT_F32X4_U); break;
+ case ONYX_INTRINSIC_F32X4_CONVERT_I32X4_S: WI(WI_F32X4_CONVERT_I32X4_S); break;
+ case ONYX_INTRINSIC_F32X4_CONVERT_I32X4_U: WI(WI_F32X4_CONVERT_I32X4_U); break;
+
+ default: assert(("Unsupported intrinsic", 0));
+ }
+
+ *pcode = code;
+}
+
+EMIT_FUNC(subscript_location, AstSubscript* sub, u64* offset_return) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ emit_expression(mod, &code, sub->expr);
+ if (sub->elem_size != 1) {
+ WID(WI_PTR_CONST, sub->elem_size);
+ WI(WI_PTR_MUL);
+ }
+
+ // CLEANUP: This is one dense clusterf**k of code...
+ u64 offset = 0;
+ if (sub->addr->kind == Ast_Kind_Subscript
+ && sub->addr->type->kind == Type_Kind_Array) {
+ emit_subscript_location(mod, &code, (AstSubscript *) sub->addr, &offset);
+ } else if (sub->addr->kind == Ast_Kind_Field_Access
+ && sub->addr->type->kind == Type_Kind_Array) {
+ emit_field_access_location(mod, &code, (AstFieldAccess *) sub->addr, &offset);
+ } else if ((sub->addr->kind == Ast_Kind_Local || sub->addr->kind == Ast_Kind_Param)
+ && sub->addr->type->kind == Type_Kind_Array) {
+ emit_local_location(mod, &code, (AstLocal *) sub->addr, &offset);
+ } else if (sub->addr->kind == Ast_Kind_Memres
+ && sub->addr->type->kind != Type_Kind_Array) {
+ emit_memory_reservation_location(mod, &code, (AstMemRes *) sub->addr);
+ } else {
+ emit_expression(mod, &code, sub->addr);
+ }
+
+ WI(WI_PTR_ADD);
+
+ *offset_return += offset;
+
+ *pcode = code;
+}
+
+EMIT_FUNC(field_access_location, AstFieldAccess* field, u64* offset_return) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ u64 offset = field->offset;
+ AstTyped* source_expr = field->expr;
+ while (source_expr->kind == Ast_Kind_Field_Access
+ && type_is_structlike_strict(source_expr->type)) {
+ offset += ((AstFieldAccess *) source_expr)->offset;
+ source_expr = (AstTyped *) ((AstFieldAccess *) source_expr)->expr;
+ }
+
+ if (source_expr->kind == Ast_Kind_Subscript
+ && source_expr->type->kind != Type_Kind_Pointer) {
+ u64 o2 = 0;
+ emit_subscript_location(mod, &code, (AstSubscript *) source_expr, &o2);
+ offset += o2;
+
+ } else if ((source_expr->kind == Ast_Kind_Local || source_expr->kind == Ast_Kind_Param)
+ && source_expr->type->kind != Type_Kind_Pointer) {
+ u64 o2 = 0;
+ emit_local_location(mod, &code, (AstLocal *) source_expr, &o2);
+ offset += o2;
+
+ } else if (source_expr->kind == Ast_Kind_Memres
+ && source_expr->type->kind != Type_Kind_Pointer) {
+ emit_memory_reservation_location(mod, &code, (AstMemRes *) source_expr);
+
+ } else {
+ emit_expression(mod, &code, source_expr);
+ }
+
+ *offset_return = offset;
+
+ *pcode = code;
+}
+
+EMIT_FUNC(memory_reservation_location, AstMemRes* memres) {
+ bh_arr(WasmInstruction) code = *pcode;
+ WID(WI_PTR_CONST, memres->addr);
+ *pcode = code;
+}
+
+EMIT_FUNC(local_location, AstLocal* local, u64* offset_return) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ u64 local_offset = (u64) bh_imap_get(&mod->local_map, (u64) local);
+
+ if (local_offset & LOCAL_IS_WASM) {
+ // This is a weird condition but it is relied on in a couple places including
+ // passing non-simple structs by value. -brendanfh 2020/09/18
+ WIL(WI_LOCAL_GET, local_offset);
+
+ } else {
+ WIL(WI_LOCAL_GET, mod->stack_base_idx);
+
+ *offset_return += local_offset;
+ }
+
+ *pcode = code;
+}
+
+EMIT_FUNC(struct_lval, AstTyped* lval) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ assert(type_is_structlike_strict(lval->type));
+
+ u64 offset = 0;
+ emit_location_return_offset(mod, &code, lval, &offset);
+ emit_compound_store(mod, &code, lval->type, offset, 1);
+
+ *pcode = code;
+}
+
+EMIT_FUNC(compound_load, Type* type, u64 offset) {
+ bh_arr(WasmInstruction) code = *pcode;
+ i32 mem_count = type_linear_member_count(type);
+ TypeWithOffset two;
+
+ if (mem_count == 1) {
+ type_linear_member_lookup(type, 0, &two);
+ emit_load_instruction(mod, &code, two.type, offset + two.offset); // two.offset should be 0
+ } else {
+ u64 tmp_idx = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+ WIL(WI_LOCAL_TEE, tmp_idx);
+
+ fori (i, 0, mem_count) {
+ type_linear_member_lookup(type, i, &two);
+ if (i != 0) WIL(WI_LOCAL_GET, tmp_idx);
+ emit_load_instruction(mod, &code, two.type, offset + two.offset);
+ }
+
+ local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+ }
+
+ *pcode = code;
+ return;
+}
+
+EMIT_FUNC(compound_store, Type* type, u64 offset, b32 location_first) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ TypeWithOffset two;
+
+ u64 loc_idx = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+ if (location_first) WIL(WI_LOCAL_SET, loc_idx);
+
+ i32 elem_count = type_linear_member_count(type);
+ u64 *temp_locals = bh_alloc_array(global_scratch_allocator, u64, elem_count);
+
+ forir (i, elem_count - 1, 0) {
+ type_linear_member_lookup(type, i, &two);
+
+ WasmType wt = onyx_type_to_wasm_type(two.type);
+ temp_locals[i] = local_raw_allocate(mod->local_alloc, wt);
+ WIL(WI_LOCAL_SET, temp_locals[i]);
+ }
+
+ if (!location_first) WIL(WI_LOCAL_SET, loc_idx);
+
+ fori (i, 0, elem_count) {
+ type_linear_member_lookup(type, i, &two);
+
+ u64 tmp_idx = temp_locals[i];
+ WIL(WI_LOCAL_GET, loc_idx);
+ WIL(WI_LOCAL_GET, tmp_idx);
+ emit_store_instruction(mod, &code, two.type, offset + two.offset);
+
+ WasmType wt = onyx_type_to_wasm_type(two.type);
+ local_raw_free(mod->local_alloc, wt);
+ }
+
+ local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+
+ // This shouldn't be necessary because the scratch allocator doesn't free.
+ bh_free(global_scratch_allocator, temp_locals);
+
+ *pcode = code;
+}
+
+EMIT_FUNC(struct_literal, AstStructLiteral* sl) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ bh_arr_each(AstTyped *, val, sl->args.values) {
+ emit_expression(mod, &code, *val);
+ }
+
+ *pcode = code;
+}
+
+EMIT_FUNC(array_store, Type* type, u32 offset) {
+ assert(type->kind == Type_Kind_Array);
+ bh_arr(WasmInstruction) code = *pcode;
+
+ Type* elem_type = type;
+ u32 elem_count = 1;
+ while (elem_type->kind == Type_Kind_Array) {
+ elem_count *= elem_type->Array.count;
+ elem_type = elem_type->Array.elem;
+ }
+ u32 elem_size = type_size_of(elem_type);
+
+ u64 lptr_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+ u64 rptr_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+ WIL(WI_LOCAL_SET, rptr_local);
+ WIL(WI_LOCAL_SET, lptr_local);
+
+ if (elem_count <= 2) {
+ // Inline copying for a small number of elements. It still may be faster to do this in a tight loop.
+
+ fori (i, 0, elem_count) {
+ if (bh_arr_last(code).type == WI_LOCAL_SET && (u64) bh_arr_last(code).data.l == lptr_local)
+ bh_arr_last(code).type = WI_LOCAL_TEE;
+ else
+ WIL(WI_LOCAL_GET, lptr_local);
+
+ WIL(WI_LOCAL_GET, rptr_local);
+ emit_load_instruction(mod, &code, elem_type, i * elem_size);
+
+ emit_store_instruction(mod, &code, elem_type, i * elem_size + offset);
+ }
+
+ } else if (context.options->use_post_mvp_features) {
+ // Use a simple memory copy if it is available. This may be what happens in the case below too at a later time.
+
+ if (bh_arr_last(code).type == WI_LOCAL_SET && (u64) bh_arr_last(code).data.l == lptr_local)
+ bh_arr_last(code).type = WI_LOCAL_TEE;
+ else
+ WIL(WI_LOCAL_GET, lptr_local);
+ WIL(WI_PTR_CONST, offset);
+ WI(WI_PTR_ADD);
+
+ WIL(WI_LOCAL_GET, rptr_local);
+ WIL(WI_I32_CONST, elem_count * elem_size);
+ WI(WI_MEMORY_COPY);
+
+ } else {
+ // Emit a loop that copies the memory. This could be switched to a tight loop that just copies word per word.
+
+ u64 offset_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+ WIL(WI_PTR_CONST, 0);
+ WIL(WI_LOCAL_SET, offset_local);
+
+ WID(WI_BLOCK_START, 0x40);
+ WID(WI_LOOP_START, 0x40);
+ WIL(WI_LOCAL_GET, offset_local);
+ WIL(WI_LOCAL_GET, lptr_local);
+ WI(WI_PTR_ADD);
+
+ WIL(WI_LOCAL_GET, offset_local);
+ WIL(WI_LOCAL_GET, rptr_local);
+ WI(WI_PTR_ADD);
+
+ emit_load_instruction(mod, &code, elem_type, 0);
+ emit_store_instruction(mod, &code, elem_type, offset);
+
+ WIL(WI_LOCAL_GET, offset_local);
+ WIL(WI_PTR_CONST, elem_size);
+ WI(WI_PTR_ADD);
+ WIL(WI_LOCAL_TEE, offset_local);
+
+ WIL(WI_PTR_CONST, elem_count * elem_size);
+ WI(WI_PTR_GE);
+ WID(WI_COND_JUMP, 0x01);
+
+ WID(WI_JUMP, 0x00);
+
+ WI(WI_LOOP_END);
+ WI(WI_BLOCK_END);
+
+ local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+ }
+
+ local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+ local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+
+ *pcode = code;
+ return;
+}
+
+EMIT_FUNC(array_literal, AstArrayLiteral* al) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ u64 local_offset = (u64) bh_imap_get(&mod->local_map, (u64) al);
+ assert((local_offset & LOCAL_IS_WASM) == 0);
+
+ assert(al->type->kind == Type_Kind_Array);
+
+ u32 elem_size = type_size_of(al->type->Array.elem);
+
+ fori (i, 0, al->type->Array.count) {
+ WIL(WI_LOCAL_GET, mod->stack_base_idx);
+ emit_expression(mod, &code, al->values[i]);
+ emit_store_instruction(mod, &code, al->type->Array.elem, local_offset + i * elem_size);
+ }
+
+ WIL(WI_LOCAL_GET, mod->stack_base_idx);
+ WIL(WI_PTR_CONST, local_offset);
+ WI(WI_PTR_ADD);
+
+ *pcode = code;
+}
+
+EMIT_FUNC(range_literal, AstRangeLiteral* range) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ emit_expression(mod, &code, range->low);
+ emit_expression(mod, &code, range->high);
+ emit_expression(mod, &code, range->step);
+
+ *pcode = code;
+}
+
+EMIT_FUNC(if_expression, AstIfExpression* if_expr) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ u64 offset = 0;
+ u64 result_local = local_allocate(mod->local_alloc, (AstTyped *) if_expr);
+ b32 result_is_local = (b32) ((result_local & LOCAL_IS_WASM) != 0);
+ bh_imap_put(&mod->local_map, (u64) if_expr, result_local);
+
+ emit_expression(mod, &code, if_expr->cond);
+
+ emit_enter_structured_block(mod, &code, SBT_Basic_If);
+ if (!result_is_local) emit_local_location(mod, &code, (AstLocal *) if_expr, &offset);
+
+ emit_expression(mod, &code, if_expr->true_expr);
+
+ if (!result_is_local) emit_store_instruction(mod, &code, if_expr->type, offset);
+ else WIL(WI_LOCAL_SET, result_local);
+
+ offset = 0;
+ WI(WI_ELSE);
+ if (!result_is_local) emit_local_location(mod, &code, (AstLocal *) if_expr, &offset);
+
+ emit_expression(mod, &code, if_expr->false_expr);
+
+ if (!result_is_local) emit_store_instruction(mod, &code, if_expr->type, offset);
+ else WIL(WI_LOCAL_SET, result_local);
+
+ emit_leave_structured_block(mod, &code);
+
+ offset = 0;
+ if (!result_is_local) {
+ emit_local_location(mod, &code, (AstLocal *) if_expr, &offset);
+ emit_load_instruction(mod, &code, if_expr->type, offset);
+
+ } else {
+ WIL(WI_LOCAL_GET, result_local);
+ }
+
+ local_free(mod->local_alloc, (AstTyped *) if_expr);
+
+ *pcode = code;
+}
+
+EMIT_FUNC(do_block, AstDoBlock* doblock) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ u64 result_local = local_allocate(mod->local_alloc, (AstTyped *) doblock);
+ b32 result_is_local = (b32) ((result_local & LOCAL_IS_WASM) != 0);
+
+ bh_imap_put(&mod->local_map, (u64) doblock, result_local);
+ bh_arr_push(mod->return_location_stack, (AstLocal *) doblock);
+
+ emit_block(mod, &code, doblock->block, 1);
+
+ u64 offset = 0;
+ if (!result_is_local) {
+ emit_local_location(mod, &code, (AstLocal *) doblock, &offset);
+ emit_load_instruction(mod, &code, doblock->type, offset);
+
+ } else {
+ WIL(WI_LOCAL_GET, result_local);
+ }
+
+ bh_arr_pop(mod->return_location_stack);
+ local_free(mod->local_alloc, (AstTyped *) doblock);
+
+ *pcode = code;
+}
+
+EMIT_FUNC(location_return_offset, AstTyped* expr, u64* offset_return) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ switch (expr->kind) {
+ case Ast_Kind_Param:
+ case Ast_Kind_Local: {
+ emit_local_location(mod, &code, (AstLocal *) expr, offset_return);
+ break;
+ }
+
+ case Ast_Kind_Dereference: {
+ emit_expression(mod, &code, ((AstDereference *) expr)->expr);
+ *offset_return = 0;
+ break;
+ }
+
+ case Ast_Kind_Subscript: {
+ AstSubscript* sub = (AstSubscript *) expr;
+ emit_subscript_location(mod, &code, sub, offset_return);
+ break;
+ }
+
+ case Ast_Kind_Field_Access: {
+ AstFieldAccess* field = (AstFieldAccess *) expr;
+ emit_field_access_location(mod, &code, field, offset_return);
+ break;
+ }
+
+ case Ast_Kind_Memres: {
+ AstMemRes* memres = (AstMemRes *) expr;
+ WID(WI_PTR_CONST, memres->addr);
+ *offset_return = 0;
+ break;
+ }
+
+ default: {
+ onyx_report_error(expr->token->pos, "Unable to generate locate for '%s'.", onyx_ast_node_kind_string(expr->kind));
+ break;
+ }
+ }
+
+ *pcode = code;
+}
+
+EMIT_FUNC(location, AstTyped* expr) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ u64 offset = 0;
+ emit_location_return_offset(mod, &code, expr, &offset);
+ if (offset != 0) {
+ WID(WI_PTR_CONST, offset);
+ WI(WI_PTR_ADD);
+ }
+
+ *pcode = code;
+}
+
+EMIT_FUNC(expression, AstTyped* expr) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ if (node_is_type((AstNode *) expr)) {
+ AstType* type = (AstType *) expr;
+ if (type->type_id != 0) {
+ WID(WI_I32_CONST, ((AstType *) expr)->type_id);
+ } else {
+ Type* t = type_build_from_ast(context.ast_alloc, type);
+ WID(WI_I32_CONST, t->id);
+ }
+
+ *pcode = code;
+ return;
+ }
+
+ switch (expr->kind) {
+ case Ast_Kind_Param: {
+ AstLocal* param = (AstLocal *) expr;
+ u64 localidx = bh_imap_get(&mod->local_map, (u64) param);
+
+ switch (type_get_param_pass(param->type)) {
+ case Param_Pass_By_Value: {
+ if (type_is_structlike_strict(expr->type)) {
+ u32 mem_count = type_structlike_mem_count(expr->type);
+ fori (idx, 0, mem_count) WIL(WI_LOCAL_GET, localidx + idx);
+
+ } else {
+ WIL(WI_LOCAL_GET, localidx);
+ }
+ break;
+ }
+
+ case Param_Pass_By_Implicit_Pointer: {
+ WIL(WI_LOCAL_GET, localidx);
+ emit_load_instruction(mod, &code, expr->type, 0);
+ break;
+ }
+
+ default: assert(0);
+ }
+
+ break;
+ }
+
+ case Ast_Kind_Local: {
+ u64 tmp = bh_imap_get(&mod->local_map, (u64) expr);
+
+ if (tmp & LOCAL_IS_WASM) {
+ if (bh_arr_last(code).type == WI_LOCAL_SET && (u64) bh_arr_last(code).data.l == tmp) {
+ bh_arr_last(code).type = WI_LOCAL_TEE;
+ } else {
+ WIL(WI_LOCAL_GET, tmp);
+ }
+
+ } else {
+ u64 offset = 0;
+ emit_local_location(mod, &code, (AstLocal *) expr, &offset);
+
+ if (expr->type->kind != Type_Kind_Array) {
+ emit_load_instruction(mod, &code, expr->type, offset);
+ } else if (offset != 0) {
+ WID(WI_PTR_CONST, offset);
+ WI(WI_PTR_ADD);
+ }
+ }
+
+ break;
+ }
+
+ case Ast_Kind_Global: {
+ i32 globalidx = (i32) bh_imap_get(&mod->index_map, (u64) expr);
+
+ WID(WI_GLOBAL_GET, globalidx);
+ break;
+ }
+
+ case Ast_Kind_NumLit: {
+ AstNumLit* lit = (AstNumLit *) expr;
+ WasmType lit_type = onyx_type_to_wasm_type(lit->type);
+ WasmInstruction instr = { WI_NOP, 0 };
+
+ if (lit_type == WASM_TYPE_INT32) {
+ instr.type = WI_I32_CONST;
+ instr.data.i1 = lit->value.i;
+ } else if (lit_type == WASM_TYPE_INT64) {
+ instr.type = WI_I64_CONST;
+ instr.data.l = lit->value.l;
+ } else if (lit_type == WASM_TYPE_FLOAT32) {
+ instr.type = WI_F32_CONST;
+ instr.data.f = lit->value.f;
+ } else if (lit_type == WASM_TYPE_FLOAT64) {
+ instr.type = WI_F64_CONST;
+ instr.data.d = lit->value.d;
+ }
+
+ bh_arr_push(code, instr);
+ break;
+ }
+
+ case Ast_Kind_StrLit: {
+ WID(WI_PTR_CONST, ((AstStrLit *) expr)->addr);
+ WID(WI_I32_CONST, ((AstStrLit *) expr)->length);
+ break;
+ }
+
+ case Ast_Kind_Struct_Literal: {
+ emit_struct_literal(mod, &code, (AstStructLiteral *) expr);
+ break;
+ }
+
+ case Ast_Kind_Array_Literal: {
+ emit_local_allocation(mod, &code, expr);
+ emit_array_literal(mod, &code, (AstArrayLiteral *) expr);
+ break;
+ }
+
+ case Ast_Kind_Range_Literal: {
+ emit_range_literal(mod, &code, (AstRangeLiteral *) expr);
+ break;
+ }
+
+ case Ast_Kind_Function: {
+ i32 elemidx = get_element_idx(mod, (AstFunction *) expr);
+
+ WID(WI_I32_CONST, elemidx);
+ break;
+ }
+
+ case Ast_Kind_Block: emit_block(mod, &code, (AstBlock *) expr, 1); break;
+ case Ast_Kind_Do_Block: emit_do_block(mod, &code, (AstDoBlock *) expr); break;
+ case Ast_Kind_Call: emit_call(mod, &code, (AstCall *) expr); break;
+ case Ast_Kind_Argument: emit_expression(mod, &code, ((AstArgument *) expr)->value); break;
+ case Ast_Kind_Intrinsic_Call: emit_intrinsic_call(mod, &code, (AstCall *) expr); break;
+ case Ast_Kind_Binary_Op: emit_binop(mod, &code, (AstBinaryOp *) expr); break;
+ case Ast_Kind_Unary_Op: emit_unaryop(mod, &code, (AstUnaryOp *) expr); break;
+ case Ast_Kind_Alias: emit_expression(mod, &code, ((AstAlias *) expr)->alias); break;
+
+ case Ast_Kind_Address_Of: {
+ AstAddressOf* aof = (AstAddressOf *) expr;
+ emit_location(mod, &code, aof->expr);
+ break;
+ }
+
+ case Ast_Kind_Dereference: {
+ AstDereference* deref = (AstDereference *) expr;
+ emit_expression(mod, &code, deref->expr);
+ emit_load_instruction(mod, &code, deref->type, 0);
+ break;
+ }
+
+ case Ast_Kind_Subscript: {
+ AstSubscript* sub = (AstSubscript *) expr;
+ u64 offset = 0;
+ emit_subscript_location(mod, &code, sub, &offset);
+ emit_load_instruction(mod, &code, sub->type, offset);
+ break;
+ }
+
+ case Ast_Kind_Field_Access: {
+ AstFieldAccess* field = (AstFieldAccess* ) expr;
+
+ if (field->expr->kind == Ast_Kind_Param) {
+ if (type_get_param_pass(field->expr->type) == Param_Pass_By_Value && !type_is_pointer(field->expr->type)) {
+ u64 localidx = bh_imap_get(&mod->local_map, (u64) field->expr) + field->idx;
+ WIL(WI_LOCAL_GET, localidx);
+ break;
+ }
+ }
+
+ if (is_lval((AstNode *) field->expr) || type_is_pointer(field->expr->type)) {
+ u64 offset = 0;
+ emit_field_access_location(mod, &code, field, &offset);
+ emit_load_instruction(mod, &code, field->type, offset);
+
+ } else {
+ emit_expression(mod, &code, field->expr);
+
+ i32 idx = type_get_idx_of_linear_member_with_offset(field->expr->type, field->offset);
+ i32 field_linear_members = type_linear_member_count(field->type);
+ i32 total_linear_members = type_linear_member_count(field->expr->type);
+
+ if (idx == 0) {
+ // Easy case: the member is the first one and all other members just have to be dropped.
+ fori (i, 0, total_linear_members - field_linear_members) WI(WI_DROP);
+
+ } else {
+ // Tough case: Stack shuffling to make the member the only thing on the stack.
+ // This is very similar to the compound_load/compound_store procedures but it is different enough
+ // that I cannot find a good way to factor them all without just introducing a ton of complexity.
+ fori (i, 0, total_linear_members - idx - field_linear_members) WI(WI_DROP);
+
+ u64 *temporaries = bh_alloc_array(global_scratch_allocator, u64, field_linear_members);
+ fori (i, 0, field_linear_members) temporaries[i] = 0;
+
+ TypeWithOffset two = { 0 };
+ forir (i, field_linear_members - 1, 0) {
+ type_linear_member_lookup(field->type, i, &two);
+
+ WasmType wt = onyx_type_to_wasm_type(two.type);
+ temporaries[i] = local_raw_allocate(mod->local_alloc, wt);
+ WIL(WI_LOCAL_SET, temporaries[i]);
+ }
+
+ fori (i, 0, idx) WI(WI_DROP);
+
+ fori (i, 0, field_linear_members) {
+ type_linear_member_lookup(field->type, i, &two);
+
+ WIL(WI_LOCAL_GET, temporaries[i]);
+
+ WasmType wt = onyx_type_to_wasm_type(two.type);
+ local_raw_free(mod->local_alloc, wt);
+ }
+
+ bh_free(global_scratch_allocator, temporaries);
+ }
+ }
+
+ break;
+ }
+
+ case Ast_Kind_Slice: {
+ AstSubscript* sl = (AstSubscript *) expr;
+
+ emit_expression(mod, &code, sl->expr);
+
+ u64 lo_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
+ u64 hi_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
+
+ WI(WI_DROP);
+ WIL(WI_LOCAL_SET, hi_local);
+ WIL(WI_LOCAL_TEE, lo_local);
+ if (sl->elem_size != 1) {
+ WID(WI_I32_CONST, sl->elem_size);
+ WI(WI_I32_MUL);
+ }
+ emit_expression(mod, &code, sl->addr);
+ WI(WI_I32_ADD);
+ WIL(WI_LOCAL_GET, hi_local);
+ WIL(WI_LOCAL_GET, lo_local);
+ WI(WI_I32_SUB);
+
+ local_raw_free(mod->local_alloc, lo_local);
+ local_raw_free(mod->local_alloc, hi_local);
+ break;
+ }
+
+ case Ast_Kind_Size_Of: {
+ AstSizeOf* so = (AstSizeOf *) expr;
+ WID(WI_I32_CONST, so->size);
+ break;
+ }
+
+ case Ast_Kind_Align_Of: {
+ AstAlignOf* ao = (AstAlignOf *) expr;
+ WID(WI_I32_CONST, ao->alignment);
+ break;
+ }
+
+ case Ast_Kind_Enum_Value: {
+ AstEnumValue* ev = (AstEnumValue *) expr;
+ WasmType backing_type = onyx_type_to_wasm_type(ev->type);
+ if (backing_type == WASM_TYPE_INT32) WID(WI_I32_CONST, ev->value->value.i);
+ else if (backing_type == WASM_TYPE_INT64) WID(WI_I64_CONST, ev->value->value.l);
+ else onyx_report_error(ev->token->pos, "Invalid backing type for enum.");
+ break;
+ }
+
+ case Ast_Kind_Memres: {
+ AstMemRes* memres = (AstMemRes *) expr;
+ WID(WI_I32_CONST, memres->addr);
+ emit_load_instruction(mod, &code, memres->type, 0);
+ break;
+ }
+
+ case Ast_Kind_File_Contents: {
+ AstFileContents* fc = (AstFileContents *) expr;
+
+ assert(fc->addr > 0);
+ assert(fc->size > 0);
+
+ WID(WI_PTR_CONST, fc->addr);
+ WID(WI_I32_CONST, fc->size);
+ break;
+ }
+
+ case Ast_Kind_Compound: {
+ AstCompound* compound = (AstCompound *) expr;
+
+ bh_arr_each(AstTyped *, expr, compound->exprs) {
+ emit_expression(mod, &code, *expr);
+ }
+ break;
+ }
+
+ case Ast_Kind_Call_Site: {
+ AstCallSite* callsite = (AstCallSite *) expr;
+
+ emit_expression(mod, &code, (AstTyped *) callsite->filename);
+ emit_expression(mod, &code, (AstTyped *) callsite->line);
+ emit_expression(mod, &code, (AstTyped *) callsite->column);
+ break;
+ }
+
+ case Ast_Kind_If_Expression: {
+ AstIfExpression* if_expr = (AstIfExpression *) expr;
+ emit_if_expression(mod, &code, if_expr);
+ break;
+ }
+
+ default:
+ bh_printf("Unhandled case: %d\n", expr->kind);
+ DEBUG_HERE;
+ assert(0);
+ }
+
+ if ((expr->flags & Ast_Flag_Expr_Ignored) != 0 && !type_results_in_void(expr->type)) {
+ i32 mem_count = 1;
+ if (type_is_compound(expr->type)) mem_count = type_linear_member_count(expr->type);
+ fori (i, 0, mem_count) WI(WI_DROP);
+ }
+
+ *pcode = code;
+}
+
+static const WasmInstructionType cast_map[][12] = {
+ // I8 U8 I16 U16 I32 U32 I64 U64 F32 F64 PTR
+ /* I8 */ { WI_NOP, WI_NOP, WI_I32_EXTEND_8_S, WI_NOP, WI_I32_EXTEND_8_S, WI_NOP, WI_I64_FROM_I32_S, WI_I64_FROM_I32_S, WI_UNREACHABLE, WI_UNREACHABLE, WI_UNREACHABLE, WI_UNREACHABLE },
+ /* U8 */ { WI_NOP, WI_NOP, WI_NOP, WI_NOP, WI_NOP, WI_NOP, WI_I64_FROM_I32_U, WI_I64_FROM_I32_U, WI_UNREACHABLE, WI_UNREACHABLE, WI_UNREACHABLE, WI_UNREACHABLE },
+ /* I16 */ { WI_NOP, WI_NOP, WI_NOP, WI_NOP, WI_I32_EXTEND_16_S, WI_NOP, WI_I64_FROM_I32_S, WI_I64_FROM_I32_S, WI_UNREACHABLE, WI_UNREACHABLE, WI_UNREACHABLE, WI_UNREACHABLE },
+ /* U16 */ { WI_NOP, WI_NOP, WI_NOP, WI_NOP, WI_NOP, WI_NOP, WI_I64_FROM_I32_U, WI_I64_FROM_I32_U, WI_UNREACHABLE, WI_UNREACHABLE, WI_UNREACHABLE, WI_UNREACHABLE },
+ /* I32 */ { WI_NOP, WI_NOP, WI_NOP, WI_NOP, WI_NOP, WI_NOP, WI_I64_FROM_I32_S, WI_I64_FROM_I32_S, WI_F32_FROM_I32_S, WI_F64_FROM_I32_S, WI_NOP, WI_NOP },
+ /* U32 */ { WI_NOP, WI_NOP, WI_NOP, WI_NOP, WI_NOP, WI_NOP, WI_I64_FROM_I32_U, WI_I64_FROM_I32_U, WI_F32_FROM_I32_U, WI_F64_FROM_I32_U, WI_NOP, WI_NOP },
+ /* I64 */ { WI_NOP, WI_I32_FROM_I64, WI_I32_FROM_I64, WI_I32_FROM_I64, WI_I32_FROM_I64, WI_I32_FROM_I64, WI_NOP, WI_NOP, WI_F32_FROM_I64_S, WI_F64_FROM_I64_S, WI_I32_FROM_I64, WI_UNREACHABLE },
+ /* U64 */ { WI_NOP, WI_I32_FROM_I64, WI_I32_FROM_I64, WI_I32_FROM_I64, WI_I32_FROM_I64, WI_I32_FROM_I64, WI_NOP, WI_NOP, WI_F32_FROM_I64_U, WI_F64_FROM_I64_U, WI_I32_FROM_I64, WI_UNREACHABLE },
+ /* F32 */ { WI_UNREACHABLE, WI_UNREACHABLE, WI_UNREACHABLE, WI_UNREACHABLE, WI_I32_FROM_F32_S, WI_I32_FROM_F32_U, WI_I64_FROM_F32_S, WI_I64_FROM_F32_U, WI_NOP, WI_F64_FROM_F32, WI_UNREACHABLE, WI_UNREACHABLE },
+ /* F64 */ { WI_UNREACHABLE, WI_UNREACHABLE, WI_UNREACHABLE, WI_UNREACHABLE, WI_I32_FROM_F64_S, WI_I32_FROM_F64_U, WI_I64_FROM_F64_S, WI_I64_FROM_F64_U, WI_F32_FROM_F64, WI_NOP, WI_UNREACHABLE, WI_UNREACHABLE },
+ /* PTR */ { WI_UNREACHABLE, WI_UNREACHABLE, WI_UNREACHABLE, WI_UNREACHABLE, WI_NOP, WI_NOP, WI_I64_FROM_I32_U, WI_I64_FROM_I32_U, WI_UNREACHABLE, WI_UNREACHABLE, WI_NOP, WI_UNREACHABLE },
+ /* TYP */ { WI_UNREACHABLE, WI_UNREACHABLE, WI_UNREACHABLE, WI_UNREACHABLE, WI_NOP, WI_NOP, WI_UNREACHABLE, WI_UNREACHABLE, WI_UNREACHABLE, WI_UNREACHABLE, WI_UNREACHABLE, WI_NOP },
+};
+
+EMIT_FUNC(cast, AstUnaryOp* cast) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ emit_expression(mod, &code, cast->expr);
+
+ Type* from = cast->expr->type;
+ Type* to = cast->type;
+ if (from->kind == Type_Kind_Enum) from = from->Enum.backing;
+ if (to->kind == Type_Kind_Enum) to = to->Enum.backing;
+
+ if (type_is_simd(from) && type_is_simd(to)) {
+ *pcode = code;
+ return;
+ }
+
+ if (to->kind == Type_Kind_Basic && to->Basic.kind == Basic_Kind_Void) {
+ WI(WI_DROP);
+ *pcode = code;
+ return;
+ }
+
+ if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_Array) {
+ WID(WI_I32_CONST, from->Array.count);
+ *pcode = code;
+ return;
+ }
+
+ if (to->kind == Type_Kind_Slice && from->kind == Type_Kind_VarArgs) {
+ // Nothing needs to be done because they are identical
+ *pcode = code;
+ return;
+ }
+
+ i32 fromidx = -1, toidx = -1;
+ if (from->Basic.flags & Basic_Flag_Pointer || from->kind == Type_Kind_Array) {
+ fromidx = 10;
+ }
+ else if (from->Basic.flags & Basic_Flag_Integer) {
+ b32 unsign = (from->Basic.flags & Basic_Flag_Unsigned) != 0;
+
+ fromidx = log2_dumb(from->Basic.size) * 2 + unsign;
+ }
+ else if (from->Basic.flags & Basic_Flag_Float) {
+ if (from->Basic.size == 4) fromidx = 8;
+ else if (from->Basic.size == 8) fromidx = 9;
+ }
+ else if (from->Basic.flags & Basic_Flag_Boolean) {
+ fromidx = 0;
+ }
+ else if (from->Basic.flags & Basic_Flag_Type_Index) {
+ fromidx = 11;
+ }
+
+ if (to->Basic.flags & Basic_Flag_Pointer || to->kind == Type_Kind_Array) {
+ toidx = 10;
+ }
+ else if (to->Basic.flags & Basic_Flag_Integer) {
+ b32 unsign = (to->Basic.flags & Basic_Flag_Unsigned) != 0;
+
+ toidx = log2_dumb(to->Basic.size) * 2 + unsign;
+ }
+ else if (to->Basic.flags & Basic_Flag_Float) {
+ if (to->Basic.size == 4) toidx = 8;
+ else if (to->Basic.size == 8) toidx = 9;
+ }
+ else if (to->Basic.flags & Basic_Flag_Boolean) {
+ toidx = 0;
+ }
+ else if (to->Basic.flags & Basic_Flag_Type_Index) {
+ toidx = 11;
+ }
+
+ if (fromidx != -1 && toidx != -1) {
+ WasmInstructionType cast_op = cast_map[fromidx][toidx];
+ assert(cast_op != WI_UNREACHABLE);
+
+ if (cast_op != WI_NOP) {
+ WI(cast_op);
+ }
+ }
+
+ *pcode = code;
+}
+
+EMIT_FUNC(return, AstReturn* ret) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ // If we have an expression to return, we see if it should be placed on the linear memory stack, or the WASM stack.
+ if (ret->expr) {
+ if (bh_arr_length(mod->return_location_stack) > 0) {
+ AstLocal* dest = bh_arr_last(mod->return_location_stack);
+ u64 dest_loc = bh_imap_get(&mod->local_map, (u64) dest);
+ b32 dest_is_local = (b32) ((dest_loc & LOCAL_IS_WASM) != 0);
+
+ u64 offset = 0;
+ if (!dest_is_local) emit_local_location(mod, &code, dest, &offset);
+
+ emit_expression(mod, &code, ret->expr);
+
+ if (!dest_is_local) emit_store_instruction(mod, &code, dest->type, offset);
+ else WIL(WI_LOCAL_SET, dest_loc);
+
+ } else if (mod->curr_cc == CC_Return_Stack) {
+ WIL(WI_LOCAL_GET, mod->stack_base_idx);
+ WID(WI_I32_CONST, type_size_of(ret->expr->type));
+ WI(WI_I32_SUB);
+
+ emit_expression(mod, &code, ret->expr);
+ emit_store_instruction(mod, &code, ret->expr->type, 0);
+
+ } else {
+ emit_expression(mod, &code, ret->expr);
+ }
+ }
+
+ // Clear the normal deferred statements
+ emit_deferred_stmts(mod, &code);
+
+ // Clear the rest of the deferred statements
+ if (bh_arr_length(mod->deferred_stmts) > 0) {
+ i32 i = bh_arr_length(mod->deferred_stmts) - 1;
+ while (i >= 0) {
+ emit_deferred_stmt(mod, &code, mod->deferred_stmts[i]);
+ i--;
+ }
+ }
+
+ i64 jump_label = get_structured_jump_label(mod, Jump_Type_Return, 1);
+ if (jump_label >= 0) {
+ WIL(WI_JUMP, jump_label);
+
+ } else {
+ // Make a patch for the two instructions needed to restore the stack pointer
+ SUBMIT_PATCH(mod->stack_leave_patches, 0);
+ WI(WI_NOP);
+ WI(WI_NOP);
+
+ WI(WI_RETURN);
+ }
+
+ *pcode = code;
+}
+
+EMIT_FUNC(stack_enter, u64 stacksize) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ bh_align(stacksize, 16);
+
+ u64 stack_top_idx = bh_imap_get(&mod->index_map, (u64) &builtin_stack_top);
+
+ // HACK: slightly... There will be space for 5 instructions
+ if (stacksize == 0) {
+ code[0] = (WasmInstruction) { WI_GLOBAL_GET, { .l = stack_top_idx } };
+ code[1] = (WasmInstruction) { WI_LOCAL_SET, { .l = mod->stack_base_idx} };
+ code[2] = (WasmInstruction) { WI_NOP, 0 };
+ code[3] = (WasmInstruction) { WI_NOP, 0 };
+ code[4] = (WasmInstruction) { WI_NOP, 0 };
+ } else {
+ code[0] = (WasmInstruction) { WI_GLOBAL_GET, { .l = stack_top_idx } };
+ code[1] = (WasmInstruction) { WI_LOCAL_TEE, { .l = mod->stack_base_idx} };
+ code[2] = (WasmInstruction) { WI_I32_CONST, { .l = stacksize } };
+ code[3] = (WasmInstruction) { WI_I32_ADD, 0 };
+ code[4] = (WasmInstruction) { WI_GLOBAL_SET, { .l = stack_top_idx } };
+ }
+
+ *pcode = code;
+}
+
+EMIT_FUNC(zero_value, WasmType wt) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ switch (wt) {
+ case WASM_TYPE_INT32: WIL(WI_I32_CONST, 0); break;
+ case WASM_TYPE_INT64: WIL(WI_I64_CONST, 0); break;
+ case WASM_TYPE_FLOAT32: WIL(WI_F32_CONST, 0); break;
+ case WASM_TYPE_FLOAT64: WIL(WI_F64_CONST, 0); break;
+ case WASM_TYPE_VAR128: {
+ static u8 zero_v128[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+ WIP(WI_V128_CONST, &zero_v128);
+ break;
+ }
+ }
+
+ *pcode = code;
+}
+
+EMIT_FUNC(zero_value_for_type, Type* type, OnyxToken* where) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ if (type_is_structlike_strict(type)) {
+ i32 mem_count = type_linear_member_count(type);
+ TypeWithOffset two;
+
+ fori (i, 0, mem_count) {
+ type_linear_member_lookup(type, i, &two);
+ emit_zero_value_for_type(mod, &code, two.type, where);
+ }
+
+ }
+ else if (type->kind == Type_Kind_Function) {
+ WID(WI_I32_CONST, mod->null_proc_func_idx);
+ }
+ else {
+ WasmType wt = onyx_type_to_wasm_type(type);
+ if (wt == WASM_TYPE_VOID) {
+ onyx_report_error(where->pos, "Cannot produce a zero-value for this type.");
+ }
+ emit_zero_value(mod, &code, wt);
+ }
+
+ *pcode = code;
+}
+
+static i32 generate_type_idx(OnyxWasmModule* mod, Type* ft) {
+ if (ft->kind != Type_Kind_Function) return -1;
+
+ static char type_repr_buf[128];
+ char* t = type_repr_buf;
+
+ Type** param_type = ft->Function.params;
+ i32 param_count = ft->Function.param_count;
+ i32 params_left = param_count;
+
+ while (params_left-- > 0) {
+ if (type_get_param_pass(*param_type) == Param_Pass_By_Implicit_Pointer) {
+ *(t++) = (char) onyx_type_to_wasm_type(&basic_types[Basic_Kind_Rawptr]);
+
+ }
+ else if (type_is_structlike_strict(*param_type)) {
+ u32 mem_count = type_structlike_mem_count(*param_type);
+ StructMember smem;
+
+ fori (i, 0, mem_count) {
+ type_lookup_member_by_idx(*param_type, i, &smem);
+ *(t++) = (char) onyx_type_to_wasm_type(smem.type);
+ }
+
+ param_count += mem_count - 1;
+
+ } else {
+ *(t++) = (char) onyx_type_to_wasm_type(*param_type);
+ }
+
+ param_type++;
+ }
+ *(t++) = ':';
+
+ // HACK: Slightly: the wasm type for structs has to be 0x00
+ WasmType return_type = onyx_type_to_wasm_type(ft->Function.return_type);
+ *(t++) = (char) return_type;
+ *t = '\0';
+
+ i32 type_idx = 0;
+ if (bh_table_has(i32, mod->type_map, type_repr_buf)) {
+ type_idx = bh_table_get(i32, mod->type_map, type_repr_buf);
+ } else {
+ // NOTE: Make a new type
+ WasmFuncType* type = (WasmFuncType*) bh_alloc(mod->allocator, sizeof(WasmFuncType) + sizeof(WasmType) * param_count);
+ type->return_type = return_type;
+ type->param_count = param_count;
+
+ // HACK ish thing
+ memcpy(type->param_types, type_repr_buf, type->param_count);
+
+ bh_arr_push(mod->types, type);
+
+ bh_table_put(i32, mod->type_map, type_repr_buf, mod->next_type_idx);
+ type_idx = mod->next_type_idx;
+ mod->next_type_idx++;
+ }
+
+ return type_idx;
+}
+
+static i32 get_element_idx(OnyxWasmModule* mod, AstFunction* func) {
+ if (bh_imap_has(&mod->elem_map, (u64) func)) {
+ return bh_imap_get(&mod->elem_map, (u64) func);
+ } else {
+ i32 idx = mod->next_elem_idx;
+ bh_imap_put(&mod->elem_map, (u64) func, idx);
+
+ i32 func_idx = bh_imap_get(&mod->index_map, (u64) func);
+ bh_arr_push(mod->elems, func_idx);
+
+ mod->next_elem_idx++;
+
+ return idx;
+ }
+}
+
+static inline b32 should_emit_function(AstFunction* fd) {
+ // NOTE: Don't output intrinsic functions
+ if (fd->flags & Ast_Flag_Intrinsic) return 0;
+
+ // NOTE: Don't output functions that are not used, only if
+ // they are also not exported.
+ if ((fd->flags & Ast_Flag_Function_Used) == 0) {
+ if (fd->flags & Ast_Flag_Exported) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+
+static void emit_function(OnyxWasmModule* mod, AstFunction* fd) {
+ if (!should_emit_function(fd)) return;
+
+ i32 type_idx = generate_type_idx(mod, fd->type);
+
+ WasmFunc wasm_func = {
+ .type_idx = type_idx,
+ .locals = {
+ .param_count = 0,
+
+ .allocated = { 0 },
+ .freed = { 0 },
+
+ .max_stack = 0,
+ .curr_stack = 0,
+ },
+ .code = NULL,
+ };
+
+ bh_arr_new(mod->allocator, wasm_func.code, 4);
+
+ i32 func_idx = (i32) bh_imap_get(&mod->index_map, (u64) fd);
+
+ // If there is no body then don't process the code
+ if (fd->body != NULL) {
+ // NOTE: Generate the local map
+ u64 localidx = 0;
+ bh_arr_each(AstParam, param, fd->params) {
+ switch (type_get_param_pass(param->local->type)) {
+ case Param_Pass_By_Value: {
+ if (type_is_structlike_strict(param->local->type)) {
+ bh_imap_put(&mod->local_map, (u64) param->local, localidx | LOCAL_IS_WASM);
+ localidx += type_structlike_mem_count(param->local->type);
+
+ break;
+ }
+ // fallthrough
+ }
+
+ case Param_Pass_By_Implicit_Pointer: {
+ bh_imap_put(&mod->local_map, (u64) param->local, localidx++ | LOCAL_IS_WASM);
+ break;
+ }
+
+ default: assert(0);
+ }
+ }
+
+ mod->local_alloc = &wasm_func.locals;
+ mod->local_alloc->param_count = localidx;
+
+ mod->curr_cc = type_function_get_cc(fd->type);
+ assert(mod->curr_cc != CC_Undefined);
+
+ bh_arr_clear(mod->stack_leave_patches);
+
+ bh_arr_insert_end(wasm_func.code, 5);
+ fori (i, 0, 5) wasm_func.code[i] = (WasmInstruction) { WI_NOP, 0 };
+
+ mod->stack_base_idx = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+
+ // Generate code
+ emit_function_body(mod, &wasm_func.code, fd);
+
+ if (mod->local_alloc->max_stack > 0 || mod->curr_cc == CC_Return_Stack) {
+ emit_stack_enter(mod, &wasm_func.code, mod->local_alloc->max_stack);
+
+ // Place all stack leaves in patch locations. These will (probably) all be
+ // right before a "return" instruction.
+ u64 stack_top_idx = bh_imap_get(&mod->index_map, (u64) &builtin_stack_top);
+ bh_arr_push(wasm_func.code, ((WasmInstruction) { WI_LOCAL_GET, { .l = mod->stack_base_idx } }));
+ bh_arr_push(wasm_func.code, ((WasmInstruction) { WI_GLOBAL_SET, { .l = stack_top_idx } }));
+
+ bh_arr_each(PatchInfo, patch, mod->stack_leave_patches) {
+ wasm_func.code[patch->instruction_index + 0] = (WasmInstruction) { WI_LOCAL_GET, { .l = mod->stack_base_idx } };
+ wasm_func.code[patch->instruction_index + 1] = (WasmInstruction) { WI_GLOBAL_SET, { .l = stack_top_idx } };
+ }
+ }
+ }
+
+ WasmFuncType* ft = mod->types[type_idx];
+ emit_zero_value(mod, &wasm_func.code, ft->return_type);
+
+ bh_arr_push(wasm_func.code, ((WasmInstruction){ WI_BLOCK_END, 0x00 }));
+
+ bh_arr_set_at(mod->funcs, func_idx - mod->foreign_function_count, wasm_func);
+
+ // NOTE: Clear the local map on exit of generating this function
+ bh_imap_clear(&mod->local_map);
+}
+
+static void emit_foreign_function(OnyxWasmModule* mod, AstFunction* fd) {
+ if (!should_emit_function(fd)) return;
+
+ i32 type_idx = generate_type_idx(mod, fd->type);
+
+ WasmImport import = {
+ .kind = WASM_FOREIGN_FUNCTION,
+ .idx = type_idx,
+ .mod = fd->foreign_module,
+ .name = fd->foreign_name,
+ };
+
+ bh_arr_push(mod->imports, import);
+ return;
+}
+
+static void emit_export_directive(OnyxWasmModule* mod, AstDirectiveExport* export) {
+ assert(export->export_name);
+ assert(export->export);
+
+ token_toggle_end(export->export_name);
+
+ i64 idx = bh_imap_get(&mod->index_map, (u64) export->export);
+
+ WasmExport wasm_export;
+ wasm_export.idx = (i32) idx;
+
+ switch (export->export->kind) {
+ case Ast_Kind_Function: wasm_export.kind = WASM_FOREIGN_FUNCTION;
+ break;
+
+ case Ast_Kind_Global: wasm_export.kind = WASM_FOREIGN_GLOBAL;
+ break;
+ }
+
+ bh_table_put(WasmExport, mod->exports, export->export_name->text, wasm_export);
+ mod->export_count++;
+
+ token_toggle_end(export->export_name);
+ return;
+}
+
+static void emit_global(OnyxWasmModule* module, AstGlobal* global) {
+ WasmType global_type = onyx_type_to_wasm_type(global->type);
+
+ WasmGlobal glob = {
+ .type = global_type,
+ .mutable = (global->flags & Ast_Flag_Const) == 0,
+ .initial_value = NULL,
+ };
+
+ i32 global_idx = (i32) bh_imap_get(&module->index_map, (u64) global);
+
+ bh_arr_new(global_heap_allocator, glob.initial_value, 1);
+
+ switch (global_type) {
+ case WASM_TYPE_INT32: bh_arr_push(glob.initial_value, ((WasmInstruction) { WI_I32_CONST, 0 })); break;
+ case WASM_TYPE_INT64: bh_arr_push(glob.initial_value, ((WasmInstruction) { WI_I64_CONST, 0 })); break;
+ case WASM_TYPE_FLOAT32: bh_arr_push(glob.initial_value, ((WasmInstruction) { WI_F32_CONST, 0 })); break;
+ case WASM_TYPE_FLOAT64: bh_arr_push(glob.initial_value, ((WasmInstruction) { WI_F64_CONST, 0 })); break;
+
+ default: assert(("Invalid global type", 0)); break;
+ }
+
+ bh_arr_set_at(module->globals, global_idx - module->foreign_global_count, glob);
+
+ if (global->flags & Ast_Flag_Global_Stack_Top)
+ module->stack_top_ptr = &module->globals[global_idx - module->foreign_global_count].initial_value[0].data.i1;
+}
+
+static void emit_foreign_global(OnyxWasmModule* module, AstGlobal* global) {
+ WasmType global_type = onyx_type_to_wasm_type(global->type);
+
+ if (global->flags & Ast_Flag_Foreign) {
+ WasmImport import = {
+ .kind = WASM_FOREIGN_GLOBAL,
+ .idx = global_type,
+ .mod = global->foreign_module,
+ .name = global->foreign_name,
+ };
+
+ bh_arr_push(module->imports, import);
+ }
+}
+
+static void emit_string_literal(OnyxWasmModule* mod, AstStrLit* strlit) {
+
+ // NOTE: Allocating more than necessary, but there are no cases
+ // in a string literal that create more bytes than already
+ // existed. You can create less however ('\n' => 0x0a).
+ i8* strdata = bh_alloc_array(global_heap_allocator, i8, strlit->token->length + 1);
+ i32 length = string_process_escape_seqs(strdata, strlit->token->text, strlit->token->length);
+
+ // Warning for having '%' in a string literal (because that probably is being used for a old print format)
+ /*
+ if (charset_contains((const char *) strdata, '%')) {
+ onyx_report_warning(strlit->token->pos, "Found string literal with '%%'");
+ }
+ */
+
+ if (bh_table_has(StrLitInfo, mod->string_literals, (char *) strdata)) {
+ StrLitInfo sti = bh_table_get(StrLitInfo, mod->string_literals, (char *) strdata);
+ strlit->addr = sti.addr;
+ strlit->length = sti.len;
+
+ bh_free(global_heap_allocator, strdata);
+ return;
+ }
+
+ WasmDatum datum = {
+ .offset = mod->next_datum_offset,
+ .length = length,
+ .data = strdata,
+ };
+
+ strlit->addr = (u32) mod->next_datum_offset,
+ strlit->length = length;
+ mod->next_datum_offset += length;
+
+ bh_table_put(StrLitInfo, mod->string_literals, (char *) strdata, ((StrLitInfo) { strlit->addr, strlit->length }));
+
+ bh_arr_push(mod->data, datum);
+}
+
+static void emit_raw_data(OnyxWasmModule* mod, ptr data, AstTyped* node) {
+ if (!emit_raw_data_(mod, data, node)) {
+ onyx_report_error(node->token->pos,
+ "Cannot generate constant data for '%s'.",
+ onyx_ast_node_kind_string(node->kind));
+ }
+}
+
+static b32 emit_raw_data_(OnyxWasmModule* mod, ptr data, AstTyped* node) {
+ b32 retval = 1;
+
+ switch (node->kind) {
+ case Ast_Kind_Array_Literal: {
+ AstArrayLiteral* al = (AstArrayLiteral *) node;
+
+ i32 i = 0;
+ i32 elem_size = type_size_of(al->type->Array.elem);
+
+ bh_arr_each(AstTyped *, expr, al->values) {
+ retval &= emit_raw_data_(mod, bh_pointer_add(data, i * elem_size), *expr);
+ i++;
+ }
+
+ break;
+ }
+
+ case Ast_Kind_Struct_Literal: {
+ AstStructLiteral* sl = (AstStructLiteral *) node;
+
+ Type* sl_type = sl->type;
+ // ROBUSTNESS: Handle cases for slices and dynamic arrays
+ if (sl_type->kind != Type_Kind_Struct && sl_type->kind != Type_Kind_Slice && sl_type->kind != Type_Kind_DynArray) {
+ retval = 0;
+ break;
+ }
+
+ i32 mem_count = type_structlike_mem_count(sl_type);
+ StructMember smem;
+
+ fori (i, 0, mem_count) {
+ type_lookup_member_by_idx(sl_type, i, &smem);
+ retval &= emit_raw_data_(mod, bh_pointer_add(data, smem.offset), sl->args.values[i]);
+ }
+
+ break;
+ }
+
+ case Ast_Kind_StrLit: {
+ AstStrLit* sl = (AstStrLit *) node;
+
+ // NOTE: This assumes the address and the length fields have been filled out
+ // by emit_string_literal.
+ u32* sdata = (u32 *) data;
+ sdata[0] = sl->addr;
+ sdata[1] = 0x00;
+ sdata[2] = sl->length;
+ break;
+ }
+
+ case Ast_Kind_Enum_Value: {
+ AstEnumValue* ev = (AstEnumValue *) node;
+ retval &= emit_raw_data_(mod, data, (AstTyped *) ev->value);
+ break;
+ }
+
+ case Ast_Kind_Function: {
+ AstFunction* func = (AstFunction *) node;
+ *((u32 *) data) = get_element_idx(mod, func);
+ break;
+ }
+
+ case Ast_Kind_NumLit: {
+ // NOTE: This makes a big assumption that we are running on a
+ // little endian machine, since WebAssembly is little endian
+ // by specification. This is probably a safe assumption, but
+ // should probably be fixed at some point.
+ // - brendanfh 2020/12/15
+
+ Type* effective_type = node->type;
+
+ if (effective_type->kind == Type_Kind_Enum)
+ effective_type = effective_type->Enum.backing;
+
+ switch (effective_type->Basic.kind) {
+ case Basic_Kind_Bool:
+ case Basic_Kind_I8:
+ case Basic_Kind_U8:
+ *((i8 *) data) = (i8) ((AstNumLit *) node)->value.i;
+ return retval;
+
+ case Basic_Kind_I16:
+ case Basic_Kind_U16:
+ *((i16 *) data) = (i16) ((AstNumLit *) node)->value.i;
+ return retval;
+
+ case Basic_Kind_I32:
+ case Basic_Kind_U32:
+ *((i32 *) data) = ((AstNumLit *) node)->value.i;
+ return retval;
+
+ case Basic_Kind_I64:
+ case Basic_Kind_U64:
+ case Basic_Kind_Rawptr:
+ *((i64 *) data) = ((AstNumLit *) node)->value.l;
+ return retval;
+
+ case Basic_Kind_F32:
+ *((f32 *) data) = ((AstNumLit *) node)->value.f;
+ return retval;
+
+ case Basic_Kind_F64:
+ *((f64 *) data) = ((AstNumLit *) node)->value.d;
+ return retval;
+
+ default: break;
+ }
+
+ //fallthrough
+ }
+
+ case Ast_Kind_Code_Block: break;
+
+ default: retval = 0;
+ }
+
+ return retval;
+}
+
+static void emit_memory_reservation(OnyxWasmModule* mod, AstMemRes* memres) {
+ Type* effective_type = memres->type;
+
+ u64 alignment = type_alignment_of(effective_type);
+ u64 size = type_size_of(effective_type);
+
+ if (type_table_node != NULL && (AstMemRes *) type_table_node == memres) {
+ u64 table_location = build_type_table(mod);
+ memres->addr = table_location;
+
+ return;
+ }
+
+ u32 offset = mod->next_datum_offset;
+ bh_align(offset, alignment);
+
+ if (memres->initial_value != NULL) {
+ u8* data = bh_alloc(global_heap_allocator, size);
+ emit_raw_data(mod, data, memres->initial_value);
+
+ WasmDatum datum = {
+ .offset = offset,
+ .length = size,
+ .data = data,
+ };
+
+ bh_arr_push(mod->data, datum);
+ }
+
+ memres->addr = offset;
+ mod->next_datum_offset = offset + size;
+}
+
+static void emit_file_contents(OnyxWasmModule* mod, AstFileContents* fc) {
+ token_toggle_end(fc->filename_token);
+
+ // INVESTIGATE: I think that filename should always be NULL when this function starts because
+ // a file contents entity is only processed once and therefore should only go through this step
+ // once. But somehow filename isn't NULL occasionally so I have to check for that...
+ // - brendanfh 2021/05/23
+ if (fc->filename == NULL) {
+ const char* parent_file = fc->token->pos.filename;
+ if (parent_file == NULL) parent_file = ".";
+
+ char* parent_folder = bh_path_get_parent(parent_file, global_scratch_allocator);
+
+ char* temp_fn = bh_alloc_array(global_scratch_allocator, char, fc->filename_token->length);
+ i32 temp_fn_len = string_process_escape_seqs(temp_fn, fc->filename_token->text, fc->filename_token->length);
+ char* filename = lookup_included_file(temp_fn, parent_folder, 0, 0);
+ fc->filename = bh_strdup(global_heap_allocator, filename);
+ }
+
+ token_toggle_end(fc->filename_token);
+
+ if (bh_table_has(StrLitInfo, mod->loaded_file_info, fc->filename)) {
+ StrLitInfo info = bh_table_get(StrLitInfo, mod->loaded_file_info, fc->filename);
+ fc->addr = info.addr;
+ fc->size = info.len;
+ return;
+ }
+
+ u32 offset = mod->next_datum_offset;
+ bh_align(offset, 16);
+
+ if (!bh_file_exists(fc->filename)) {
+ onyx_report_error(fc->filename_token->pos,
+ "Unable to open file for reading, '%s'.",
+ fc->filename);
+ return;
+ }
+
+ // :RelativeFiles This should be relative to the current directory by default; However,
+ // if the filename is prefixed with a './' or '.\\' then it should be relative to the
+ // file in which is was inclded. The loaded file info above should probably use the full
+ // file path in order to avoid duplicates.
+ bh_file_contents contents = bh_file_read_contents(global_heap_allocator, fc->filename);
+ u8* actual_data = bh_alloc(global_heap_allocator, contents.length + 1);
+ u32 length = contents.length + 1;
+ memcpy(actual_data, contents.data, contents.length);
+ actual_data[contents.length] = 0;
+ bh_file_contents_free(&contents);
+
+ bh_table_put(StrLitInfo, mod->loaded_file_info, fc->filename, ((StrLitInfo) {
+ .addr = offset,
+ .len = length - 1,
+ }));
+
+ fc->addr = offset;
+ fc->size = length - 1;
+
+ WasmDatum datum = {
+ .offset = offset,
+ .length = length,
+ .data = actual_data,
+ };
+
+ bh_arr_push(mod->data, datum);
+
+ mod->next_datum_offset = offset + length;
+}
+
+OnyxWasmModule onyx_wasm_module_create(bh_allocator alloc) {
+ OnyxWasmModule module = {
+ .allocator = alloc,
+
+ .type_map = NULL,
+ .next_type_idx = 0,
+ .types = NULL,
+
+ .funcs = NULL,
+ .next_func_idx = 0,
+
+ .exports = NULL,
+ .export_count = 0,
+
+ .imports = NULL,
+
+ .globals = NULL,
+ .next_global_idx = 0,
+
+ .data = NULL,
+ .next_datum_offset = 32, // Starting offset so null pointers don't immediately
+ // break constant data. - brendanfh 2020/12/16
+
+ .elems = NULL,
+ .next_elem_idx = 0,
+
+ .structured_jump_target = NULL,
+ .return_location_stack = NULL,
+ .local_allocations = NULL,
+ .stack_leave_patches = NULL,
+ .deferred_stmts = NULL,
+
+ .stack_top_ptr = NULL,
+ .stack_base_idx = 0,
+
+ .foreign_function_count = 0,
+ .foreign_global_count = 0,
+
+ .null_proc_func_idx = -1,
+ };
+
+ bh_arena* eid = bh_alloc(global_heap_allocator, sizeof(bh_arena));
+ bh_arena_init(eid, global_heap_allocator, 8196);
+ module.extended_instr_data = eid;
+ module.extended_instr_alloc = bh_arena_allocator(eid);
+
+ bh_arr_new(alloc, module.types, 4);
+ bh_arr_new(alloc, module.funcs, 4);
+ bh_arr_new(alloc, module.imports, 4);
+ bh_arr_new(alloc, module.globals, 4);
+ bh_arr_new(alloc, module.data, 4);
+ bh_arr_new(alloc, module.elems, 4);
+
+ bh_arr_new(global_heap_allocator, module.return_location_stack, 4);
+ bh_arr_new(global_heap_allocator, module.structured_jump_target, 16);
+ bh_arr_set_length(module.structured_jump_target, 0);
+
+ bh_table_init(global_heap_allocator, module.type_map, 61);
+ bh_table_init(global_heap_allocator, module.exports, 61);
+ bh_table_init(global_heap_allocator, module.loaded_file_info, 7);
+ bh_table_init(global_heap_allocator, module.string_literals, 16);
+
+ bh_imap_init(&module.index_map, global_heap_allocator, 128);
+ bh_imap_init(&module.local_map, global_heap_allocator, 16);
+ bh_imap_init(&module.elem_map, global_heap_allocator, 16);
+
+ bh_arr_new(global_heap_allocator, module.deferred_stmts, 4);
+ bh_arr_new(global_heap_allocator, module.local_allocations, 4);
+ bh_arr_new(global_heap_allocator, module.stack_leave_patches, 4);
+
+ WasmExport mem_export = {
+ .kind = WASM_FOREIGN_MEMORY,
+ .idx = 0,
+ };
+ // :ArbitraryConstant
+ // :WasmMemory
+ bh_table_put(WasmExport, module.exports, "memory", mem_export);
+ module.export_count++;
+
+ return module;
+}
+
+void emit_entity(Entity* ent) {
+ OnyxWasmModule* module = context.wasm_module;
+
+ if (module->stack_top_ptr) {
+ *module->stack_top_ptr = module->next_datum_offset;
+
+ if (*module->stack_top_ptr % 16 != 0) {
+ *module->stack_top_ptr += 16 - (*module->stack_top_ptr % 16);
+ }
+
+ builtin_heap_start.value.i = *module->stack_top_ptr + (1 << 20);
+ if (builtin_heap_start.value.i % 16 != 0) {
+ builtin_heap_start.value.i += 16 - (builtin_heap_start.value.i % 16);
+ }
+ }
+
+ switch (ent->type) {
+ case Entity_Type_Foreign_Function_Header:
+ if (!should_emit_function(ent->function)) break;
+
+ module->foreign_function_count++;
+ emit_foreign_function(module, ent->function);
+ // fallthrough
+
+ case Entity_Type_Function_Header:
+ if (!should_emit_function(ent->function)) break;
+
+ if (context.options->print_function_mappings) {
+ bh_printf("%d -> %s:%d:%d\n",
+ module->next_func_idx,
+ ent->expr->token->pos.filename,
+ ent->expr->token->pos.line,
+ ent->expr->token->pos.column);
+ }
+
+ bh_imap_put(&module->index_map, (u64) ent->function, module->next_func_idx++);
+
+ if (ent->function->flags & Ast_Flag_Proc_Is_Null) {
+ if (module->null_proc_func_idx == -1) module->null_proc_func_idx = get_element_idx(module, ent->function);
+ }
+ break;
+
+ case Entity_Type_Foreign_Global_Header:
+ module->foreign_global_count++;
+ emit_foreign_global(module, ent->global);
+ // fallthrough
+
+ case Entity_Type_Global_Header:
+ bh_imap_put(&module->index_map, (u64) ent->global, module->next_global_idx++);
+ break;
+
+ case Entity_Type_String_Literal: {
+ emit_string_literal(module, (AstStrLit *) ent->strlit);
+ break;
+ }
+
+ case Entity_Type_File_Contents: {
+ emit_file_contents(module, (AstFileContents *) ent->file_contents);
+ break;
+ }
+
+ case Entity_Type_Memory_Reservation: {
+ emit_memory_reservation(module, (AstMemRes *) ent->mem_res);
+ break;
+ }
+
+ case Entity_Type_Process_Directive: {
+ if (ent->expr->kind == Ast_Kind_Directive_Export) {
+ emit_export_directive(module, (AstDirectiveExport *) ent->expr);
+ }
+ break;
+ }
+
+ case Entity_Type_Function: emit_function(module, ent->function); break;
+ case Entity_Type_Global: emit_global(module, ent->global); break;
+
+ // Cleanup: Maybe these should be printed elsewhere?
+ // Also, they should be sorted? Or have that ability?
+ case Entity_Type_Note: {
+ if (!context.options->print_notes) break;
+
+ AstNote* note = (AstNote *) ent->expr;
+ OnyxFilePos pos = note->token->pos;
+
+ bh_printf("Note: %b %s:%d:%d\n", note->token->text, note->token->length, pos.filename, pos.line, pos.column);
+
+ break;
+ }
+
+ default: break;
+ }
+
+ ent->state = Entity_State_Finalized;
+}
+
+void onyx_wasm_module_free(OnyxWasmModule* module) {
+ if (module->extended_instr_data != NULL)
+ bh_arena_free(module->extended_instr_data);
+
+ bh_arr_free(module->types);
+ bh_arr_free(module->funcs);
+ bh_imap_free(&module->local_map);
+ bh_imap_free(&module->index_map);
+ bh_table_free(module->type_map);
+ bh_table_free(module->exports);
+}
+
+
+#include "wasm_output.c"
--- /dev/null
+// This file is directly included in src/onxywasm.c
+// It is here purely to decrease the amount of clutter in the main file.
+
+
+// IMPROVE: This implementation assumes that the source and destination buffers do not overlap.
+// The specification for memory.copy in WASM does work even if the buffers overlap.
+// Also, this implementation copies byte-by-byte, which is terrible. It should copy
+// quad word by quad word, and then the additional bytes if the count was not divisible by 8.
+// :32BitPointers
+EMIT_FUNC_NO_ARGS(intrinsic_memory_copy) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ // The stack should look like this:
+ // <count>
+ // <source>
+ // <dest>
+
+ u64 count_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
+ u64 source_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+ u64 dest_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+
+ WIL(WI_LOCAL_SET, count_local);
+ WIL(WI_LOCAL_SET, source_local);
+ WIL(WI_LOCAL_SET, dest_local);
+
+ // count is greater than 0
+ WIL(WI_LOCAL_GET, count_local);
+ WID(WI_I32_CONST, 0);
+ WI(WI_I32_GT_S);
+
+ WID(WI_IF_START, 0x40);
+ WID(WI_LOOP_START, 0x40);
+
+ WIL(WI_LOCAL_GET, count_local);
+ WID(WI_I32_CONST, 1);
+ WI(WI_I32_SUB);
+ WIL(WI_LOCAL_SET, count_local);
+
+ WIL(WI_LOCAL_GET, dest_local);
+ WIL(WI_LOCAL_GET, count_local);
+ WI(WI_PTR_ADD);
+
+ WIL(WI_LOCAL_GET, source_local);
+ WIL(WI_LOCAL_GET, count_local);
+ WI(WI_PTR_ADD);
+
+ WID(WI_I32_LOAD_8_U, ((WasmInstructionData) { 0, 0 }));
+ WID(WI_I32_STORE_8, ((WasmInstructionData) { 0, 0 }));
+
+ WIL(WI_LOCAL_GET, count_local);
+ WID(WI_I32_CONST, 0);
+ WI(WI_I32_GT_S);
+ WID(WI_COND_JUMP, 0x00);
+
+ WI(WI_LOOP_END);
+ WI(WI_IF_END);
+
+ local_raw_free(mod->local_alloc, WASM_TYPE_INT32);
+ local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+ local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+
+ *pcode = code;
+}
+
+EMIT_FUNC_NO_ARGS(intrinsic_memory_fill) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ // The stack should look like this:
+ // <count>
+ // <byte>
+ // <dest>
+
+ u64 count_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
+ u64 byte_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
+ u64 dest_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+
+ WIL(WI_LOCAL_SET, count_local);
+ WIL(WI_LOCAL_SET, byte_local);
+ WIL(WI_LOCAL_SET, dest_local);
+
+ // count is greater than 0
+ WIL(WI_LOCAL_GET, count_local);
+ WID(WI_I32_CONST, 0);
+ WI(WI_I32_GT_S);
+
+ WID(WI_IF_START, 0x40);
+ WID(WI_LOOP_START, 0x40);
+
+ WIL(WI_LOCAL_GET, count_local);
+ WID(WI_I32_CONST, 1);
+ WI(WI_I32_SUB);
+ WIL(WI_LOCAL_SET, count_local);
+
+ WIL(WI_LOCAL_GET, dest_local);
+ WIL(WI_LOCAL_GET, count_local);
+ WI(WI_PTR_ADD);
+
+ WIL(WI_LOCAL_GET, byte_local);
+ WID(WI_I32_STORE_8, ((WasmInstructionData) { 0, 0 }));
+
+ WIL(WI_LOCAL_GET, count_local);
+ WID(WI_I32_CONST, 0);
+ WI(WI_I32_GT_S);
+ WID(WI_COND_JUMP, 0x00);
+
+ WI(WI_LOOP_END);
+ WI(WI_IF_END);
+
+ local_raw_free(mod->local_alloc, WASM_TYPE_INT32);
+ local_raw_free(mod->local_alloc, WASM_TYPE_INT32);
+ local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+
+ *pcode = code;
+}
+
+EMIT_FUNC(initialize_type, Type* type, OnyxToken* where) {
+ bh_arr(WasmInstruction) code = *pcode;
+
+ switch (type->kind) {
+ case Type_Kind_Pointer:
+ case Type_Kind_Basic: {
+ WasmType basic_type = onyx_type_to_wasm_type(type);
+ emit_zero_value(mod, &code, basic_type);
+ emit_store_instruction(mod, &code, type, 0);
+ break;
+ }
+
+ case Type_Kind_Struct: {
+ u64 value_ptr = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+ WIL(WI_LOCAL_SET, value_ptr);
+
+ bh_arr_each(StructMember *, psmem, type->Struct.memarr) {
+ StructMember* smem = *psmem;
+ if (smem->initial_value == NULL || *smem->initial_value == NULL) continue;
+
+ WIL(WI_LOCAL_GET, value_ptr);
+ emit_expression(mod, &code, *smem->initial_value);
+ emit_store_instruction(mod, &code, smem->type, smem->offset);
+ }
+
+ local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
+ break;
+ }
+
+ default:
+ onyx_report_error(where->pos,
+ "Unable to initialize type, '%s'. The reason for this is largely due to the compiler not knowing what the initial value should be.",
+ type_get_name(type));
+ break;
+ }
+
+ *pcode = code;
+}
--- /dev/null
+// This file is included in src/onyxwasm.c.
+// It is separated because of its fundamentally different goals.
+
+//-------------------------------------------------
+// BINARY OUPUT
+//-------------------------------------------------
+
+#define WASM_SECTION_ID_TYPE 1
+#define WASM_SECTION_ID_IMPORT 2
+#define WASM_SECTION_ID_FUNCTION 3
+#define WASM_SECTION_ID_TABLE 4
+#define WASM_SECTION_ID_MEMORY 5
+#define WASM_SECTION_ID_GLOBAL 6
+#define WASM_SECTION_ID_EXPORT 7
+#define WASM_SECTION_ID_START 8
+#define WASM_SECTION_ID_ELEMENT 9
+#define WASM_SECTION_ID_CODE 10
+#define WASM_SECTION_ID_DATA 11
+
+typedef i32 vector_func(void*, bh_buffer*);
+
+static const u8 WASM_MAGIC_STRING[] = { 0x00, 0x61, 0x73, 0x6D };
+static const u8 WASM_VERSION[] = { 0x01, 0x00, 0x00, 0x00 };
+
+static void output_instruction(WasmFunc* func, WasmInstruction* instr, bh_buffer* buff);
+
+static i32 output_vector(void** arr, i32 stride, i32 arrlen, vector_func elem, bh_buffer* vec_buff) {
+ i32 len;
+ u8* leb = uint_to_uleb128((u64) arrlen, &len);
+ bh_buffer_append(vec_buff, leb, len);
+
+ i32 i = 0;
+ while (i < arrlen) {
+ elem(*arr, vec_buff);
+ arr = bh_pointer_add(arr, stride);
+ i++;
+ }
+
+ return vec_buff->length;
+}
+
+static i32 output_name(const char* start, i32 length, bh_buffer* buff) {
+ i32 leb_len, prev_len = buff->length;
+ u8* leb = uint_to_uleb128((u64) length, &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+ bh_buffer_append(buff, start, length);
+ return buff->length - prev_len;
+}
+
+static i32 output_limits(i32 min, i32 max, bh_buffer* buff) {
+ i32 leb_len, prev_len = buff->length;
+ u8* leb;
+
+ bh_buffer_write_byte(buff, (max >= 0) ? 0x01 : 0x00);
+
+ leb = uint_to_uleb128((u64) min, &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+
+ if (max >= 0) {
+ leb = uint_to_uleb128((u64) max, &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+ }
+
+ return buff->length - prev_len;
+}
+
+static i32 output_functype(WasmFuncType* type, bh_buffer* buff) {
+ i32 prev_len = buff->length;
+
+ bh_buffer_write_byte(buff, 0x60);
+
+ i32 len;
+ u8* leb_buff = uint_to_uleb128(type->param_count, &len);
+ bh_buffer_append(buff, leb_buff, len);
+ bh_buffer_append(buff, type->param_types, type->param_count);
+
+ if (type->return_type != WASM_TYPE_VOID) {
+ bh_buffer_write_byte(buff, 0x01);
+ bh_buffer_write_byte(buff, type->return_type);
+ } else {
+ bh_buffer_write_byte(buff, 0x00);
+ }
+
+ return buff->length - prev_len;
+}
+
+static i32 output_typesection(OnyxWasmModule* module, bh_buffer* buff) {
+ i32 prev_len = buff->length;
+ bh_buffer_write_byte(buff, 0x01);
+
+ bh_buffer vec_buff;
+ bh_buffer_init(&vec_buff, buff->allocator, 128);
+
+ i32 vec_len = output_vector(
+ (void**) module->types,
+ sizeof(WasmFuncType*),
+ bh_arr_length(module->types),
+ (vector_func *) output_functype,
+ &vec_buff);
+
+ i32 leb_len;
+ u8* leb = uint_to_uleb128((u64) vec_len, &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+
+ bh_buffer_concat(buff, vec_buff);
+ bh_buffer_free(&vec_buff);
+
+ return buff->length - prev_len;
+}
+
+static i32 output_funcsection(OnyxWasmModule* module, bh_buffer* buff) {
+ i32 prev_len = buff->length;
+ bh_buffer_write_byte(buff, WASM_SECTION_ID_FUNCTION);
+
+ bh_buffer vec_buff;
+ bh_buffer_init(&vec_buff, buff->allocator, 128);
+
+ i32 leb_len;
+ u8* leb = uint_to_uleb128((u64) (bh_arr_length(module->funcs)), &leb_len);
+ bh_buffer_append(&vec_buff, leb, leb_len);
+
+ bh_arr_each(WasmFunc, func, module->funcs) {
+ leb = uint_to_uleb128((u64) (func->type_idx), &leb_len);
+ bh_buffer_append(&vec_buff, leb, leb_len);
+ }
+
+ leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+
+ bh_buffer_concat(buff, vec_buff);
+ bh_buffer_free(&vec_buff);
+
+ return buff->length - prev_len;
+}
+
+static i32 output_tablesection(OnyxWasmModule* module, bh_buffer* buff) {
+ if (bh_arr_length(module->elems) == 0) return 0;
+
+ i32 prev_len = buff->length;
+ bh_buffer_write_byte(buff, WASM_SECTION_ID_TABLE);
+
+ bh_buffer vec_buff;
+ bh_buffer_init(&vec_buff, buff->allocator, 128);
+
+ i32 leb_len;
+ u8* leb = uint_to_uleb128((u64) 1, &leb_len);
+ bh_buffer_append(&vec_buff, leb, leb_len);
+
+ // NOTE: funcrefs are the only valid table element type
+ bh_buffer_write_byte(&vec_buff, 0x70);
+ output_limits(bh_arr_length(module->elems), -1, &vec_buff);
+
+ leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+
+ bh_buffer_concat(buff, vec_buff);
+ bh_buffer_free(&vec_buff);
+
+ return buff->length - prev_len;
+}
+
+static i32 output_memorysection(OnyxWasmModule* module, bh_buffer* buff) {
+ i32 prev_len = buff->length;
+ bh_buffer_write_byte(buff, WASM_SECTION_ID_MEMORY);
+
+ bh_buffer vec_buff;
+ bh_buffer_init(&vec_buff, buff->allocator, 128);
+
+ i32 leb_len;
+ u8* leb = uint_to_uleb128((u64) 1, &leb_len);
+ bh_buffer_append(&vec_buff, leb, leb_len);
+
+ // FIXME: This needs to be dynamically chosen depending on the size of
+ // the data section and stack size pre-requeseted.
+ // :WasmMemory
+ output_limits(1024, -1, &vec_buff);
+
+ leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+
+ bh_buffer_concat(buff, vec_buff);
+ bh_buffer_free(&vec_buff);
+
+ return buff->length - prev_len;
+}
+
+static i32 output_globalsection(OnyxWasmModule* module, bh_buffer* buff) {
+ i32 prev_len = buff->length;
+ bh_buffer_write_byte(buff, WASM_SECTION_ID_GLOBAL);
+
+ bh_buffer vec_buff;
+ bh_buffer_init(&vec_buff, buff->allocator, 128);
+
+ i32 leb_len;
+ u8* leb = uint_to_uleb128((u64) (bh_arr_length(module->globals)), &leb_len);
+ bh_buffer_append(&vec_buff, leb, leb_len);
+
+ bh_arr_each(WasmGlobal, global, module->globals) {
+ bh_buffer_write_byte(&vec_buff, global->type);
+ bh_buffer_write_byte(&vec_buff, 0x01);
+
+ bh_arr_each(WasmInstruction, instr, global->initial_value)
+ output_instruction(NULL, instr, &vec_buff);
+
+ // NOTE: Initial value expression terminator
+ bh_buffer_write_byte(&vec_buff, (u8) WI_BLOCK_END);
+ }
+
+ leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+
+ bh_buffer_concat(buff, vec_buff);
+ bh_buffer_free(&vec_buff);
+
+ return buff->length - prev_len;
+}
+
+static i32 output_importsection(OnyxWasmModule* module, bh_buffer* buff) {
+ i32 prev_len = buff->length;
+ bh_buffer_write_byte(buff, WASM_SECTION_ID_IMPORT);
+
+ bh_buffer vec_buff;
+ bh_buffer_init(&vec_buff, buff->allocator, 128);
+
+ i32 leb_len;
+ u8* leb = uint_to_uleb128((u64) (bh_arr_length(module->imports)), &leb_len);
+ bh_buffer_append(&vec_buff, leb, leb_len);
+
+ bh_arr_each(WasmImport, import, module->imports) {
+ output_name(import->mod->text, import->mod->length, &vec_buff);
+ output_name(import->name->text, import->name->length, &vec_buff);
+ bh_buffer_write_byte(&vec_buff, (u8) import->kind);
+
+ leb = uint_to_uleb128((u64) import->idx, &leb_len);
+ bh_buffer_append(&vec_buff, leb, leb_len);
+
+ if (import->kind == WASM_FOREIGN_GLOBAL) {
+ // NOTE: All foreign globals are mutable
+ bh_buffer_write_byte(&vec_buff, 0x01);
+ }
+ }
+
+ leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+
+ bh_buffer_concat(buff, vec_buff);
+ bh_buffer_free(&vec_buff);
+
+ return buff->length - prev_len;
+}
+
+static i32 output_exportsection(OnyxWasmModule* module, bh_buffer* buff) {
+ i32 prev_len = buff->length;
+ bh_buffer_write_byte(buff, WASM_SECTION_ID_EXPORT);
+
+ bh_buffer vec_buff;
+ bh_buffer_init(&vec_buff, buff->allocator, 128);
+
+ i32 leb_len;
+ u8* leb = uint_to_uleb128((u64) (module->export_count), &leb_len);
+ bh_buffer_append(&vec_buff, leb, leb_len);
+
+ i32 key_len = 0;
+ bh_table_each_start(WasmExport, module->exports);
+ key_len = strlen(key);
+ output_name(key, key_len, &vec_buff);
+
+ bh_buffer_write_byte(&vec_buff, (u8) (value.kind));
+ leb = uint_to_uleb128((u64) value.idx, &leb_len);
+ bh_buffer_append(&vec_buff, leb, leb_len);
+ bh_table_each_end;
+
+ leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+
+ bh_buffer_concat(buff, vec_buff);
+ bh_buffer_free(&vec_buff);
+
+ return buff->length - prev_len;
+}
+
+static i32 output_startsection(OnyxWasmModule* module, bh_buffer* buff) {
+ i32 prev_len = buff->length;
+
+ i32 start_idx = -1;
+ bh_table_each_start(WasmExport, module->exports) {
+ if (value.kind == WASM_FOREIGN_FUNCTION) {
+ if (strncmp("main", key, 5) == 0) {
+ start_idx = value.idx;
+ break;
+ }
+ }
+ } bh_table_each_end;
+
+ if (start_idx != -1) {
+ bh_buffer_write_byte(buff, WASM_SECTION_ID_START);
+
+ i32 start_leb_len, section_leb_len;
+ uint_to_uleb128((u64) start_idx, &start_leb_len);
+ u8* section_leb = uint_to_uleb128((u64) start_leb_len, §ion_leb_len);
+ bh_buffer_append(buff, section_leb, section_leb_len);
+
+ u8* start_leb = uint_to_uleb128((u64) start_idx, &start_leb_len);
+ bh_buffer_append(buff, start_leb, start_leb_len);
+ }
+
+ return buff->length - prev_len;
+}
+
+static i32 output_elemsection(OnyxWasmModule* module, bh_buffer* buff) {
+ if (bh_arr_length(module->elems) == 0) return 0;
+
+ i32 prev_len = buff->length;
+
+ bh_buffer_write_byte(buff, WASM_SECTION_ID_ELEMENT);
+
+ bh_buffer vec_buff;
+ bh_buffer_init(&vec_buff, buff->allocator, 128);
+
+ i32 leb_len;
+ u8* leb;
+
+ // NOTE: 0x01 count of elems
+ bh_buffer_write_byte(&vec_buff, 0x01);
+
+ // NOTE: 0x00 table index
+ bh_buffer_write_byte(&vec_buff, 0x00);
+
+ bh_buffer_write_byte(&vec_buff, WI_I32_CONST);
+ bh_buffer_write_byte(&vec_buff, 0x00);
+ bh_buffer_write_byte(&vec_buff, WI_BLOCK_END);
+
+ leb = uint_to_uleb128((u64) bh_arr_length(module->elems), &leb_len);
+ bh_buffer_append(&vec_buff, leb, leb_len);
+
+ bh_arr_each(i32, elem, module->elems) {
+ leb = uint_to_uleb128((u64) *elem, &leb_len);
+ bh_buffer_append(&vec_buff, leb, leb_len);
+ }
+
+ leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+
+ bh_buffer_concat(buff, vec_buff);
+ bh_buffer_free(&vec_buff);
+
+ return buff->length - prev_len;
+}
+
+static i32 output_locals(WasmFunc* func, bh_buffer* buff) {
+ i32 prev_len = buff->length;
+
+ // NOTE: Output vector length
+ i32 total_locals =
+ (i32) (func->locals.allocated[0] != 0) +
+ (i32) (func->locals.allocated[1] != 0) +
+ (i32) (func->locals.allocated[2] != 0) +
+ (i32) (func->locals.allocated[3] != 0) +
+ (i32) (func->locals.allocated[4] != 0);
+
+ i32 leb_len;
+ u8* leb = uint_to_uleb128((u64) total_locals, &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+
+ if (func->locals.allocated[0] != 0) {
+ leb = uint_to_uleb128((u64) func->locals.allocated[0], &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+ bh_buffer_write_byte(buff, WASM_TYPE_INT32);
+ }
+ if (func->locals.allocated[1] != 0) {
+ leb = uint_to_uleb128((u64) func->locals.allocated[1], &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+ bh_buffer_write_byte(buff, WASM_TYPE_INT64);
+ }
+ if (func->locals.allocated[2] != 0) {
+ leb = uint_to_uleb128((u64) func->locals.allocated[2], &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+ bh_buffer_write_byte(buff, WASM_TYPE_FLOAT32);
+ }
+ if (func->locals.allocated[3] != 0) {
+ leb = uint_to_uleb128((u64) func->locals.allocated[3], &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+ bh_buffer_write_byte(buff, WASM_TYPE_FLOAT64);
+ }
+ if (func->locals.allocated[4] != 0) {
+ leb = uint_to_uleb128((u64) func->locals.allocated[4], &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+ bh_buffer_write_byte(buff, WASM_TYPE_VAR128);
+ }
+
+ return buff->length - prev_len;
+}
+
+static void output_instruction(WasmFunc* func, WasmInstruction* instr, bh_buffer* buff) {
+ i32 leb_len;
+ u8* leb;
+
+ if (instr->type == WI_NOP) return;
+
+ if (instr->type & SIMD_INSTR_MASK) {
+ bh_buffer_write_byte(buff, 0xFD);
+ leb = uint_to_uleb128((u64) (instr->type &~ SIMD_INSTR_MASK), &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+
+ } else if (instr->type & EXT_INSTR_MASK) {
+ bh_buffer_write_byte(buff, 0xFC);
+ leb = uint_to_uleb128((u64) (instr->type &~ EXT_INSTR_MASK), &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+
+ } else {
+ bh_buffer_write_byte(buff, (u8) instr->type);
+ }
+
+ switch (instr->type) {
+ case WI_LOCAL_GET:
+ case WI_LOCAL_SET:
+ case WI_LOCAL_TEE: {
+ u64 actual_idx = local_lookup_idx(&func->locals, instr->data.l);
+ leb = uint_to_uleb128(actual_idx, &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+ break;
+ }
+
+ case WI_GLOBAL_GET:
+ case WI_GLOBAL_SET:
+ case WI_CALL:
+ case WI_BLOCK_START:
+ case WI_LOOP_START:
+ case WI_JUMP:
+ case WI_COND_JUMP:
+ case WI_IF_START:
+ case WI_MEMORY_SIZE:
+ case WI_MEMORY_GROW:
+ case WI_MEMORY_FILL:
+ leb = uint_to_uleb128((u64) instr->data.i1, &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+ break;
+
+ case WI_MEMORY_COPY:
+ leb = uint_to_uleb128((u64) instr->data.i1, &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+
+ leb = uint_to_uleb128((u64) instr->data.i2, &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+ break;
+
+ case WI_JUMP_TABLE: {
+ BranchTable* bt = (BranchTable *) instr->data.p;
+
+ leb = uint_to_uleb128((u64) bt->count, &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+
+ fori (i, 0, bt->count) {
+ leb = uint_to_uleb128((u64) bt->cases[i], &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+ }
+
+ leb = uint_to_uleb128((u64) bt->default_case, &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+ break;
+ }
+
+
+ case WI_CALL_INDIRECT:
+ case WI_I32_STORE: case WI_I32_STORE_8: case WI_I32_STORE_16:
+ case WI_I64_STORE: case WI_I64_STORE_8: case WI_I64_STORE_16: case WI_I64_STORE_32:
+ case WI_F32_STORE: case WI_F64_STORE:
+ case WI_V128_STORE:
+ case WI_I32_LOAD:
+ case WI_I32_LOAD_8_S: case WI_I32_LOAD_8_U:
+ case WI_I32_LOAD_16_S: case WI_I32_LOAD_16_U:
+ case WI_I64_LOAD:
+ case WI_I64_LOAD_8_S: case WI_I64_LOAD_8_U:
+ case WI_I64_LOAD_16_S: case WI_I64_LOAD_16_U:
+ case WI_I64_LOAD_32_S: case WI_I64_LOAD_32_U:
+ case WI_F32_LOAD: case WI_F64_LOAD:
+ case WI_V128_LOAD:
+ leb = uint_to_uleb128((u64) instr->data.i1, &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+ leb = uint_to_uleb128((u64) instr->data.i2, &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+ break;
+
+ case WI_I32_CONST:
+ leb = int_to_leb128((i64) instr->data.i1, &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+ break;
+ case WI_I64_CONST:
+ leb = int_to_leb128((i64) instr->data.l, &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+ break;
+ case WI_F32_CONST:
+ leb = float_to_ieee754(instr->data.f, 0);
+ bh_buffer_append(buff, leb, 4);
+ break;
+ case WI_F64_CONST:
+ leb = double_to_ieee754(instr->data.d, 0);
+ bh_buffer_append(buff, leb, 8);
+ break;
+
+ case WI_V128_CONST:
+ case WI_I8X16_SHUFFLE:
+ fori (i, 0, 16) bh_buffer_write_byte(buff, ((u8*) instr->data.p)[i]);
+ break;
+
+ case WI_I8X16_EXTRACT_LANE_S: case WI_I8X16_EXTRACT_LANE_U: case WI_I8X16_REPLACE_LANE:
+ case WI_I16X8_EXTRACT_LANE_S: case WI_I16X8_EXTRACT_LANE_U: case WI_I16X8_REPLACE_LANE:
+ case WI_I32X4_EXTRACT_LANE: case WI_I32X4_REPLACE_LANE:
+ case WI_I64X2_EXTRACT_LANE: case WI_I64X2_REPLACE_LANE:
+ case WI_F32X4_EXTRACT_LANE: case WI_F32X4_REPLACE_LANE:
+ case WI_F64X2_EXTRACT_LANE: case WI_F64X2_REPLACE_LANE:
+ bh_buffer_write_byte(buff, (u8) instr->data.i1);
+ break;
+
+ default: break;
+ }
+}
+
+static i32 output_code(WasmFunc* func, bh_buffer* buff) {
+
+ bh_buffer code_buff;
+ bh_buffer_init(&code_buff, buff->allocator, 128);
+
+ // Output locals
+ output_locals(func, &code_buff);
+
+ // Output code
+ bh_arr_each(WasmInstruction, instr, func->code) output_instruction(func, instr, &code_buff);
+
+ i32 leb_len;
+ u8* leb = uint_to_uleb128((u64) code_buff.length, &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+
+ bh_buffer_concat(buff, code_buff);
+ bh_buffer_free(&code_buff);
+
+ return 0;
+}
+
+static i32 output_codesection(OnyxWasmModule* module, bh_buffer* buff) {
+ i32 prev_len = buff->length;
+
+ bh_buffer_write_byte(buff, WASM_SECTION_ID_CODE);
+
+ bh_buffer vec_buff;
+ bh_buffer_init(&vec_buff, buff->allocator, 128);
+
+ i32 leb_len;
+ u8* leb = uint_to_uleb128((u64) bh_arr_length(module->funcs), &leb_len);
+ bh_buffer_append(&vec_buff, leb, leb_len);
+
+ // DEBUG_HERE;
+
+ bh_arr_each(WasmFunc, func, module->funcs) output_code(func, &vec_buff);
+
+ leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+
+ bh_buffer_concat(buff, vec_buff);
+ bh_buffer_free(&vec_buff);
+
+ return buff->length - prev_len;
+}
+
+static i32 output_datasection(OnyxWasmModule* module, bh_buffer* buff) {
+ i32 prev_len = buff->length;
+
+ bh_buffer_write_byte(buff, WASM_SECTION_ID_DATA);
+
+ bh_buffer vec_buff;
+ bh_buffer_init(&vec_buff, buff->allocator, 128);
+
+ i32 leb_len;
+ u8* leb = uint_to_uleb128((u64) bh_arr_length(module->data), &leb_len);
+ bh_buffer_append(&vec_buff, leb, leb_len);
+
+ bh_arr_each(WasmDatum, datum, module->data) {
+ if (datum->data == NULL) continue;
+
+ // NOTE: 0x00 memory index
+ bh_buffer_write_byte(&vec_buff, 0x00);
+
+ bh_buffer_write_byte(&vec_buff, WI_I32_CONST);
+ leb = int_to_leb128((i64) datum->offset, &leb_len);
+ bh_buffer_append(&vec_buff, leb, leb_len);
+ bh_buffer_write_byte(&vec_buff, WI_BLOCK_END);
+
+ leb = uint_to_uleb128((u64) datum->length, &leb_len);
+ bh_buffer_append(&vec_buff, leb, leb_len);
+ fori (i, 0, datum->length) bh_buffer_write_byte(&vec_buff, ((u8 *) datum->data)[i]);
+ }
+
+ leb = uint_to_uleb128((u64) (vec_buff.length), &leb_len);
+ bh_buffer_append(buff, leb, leb_len);
+
+ bh_buffer_concat(buff, vec_buff);
+ bh_buffer_free(&vec_buff);
+
+ return buff->length - prev_len;
+}
+
+void onyx_wasm_module_write_to_file(OnyxWasmModule* module, bh_file file) {
+ bh_buffer master_buffer;
+ bh_buffer_init(&master_buffer, global_heap_allocator, 128);
+ bh_buffer_append(&master_buffer, WASM_MAGIC_STRING, 4);
+ bh_buffer_append(&master_buffer, WASM_VERSION, 4);
+
+ output_typesection(module, &master_buffer);
+ output_importsection(module, &master_buffer);
+ output_funcsection(module, &master_buffer);
+ output_tablesection(module, &master_buffer);
+ output_memorysection(module, &master_buffer);
+ output_globalsection(module, &master_buffer);
+ output_exportsection(module, &master_buffer);
+ output_startsection(module, &master_buffer);
+ output_elemsection(module, &master_buffer);
+ output_codesection(module, &master_buffer);
+ output_datasection(module, &master_buffer);
+
+ bh_file_write(&file, master_buffer.data, master_buffer.length);
+}
--- /dev/null
+// This file is directly included in src/onxywasm.c
+// It is here purely to decrease the amount of clutter in the main file.
+
+
+u64 build_type_table(OnyxWasmModule* module) {
+
+ bh_arr(u32) base_patch_locations=NULL;
+ bh_arr_new(global_heap_allocator, base_patch_locations, 256);
+
+#define PATCH (bh_arr_push(base_patch_locations, table_buffer.length))
+
+ // This is the data behind the "type_table" slice in type_info.onyx
+ u32 type_count = bh_arr_length(type_map.entries) + 1;
+ u64* table_info = bh_alloc_array(global_heap_allocator, u64, type_count); // HACK
+ memset(table_info, 0, type_count * sizeof(u64));
+
+ bh_buffer table_buffer;
+ bh_buffer_init(&table_buffer, global_heap_allocator, 4096);
+
+ // Write a "NULL" at the beginning so nothing will have to point to the first byte of the buffer.
+ bh_buffer_write_u64(&table_buffer, 0);
+
+ bh_arr_each(bh__imap_entry, type_entry, type_map.entries) {
+ u64 type_idx = type_entry->key;
+ Type* type = (Type *) type_entry->value;
+
+ switch (type->kind) {
+ case Type_Kind_Basic: {
+ table_info[type_idx] = table_buffer.length;
+ bh_buffer_write_u32(&table_buffer, type->kind);
+ bh_buffer_write_u32(&table_buffer, type_size_of(type));
+ bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
+ bh_buffer_write_u32(&table_buffer, type->Basic.kind);
+ break;
+ }
+
+ case Type_Kind_Pointer: {
+ table_info[type_idx] = table_buffer.length;
+ bh_buffer_write_u32(&table_buffer, type->kind);
+ bh_buffer_write_u32(&table_buffer, type_size_of(type));
+ bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
+ bh_buffer_write_u32(&table_buffer, type->Pointer.elem->id);
+ break;
+ }
+
+ case Type_Kind_Array: {
+ table_info[type_idx] = table_buffer.length;
+ bh_buffer_write_u32(&table_buffer, type->kind);
+ bh_buffer_write_u32(&table_buffer, type_size_of(type));
+ bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
+ bh_buffer_write_u32(&table_buffer, type->Array.elem->id);
+ bh_buffer_write_u32(&table_buffer, type->Array.count);
+ break;
+ }
+
+ case Type_Kind_Slice: {
+ table_info[type_idx] = table_buffer.length;
+ bh_buffer_write_u32(&table_buffer, type->kind);
+ bh_buffer_write_u32(&table_buffer, type_size_of(type));
+ bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
+ bh_buffer_write_u32(&table_buffer, type->Slice.ptr_to_data->Pointer.elem->id);
+ break;
+ }
+
+ case Type_Kind_DynArray: {
+ table_info[type_idx] = table_buffer.length;
+ bh_buffer_write_u32(&table_buffer, type->kind);
+ bh_buffer_write_u32(&table_buffer, type_size_of(type));
+ bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
+ bh_buffer_write_u32(&table_buffer, type->DynArray.ptr_to_data->Pointer.elem->id);
+ break;
+ }
+
+ case Type_Kind_VarArgs: {
+ table_info[type_idx] = table_buffer.length;
+ bh_buffer_write_u32(&table_buffer, type->kind);
+ bh_buffer_write_u32(&table_buffer, type_size_of(type));
+ bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
+ bh_buffer_write_u32(&table_buffer, type->VarArgs.ptr_to_data->Pointer.elem->id);
+ break;
+ }
+
+ case Type_Kind_Compound: {
+ u32 components_base = table_buffer.length;
+
+ u32 components_count = type->Compound.count;
+ fori (i, 0, components_count) {
+ u32 type_idx = type->Compound.types[i]->id;
+ bh_buffer_write_u32(&table_buffer, type_idx);
+ }
+
+ bh_buffer_align(&table_buffer, 8);
+ table_info[type_idx] = table_buffer.length;
+ bh_buffer_write_u32(&table_buffer, type->kind);
+ bh_buffer_write_u32(&table_buffer, type_size_of(type));
+ bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
+ bh_buffer_align(&table_buffer, 8);
+ PATCH;
+ bh_buffer_write_u64(&table_buffer, components_base);
+ bh_buffer_write_u64(&table_buffer, components_count);
+ break;
+ }
+
+ case Type_Kind_Function: {
+ u32 parameters_base = table_buffer.length;
+
+ u32 parameters_count = type->Function.param_count;
+ fori (i, 0, parameters_count) {
+ u32 type_idx = type->Function.params[i]->id;
+ bh_buffer_write_u32(&table_buffer, type_idx);
+ }
+
+ table_info[type_idx] = table_buffer.length;
+ bh_buffer_write_u32(&table_buffer, type->kind);
+ bh_buffer_write_u32(&table_buffer, type_size_of(type));
+ bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
+ bh_buffer_write_u32(&table_buffer, type->Function.return_type->id);
+
+ PATCH;
+ bh_buffer_write_u64(&table_buffer, parameters_base);
+ bh_buffer_write_u64(&table_buffer, parameters_count);
+
+ bh_buffer_write_u32(&table_buffer, type->Function.vararg_arg_pos > 0 ? 1 : 0);
+ break;
+ }
+
+ case Type_Kind_Enum: {
+ AstEnumType* ast_enum = (AstEnumType *) type->ast_type;
+ u32 member_count = bh_arr_length(ast_enum->values);
+ u32* name_locations = bh_alloc_array(global_scratch_allocator, u32, member_count);
+
+ u32 i = 0;
+ bh_arr_each(AstEnumValue *, value, ast_enum->values) {
+ name_locations[i++] = table_buffer.length;
+
+ bh_buffer_append(&table_buffer, (*value)->token->text, (*value)->token->length);
+ }
+ bh_buffer_align(&table_buffer, 8);
+
+ u32 member_base = table_buffer.length;
+ i = 0;
+ bh_arr_each(AstEnumValue *, value, ast_enum->values) {
+ u32 name_loc = name_locations[i++];
+
+ bh_buffer_align(&table_buffer, 8);
+ PATCH;
+ bh_buffer_write_u64(&table_buffer, name_loc);
+ bh_buffer_write_u64(&table_buffer, (*value)->token->length);
+ bh_buffer_write_u64(&table_buffer, (*value)->value->value.l);
+ }
+
+ u32 name_base = table_buffer.length;
+ u32 name_length = strlen(type->Enum.name);
+ bh_buffer_append(&table_buffer, type->Enum.name, name_length);
+ bh_buffer_align(&table_buffer, 8);
+
+ table_info[type_idx] = table_buffer.length;
+ bh_buffer_write_u32(&table_buffer, type->kind);
+ bh_buffer_write_u32(&table_buffer, type_size_of(type));
+ bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
+ bh_buffer_write_u32(&table_buffer, type->Enum.backing->id);
+ PATCH;
+ bh_buffer_write_u64(&table_buffer, name_base);
+ bh_buffer_write_u64(&table_buffer, name_length);
+ PATCH;
+ bh_buffer_write_u64(&table_buffer, member_base);
+ bh_buffer_write_u64(&table_buffer, member_count);
+ bh_buffer_write_u32(&table_buffer, type->Enum.is_flags ? 1 : 0);
+ break;
+ }
+
+ case Type_Kind_Struct: {
+ TypeStruct* s = &type->Struct;
+ u32* name_locations = bh_alloc_array(global_scratch_allocator, u32, s->mem_count);
+ u32* param_locations = bh_alloc_array(global_scratch_allocator, u32, bh_arr_length(s->poly_sln));
+ u32* value_locations = bh_alloc_array(global_scratch_allocator, u32, s->mem_count);
+ memset(value_locations, 0, s->mem_count * sizeof(u32));
+
+ u32 i = 0;
+ bh_arr_each(StructMember*, pmem, s->memarr) {
+ StructMember* mem = *pmem;
+
+ name_locations[i++] = table_buffer.length;
+ bh_buffer_append(&table_buffer, mem->name, strlen(mem->name));
+ }
+
+ bh_buffer_align(&table_buffer, 8);
+
+ i = 0;
+ bh_arr_each(AstPolySolution, sln, s->poly_sln) {
+ bh_buffer_align(&table_buffer, 8);
+ param_locations[i++] = table_buffer.length;
+
+ switch (sln->kind) {
+ case PSK_Type: {
+ // NOTE: This assumes a little endian compiler (which is assumed in other part of the code too)
+ bh_buffer_append(&table_buffer, &sln->type->id, 4);
+ break;
+ }
+
+ case PSK_Value: {
+ assert(sln->value->type);
+ u32 size = type_size_of(sln->value->type);
+
+ bh_buffer_grow(&table_buffer, table_buffer.length + size);
+ u8* buffer = table_buffer.data + table_buffer.length;
+ emit_raw_data(module, buffer, sln->value);
+ table_buffer.length += size;
+ break;
+ }
+
+ default: {
+ // Set to null if this is not known how to encode
+ param_locations[i-1] = 0;
+ break;
+ }
+ }
+ }
+
+ bh_buffer_align(&table_buffer, 8);
+
+ i = 0;
+ bh_arr_each(StructMember*, pmem, s->memarr) {
+ StructMember* mem = *pmem;
+
+ if (mem->initial_value == NULL || *mem->initial_value == NULL) {
+ i++;
+ continue;
+ }
+
+ AstTyped* value = *mem->initial_value;
+ assert(value->type);
+
+ if ((value->flags & Ast_Flag_Comptime) == 0) {
+ // onyx_report_warning(value->token->pos, "Warning: skipping generating default value for '%s' in '%s' because it is not compile-time known.\n", mem->name, s->name);
+ i++;
+ continue;
+ }
+
+ u32 size = type_size_of(value->type);
+ bh_buffer_align(&table_buffer, type_alignment_of(value->type));
+
+ bh_buffer_grow(&table_buffer, table_buffer.length + size);
+ u8* buffer = table_buffer.data + table_buffer.length;
+
+ if (!emit_raw_data_(module, buffer, value)) {
+ // Failed to generate raw data
+ // onyx_report_warning(value->token->pos, "Warning: failed to generate default value for '%s' in '%s'.\n", mem->name, s->name);
+ value_locations[i++] = 0;
+
+ } else {
+ // Success
+ value_locations[i++] = table_buffer.length;
+ table_buffer.length += size;
+ }
+ }
+
+ bh_buffer_align(&table_buffer, 8);
+ u32 members_base = table_buffer.length;
+
+ i = 0;
+ bh_arr_each(StructMember*, pmem, s->memarr) {
+ StructMember* mem = *pmem;
+
+ u32 name_loc = name_locations[i];
+ u32 value_loc = value_locations[i++];
+
+ bh_buffer_align(&table_buffer, 8);
+ PATCH;
+ bh_buffer_write_u64(&table_buffer, name_loc);
+ bh_buffer_write_u64(&table_buffer, strlen(mem->name));
+ bh_buffer_write_u32(&table_buffer, mem->offset);
+ bh_buffer_write_u32(&table_buffer, mem->type->id);
+ bh_buffer_write_byte(&table_buffer, mem->used ? 1 : 0);
+
+ bh_buffer_align(&table_buffer, 8);
+ PATCH;
+ bh_buffer_write_u64(&table_buffer, value_loc);
+ }
+
+ bh_buffer_align(&table_buffer, 8);
+ u32 params_base = table_buffer.length;
+
+ i = 0;
+ bh_arr_each(AstPolySolution, sln, s->poly_sln) {
+ bh_buffer_align(&table_buffer, 8);
+ PATCH;
+ bh_buffer_write_u64(&table_buffer, param_locations[i++]);
+
+ if (sln->kind == PSK_Type) bh_buffer_write_u32(&table_buffer, basic_types[Basic_Kind_Type_Index].id);
+ else bh_buffer_write_u32(&table_buffer, sln->value->type->id);
+ }
+
+ u32 name_base = 0;
+ u32 name_length = 0;
+ if (s->name) {
+ name_length = strlen(s->name);
+ name_base = table_buffer.length;
+ bh_buffer_append(&table_buffer, s->name, name_length);
+ }
+
+ bh_buffer_align(&table_buffer, 8);
+ table_info[type_idx] = table_buffer.length;
+ bh_buffer_write_u32(&table_buffer, type->kind);
+ bh_buffer_write_u32(&table_buffer, type_size_of(type));
+ bh_buffer_write_u32(&table_buffer, type_alignment_of(type));
+ bh_buffer_write_u32(&table_buffer, 0);
+ PATCH;
+ bh_buffer_write_u64(&table_buffer, name_base);
+ bh_buffer_write_u64(&table_buffer, name_length);
+ PATCH;
+ bh_buffer_write_u64(&table_buffer, members_base);
+ bh_buffer_write_u64(&table_buffer, s->mem_count);
+ PATCH;
+ bh_buffer_write_u64(&table_buffer, params_base);
+ bh_buffer_write_u64(&table_buffer, bh_arr_length(s->poly_sln));
+
+ break;
+ }
+ }
+ }
+
+ u32 offset = module->next_datum_offset;
+ bh_align(offset, 8);
+
+ u64 type_table_location = offset;
+
+ WasmDatum type_table_data = {
+ .offset = offset,
+ .length = type_count * 8,
+ .data = table_info,
+ };
+ bh_arr_push(module->data, type_table_data);
+
+ offset += type_table_data.length;
+
+ fori (i, 0, type_count) {
+ table_info[i] += offset;
+ }
+
+ bh_arr_each(u32, patch_loc, base_patch_locations) {
+ u64* loc = bh_pointer_add(table_buffer.data, *patch_loc);
+ if (*loc == 0) continue;
+
+ *loc += offset;
+ }
+
+ WasmDatum type_info_data = {
+ .offset = offset,
+ .length = table_buffer.length,
+ .data = table_buffer.data,
+ };
+ bh_arr_push(module->data, type_info_data);
+ offset += type_info_data.length;
+
+ u64 global_data_ptr = offset;
+
+ u64* tmp_data = bh_alloc(global_heap_allocator, 16);
+ tmp_data[0] = type_table_location;
+ tmp_data[1] = type_count;
+ WasmDatum type_table_global_data = {
+ .offset = offset,
+ .length = 16,
+ .data = tmp_data,
+ };
+ bh_arr_push(module->data, type_table_global_data);
+ offset += type_table_global_data.length;
+
+ module->next_datum_offset = offset;
+
+ return global_data_ptr;
+
+#undef PATCH
+}