From: Brendan Hansen Date: Wed, 8 Sep 2021 13:16:46 +0000 (-0500) Subject: renamed the compiler source files. X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=36b9586b3a370ee1e909f3f600d7cfd875b806e7;p=onyx.git renamed the compiler source files. --- diff --git a/bin/onyx b/bin/onyx index 8a4f175d..4a8dd734 100755 Binary files a/bin/onyx and b/bin/onyx differ diff --git a/build.bat b/build.bat index 730d3119..4c826e11 100644 --- a/build.bat +++ b/build.bat @@ -1,6 +1,6 @@ @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 @@ -13,4 +13,4 @@ del *.ilk > NUL 2> NUL 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 diff --git a/build.sh b/build.sh index 1558a98c..41bde8ac 100755 --- a/build.sh +++ b/build.sh @@ -7,7 +7,7 @@ sudo cp -r ./core/ "$CORE_DIR" [ "$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' diff --git a/docs/bugs b/docs/bugs index 209cc77a..0cca7527 100644 --- a/docs/bugs +++ b/docs/bugs @@ -8,6 +8,10 @@ List of known bugs: 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: diff --git a/include/astnodes.h b/include/astnodes.h new file mode 100644 index 00000000..e15aef54 --- /dev/null +++ b/include/astnodes.h @@ -0,0 +1,1448 @@ +#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 diff --git a/include/doc.h b/include/doc.h new file mode 100644 index 00000000..75d4bfc5 --- /dev/null +++ b/include/doc.h @@ -0,0 +1,38 @@ +#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 diff --git a/include/errors.h b/include/errors.h new file mode 100644 index 00000000..41aebdde --- /dev/null +++ b/include/errors.h @@ -0,0 +1,33 @@ +#ifndef ONYXERRORS_H +#define ONYXERRORS_H + +#include "bh.h" +#include "lex.h" + +#include + +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 diff --git a/include/lex.h b/include/lex.h new file mode 100644 index 00000000..43fed670 --- /dev/null +++ b/include/lex.h @@ -0,0 +1,121 @@ +#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 diff --git a/include/onyxastnodes.h b/include/onyxastnodes.h deleted file mode 100644 index a4f5f556..00000000 --- a/include/onyxastnodes.h +++ /dev/null @@ -1,1448 +0,0 @@ -#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 diff --git a/include/onyxdoc.h b/include/onyxdoc.h deleted file mode 100644 index 0e6e505c..00000000 --- a/include/onyxdoc.h +++ /dev/null @@ -1,38 +0,0 @@ -#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 diff --git a/include/onyxerrors.h b/include/onyxerrors.h deleted file mode 100644 index 993f4f70..00000000 --- a/include/onyxerrors.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef ONYXERRORS_H -#define ONYXERRORS_H - -#include "bh.h" -#include "onyxlex.h" - -#include - -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 diff --git a/include/onyxlex.h b/include/onyxlex.h deleted file mode 100644 index 43fed670..00000000 --- a/include/onyxlex.h +++ /dev/null @@ -1,121 +0,0 @@ -#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 diff --git a/include/onyxparser.h b/include/onyxparser.h deleted file mode 100644 index 712ac282..00000000 --- a/include/onyxparser.h +++ /dev/null @@ -1,42 +0,0 @@ -#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 diff --git a/include/onyxtypes.h b/include/onyxtypes.h deleted file mode 100644 index 03574c74..00000000 --- a/include/onyxtypes.h +++ /dev/null @@ -1,221 +0,0 @@ -#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 diff --git a/include/onyxutils.h b/include/onyxutils.h deleted file mode 100644 index 96957887..00000000 --- a/include/onyxutils.h +++ /dev/null @@ -1,42 +0,0 @@ -#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; diff --git a/include/onyxwasm.h b/include/onyxwasm.h deleted file mode 100644 index b423403d..00000000 --- a/include/onyxwasm.h +++ /dev/null @@ -1,592 +0,0 @@ -#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 diff --git a/include/parser.h b/include/parser.h new file mode 100644 index 00000000..bfad9417 --- /dev/null +++ b/include/parser.h @@ -0,0 +1,42 @@ +#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 diff --git a/include/types.h b/include/types.h new file mode 100644 index 00000000..03574c74 --- /dev/null +++ b/include/types.h @@ -0,0 +1,221 @@ +#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 diff --git a/include/utils.h b/include/utils.h new file mode 100644 index 00000000..48c58f5f --- /dev/null +++ b/include/utils.h @@ -0,0 +1,42 @@ +#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; diff --git a/include/wasm.h b/include/wasm.h new file mode 100644 index 00000000..7d0ddc30 --- /dev/null +++ b/include/wasm.h @@ -0,0 +1,592 @@ +#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 diff --git a/src/astnodes.c b/src/astnodes.c new file mode 100644 index 00000000..7cdbc445 --- /dev/null +++ b/src/astnodes.c @@ -0,0 +1,1046 @@ +#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 ""; + + 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 ""; +} + +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; +} diff --git a/src/builtins.c b/src/builtins.c new file mode 100644 index 00000000..7be8a782 --- /dev/null +++ b/src/builtins.c @@ -0,0 +1,435 @@ +#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); +} diff --git a/src/checker.c b/src/checker.c new file mode 100644 index 00000000..0e595ae1 --- /dev/null +++ b/src/checker.c @@ -0,0 +1,2370 @@ +#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; + } +} diff --git a/src/clone.c b/src/clone.c new file mode 100644 index 00000000..ac4a8932 --- /dev/null +++ b/src/clone.c @@ -0,0 +1,496 @@ +#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); +} diff --git a/src/doc.c b/src/doc.c new file mode 100644 index 00000000..b6e06c32 --- /dev/null +++ b/src/doc.c @@ -0,0 +1,286 @@ +#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, " = ", 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, "", 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); +} + diff --git a/src/entities.c b/src/entities.c new file mode 100644 index 00000000..ed209a28 --- /dev/null +++ b/src/entities.c @@ -0,0 +1,354 @@ +#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; +} diff --git a/src/errors.c b/src/errors.c new file mode 100644 index 00000000..0a074521 --- /dev/null +++ b/src/errors.c @@ -0,0 +1,117 @@ +#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); +} diff --git a/src/lex.c b/src/lex.c new file mode 100644 index 00000000..a1f1ac3f --- /dev/null +++ b/src/lex.c @@ -0,0 +1,504 @@ +#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; +} diff --git a/src/onyx.c b/src/onyx.c index 0352896c..6f1557d2 100644 --- a/src/onyx.c +++ b/src/onyx.c @@ -2,12 +2,12 @@ #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" diff --git a/src/onyxastnodes.c b/src/onyxastnodes.c deleted file mode 100644 index 666a2038..00000000 --- a/src/onyxastnodes.c +++ /dev/null @@ -1,1046 +0,0 @@ -#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 ""; - - 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 ""; -} - -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; -} diff --git a/src/onyxbuiltins.c b/src/onyxbuiltins.c deleted file mode 100644 index 8731a619..00000000 --- a/src/onyxbuiltins.c +++ /dev/null @@ -1,435 +0,0 @@ -#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); -} diff --git a/src/onyxchecker.c b/src/onyxchecker.c deleted file mode 100644 index e2edab7a..00000000 --- a/src/onyxchecker.c +++ /dev/null @@ -1,2370 +0,0 @@ -#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; - } -} diff --git a/src/onyxclone.c b/src/onyxclone.c deleted file mode 100644 index 8940a035..00000000 --- a/src/onyxclone.c +++ /dev/null @@ -1,496 +0,0 @@ -#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); -} diff --git a/src/onyxdoc.c b/src/onyxdoc.c deleted file mode 100644 index ab1bc9dc..00000000 --- a/src/onyxdoc.c +++ /dev/null @@ -1,286 +0,0 @@ -#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, " = ", 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, "", 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); -} - diff --git a/src/onyxentities.c b/src/onyxentities.c deleted file mode 100644 index 7a938621..00000000 --- a/src/onyxentities.c +++ /dev/null @@ -1,354 +0,0 @@ -#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; -} diff --git a/src/onyxerrors.c b/src/onyxerrors.c deleted file mode 100644 index d6ad29cd..00000000 --- a/src/onyxerrors.c +++ /dev/null @@ -1,117 +0,0 @@ -#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); -} diff --git a/src/onyxlex.c b/src/onyxlex.c deleted file mode 100644 index eefcdd16..00000000 --- a/src/onyxlex.c +++ /dev/null @@ -1,504 +0,0 @@ -#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; -} diff --git a/src/onyxparser.c b/src/onyxparser.c deleted file mode 100644 index 1c60685f..00000000 --- a/src/onyxparser.c +++ /dev/null @@ -1,2865 +0,0 @@ -// 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; -} diff --git a/src/onyxsymres.c b/src/onyxsymres.c deleted file mode 100644 index b048e6c8..00000000 --- a/src/onyxsymres.c +++ /dev/null @@ -1,1252 +0,0 @@ -#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; -} diff --git a/src/onyxtypes.c b/src/onyxtypes.c deleted file mode 100644 index ed4f955e..00000000 --- a/src/onyxtypes.c +++ /dev/null @@ -1,1268 +0,0 @@ -#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", "", 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", "", 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 ""; - case Type_Kind_Enum: - if (type->Enum.name) - return type->Enum.name; - else - return ""; - - 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; -} diff --git a/src/onyxutils.c b/src/onyxutils.c deleted file mode 100644 index e53256ce..00000000 --- a/src/onyxutils.c +++ /dev/null @@ -1,1753 +0,0 @@ -#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, "", 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 -} - diff --git a/src/onyxwasm.c b/src/onyxwasm.c deleted file mode 100644 index beeddfb9..00000000 --- a/src/onyxwasm.c +++ /dev/null @@ -1,3672 +0,0 @@ -// -// 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, - // - // - // 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" diff --git a/src/onyxwasm_intrinsics.c b/src/onyxwasm_intrinsics.c deleted file mode 100644 index b39fad86..00000000 --- a/src/onyxwasm_intrinsics.c +++ /dev/null @@ -1,153 +0,0 @@ -// 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: - // - // - // - - 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: - // - // - // - - 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; -} diff --git a/src/onyxwasm_output.c b/src/onyxwasm_output.c deleted file mode 100644 index e1c57645..00000000 --- a/src/onyxwasm_output.c +++ /dev/null @@ -1,621 +0,0 @@ -// 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); -} diff --git a/src/onyxwasm_type_table.c b/src/onyxwasm_type_table.c deleted file mode 100644 index 4daf9d1c..00000000 --- a/src/onyxwasm_type_table.c +++ /dev/null @@ -1,374 +0,0 @@ -// 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 -} diff --git a/src/parser.c b/src/parser.c new file mode 100644 index 00000000..ced1fdc2 --- /dev/null +++ b/src/parser.c @@ -0,0 +1,2862 @@ +// 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; +} diff --git a/src/polymorph.c b/src/polymorph.c new file mode 100644 index 00000000..c05734bb --- /dev/null +++ b/src/polymorph.c @@ -0,0 +1,896 @@ + +// +// 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, "", 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; +} diff --git a/src/symres.c b/src/symres.c new file mode 100644 index 00000000..4e1bcef7 --- /dev/null +++ b/src/symres.c @@ -0,0 +1,1252 @@ +#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; +} diff --git a/src/types.c b/src/types.c new file mode 100644 index 00000000..84ccbd01 --- /dev/null +++ b/src/types.c @@ -0,0 +1,1268 @@ +#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", "", 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", "", 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 ""; + case Type_Kind_Enum: + if (type->Enum.name) + return type->Enum.name; + else + return ""; + + 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; +} diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 00000000..b98a9e21 --- /dev/null +++ b/src/utils.c @@ -0,0 +1,871 @@ +#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 +} + diff --git a/src/wasm.c b/src/wasm.c new file mode 100644 index 00000000..144deaba --- /dev/null +++ b/src/wasm.c @@ -0,0 +1,3672 @@ +// +// 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, + // + // + // 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" diff --git a/src/wasm_intrinsics.c b/src/wasm_intrinsics.c new file mode 100644 index 00000000..b39fad86 --- /dev/null +++ b/src/wasm_intrinsics.c @@ -0,0 +1,153 @@ +// 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: + // + // + // + + 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: + // + // + // + + 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; +} diff --git a/src/wasm_output.c b/src/wasm_output.c new file mode 100644 index 00000000..e1c57645 --- /dev/null +++ b/src/wasm_output.c @@ -0,0 +1,621 @@ +// 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); +} diff --git a/src/wasm_type_table.c b/src/wasm_type_table.c new file mode 100644 index 00000000..4daf9d1c --- /dev/null +++ b/src/wasm_type_table.c @@ -0,0 +1,374 @@ +// 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 +}